Dumping AF_UNIX sockets via netlink
Subsystems (5)
cover Dumping AF_UNIX sockets via netlink
Make the unix_diag.ko module, which is the AF_UNIX client for the sock_diag.
Use the sock_i_ino() as the primary ID key for sockets. This is currently the only unique (except for the sk address itself) ID of a unix socket and is de facto used in the ss tool to identify sockets. Thus the basic nlk request and response structures operate on this ID. Other socket info (sun_name, peer, etc.) are reported in the respective NLA-s (patches 8 through 12).
There's a locking trickery in patch #11. I've tried to study it carefully and checked with lockdep, but anyway, please, pay special attention to it.
The patch for ss tool is also included.
PATCH 1/13 sock_diag: Move the SOCK_DIAG_BY_FAMILY cmd declaration
It should belong to sock_diag, not inet_diag.
--- include/linux/inet_diag.h | 1 - include/linux/sock_diag.h | 3 +++ 2 files changed, 3 insertions(+), 1 deletions(-)
include/linux/inet_diag.h +0 -1
@@ -6,7 +6,6 @@/* Just some random number */ #define TCPDIAG_GETSOCK 18#define DCCPDIAG_GETSOCK 19 -#define SOCK_DIAG_BY_FAMILY 20#define INET_DIAG_GETSOCK_MAX 24
include/linux/sock_diag.h +3 -0
@@ -1,5 +1,8 @@#ifndef __SOCK_DIAG_H__#define __SOCK_DIAG_H__ + +#define SOCK_DIAG_BY_FAMILY 20 +struct sk_buff ;struct nlmsghdr ;
Signature · 1 line
1.5.5.6
PATCH 2/13 sock_diag: Fix module netlink aliases
In reply to Pavel EmelyanovI've made a mistake when fixing the sock_/inet_diag aliases :(
1. The sock_diag layer should request the family-based alias, not just the IPPROTO_IP one;
2. The inet_diag layer should request for AF_INET+protocol alias, not just the protocol one.
Thus fix this.
--- net/core/sock_diag.c | 4 ++-- net/dccp/diag.c | 2 +- net/ipv4/inet_diag.c | 7 ++++--- net/ipv4/tcp_diag.c | 2 +- net/ipv4/udp_diag.c | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-)
net/core/sock_diag.c +2 -2
@@ -64,7 +64,7 @@ static inline struct sock_diag_handler *sock_diag_lock_handler(int family) {if (sock_diag_handlers[family] ==NULL )request_module ("net-pf-%d-proto-%d-type-%d" ,PF_NETLINK , -NETLINK_SOCK_DIAG ,IPPROTO_IP ); +NETLINK_SOCK_DIAG , family);mutex_lock (&sock_diag_table_mutex);return sock_diag_handlers[family];
@@ -103,7 +103,7 @@ static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)case DCCPDIAG_GETSOCK :if (inet_rcv_compat ==NULL )request_module ("net-pf-%d-proto-%d-type-%d" ,PF_NETLINK , -NETLINK_SOCK_DIAG ,IPPROTO_IP ); +NETLINK_SOCK_DIAG ,AF_INET );mutex_lock (&sock_diag_table_mutex);if (inet_rcv_compat !=NULL )
net/dccp/diag.c +1 -1
@@ -83,4 +83,4 @@ module_exit(dccp_diag_fini);MODULE_LICENSE ("GPL" );MODULE_AUTHOR ("Arnaldo Carvalho de Melo <[email protected]>" );MODULE_DESCRIPTION ("DCCP inet_diag handler" ); -MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,33 ); +MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,2 -33 /* AF_INET - IPPROTO_DCCP */ );
net/ipv4/inet_diag.c +4 -3
@@ -54,8 +54,8 @@ static DEFINE_MUTEX(inet_diag_table_mutex);static const struct inet_diag_handler *inet_diag_lock_handler (int proto) {if (!inet_diag_table[proto]) -request_module ("net-pf-%d-proto-%d-type-%d" ,PF_NETLINK , -NETLINK_SOCK_DIAG , proto); +request_module ("net-pf-%d-proto-%d-type-%d-%d" ,PF_NETLINK , +NETLINK_SOCK_DIAG ,AF_INET , proto);mutex_lock (&inet_diag_table_mutex);if (!inet_diag_table[proto])
@@ -1087,4 +1087,5 @@ static void __exit inet_diag_exit(void)module_init (inet_diag_init);module_exit (inet_diag_exit);MODULE_LICENSE ("GPL" ); -MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,0 ); +MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,2 /* AF_INET */ ); +MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,10 /* AF_INET6 */ );
net/ipv4/tcp_diag.c +1 -1
@@ -66,4 +66,4 @@ static void __exit tcp_diag_exit(void)module_init (tcp_diag_init);module_exit (tcp_diag_exit);MODULE_LICENSE ("GPL" ); -MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,6 ); +MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,2 -6 /* AF_INET - IPPROTO_TCP */ );
net/ipv4/udp_diag.c +2 -2
@@ -197,5 +197,5 @@ static void __exit udp_diag_exit(void)module_init (udp_diag_init);module_exit (udp_diag_exit);MODULE_LICENSE ("GPL" ); -MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,17 ); -MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,136 ); +MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,2 -17 /* AF_INET - IPPROTO_UDP */ ); +MODULE_ALIAS_NET_PF_PROTO_TYPE (PF_NETLINK ,NETLINK_SOCK_DIAG ,2 -136 /* AF_INET - IPPROTO_UDPLITE */ );
Signature · 1 line
1.5.5.6
PATCH 3/13 sock_diag: Generalize requests cookies managements
In reply to Pavel EmelyanovThe sk address is used as a cookie between dump/get_exact calls. It will be required for unix socket sdumping, so move it from inet_diag to sock_diag.
--- include/linux/inet_diag.h | 1 - include/linux/sock_diag.h | 3 +++ net/core/sock_diag.c | 19 +++++++++++++++++++ net/ipv4/inet_diag.c | 23 ++++------------------- net/ipv4/udp_diag.c | 2 +- 5 files changed, 27 insertions(+), 21 deletions(-)
include/linux/inet_diag.h +0 -1
@@ -168,7 +168,6 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct inet_diag_req *req);int inet_diag_bc_sk (const struct nlattr *_bc,struct sock *sk); -int inet_diag_check_cookie (struct sock *sk,struct inet_diag_req *req);extern int inet_diag_register (const struct inet_diag_handler *handler);extern void inet_diag_unregister (const struct inet_diag_handler *handler);
include/linux/sock_diag.h +3 -0
@@ -22,5 +22,8 @@ void sock_diag_unregister(struct sock_diag_handler *h);void sock_diag_register_inet_compat (int (*fn)(struct sk_buff *skb,struct nlmsghdr *nlh));void sock_diag_unregister_inet_compat (int (*fn)(struct sk_buff *skb,struct nlmsghdr *nlh)); +int sock_diag_check_cookie (void *sk,__u32 *cookie); +void sock_diag_save_cookie (void *sk,__u32 *cookie); +extern struct sock *sock_diag_nlsk;#endif
net/core/sock_diag.c +19 -0
@@ -12,6 +12,25 @@ static struct sock_diag_handler *sock_diag_handlers[AF_MAX];static int (*inet_rcv_compat)(struct sk_buff *skb,struct nlmsghdr *nlh);static DEFINE_MUTEX (sock_diag_table_mutex); +int sock_diag_check_cookie (void *sk,__u32 *cookie) +{ +if ((cookie[0 ] !=INET_DIAG_NOCOOKIE || + cookie[1 ] !=INET_DIAG_NOCOOKIE ) && + ((u32 )(unsigned long )sk != cookie[0 ] || + (u32)((((unsigned long )sk) >>31 ) >>1 ) != cookie[1 ])) +return -ESTALE ; +else +return 0 ; +} +EXPORT_SYMBOL_GPL (sock_diag_check_cookie); + +void sock_diag_save_cookie (void *sk,__u32 *cookie) +{ + cookie[0 ] = (u32 )(unsigned long )sk; + cookie[1 ] = (u32)(((unsigned long )sk >>31 ) >>1 ); +} +EXPORT_SYMBOL_GPL (sock_diag_save_cookie); +void sock_diag_register_inet_compat (int (*fn)(struct sk_buff *skb,struct nlmsghdr *nlh)) {mutex_lock (&sock_diag_table_mutex);
net/ipv4/inet_diag.c +4 -19
@@ -102,8 +102,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, r->idiag_retrans =0 ; r->id.idiag_if = sk->sk_bound_dev_if; - r->id.idiag_cookie[0 ] = (u32 )(unsigned long )sk; - r->id.idiag_cookie[1 ] = (u32)(((unsigned long )sk >>31 ) >>1 ); +sock_diag_save_cookie (sk, r->id.idiag_cookie); r->id.idiag_sport = inet->inet_sport; r->id.idiag_dport = inet->inet_dport;
@@ -221,8 +220,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, r->idiag_family = tw->tw_family; r->idiag_retrans =0 ; r->id.idiag_if = tw->tw_bound_dev_if; - r->id.idiag_cookie[0 ] = (u32 )(unsigned long )tw; - r->id.idiag_cookie[1 ] = (u32)(((unsigned long )tw >>31 ) >>1 ); +sock_diag_save_cookie (tw, r->id.idiag_cookie); r->id.idiag_sport = tw->tw_sport; r->id.idiag_dport = tw->tw_dport; r->id.idiag_src[0 ] = tw->tw_rcv_saddr;
@@ -261,18 +259,6 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,return inet_csk_diag_fill (sk, skb, r, pid, seq, nlmsg_flags, unlh); } -int inet_diag_check_cookie (struct sock *sk,struct inet_diag_req *req) -{ -if ((req->id.idiag_cookie[0 ] !=INET_DIAG_NOCOOKIE || - req->id.idiag_cookie[1 ] !=INET_DIAG_NOCOOKIE ) && - ((u32 )(unsigned long )sk != req->id.idiag_cookie[0 ] || - (u32)((((unsigned long )sk) >>31 ) >>1 ) != req->id.idiag_cookie[1 ])) -return -ESTALE ; -else -return 0 ; -} -EXPORT_SYMBOL_GPL (inet_diag_check_cookie); -int inet_diag_dump_one_icsk (struct inet_hashinfo *hashinfo,struct sk_buff *in_skb,const struct nlmsghdr *nlh,struct inet_diag_req *req) {
@@ -304,7 +290,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_sif (sk ==NULL ) goto out_nosk; - err =inet_diag_check_cookie (sk, req); + err =sock_diag_check_cookie (sk, req->id.idiag_cookie);if (err) goto out;
@@ -617,8 +603,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, r->idiag_retrans = req->retrans; r->id.idiag_if = sk->sk_bound_dev_if; - r->id.idiag_cookie[0 ] = (u32 )(unsigned long )req; - r->id.idiag_cookie[1 ] = (u32)(((unsigned long )req >>31 ) >>1 ); +sock_diag_save_cookie (req, r->id.idiag_cookie); tmo = req->expires - jiffies;if (tmo <0 )
net/ipv4/udp_diag.c +1 -1
@@ -57,7 +57,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,if (sk ==NULL ) goto out_nosk; - err =inet_diag_check_cookie (sk, req); + err =sock_diag_check_cookie (sk, req->id.idiag_cookie);if (err) goto out;
Signature · 1 line
1.5.5.6
PATCH 4/13 af_unix: Export stuff required for diag module
In reply to Pavel Emelyanov--- include/net/af_unix.h | 3 +++ net/unix/af_unix.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-)
include/net/af_unix.h +3 -0
@@ -11,10 +11,13 @@ extern void unix_notinflight(struct file *fp);extern void unix_gc (void );extern void wait_for_unix_gc (void );extern struct sock *unix_get_socket (struct file *filp); +extern struct sock *unix_peer_get (struct sock *);#define UNIX_HASH_SIZE 256extern unsigned int unix_tot_inflight; +extern spinlock_t unix_table_lock; +extern struct hlist_head unix_socket_table[UNIX_HASH_SIZE +1 ];struct unix_address {atomic_t refcnt;
net/unix/af_unix.c +6 -3
@@ -115,8 +115,10 @@#include <net/checksum.h> #include <linux/security.h> -static struct hlist_head unix_socket_table[UNIX_HASH_SIZE +1 ]; -static DEFINE_SPINLOCK (unix_table_lock); +struct hlist_head unix_socket_table[UNIX_HASH_SIZE +1 ]; +EXPORT_SYMBOL_GPL (unix_socket_table); +DEFINE_SPINLOCK (unix_table_lock); +EXPORT_SYMBOL_GPL (unix_table_lock);static atomic_long_t unix_nr_socks;#define unix_sockets_unbound (&unix_socket_table[UNIX_HASH_SIZE])
@@ -172,7 +174,7 @@ static inline int unix_recvq_full(struct sock const *sk)return skb_queue_len (&sk->sk_receive_queue) > sk->sk_max_ack_backlog; } -static struct sock *unix_peer_get (struct sock *s) +struct sock *unix_peer_get (struct sock *s) {struct sock *peer;
@@ -183,6 +185,7 @@ static struct sock *unix_peer_get(struct sock *s)unix_state_unlock (s);return peer; } +EXPORT_SYMBOL_GPL (unix_peer_get);static inline void unix_release_addr (struct unix_address *addr) {
Signature · 1 line
1.5.5.6
PATCH 5/13 unix_diag: Basic module skeleton
In reply to Pavel EmelyanovIncludes basic module_init/_exit functionality, dump/get_exact stubs and declares the basic API structures for request and response.
--- include/linux/unix_diag.h | 24 +++++++++++++++++++ net/unix/diag.c | 57 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 0 deletions(-) create mode 100644 include/linux/unix_diag.h create mode 100644 net/unix/diag.c
include/linux/unix_diag.h new file +24 -0
@@ -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
net/unix/diag.c new file +57 -0
57 lines not on this page · raw message
Signature · 1 line
1.5.5.6
PATCH 6/13 unix_diag: Dumping all sockets core
In reply to Pavel EmelyanovWalk the unix sockets table and fill the core response structure, which includes type, state and inode.
--- net/unix/diag.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 75 insertions(+), 1 deletions(-)
net/unix/diag.c +75 -1
@@ -10,9 +10,83 @@#define UNIX_DIAG_PUT (skb, attrtype, attrlen) \ RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) +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) +{ +unsigned char *b =skb_tail_pointer (skb); +struct nlmsghdr *nlh; +struct unix_diag_msg *rep; + + nlh =NLMSG_PUT (skb, pid, seq,SOCK_DIAG_BY_FAMILY ,sizeof (*rep)); + nlh->nlmsg_flags = flags; + + rep =NLMSG_DATA (nlh); + + rep->udiag_family =AF_UNIX ; + rep->udiag_type = sk->sk_type; + rep->udiag_state = sk->sk_state; + rep->udiag_ino = sk_ino; +sock_diag_save_cookie (sk, rep->udiag_cookie); + + nlh->nlmsg_len =skb_tail_pointer (skb) - b; +return skb->len; + +nlmsg_failure: +nlmsg_trim (skb, b); +return -EMSGSIZE ; +} + +static int sk_diag_dump (struct sock *sk,struct sk_buff *skb,struct unix_diag_req *req, +u32 pid,u32 seq,u32 flags) +{ +int sk_ino; + +unix_state_lock (sk); + sk_ino =sock_i_ino (sk); +unix_state_unlock (sk); + +if (!sk_ino) +return 0 ; + +return sk_diag_fill (sk, skb, req, pid, seq, flags, sk_ino); +} +static int unix_diag_dump (struct sk_buff *skb,struct netlink_callback *cb) { -return 0 ; +struct unix_diag_req *req; +int num, s_num, slot, s_slot; + + req =NLMSG_DATA (cb->nlh); + + s_slot = cb->args[0 ]; + num = s_num = cb->args[1 ]; + +spin_lock (&unix_table_lock); +for (slot = s_slot; slot <=UNIX_HASH_SIZE ; s_num =0 , slot++) { +struct sock *sk; +struct hlist_node *node; + + num =0 ; +sk_for_each (sk, node, &unix_socket_table[slot]) { +if (num < s_num) + goto next; +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 ) + goto done; +next: + num++; + } + } +done: +spin_unlock (&unix_table_lock); + cb->args[0 ] = slot; + cb->args[1 ] = num; + +return skb->len; } static int unix_diag_get_exact(struct sk_buff *in_skb,
Signature · 1 line
1.5.5.6
PATCH 7/13 unix_diag: Dumping exact socket core
In reply to Pavel EmelyanovThe socket inode is used as a key for lookup. This is effectively the only really unique ID of a unix socket, but using this for search currently has one problem -- it is O(number of sockets) :(
Does it worth fixing this lookup or inventing some other ID for unix sockets?
--- net/unix/diag.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 66 insertions(+), 1 deletions(-)
net/unix/diag.c +66 -1
@@ -89,11 +89,76 @@ done:return skb->len; } +static struct sock *unix_lookup_by_ino (int ino) +{ +int i; +struct sock *sk; + +spin_lock (&unix_table_lock); +for (i =0 ; i <=UNIX_HASH_SIZE ; i++) { +struct hlist_node *node; + +sk_for_each (sk, node, &unix_socket_table[i]) +if (ino ==sock_i_ino (sk)) { +sock_hold (sk); +spin_unlock (&unix_table_lock); + +return sk; + } + } + +spin_unlock (&unix_table_lock); +return NULL ; +} +static int unix_diag_get_exact (struct sk_buff *in_skb,const struct nlmsghdr *nlh,struct unix_diag_req *req) { -return -EAFNOSUPPORT ; +int err = -EINVAL ; +struct sock *sk; +struct sk_buff *rep; +unsigned int extra_len; + +if (req->udiag_ino ==0 ) + goto out_nosk; + + sk =unix_lookup_by_ino (req->udiag_ino); + err = -ENOENT ; +if (sk ==NULL ) + goto out_nosk; + + err =sock_diag_check_cookie (sk, req->udiag_cookie); +if (err) + goto out; + + extra_len =256 ; +again: + err = -ENOMEM ; + rep =alloc_skb (NLMSG_SPACE ((sizeof (struct unix_diag_msg ) + extra_len)), +GFP_KERNEL ); +if (!rep) + goto out; + + err =sk_diag_fill (sk, rep, req,NETLINK_CB (in_skb).pid, + nlh->nlmsg_seq,0 , req->udiag_ino); +if (err <0 ) { +kfree_skb (rep); + extra_len +=256 ; +if (extra_len >=PAGE_SIZE ) + goto out; + + goto again; + } + err =netlink_unicast (sock_diag_nlsk, rep,NETLINK_CB (in_skb).pid, +MSG_DONTWAIT ); +if (err >0 ) + err =0 ; +out: +if (sk) +sock_put (sk); +out_nosk: +return err; }static int unix_diag_handler_dump (struct sk_buff *skb,struct nlmsghdr *h)
Signature · 1 line
1.5.5.6
PATCH 8/13 unix_diag: Unix socket name NLA
In reply to Pavel EmelyanovReport the sun_path when requested as NLA. With leading '\0' if present but without the leading AF_UNIX bits.
--- include/linux/unix_diag.h | 8 ++++++++ net/unix/diag.c | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 0 deletions(-)
include/linux/unix_diag.h +8 -0
@@ -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
net/unix/diag.c +20 -0
@@ -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;
Signature · 1 line
1.5.5.6
PATCH 9/13 unix_diag: Unix inode info NLA
In reply to Pavel EmelyanovActually, 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.
--- include/linux/unix_diag.h | 7 +++++++ net/unix/diag.c | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 0 deletions(-)
include/linux/unix_diag.h +7 -0
@@ -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
net/unix/diag.c +21 -0
@@ -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;
Signature · 1 line
1.5.5.6
PATCH 10/13 unix_diag: Unix peer inode NLA
In reply to Pavel EmelyanovReport the peer socket inode ID as NLA. With this it's finally possible to find out the other end of an interesting unix connection.
--- include/linux/unix_diag.h | 2 ++ net/unix/diag.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 0 deletions(-)
include/linux/unix_diag.h +2 -0
@@ -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 , };
net/unix/diag.c +24 -0
@@ -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;
Signature · 1 line
1.5.5.6
PATCH 11/13 unix_diag: Pending connections IDs NLA
In reply to Pavel EmelyanovWhen 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.
--- include/linux/unix_diag.h | 2 ++ net/unix/diag.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 0 deletions(-)
include/linux/unix_diag.h +2 -0
@@ -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, };
net/unix/diag.c +39 -0
@@ -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;
Signature · 1 line
1.5.5.6
PATCH 12/13 unix_diag: Receive queue lenght NLA
In reply to Pavel Emelyanov--- include/linux/unix_diag.h | 2 ++ net/unix/diag.c | 13 +++++++++++++ 2 files changed, 15 insertions(+), 0 deletions(-)
include/linux/unix_diag.h +2 -0
@@ -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, };
net/unix/diag.c +13 -0
@@ -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;
Signature · 1 line
1.5.5.6
PATCH 13/13 unix_diag: Write it into kbuild
In reply to Pavel Emelyanov--- net/unix/Kconfig | 7 +++++++ net/unix/Makefile | 3 +++ 2 files changed, 10 insertions(+), 0 deletions(-)
net/unix/Kconfig +7 -0
@@ -19,3 +19,10 @@ config UNIX Say Y unless you know what you are doing. +config UNIX_DIAG + tristate "UNIX: socket monitoring interface" + depends on UNIX + default UNIX + ---help--- + Support for UNIX socket monitoring interface used by the ss tool. + If unsure, say Y.
net/unix/Makefile +3 -0
@@ -6,3 +6,6 @@ obj-$(CONFIG_UNIX) += unix.o unix-y := af_unix.o garbage.o unix-$(CONFIG_SYSCTL) += sysctl_net_unix.o + +obj-$(CONFIG_UNIX_DIAG) += unix_diag.o +unix_diag-y := diag.o
Signature · 1 line
1.5.5.6
PATCH 1/1 iproute: Dump unix sockets via netlink
In reply to Pavel EmelyanovGet the same info as from /proc file plus the peer inode.
Applies on top of new sock diag patch and udp diag patch.
---
include/linux/unix_diag.h new file +45 -0
@@ -0,0 +1,45 @@ +#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 ]; +}; + +#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 */ +#define UDIAG_SHOW_RQLEN 0x00000010/* show skb receive queue len */ + +struct unix_diag_msg { +__u8 udiag_family; +__u8 udiag_type; +__u8 udiag_state; +__u8 pad; + +__u32 udiag_ino; +__u32 udiag_cookie[2 ]; +}; + +enum { +UNIX_DIAG_NAME , +UNIX_DIAG_VFS , +UNIX_DIAG_PEER , +UNIX_DIAG_ICONS , +UNIX_DIAG_RQLEN , + +UNIX_DIAG_MAX , +}; + +struct unix_diag_vfs { +__u32 udiag_vfs_ino; +__u32 udiag_vfs_dev; +}; + +#endif
misc/ss.c +178 -0
196 lines not on this page · raw message
Stephen Hemminger
AppliedRe: [PATCH] iproute: Dump unix sockets via netlink
Quoted 7 lines · On Thu, 15 Dec 2011 17:28:15 +0400 Pavel Emelyanov <[email protected]> wrote:
Get the same info as from /proc file plus the peer inode.
Applies on top of new sock diag patch and udp diag patch.
Signed-off-by Pavel Emelyanov <[email protected]>---
Applied, but was missing sock_diag.h in include's.
Also, please update manual pages.
David Miller
maintainerFrom: Pavel Emelyanov <[email protected]>
Date: Thu, 15 Dec 2011 16:42:18 +0400
Quoted 8 lines
Make the unix_diag.ko module, which is the
AF_UNIXclient for thesock_diag.Use the
sock_i_ino()as the primary ID key for sockets. This is currently the only unique (except for the sk address itself) ID of a unix socket and is de facto used in the ss tool to identify sockets. Thus the basic nlk request and response structures operate on this ID. Other socket info (sun_name, peer, etc.) are reported in the respective NLA-s (patches 8 through 12).
Quoted 6 lines
There's a locking trickery in patch #11. I've tried to study it carefully and checked with lockdep, but anyway, please, pay special attention to it.
The patch for ss tool is also included.
Signed-off-by Pavel Emelyanov <[email protected]>
Looks good, applied.
I'm slightly confused by the module alias strings these diag modules are using, can you explain it to me?
On one side it looks like it wants to see a suffix of "-${AF_INET}-${IPPROTO_TCP}" but in the macros you pass in one numerical value, which is AF_INET minus the protocol value.
How does that work?
Thanks.
Quoted 18 lines · David Miller
From: Pavel Emelyanov <[email protected]>
Date: Thu, 15 Dec 2011 16:42:18 +0400Quoted 8 lines
Make the unix_diag.ko module, which is the
AF_UNIXclient for thesock_diag.Use the
sock_i_ino()as the primary ID key for sockets. This is currently the only unique (except for the sk address itself) ID of a unix socket and is de facto used in the ss tool to identify sockets. Thus the basic nlk request and response structures operate on this ID. Other socket info (sun_name, peer, etc.) are reported in the respective NLA-s (patches 8 through 12).Quoted 6 lines
There's a locking trickery in patch #11. I've tried to study it carefully and checked with lockdep, but anyway, please, pay special attention to it.
The patch for ss tool is also included.
Signed-off-by Pavel Emelyanov <[email protected]>
Quoted 8 lines
Looks good, applied.
I'm slightly confused by the module alias strings these diag modules are using, can you explain it to me?
On one side it looks like it wants to see a suffix of "-${AF_INET}-${IPPROTO_TCP}" but in the macros you pass in one numerical value, which is
AF_INETminus the protocol value.
You're right, the intention is to get the "family-protocol" pair, and the minus you see (surrounded with numbers) is put into the stringify macro, which works like
stringify(2-16) => "2-16"
since (as far as I understand this) C pre-processor doesn't perform arithmetic calculations.
Quoted 5 lines
How does that work?
Thanks.
.
Thanks,
Pavel