📄 af_decnet.c
字号:
struct sk_buff *skb; int val; switch(cmd) { case SIOCGIFADDR: case SIOCSIFADDR: return dn_dev_ioctl(cmd, (void __user *)arg); case SIOCATMARK: lock_sock(sk); val = !skb_queue_empty(&scp->other_receive_queue); if (scp->state != DN_RUN) val = -ENOTCONN; release_sock(sk); return val; case TIOCOUTQ: amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); if (amount < 0) amount = 0; err = put_user(amount, (int __user *)arg); break; case TIOCINQ: lock_sock(sk); if ((skb = skb_peek(&scp->other_receive_queue)) != NULL) { amount = skb->len; } else { struct sk_buff *skb = sk->sk_receive_queue.next; for(;;) { if (skb == (struct sk_buff *)&sk->sk_receive_queue) break; amount += skb->len; skb = skb->next; } } release_sock(sk); err = put_user(amount, (int __user *)arg); break; default: err = -ENOIOCTLCMD; break; } return err;}static int dn_listen(struct socket *sock, int backlog){ struct sock *sk = sock->sk; int err = -EINVAL; lock_sock(sk); if (sock_flag(sk, SOCK_ZAPPED)) goto out; if ((DN_SK(sk)->state != DN_O) || (sk->sk_state == TCP_LISTEN)) goto out; sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; sk->sk_state = TCP_LISTEN; err = 0; dn_rehash_sock(sk);out: release_sock(sk); return err;}static int dn_shutdown(struct socket *sock, int how){ struct sock *sk = sock->sk; struct dn_scp *scp = DN_SK(sk); int err = -ENOTCONN; lock_sock(sk); if (sock->state == SS_UNCONNECTED) goto out; err = 0; if (sock->state == SS_DISCONNECTING) goto out; err = -EINVAL; if (scp->state == DN_O) goto out; if (how != SHUTDOWN_MASK) goto out; sk->sk_shutdown = how; dn_destroy_sock(sk); err = 0;out: release_sock(sk); return err;}static int dn_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen){ struct sock *sk = sock->sk; int err; lock_sock(sk); err = __dn_setsockopt(sock, level, optname, optval, optlen, 0); release_sock(sk); return err;}static int __dn_setsockopt(struct socket *sock, int level,int optname, char __user *optval, int optlen, int flags){ struct sock *sk = sock->sk; struct dn_scp *scp = DN_SK(sk); long timeo; union { struct optdata_dn opt; struct accessdata_dn acc; int mode; unsigned long win; int val; unsigned char services; unsigned char info; } u; int err; if (optlen && !optval) return -EINVAL; if (optlen > sizeof(u)) return -EINVAL; if (copy_from_user(&u, optval, optlen)) return -EFAULT; switch(optname) { case DSO_CONDATA: if (sock->state == SS_CONNECTED) return -EISCONN; if ((scp->state != DN_O) && (scp->state != DN_CR)) return -EINVAL; if (optlen != sizeof(struct optdata_dn)) return -EINVAL; if (dn_ntohs(u.opt.opt_optl) > 16) return -EINVAL; memcpy(&scp->conndata_out, &u.opt, optlen); break; case DSO_DISDATA: if (sock->state != SS_CONNECTED && scp->accept_mode == ACC_IMMED) return -ENOTCONN; if (optlen != sizeof(struct optdata_dn)) return -EINVAL; if (dn_ntohs(u.opt.opt_optl) > 16) return -EINVAL; memcpy(&scp->discdata_out, &u.opt, optlen); break; case DSO_CONACCESS: if (sock->state == SS_CONNECTED) return -EISCONN; if (scp->state != DN_O) return -EINVAL; if (optlen != sizeof(struct accessdata_dn)) return -EINVAL; if ((u.acc.acc_accl > DN_MAXACCL) || (u.acc.acc_passl > DN_MAXACCL) || (u.acc.acc_userl > DN_MAXACCL)) return -EINVAL; memcpy(&scp->accessdata, &u.acc, optlen); break; case DSO_ACCEPTMODE: if (sock->state == SS_CONNECTED) return -EISCONN; if (scp->state != DN_O) return -EINVAL; if (optlen != sizeof(int)) return -EINVAL; if ((u.mode != ACC_IMMED) && (u.mode != ACC_DEFER)) return -EINVAL; scp->accept_mode = (unsigned char)u.mode; break; case DSO_CONACCEPT: if (scp->state != DN_CR) return -EINVAL; timeo = sock_rcvtimeo(sk, 0); err = dn_confirm_accept(sk, &timeo, sk->sk_allocation); return err; case DSO_CONREJECT: if (scp->state != DN_CR) return -EINVAL; scp->state = DN_DR; sk->sk_shutdown = SHUTDOWN_MASK; dn_nsp_send_disc(sk, 0x38, 0, sk->sk_allocation); break; default:#ifdef CONFIG_NETFILTER return nf_setsockopt(sk, PF_DECnet, optname, optval, optlen);#endif case DSO_LINKINFO: case DSO_STREAM: case DSO_SEQPACKET: return -ENOPROTOOPT; case DSO_MAXWINDOW: if (optlen != sizeof(unsigned long)) return -EINVAL; if (u.win > NSP_MAX_WINDOW) u.win = NSP_MAX_WINDOW; if (u.win == 0) return -EINVAL; scp->max_window = u.win; if (scp->snd_window > u.win) scp->snd_window = u.win; break; case DSO_NODELAY: if (optlen != sizeof(int)) return -EINVAL; if (scp->nonagle == 2) return -EINVAL; scp->nonagle = (u.val == 0) ? 0 : 1; /* if (scp->nonagle == 1) { Push pending frames } */ break; case DSO_CORK: if (optlen != sizeof(int)) return -EINVAL; if (scp->nonagle == 1) return -EINVAL; scp->nonagle = (u.val == 0) ? 0 : 2; /* if (scp->nonagle == 0) { Push pending frames } */ break; case DSO_SERVICES: if (optlen != sizeof(unsigned char)) return -EINVAL; if ((u.services & ~NSP_FC_MASK) != 0x01) return -EINVAL; if ((u.services & NSP_FC_MASK) == NSP_FC_MASK) return -EINVAL; scp->services_loc = u.services; break; case DSO_INFO: if (optlen != sizeof(unsigned char)) return -EINVAL; if (u.info & 0xfc) return -EINVAL; scp->info_loc = u.info; break; } return 0;}static int dn_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen){ struct sock *sk = sock->sk; int err; lock_sock(sk); err = __dn_getsockopt(sock, level, optname, optval, optlen, 0); release_sock(sk); return err;}static int __dn_getsockopt(struct socket *sock, int level,int optname, char __user *optval,int __user *optlen, int flags){ struct sock *sk = sock->sk; struct dn_scp *scp = DN_SK(sk); struct linkinfo_dn link; unsigned int r_len; void *r_data = NULL; unsigned int val; if(get_user(r_len , optlen)) return -EFAULT; switch(optname) { case DSO_CONDATA: if (r_len > sizeof(struct optdata_dn)) r_len = sizeof(struct optdata_dn); r_data = &scp->conndata_in; break; case DSO_DISDATA: if (r_len > sizeof(struct optdata_dn)) r_len = sizeof(struct optdata_dn); r_data = &scp->discdata_in; break; case DSO_CONACCESS: if (r_len > sizeof(struct accessdata_dn)) r_len = sizeof(struct accessdata_dn); r_data = &scp->accessdata; break; case DSO_ACCEPTMODE: if (r_len > sizeof(unsigned char)) r_len = sizeof(unsigned char); r_data = &scp->accept_mode; break; case DSO_LINKINFO: if (r_len > sizeof(struct linkinfo_dn)) r_len = sizeof(struct linkinfo_dn); switch(sock->state) { case SS_CONNECTING: link.idn_linkstate = LL_CONNECTING; break; case SS_DISCONNECTING: link.idn_linkstate = LL_DISCONNECTING; break; case SS_CONNECTED: link.idn_linkstate = LL_RUNNING; break; default: link.idn_linkstate = LL_INACTIVE; } link.idn_segsize = scp->segsize_rem; r_data = &link; break; default:#ifdef CONFIG_NETFILTER { int val, len; if(get_user(len, optlen)) return -EFAULT; val = nf_getsockopt(sk, PF_DECnet, optname, optval, &len); if (val >= 0) val = put_user(len, optlen); return val; }#endif case DSO_STREAM: case DSO_SEQPACKET: case DSO_CONACCEPT: case DSO_CONREJECT: return -ENOPROTOOPT; case DSO_MAXWINDOW: if (r_len > sizeof(unsigned long)) r_len = sizeof(unsigned long); r_data = &scp->max_window; break; case DSO_NODELAY: if (r_len > sizeof(int)) r_len = sizeof(int); val = (scp->nonagle == 1); r_data = &val; break; case DSO_CORK: if (r_len > sizeof(int)) r_len = sizeof(int); val = (scp->nonagle == 2); r_data = &val; break; case DSO_SERVICES: if (r_len > sizeof(unsigned char)) r_len = sizeof(unsigned char); r_data = &scp->services_rem; break; case DSO_INFO: if (r_len > sizeof(unsigned char)) r_len = sizeof(unsigned char); r_data = &scp->info_rem; break; } if (r_data) { if (copy_to_user(optval, r_data, r_len)) return -EFAULT; if (put_user(r_len, optlen)) return -EFAULT; } return 0;}static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int target){ struct sk_buff *skb = q->next; int len = 0; if (flags & MSG_OOB) return !skb_queue_empty(q) ? 1 : 0; while(skb != (struct sk_buff *)q) { struct dn_skb_cb *cb = DN_SKB_CB(skb); len += skb->len; if (cb->nsp_flags & 0x40) { /* SOCK_SEQPACKET reads to EOM */ if (sk->sk_type == SOCK_SEQPACKET) return 1; /* so does SOCK_STREAM unless WAITALL is specified */ if (!(flags & MSG_WAITALL)) return 1; } /* minimum data length for read exceeded */ if (len >= target) return 1; skb = skb->next; } return 0;}static int dn_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags){ struct sock *sk = sock->sk; struct dn_scp *scp = DN_SK(sk); struct sk_buff_head *queue = &sk->sk_receive_queue; size_t target = size > 1 ? 1 : 0; size_t copied = 0; int rv = 0; struct sk_buff *skb, *nskb; struct dn_skb_cb *cb = NULL; unsigned char eor = 0; long timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); lock_sock(sk); if (sock_flag(sk, SOCK_ZAPPED)) { rv = -EADDRNOTAVAIL; goto out; } if (sk->sk_shutdown & RCV_SHUTDOWN) { rv = 0; goto out; } rv = dn_check_state(sk, NULL, 0, &timeo, flags); if (rv) goto out; if (flags & ~(MSG_CMSG_COMPAT|MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT|MSG_NOSIGNAL)) { rv = -EOPNOTSUPP; goto out; } if (flags & MSG_OOB) queue = &scp->other_receive_queue; if (flags & MSG_WAITALL) target = size; /* * See if there is data ready to read, sleep if there isn't */ for(;;) { if (sk->sk_err) goto out; if (!skb_queue_empty(&scp->other_receive_queue)) { if (!(flags & MSG_OOB)) { msg->msg_flags |= MSG_OOB; if (!scp->other_report) { scp->other_report = 1; goto out; } } } if (scp->state != DN_RUN) goto out; if (signal_pending(current)) { rv = sock_intr_errno(timeo); goto out; } if (dn_data_ready(sk, queue, flags, target)) break; if (flags & MSG_DONTWAIT) { rv = -EWOULDBLOCK; goto out; } set_bit(SOCK_ASYNC_WAITDATA, &sock->flags); SOCK_SLEEP_PRE(sk) if (!dn_data_ready(sk, queue, flags, target)) schedule(); SOCK_SLEEP_POST(sk) clear_bit(SOCK_ASYNC_WAITDATA, &sock->flags); } for(skb = queue->next; skb != (struct sk_buff *)queue; skb = nskb) { unsigned int chunk = skb->len; cb = DN_SKB_CB(skb); if ((chunk + copied) > size) chunk = size - copied; if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { rv = -EFAULT; break; } copied += chunk; if (!(flags & MSG_PEEK)) skb_pull(skb, chunk); eor = cb->nsp_flags & 0x40; nskb = skb->next; if (skb->len == 0) { skb_unlink(skb, queue); kfree_skb(skb); /* * N.B. Don't refer to skb or cb after this point * in loop. */ if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) { scp->flowloc_sw = DN_SEND; dn_nsp_send_link(sk, DN_SEND, 0); } } if (eor) { if (sk->sk_type == SOCK_SEQPACKET) break; if (!(flags & MSG_WAITALL)) break; } if (flags & MSG_OOB) break; if (copied >= target) break; } rv = copied; if (eor && (sk->sk_type == SOCK_SEQPACKET)) msg->msg_flags |= MSG_EOR;out: if (rv == 0) rv = (flags & MSG_PEEK) ? -sk->sk_err : sock_error(sk); if ((rv >= 0) && msg->msg_name) { memcpy(msg->msg_name, &scp->peer, sizeof(struct sockaddr_dn)); msg->msg_namelen = sizeof(struct sockaddr_dn); } release_sock(sk); return rv;}static inline int dn_queue_too_long(struct dn_scp *scp, struct sk_buff_head *queue, int flags){ unsigned char fctype = scp->services_rem & NSP_FC_MASK; if (skb_queue_len(queue) >= scp->snd_window) return 1; if (fctype != NSP_FC_NONE) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -