Blame view

net/ipv4/inet_diag.c 35.3 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
3
   * inet_diag.c	Module for monitoring INET transport protocols sockets.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
   */
172589ccd   Ilpo Järvinen   [NET]: DIV_ROUND_...
7
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/fcntl.h>
  #include <linux/random.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
12
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
19
20
  #include <linux/cache.h>
  #include <linux/init.h>
  #include <linux/time.h>
  
  #include <net/icmp.h>
  #include <net/tcp.h>
  #include <net/ipv6.h>
  #include <net/inet_common.h>
505cbfc57   Arnaldo Carvalho de Melo   [IPV6]: Generalis...
21
22
23
24
  #include <net/inet_connection_sock.h>
  #include <net/inet_hashtables.h>
  #include <net/inet_timewait_sock.h>
  #include <net/inet6_hashtables.h>
085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
25
  #include <net/bpf_sk_storage.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
26
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
  
  #include <linux/inet.h>
  #include <linux/stddef.h>
a8c2190ee   Arnaldo Carvalho de Melo   [INET_DIAG]: Rena...
30
  #include <linux/inet_diag.h>
d366477a5   Pavel Emelyanov   sock_diag: Initia...
31
  #include <linux/sock_diag.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32

4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
33
  static const struct inet_diag_handler **inet_diag_table;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
34
  struct inet_diag_entry {
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
35
36
  	const __be32 *saddr;
  	const __be32 *daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
  	u16 sport;
  	u16 dport;
  	u16 family;
  	u16 userlocks;
637c841dd   David Ahern   net: diag: Add su...
41
  	u32 ifindex;
a52e95abf   Lorenzo Colitti   net: diag: allow ...
42
  	u32 mark;
b1f3e43db   Dmitry Yakunin   inet_diag: add su...
43
44
45
  #ifdef CONFIG_SOCK_CGROUP_DATA
  	u64 cgroup_id;
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
  };
d523a328f   Herbert Xu   [INET]: Fix inet_...
47
  static DEFINE_MUTEX(inet_diag_table_mutex);
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
48
  static const struct inet_diag_handler *inet_diag_lock_handler(int proto)
d523a328f   Herbert Xu   [INET]: Fix inet_...
49
  {
3f935c75e   Paolo Abeni   inet_diag: suppor...
50
51
52
53
  	if (proto < 0 || proto >= IPPROTO_MAX) {
  		mutex_lock(&inet_diag_table_mutex);
  		return ERR_PTR(-ENOENT);
  	}
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
54
  	if (!inet_diag_table[proto])
bf2ae2e4b   Xin Long   sock_diag: reques...
55
  		sock_load_diag_module(AF_INET, proto);
d523a328f   Herbert Xu   [INET]: Fix inet_...
56
57
  
  	mutex_lock(&inet_diag_table_mutex);
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
58
  	if (!inet_diag_table[proto])
d523a328f   Herbert Xu   [INET]: Fix inet_...
59
  		return ERR_PTR(-ENOENT);
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
60
  	return inet_diag_table[proto];
d523a328f   Herbert Xu   [INET]: Fix inet_...
61
  }
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
62
  static void inet_diag_unlock_handler(const struct inet_diag_handler *handler)
d523a328f   Herbert Xu   [INET]: Fix inet_...
63
64
65
  {
  	mutex_unlock(&inet_diag_table_mutex);
  }
cb2050a7b   Xin Long   sctp: export some...
66
  void inet_diag_msg_common_fill(struct inet_diag_msg *r, struct sock *sk)
a4458343a   Eric Dumazet   inet_diag: factor...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  {
  	r->idiag_family = sk->sk_family;
  
  	r->id.idiag_sport = htons(sk->sk_num);
  	r->id.idiag_dport = sk->sk_dport;
  	r->id.idiag_if = sk->sk_bound_dev_if;
  	sock_diag_save_cookie(sk, r->id.idiag_cookie);
  
  #if IS_ENABLED(CONFIG_IPV6)
  	if (sk->sk_family == AF_INET6) {
  		*(struct in6_addr *)r->id.idiag_src = sk->sk_v6_rcv_saddr;
  		*(struct in6_addr *)r->id.idiag_dst = sk->sk_v6_daddr;
  	} else
  #endif
  	{
  	memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
  	memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
  
  	r->id.idiag_src[0] = sk->sk_rcv_saddr;
  	r->id.idiag_dst[0] = sk->sk_daddr;
  	}
  }
cb2050a7b   Xin Long   sctp: export some...
89
  EXPORT_SYMBOL_GPL(inet_diag_msg_common_fill);
a4458343a   Eric Dumazet   inet_diag: factor...
90

b37e88407   Ivan Delalande   inet_diag: allow ...
91
92
93
  static size_t inet_sk_attr_size(struct sock *sk,
  				const struct inet_diag_req_v2 *req,
  				bool net_admin)
c8e2c80d7   Eric Dumazet   inet_diag: fix po...
94
  {
b37e88407   Ivan Delalande   inet_diag: allow ...
95
96
97
98
99
100
  	const struct inet_diag_handler *handler;
  	size_t aux = 0;
  
  	handler = inet_diag_table[req->sdiag_protocol];
  	if (handler && handler->idiag_get_aux_size)
  		aux = handler->idiag_get_aux_size(sk, net_admin);
c8e2c80d7   Eric Dumazet   inet_diag: fix po...
101
  	return	  nla_total_size(sizeof(struct tcp_info))
c8e2c80d7   Eric Dumazet   inet_diag: fix po...
102
  		+ nla_total_size(sizeof(struct inet_diag_msg))
83f73c5bb   Dmitry Yakunin   inet_diag: return...
103
104
  		+ inet_diag_msg_attrs_size()
  		+ nla_total_size(sizeof(struct inet_diag_meminfo))
c8e2c80d7   Eric Dumazet   inet_diag: fix po...
105
106
107
  		+ nla_total_size(SK_MEMINFO_VARS * sizeof(u32))
  		+ nla_total_size(TCP_CA_NAME_MAX)
  		+ nla_total_size(sizeof(struct tcpvegas_info))
b37e88407   Ivan Delalande   inet_diag: allow ...
108
  		+ aux
c8e2c80d7   Eric Dumazet   inet_diag: fix po...
109
110
  		+ 64;
  }
cb2050a7b   Xin Long   sctp: export some...
111
112
  int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
  			     struct inet_diag_msg *r, int ext,
d545caca8   Lorenzo Colitti   net: inet: diag: ...
113
114
  			     struct user_namespace *user_ns,
  			     bool net_admin)
cb2050a7b   Xin Long   sctp: export some...
115
116
  {
  	const struct inet_sock *inet = inet_sk(sk);
c10776161   Wei Wang   ip: expose inet s...
117
  	struct inet_diag_sockopt inet_sockopt;
cb2050a7b   Xin Long   sctp: export some...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  
  	if (nla_put_u8(skb, INET_DIAG_SHUTDOWN, sk->sk_shutdown))
  		goto errout;
  
  	/* IPv6 dual-stack sockets use inet->tos for IPv4 connections,
  	 * hence this needs to be included regardless of socket family.
  	 */
  	if (ext & (1 << (INET_DIAG_TOS - 1)))
  		if (nla_put_u8(skb, INET_DIAG_TOS, inet->tos) < 0)
  			goto errout;
  
  #if IS_ENABLED(CONFIG_IPV6)
  	if (r->idiag_family == AF_INET6) {
  		if (ext & (1 << (INET_DIAG_TCLASS - 1)))
  			if (nla_put_u8(skb, INET_DIAG_TCLASS,
  				       inet6_sk(sk)->tclass) < 0)
  				goto errout;
  
  		if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) &&
  		    nla_put_u8(skb, INET_DIAG_SKV6ONLY, ipv6_only_sock(sk)))
  			goto errout;
  	}
  #endif
d545caca8   Lorenzo Colitti   net: inet: diag: ...
141
142
  	if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, sk->sk_mark))
  		goto errout;
83f73c5bb   Dmitry Yakunin   inet_diag: return...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  	if (ext & (1 << (INET_DIAG_CLASS_ID - 1)) ||
  	    ext & (1 << (INET_DIAG_TCLASS - 1))) {
  		u32 classid = 0;
  
  #ifdef CONFIG_SOCK_CGROUP_DATA
  		classid = sock_cgroup_classid(&sk->sk_cgrp_data);
  #endif
  		/* Fallback to socket priority if class id isn't set.
  		 * Classful qdiscs use it as direct reference to class.
  		 * For cgroup2 classid is always zero.
  		 */
  		if (!classid)
  			classid = sk->sk_priority;
  
  		if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid))
  			goto errout;
  	}
6e3a401fc   Dmitry Yakunin   inet_diag: add cg...
160
161
162
163
164
165
  #ifdef CONFIG_SOCK_CGROUP_DATA
  	if (nla_put_u64_64bit(skb, INET_DIAG_CGROUP_ID,
  			      cgroup_id(sock_cgroup_ptr(&sk->sk_cgrp_data)),
  			      INET_DIAG_PAD))
  		goto errout;
  #endif
cb2050a7b   Xin Long   sctp: export some...
166
167
  	r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
  	r->idiag_inode = sock_i_ino(sk);
c10776161   Wei Wang   ip: expose inet s...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  	memset(&inet_sockopt, 0, sizeof(inet_sockopt));
  	inet_sockopt.recverr	= inet->recverr;
  	inet_sockopt.is_icsk	= inet->is_icsk;
  	inet_sockopt.freebind	= inet->freebind;
  	inet_sockopt.hdrincl	= inet->hdrincl;
  	inet_sockopt.mc_loop	= inet->mc_loop;
  	inet_sockopt.transparent = inet->transparent;
  	inet_sockopt.mc_all	= inet->mc_all;
  	inet_sockopt.nodefrag	= inet->nodefrag;
  	inet_sockopt.bind_address_no_port = inet->bind_address_no_port;
  	inet_sockopt.recverr_rfc4884 = inet->recverr_rfc4884;
  	inet_sockopt.defer_connect = inet->defer_connect;
  	if (nla_put(skb, INET_DIAG_SOCKOPT, sizeof(inet_sockopt),
  		    &inet_sockopt))
  		goto errout;
cb2050a7b   Xin Long   sctp: export some...
183
184
185
186
187
  	return 0;
  errout:
  	return 1;
  }
  EXPORT_SYMBOL_GPL(inet_diag_msg_attrs_fill);
d5e4d0a5e   Eric Dumazet   inet_diag: valida...
188
189
  static int inet_diag_parse_attrs(const struct nlmsghdr *nlh, int hdrlen,
  				 struct nlattr **req_nlas)
3f935c75e   Paolo Abeni   inet_diag: suppor...
190
191
192
193
194
195
  {
  	struct nlattr *nla;
  	int remaining;
  
  	nlmsg_for_each_attr(nla, nlh, hdrlen, remaining) {
  		int type = nla_type(nla);
d5e4d0a5e   Eric Dumazet   inet_diag: valida...
196
197
  		if (type == INET_DIAG_REQ_PROTOCOL && nla_len(nla) != sizeof(u32))
  			return -EINVAL;
3f935c75e   Paolo Abeni   inet_diag: suppor...
198
199
200
  		if (type < __INET_DIAG_REQ_MAX)
  			req_nlas[type] = nla;
  	}
d5e4d0a5e   Eric Dumazet   inet_diag: valida...
201
  	return 0;
3f935c75e   Paolo Abeni   inet_diag: suppor...
202
203
204
205
206
207
208
209
210
  }
  
  static int inet_diag_get_protocol(const struct inet_diag_req_v2 *req,
  				  const struct inet_diag_dump_data *data)
  {
  	if (data->req_nlas[INET_DIAG_REQ_PROTOCOL])
  		return nla_get_u32(data->req_nlas[INET_DIAG_REQ_PROTOCOL]);
  	return req->sdiag_protocol;
  }
085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
211
  #define MAX_DUMP_ALLOC_SIZE (KMALLOC_MAX_SIZE - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
212
  int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
5682d393b   Martin KaFai Lau   inet_diag: Refact...
213
214
215
  		      struct sk_buff *skb, struct netlink_callback *cb,
  		      const struct inet_diag_req_v2 *req,
  		      u16 nlmsg_flags, bool net_admin)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  {
521f1cf1d   Eric Dumazet   inet_diag: fix ac...
217
  	const struct tcp_congestion_ops *ca_ops;
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
218
  	const struct inet_diag_handler *handler;
085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
219
  	struct inet_diag_dump_data *cb_data;
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
220
  	int ext = req->idiag_ext;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
221
  	struct inet_diag_msg *r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  	struct nlmsghdr  *nlh;
6e277ed59   Thomas Graf   inet_diag: Do not...
223
  	struct nlattr *attr;
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
224
  	void *info = NULL;
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
225

085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
226
  	cb_data = cb->data;
3f935c75e   Paolo Abeni   inet_diag: suppor...
227
  	handler = inet_diag_table[inet_diag_get_protocol(req, cb_data)];
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
228
  	BUG_ON(!handler);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229

5682d393b   Martin KaFai Lau   inet_diag: Refact...
230
231
  	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
  			cb->nlh->nlmsg_type, sizeof(*r), nlmsg_flags);
6e277ed59   Thomas Graf   inet_diag: Do not...
232
  	if (!nlh)
d106352d9   David S. Miller   inet_diag: Move a...
233
  		return -EMSGSIZE;
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
234

d106352d9   David S. Miller   inet_diag: Move a...
235
  	r = nlmsg_data(nlh);
a58917f58   Eric Dumazet   inet_diag: allow ...
236
  	BUG_ON(!sk_fullsock(sk));
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
237

a4458343a   Eric Dumazet   inet_diag: factor...
238
  	inet_diag_msg_common_fill(r, sk);
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
239
240
241
  	r->idiag_state = sk->sk_state;
  	r->idiag_timer = 0;
  	r->idiag_retrans = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242

5682d393b   Martin KaFai Lau   inet_diag: Refact...
243
244
245
  	if (inet_diag_msg_attrs_fill(sk, skb, r, ext,
  				     sk_user_ns(NETLINK_CB(cb->skb).sk),
  				     net_admin))
e4e541a84   Pavel Emelyanov   sock-diag: Report...
246
  		goto errout;
6e277ed59   Thomas Graf   inet_diag: Do not...
247
248
249
  	if (ext & (1 << (INET_DIAG_MEMINFO - 1))) {
  		struct inet_diag_meminfo minfo = {
  			.idiag_rmem = sk_rmem_alloc_get(sk),
ab4e846a8   Eric Dumazet   tcp: annotate sk-...
250
  			.idiag_wmem = READ_ONCE(sk->sk_wmem_queued),
6e277ed59   Thomas Graf   inet_diag: Do not...
251
252
253
254
255
256
  			.idiag_fmem = sk->sk_forward_alloc,
  			.idiag_tmem = sk_wmem_alloc_get(sk),
  		};
  
  		if (nla_put(skb, INET_DIAG_MEMINFO, sizeof(minfo), &minfo) < 0)
  			goto errout;
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
257
  	}
c0636faa5   Pavel Emelyanov   inet_diag: Add th...
258
259
  	if (ext & (1 << (INET_DIAG_SKMEMINFO - 1)))
  		if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO))
6e277ed59   Thomas Graf   inet_diag: Do not...
260
  			goto errout;
c0636faa5   Pavel Emelyanov   inet_diag: Add th...
261

432490f9d   Cyrill Gorcunov   net: ip, diag -- ...
262
263
264
265
266
267
268
269
  	/*
  	 * RAW sockets might have user-defined protocols assigned,
  	 * so report the one supplied on socket creation.
  	 */
  	if (sk->sk_type == SOCK_RAW) {
  		if (nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol))
  			goto errout;
  	}
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
270
  	if (!icsk) {
62ad6fcd7   Shan Wei   udp_diag: impleme...
271
  		handler->idiag_get_info(sk, r, NULL);
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
272
273
  		goto out;
  	}
6ba8a3b19   Nandita Dukkipati   tcp: Tail loss pr...
274
  	if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
57dde7f70   Yuchung Cheng   tcp: add reorderi...
275
  	    icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
6ba8a3b19   Nandita Dukkipati   tcp: Tail loss pr...
276
  	    icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
277
278
  		r->idiag_timer = 1;
  		r->idiag_retrans = icsk->icsk_retransmits;
b7de529c7   Xin Long   net: use jiffies_...
279
  		r->idiag_expires =
3828a93f5   Eric Dumazet   inet_diag: use ji...
280
  			jiffies_delta_to_msecs(icsk->icsk_timeout - jiffies);
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
281
  	} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
282
283
  		r->idiag_timer = 4;
  		r->idiag_retrans = icsk->icsk_probes_out;
b7de529c7   Xin Long   net: use jiffies_...
284
  		r->idiag_expires =
3828a93f5   Eric Dumazet   inet_diag: use ji...
285
  			jiffies_delta_to_msecs(icsk->icsk_timeout - jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  	} else if (timer_pending(&sk->sk_timer)) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
287
288
  		r->idiag_timer = 2;
  		r->idiag_retrans = icsk->icsk_probes_out;
b7de529c7   Xin Long   net: use jiffies_...
289
  		r->idiag_expires =
3828a93f5   Eric Dumazet   inet_diag: use ji...
290
  			jiffies_delta_to_msecs(sk->sk_timer.expires - jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  	} else {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
292
293
  		r->idiag_timer = 0;
  		r->idiag_expires = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  	}
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
295

