LCOV - code coverage report
Current view: top level - mptcp/mptcp_diag.c (source / functions) Coverage Total Hit
Test: export-net Lines: 72.9 % 133 97
Test Date: 2024-10-18 11:14:13 Functions: 71.4 % 7 5
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 64.3 % 70 45

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-2.0
       2                 :             : /* MPTCP socket monitoring support
       3                 :             :  *
       4                 :             :  * Copyright (c) 2020 Red Hat
       5                 :             :  *
       6                 :             :  * Author: Paolo Abeni <pabeni@redhat.com>
       7                 :             :  */
       8                 :             : 
       9                 :             : #include <linux/kernel.h>
      10                 :             : #include <linux/net.h>
      11                 :             : #include <linux/inet_diag.h>
      12                 :             : #include <net/netlink.h>
      13                 :             : #include "protocol.h"
      14                 :             : 
      15                 :        1318 : static int sk_diag_dump(struct sock *sk, struct sk_buff *skb,
      16                 :             :                         struct netlink_callback *cb,
      17                 :             :                         const struct inet_diag_req_v2 *req,
      18                 :             :                         struct nlattr *bc, bool net_admin)
      19                 :             : {
      20         [ +  + ]:        1318 :         if (!inet_diag_bc_sk(bc, sk))
      21                 :             :                 return 0;
      22                 :             : 
      23                 :        1304 :         return inet_sk_diag_fill(sk, inet_csk(sk), skb, cb, req, NLM_F_MULTI,
      24                 :             :                                  net_admin);
      25                 :             : }
      26                 :             : 
      27                 :           0 : static int mptcp_diag_dump_one(struct netlink_callback *cb,
      28                 :             :                                const struct inet_diag_req_v2 *req)
      29                 :             : {
      30                 :           0 :         struct sk_buff *in_skb = cb->skb;
      31                 :           0 :         struct mptcp_sock *msk = NULL;
      32                 :           0 :         struct sk_buff *rep;
      33                 :           0 :         int err = -ENOENT;
      34                 :           0 :         struct net *net;
      35                 :           0 :         struct sock *sk;
      36                 :             : 
      37                 :           0 :         net = sock_net(in_skb->sk);
      38                 :           0 :         msk = mptcp_token_get_sock(net, req->id.idiag_cookie[0]);
      39         [ #  # ]:           0 :         if (!msk)
      40                 :           0 :                 goto out_nosk;
      41                 :             : 
      42                 :           0 :         err = -ENOMEM;
      43                 :           0 :         sk = (struct sock *)msk;
      44                 :           0 :         rep = nlmsg_new(nla_total_size(sizeof(struct inet_diag_msg)) +
      45                 :             :                         inet_diag_msg_attrs_size() +
      46                 :             :                         nla_total_size(sizeof(struct mptcp_info)) +
      47                 :             :                         nla_total_size(sizeof(struct inet_diag_meminfo)) + 64,
      48                 :             :                         GFP_KERNEL);
      49         [ #  # ]:           0 :         if (!rep)
      50                 :           0 :                 goto out;
      51                 :             : 
      52                 :           0 :         err = inet_sk_diag_fill(sk, inet_csk(sk), rep, cb, req, 0,
      53                 :           0 :                                 netlink_net_capable(in_skb, CAP_NET_ADMIN));
      54         [ #  # ]:           0 :         if (err < 0) {
      55         [ #  # ]:           0 :                 WARN_ON(err == -EMSGSIZE);
      56                 :           0 :                 kfree_skb(rep);
      57                 :           0 :                 goto out;
      58                 :             :         }
      59                 :           0 :         err = nlmsg_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid);
      60                 :             : 
      61                 :           0 : out:
      62                 :           0 :         sock_put(sk);
      63                 :             : 
      64                 :           0 : out_nosk:
      65                 :           0 :         return err;
      66                 :             : }
      67                 :             : 
      68                 :             : struct mptcp_diag_ctx {
      69                 :             :         long s_slot;
      70                 :             :         long s_num;
      71                 :             :         unsigned int l_slot;
      72                 :             :         unsigned int l_num;
      73                 :             : };
      74                 :             : 
      75                 :          67 : static void mptcp_diag_dump_listeners(struct sk_buff *skb, struct netlink_callback *cb,
      76                 :             :                                       const struct inet_diag_req_v2 *r,
      77                 :             :                                       bool net_admin)
      78                 :             : {
      79                 :          67 :         struct inet_diag_dump_data *cb_data = cb->data;
      80                 :          67 :         struct mptcp_diag_ctx *diag_ctx = (void *)cb->ctx;
      81                 :          67 :         struct nlattr *bc = cb_data->inet_diag_nla_bc;
      82                 :          67 :         struct net *net = sock_net(skb->sk);
      83                 :          67 :         struct inet_hashinfo *hinfo;
      84                 :          67 :         int i;
      85                 :             : 
      86                 :          67 :         hinfo = net->ipv4.tcp_death_row.hashinfo;
      87                 :             : 
      88         [ +  + ]:       51267 :         for (i = diag_ctx->l_slot; i <= hinfo->lhash2_mask; i++) {
      89                 :       51206 :                 struct inet_listen_hashbucket *ilb;
      90                 :       51206 :                 struct hlist_nulls_node *node;
      91                 :       51206 :                 struct sock *sk;
      92                 :       51206 :                 int num = 0;
      93                 :             : 
      94                 :       51206 :                 ilb = &hinfo->lhash2[i];
      95                 :             : 
      96                 :       51206 :                 rcu_read_lock();
      97                 :       51206 :                 spin_lock(&ilb->lock);
      98         [ +  + ]:       51808 :                 sk_nulls_for_each(sk, node, &ilb->nulls_head) {
      99         [ -  + ]:         608 :                         const struct mptcp_subflow_context *ctx = mptcp_subflow_ctx(sk);
     100                 :         608 :                         struct inet_sock *inet = inet_sk(sk);
     101                 :         608 :                         int ret;
     102                 :             : 
     103         [ -  + ]:         608 :                         if (num < diag_ctx->l_num)
     104                 :           0 :                                 goto next_listen;
     105                 :             : 
     106   [ +  -  -  + ]:         608 :                         if (!ctx || strcmp(inet_csk(sk)->icsk_ulp_ops->name, "mptcp"))
     107                 :           0 :                                 goto next_listen;
     108                 :             : 
     109                 :         608 :                         sk = ctx->conn;
     110   [ +  -  +  + ]:         914 :                         if (!sk || !net_eq(sock_net(sk), net))
     111                 :           4 :                                 goto next_listen;
     112                 :             : 
     113         [ +  - ]:         604 :                         if (r->sdiag_family != AF_UNSPEC &&
     114         [ +  + ]:         604 :                             sk->sk_family != r->sdiag_family)
     115                 :         298 :                                 goto next_listen;
     116                 :             : 
     117   [ +  -  -  + ]:         306 :                         if (r->id.idiag_sport != inet->inet_sport &&
     118                 :             :                             r->id.idiag_sport)
     119                 :           0 :                                 goto next_listen;
     120                 :             : 
     121         [ -  + ]:         306 :                         if (!refcount_inc_not_zero(&sk->sk_refcnt))
     122                 :           0 :                                 goto next_listen;
     123                 :             : 
     124                 :         306 :                         ret = sk_diag_dump(sk, skb, cb, r, bc, net_admin);
     125                 :             : 
     126                 :         306 :                         sock_put(sk);
     127                 :             : 
     128         [ +  + ]:         306 :                         if (ret < 0) {
     129                 :           6 :                                 spin_unlock(&ilb->lock);
     130                 :           6 :                                 rcu_read_unlock();
     131                 :           6 :                                 diag_ctx->l_slot = i;
     132                 :           6 :                                 diag_ctx->l_num = num;
     133                 :           6 :                                 return;
     134                 :             :                         }
     135                 :         300 :                         diag_ctx->l_num = num + 1;
     136                 :         300 :                         num = 0;
     137                 :         602 : next_listen:
     138                 :         602 :                         ++num;
     139                 :             :                 }
     140                 :       51200 :                 spin_unlock(&ilb->lock);
     141                 :       51200 :                 rcu_read_unlock();
     142                 :             : 
     143                 :       51200 :                 cond_resched();
     144                 :       51200 :                 diag_ctx->l_num = 0;
     145                 :             :         }
     146                 :             : 
     147                 :          61 :         diag_ctx->l_num = 0;
     148                 :          61 :         diag_ctx->l_slot = i;
     149                 :             : }
     150                 :             : 
     151                 :         731 : static void mptcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
     152                 :             :                             const struct inet_diag_req_v2 *r)
     153                 :             : {
     154                 :         731 :         bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN);
     155                 :         731 :         struct mptcp_diag_ctx *diag_ctx = (void *)cb->ctx;
     156                 :         731 :         struct net *net = sock_net(skb->sk);
     157                 :         731 :         struct inet_diag_dump_data *cb_data;
     158                 :         731 :         struct mptcp_sock *msk;
     159                 :         731 :         struct nlattr *bc;
     160                 :             : 
     161                 :         731 :         BUILD_BUG_ON(sizeof(cb->ctx) < sizeof(*diag_ctx));
     162                 :             : 
     163                 :         731 :         cb_data = cb->data;
     164                 :         731 :         bc = cb_data->inet_diag_nla_bc;
     165                 :             : 
     166                 :         731 :         while ((msk = mptcp_token_iter_next(net, &diag_ctx->s_slot,
     167         [ +  + ]:        3543 :                                             &diag_ctx->s_num)) != NULL) {
     168                 :        2824 :                 struct inet_sock *inet = (struct inet_sock *)msk;
     169                 :        2824 :                 struct sock *sk = (struct sock *)msk;
     170                 :        2824 :                 int ret = 0;
     171                 :             : 
     172   [ +  +  +  + ]:        2824 :                 if (!(r->idiag_states & (1 << sk->sk_state)))
     173                 :         812 :                         goto next;
     174         [ +  - ]:        2012 :                 if (r->sdiag_family != AF_UNSPEC &&
     175         [ +  + ]:        2012 :                     sk->sk_family != r->sdiag_family)
     176                 :        1000 :                         goto next;
     177   [ +  -  -  + ]:        1012 :                 if (r->id.idiag_sport != inet->inet_sport &&
     178                 :             :                     r->id.idiag_sport)
     179                 :           0 :                         goto next;
     180   [ +  -  -  + ]:        1012 :                 if (r->id.idiag_dport != inet->inet_dport &&
     181                 :             :                     r->id.idiag_dport)
     182                 :           0 :                         goto next;
     183                 :             : 
     184                 :        1012 :                 ret = sk_diag_dump(sk, skb, cb, r, bc, net_admin);
     185                 :        2824 : next:
     186                 :        2824 :                 sock_put(sk);
     187         [ +  + ]:        2824 :                 if (ret < 0) {
     188                 :             :                         /* will retry on the same position */
     189                 :          12 :                         diag_ctx->s_num--;
     190                 :          12 :                         break;
     191                 :             :                 }
     192                 :        2812 :                 cond_resched();
     193                 :             :         }
     194                 :             : 
     195   [ +  +  +  - ]:         731 :         if ((r->idiag_states & TCPF_LISTEN) && r->id.idiag_dport == 0)
     196                 :          67 :                 mptcp_diag_dump_listeners(skb, cb, r, net_admin);
     197                 :         731 : }
     198                 :             : 
     199                 :        1286 : static void mptcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
     200                 :             :                                 void *_info)
     201                 :             : {
     202         [ -  + ]:        1286 :         struct mptcp_sock *msk = mptcp_sk(sk);
     203                 :        1286 :         struct mptcp_info *info = _info;
     204                 :             : 
     205                 :        1286 :         r->idiag_rqueue = sk_rmem_alloc_get(sk);
     206                 :        1286 :         r->idiag_wqueue = sk_wmem_alloc_get(sk);
     207                 :             : 
     208         [ +  + ]:        1286 :         if (inet_sk_state_load(sk) == TCP_LISTEN) {
     209                 :         298 :                 struct sock *lsk = READ_ONCE(msk->first);
     210                 :             : 
     211         [ +  - ]:         298 :                 if (lsk) {
     212                 :             :                         /* override with settings from tcp listener,
     213                 :             :                          * so Send-Q will show accept queue.
     214                 :             :                          */
     215                 :         298 :                         r->idiag_rqueue = READ_ONCE(lsk->sk_ack_backlog);
     216                 :         298 :                         r->idiag_wqueue = READ_ONCE(lsk->sk_max_ack_backlog);
     217                 :             :                 }
     218                 :             :         }
     219                 :             : 
     220         [ +  + ]:        1286 :         if (!info)
     221                 :             :                 return;
     222                 :             : 
     223                 :         988 :         mptcp_diag_fill_info(msk, info);
     224                 :             : }
     225                 :             : 
     226                 :             : static const struct inet_diag_handler mptcp_diag_handler = {
     227                 :             :         .owner           = THIS_MODULE,
     228                 :             :         .dump            = mptcp_diag_dump,
     229                 :             :         .dump_one        = mptcp_diag_dump_one,
     230                 :             :         .idiag_get_info  = mptcp_diag_get_info,
     231                 :             :         .idiag_type      = IPPROTO_MPTCP,
     232                 :             :         .idiag_info_size = sizeof(struct mptcp_info),
     233                 :             : };
     234                 :             : 
     235                 :           2 : static int __init mptcp_diag_init(void)
     236                 :             : {
     237                 :           2 :         return inet_diag_register(&mptcp_diag_handler);
     238                 :             : }
     239                 :             : 
     240                 :           0 : static void __exit mptcp_diag_exit(void)
     241                 :             : {
     242                 :           0 :         inet_diag_unregister(&mptcp_diag_handler);
     243                 :           0 : }
     244                 :             : 
     245                 :             : module_init(mptcp_diag_init);
     246                 :             : module_exit(mptcp_diag_exit);
     247                 :             : MODULE_LICENSE("GPL");
     248                 :             : MODULE_DESCRIPTION("MPTCP socket monitoring via SOCK_DIAG");
     249                 :             : MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-262 /* AF_INET - IPPROTO_MPTCP */);
        

Generated by: LCOV version 2.0-1