This patch is _NOT_ ready to merge, just RFC. Any comments are more than welcome! Description: A customer wants socket flags set to a socket fd to be exported from /proc, such as TCP_NODELAY, so finally they can get these values with 'lsof'. David Miller pointed out Solaris already has this feature, but he doesn't like this to be in Linux. I don't know how Solaris does this, I just do what I think. A sample output is like this: #cat /proc/`pgrep sshd`/sockinfo/3 level optname value length 1 1 0 4 1 5 0 4 1 6 0 4 1 7 16384 4 1 8 87380 4 1 2 1 4 1 9 0 4 1 3 1 4 1 4 0 4 1 10 0 4 1 11 0 4 1 12 0 4 1 13 0 4 1 29 0 4 1 35 0 4 1 37 0 4 1 20 0 8 1 20 0 8 1 18 1 4 1 19 1 4 1 16 0 4 1 30 1 4 1 34 0 4 1 36 0 4 6 2 536 4 6 1 0 4 6 3 0 4 6 4 7200 4 6 5 75 4 6 6 9 4 6 7 5 4 6 8 60 4 6 9 0 4 6 10 0 4 6 12 1 4 Currently, only most of TCP, UDP and socket flags are exported. I tested this on x86_64, it works fine, just as the above sample output shows. TODO: Make the patch against rhel5 kernel. Export more other flags. Refine the output format. Add lsof support. --- diff --git a/fs/file_table.c b/fs/file_table.c index 54018fe..5cff2c4 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -295,10 +295,9 @@ void __fput(struct file *file) mntput(mnt); } -struct file *fget(unsigned int fd) +struct file *fget_files(struct files_struct *files, unsigned int fd) { struct file *file; - struct files_struct *files = current->files; rcu_read_lock(); file = fcheck_files(files, fd); @@ -312,6 +311,12 @@ struct file *fget(unsigned int fd) rcu_read_unlock(); return file; + +} + +struct file *fget(unsigned int fd) +{ + return fget_files(current->files, fd); } EXPORT_SYMBOL(fget); diff --git a/fs/proc/base.c b/fs/proc/base.c index fb45615..1438460 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -81,6 +81,7 @@ #include #include #include +#include #include "internal.h" /* NOTE: @@ -1659,6 +1660,42 @@ static int proc_fd_info(struct inode *inode, struct path *path, char *info) return -ENOENT; } +static int proc_sock_info(struct inode *inode, char *info) +{ + struct task_struct *task = get_proc_task(inode); + struct files_struct *files = NULL; + struct socket *sock; + int err; + int len; + int fd = proc_fd(inode); + + if (task) { + files = get_files_struct(task); + put_task_struct(task); + } + if (files) { + /* + * We are not taking a ref to the file structure, so we must + * hold ->file_lock. + */ + spin_lock(&files->file_lock); + sock = sockfd_files_lookup(files, fd, &err); + if (sock) { + len = sprintf(info,"level\toptname\tvalue\tlength\n"); + info += len; + len = 2*PAGE_SIZE - len; + sock_get_all_sockopt(sock, info, &len); + sockfd_put(sock); + spin_unlock(&files->file_lock); + put_files_struct(files); + return 0; + } + spin_unlock(&files->file_lock); + put_files_struct(files); + } + return -ENOENT; +} + static int proc_fd_link(struct inode *inode, struct path *path) { return proc_fd_info(inode, path, NULL); @@ -1842,6 +1879,70 @@ out_no_task: return retval; } +static int proc_readsock_common(struct file * filp, void * dirent, + filldir_t filldir, instantiate_t instantiate) +{ + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = dentry->d_inode; + struct task_struct *p = get_proc_task(inode); + unsigned int fd, ino; + int retval; + int err; + struct files_struct * files; + struct socket *sock = NULL; + + retval = -ENOENT; + if (!p) + goto out_no_task; + retval = 0; + + fd = filp->f_pos; + switch (fd) { + case 0: + if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) + goto out; + filp->f_pos++; + case 1: + ino = parent_ino(dentry); + if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0) + goto out; + filp->f_pos++; + default: + files = get_files_struct(p); + if (!files) + goto out; + rcu_read_lock(); + for (fd = filp->f_pos-2; + fd < files_fdtable(files)->max_fds; + fd++, filp->f_pos++) { + char name[PROC_NUMBUF]; + int len; + + sock = sockfd_files_lookup(files, fd, &err); + if (!sock) + continue; + rcu_read_unlock(); + + len = snprintf(name, sizeof(name), "%d", fd); + if (proc_fill_cache(filp, dirent, filldir, + name, len, instantiate, + p, &fd) < 0) { + rcu_read_lock(); + break; + } + rcu_read_lock(); + sockfd_put(sock); + } + rcu_read_unlock(); + put_files_struct(files); + } +out: + put_task_struct(p); +out_no_task: + return retval; +} + + static struct dentry *proc_lookupfd(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { @@ -1863,11 +1964,33 @@ static ssize_t proc_fdinfo_read(struct file *file, char __user *buf, return err; } +static ssize_t proc_sockinfo_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + char *tmp; + int err; + + tmp = kzalloc(2*PAGE_SIZE, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + err = proc_sock_info(file->f_path.dentry->d_inode, tmp); + if (!err) + err = simple_read_from_buffer(buf, len, ppos, tmp, strlen(tmp)); + kfree(tmp); + return err; +} + static const struct file_operations proc_fdinfo_file_operations = { .open = nonseekable_open, .read = proc_fdinfo_read, }; +static const struct file_operations proc_sockinfo_file_operations = { + .open = nonseekable_open, + .read = proc_sockinfo_read, +}; + static const struct file_operations proc_fd_operations = { .read = generic_read_dir, .readdir = proc_readfd, @@ -1923,6 +2046,33 @@ static struct dentry *proc_fdinfo_instantiate(struct inode *dir, return error; } +static struct dentry *proc_sockinfo_instantiate(struct inode *dir, + struct dentry *dentry, struct task_struct *task, const void *ptr) +{ + unsigned fd = *(unsigned *)ptr; + struct inode *inode; + struct proc_inode *ei; + struct dentry *error = ERR_PTR(-ENOENT); + + inode = proc_pid_make_inode(dir->i_sb, task); + if (!inode) + goto out; + ei = PROC_I(inode); + ei->fd = fd; + inode->i_mode = S_IFREG | S_IRUSR; + inode->i_fop = &proc_sockinfo_file_operations; + dentry->d_op = &tid_fd_dentry_operations; + d_add(dentry, inode); + /* Close the race of the process dying before we return the dentry */ + if (tid_fd_revalidate(dentry, NULL)) + error = NULL; + + out: + return error; +} + + + static struct dentry *proc_lookupfdinfo(struct inode *dir, struct dentry *dentry, struct nameidata *nd) @@ -1930,17 +2080,35 @@ static struct dentry *proc_lookupfdinfo(struct inode *dir, return proc_lookupfd_common(dir, dentry, proc_fdinfo_instantiate); } +static struct dentry *proc_lookupsockinfo(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + return proc_lookupfd_common(dir, dentry, proc_sockinfo_instantiate); +} + static int proc_readfdinfo(struct file *filp, void *dirent, filldir_t filldir) { return proc_readfd_common(filp, dirent, filldir, proc_fdinfo_instantiate); } +static int proc_readsockinfo(struct file *filp, void *dirent, filldir_t filldir) +{ + return proc_readsock_common(filp, dirent, filldir, + proc_sockinfo_instantiate); +} + static const struct file_operations proc_fdinfo_operations = { .read = generic_read_dir, .readdir = proc_readfdinfo, }; +static const struct file_operations proc_sockinfo_operations = { + .read = generic_read_dir, + .readdir = proc_readsockinfo, +}; + /* * proc directories can do almost nothing.. */ @@ -1949,6 +2117,11 @@ static const struct inode_operations proc_fdinfo_inode_operations = { .setattr = proc_setattr, }; +static const struct inode_operations proc_sockinfo_inode_operations = { + .lookup = proc_lookupsockinfo, + .setattr = proc_setattr, +}; + static struct dentry *proc_pident_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) @@ -2475,6 +2648,7 @@ static const struct pid_entry tgid_base_stuff[] = { DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations), #ifdef CONFIG_NET DIR("net", S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations), + DIR("sockinfo", S_IRUSR|S_IXUSR, proc_sockinfo_inode_operations, proc_sockinfo_operations), #endif REG("environ", S_IRUSR, proc_environ_operations), INF("auxv", S_IRUSR, proc_pid_auxv), diff --git a/include/linux/file.h b/include/linux/file.h index 335a0a5..217c910 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -8,6 +8,7 @@ #include #include #include +#include struct file; @@ -30,7 +31,9 @@ static inline void fput_light(struct file *file, int fput_needed) fput(file); } +struct files_struct; extern struct file *fget(unsigned int fd); +extern struct file *fget_files(struct files_struct* files, unsigned int fd); extern struct file *fget_light(unsigned int fd, int *fput_needed); extern void set_close_on_exec(unsigned int fd, int flag); extern void put_filp(struct file *); diff --git a/include/linux/net.h b/include/linux/net.h index 4fc2ffd..b3de91d 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -225,8 +225,14 @@ extern int sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags); extern int sock_map_fd(struct socket *sock, int flags); extern struct socket *sockfd_lookup(int fd, int *err); + +struct files_struct; +extern struct socket *sockfd_files_lookup(struct files_struct *files, + int fd, int *err); + #define sockfd_put(sock) fput(sock->file) extern int net_ratelimit(void); +extern void sock_get_all_sockopt(struct socket *, char *, int *); #define net_random() random32() #define net_srandom(seed) srandom32((__force u32)seed) diff --git a/include/linux/socket.h b/include/linux/socket.h index 421afb4..92787e2 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -304,6 +304,10 @@ struct ucred { #define SOL_PNPIPE 275 #define SOL_RDS 276 +#ifdef __KERNEL__ +#define SO_ALL -1 +#endif + /* IPX options */ #define IPX_TYPE 1 diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 9d5078b..67f652d 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -96,6 +96,9 @@ enum { #define TCP_QUICKACK 12 /* Block/reenable quick acks */ #define TCP_CONGESTION 13 /* Congestion control algorithm */ #define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ +#ifdef __KERNEL__ +#define TCP_ALL -1 /* Get the values of all options */ +#endif #define TCPI_OPT_TIMESTAMPS 1 #define TCPI_OPT_SACK 2 diff --git a/include/linux/udp.h b/include/linux/udp.h index 0cf5c4c..a9473c0 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -30,6 +30,9 @@ struct udphdr { #define UDP_CORK 1 /* Never send partially complete segments */ #define UDP_ENCAP 100 /* Set the socket to accept encapsulated packets */ +#ifdef __KERNEL__ +#define UDP_ALL -1 +#endif /* UDP encapsulation types */ #define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ #define UDP_ENCAP_ESPINUDP 2 /* draft-ietf-ipsec-udp-encaps-06 */ diff --git a/net/core/sock.c b/net/core/sock.c index 7dbf3ff..2d528a1 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -719,83 +719,95 @@ int sock_getsockopt(struct socket *sock, int level, int optname, unsigned int lv = sizeof(int); int len; + char *buf; - if (get_user(len, optlen)) - return -EFAULT; - if (len < 0) - return -EINVAL; + if (optname != SO_ALL) { + if (get_user(len, optlen)) + return -EFAULT; + if (len < 0) + return -EINVAL; + } else + len = 0; memset(&v, 0, sizeof(v)); +#define BREAK(OPT, val) \ + if (optname != SO_ALL) \ + break; \ + len += scnprintf(buf+len, *(int *)optlen - len, "%d\t%d\t%ld\t%d\n", \ + level, OPT, (long)(val), (int)sizeof(val)) +#define STOP() if (optname == SO_ALL) { \ + *(int *)optlen = len; \ + return 0; \ + } + + buf = (char *) optval; switch(optname) { + case SO_ALL: case SO_DEBUG: v.val = sock_flag(sk, SOCK_DBG); - break; + BREAK(SO_DEBUG, v.val); case SO_DONTROUTE: v.val = sock_flag(sk, SOCK_LOCALROUTE); - break; + BREAK(SO_DONTROUTE, v.val); - case SO_BROADCAST: + case SO_BROADCAST: v.val = !!sock_flag(sk, SOCK_BROADCAST); - break; + BREAK(SO_BROADCAST, v.val); case SO_SNDBUF: v.val = sk->sk_sndbuf; - break; + BREAK(SO_SNDBUF, v.val); case SO_RCVBUF: v.val = sk->sk_rcvbuf; - break; + BREAK(SO_RCVBUF, v.val); case SO_REUSEADDR: v.val = sk->sk_reuse; - break; + BREAK(SO_REUSEADDR, v.val); case SO_KEEPALIVE: v.val = !!sock_flag(sk, SOCK_KEEPOPEN); - break; + BREAK(SO_KEEPALIVE, v.val); case SO_TYPE: v.val = sk->sk_type; - break; + BREAK(SO_TYPE, v.val); case SO_ERROR: v.val = -sock_error(sk); if (v.val==0) v.val = xchg(&sk->sk_err_soft, 0); - break; + BREAK(SO_ERROR, v.val); case SO_OOBINLINE: v.val = !!sock_flag(sk, SOCK_URGINLINE); - break; + BREAK(SO_OOBINLINE, v.val); case SO_NO_CHECK: v.val = sk->sk_no_check; - break; + BREAK(SO_NO_CHECK, v.val); case SO_PRIORITY: v.val = sk->sk_priority; - break; + BREAK(SO_PRIORITY, v.val); case SO_LINGER: lv = sizeof(v.ling); v.ling.l_onoff = !!sock_flag(sk, SOCK_LINGER); v.ling.l_linger = sk->sk_lingertime / HZ; - break; - - case SO_BSDCOMPAT: - sock_warn_obsolete_bsdism("getsockopt"); - break; + BREAK(SO_LINGER, v.ling.l_onoff); case SO_TIMESTAMP: v.val = sock_flag(sk, SOCK_RCVTSTAMP) && !sock_flag(sk, SOCK_RCVTSTAMPNS); - break; + BREAK(SO_TIMESTAMP, v.val); case SO_TIMESTAMPNS: v.val = sock_flag(sk, SOCK_RCVTSTAMPNS); - break; + BREAK(SO_TIMESTAMPNS, v.val); case SO_TIMESTAMPING: v.val = 0; @@ -813,7 +825,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val |= SOF_TIMESTAMPING_SYS_HARDWARE; if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)) v.val |= SOF_TIMESTAMPING_RAW_HARDWARE; - break; + BREAK(SO_TIMESTAMPING, v.val); case SO_RCVTIMEO: lv=sizeof(struct timeval); @@ -824,7 +836,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.tm.tv_sec = sk->sk_rcvtimeo / HZ; v.tm.tv_usec = ((sk->sk_rcvtimeo % HZ) * 1000000) / HZ; } - break; + BREAK(SO_RCVTIMEO, v.tm.tv_sec*1000000+v.tm.tv_usec); case SO_SNDTIMEO: lv=sizeof(struct timeval); @@ -835,19 +847,36 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.tm.tv_sec = sk->sk_sndtimeo / HZ; v.tm.tv_usec = ((sk->sk_sndtimeo % HZ) * 1000000) / HZ; } - break; + BREAK(SO_RCVTIMEO, v.tm.tv_sec*1000000+v.tm.tv_usec); case SO_RCVLOWAT: v.val = sk->sk_rcvlowat; - break; + BREAK(SO_RCVLOWAT, v.val); case SO_SNDLOWAT: v.val=1; - break; + BREAK(SO_SNDLOWAT, v.val); case SO_PASSCRED: v.val = test_bit(SOCK_PASSCRED, &sock->flags) ? 1 : 0; - break; + BREAK(SO_PASSCRED, v.val); + + + /* Dubious BSD thing... Probably nobody even uses it, but + * the UNIX standard wants it for whatever reason... -DaveM + */ + case SO_ACCEPTCONN: + v.val = sk->sk_state == TCP_LISTEN; + BREAK(SO_ACCEPTCONN, v.val); + + case SO_PASSSEC: + v.val = test_bit(SOCK_PASSSEC, &sock->flags) ? 1 : 0; + BREAK(SO_PASSSEC, v.val); + + case SO_MARK: + v.val = sk->sk_mark; + BREAK(SO_MARK, v.val); + STOP(); case SO_PEERCRED: if (len > sizeof(sk->sk_peercred)) @@ -869,24 +898,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname, goto lenout; } - /* Dubious BSD thing... Probably nobody even uses it, but - * the UNIX standard wants it for whatever reason... -DaveM - */ - case SO_ACCEPTCONN: - v.val = sk->sk_state == TCP_LISTEN; - break; - - case SO_PASSSEC: - v.val = test_bit(SOCK_PASSSEC, &sock->flags) ? 1 : 0; + case SO_BSDCOMPAT: + sock_warn_obsolete_bsdism("getsockopt"); break; case SO_PEERSEC: return security_socket_getpeersec_stream(sock, optval, optlen, len); - - case SO_MARK: - v.val = sk->sk_mark; - break; - default: return -ENOPROTOOPT; } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1d7f49c..4e493bf 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2309,51 +2309,73 @@ static int do_tcp_getsockopt(struct sock *sk, int level, struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); int val, len; + int length = 0; + char *buf; - if (get_user(len, optlen)) - return -EFAULT; + if (optname != TCP_ALL) { + if (get_user(len, optlen)) + return -EFAULT; - len = min_t(unsigned int, len, sizeof(int)); + len = min_t(unsigned int, len, sizeof(int)); + if (len < 0) + return -EINVAL; + } else { + len = sizeof(int); + } - if (len < 0) - return -EINVAL; +#define BREAK(OPT, val) \ + if (optname != TCP_ALL) \ + break; \ + length += scnprintf(buf+length, *(int *)optlen - length, \ + "%d\t%d\t%ld\t%d\n", \ + level, OPT, (long)(val), (int)sizeof(val)) +#define STOP() if (optname == TCP_ALL) { \ + *(int *)optlen = length; \ + return 0; \ + } + buf = (char *)optval; switch (optname) { + case TCP_ALL: case TCP_MAXSEG: val = tp->mss_cache; if (!val && ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) val = tp->rx_opt.user_mss; - break; + BREAK(TCP_MAXSEG, val); case TCP_NODELAY: val = !!(tp->nonagle&TCP_NAGLE_OFF); - break; + BREAK(TCP_NODELAY, val); case TCP_CORK: val = !!(tp->nonagle&TCP_NAGLE_CORK); - break; + BREAK(TCP_CORK, val); case TCP_KEEPIDLE: val = (tp->keepalive_time ? : sysctl_tcp_keepalive_time) / HZ; - break; + BREAK(TCP_KEEPIDLE, val); case TCP_KEEPINTVL: val = (tp->keepalive_intvl ? : sysctl_tcp_keepalive_intvl) / HZ; - break; + BREAK(TCP_KEEPINTVL, val); case TCP_KEEPCNT: val = tp->keepalive_probes ? : sysctl_tcp_keepalive_probes; - break; + BREAK(TCP_KEEPCNT, val); case TCP_SYNCNT: val = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries; - break; + BREAK(TCP_SYNCNT, val); case TCP_LINGER2: val = tp->linger2; if (val >= 0) val = (val ? : sysctl_tcp_fin_timeout) / HZ; - break; + BREAK(TCP_LINGER2, val); case TCP_DEFER_ACCEPT: val = !icsk->icsk_accept_queue.rskq_defer_accept ? 0 : ((TCP_TIMEOUT_INIT / HZ) << (icsk->icsk_accept_queue.rskq_defer_accept - 1)); - break; + BREAK(TCP_DEFER_ACCEPT, val); case TCP_WINDOW_CLAMP: val = tp->window_clamp; - break; + BREAK(TCP_WINDOW_CLAMP, val); + case TCP_QUICKACK: + val = !icsk->icsk_ack.pingpong; + BREAK(TCP_QUICKACK, val); + STOP(); case TCP_INFO: { struct tcp_info info; @@ -2369,9 +2391,6 @@ static int do_tcp_getsockopt(struct sock *sk, int level, return -EFAULT; return 0; } - case TCP_QUICKACK: - val = !icsk->icsk_ack.pingpong; - break; case TCP_CONGESTION: if (get_user(len, optlen)) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 7a1d1ce..c1ed098 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1451,33 +1451,54 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, { struct udp_sock *up = udp_sk(sk); int val, len; + int length = 0; + char *buf; - if (get_user(len,optlen)) - return -EFAULT; + if (optname != UDP_ALL) { + if (get_user(len,optlen)) + return -EFAULT; - len = min_t(unsigned int, len, sizeof(int)); + len = min_t(unsigned int, len, sizeof(int)); - if (len < 0) - return -EINVAL; + if (len < 0) + return -EINVAL; + } else { + len = sizeof(int); + } +#define BREAK(OPT, val) \ + if (optname != UDP_ALL) \ + break; \ + printk(KERN_INFO "bytes used in buffer @udp: %d\n", length);\ + length += scnprintf(buf+length, *(int *)optlen - length, \ + "%d\t%d\t%ld\t%d\n", \ + level, OPT, (long)(val), (int)sizeof(val)) +#define STOP() if (optname == UDP_ALL) { \ + *(int *)optlen = length; \ + return 0; \ + } + + buf = (char *) optval; switch (optname) { + case UDP_ALL: case UDP_CORK: val = up->corkflag; - break; + BREAK(UDP_CORK, val); case UDP_ENCAP: val = up->encap_type; - break; + BREAK(UDP_ENCAP, val); /* The following two cannot be changed on UDP sockets, the return is * always 0 (which corresponds to the full checksum coverage of UDP). */ case UDPLITE_SEND_CSCOV: val = up->pcslen; - break; + BREAK(UDPLITE_SEND_CSCOV, val); case UDPLITE_RECV_CSCOV: val = up->pcrlen; - break; + BREAK(UDPLITE_RECV_CSCOV, val); + STOP(); default: return -ENOPROTOOPT; diff --git a/net/socket.c b/net/socket.c index 791d71a..9eb18af 100644 --- a/net/socket.c +++ b/net/socket.c @@ -415,7 +415,7 @@ int sock_map_fd(struct socket *sock, int flags) return fd; } -static struct socket *sock_from_file(struct file *file, int *err) +struct socket *sock_from_file(struct file *file, int *err) { if (file->f_op == &socket_file_ops) return file->private_data; /* set in sock_map_fd */ @@ -454,6 +454,23 @@ struct socket *sockfd_lookup(int fd, int *err) return sock; } +struct socket *sockfd_files_lookup(struct files_struct *files, int fd, int *err) +{ + struct file *file; + struct socket *sock; + + file = fget_files(files, fd); + if (!file) { + *err = -EBADF; + return NULL; + } + + sock = sock_from_file(file, err); + if (!sock) + fput(file); + return sock; +} + static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed) { struct file *file; @@ -1802,6 +1819,61 @@ out_put: return err; } +static int get_all_sockopt_level(struct socket *sock, int level, + char *buf, int *len) +{ + if (level == SOL_SOCKET) + return sock_getsockopt(sock, level, SO_ALL, buf, len); + else + return sock->ops->getsockopt(sock, level, -1, buf, len); +} +void sock_get_all_sockopt(struct socket *sock, char *buf, int *len) +{ + int i = 0; + int rest = *len; + int sol_array[] = { + SOL_SOCKET, + SOL_IP, + SOL_TCP, + SOL_UDP, + SOL_IPV6, + SOL_ICMPV6, + SOL_SCTP, + SOL_UDPLITE, + SOL_RAW, + SOL_IPX, + SOL_AX25, + SOL_ATALK, + SOL_NETROM, + SOL_ROSE, + SOL_DECNET, + SOL_X25, + SOL_PACKET, + SOL_ATM, + SOL_AAL, + SOL_IRDA, + SOL_NETBEUI, + SOL_LLC, + SOL_DCCP, + SOL_NETLINK, + SOL_TIPC, + SOL_RXRPC, + SOL_PPPOL2TP, + SOL_BLUETOOTH, + SOL_PNPIPE, + SOL_RDS, + }; + + for (i = 0; i < ARRAY_SIZE(sol_array); i++) { + if (get_all_sockopt_level(sock, sol_array[i], buf, len) < 0) + continue; + printk(KERN_INFO "i = %d rest = %d len = %d\n", i, rest, *len); + rest -= (*len); + buf += (*len); + (*len) = rest; + } +} + /* * 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.