3fd22af80   Craig Gallek   sock_diag: specif...
296
  	if ((ext & (1 << (INET_DIAG_INFO - 1))) && handler->idiag_info_size) {
6ed46d124   Nicolas Dichtel   sock_diag: align ...
297
298
299
  		attr = nla_reserve_64bit(skb, INET_DIAG_INFO,
  					 handler->idiag_info_size,
  					 INET_DIAG_PAD);
6e277ed59   Thomas Graf   inet_diag: Do not...
300
301
  		if (!attr)
  			goto errout;
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
302

6e277ed59   Thomas Graf   inet_diag: Do not...
303
  		info = nla_data(attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  	}
521f1cf1d   Eric Dumazet   inet_diag: fix ac...
305
306
307
308
309
310
311
312
313
  	if (ext & (1 << (INET_DIAG_CONG - 1))) {
  		int err = 0;
  
  		rcu_read_lock();
  		ca_ops = READ_ONCE(icsk->icsk_ca_ops);
  		if (ca_ops)
  			err = nla_put_string(skb, INET_DIAG_CONG, ca_ops->name);
  		rcu_read_unlock();
  		if (err < 0)
6e277ed59   Thomas Graf   inet_diag: Do not...
314
  			goto errout;
521f1cf1d   Eric Dumazet   inet_diag: fix ac...
315
  	}
6e277ed59   Thomas Graf   inet_diag: Do not...
316

4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
317
  	handler->idiag_get_info(sk, r, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318

b37e88407   Ivan Delalande   inet_diag: allow ...
319
320
321
  	if (ext & (1 << (INET_DIAG_INFO - 1)) && handler->idiag_get_aux)
  		if (handler->idiag_get_aux(sk, net_admin, skb) < 0)
  			goto errout;
521f1cf1d   Eric Dumazet   inet_diag: fix ac...
322
  	if (sk->sk_state < TCP_TIME_WAIT) {
64f40ff5b   Eric Dumazet   tcp: prepare CC g...
323
324
325
  		union tcp_cc_info info;
  		size_t sz = 0;
  		int attr;
521f1cf1d   Eric Dumazet   inet_diag: fix ac...
326
327
328
329
  
  		rcu_read_lock();
  		ca_ops = READ_ONCE(icsk->icsk_ca_ops);
  		if (ca_ops && ca_ops->get_info)
64f40ff5b   Eric Dumazet   tcp: prepare CC g...
330
  			sz = ca_ops->get_info(sk, ext, &attr, &info);
521f1cf1d   Eric Dumazet   inet_diag: fix ac...
331
  		rcu_read_unlock();
64f40ff5b   Eric Dumazet   tcp: prepare CC g...
332
  		if (sz && nla_put(skb, attr, sz, &info) < 0)
521f1cf1d   Eric Dumazet   inet_diag: fix ac...
333
334
  			goto errout;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335

085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
  	/* Keep it at the end for potential retry with a larger skb,
  	 * or else do best-effort fitting, which is only done for the
  	 * first_nlmsg.
  	 */
  	if (cb_data->bpf_stg_diag) {
  		bool first_nlmsg = ((unsigned char *)nlh == skb->data);
  		unsigned int prev_min_dump_alloc;
  		unsigned int total_nla_size = 0;
  		unsigned int msg_len;
  		int err;
  
  		msg_len = skb_tail_pointer(skb) - (unsigned char *)nlh;
  		err = bpf_sk_storage_diag_put(cb_data->bpf_stg_diag, sk, skb,
  					      INET_DIAG_SK_BPF_STORAGES,
  					      &total_nla_size);
  
  		if (!err)
  			goto out;
  
  		total_nla_size += msg_len;
  		prev_min_dump_alloc = cb->min_dump_alloc;
  		if (total_nla_size > prev_min_dump_alloc)
  			cb->min_dump_alloc = min_t(u32, total_nla_size,
  						   MAX_DUMP_ALLOC_SIZE);
  
  		if (!first_nlmsg)
  			goto errout;
  
  		if (cb->min_dump_alloc > prev_min_dump_alloc)
  			/* Retry with pskb_expand_head() with
  			 * __GFP_DIRECT_RECLAIM
  			 */
  			goto errout;
  
  		WARN_ON_ONCE(total_nla_size <= prev_min_dump_alloc);
  
  		/* Send what we have for this sk
  		 * and move on to the next sk in the following
  		 * dump()
  		 */
  	}
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
377
  out:
053c095a8   Johannes Berg   netlink: make nlm...
378
379
  	nlmsg_end(skb, nlh);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380

6e277ed59   Thomas Graf   inet_diag: Do not...
381
382
  errout:
  	nlmsg_cancel(skb, nlh);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
383
  	return -EMSGSIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  }
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
385
  EXPORT_SYMBOL_GPL(inet_sk_diag_fill);
33cf7c90f   Eric Dumazet   net: add real soc...
386
  static int inet_twsk_diag_fill(struct sock *sk,
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
387
  			       struct sk_buff *skb,
5682d393b   Martin KaFai Lau   inet_diag: Refact...
388
389
  			       struct netlink_callback *cb,
  			       u16 nlmsg_flags)
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
390
  {
33cf7c90f   Eric Dumazet   net: add real soc...
391
  	struct inet_timewait_sock *tw = inet_twsk(sk);
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
392
  	struct inet_diag_msg *r;
6e277ed59   Thomas Graf   inet_diag: Do not...
393
  	struct nlmsghdr *nlh;
789f558cf   Eric Dumazet   tcp/dccp: get rid...
394
  	long tmo;
d106352d9   David S. Miller   inet_diag: Move a...
395

5682d393b   Martin KaFai Lau   inet_diag: Refact...
396
397
398
  	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
  			cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type,
  			sizeof(*r), nlmsg_flags);
6e277ed59   Thomas Graf   inet_diag: Do not...
399
  	if (!nlh)
d106352d9   David S. Miller   inet_diag: Move a...
400
  		return -EMSGSIZE;
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
401

d106352d9   David S. Miller   inet_diag: Move a...
402
  	r = nlmsg_data(nlh);
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
403
  	BUG_ON(tw->tw_state != TCP_TIME_WAIT);
a4458343a   Eric Dumazet   inet_diag: factor...
404
  	inet_diag_msg_common_fill(r, sk);
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
405
  	r->idiag_retrans      = 0;
b1aac815c   Daniel Borkmann   net: inet_diag: z...
406

c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
407
408
  	r->idiag_state	      = tw->tw_substate;
  	r->idiag_timer	      = 3;
3828a93f5   Eric Dumazet   inet_diag: use ji...
409
410
  	tmo = tw->tw_timer.expires - jiffies;
  	r->idiag_expires      = jiffies_delta_to_msecs(tmo);
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
411
412
413
414
  	r->idiag_rqueue	      = 0;
  	r->idiag_wqueue	      = 0;
  	r->idiag_uid	      = 0;
  	r->idiag_inode	      = 0;
6e277ed59   Thomas Graf   inet_diag: Do not...
415

053c095a8   Johannes Berg   netlink: make nlm...
416
417
  	nlmsg_end(skb, nlh);
  	return 0;
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
418
  }
a58917f58   Eric Dumazet   inet_diag: allow ...
419
  static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb,
5682d393b   Martin KaFai Lau   inet_diag: Refact...
420
421
  			      struct netlink_callback *cb,
  			      u16 nlmsg_flags, bool net_admin)
a58917f58   Eric Dumazet   inet_diag: allow ...
422
  {
d545caca8   Lorenzo Colitti   net: inet: diag: ...
423
  	struct request_sock *reqsk = inet_reqsk(sk);
a58917f58   Eric Dumazet   inet_diag: allow ...
424
425
426
  	struct inet_diag_msg *r;
  	struct nlmsghdr *nlh;
  	long tmo;
5682d393b   Martin KaFai Lau   inet_diag: Refact...
427
428
  	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
  			cb->nlh->nlmsg_type, sizeof(*r), nlmsg_flags);
a58917f58   Eric Dumazet   inet_diag: allow ...
429
430
431
432
433
434
435
  	if (!nlh)
  		return -EMSGSIZE;
  
  	r = nlmsg_data(nlh);
  	inet_diag_msg_common_fill(r, sk);
  	r->idiag_state = TCP_SYN_RECV;
  	r->idiag_timer = 1;
d545caca8   Lorenzo Colitti   net: inet: diag: ...
436
  	r->idiag_retrans = reqsk->num_retrans;
a58917f58   Eric Dumazet   inet_diag: allow ...
437
438
439
  
  	BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) !=
  		     offsetof(struct sock, sk_cookie));
fa76ce732   Eric Dumazet   inet: get rid of ...
440
  	tmo = inet_reqsk(sk)->rsk_timer.expires - jiffies;
3828a93f5   Eric Dumazet   inet_diag: use ji...
441
  	r->idiag_expires = jiffies_delta_to_msecs(tmo);
a58917f58   Eric Dumazet   inet_diag: allow ...
442
443
444
445
  	r->idiag_rqueue	= 0;
  	r->idiag_wqueue	= 0;
  	r->idiag_uid	= 0;
  	r->idiag_inode	= 0;
d545caca8   Lorenzo Colitti   net: inet: diag: ...
446
  	if (net_admin && nla_put_u32(skb, INET_DIAG_MARK,
e33de7c53   Wang Hai   inet_diag: Fix er...
447
448
  				     inet_rsk(reqsk)->ir_mark)) {
  		nlmsg_cancel(skb, nlh);
d545caca8   Lorenzo Colitti   net: inet: diag: ...
449
  		return -EMSGSIZE;
e33de7c53   Wang Hai   inet_diag: Fix er...
450
  	}
d545caca8   Lorenzo Colitti   net: inet: diag: ...
451

a58917f58   Eric Dumazet   inet_diag: allow ...
452
453
454
  	nlmsg_end(skb, nlh);
  	return 0;
  }
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
455
  static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
5682d393b   Martin KaFai Lau   inet_diag: Refact...
456
  			struct netlink_callback *cb,
34160ea3f   Eric Dumazet   inet_diag: add co...
457
  			const struct inet_diag_req_v2 *r,
5682d393b   Martin KaFai Lau   inet_diag: Refact...
458
  			u16 nlmsg_flags, bool net_admin)
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
459
460
  {
  	if (sk->sk_state == TCP_TIME_WAIT)
5682d393b   Martin KaFai Lau   inet_diag: Refact...
461
  		return inet_twsk_diag_fill(sk, skb, cb, nlmsg_flags);
efe4208f4   Eric Dumazet   ipv6: make lookup...
462

a58917f58   Eric Dumazet   inet_diag: allow ...
463
  	if (sk->sk_state == TCP_NEW_SYN_RECV)
5682d393b   Martin KaFai Lau   inet_diag: Refact...
464
  		return inet_req_diag_fill(sk, skb, cb, nlmsg_flags, net_admin);
a58917f58   Eric Dumazet   inet_diag: allow ...
465

5682d393b   Martin KaFai Lau   inet_diag: Refact...
466
467
  	return inet_sk_diag_fill(sk, inet_csk(sk), skb, cb, r, nlmsg_flags,
  				 net_admin);
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
468
  }
b613f56ec   Lorenzo Colitti   net: diag: split ...
469
470
471
  struct sock *inet_diag_find_one_icsk(struct net *net,
  				     struct inet_hashinfo *hashinfo,
  				     const struct inet_diag_req_v2 *req)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
  {
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
473
  	struct sock *sk;
d523a328f   Herbert Xu   [INET]: Fix inet_...
474

2d331915a   Eric Dumazet   tcp/dccp: use rcu...
475
  	rcu_read_lock();
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
476
  	if (req->sdiag_family == AF_INET)
a583636a8   Craig Gallek   inet: refactor in...
477
  		sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[0],
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
478
479
  				 req->id.idiag_dport, req->id.idiag_src[0],
  				 req->id.idiag_sport, req->id.idiag_if);
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
480
  #if IS_ENABLED(CONFIG_IPV6)
7c1306723   Eric Dumazet   net: diag: suppor...
481
482
483
  	else if (req->sdiag_family == AF_INET6) {
  		if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) &&
  		    ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_src))
a583636a8   Craig Gallek   inet: refactor in...
484
  			sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[3],
7c1306723   Eric Dumazet   net: diag: suppor...
485
486
487
  					 req->id.idiag_dport, req->id.idiag_src[3],
  					 req->id.idiag_sport, req->id.idiag_if);
  		else
a583636a8   Craig Gallek   inet: refactor in...
488
  			sk = inet6_lookup(net, hashinfo, NULL, 0,
7c1306723   Eric Dumazet   net: diag: suppor...
489
490
491
492
493
494
  					  (struct in6_addr *)req->id.idiag_dst,
  					  req->id.idiag_dport,
  					  (struct in6_addr *)req->id.idiag_src,
  					  req->id.idiag_sport,
  					  req->id.idiag_if);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
  #endif
2d331915a   Eric Dumazet   tcp/dccp: use rcu...
496
497
  	else {
  		rcu_read_unlock();
b613f56ec   Lorenzo Colitti   net: diag: split ...
498
  		return ERR_PTR(-EINVAL);
2d331915a   Eric Dumazet   tcp/dccp: use rcu...
499
500
  	}
  	rcu_read_unlock();
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
501
  	if (!sk)
b613f56ec   Lorenzo Colitti   net: diag: split ...
502
  		return ERR_PTR(-ENOENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503

b613f56ec   Lorenzo Colitti   net: diag: split ...
504
505
506
507
508
509
510
511
512
513
  	if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) {
  		sock_gen_put(sk);
  		return ERR_PTR(-ENOENT);
  	}
  
  	return sk;
  }
  EXPORT_SYMBOL_GPL(inet_diag_find_one_icsk);
  
  int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
5682d393b   Martin KaFai Lau   inet_diag: Refact...
514
  			    struct netlink_callback *cb,
b613f56ec   Lorenzo Colitti   net: diag: split ...
515
516
  			    const struct inet_diag_req_v2 *req)
  {
5682d393b   Martin KaFai Lau   inet_diag: Refact...
517
  	struct sk_buff *in_skb = cb->skb;
b37e88407   Ivan Delalande   inet_diag: allow ...
518
  	bool net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN);
b613f56ec   Lorenzo Colitti   net: diag: split ...
519
520
521
522
523
524
525
526
  	struct net *net = sock_net(in_skb->sk);
  	struct sk_buff *rep;
  	struct sock *sk;
  	int err;
  
  	sk = inet_diag_find_one_icsk(net, hashinfo, req);
  	if (IS_ERR(sk))
  		return PTR_ERR(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527

b37e88407   Ivan Delalande   inet_diag: allow ...
528
  	rep = nlmsg_new(inet_sk_attr_size(sk, req, net_admin), GFP_KERNEL);
6e277ed59   Thomas Graf   inet_diag: Do not...
529
530
  	if (!rep) {
  		err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
  		goto out;
6e277ed59   Thomas Graf   inet_diag: Do not...
532
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533

5682d393b   Martin KaFai Lau   inet_diag: Refact...
534
  	err = sk_diag_fill(sk, rep, cb, req, 0, net_admin);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
535
536
  	if (err < 0) {
  		WARN_ON(err == -EMSGSIZE);
6e277ed59   Thomas Graf   inet_diag: Do not...
537
  		nlmsg_free(rep);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
538
539
  		goto out;
  	}
15e473046   Eric W. Biederman   netlink: Rename p...
540
  	err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid,
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
541
  			      MSG_DONTWAIT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
545
  	if (err > 0)
  		err = 0;
  
  out:
c1d607cc4   Eric Dumazet   inet_diag: use so...
546
547
  	if (sk)
  		sock_gen_put(sk);
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
548
549
  	return err;
  }
1942c518c   Pavel Emelyanov   inet_diag: Genera...
550
  EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk);
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
551

6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
552
  static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb,
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
553
  			       const struct nlmsghdr *nlh,
3f935c75e   Paolo Abeni   inet_diag: suppor...
554
  			       int hdrlen,
34160ea3f   Eric Dumazet   inet_diag: add co...
555
  			       const struct inet_diag_req_v2 *req)
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
556
557
  {
  	const struct inet_diag_handler *handler;
3f935c75e   Paolo Abeni   inet_diag: suppor...
558
559
560
561
  	struct inet_diag_dump_data dump_data;
  	int err, protocol;
  
  	memset(&dump_data, 0, sizeof(dump_data));
d5e4d0a5e   Eric Dumazet   inet_diag: valida...
562
563
564
  	err = inet_diag_parse_attrs(nlh, hdrlen, dump_data.req_nlas);
  	if (err)
  		return err;
3f935c75e   Paolo Abeni   inet_diag: suppor...
565
  	protocol = inet_diag_get_protocol(req, &dump_data);
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
566

3f935c75e   Paolo Abeni   inet_diag: suppor...
567
  	handler = inet_diag_lock_handler(protocol);
5682d393b   Martin KaFai Lau   inet_diag: Refact...
568
  	if (IS_ERR(handler)) {
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
569
  		err = PTR_ERR(handler);
5682d393b   Martin KaFai Lau   inet_diag: Refact...
570
571
572
573
  	} else if (cmd == SOCK_DIAG_BY_FAMILY) {
  		struct netlink_callback cb = {
  			.nlh = nlh,
  			.skb = in_skb,
3f935c75e   Paolo Abeni   inet_diag: suppor...
574
  			.data = &dump_data,
5682d393b   Martin KaFai Lau   inet_diag: Refact...
575
576
577
  		};
  		err = handler->dump_one(&cb, req);
  	} else if (cmd == SOCK_DESTROY && handler->destroy) {
6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
578
  		err = handler->destroy(in_skb, req);
5682d393b   Martin KaFai Lau   inet_diag: Refact...
579
  	} else {
6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
580
  		err = -EOPNOTSUPP;
5682d393b   Martin KaFai Lau   inet_diag: Refact...
581
  	}
d523a328f   Herbert Xu   [INET]: Fix inet_...
582
  	inet_diag_unlock_handler(handler);
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
583

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
  	return err;
  }
9f8552996   Al Viro   [IPV4]: inet_diag...
586
  static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
588
589
590
591
592
593
594
595
596
  {
  	int words = bits >> 5;
  
  	bits &= 0x1f;
  
  	if (words) {
  		if (memcmp(a1, a2, words << 2))
  			return 0;
  	}
  	if (bits) {
9f8552996   Al Viro   [IPV4]: inet_diag...
597
598
  		__be32 w1, w2;
  		__be32 mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
600
601
602
603
604
605
606
607
608
609
610
  
  		w1 = a1[words];
  		w2 = a2[words];
  
  		mask = htonl((0xffffffff) << (32 - bits));
  
  		if ((w1 ^ w2) & mask)
  			return 0;
  	}
  
  	return 1;
  }
87c22ea52   Pavel Emelyanov   inet_diag: Reduce...
611
  static int inet_diag_bc_run(const struct nlattr *_bc,
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
612
  			    const struct inet_diag_entry *entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
  {
87c22ea52   Pavel Emelyanov   inet_diag: Reduce...
614
615
  	const void *bc = nla_data(_bc);
  	int len = nla_len(_bc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
  	while (len > 0) {
  		int yes = 1;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
618
  		const struct inet_diag_bc_op *op = bc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
  
  		switch (op->code) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
621
  		case INET_DIAG_BC_NOP:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
623
  		case INET_DIAG_BC_JMP:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
  			yes = 0;
  			break;
bbb6189df   Kristian Evensen   inet_diag: Add eq...
626
627
628
  		case INET_DIAG_BC_S_EQ:
  			yes = entry->sport == op[1].no;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
629
  		case INET_DIAG_BC_S_GE:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
  			yes = entry->sport >= op[1].no;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
632
  		case INET_DIAG_BC_S_LE:
b4ced2b76   Roel Kluin   netlink: With opc...
633
  			yes = entry->sport <= op[1].no;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  			break;
bbb6189df   Kristian Evensen   inet_diag: Add eq...
635
636
637
  		case INET_DIAG_BC_D_EQ:
  			yes = entry->dport == op[1].no;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
638
  		case INET_DIAG_BC_D_GE:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
640
  			yes = entry->dport >= op[1].no;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
641
  		case INET_DIAG_BC_D_LE:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
  			yes = entry->dport <= op[1].no;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
644
  		case INET_DIAG_BC_AUTO:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
646
  			yes = !(entry->userlocks & SOCK_BINDPORT_LOCK);
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
647
  		case INET_DIAG_BC_S_COND:
a8c2190ee   Arnaldo Carvalho de Melo   [INET_DIAG]: Rena...
648
  		case INET_DIAG_BC_D_COND: {
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
649
650
  			const struct inet_diag_hostcond *cond;
  			const __be32 *addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651

e31c5e0e4   Eric Dumazet   inet_diag: cleanups
652
  			cond = (const struct inet_diag_hostcond *)(op + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
  			if (cond->port != -1 &&
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
654
  			    cond->port != (op->code == INET_DIAG_BC_S_COND ?
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
657
658
  					     entry->sport : entry->dport)) {
  				yes = 0;
  				break;
  			}
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
659

73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
660
  			if (op->code == INET_DIAG_BC_S_COND)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
662
663
  				addr = entry->saddr;
  			else
  				addr = entry->daddr;
f67caec90   Neal Cardwell   inet_diag: avoid ...
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
  			if (cond->family != AF_UNSPEC &&
  			    cond->family != entry->family) {
  				if (entry->family == AF_INET6 &&
  				    cond->family == AF_INET) {
  					if (addr[0] == 0 && addr[1] == 0 &&
  					    addr[2] == htonl(0xffff) &&
  					    bitstring_match(addr + 3,
  							    cond->addr,
  							    cond->prefix_len))
  						break;
  				}
  				yes = 0;
  				break;
  			}
  
  			if (cond->prefix_len == 0)
  				break;
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
681
682
  			if (bitstring_match(addr, cond->addr,
  					    cond->prefix_len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
686
  			yes = 0;
  			break;
  		}
637c841dd   David Ahern   net: diag: Add su...
687
688
689
690
691
692
693
694
  		case INET_DIAG_BC_DEV_COND: {
  			u32 ifindex;
  
  			ifindex = *((const u32 *)(op + 1));
  			if (ifindex != entry->ifindex)
  				yes = 0;
  			break;
  		}
a52e95abf   Lorenzo Colitti   net: diag: allow ...
695
696
697
698
699
700
701
702
  		case INET_DIAG_BC_MARK_COND: {
  			struct inet_diag_markcond *cond;
  
  			cond = (struct inet_diag_markcond *)(op + 1);
  			if ((entry->mark & cond->mask) != cond->mark)
  				yes = 0;
  			break;
  		}
b1f3e43db   Dmitry Yakunin   inet_diag: add su...
703
704
705
706
707
708
709
710
711
712
  #ifdef CONFIG_SOCK_CGROUP_DATA
  		case INET_DIAG_BC_CGROUP_COND: {
  			u64 cgroup_id;
  
  			cgroup_id = get_unaligned((const u64 *)(op + 1));
  			if (cgroup_id != entry->cgroup_id)
  				yes = 0;
  			break;
  		}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
  		}
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
714
  		if (yes) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
718
719
720
721
  			len -= op->yes;
  			bc += op->yes;
  		} else {
  			len -= op->no;
  			bc += op->no;
  		}
  	}
a02cec215   Eric Dumazet   net: return opera...
722
  	return len == 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
  }
a4458343a   Eric Dumazet   inet_diag: factor...
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
  /* This helper is available for all sockets (ESTABLISH, TIMEWAIT, SYN_RECV)
   */
  static void entry_fill_addrs(struct inet_diag_entry *entry,
  			     const struct sock *sk)
  {
  #if IS_ENABLED(CONFIG_IPV6)
  	if (sk->sk_family == AF_INET6) {
  		entry->saddr = sk->sk_v6_rcv_saddr.s6_addr32;
  		entry->daddr = sk->sk_v6_daddr.s6_addr32;
  	} else
  #endif
  	{
  		entry->saddr = &sk->sk_rcv_saddr;
  		entry->daddr = &sk->sk_daddr;
  	}
  }
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
740
741
  int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk)
  {
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
742
  	struct inet_sock *inet = inet_sk(sk);
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
743
  	struct inet_diag_entry entry;
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
744

e31c5e0e4   Eric Dumazet   inet_diag: cleanups
745
  	if (!bc)
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
746
747
748
  		return 1;
  
  	entry.family = sk->sk_family;
a4458343a   Eric Dumazet   inet_diag: factor...
749
  	entry_fill_addrs(&entry, sk);
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
750
751
  	entry.sport = inet->inet_num;
  	entry.dport = ntohs(inet->inet_dport);
637c841dd   David Ahern   net: diag: Add su...
752
  	entry.ifindex = sk->sk_bound_dev_if;
a58917f58   Eric Dumazet   inet_diag: allow ...
753
  	entry.userlocks = sk_fullsock(sk) ? sk->sk_userlocks : 0;
a52e95abf   Lorenzo Colitti   net: diag: allow ...
754
755
756
757
758
759
  	if (sk_fullsock(sk))
  		entry.mark = sk->sk_mark;
  	else if (sk->sk_state == TCP_NEW_SYN_RECV)
  		entry.mark = inet_rsk(inet_reqsk(sk))->ir_mark;
  	else
  		entry.mark = 0;
b1f3e43db   Dmitry Yakunin   inet_diag: add su...
760
  #ifdef CONFIG_SOCK_CGROUP_DATA
ee1bd483c   Dmitry Yakunin   inet_diag: bc: re...
761
762
  	entry.cgroup_id = sk_fullsock(sk) ?
  		cgroup_id(sock_cgroup_ptr(&sk->sk_cgrp_data)) : 0;
b1f3e43db   Dmitry Yakunin   inet_diag: add su...
763
  #endif
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
764
765
766
767
  
  	return inet_diag_bc_run(bc, &entry);
  }
  EXPORT_SYMBOL_GPL(inet_diag_bc_sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
769
770
  static int valid_cc(const void *bc, int len, int cc)
  {
  	while (len >= 0) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
771
  		const struct inet_diag_bc_op *op = bc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
773
774
775
776
  
  		if (cc > len)
  			return 0;
  		if (cc == len)
  			return 1;
eeb149727   Eric Dumazet   inet_diag: fix in...
777
  		if (op->yes < 4 || op->yes & 3)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
781
782
783
  			return 0;
  		len -= op->yes;
  		bc  += op->yes;
  	}
  	return 0;
  }
637c841dd   David Ahern   net: diag: Add su...
784
785
786
787
788
789
790
791
792
793
794
  /* data is u32 ifindex */
  static bool valid_devcond(const struct inet_diag_bc_op *op, int len,
  			  int *min_len)
  {
  	/* Check ifindex space. */
  	*min_len += sizeof(u32);
  	if (len < *min_len)
  		return false;
  
  	return true;
  }
405c00594   Neal Cardwell   inet_diag: valida...
795
796
797
798
  /* Validate an inet_diag_hostcond. */
  static bool valid_hostcond(const struct inet_diag_bc_op *op, int len,
  			   int *min_len)
  {
405c00594   Neal Cardwell   inet_diag: valida...
799
  	struct inet_diag_hostcond *cond;
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
800
  	int addr_len;
405c00594   Neal Cardwell   inet_diag: valida...
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
  
  	/* Check hostcond space. */
  	*min_len += sizeof(struct inet_diag_hostcond);
  	if (len < *min_len)
  		return false;
  	cond = (struct inet_diag_hostcond *)(op + 1);
  
  	/* Check address family and address length. */
  	switch (cond->family) {
  	case AF_UNSPEC:
  		addr_len = 0;
  		break;
  	case AF_INET:
  		addr_len = sizeof(struct in_addr);
  		break;
  	case AF_INET6:
  		addr_len = sizeof(struct in6_addr);
  		break;
  	default:
  		return false;
  	}
  	*min_len += addr_len;
  	if (len < *min_len)
  		return false;
  
  	/* Check prefix length (in bits) vs address length (in bytes). */
  	if (cond->prefix_len > 8 * addr_len)
  		return false;
  
  	return true;
  }
5e1f54201   Neal Cardwell   inet_diag: valida...
832
  /* Validate a port comparison operator. */
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
833
834
  static bool valid_port_comparison(const struct inet_diag_bc_op *op,
  				  int len, int *min_len)
5e1f54201   Neal Cardwell   inet_diag: valida...
835
836
837
838
839
840
841
  {
  	/* Port comparisons put the port in a follow-on inet_diag_bc_op. */
  	*min_len += sizeof(struct inet_diag_bc_op);
  	if (len < *min_len)
  		return false;
  	return true;
  }
a52e95abf   Lorenzo Colitti   net: diag: allow ...
842
843
  static bool valid_markcond(const struct inet_diag_bc_op *op, int len,
  			   int *min_len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
  {
a52e95abf   Lorenzo Colitti   net: diag: allow ...
845
846
847
  	*min_len += sizeof(struct inet_diag_markcond);
  	return len >= *min_len;
  }
b1f3e43db   Dmitry Yakunin   inet_diag: add su...
848
849
850
851
852
853
854
855
  #ifdef CONFIG_SOCK_CGROUP_DATA
  static bool valid_cgroupcond(const struct inet_diag_bc_op *op, int len,
  			     int *min_len)
  {
  	*min_len += sizeof(u64);
  	return len >= *min_len;
  }
  #endif
a52e95abf   Lorenzo Colitti   net: diag: allow ...
856
857
858
859
  static int inet_diag_bc_audit(const struct nlattr *attr,
  			      const struct sk_buff *skb)
  {
  	bool net_admin = netlink_net_capable(skb, CAP_NET_ADMIN);
627cc4add   Lorenzo Colitti   net: diag: slight...
860
861
862
863
864
865
866
867
  	const void *bytecode, *bc;
  	int bytecode_len, len;
  
  	if (!attr || nla_len(attr) < sizeof(struct inet_diag_bc_op))
  		return -EINVAL;
  
  	bytecode = bc = nla_data(attr);
  	len = bytecode_len = nla_len(attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
  
  	while (len > 0) {
405c00594   Neal Cardwell   inet_diag: valida...
870
  		int min_len = sizeof(struct inet_diag_bc_op);
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
871
  		const struct inet_diag_bc_op *op = bc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
  		switch (op->code) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
874
875
  		case INET_DIAG_BC_S_COND:
  		case INET_DIAG_BC_D_COND:
405c00594   Neal Cardwell   inet_diag: valida...
876
877
  			if (!valid_hostcond(bc, len, &min_len))
  				return -EINVAL;
5e1f54201   Neal Cardwell   inet_diag: valida...
878
  			break;
637c841dd   David Ahern   net: diag: Add su...
879
880
881
882
  		case INET_DIAG_BC_DEV_COND:
  			if (!valid_devcond(bc, len, &min_len))
  				return -EINVAL;
  			break;
bbb6189df   Kristian Evensen   inet_diag: Add eq...
883
  		case INET_DIAG_BC_S_EQ:
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
884
885
  		case INET_DIAG_BC_S_GE:
  		case INET_DIAG_BC_S_LE:
bbb6189df   Kristian Evensen   inet_diag: Add eq...
886
  		case INET_DIAG_BC_D_EQ:
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
887
888
  		case INET_DIAG_BC_D_GE:
  		case INET_DIAG_BC_D_LE:
5e1f54201   Neal Cardwell   inet_diag: valida...
889
  			if (!valid_port_comparison(bc, len, &min_len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
890
891
  				return -EINVAL;
  			break;
a52e95abf   Lorenzo Colitti   net: diag: allow ...
892
893
894
895
896
897
  		case INET_DIAG_BC_MARK_COND:
  			if (!net_admin)
  				return -EPERM;
  			if (!valid_markcond(bc, len, &min_len))
  				return -EINVAL;
  			break;
b1f3e43db   Dmitry Yakunin   inet_diag: add su...
898
899
900
901
902
903
  #ifdef CONFIG_SOCK_CGROUP_DATA
  		case INET_DIAG_BC_CGROUP_COND:
  			if (!valid_cgroupcond(bc, len, &min_len))
  				return -EINVAL;
  			break;
  #endif
5e1f54201   Neal Cardwell   inet_diag: valida...
904
905
  		case INET_DIAG_BC_AUTO:
  		case INET_DIAG_BC_JMP:
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
906
  		case INET_DIAG_BC_NOP:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
908
909
910
  			break;
  		default:
  			return -EINVAL;
  		}
5e1f54201   Neal Cardwell   inet_diag: valida...
911
912
913
914
915
916
917
918
  
  		if (op->code != INET_DIAG_BC_NOP) {
  			if (op->no < min_len || op->no > len + 4 || op->no & 3)
  				return -EINVAL;
  			if (op->no < len &&
  			    !valid_cc(bytecode, bytecode_len, len - op->no))
  				return -EINVAL;
  		}
405c00594   Neal Cardwell   inet_diag: valida...
919
  		if (op->yes < min_len || op->yes > len + 4 || op->yes & 3)
eeb149727   Eric Dumazet   inet_diag: fix in...
920
  			return -EINVAL;
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
921
  		bc  += op->yes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922
923
924
925
  		len -= op->yes;
  	}
  	return len == 0 ? 0 : -EINVAL;
  }
496127290   Eric Dumazet   inet_diag: remove...
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
  static void twsk_build_assert(void)
  {
  	BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_family) !=
  		     offsetof(struct sock, sk_family));
  
  	BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_num) !=
  		     offsetof(struct inet_sock, inet_num));
  
  	BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_dport) !=
  		     offsetof(struct inet_sock, inet_dport));
  
  	BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_rcv_saddr) !=
  		     offsetof(struct inet_sock, inet_rcv_saddr));
  
  	BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_daddr) !=
  		     offsetof(struct inet_sock, inet_daddr));
  
  #if IS_ENABLED(CONFIG_IPV6)
  	BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_v6_rcv_saddr) !=
  		     offsetof(struct sock, sk_v6_rcv_saddr));
  
  	BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_v6_daddr) !=
  		     offsetof(struct sock, sk_v6_daddr));
  #endif
  }
