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) 183 { 184 struct inet_hashinfo *hinfo; 185 186 hinfo = sock_net(cb->skb->sk)->ipv4.tcp_death_row.hashinfo; 187 188 inet_diag_dump_icsk(hinfo, skb, cb, r); 189 } 190 191 static int tcp_diag_dump_one(struct netlink_callback *cb, 192 const struct inet_diag_req_v2 *req) 193 { 194 struct inet_hashinfo *hinfo; 195 196 hinfo = sock_net(cb->skb->sk)->ipv4.tcp_death_row.hashinfo; 197 198 return inet_diag_dump_one_icsk(hinfo, cb, req); 199 } 200 201 #ifdef CONFIG_INET_DIAG_DESTROY 202 static int tcp_diag_destroy(struct sk_buff *in_skb, 203 const struct inet_diag_req_v2 *req) 204 { 205 struct net *net = sock_net(in_skb->sk); 206 struct inet_hashinfo *hinfo; 207 struct sock *sk; 208 int err; 209 210 hinfo = net->ipv4.tcp_death_row.hashinfo; 211 sk = inet_diag_find_one_icsk(net, hinfo, req); 212 213 if (IS_ERR(sk)) 214 return PTR_ERR(sk); 215 216 err = sock_diag_destroy(sk, ECONNABORTED); 217 218 sock_gen_put(sk); 219 220 return err; 221 } 222 #endif 223 224 static const struct inet_diag_handler tcp_diag_handler = { 225 .owner = THIS_MODULE, 226 .dump = tcp_diag_dump, 227 .dump_one = tcp_diag_dump_one, 228 .idiag_get_info = tcp_diag_get_info, 229 .idiag_get_aux = tcp_diag_get_aux, 230 .idiag_get_aux_size = tcp_diag_get_aux_size, 231 .idiag_type = IPPROTO_TCP, 232 .idiag_info_size = sizeof(struct tcp_info), 233 #ifdef CONFIG_INET_DIAG_DESTROY 234 .destroy = tcp_diag_destroy, 235 #endif 236 }; 237 238 static int __init tcp_diag_init(void) 239 { 240 return inet_diag_register(&tcp_diag_handler); 241 } 242 243 static void __exit tcp_diag_exit(void) 244 { 245 inet_diag_unregister(&tcp_diag_handler); 246 } 247 248 module_init(tcp_diag_init); 249 module_exit(tcp_diag_exit); 250 MODULE_LICENSE("GPL"); 251 MODULE_DESCRIPTION("TCP socket monitoring via SOCK_DIAG"); 252 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */); 253
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.