linux/net/ipv4/tcp_diag.c
<<
>>
Prefs
   1/*
   2 * tcp_diag.c   Module for monitoring TCP transport protocols sockets.
   3 *
   4 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   5 *
   6 *      This program is free software; you can redistribute it and/or
   7 *      modify it under the terms of the GNU General Public License
   8 *      as published by the Free Software Foundation; either version
   9 *      2 of the License, or (at your option) any later version.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/net.h>
  14#include <linux/sock_diag.h>
  15#include <linux/inet_diag.h>
  16
  17#include <linux/tcp.h>
  18
  19#include <net/netlink.h>
  20#include <net/tcp.h>
  21
  22static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
  23                              void *_info)
  24{
  25        struct tcp_info *info = _info;
  26
  27        if (inet_sk_state_load(sk) == TCP_LISTEN) {
  28                r->idiag_rqueue = sk->sk_ack_backlog;
  29                r->idiag_wqueue = sk->sk_max_ack_backlog;
  30        } else if (sk->sk_type == SOCK_STREAM) {
  31                const struct tcp_sock *tp = tcp_sk(sk);
  32
  33                r->idiag_rqueue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
  34                r->idiag_wqueue = tp->write_seq - tp->snd_una;
  35        }
  36        if (info)
  37                tcp_get_info(sk, info);
  38}
  39
  40#ifdef CONFIG_TCP_MD5SIG
  41static void tcp_diag_md5sig_fill(struct tcp_diag_md5sig *info,
  42                                 const struct tcp_md5sig_key *key)
  43{
  44        info->tcpm_family = key->family;
  45        info->tcpm_prefixlen = key->prefixlen;
  46        info->tcpm_keylen = key->keylen;
  47        memcpy(info->tcpm_key, key->key, key->keylen);
  48
  49        if (key->family == AF_INET)
  50                info->tcpm_addr[0] = key->addr.a4.s_addr;
  51        #if IS_ENABLED(CONFIG_IPV6)
  52        else if (key->family == AF_INET6)
  53                memcpy(&info->tcpm_addr, &key->addr.a6,
  54                       sizeof(info->tcpm_addr));
  55        #endif
  56}
  57
  58static int tcp_diag_put_md5sig(struct sk_buff *skb,
  59                               const struct tcp_md5sig_info *md5sig)
  60{
  61        const struct tcp_md5sig_key *key;
  62        struct tcp_diag_md5sig *info;
  63        struct nlattr *attr;
  64        int md5sig_count = 0;
  65
  66        hlist_for_each_entry_rcu(key, &md5sig->head, node)
  67                md5sig_count++;
  68        if (md5sig_count == 0)
  69                return 0;
  70
  71        attr = nla_reserve(skb, INET_DIAG_MD5SIG,
  72                           md5sig_count * sizeof(struct tcp_diag_md5sig));
  73        if (!attr)
  74                return -EMSGSIZE;
  75
  76        info = nla_data(attr);
  77        memset(info, 0, md5sig_count * sizeof(struct tcp_diag_md5sig));
  78        hlist_for_each_entry_rcu(key, &md5sig->head, node) {
  79                tcp_diag_md5sig_fill(info++, key);
  80                if (--md5sig_count == 0)
  81                        break;
  82        }
  83
  84        return 0;
  85}
  86#endif
  87
  88static int tcp_diag_get_aux(struct sock *sk, bool net_admin,
  89                            struct sk_buff *skb)
  90{
  91#ifdef CONFIG_TCP_MD5SIG
  92        if (net_admin) {
  93                struct tcp_md5sig_info *md5sig;
  94                int err = 0;
  95
  96                rcu_read_lock();
  97                md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
  98                if (md5sig)
  99                        err = tcp_diag_put_md5sig(skb, md5sig);
 100                rcu_read_unlock();
 101                if (err < 0)
 102                        return err;
 103        }
 104#endif
 105
 106        return 0;
 107}
 108
 109static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin)
 110{
 111        size_t size = 0;
 112
 113#ifdef CONFIG_TCP_MD5SIG
 114        if (net_admin && sk_fullsock(sk)) {
 115                const struct tcp_md5sig_info *md5sig;
 116                const struct tcp_md5sig_key *key;
 117                size_t md5sig_count = 0;
 118
 119                rcu_read_lock();
 120                md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
 121                if (md5sig) {
 122                        hlist_for_each_entry_rcu(key, &md5sig->head, node)
 123                                md5sig_count++;
 124                }
 125                rcu_read_unlock();
 126                size += nla_total_size(md5sig_count *
 127                                       sizeof(struct tcp_diag_md5sig));
 128        }
 129#endif
 130
 131        return size;
 132}
 133
 134static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
 135                          const struct inet_diag_req_v2 *r, struct nlattr *bc)
 136{
 137        inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r, bc);
 138}
 139
 140static int tcp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh,
 141                             const struct inet_diag_req_v2 *req)
 142{
 143        return inet_diag_dump_one_icsk(&tcp_hashinfo, in_skb, nlh, req);
 144}
 145
 146#ifdef CONFIG_INET_DIAG_DESTROY
 147static int tcp_diag_destroy(struct sk_buff *in_skb,
 148                            const struct inet_diag_req_v2 *req)
 149{
 150        struct net *net = sock_net(in_skb->sk);
 151        struct sock *sk = inet_diag_find_one_icsk(net, &tcp_hashinfo, req);
 152        int err;
 153
 154        if (IS_ERR(sk))
 155                return PTR_ERR(sk);
 156
 157        err = sock_diag_destroy(sk, ECONNABORTED);
 158
 159        sock_gen_put(sk);
 160
 161        return err;
 162}
 163#endif
 164
 165static const struct inet_diag_handler tcp_diag_handler = {
 166        .dump                   = tcp_diag_dump,
 167        .dump_one               = tcp_diag_dump_one,
 168        .idiag_get_info         = tcp_diag_get_info,
 169        .idiag_get_aux          = tcp_diag_get_aux,
 170        .idiag_get_aux_size     = tcp_diag_get_aux_size,
 171        .idiag_type             = IPPROTO_TCP,
 172        .idiag_info_size        = sizeof(struct tcp_info),
 173#ifdef CONFIG_INET_DIAG_DESTROY
 174        .destroy                = tcp_diag_destroy,
 175#endif
 176};
 177
 178static int __init tcp_diag_init(void)
 179{
 180        return inet_diag_register(&tcp_diag_handler);
 181}
 182
 183static void __exit tcp_diag_exit(void)
 184{
 185        inet_diag_unregister(&tcp_diag_handler);
 186}
 187
 188module_init(tcp_diag_init);
 189module_exit(tcp_diag_exit);
 190MODULE_LICENSE("GPL");
 191MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */);
 192