Blame view

net/ipv4/tcp_diag.c 5.45 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
2
3
4
  /*
   * tcp_diag.c	Module for monitoring TCP transport protocols sockets.
   *
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
5
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
6
   */
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
7
  #include <linux/module.h>
c1e64e298   Lorenzo Colitti   net: diag: Suppor...
8
9
  #include <linux/net.h>
  #include <linux/sock_diag.h>
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
10
11
12
  #include <linux/inet_diag.h>
  
  #include <linux/tcp.h>
c03fa9bca   Ivan Delalande   tcp_diag: report ...
13
  #include <net/netlink.h>
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
14
15
16
17
18
  #include <net/tcp.h>
  
  static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
  			      void *_info)
  {
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
19
  	struct tcp_info *info = _info;
986ffdfd0   Yafang Shao   net: sock: replac...
20
  	if (inet_sk_state_load(sk) == TCP_LISTEN) {
288efe860   Eric Dumazet   net: annotate loc...
21
  		r->idiag_rqueue = READ_ONCE(sk->sk_ack_backlog);
099ecf59f   Eric Dumazet   net: annotate loc...
22
  		r->idiag_wqueue = READ_ONCE(sk->sk_max_ack_backlog);
35ac838a9   Craig Gallek   sock_diag: implem...
23
24
  	} else if (sk->sk_type == SOCK_STREAM) {
  		const struct tcp_sock *tp = tcp_sk(sk);
7db48e983   Eric Dumazet   tcp: annotate tp-...
25
26
  		r->idiag_rqueue = max_t(int, READ_ONCE(tp->rcv_nxt) -
  					     READ_ONCE(tp->copied_seq), 0);
0f3174645   Eric Dumazet   tcp: annotate tp-...
27
  		r->idiag_wqueue = READ_ONCE(tp->write_seq) - tp->snd_una;
5ee3afba8   Rick Jones   [TCP]: Return use...
28
  	}
00db41243   Ian Morris   ipv4: coding styl...
29
  	if (info)
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
30
31
  		tcp_get_info(sk, info);
  }
c03fa9bca   Ivan Delalande   tcp_diag: report ...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  #ifdef CONFIG_TCP_MD5SIG
  static void tcp_diag_md5sig_fill(struct tcp_diag_md5sig *info,
  				 const struct tcp_md5sig_key *key)
  {
  	info->tcpm_family = key->family;
  	info->tcpm_prefixlen = key->prefixlen;
  	info->tcpm_keylen = key->keylen;
  	memcpy(info->tcpm_key, key->key, key->keylen);
  
  	if (key->family == AF_INET)
  		info->tcpm_addr[0] = key->addr.a4.s_addr;
  	#if IS_ENABLED(CONFIG_IPV6)
  	else if (key->family == AF_INET6)
  		memcpy(&info->tcpm_addr, &key->addr.a6,
  		       sizeof(info->tcpm_addr));
  	#endif
  }
  
  static int tcp_diag_put_md5sig(struct sk_buff *skb,
  			       const struct tcp_md5sig_info *md5sig)
  {
  	const struct tcp_md5sig_key *key;
  	struct tcp_diag_md5sig *info;
  	struct nlattr *attr;
  	int md5sig_count = 0;
  
  	hlist_for_each_entry_rcu(key, &md5sig->head, node)
  		md5sig_count++;
  	if (md5sig_count == 0)
  		return 0;
  
  	attr = nla_reserve(skb, INET_DIAG_MD5SIG,
  			   md5sig_count * sizeof(struct tcp_diag_md5sig));
  	if (!attr)
  		return -EMSGSIZE;
  
  	info = nla_data(attr);
  	memset(info, 0, md5sig_count * sizeof(struct tcp_diag_md5sig));
  	hlist_for_each_entry_rcu(key, &md5sig->head, node) {
  		tcp_diag_md5sig_fill(info++, key);
  		if (--md5sig_count == 0)
  			break;
  	}
  
  	return 0;
  }
  #endif
61723b393   Davide Caratti   tcp: ulp: add fun...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  static int tcp_diag_put_ulp(struct sk_buff *skb, struct sock *sk,
  			    const struct tcp_ulp_ops *ulp_ops)
  {
  	struct nlattr *nest;
  	int err;
  
  	nest = nla_nest_start_noflag(skb, INET_DIAG_ULP_INFO);
  	if (!nest)
  		return -EMSGSIZE;
  
  	err = nla_put_string(skb, INET_ULP_INFO_NAME, ulp_ops->name);
  	if (err)
  		goto nla_failure;
  
  	if (ulp_ops->get_info)
  		err = ulp_ops->get_info(sk, skb);
  	if (err)
  		goto nla_failure;
  
  	nla_nest_end(skb, nest);
  	return 0;
  
  nla_failure:
  	nla_nest_cancel(skb, nest);
  	return err;
  }
c03fa9bca   Ivan Delalande   tcp_diag: report ...
105
106
107
  static int tcp_diag_get_aux(struct sock *sk, bool net_admin,
  			    struct sk_buff *skb)
  {
61723b393   Davide Caratti   tcp: ulp: add fun...
108
109
  	struct inet_connection_sock *icsk = inet_csk(sk);
  	int err = 0;
c03fa9bca   Ivan Delalande   tcp_diag: report ...
110
111
112
  #ifdef CONFIG_TCP_MD5SIG
  	if (net_admin) {
  		struct tcp_md5sig_info *md5sig;
c03fa9bca   Ivan Delalande   tcp_diag: report ...
113
114
115
116
117
118
119
120
121
122
  
  		rcu_read_lock();
  		md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
  		if (md5sig)
  			err = tcp_diag_put_md5sig(skb, md5sig);
  		rcu_read_unlock();
  		if (err < 0)
  			return err;
  	}
  #endif
61723b393   Davide Caratti   tcp: ulp: add fun...
123
124
125
126
127
128
129
130
131
  	if (net_admin) {
  		const struct tcp_ulp_ops *ulp_ops;
  
  		ulp_ops = icsk->icsk_ulp_ops;
  		if (ulp_ops)
  			err = tcp_diag_put_ulp(skb, sk, ulp_ops);
  		if (err)
  			return err;
  	}
c03fa9bca   Ivan Delalande   tcp_diag: report ...
132
133
134
135
136
  	return 0;
  }
  
  static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin)
  {
61723b393   Davide Caratti   tcp: ulp: add fun...
137
  	struct inet_connection_sock *icsk = inet_csk(sk);
c03fa9bca   Ivan Delalande   tcp_diag: report ...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  	size_t size = 0;
  
  #ifdef CONFIG_TCP_MD5SIG
  	if (net_admin && sk_fullsock(sk)) {
  		const struct tcp_md5sig_info *md5sig;
  		const struct tcp_md5sig_key *key;
  		size_t md5sig_count = 0;
  
  		rcu_read_lock();
  		md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
  		if (md5sig) {
  			hlist_for_each_entry_rcu(key, &md5sig->head, node)
  				md5sig_count++;
  		}
  		rcu_read_unlock();
  		size += nla_total_size(md5sig_count *
  				       sizeof(struct tcp_diag_md5sig));
  	}
  #endif
b58662a5f   Eric Dumazet   tcp: ulp: fix pos...
157
  	if (net_admin && sk_fullsock(sk)) {
61723b393   Davide Caratti   tcp: ulp: add fun...
158
159
160
161
162
163
164
165
166
167
  		const struct tcp_ulp_ops *ulp_ops;
  
  		ulp_ops = icsk->icsk_ulp_ops;
  		if (ulp_ops) {
  			size += nla_total_size(0) +
  				nla_total_size(TCP_ULP_NAME_MAX);
  			if (ulp_ops->get_info_size)
  				size += ulp_ops->get_info_size(sk);
  		}
  	}
c03fa9bca   Ivan Delalande   tcp_diag: report ...
168
169
  	return size;
  }
1942c518c   Pavel Emelyanov   inet_diag: Genera...
170
  static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
171
  			  const struct inet_diag_req_v2 *r)
1942c518c   Pavel Emelyanov   inet_diag: Genera...
172
  {
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
173
  	inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r);
1942c518c   Pavel Emelyanov   inet_diag: Genera...
174
  }
5682d393b   Martin KaFai Lau   inet_diag: Refact...
175
  static int tcp_diag_dump_one(struct netlink_callback *cb,
34160ea3f   Eric Dumazet   inet_diag: add co...
176
  			     const struct inet_diag_req_v2 *req)
1942c518c   Pavel Emelyanov   inet_diag: Genera...
177
  {
5682d393b   Martin KaFai Lau   inet_diag: Refact...
178
  	return inet_diag_dump_one_icsk(&tcp_hashinfo, cb, req);
1942c518c   Pavel Emelyanov   inet_diag: Genera...
179
  }
c1e64e298   Lorenzo Colitti   net: diag: Suppor...
180
181
182
183
184
185
  #ifdef CONFIG_INET_DIAG_DESTROY
  static int tcp_diag_destroy(struct sk_buff *in_skb,
  			    const struct inet_diag_req_v2 *req)
  {
  	struct net *net = sock_net(in_skb->sk);
  	struct sock *sk = inet_diag_find_one_icsk(net, &tcp_hashinfo, req);
d7226c7a4   David Ahern   net: diag: Fix re...
186
  	int err;
c1e64e298   Lorenzo Colitti   net: diag: Suppor...
187
188
189
  
  	if (IS_ERR(sk))
  		return PTR_ERR(sk);
d7226c7a4   David Ahern   net: diag: Fix re...
190
191
192
193
194
  	err = sock_diag_destroy(sk, ECONNABORTED);
  
  	sock_gen_put(sk);
  
  	return err;
c1e64e298   Lorenzo Colitti   net: diag: Suppor...
195
196
  }
  #endif
a7a0d6a87   Eric Dumazet   net: inet_diag_ha...
197
  static const struct inet_diag_handler tcp_diag_handler = {
c03fa9bca   Ivan Delalande   tcp_diag: report ...
198
199
200
201
202
203
204
  	.dump			= tcp_diag_dump,
  	.dump_one		= tcp_diag_dump_one,
  	.idiag_get_info		= tcp_diag_get_info,
  	.idiag_get_aux		= tcp_diag_get_aux,
  	.idiag_get_aux_size	= tcp_diag_get_aux_size,
  	.idiag_type		= IPPROTO_TCP,
  	.idiag_info_size	= sizeof(struct tcp_info),
c1e64e298   Lorenzo Colitti   net: diag: Suppor...
205
  #ifdef CONFIG_INET_DIAG_DESTROY
c03fa9bca   Ivan Delalande   tcp_diag: report ...
206
  	.destroy		= tcp_diag_destroy,
c1e64e298   Lorenzo Colitti   net: diag: Suppor...
207
  #endif
17b085eac   Arnaldo Carvalho de Melo   [INET_DIAG]: Move...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  };
  
  static int __init tcp_diag_init(void)
  {
  	return inet_diag_register(&tcp_diag_handler);
  }
  
  static void __exit tcp_diag_exit(void)
  {
  	inet_diag_unregister(&tcp_diag_handler);
  }
  
  module_init(tcp_diag_init);
  module_exit(tcp_diag_exit);
  MODULE_LICENSE("GPL");
aec8dc62f   Pavel Emelyanov   sock_diag: Fix mo...
223
  MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */);