📄 socket.c
字号:
/* * Send a datagram to a given address. We move the address into kernel * space and check the user space data area is readable before invoking * the protocol. */asmlinkage long sys_sendto(int fd, void * buff, size_t len, unsigned flags, struct sockaddr *addr, int addr_len){ struct socket *sock; char address[MAX_SOCK_ADDR]; int err; struct msghdr msg; struct iovec iov; sock = sockfd_lookup(fd, &err); if (!sock) goto out; iov.iov_base=buff; iov.iov_len=len; msg.msg_name=NULL; msg.msg_iov=&iov; msg.msg_iovlen=1; msg.msg_control=NULL; msg.msg_controllen=0; msg.msg_namelen=0; if(addr) { err = move_addr_to_kernel(addr, addr_len, address); if (err < 0) goto out_put; msg.msg_name=address; msg.msg_namelen=addr_len; } if (sock->file->f_flags & O_NONBLOCK) flags |= MSG_DONTWAIT; msg.msg_flags = flags; err = sock_sendmsg(sock, &msg, len);out_put: sockfd_put(sock);out: return err;}/* * Send a datagram down a socket. */asmlinkage long sys_send(int fd, void * buff, size_t len, unsigned flags){ return sys_sendto(fd, buff, len, flags, NULL, 0);}/* * Receive a frame from the socket and optionally record the address of the * sender. We verify the buffers are writable and if needed move the * sender address from kernel to user space. */asmlinkage long sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags, struct sockaddr *addr, int *addr_len){ struct socket *sock; struct iovec iov; struct msghdr msg; char address[MAX_SOCK_ADDR]; int err,err2; sock = sockfd_lookup(fd, &err); if (!sock) goto out; msg.msg_control=NULL; msg.msg_controllen=0; msg.msg_iovlen=1; msg.msg_iov=&iov; iov.iov_len=size; iov.iov_base=ubuf; msg.msg_name=address; msg.msg_namelen=MAX_SOCK_ADDR; if (sock->file->f_flags & O_NONBLOCK) flags |= MSG_DONTWAIT; err=sock_recvmsg(sock, &msg, size, flags); if(err >= 0 && addr != NULL && msg.msg_namelen) { err2=move_addr_to_user(address, msg.msg_namelen, addr, addr_len); if(err2<0) err=err2; } sockfd_put(sock); out: return err;}/* * Receive a datagram from a socket. */asmlinkage long sys_recv(int fd, void * ubuf, size_t size, unsigned flags){ return sys_recvfrom(fd, ubuf, size, flags, NULL, NULL);}/* * Set a socket option. Because we don't know the option lengths we have * to pass the user mode parameter for the protocols to sort out. */asmlinkage long sys_setsockopt(int fd, int level, int optname, char *optval, int optlen){ int err; struct socket *sock; if (optlen < 0) return -EINVAL; if ((sock = sockfd_lookup(fd, &err))!=NULL) { if (level == SOL_SOCKET) err=sock_setsockopt(sock,level,optname,optval,optlen); else err=sock->ops->setsockopt(sock, level, optname, optval, optlen); sockfd_put(sock); } return err;}/* * Get a socket option. Because we don't know the option lengths we have * to pass a user mode parameter for the protocols to sort out. */asmlinkage long sys_getsockopt(int fd, int level, int optname, char *optval, int *optlen){ int err; struct socket *sock; if ((sock = sockfd_lookup(fd, &err))!=NULL) { if (level == SOL_SOCKET) err=sock_getsockopt(sock,level,optname,optval,optlen); else err=sock->ops->getsockopt(sock, level, optname, optval, optlen); sockfd_put(sock); } return err;}/* * Shutdown a socket. */asmlinkage long sys_shutdown(int fd, int how){ int err; struct socket *sock; if ((sock = sockfd_lookup(fd, &err))!=NULL) { err=sock->ops->shutdown(sock, how); sockfd_put(sock); } return err;}/* * BSD sendmsg interface */asmlinkage long sys_sendmsg(int fd, struct msghdr *msg, unsigned flags){ struct socket *sock; char address[MAX_SOCK_ADDR]; struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; unsigned char ctl[sizeof(struct cmsghdr) + 20]; /* 20 is size of ipv6_pktinfo */ unsigned char *ctl_buf = ctl; struct msghdr msg_sys; int err, ctl_len, iov_size, total_len; err = -EFAULT; if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr))) goto out; sock = sockfd_lookup(fd, &err); if (!sock) goto out; /* do not move before msg_sys is valid */ err = -EINVAL; if (msg_sys.msg_iovlen > UIO_MAXIOV) goto out_put; /* Check whether to allocate the iovec area*/ err = -ENOMEM; iov_size = msg_sys.msg_iovlen * sizeof(struct iovec); if (msg_sys.msg_iovlen > UIO_FASTIOV) { iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); if (!iov) goto out_put; } /* This will also move the address data into kernel space */ err = verify_iovec(&msg_sys, iov, address, VERIFY_READ); if (err < 0) goto out_freeiov; total_len = err; err = -ENOBUFS; if (msg_sys.msg_controllen > INT_MAX) goto out_freeiov; ctl_len = msg_sys.msg_controllen; if (ctl_len) { if (ctl_len > sizeof(ctl)) { ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL); if (ctl_buf == NULL) goto out_freeiov; } err = -EFAULT; if (copy_from_user(ctl_buf, msg_sys.msg_control, ctl_len)) goto out_freectl; msg_sys.msg_control = ctl_buf; } msg_sys.msg_flags = flags; if (sock->file->f_flags & O_NONBLOCK) msg_sys.msg_flags |= MSG_DONTWAIT; err = sock_sendmsg(sock, &msg_sys, total_len);out_freectl: if (ctl_buf != ctl) sock_kfree_s(sock->sk, ctl_buf, ctl_len);out_freeiov: if (iov != iovstack) sock_kfree_s(sock->sk, iov, iov_size);out_put: sockfd_put(sock);out: return err;}/* * BSD recvmsg interface */asmlinkage long sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags){ struct socket *sock; struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov=iovstack; struct msghdr msg_sys; unsigned long cmsg_ptr; int err, iov_size, total_len, len; /* kernel mode address */ char addr[MAX_SOCK_ADDR]; /* user mode address pointers */ struct sockaddr *uaddr; int *uaddr_len; err=-EFAULT; if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr))) goto out; sock = sockfd_lookup(fd, &err); if (!sock) goto out; err = -EINVAL; if (msg_sys.msg_iovlen > UIO_MAXIOV) goto out_put; /* Check whether to allocate the iovec area*/ err = -ENOMEM; iov_size = msg_sys.msg_iovlen * sizeof(struct iovec); if (msg_sys.msg_iovlen > UIO_FASTIOV) { iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); if (!iov) goto out_put; } /* * Save the user-mode address (verify_iovec will change the * kernel msghdr to use the kernel address space) */ uaddr = msg_sys.msg_name; uaddr_len = &msg->msg_namelen; err = verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE); if (err < 0) goto out_freeiov; total_len=err; cmsg_ptr = (unsigned long)msg_sys.msg_control; msg_sys.msg_flags = 0; if (sock->file->f_flags & O_NONBLOCK) flags |= MSG_DONTWAIT; err = sock_recvmsg(sock, &msg_sys, total_len, flags); if (err < 0) goto out_freeiov; len = err; if (uaddr != NULL && msg_sys.msg_namelen) { err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len); if (err < 0) goto out_freeiov; } err = __put_user(msg_sys.msg_flags, &msg->msg_flags); if (err) goto out_freeiov; err = __put_user((unsigned long)msg_sys.msg_control-cmsg_ptr, &msg->msg_controllen); if (err) goto out_freeiov; err = len;out_freeiov: if (iov != iovstack) sock_kfree_s(sock->sk, iov, iov_size);out_put: sockfd_put(sock);out: return err;}/* * Perform a file control on a socket file descriptor. * * Doesn't acquire a fd lock, because no network fcntl * function sleeps currently. */int sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg){ struct socket *sock; sock = socki_lookup (filp->f_dentry->d_inode); if (sock && sock->ops) return sock_no_fcntl(sock, cmd, arg); return(-EINVAL);}/* Argument list sizes for sys_socketcall */#define AL(x) ((x) * sizeof(unsigned long))static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};#undef AL/* * System call vectors. * * Argument checking cleaned up. Saved 20% in size. * This function doesn't need to set the kernel lock because * it is set by the callees. */asmlinkage long sys_socketcall(int call, unsigned long *args){ unsigned long a[6]; unsigned long a0,a1; int err; if(call<1||call>SYS_RECVMSG) return -EINVAL; /* copy_from_user should be SMP safe. */ if (copy_from_user(a, args, nargs[call])) return -EFAULT; a0=a[0]; a1=a[1]; switch(call) { case SYS_SOCKET: err = sys_socket(a0,a1,a[2]); break; case SYS_BIND: err = sys_bind(a0,(struct sockaddr *)a1, a[2]); break; case SYS_CONNECT: err = sys_connect(a0, (struct sockaddr *)a1, a[2]); break; case SYS_LISTEN: err = sys_listen(a0,a1); break; case SYS_ACCEPT: err = sys_accept(a0,(struct sockaddr *)a1, (int *)a[2]); break; case SYS_GETSOCKNAME: err = sys_getsockname(a0,(struct sockaddr *)a1, (int *)a[2]); break; case SYS_GETPEERNAME: err = sys_getpeername(a0, (struct sockaddr *)a1, (int *)a[2]); break; case SYS_SOCKETPAIR: err = sys_socketpair(a0,a1, a[2], (int *)a[3]); break; case SYS_SEND: err = sys_send(a0, (void *)a1, a[2], a[3]); break; case SYS_SENDTO: err = sys_sendto(a0,(void *)a1, a[2], a[3], (struct sockaddr *)a[4], a[5]); break; case SYS_RECV: err = sys_recv(a0, (void *)a1, a[2], a[3]); break; case SYS_RECVFROM: err = sys_recvfrom(a0, (void *)a1, a[2], a[3], (struct sockaddr *)a[4], (int *)a[5]); break; case SYS_SHUTDOWN: err = sys_shutdown(a0,a1); break; case SYS_SETSOCKOPT: err = sys_setsockopt(a0, a1, a[2], (char *)a[3], a[4]); break; case SYS_GETSOCKOPT: err = sys_getsockopt(a0, a1, a[2], (char *)a[3], (int *)a[4]); break; case SYS_SENDMSG: err = sys_sendmsg(a0, (struct msghdr *) a1, a[2]); break; case SYS_RECVMSG: err = sys_recvmsg(a0, (struct msghdr *) a1, a[2]); break; default: err = -EINVAL; break; } return err;}/* * This function is called by a protocol handler that wants to * advertise its address family, and have it linked into the * SOCKET module. */int sock_register(struct net_proto_family *ops){ int err; if (ops->family >= NPROTO) { printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family, NPROTO); return -ENOBUFS; } net_family_write_lock(); err = -EEXIST; if (net_families[ops->family] == NULL) { net_families[ops->family]=ops; err = 0; } net_family_write_unlock(); return err;}/* * This function is called by a protocol handler that wants to * remove its address family, and have it unlinked from the * SOCKET module. */int sock_unregister(int family){ if (family < 0 || family >= NPROTO) return -1; net_family_write_lock(); net_families[family]=NULL; net_family_write_unlock(); return 0;}extern void sk_init(void);#ifdef CONFIG_WAN_ROUTERextern void wanrouter_init(void);#endif#ifdef CONFIG_BLUEZextern void bluez_init(void);#endifvoid __init sock_init(void){ int i; printk(KERN_INFO "Linux NET4.0 for Linux 2.4\n"); printk(KERN_INFO "Based upon Swansea University Computer Society NET3.039\n"); /* * Initialize all address (protocol) families. */ for (i = 0; i < NPROTO; i++) net_families[i] = NULL; /* * Initialize sock SLAB cache. */ sk_init();#ifdef SLAB_SKB /* * Initialize skbuff SLAB cache */ skb_init();#endif /* * Wan router layer. */#ifdef CONFIG_WAN_ROUTER wanrouter_init();#endif /* * Initialize the protocols module. */ register_filesystem(&sock_fs_type); sock_mnt = kern_mount(&sock_fs_type); /* The real protocol initialization is performed when * do_initcalls is run. */ /* * The netlink device handler may be needed early. */#ifdef CONFIG_NET rtnetlink_init();#endif#ifdef CONFIG_NETLINK_DEV init_netlink();#endif#ifdef CONFIG_NETFILTER netfilter_init();#endif#ifdef CONFIG_BLUEZ bluez_init();#endif}int socket_get_info(char *buffer, char **start, off_t offset, int length){ int len, cpu; int counter = 0; for (cpu=0; cpu<smp_num_cpus; cpu++) counter += sockets_in_use[cpu_logical_map(cpu)].counter; /* It can be negative, by the way. 8) */ if (counter < 0) counter = 0; len = sprintf(buffer, "sockets: used %d\n", counter); if (offset >= len) { *start = buffer; return 0; } *start = buffer + offset; len -= offset; if (len > length) len = length; if (len < 0) len = 0; return len;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -