mptcp: listen diag dump support
authorFlorian Westphal <fw@strlen.de>
Fri, 8 Apr 2022 19:46:00 +0000 (12:46 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Apr 2022 10:55:53 +0000 (11:55 +0100)
makes 'ss -Ml' show mptcp listen sockets.

Iterate over the tcp listen sockets and pick those that have mptcp ulp
info attached.

mptcp_diag_get_info() is modified to prefer msk->first for mptcp sockets
in listen state.  This reports accurate number for recv and send queue
(pending / max connection backlog counters).

Sample output:
ss -Mil
State        Recv-Q Send-Q Local Address:Port  Peer Address:Port
LISTEN       0      20     127.0.0.1:12000     0.0.0.0:*
         subflows_max:2

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/mptcp/mptcp_diag.c

index c4992ee..dbb6d87 100644 (file)
@@ -69,8 +69,83 @@ out_nosk:
 struct mptcp_diag_ctx {
        long s_slot;
        long s_num;
+       unsigned int l_slot;
+       unsigned int l_num;
 };
 
+static void mptcp_diag_dump_listeners(struct sk_buff *skb, struct netlink_callback *cb,
+                                     const struct inet_diag_req_v2 *r,
+                                     bool net_admin)
+{
+       struct inet_diag_dump_data *cb_data = cb->data;
+       struct mptcp_diag_ctx *diag_ctx = (void *)cb->ctx;
+       struct nlattr *bc = cb_data->inet_diag_nla_bc;
+       struct net *net = sock_net(skb->sk);
+       int i;
+
+       for (i = diag_ctx->l_slot; i < INET_LHTABLE_SIZE; i++) {
+               struct inet_listen_hashbucket *ilb;
+               struct hlist_nulls_node *node;
+               struct sock *sk;
+               int num = 0;
+
+               ilb = &tcp_hashinfo.listening_hash[i];
+
+               rcu_read_lock();
+               spin_lock(&ilb->lock);
+               sk_nulls_for_each(sk, node, &ilb->nulls_head) {
+                       const struct mptcp_subflow_context *ctx = mptcp_subflow_ctx(sk);
+                       struct inet_sock *inet = inet_sk(sk);
+                       int ret;
+
+                       if (num < diag_ctx->l_num)
+                               goto next_listen;
+
+                       if (!ctx || strcmp(inet_csk(sk)->icsk_ulp_ops->name, "mptcp"))
+                               goto next_listen;
+
+                       sk = ctx->conn;
+                       if (!sk || !net_eq(sock_net(sk), net))
+                               goto next_listen;
+
+                       if (r->sdiag_family != AF_UNSPEC &&
+                           sk->sk_family != r->sdiag_family)
+                               goto next_listen;
+
+                       if (r->id.idiag_sport != inet->inet_sport &&
+                           r->id.idiag_sport)
+                               goto next_listen;
+
+                       if (!refcount_inc_not_zero(&sk->sk_refcnt))
+                               goto next_listen;
+
+                       ret = sk_diag_dump(sk, skb, cb, r, bc, net_admin);
+
+                       sock_put(sk);
+
+                       if (ret < 0) {
+                               spin_unlock(&ilb->lock);
+                               rcu_read_unlock();
+                               diag_ctx->l_slot = i;
+                               diag_ctx->l_num = num;
+                               return;
+                       }
+                       diag_ctx->l_num = num + 1;
+                       num = 0;
+next_listen:
+                       ++num;
+               }
+               spin_unlock(&ilb->lock);
+               rcu_read_unlock();
+
+               cond_resched();
+               diag_ctx->l_num = 0;
+       }
+
+       diag_ctx->l_num = 0;
+       diag_ctx->l_slot = i;
+}
+
 static void mptcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
                            const struct inet_diag_req_v2 *r)
 {
@@ -114,6 +189,9 @@ next:
                }
                cond_resched();
        }
+
+       if ((r->idiag_states & TCPF_LISTEN) && r->id.idiag_dport == 0)
+               mptcp_diag_dump_listeners(skb, cb, r, net_admin);
 }
 
 static void mptcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
@@ -124,6 +202,19 @@ static void mptcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
 
        r->idiag_rqueue = sk_rmem_alloc_get(sk);
        r->idiag_wqueue = sk_wmem_alloc_get(sk);
+
+       if (inet_sk_state_load(sk) == TCP_LISTEN) {
+               struct sock *lsk = READ_ONCE(msk->first);
+
+               if (lsk) {
+                       /* override with settings from tcp listener,
+                        * so Send-Q will show accept queue.
+                        */
+                       r->idiag_rqueue = READ_ONCE(lsk->sk_ack_backlog);
+                       r->idiag_wqueue = READ_ONCE(lsk->sk_max_ack_backlog);
+               }
+       }
+
        if (!info)
                return;