From 22931d3b906cd0a1726a49a09713f9220a5fab8a Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 15 Dec 2011 02:44:35 +0000 Subject: unix_diag: Basic module skeleton Includes basic module_init/_exit functionality, dump/get_exact stubs and declares the basic API structures for request and response. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/unix_diag.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 include/linux/unix_diag.h (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h new file mode 100644 index 000000000000..445184a85763 --- /dev/null +++ b/include/linux/unix_diag.h @@ -0,0 +1,24 @@ +#ifndef __UNIX_DIAG_H__ +#define __UNIX_DIAG_H__ + +struct unix_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u16 pad; + __u32 udiag_states; + __u32 udiag_ino; + __u32 udiag_show; + __u32 udiag_cookie[2]; +}; + +struct unix_diag_msg { + __u8 udiag_family; + __u8 udiag_type; + __u8 udiag_state; + __u8 pad; + + __u32 udiag_ino; + __u32 udiag_cookie[2]; +}; + +#endif -- cgit From f5248b48a64c221dd6157ab9cbee5a36ee45e6ed Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 15 Dec 2011 02:45:24 +0000 Subject: unix_diag: Unix socket name NLA Report the sun_path when requested as NLA. With leading '\0' if present but without the leading AF_UNIX bits. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/unix_diag.h | 8 ++++++++ net/unix/diag.c | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h index 445184a85763..cc4df34d4c14 100644 --- a/include/linux/unix_diag.h +++ b/include/linux/unix_diag.h @@ -11,6 +11,8 @@ struct unix_diag_req { __u32 udiag_cookie[2]; }; +#define UDIAG_SHOW_NAME 0x00000001 /* show name (not path) */ + struct unix_diag_msg { __u8 udiag_family; __u8 udiag_type; @@ -21,4 +23,10 @@ struct unix_diag_msg { __u32 udiag_cookie[2]; }; +enum { + UNIX_DIAG_NAME, + + UNIX_DIAG_MAX, +}; + #endif diff --git a/net/unix/diag.c b/net/unix/diag.c index d7bd48c49ee5..161ce6c05e31 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -10,6 +10,22 @@ #define UNIX_DIAG_PUT(skb, attrtype, attrlen) \ RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) +static int sk_diag_dump_name(struct sock *sk, struct sk_buff *nlskb) +{ + struct unix_address *addr = unix_sk(sk)->addr; + char *s; + + if (addr) { + s = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_NAME, addr->len - sizeof(short)); + memcpy(s, addr->name->sun_path, addr->len - sizeof(short)); + } + + return 0; + +rtattr_failure: + return -EMSGSIZE; +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { @@ -28,6 +44,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r rep->udiag_ino = sk_ino; sock_diag_save_cookie(sk, rep->udiag_cookie); + if ((req->udiag_show & UDIAG_SHOW_NAME) && + sk_diag_dump_name(sk, skb)) + goto nlmsg_failure; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; -- cgit From 5f7b0569460b7d8d01ca776430a00505a68b7584 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 15 Dec 2011 02:45:43 +0000 Subject: unix_diag: Unix inode info NLA Actually, the socket path if it's not anonymous doesn't give a clue to which file the socket is bound to. Even if the path is absolute, it can be unlinked and then new socket can be bound to it. With this NLA it's possible to check which file a particular socket is really bound to. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/unix_diag.h | 7 +++++++ net/unix/diag.c | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h index cc4df34d4c14..3e53adbe9c7f 100644 --- a/include/linux/unix_diag.h +++ b/include/linux/unix_diag.h @@ -12,6 +12,7 @@ struct unix_diag_req { }; #define UDIAG_SHOW_NAME 0x00000001 /* show name (not path) */ +#define UDIAG_SHOW_VFS 0x00000002 /* show VFS inode info */ struct unix_diag_msg { __u8 udiag_family; @@ -25,8 +26,14 @@ struct unix_diag_msg { enum { UNIX_DIAG_NAME, + UNIX_DIAG_VFS, UNIX_DIAG_MAX, }; +struct unix_diag_vfs { + __u32 udiag_vfs_ino; + __u32 udiag_vfs_dev; +}; + #endif diff --git a/net/unix/diag.c b/net/unix/diag.c index 161ce6c05e31..83799ef19b49 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -26,6 +26,23 @@ rtattr_failure: return -EMSGSIZE; } +static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb) +{ + struct dentry *dentry = unix_sk(sk)->dentry; + struct unix_diag_vfs *uv; + + if (dentry) { + uv = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_VFS, sizeof(*uv)); + uv->udiag_vfs_ino = dentry->d_inode->i_ino; + uv->udiag_vfs_dev = dentry->d_sb->s_dev; + } + + return 0; + +rtattr_failure: + return -EMSGSIZE; +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { @@ -48,6 +65,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r sk_diag_dump_name(sk, skb)) goto nlmsg_failure; + if ((req->udiag_show & UDIAG_SHOW_VFS) && + sk_diag_dump_vfs(sk, skb)) + goto nlmsg_failure; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; -- cgit From ac02be8d96af9f66a4de86781ee9facc2dff99d4 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 15 Dec 2011 02:45:58 +0000 Subject: unix_diag: Unix peer inode NLA Report the peer socket inode ID as NLA. With this it's finally possible to find out the other end of an interesting unix connection. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/unix_diag.h | 2 ++ net/unix/diag.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h index 3e53adbe9c7f..2d74a86024ac 100644 --- a/include/linux/unix_diag.h +++ b/include/linux/unix_diag.h @@ -13,6 +13,7 @@ struct unix_diag_req { #define UDIAG_SHOW_NAME 0x00000001 /* show name (not path) */ #define UDIAG_SHOW_VFS 0x00000002 /* show VFS inode info */ +#define UDIAG_SHOW_PEER 0x00000004 /* show peer socket info */ struct unix_diag_msg { __u8 udiag_family; @@ -27,6 +28,7 @@ struct unix_diag_msg { enum { UNIX_DIAG_NAME, UNIX_DIAG_VFS, + UNIX_DIAG_PEER, UNIX_DIAG_MAX, }; diff --git a/net/unix/diag.c b/net/unix/diag.c index 83799ef19b49..0e0fda786afe 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -43,6 +43,26 @@ rtattr_failure: return -EMSGSIZE; } +static int sk_diag_dump_peer(struct sock *sk, struct sk_buff *nlskb) +{ + struct sock *peer; + int ino; + + peer = unix_peer_get(sk); + if (peer) { + unix_state_lock(peer); + ino = sock_i_ino(peer); + unix_state_unlock(peer); + sock_put(peer); + + RTA_PUT_U32(nlskb, UNIX_DIAG_PEER, ino); + } + + return 0; +rtattr_failure: + return -EMSGSIZE; +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { @@ -69,6 +89,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r sk_diag_dump_vfs(sk, skb)) goto nlmsg_failure; + if ((req->udiag_show & UDIAG_SHOW_PEER) && + sk_diag_dump_peer(sk, skb)) + goto nlmsg_failure; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; -- cgit From 2aac7a2cb0d9d8c65fc7dde3e19e46b3e878d23d Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 15 Dec 2011 02:46:14 +0000 Subject: unix_diag: Pending connections IDs NLA When establishing a unix connection on stream sockets the server end receives an skb with socket in its receive queue. Report who is waiting for these ends to be accepted for listening sockets via NLA. There's a lokcing issue with this -- the unix sk state lock is required to access the peer, and it is taken under the listening sk's queue lock. Strictly speaking the queue lock should be taken inside the state lock, but since in this case these two sockets are different it shouldn't lead to deadlock. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/unix_diag.h | 2 ++ net/unix/diag.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h index 2d74a86024ac..03ffb7de15b6 100644 --- a/include/linux/unix_diag.h +++ b/include/linux/unix_diag.h @@ -14,6 +14,7 @@ struct unix_diag_req { #define UDIAG_SHOW_NAME 0x00000001 /* show name (not path) */ #define UDIAG_SHOW_VFS 0x00000002 /* show VFS inode info */ #define UDIAG_SHOW_PEER 0x00000004 /* show peer socket info */ +#define UDIAG_SHOW_ICONS 0x00000008 /* show pending connections */ struct unix_diag_msg { __u8 udiag_family; @@ -29,6 +30,7 @@ enum { UNIX_DIAG_NAME, UNIX_DIAG_VFS, UNIX_DIAG_PEER, + UNIX_DIAG_ICONS, UNIX_DIAG_MAX, }; diff --git a/net/unix/diag.c b/net/unix/diag.c index 0e0fda786afe..24c7a65d9cb1 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -63,6 +63,41 @@ rtattr_failure: return -EMSGSIZE; } +static int sk_diag_dump_icons(struct sock *sk, struct sk_buff *nlskb) +{ + struct sk_buff *skb; + u32 *buf; + int i; + + if (sk->sk_state == TCP_LISTEN) { + spin_lock(&sk->sk_receive_queue.lock); + buf = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_ICONS, sk->sk_receive_queue.qlen); + i = 0; + skb_queue_walk(&sk->sk_receive_queue, skb) { + struct sock *req, *peer; + + req = skb->sk; + /* + * The state lock is outer for the same sk's + * queue lock. With the other's queue locked it's + * OK to lock the state. + */ + unix_state_lock_nested(req); + peer = unix_sk(req)->peer; + if (peer) + buf[i++] = sock_i_ino(peer); + unix_state_unlock(req); + } + spin_unlock(&sk->sk_receive_queue.lock); + } + + return 0; + +rtattr_failure: + spin_unlock(&sk->sk_receive_queue.lock); + return -EMSGSIZE; +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { @@ -93,6 +128,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r sk_diag_dump_peer(sk, skb)) goto nlmsg_failure; + if ((req->udiag_show & UDIAG_SHOW_ICONS) && + sk_diag_dump_icons(sk, skb)) + goto nlmsg_failure; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; -- cgit From cbf391958afb9b82c72324a15891eb3102200085 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 15 Dec 2011 02:46:31 +0000 Subject: unix_diag: Receive queue lenght NLA Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/unix_diag.h | 2 ++ net/unix/diag.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h index 03ffb7de15b6..3f7afb007d70 100644 --- a/include/linux/unix_diag.h +++ b/include/linux/unix_diag.h @@ -15,6 +15,7 @@ struct unix_diag_req { #define UDIAG_SHOW_VFS 0x00000002 /* show VFS inode info */ #define UDIAG_SHOW_PEER 0x00000004 /* show peer socket info */ #define UDIAG_SHOW_ICONS 0x00000008 /* show pending connections */ +#define UDIAG_SHOW_RQLEN 0x00000010 /* show skb receive queue len */ struct unix_diag_msg { __u8 udiag_family; @@ -31,6 +32,7 @@ enum { UNIX_DIAG_VFS, UNIX_DIAG_PEER, UNIX_DIAG_ICONS, + UNIX_DIAG_RQLEN, UNIX_DIAG_MAX, }; diff --git a/net/unix/diag.c b/net/unix/diag.c index 24c7a65d9cb1..a5c4aab0380d 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -98,6 +98,15 @@ rtattr_failure: return -EMSGSIZE; } +static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb) +{ + RTA_PUT_U32(nlskb, UNIX_DIAG_RQLEN, sk->sk_receive_queue.qlen); + return 0; + +rtattr_failure: + return -EMSGSIZE; +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req, u32 pid, u32 seq, u32 flags, int sk_ino) { @@ -132,6 +141,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r sk_diag_dump_icons(sk, skb)) goto nlmsg_failure; + if ((req->udiag_show & UDIAG_SHOW_RQLEN) && + sk_diag_show_rqlen(sk, skb)) + goto nlmsg_failure; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; -- cgit From 288461e1546fa4162fa237eeed8ea09a16521dcd Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 30 Dec 2011 00:52:51 +0000 Subject: unix_diag: Include unix_diag.h into header-y target The headers check complains it should include the linux/types.h withing, thus add this one. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + include/linux/unix_diag.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 8e484d660bc3..c94e71781b79 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -197,6 +197,7 @@ header-y += in6.h header-y += in_route.h header-y += sock_diag.h header-y += inet_diag.h +header-y += unix_diag.h header-y += inotify.h header-y += input.h header-y += ioctl.h diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h index 3f7afb007d70..a5ce0f325745 100644 --- a/include/linux/unix_diag.h +++ b/include/linux/unix_diag.h @@ -1,6 +1,8 @@ #ifndef __UNIX_DIAG_H__ #define __UNIX_DIAG_H__ +#include + struct unix_diag_req { __u8 sdiag_family; __u8 sdiag_protocol; -- cgit From 257b529876cb45ec791eaa89e3d2ee0d16b49383 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 30 Dec 2011 09:27:43 +0000 Subject: unix_diag: Add the MEMINFO extension [ Fix indentation of sock_diag*() calls. -DaveM ] Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/unix_diag.h | 2 ++ net/unix/diag.c | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h index a5ce0f325745..93fdb782468a 100644 --- a/include/linux/unix_diag.h +++ b/include/linux/unix_diag.h @@ -18,6 +18,7 @@ struct unix_diag_req { #define UDIAG_SHOW_PEER 0x00000004 /* show peer socket info */ #define UDIAG_SHOW_ICONS 0x00000008 /* show pending connections */ #define UDIAG_SHOW_RQLEN 0x00000010 /* show skb receive queue len */ +#define UDIAG_SHOW_MEMINFO 0x00000020 /* show memory info of a socket */ struct unix_diag_msg { __u8 udiag_family; @@ -35,6 +36,7 @@ enum { UNIX_DIAG_PEER, UNIX_DIAG_ICONS, UNIX_DIAG_RQLEN, + UNIX_DIAG_MEMINFO, UNIX_DIAG_MAX, }; diff --git a/net/unix/diag.c b/net/unix/diag.c index c5bdbcb1c30b..98945f29da4f 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -127,23 +127,27 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r sock_diag_save_cookie(sk, rep->udiag_cookie); if ((req->udiag_show & UDIAG_SHOW_NAME) && - sk_diag_dump_name(sk, skb)) + sk_diag_dump_name(sk, skb)) goto nlmsg_failure; if ((req->udiag_show & UDIAG_SHOW_VFS) && - sk_diag_dump_vfs(sk, skb)) + sk_diag_dump_vfs(sk, skb)) goto nlmsg_failure; if ((req->udiag_show & UDIAG_SHOW_PEER) && - sk_diag_dump_peer(sk, skb)) + sk_diag_dump_peer(sk, skb)) goto nlmsg_failure; if ((req->udiag_show & UDIAG_SHOW_ICONS) && - sk_diag_dump_icons(sk, skb)) + sk_diag_dump_icons(sk, skb)) goto nlmsg_failure; if ((req->udiag_show & UDIAG_SHOW_RQLEN) && - sk_diag_show_rqlen(sk, skb)) + sk_diag_show_rqlen(sk, skb)) + goto nlmsg_failure; + + if ((req->udiag_show & UDIAG_SHOW_MEMINFO) && + sock_diag_put_meminfo(sk, skb, UNIX_DIAG_MEMINFO)) goto nlmsg_failure; nlh->nlmsg_len = skb_tail_pointer(skb) - b; @@ -191,9 +195,9 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) if (!(req->udiag_states & (1 << sk->sk_state))) goto next; if (sk_diag_dump(sk, skb, req, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, - NLM_F_MULTI) < 0) + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + NLM_F_MULTI) < 0) goto done; next: num++; -- cgit From c9da99e6475f92653139e43f3c30c0cd011a0fd8 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 30 Dec 2011 00:54:39 +0000 Subject: unix_diag: Fixup RQLEN extension report While it's not too late fix the recently added RQLEN diag extension to report rqlen and wqlen in the same way as TCP does. I.e. for listening sockets the ack backlog length (which is the input queue length for socket) in rqlen and the max ack backlog length in wqlen, and what the CINQ/OUTQ ioctls do for established. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/unix_diag.h | 5 +++++ net/unix/diag.c | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include/linux/unix_diag.h') diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h index 93fdb782468a..b1d2bf16b33c 100644 --- a/include/linux/unix_diag.h +++ b/include/linux/unix_diag.h @@ -46,4 +46,9 @@ struct unix_diag_vfs { __u32 udiag_vfs_dev; }; +struct unix_diag_rqlen { + __u32 udiag_rqueue; + __u32 udiag_wqueue; +}; + #endif diff --git a/net/unix/diag.c b/net/unix/diag.c index 98945f29da4f..6b7697fd911b 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -101,7 +101,18 @@ rtattr_failure: static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb) { - RTA_PUT_U32(nlskb, UNIX_DIAG_RQLEN, sk->sk_receive_queue.qlen); + struct unix_diag_rqlen *rql; + + rql = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_RQLEN, sizeof(*rql)); + + if (sk->sk_state == TCP_LISTEN) { + rql->udiag_rqueue = sk->sk_receive_queue.qlen; + rql->udiag_wqueue = sk->sk_max_ack_backlog; + } else { + rql->udiag_rqueue = (__u32)unix_inq_len(sk); + rql->udiag_wqueue = (__u32)unix_outq_len(sk); + } + return 0; rtattr_failure: -- cgit