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 18 static 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 38 static 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 55 static 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 85 static 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 107 nla_failure: 108 nla_nest_cancel(skb, nest); 109 return err; 110 } 111 112 static 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 144 static 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 181 static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 182 const struct inet_diag_req_v2 *r, struct nlattr *bc) 183 { 184 inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r, bc); 185 } 186 187 static int tcp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, 188 const struct inet_diag_req_v2 *req) 189 { 190 return inet_diag_dump_one_icsk(&tcp_hashinfo, in_skb, nlh, req); 191 } 192 193 #ifdef CONFIG_INET_DIAG_DESTROY 194 static 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 212 static 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 225 static int __init tcp_diag_init(void) 226 { 227 return inet_diag_register(&tcp_diag_handler); 228 } 229 230 static void __exit tcp_diag_exit(void) 231 { 232 inet_diag_unregister(&tcp_diag_handler); 233 } 234 235 module_init(tcp_diag_init); 236 module_exit(tcp_diag_exit); 237 MODULE_LICENSE("GPL"); 238 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */); 239