1942c518c   Pavel Emelyanov   inet_diag: Genera...
951
  void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
952
  			 struct netlink_callback *cb,
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
953
  			 const struct inet_diag_req_v2 *r)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
954
  {
67db3e4bf   Eric Dumazet   tcp: no longer ho...
955
  	bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN);
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
956
  	struct inet_diag_dump_data *cb_data = cb->data;
51d7cccf0   Andrey Vagin   net: make sock di...
957
  	struct net *net = sock_net(skb->sk);
079096f10   Eric Dumazet   tcp/dccp: install...
958
  	u32 idiag_states = r->idiag_states;
67db3e4bf   Eric Dumazet   tcp: no longer ho...
959
  	int i, num, s_i, s_num;
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
960
  	struct nlattr *bc;
67db3e4bf   Eric Dumazet   tcp: no longer ho...
961
  	struct sock *sk;
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
962

0df6d3284   Martin KaFai Lau   inet_diag: Move t...
963
  	bc = cb_data->inet_diag_nla_bc;
079096f10   Eric Dumazet   tcp/dccp: install...
964
965
  	if (idiag_states & TCPF_SYN_RECV)
  		idiag_states |= TCPF_NEW_SYN_RECV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
967
  	s_i = cb->args[1];
  	s_num = num = cb->args[2];
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
968

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
  	if (cb->args[0] == 0) {
9652dc2eb   Eric Dumazet   tcp: relax listen...
970
  		if (!(idiag_states & TCPF_LISTEN) || r->id.idiag_dport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
  			goto skip_listen_ht;
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
972

0f7ff9274   Arnaldo Carvalho de Melo   [INET]: Just rena...
973
  		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
5caea4ea7   Eric Dumazet   net: listening_ha...
974
  			struct inet_listen_hashbucket *ilb;
8dbd76e79   Eric Dumazet   tcp/dccp: fix pos...
975
  			struct hlist_nulls_node *node;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
976
977
  
  			num = 0;
5caea4ea7   Eric Dumazet   net: listening_ha...
978
  			ilb = &hashinfo->listening_hash[i];
9652dc2eb   Eric Dumazet   tcp: relax listen...
979
  			spin_lock(&ilb->lock);
8dbd76e79   Eric Dumazet   tcp/dccp: fix pos...
980
  			sk_nulls_for_each(sk, node, &ilb->nulls_head) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
  				struct inet_sock *inet = inet_sk(sk);
51d7cccf0   Andrey Vagin   net: make sock di...
982
983
  				if (!net_eq(sock_net(sk), net))
  					continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984
985
986
987
  				if (num < s_num) {
  					num++;
  					continue;
  				}
d23deaa07   Pavel Emelyanov   inet_diag: Introd...
988
  				if (r->sdiag_family != AF_UNSPEC &&
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
989
  				    sk->sk_family != r->sdiag_family)
d23deaa07   Pavel Emelyanov   inet_diag: Introd...
990
  					goto next_listen;
c720c7e83   Eric Dumazet   inet: rename some...
991
  				if (r->id.idiag_sport != inet->inet_sport &&
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
992
  				    r->id.idiag_sport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
993
  					goto next_listen;
5682d393b   Martin KaFai Lau   inet_diag: Refact...
994
995
996
997
998
999
  				if (!inet_diag_bc_sk(bc, sk))
  					goto next_listen;
  
  				if (inet_sk_diag_fill(sk, inet_csk(sk), skb,
  						      cb, r, NLM_F_MULTI,
  						      net_admin) < 0) {
9652dc2eb   Eric Dumazet   tcp: relax listen...
1000
  					spin_unlock(&ilb->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
1002
1003
1004
  					goto done;
  				}
  
  next_listen:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
1006
  				++num;
  			}
9652dc2eb   Eric Dumazet   tcp: relax listen...
1007
  			spin_unlock(&ilb->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
1009
  
  			s_num = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1010
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011
1012
1013
1014
  skip_listen_ht:
  		cb->args[0] = 1;
  		s_i = num = s_num = 0;
  	}
079096f10   Eric Dumazet   tcp/dccp: install...
1015
  	if (!(idiag_states & ~TCPF_LISTEN))
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
1016
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1017

67db3e4bf   Eric Dumazet   tcp: no longer ho...
1018
  #define SKARR_SZ 16
f373b53b5   Eric Dumazet   tcp: replace ehas...
1019
  	for (i = s_i; i <= hashinfo->ehash_mask; i++) {
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
1020
  		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
7e3aab4a9   David S. Miller   inet_diag: Missed...
1021
  		spinlock_t *lock = inet_ehash_lockp(hashinfo, i);
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
1022
  		struct hlist_nulls_node *node;
67db3e4bf   Eric Dumazet   tcp: no longer ho...
1023
1024
1025
  		struct sock *sk_arr[SKARR_SZ];
  		int num_arr[SKARR_SZ];
  		int idx, accum, res;
6be547a61   Andi Kleen   inet_diag: Add em...
1026

05dbc7b59   Eric Dumazet   tcp/dccp: remove ...
1027
  		if (hlist_nulls_empty(&head->chain))
6be547a61   Andi Kleen   inet_diag: Add em...
1028
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
1030
  		if (i > s_i)
  			s_num = 0;
67db3e4bf   Eric Dumazet   tcp: no longer ho...
1031
1032
1033
  next_chunk:
  		num = 0;
  		accum = 0;
7e3aab4a9   David S. Miller   inet_diag: Missed...
1034
  		spin_lock_bh(lock);
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
1035
  		sk_nulls_for_each(sk, node, &head->chain) {
67db3e4bf   Eric Dumazet   tcp: no longer ho...
1036
  			int state;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1037

51d7cccf0   Andrey Vagin   net: make sock di...
1038
1039
  			if (!net_eq(sock_net(sk), net))
  				continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1040
1041
  			if (num < s_num)
  				goto next_normal;
70315d22d   Neal Cardwell   inet_diag: fix in...
1042
1043
  			state = (sk->sk_state == TCP_TIME_WAIT) ?
  				inet_twsk(sk)->tw_substate : sk->sk_state;
079096f10   Eric Dumazet   tcp/dccp: install...
1044
  			if (!(idiag_states & (1 << state)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
  				goto next_normal;
d23deaa07   Pavel Emelyanov   inet_diag: Introd...
1046
  			if (r->sdiag_family != AF_UNSPEC &&
05dbc7b59   Eric Dumazet   tcp/dccp: remove ...
1047
  			    sk->sk_family != r->sdiag_family)
d23deaa07   Pavel Emelyanov   inet_diag: Introd...
1048
  				goto next_normal;
05dbc7b59   Eric Dumazet   tcp/dccp: remove ...
1049
  			if (r->id.idiag_sport != htons(sk->sk_num) &&
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
1050
  			    r->id.idiag_sport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
  				goto next_normal;
05dbc7b59   Eric Dumazet   tcp/dccp: remove ...
1052
  			if (r->id.idiag_dport != sk->sk_dport &&
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
1053
  			    r->id.idiag_dport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054
  				goto next_normal;
a58917f58   Eric Dumazet   inet_diag: allow ...
1055
1056
1057
1058
  			twsk_build_assert();
  
  			if (!inet_diag_bc_sk(bc, sk))
  				goto next_normal;
f0c928d87   Eric Dumazet   tcp: fix a race i...
1059
1060
  			if (!refcount_inc_not_zero(&sk->sk_refcnt))
  				goto next_normal;
67db3e4bf   Eric Dumazet   tcp: no longer ho...
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
  			num_arr[accum] = num;
  			sk_arr[accum] = sk;
  			if (++accum == SKARR_SZ)
  				break;
  next_normal:
  			++num;
  		}
  		spin_unlock_bh(lock);
  		res = 0;
  		for (idx = 0; idx < accum; idx++) {
  			if (res >= 0) {
5682d393b   Martin KaFai Lau   inet_diag: Refact...
1072
1073
  				res = sk_diag_fill(sk_arr[idx], skb, cb, r,
  						   NLM_F_MULTI, net_admin);
67db3e4bf   Eric Dumazet   tcp: no longer ho...
1074
1075
  				if (res < 0)
  					num = num_arr[idx];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
  			}
67db3e4bf   Eric Dumazet   tcp: no longer ho...
1077
  			sock_gen_put(sk_arr[idx]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
  		}
67db3e4bf   Eric Dumazet   tcp: no longer ho...
1079
1080
  		if (res < 0)
  			break;
acffb584c   Eric Dumazet   net: diag: add a ...
1081
  		cond_resched();
67db3e4bf   Eric Dumazet   tcp: no longer ho...
1082
1083
1084
1085
  		if (accum == SKARR_SZ) {
  			s_num = num + 1;
  			goto next_chunk;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
1087
1088
1089
1090
  	}
  
  done:
  	cb->args[1] = i;
  	cb->args[2] = num;
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
1091
1092
1093
  out:
  	;
  }
1942c518c   Pavel Emelyanov   inet_diag: Genera...
1094
  EXPORT_SYMBOL_GPL(inet_diag_dump_icsk);
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
1095
1096
  
  static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1097
  			    const struct inet_diag_req_v2 *r)
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
1098
  {
3f935c75e   Paolo Abeni   inet_diag: suppor...
1099
  	struct inet_diag_dump_data *cb_data = cb->data;
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
1100
  	const struct inet_diag_handler *handler;
085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
1101
  	u32 prev_min_dump_alloc;
3f935c75e   Paolo Abeni   inet_diag: suppor...
1102
1103
1104
  	int protocol, err = 0;
  
  	protocol = inet_diag_get_protocol(r, cb_data);
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
1105

085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
1106
1107
  again:
  	prev_min_dump_alloc = cb->min_dump_alloc;
3f935c75e   Paolo Abeni   inet_diag: suppor...
1108
  	handler = inet_diag_lock_handler(protocol);
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
1109
  	if (!IS_ERR(handler))
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1110
  		handler->dump(skb, cb, r);
cacb6ba0f   Cyrill Gorcunov   net: inet_diag --...
1111
1112
  	else
  		err = PTR_ERR(handler);
d523a328f   Herbert Xu   [INET]: Fix inet_...
1113
  	inet_diag_unlock_handler(handler);
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
1114

085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
1115
1116
1117
1118
1119
1120
1121
1122
  	/* The skb is not large enough to fit one sk info and
  	 * inet_sk_diag_fill() has requested for a larger skb.
  	 */
  	if (!skb->len && cb->min_dump_alloc > prev_min_dump_alloc) {
  		err = pskb_expand_head(skb, 0, cb->min_dump_alloc, GFP_KERNEL);
  		if (!err)
  			goto again;
  	}
cacb6ba0f   Cyrill Gorcunov   net: inet_diag --...
1123
  	return err ? : skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1124
  }
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
1125
1126
  static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
  {
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1127
1128
1129
1130
1131
1132
1133
1134
1135
  	return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh));
  }
  
  static int __inet_diag_dump_start(struct netlink_callback *cb, int hdrlen)
  {
  	const struct nlmsghdr *nlh = cb->nlh;
  	struct inet_diag_dump_data *cb_data;
  	struct sk_buff *skb = cb->skb;
  	struct nlattr *nla;
3f935c75e   Paolo Abeni   inet_diag: suppor...
1136
  	int err;
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1137
1138
1139
1140
  
  	cb_data = kzalloc(sizeof(*cb_data), GFP_KERNEL);
  	if (!cb_data)
  		return -ENOMEM;
d5e4d0a5e   Eric Dumazet   inet_diag: valida...
1141
1142
1143
1144
1145
  	err = inet_diag_parse_attrs(nlh, hdrlen, cb_data->req_nlas);
  	if (err) {
  		kfree(cb_data);
  		return err;
  	}
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1146
1147
1148
1149
1150
1151
1152
1153
  	nla = cb_data->inet_diag_nla_bc;
  	if (nla) {
  		err = inet_diag_bc_audit(nla, skb);
  		if (err) {
  			kfree(cb_data);
  			return err;
  		}
  	}
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
1154

085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
  	nla = cb_data->inet_diag_nla_bpf_stgs;
  	if (nla) {
  		struct bpf_sk_storage_diag *bpf_stg_diag;
  
  		bpf_stg_diag = bpf_sk_storage_diag_alloc(nla);
  		if (IS_ERR(bpf_stg_diag)) {
  			kfree(cb_data);
  			return PTR_ERR(bpf_stg_diag);
  		}
  		cb_data->bpf_stg_diag = bpf_stg_diag;
  	}
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1166
1167
1168
  	cb->data = cb_data;
  	return 0;
  }
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
1169

0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
  static int inet_diag_dump_start(struct netlink_callback *cb)
  {
  	return __inet_diag_dump_start(cb, sizeof(struct inet_diag_req_v2));
  }
  
  static int inet_diag_dump_start_compat(struct netlink_callback *cb)
  {
  	return __inet_diag_dump_start(cb, sizeof(struct inet_diag_req));
  }
  
  static int inet_diag_dump_done(struct netlink_callback *cb)
  {
085c20cac   Martin KaFai Lau   bpf: inet_diag: D...
1182
1183
1184
  	struct inet_diag_dump_data *cb_data = cb->data;
  
  	bpf_sk_storage_diag_free(cb_data->bpf_stg_diag);
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1185
1186
1187
  	kfree(cb->data);
  
  	return 0;
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
1188
  }
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
1189
  static int inet_diag_type2proto(int type)
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
  {
  	switch (type) {
  	case TCPDIAG_GETSOCK:
  		return IPPROTO_TCP;
  	case DCCPDIAG_GETSOCK:
  		return IPPROTO_DCCP;
  	default:
  		return 0;
  	}
  }
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
1200
1201
  static int inet_diag_dump_compat(struct sk_buff *skb,
  				 struct netlink_callback *cb)
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
1202
  {
d106352d9   David S. Miller   inet_diag: Move a...
1203
  	struct inet_diag_req *rc = nlmsg_data(cb->nlh);
c8991362a   Pavel Emelyanov   inet_diag: Rename...
1204
  	struct inet_diag_req_v2 req;
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
1205

d23deaa07   Pavel Emelyanov   inet_diag: Introd...
1206
  	req.sdiag_family = AF_UNSPEC; /* compatibility */
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
1207
1208
1209
1210
  	req.sdiag_protocol = inet_diag_type2proto(cb->nlh->nlmsg_type);
  	req.idiag_ext = rc->idiag_ext;
  	req.idiag_states = rc->idiag_states;
  	req.id = rc->id;
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1211
  	return __inet_diag_dump(skb, cb, &req);
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
1212
  }
fe50ce284   Pavel Emelyanov   inet_diag: Switch...
1213
  static int inet_diag_get_exact_compat(struct sk_buff *in_skb,
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
1214
  				      const struct nlmsghdr *nlh)
fe50ce284   Pavel Emelyanov   inet_diag: Switch...
1215
  {
d106352d9   David S. Miller   inet_diag: Move a...
1216
  	struct inet_diag_req *rc = nlmsg_data(nlh);
c8991362a   Pavel Emelyanov   inet_diag: Rename...
1217
  	struct inet_diag_req_v2 req;
fe50ce284   Pavel Emelyanov   inet_diag: Switch...
1218
1219
1220
1221
1222
1223
  
  	req.sdiag_family = rc->idiag_family;
  	req.sdiag_protocol = inet_diag_type2proto(nlh->nlmsg_type);
  	req.idiag_ext = rc->idiag_ext;
  	req.idiag_states = rc->idiag_states;
  	req.id = rc->id;
3f935c75e   Paolo Abeni   inet_diag: suppor...
1224
1225
  	return inet_diag_cmd_exact(SOCK_DIAG_BY_FAMILY, in_skb, nlh,
  				   sizeof(struct inet_diag_req), &req);
fe50ce284   Pavel Emelyanov   inet_diag: Switch...
1226
  }
8d34172df   Pavel Emelyanov   sock_diag: Introd...
1227
  static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1228
  {
3b09c84cb   Pavel Emelyanov   inet_diag: Rename...
1229
  	int hdrlen = sizeof(struct inet_diag_req);
51d7cccf0   Andrey Vagin   net: make sock di...
1230
  	struct net *net = sock_net(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1231

ead592ba2   Thomas Graf   [IPv4] diag: Use ...
1232
1233
1234
  	if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
  	    nlmsg_len(nlh) < hdrlen)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1235

b8f3ab429   David S. Miller   Revert "netlink: ...
1236
  	if (nlh->nlmsg_flags & NLM_F_DUMP) {
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1237
1238
1239
1240
1241
1242
  		struct netlink_dump_control c = {
  			.start = inet_diag_dump_start_compat,
  			.done = inet_diag_dump_done,
  			.dump = inet_diag_dump_compat,
  		};
  		return netlink_dump_start(net->diag_nlsk, skb, nlh, &c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
  	}
ead592ba2   Thomas Graf   [IPv4] diag: Use ...
1244

fe50ce284   Pavel Emelyanov   inet_diag: Switch...
1245
  	return inet_diag_get_exact_compat(skb, nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1246
  }
6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
1247
  static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h)
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1248
  {
c8991362a   Pavel Emelyanov   inet_diag: Rename...
1249
  	int hdrlen = sizeof(struct inet_diag_req_v2);
51d7cccf0   Andrey Vagin   net: make sock di...
1250
  	struct net *net = sock_net(skb->sk);
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1251
1252
1253
  
  	if (nlmsg_len(h) < hdrlen)
  		return -EINVAL;
6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
1254
1255
  	if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY &&
  	    h->nlmsg_flags & NLM_F_DUMP) {
0df6d3284   Martin KaFai Lau   inet_diag: Move t...
1256
1257
1258
1259
1260
1261
  		struct netlink_dump_control c = {
  			.start = inet_diag_dump_start,
  			.done = inet_diag_dump_done,
  			.dump = inet_diag_dump,
  		};
  		return netlink_dump_start(net->diag_nlsk, skb, h, &c);
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1262
  	}
3f935c75e   Paolo Abeni   inet_diag: suppor...
1263
1264
  	return inet_diag_cmd_exact(h->nlmsg_type, skb, h, hdrlen,
  				   nlmsg_data(h));
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1265
  }
35ac838a9   Craig Gallek   sock_diag: implem...
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
  static
  int inet_diag_handler_get_info(struct sk_buff *skb, struct sock *sk)
  {
  	const struct inet_diag_handler *handler;
  	struct nlmsghdr *nlh;
  	struct nlattr *attr;
  	struct inet_diag_msg *r;
  	void *info = NULL;
  	int err = 0;
  
  	nlh = nlmsg_put(skb, 0, 0, SOCK_DIAG_BY_FAMILY, sizeof(*r), 0);
  	if (!nlh)
  		return -ENOMEM;
  
  	r = nlmsg_data(nlh);
  	memset(r, 0, sizeof(*r));
  	inet_diag_msg_common_fill(r, sk);
e0df02e0c   Craig Gallek   sock_diag: fetch ...
1283
1284
  	if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_STREAM)
  		r->id.idiag_sport = inet_sk(sk)->inet_sport;
35ac838a9   Craig Gallek   sock_diag: implem...
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
  	r->idiag_state = sk->sk_state;
  
  	if ((err = nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol))) {
  		nlmsg_cancel(skb, nlh);
  		return err;
  	}
  
  	handler = inet_diag_lock_handler(sk->sk_protocol);
  	if (IS_ERR(handler)) {
  		inet_diag_unlock_handler(handler);
  		nlmsg_cancel(skb, nlh);
  		return PTR_ERR(handler);
  	}
  
  	attr = handler->idiag_info_size
6ed46d124   Nicolas Dichtel   sock_diag: align ...
1300
1301
1302
  		? nla_reserve_64bit(skb, INET_DIAG_INFO,
  				    handler->idiag_info_size,
  				    INET_DIAG_PAD)
35ac838a9   Craig Gallek   sock_diag: implem...
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
  		: NULL;
  	if (attr)
  		info = nla_data(attr);
  
  	handler->idiag_get_info(sk, r, info);
  	inet_diag_unlock_handler(handler);
  
  	nlmsg_end(skb, nlh);
  	return 0;
  }
8dcf01fc0   Shan Wei   net: sock_diag_ha...
1313
  static const struct sock_diag_handler inet_diag_handler = {
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1314
  	.family = AF_INET,
6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
1315
  	.dump = inet_diag_handler_cmd,
35ac838a9   Craig Gallek   sock_diag: implem...
1316
  	.get_info = inet_diag_handler_get_info,
6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
1317
  	.destroy = inet_diag_handler_cmd,
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1318
  };
8dcf01fc0   Shan Wei   net: sock_diag_ha...
1319
  static const struct sock_diag_handler inet6_diag_handler = {
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1320
  	.family = AF_INET6,
6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
1321
  	.dump = inet_diag_handler_cmd,
35ac838a9   Craig Gallek   sock_diag: implem...
1322
  	.get_info = inet_diag_handler_get_info,
6eb5d2e08   Lorenzo Colitti   net: diag: Suppor...
1323
  	.destroy = inet_diag_handler_cmd,
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1324
  };
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1325
1326
1327
1328
  int inet_diag_register(const struct inet_diag_handler *h)
  {
  	const __u16 type = h->idiag_type;
  	int err = -EINVAL;
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
1329
  	if (type >= IPPROTO_MAX)
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1330
  		goto out;
d523a328f   Herbert Xu   [INET]: Fix inet_...
1331
  	mutex_lock(&inet_diag_table_mutex);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1332
  	err = -EEXIST;
e31c5e0e4   Eric Dumazet   inet_diag: cleanups
1333
  	if (!inet_diag_table[type]) {
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1334
1335
1336
  		inet_diag_table[type] = h;
  		err = 0;
  	}
d523a328f   Herbert Xu   [INET]: Fix inet_...
1337
  	mutex_unlock(&inet_diag_table_mutex);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1338
1339
1340
1341
1342
1343
1344
1345
  out:
  	return err;
  }
  EXPORT_SYMBOL_GPL(inet_diag_register);
  
  void inet_diag_unregister(const struct inet_diag_handler *h)
  {
  	const __u16 type = h->idiag_type;
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
1346
  	if (type >= IPPROTO_MAX)
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1347
  		return;
d523a328f   Herbert Xu   [INET]: Fix inet_...
1348
  	mutex_lock(&inet_diag_table_mutex);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1349
  	inet_diag_table[type] = NULL;
d523a328f   Herbert Xu   [INET]: Fix inet_...
1350
  	mutex_unlock(&inet_diag_table_mutex);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1351
1352
  }
  EXPORT_SYMBOL_GPL(inet_diag_unregister);
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
1353
  static int __init inet_diag_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1354
  {
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
1355
  	const int inet_diag_table_size = (IPPROTO_MAX *
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1356
1357
  					  sizeof(struct inet_diag_handler *));
  	int err = -ENOMEM;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
1358
  	inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1359
1360
  	if (!inet_diag_table)
  		goto out;
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1361
1362
1363
1364
1365
1366
1367
  	err = sock_diag_register(&inet_diag_handler);
  	if (err)
  		goto out_free_nl;
  
  	err = sock_diag_register(&inet6_diag_handler);
  	if (err)
  		goto out_free_inet;
8ef874bfc   Pavel Emelyanov   sock_diag: Move t...
1368
  	sock_diag_register_inet_compat(inet_diag_rcv_msg_compat);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1369
1370
  out:
  	return err;
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1371
1372
1373
1374
  
  out_free_inet:
  	sock_diag_unregister(&inet_diag_handler);
  out_free_nl:
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1375
1376
  	kfree(inet_diag_table);
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1377
  }
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
1378
  static void __exit inet_diag_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1379
  {
d366477a5   Pavel Emelyanov   sock_diag: Initia...
1380
1381
  	sock_diag_unregister(&inet6_diag_handler);
  	sock_diag_unregister(&inet_diag_handler);
8ef874bfc   Pavel Emelyanov   sock_diag: Move t...
1382
  	sock_diag_unregister_inet_compat(inet_diag_rcv_msg_compat);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1383
  	kfree(inet_diag_table);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1384
  }
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
1385
1386
  module_init(inet_diag_init);
  module_exit(inet_diag_exit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1387
  MODULE_LICENSE("GPL");
aec8dc62f   Pavel Emelyanov   sock_diag: Fix mo...
1388
1389
  MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2 /* AF_INET */);
  MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10 /* AF_INET6 */);