~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ipv4/tcp_diag.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php