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, bool net_admin) 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, net_admin); 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 const struct tcp_ulp_ops *ulp_ops; 117 int err = 0; 118 119 #ifdef CONFIG_TCP_MD5SIG 120 if (net_admin) { 121 struct tcp_md5sig_info *md5sig; 122 123 rcu_read_lock(); 124 md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info); 125 if (md5sig) 126 err = tcp_diag_put_md5sig(skb, md5sig); 127 rcu_read_unlock(); 128 if (err < 0) 129 return err; 130 } 131 #endif 132 133 ulp_ops = icsk->icsk_ulp_ops; 134 if (ulp_ops) { 135 err = tcp_diag_put_ulp(skb, sk, ulp_ops, net_admin); 136 if (err < 0) 137 return err; 138 } 139 140 return 0; 141 } 142 143 static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin) 144 { 145 struct inet_connection_sock *icsk = inet_csk(sk); 146 size_t size = 0; 147 148 #ifdef CONFIG_TCP_MD5SIG 149 if (net_admin && sk_fullsock(sk)) { 150 const struct tcp_md5sig_info *md5sig; 151 const struct tcp_md5sig_key *key; 152 size_t md5sig_count = 0; 153 154 rcu_read_lock(); 155 md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info); 156 if (md5sig) { 157 hlist_for_each_entry_rcu(key, &md5sig->head, node) 158 md5sig_count++; 159 } 160 rcu_read_unlock(); 161 size += nla_total_size(md5sig_count * 162 sizeof(struct tcp_diag_md5sig)); 163 } 164 #endif 165 166 if (sk_fullsock(sk)) { 167 const struct tcp_ulp_ops *ulp_ops; 168 169 ulp_ops = icsk->icsk_ulp_ops; 170 if (ulp_ops) { 171 size += nla_total_size(0) + 172 nla_total_size(TCP_ULP_NAME_MAX); 173 if (ulp_ops->get_info_size) 174 size += ulp_ops->get_info_size(sk, net_admin); 175 } 176 } 177 return size; 178 } 179 180 static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 181 const struct inet_diag_req_v2 *r) 182 { 183 struct inet_hashinfo *hinfo; 184 185 hinfo = sock_net(cb->skb->sk)->ipv4.tcp_death_row.hashinfo; 186 187 inet_diag_dump_icsk(hinfo, skb, cb, r); 188 } 189 190 static int tcp_diag_dump_one(struct netlink_callback *cb, 191 const struct inet_diag_req_v2 *req) 192 { 193 struct inet_hashinfo *hinfo; 194 195 hinfo = sock_net(cb->skb->sk)->ipv4.tcp_death_row.hashinfo; 196 197 return inet_diag_dump_one_icsk(hinfo, cb, req); 198 } 199 200 #ifdef CONFIG_INET_DIAG_DESTROY 201 static int tcp_diag_destroy(struct sk_buff *in_skb, 202 const struct inet_diag_req_v2 *req) 203 { 204 struct net *net = sock_net(in_skb->sk); 205 struct inet_hashinfo *hinfo; 206 struct sock *sk; 207 int err; 208 209 hinfo = net->ipv4.tcp_death_row.hashinfo; 210 sk = inet_diag_find_one_icsk(net, hinfo, req); 211 212 if (IS_ERR(sk)) 213 return PTR_ERR(sk); 214 215 err = sock_diag_destroy(sk, ECONNABORTED); 216 217 sock_gen_put(sk); 218 219 return err; 220 } 221 #endif 222 223 static const struct inet_diag_handler tcp_diag_handler = { 224 .owner = THIS_MODULE, 225 .dump = tcp_diag_dump, 226 .dump_one = tcp_diag_dump_one, 227 .idiag_get_info = tcp_diag_get_info, 228 .idiag_get_aux = tcp_diag_get_aux, 229 .idiag_get_aux_size = tcp_diag_get_aux_size, 230 .idiag_type = IPPROTO_TCP, 231 .idiag_info_size = sizeof(struct tcp_info), 232 #ifdef CONFIG_INET_DIAG_DESTROY 233 .destroy = tcp_diag_destroy, 234 #endif 235 }; 236 237 static int __init tcp_diag_init(void) 238 { 239 return inet_diag_register(&tcp_diag_handler); 240 } 241 242 static void __exit tcp_diag_exit(void) 243 { 244 inet_diag_unregister(&tcp_diag_handler); 245 } 246 247 module_init(tcp_diag_init); 248 module_exit(tcp_diag_exit); 249 MODULE_LICENSE("GPL"); 250 MODULE_DESCRIPTION("TCP socket monitoring via SOCK_DIAG"); 251 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */); 252