]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
unix_diag: Pending connections IDs NLA
authorPavel Emelyanov <xemul@parallels.com>
Thu, 15 Dec 2011 02:46:14 +0000 (02:46 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 16 Dec 2011 18:48:28 +0000 (13:48 -0500)
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 <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/unix_diag.h
net/unix/diag.c

index 2d74a86024ace1503c96a2d7572baa87aa5affff..03ffb7de15b6188b29ef4b8a471aed810cd8fd6b 100644 (file)
@@ -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,
 };
index 0e0fda786afeea2607051a644d90dd9613d930e0..24c7a65d9cb186528af23d66639110fa0f642a4f 100644 (file)
@@ -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;