linux/net/ipv4/tcp_diag.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * tcp_diag.c   Module for monitoring TCP transport protocols sockets.
   4 *
   5 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/net.h>
  10#include <linux/sock_diag.h>
  11#include <linux/inet_diag.h>
  12
  13#include <linux/tcp.h>
  14
  15#include <net/netlink.h>
  16#include <net/tcp.h>
  17
  18static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
  19                              void *_info)
  20{
  21        struct tcp_info *info = _info;
  22
  23        if (inet_sk_state_load(sk) == TCP_LISTEN) {
  24                r->idiag_rqueue = READ_ONCE(sk->sk_ack_backlog);
  25                r->idiag_wqueue = READ_ONCE(sk->sk_max_ack_backlog);
  26        } else if (sk->sk_type == SOCK_STREAM) {
  27                const struct tcp_sock *tp = tcp_sk(sk);
  28
  29                r->idiag_rqueue = max_t(int, READ_ONCE(tp->rcv_nxt) -
  30                                             READ_ONCE(tp->copied_seq), 0);
  31                r->idiag_wqueue = READ_ONCE(tp->write_seq) - tp->snd_una;
  32        }
  33        if (info)
  34                tcp_get_info(sk, info);
  35}
  36
  37#ifdef CONFIG_TCP_MD5SIG
  38static void tcp_diag_md5sig_fill(struct tcp_diag_md5sig *info,
  39                                 const struct tcp_md5sig_key *key)
  40{
  41        info->tcpm_family = key->family;
  42        info->tcpm_prefixlen = key->prefixlen;
  43        info->tcpm_keylen = key->keylen;
  44        memcpy(info->tcpm_key, key->key, key->keylen);
  45
  46        if (key->family == AF_INET)
  47                info->tcpm_addr[0] = key->addr.a4.s_addr;
  48        #if IS_ENABLED(CONFIG_IPV6)
  49        else if (key->family == AF_INET6)
  50                memcpy(&info->tcpm_addr, &key->addr.a6,
  51                       sizeof(info->tcpm_addr));
  52        #endif
  53}
  54
  55static int tcp_diag_put_md5sig(struct sk_buff *skb,
  56                               const struct tcp_md5sig_info *md5sig)
  57{
  58        const struct tcp_md5sig_key *key;
  59        struct tcp_diag_md5sig *info;
  60        struct nlattr *attr;
  61        int md5sig_count = 0;
  62
  63        hlist_for_each_entry_rcu(key, &md5sig->head, node)
  64                md5sig_count++;
  65        if (md5sig_count == 0)
  66                return 0;
  67
  68        attr = nla_reserve(skb, INET_DIAG_MD5SIG,
  69                           md5sig_count * sizeof(struct tcp_diag_md5sig));
  70        if (!attr)
  71                return -EMSGSIZE;
  72
  73        info = nla_data(attr);
  74        memset(info, 0, md5sig_count * sizeof(struct tcp_diag_md5sig));
  75        hlist_for_each_entry_rcu(key, &md5sig->head, node) {
  76                tcp_diag_md5sig_fill(info++, key);
  77                if (--md5sig_count == 0)
  78                        break;
  79        }
  80
  81        return 0;
  82}
  83#endif
  84
  85static int tcp_diag_put_ulp(struct sk_buff *skb, struct sock *sk,
  86                            const struct tcp_ulp_ops *ulp_ops)
  87{
  88        struct nlattr *nest;
  89        int err;
  90
  91        nest = nla_nest_start_noflag(skb, INET_DIAG_ULP_INFO);
  92        if (!nest)
  93                return -EMSGSIZE;
  94
  95        err = nla_put_string(skb, INET_ULP_INFO_NAME, ulp_ops->name);
  96        if (err)
  97                goto nla_failure;
  98
  99        if (ulp_ops->get_info)
 100                err = ulp_ops->get_info(sk, skb);
 101        if (err)
 102                goto nla_failure;
 103
 104        nla_nest_end(skb, nest);
 105        return 0;
 106
 107nla_failure:
 108        nla_nest_cancel(skb, nest);
 109        return err;
 110}
 111
 112static int tcp_diag_get_aux(struct sock *sk, bool net_admin,
 113                            struct sk_buff *skb)
 114{
 115        struct inet_connection_sock *icsk = inet_csk(sk);
 116        int err = 0;
 117
 118#ifdef CONFIG_TCP_MD5SIG
 119        if (net_admin) {
 120                struct tcp_md5sig_info *md5sig;
 121
 122                rcu_read_lock();
 123                md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
 124                if (md5sig)
 125                        err = tcp_diag_put_md5sig(skb, md5sig);
 126                rcu_read_unlock();
 127                if (err < 0)
 128                        return err;
 129        }
 130#endif
 131
 132        if (net_admin) {
 133                const struct tcp_ulp_ops *ulp_ops;
 134
 135                ulp_ops = icsk->icsk_ulp_ops;
 136                if (ulp_ops)
 137                        err = tcp_diag_put_ulp(skb, sk, ulp_ops);
 138                if (err)
 139                        return err;
 140        }
 141        return 0;
 142}
 143
 144static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin)
 145{
 146        struct inet_connection_sock *icsk = inet_csk(sk);
 147        size_t size = 0;
 148
 149#ifdef CONFIG_TCP_MD5SIG
 150        if (net_admin && sk_fullsock(sk)) {
 151                const struct tcp_md5sig_info *md5sig;
 152                const struct tcp_md5sig_key *key;
 153                size_t md5sig_count = 0;
 154
 155                rcu_read_lock();
 156                md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
 157                if (md5sig) {
 158                        hlist_for_each_entry_rcu(key, &md5sig->head, node)
 159                                md5sig_count++;
 160                }
 161                rcu_read_unlock();
 162                size += nla_total_size(md5sig_count *
 163                                       sizeof(struct tcp_diag_md5sig));
 164        }
 165#endif
 166
 167        if (net_admin && sk_fullsock(sk)) {
 168                const struct tcp_ulp_ops *ulp_ops;
 169
 170                ulp_ops = icsk->icsk_ulp_ops;
 171                if (ulp_ops) {
 172                        size += nla_total_size(0) +
 173                                nla_total_size(TCP_ULP_NAME_MAX);
 174                        if (ulp_ops->get_info_size)
 175                                size += ulp_ops->get_info_size(sk);
 176                }
 177        }
 178        return size;
 179}
 180
 181static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
 182                          const struct inet_diag_req_v2 *r)
 183{
 184        inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r);
 185}
 186
 187static int tcp_diag_dump_one(struct netlink_callback *cb,
 188                             const struct inet_diag_req_v2 *req)
 189{
 190        return inet_diag_dump_one_icsk(&tcp_hashinfo, cb, req);
 191}
 192
 193#ifdef CONFIG_INET_DIAG_DESTROY
 194static int tcp_diag_destroy(struct sk_buff *in_skb,
 195                            const struct inet_diag_req_v2 *req)
 196{
 197        struct net *net = sock_net(in_skb->sk);
 198        struct sock *sk = inet_diag_find_one_icsk(net, &tcp_hashinfo, req);
 199        int err;
 200
 201        if (IS_ERR(sk))
 202                return PTR_ERR(sk);
 203
 204        err = sock_diag_destroy(sk, ECONNABORTED);
 205
 206        sock_gen_put(sk);
 207
 208        return err;
 209}
 210#endif
 211
 212static const struct inet_diag_handler tcp_diag_handler = {
 213        .dump                   = tcp_diag_dump,
 214        .dump_one               = tcp_diag_dump_one,
 215        .idiag_get_info         = tcp_diag_get_info,
 216        .idiag_get_aux          = tcp_diag_get_aux,
 217        .idiag_get_aux_size     = tcp_diag_get_aux_size,
 218        .idiag_type             = IPPROTO_TCP,
 219        .idiag_info_size        = sizeof(struct tcp_info),
 220#ifdef CONFIG_INET_DIAG_DESTROY
 221        .destroy                = tcp_diag_destroy,
 222#endif
 223};
 224
 225static int __init tcp_diag_init(void)
 226{
 227        return inet_diag_register(&tcp_diag_handler);
 228}
 229
 230static void __exit tcp_diag_exit(void)
 231{
 232        inet_diag_unregister(&tcp_diag_handler);
 233}
 234
 235module_init(tcp_diag_init);
 236module_exit(tcp_diag_exit);
 237MODULE_LICENSE("GPL");
 238MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */);
 239