Blame view

net/ipv4/inet_diag.c 25.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
2
   * inet_diag.c	Module for monitoring INET transport protocols sockets.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   *
   *	This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
   *      as published by the Free Software Foundation; either version
   *      2 of the License, or (at your option) any later version.
   */
172589ccd   Ilpo Järvinen   [NET]: DIV_ROUND_...
11
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/fcntl.h>
  #include <linux/random.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
24
  #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...
25
26
27
28
  #include <net/inet_connection_sock.h>
  #include <net/inet_hashtables.h>
  #include <net/inet_timewait_sock.h>
  #include <net/inet6_hashtables.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
29
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
  
  #include <linux/inet.h>
  #include <linux/stddef.h>
a8c2190ee   Arnaldo Carvalho de Melo   [INET_DIAG]: Rena...
33
  #include <linux/inet_diag.h>
d366477a5   Pavel Emelyanov   sock_diag: Initia...
34
  #include <linux/sock_diag.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35

4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
36
  static const struct inet_diag_handler **inet_diag_table;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
37
  struct inet_diag_entry {
9f8552996   Al Viro   [IPV4]: inet_diag...
38
39
  	__be32 *saddr;
  	__be32 *daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
  	u16 sport;
  	u16 dport;
  	u16 family;
  	u16 userlocks;
  };
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
45
  #define INET_DIAG_PUT(skb, attrtype, attrlen) \
7c99c909f   Stephen Hemminger   [TCP]: Change tcp...
46
  	RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47

d523a328f   Herbert Xu   [INET]: Fix inet_...
48
  static DEFINE_MUTEX(inet_diag_table_mutex);
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
49
  static const struct inet_diag_handler *inet_diag_lock_handler(int proto)
d523a328f   Herbert Xu   [INET]: Fix inet_...
50
  {
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
51
  	if (!inet_diag_table[proto])
aec8dc62f   Pavel Emelyanov   sock_diag: Fix mo...
52
53
  		request_module("net-pf-%d-proto-%d-type-%d-%d", PF_NETLINK,
  			       NETLINK_SOCK_DIAG, AF_INET, proto);
d523a328f   Herbert Xu   [INET]: Fix inet_...
54
55
  
  	mutex_lock(&inet_diag_table_mutex);
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
56
  	if (!inet_diag_table[proto])
d523a328f   Herbert Xu   [INET]: Fix inet_...
57
  		return ERR_PTR(-ENOENT);
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
58
  	return inet_diag_table[proto];
d523a328f   Herbert Xu   [INET]: Fix inet_...
59
60
61
62
63
64
65
  }
  
  static inline void inet_diag_unlock_handler(
  	const struct inet_diag_handler *handler)
  {
  	mutex_unlock(&inet_diag_table_mutex);
  }
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
66
  int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
67
  			      struct sk_buff *skb, struct inet_diag_req_v2 *req,
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
68
  			      u32 pid, u32 seq, u16 nlmsg_flags,
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
69
  			      const struct nlmsghdr *unlh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  {
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
71
  	const struct inet_sock *inet = inet_sk(sk);
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
72
  	struct inet_diag_msg *r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  	struct nlmsghdr  *nlh;
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
74
  	void *info = NULL;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
75
  	struct inet_diag_meminfo  *minfo = NULL;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
76
  	unsigned char	 *b = skb_tail_pointer(skb);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
77
  	const struct inet_diag_handler *handler;
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
78
  	int ext = req->idiag_ext;
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
79

a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
80
  	handler = inet_diag_table[req->sdiag_protocol];
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
81
  	BUG_ON(handler == NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82

540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
83
  	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  	nlh->nlmsg_flags = nlmsg_flags;
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
85

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  	r = NLMSG_DATA(nlh);
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
87
88
89
90
  	BUG_ON(sk->sk_state == TCP_TIME_WAIT);
  
  	if (ext & (1 << (INET_DIAG_MEMINFO - 1)))
  		minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO, sizeof(*minfo));
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
91
92
93
94
  	r->idiag_family = sk->sk_family;
  	r->idiag_state = sk->sk_state;
  	r->idiag_timer = 0;
  	r->idiag_retrans = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95

73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
96
  	r->id.idiag_if = sk->sk_bound_dev_if;
f65c1b534   Pavel Emelyanov   sock_diag: Genera...
97
  	sock_diag_save_cookie(sk, r->id.idiag_cookie);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98

c720c7e83   Eric Dumazet   inet: rename some...
99
100
101
102
  	r->id.idiag_sport = inet->inet_sport;
  	r->id.idiag_dport = inet->inet_dport;
  	r->id.idiag_src[0] = inet->inet_rcv_saddr;
  	r->id.idiag_dst[0] = inet->inet_daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103

717b6d836   Maciej Żenczykowski   net-netlink: fix ...
104
105
106
107
108
  	/* 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)))
  		RTA_PUT_U8(skb, INET_DIAG_TOS, inet->tos);
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
109
  #if IS_ENABLED(CONFIG_IPV6)
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
110
  	if (r->idiag_family == AF_INET6) {
b71d1d426   Eric Dumazet   inet: constify ip...
111
  		const struct ipv6_pinfo *np = inet6_sk(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112

4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
113
114
  		*(struct in6_addr *)r->id.idiag_src = np->rcv_saddr;
  		*(struct in6_addr *)r->id.idiag_dst = np->daddr;
06236ac37   Maciej Żenczykowski   net-netlink: Add ...
115
116
  		if (ext & (1 << (INET_DIAG_TCLASS - 1)))
  			RTA_PUT_U8(skb, INET_DIAG_TCLASS, np->tclass);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
  	}
  #endif
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
119
120
121
122
123
124
125
126
127
  	r->idiag_uid = sock_i_uid(sk);
  	r->idiag_inode = sock_i_ino(sk);
  
  	if (minfo) {
  		minfo->idiag_rmem = sk_rmem_alloc_get(sk);
  		minfo->idiag_wmem = sk->sk_wmem_queued;
  		minfo->idiag_fmem = sk->sk_forward_alloc;
  		minfo->idiag_tmem = sk_wmem_alloc_get(sk);
  	}
c0636faa5   Pavel Emelyanov   inet_diag: Add th...
128
129
130
  	if (ext & (1 << (INET_DIAG_SKMEMINFO - 1)))
  		if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO))
  			goto rtattr_failure;
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
131
132
133
134
  	if (icsk == NULL) {
  		r->idiag_rqueue = r->idiag_wqueue = 0;
  		goto out;
  	}
172589ccd   Ilpo Järvinen   [NET]: DIV_ROUND_...
135
  #define EXPIRES_IN_MS(tmo)  DIV_ROUND_UP((tmo - jiffies) * 1000, HZ)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
137
  	if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
138
139
140
  		r->idiag_timer = 1;
  		r->idiag_retrans = icsk->icsk_retransmits;
  		r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
141
  	} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
142
143
144
  		r->idiag_timer = 4;
  		r->idiag_retrans = icsk->icsk_probes_out;
  		r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  	} else if (timer_pending(&sk->sk_timer)) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
146
147
148
  		r->idiag_timer = 2;
  		r->idiag_retrans = icsk->icsk_probes_out;
  		r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  	} else {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
150
151
  		r->idiag_timer = 0;
  		r->idiag_expires = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
  	}
  #undef EXPIRES_IN_MS
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
154

3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
155
156
  	if (ext & (1 << (INET_DIAG_INFO - 1)))
  		info = INET_DIAG_PUT(skb, INET_DIAG_INFO, sizeof(struct tcp_info));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157

3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
158
159
160
161
162
  	if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) {
  		const size_t len = strlen(icsk->icsk_ca_ops->name);
  
  		strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1),
  		       icsk->icsk_ca_ops->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  	}
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
164
  	handler->idiag_get_info(sk, r, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165

6687e988d   Arnaldo Carvalho de Melo   [ICSK]: Move TCP ...
166
167
168
  	if (sk->sk_state < TCP_TIME_WAIT &&
  	    icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info)
  		icsk->icsk_ca_ops->get_info(sk, ext, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169

3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
170
  out:
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
171
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  	return skb->len;
7c99c909f   Stephen Hemminger   [TCP]: Change tcp...
173
  rtattr_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  nlmsg_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
175
  	nlmsg_trim(skb, b);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
176
  	return -EMSGSIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  }
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
178
179
180
  EXPORT_SYMBOL_GPL(inet_sk_diag_fill);
  
  static int inet_csk_diag_fill(struct sock *sk,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
181
  			      struct sk_buff *skb, struct inet_diag_req_v2 *req,
3c4d05c80   Pavel Emelyanov   inet_diag: Introd...
182
183
184
185
186
187
  			      u32 pid, u32 seq, u16 nlmsg_flags,
  			      const struct nlmsghdr *unlh)
  {
  	return inet_sk_diag_fill(sk, inet_csk(sk),
  			skb, req, pid, seq, nlmsg_flags, unlh);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188

c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
189
  static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
190
  			       struct sk_buff *skb, struct inet_diag_req_v2 *req,
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
191
  			       u32 pid, u32 seq, u16 nlmsg_flags,
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
192
193
194
195
  			       const struct nlmsghdr *unlh)
  {
  	long tmo;
  	struct inet_diag_msg *r;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
196
  	const unsigned char *previous_tail = skb_tail_pointer(skb);
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
197
198
199
200
201
202
203
204
205
206
207
208
209
  	struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq,
  					 unlh->nlmsg_type, sizeof(*r));
  
  	r = NLMSG_DATA(nlh);
  	BUG_ON(tw->tw_state != TCP_TIME_WAIT);
  
  	nlh->nlmsg_flags = nlmsg_flags;
  
  	tmo = tw->tw_ttd - jiffies;
  	if (tmo < 0)
  		tmo = 0;
  
  	r->idiag_family	      = tw->tw_family;
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
210
211
  	r->idiag_retrans      = 0;
  	r->id.idiag_if	      = tw->tw_bound_dev_if;
f65c1b534   Pavel Emelyanov   sock_diag: Genera...
212
  	sock_diag_save_cookie(tw, r->id.idiag_cookie);
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
213
214
215
216
217
218
  	r->id.idiag_sport     = tw->tw_sport;
  	r->id.idiag_dport     = tw->tw_dport;
  	r->id.idiag_src[0]    = tw->tw_rcv_saddr;
  	r->id.idiag_dst[0]    = tw->tw_daddr;
  	r->idiag_state	      = tw->tw_substate;
  	r->idiag_timer	      = 3;
172589ccd   Ilpo Järvinen   [NET]: DIV_ROUND_...
219
  	r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
220
221
222
223
  	r->idiag_rqueue	      = 0;
  	r->idiag_wqueue	      = 0;
  	r->idiag_uid	      = 0;
  	r->idiag_inode	      = 0;
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
224
  #if IS_ENABLED(CONFIG_IPV6)
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
225
226
227
  	if (tw->tw_family == AF_INET6) {
  		const struct inet6_timewait_sock *tw6 =
  						inet6_twsk((struct sock *)tw);
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
228
229
  		*(struct in6_addr *)r->id.idiag_src = tw6->tw_v6_rcv_saddr;
  		*(struct in6_addr *)r->id.idiag_dst = tw6->tw_v6_daddr;
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
230
231
  	}
  #endif
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
232
  	nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail;
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
233
234
  	return skb->len;
  nlmsg_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
235
  	nlmsg_trim(skb, previous_tail);
26932566a   Patrick McHardy   [NETLINK]: Don't ...
236
  	return -EMSGSIZE;
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
237
  }
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
238
  static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
239
  			struct inet_diag_req_v2 *r, u32 pid, u32 seq, u16 nlmsg_flags,
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
240
241
242
243
  			const struct nlmsghdr *unlh)
  {
  	if (sk->sk_state == TCP_TIME_WAIT)
  		return inet_twsk_diag_fill((struct inet_timewait_sock *)sk,
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
244
  					   skb, r, pid, seq, nlmsg_flags,
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
245
  					   unlh);
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
246
  	return inet_csk_diag_fill(sk, skb, r, pid, seq, nlmsg_flags, unlh);
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
247
  }
1942c518c   Pavel Emelyanov   inet_diag: Genera...
248
  int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
249
  		const struct nlmsghdr *nlh, struct inet_diag_req_v2 *req)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
  {
  	int err;
  	struct sock *sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  	struct sk_buff *rep;
d523a328f   Herbert Xu   [INET]: Fix inet_...
254

d523a328f   Herbert Xu   [INET]: Fix inet_...
255
  	err = -EINVAL;
fe50ce284   Pavel Emelyanov   inet_diag: Switch...
256
  	if (req->sdiag_family == AF_INET) {
c67499c0e   Pavel Emelyanov   [NETNS]: Tcp-v4 s...
257
  		sk = inet_lookup(&init_net, hashinfo, req->id.idiag_dst[0],
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
258
259
  				 req->id.idiag_dport, req->id.idiag_src[0],
  				 req->id.idiag_sport, req->id.idiag_if);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  	}
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
261
  #if IS_ENABLED(CONFIG_IPV6)
fe50ce284   Pavel Emelyanov   inet_diag: Switch...
262
  	else if (req->sdiag_family == AF_INET6) {
d86e0dac2   Pavel Emelyanov   [NETNS]: Tcp-v6 s...
263
  		sk = inet6_lookup(&init_net, hashinfo,
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
264
265
266
267
268
  				  (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
269
270
271
  	}
  #endif
  	else {
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
272
  		goto out_nosk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  	}
d523a328f   Herbert Xu   [INET]: Fix inet_...
274
  	err = -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  	if (sk == NULL)
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
276
  		goto out_nosk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277

f65c1b534   Pavel Emelyanov   sock_diag: Genera...
278
  	err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
b005ab4ef   Pavel Emelyanov   inet_diag: Export...
279
  	if (err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
  		goto out;
  
  	err = -ENOMEM;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
283
284
  	rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) +
  				     sizeof(struct inet_diag_meminfo) +
7b35eadd7   Pavel Emelyanov   inet_diag: Remove...
285
  				     sizeof(struct tcp_info) + 64)),
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
286
  			GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
  	if (!rep)
  		goto out;
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
289
  	err = sk_diag_fill(sk, rep, req,
26932566a   Patrick McHardy   [NETLINK]: Don't ...
290
291
292
293
294
295
296
  			   NETLINK_CB(in_skb).pid,
  			   nlh->nlmsg_seq, 0, nlh);
  	if (err < 0) {
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(rep);
  		goto out;
  	}
8ef874bfc   Pavel Emelyanov   sock_diag: Move t...
297
  	err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid,
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
298
  			      MSG_DONTWAIT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
302
303
304
  	if (err > 0)
  		err = 0;
  
  out:
  	if (sk) {
  		if (sk->sk_state == TCP_TIME_WAIT)
8feaf0c0a   Arnaldo Carvalho de Melo   [INET]: Generalis...
305
  			inet_twsk_put((struct inet_timewait_sock *)sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
  		else
  			sock_put(sk);
  	}
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
309
310
311
  out_nosk:
  	return err;
  }
1942c518c   Pavel Emelyanov   inet_diag: Genera...
312
  EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk);
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
313
314
315
  
  static int inet_diag_get_exact(struct sk_buff *in_skb,
  			       const struct nlmsghdr *nlh,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
316
  			       struct inet_diag_req_v2 *req)
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
317
318
319
320
321
322
323
324
  {
  	const struct inet_diag_handler *handler;
  	int err;
  
  	handler = inet_diag_lock_handler(req->sdiag_protocol);
  	if (IS_ERR(handler))
  		err = PTR_ERR(handler);
  	else
1942c518c   Pavel Emelyanov   inet_diag: Genera...
325
  		err = handler->dump_one(in_skb, nlh, req);
d523a328f   Herbert Xu   [INET]: Fix inet_...
326
  	inet_diag_unlock_handler(handler);
476f7dbff   Pavel Emelyanov   inet_diag: Split ...
327

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
  	return err;
  }
9f8552996   Al Viro   [IPV4]: inet_diag...
330
  static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
334
335
336
337
338
339
340
  {
  	int words = bits >> 5;
  
  	bits &= 0x1f;
  
  	if (words) {
  		if (memcmp(a1, a2, words << 2))
  			return 0;
  	}
  	if (bits) {
9f8552996   Al Viro   [IPV4]: inet_diag...
341
342
  		__be32 w1, w2;
  		__be32 mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
349
350
351
352
353
354
  
  		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...
355
356
  static int inet_diag_bc_run(const struct nlattr *_bc,
  		const struct inet_diag_entry *entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  {
87c22ea52   Pavel Emelyanov   inet_diag: Reduce...
358
359
  	const void *bc = nla_data(_bc);
  	int len = nla_len(_bc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
  	while (len > 0) {
  		int yes = 1;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
362
  		const struct inet_diag_bc_op *op = bc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
  
  		switch (op->code) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
365
  		case INET_DIAG_BC_NOP:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
367
  		case INET_DIAG_BC_JMP:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
  			yes = 0;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
370
  		case INET_DIAG_BC_S_GE:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
  			yes = entry->sport >= op[1].no;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
373
  		case INET_DIAG_BC_S_LE:
b4ced2b76   Roel Kluin   netlink: With opc...
374
  			yes = entry->sport <= op[1].no;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
376
  		case INET_DIAG_BC_D_GE:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
  			yes = entry->dport >= op[1].no;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
379
  		case INET_DIAG_BC_D_LE:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
  			yes = entry->dport <= op[1].no;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
382
  		case INET_DIAG_BC_AUTO:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
  			yes = !(entry->userlocks & SOCK_BINDPORT_LOCK);
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
385
  		case INET_DIAG_BC_S_COND:
a8c2190ee   Arnaldo Carvalho de Melo   [INET_DIAG]: Rena...
386
387
  		case INET_DIAG_BC_D_COND: {
  			struct inet_diag_hostcond *cond;
9f8552996   Al Viro   [IPV4]: inet_diag...
388
  			__be32 *addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389

a8c2190ee   Arnaldo Carvalho de Melo   [INET_DIAG]: Rena...
390
  			cond = (struct inet_diag_hostcond *)(op + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
  			if (cond->port != -1 &&
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
392
  			    cond->port != (op->code == INET_DIAG_BC_S_COND ?
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
396
  					     entry->sport : entry->dport)) {
  				yes = 0;
  				break;
  			}
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
397

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
  			if (cond->prefix_len == 0)
  				break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
400
  			if (op->code == INET_DIAG_BC_S_COND)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
  				addr = entry->saddr;
  			else
  				addr = entry->daddr;
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
404
405
  			if (bitstring_match(addr, cond->addr,
  					    cond->prefix_len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
  				break;
  			if (entry->family == AF_INET6 &&
  			    cond->family == AF_INET) {
  				if (addr[0] == 0 && addr[1] == 0 &&
  				    addr[2] == htonl(0xffff) &&
a8c2190ee   Arnaldo Carvalho de Melo   [INET_DIAG]: Rena...
411
  				    bitstring_match(addr + 3, cond->addr,
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
412
  						    cond->prefix_len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
418
  					break;
  			}
  			yes = 0;
  			break;
  		}
  		}
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
419
  		if (yes) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
421
422
423
424
425
426
  			len -= op->yes;
  			bc += op->yes;
  		} else {
  			len -= op->no;
  			bc += op->no;
  		}
  	}
a02cec215   Eric Dumazet   net: return opera...
427
  	return len == 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
  }
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
429
430
431
432
433
434
435
436
437
  int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk)
  {
  	struct inet_diag_entry entry;
  	struct inet_sock *inet = inet_sk(sk);
  
  	if (bc == NULL)
  		return 1;
  
  	entry.family = sk->sk_family;
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
438
  #if IS_ENABLED(CONFIG_IPV6)
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  	if (entry.family == AF_INET6) {
  		struct ipv6_pinfo *np = inet6_sk(sk);
  
  		entry.saddr = np->rcv_saddr.s6_addr32;
  		entry.daddr = np->daddr.s6_addr32;
  	} else
  #endif
  	{
  		entry.saddr = &inet->inet_rcv_saddr;
  		entry.daddr = &inet->inet_daddr;
  	}
  	entry.sport = inet->inet_num;
  	entry.dport = ntohs(inet->inet_dport);
  	entry.userlocks = sk->sk_userlocks;
  
  	return inet_diag_bc_run(bc, &entry);
  }
  EXPORT_SYMBOL_GPL(inet_diag_bc_sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
  static int valid_cc(const void *bc, int len, int cc)
  {
  	while (len >= 0) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
460
  		const struct inet_diag_bc_op *op = bc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
462
463
464
465
  
  		if (cc > len)
  			return 0;
  		if (cc == len)
  			return 1;
eeb149727   Eric Dumazet   inet_diag: fix in...
466
  		if (op->yes < 4 || op->yes & 3)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
472
  			return 0;
  		len -= op->yes;
  		bc  += op->yes;
  	}
  	return 0;
  }
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
473
  static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
  {
eeb149727   Eric Dumazet   inet_diag: fix in...
475
  	const void *bc = bytecode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
  	int  len = bytecode_len;
  
  	while (len > 0) {
eeb149727   Eric Dumazet   inet_diag: fix in...
479
  		const struct inet_diag_bc_op *op = bc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
483
  
  //printk("BC: %d %d %d {%d} / %d
  ", op->code, op->yes, op->no, op[1].no, len);
  		switch (op->code) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
484
485
486
487
488
489
490
  		case INET_DIAG_BC_AUTO:
  		case INET_DIAG_BC_S_COND:
  		case INET_DIAG_BC_D_COND:
  		case INET_DIAG_BC_S_GE:
  		case INET_DIAG_BC_S_LE:
  		case INET_DIAG_BC_D_GE:
  		case INET_DIAG_BC_D_LE:
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
491
  		case INET_DIAG_BC_JMP:
eeb149727   Eric Dumazet   inet_diag: fix in...
492
  			if (op->no < 4 || op->no > len + 4 || op->no & 3)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
  				return -EINVAL;
  			if (op->no < len &&
a8c2190ee   Arnaldo Carvalho de Melo   [INET_DIAG]: Rena...
495
  			    !valid_cc(bytecode, bytecode_len, len - op->no))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
  				return -EINVAL;
  			break;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
498
  		case INET_DIAG_BC_NOP:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
501
502
  			break;
  		default:
  			return -EINVAL;
  		}
eeb149727   Eric Dumazet   inet_diag: fix in...
503
504
  		if (op->yes < 4 || op->yes > len + 4 || op->yes & 3)
  			return -EINVAL;
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
505
  		bc  += op->yes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
508
509
  		len -= op->yes;
  	}
  	return len == 0 ? 0 : -EINVAL;
  }
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
510
511
  static int inet_csk_diag_dump(struct sock *sk,
  			      struct sk_buff *skb,
37f352b5e   Pavel Emelyanov   inet_diag: Move b...
512
  			      struct netlink_callback *cb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
513
  			      struct inet_diag_req_v2 *r,
37f352b5e   Pavel Emelyanov   inet_diag: Move b...
514
  			      const struct nlattr *bc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
  {
8d07d1518   Pavel Emelyanov   inet_diag: Introd...
516
517
  	if (!inet_diag_bc_sk(bc, sk))
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518

a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
519
  	return inet_csk_diag_fill(sk, skb, r,
dff2c0353   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
520
521
  				  NETLINK_CB(cb->skb).pid,
  				  cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
  }
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
523
524
  static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
  			       struct sk_buff *skb,
37f352b5e   Pavel Emelyanov   inet_diag: Move b...
525
  			       struct netlink_callback *cb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
526
  			       struct inet_diag_req_v2 *r,
37f352b5e   Pavel Emelyanov   inet_diag: Move b...
527
  			       const struct nlattr *bc)
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
528
  {
37f352b5e   Pavel Emelyanov   inet_diag: Move b...
529
  	if (bc != NULL) {
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
530
  		struct inet_diag_entry entry;
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
531
532
  
  		entry.family = tw->tw_family;
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
533
  #if IS_ENABLED(CONFIG_IPV6)
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
534
535
536
537
538
539
540
541
542
543
544
545
546
  		if (tw->tw_family == AF_INET6) {
  			struct inet6_timewait_sock *tw6 =
  						inet6_twsk((struct sock *)tw);
  			entry.saddr = tw6->tw_v6_rcv_saddr.s6_addr32;
  			entry.daddr = tw6->tw_v6_daddr.s6_addr32;
  		} else
  #endif
  		{
  			entry.saddr = &tw->tw_rcv_saddr;
  			entry.daddr = &tw->tw_daddr;
  		}
  		entry.sport = tw->tw_num;
  		entry.dport = ntohs(tw->tw_dport);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
547
  		entry.userlocks = 0;
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
548

87c22ea52   Pavel Emelyanov   inet_diag: Reduce...
549
  		if (!inet_diag_bc_run(bc, &entry))
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
550
551
  			return 0;
  	}
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
552
  	return inet_twsk_diag_fill(tw, skb, r,
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
553
554
555
  				   NETLINK_CB(cb->skb).pid,
  				   cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
  }
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
556
  static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
557
558
  			      struct request_sock *req, u32 pid, u32 seq,
  			      const struct nlmsghdr *unlh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  {
2e6599cb8   Arnaldo Carvalho de Melo   [NET] Generalise ...
560
  	const struct inet_request_sock *ireq = inet_rsk(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  	struct inet_sock *inet = inet_sk(sk);
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
562
  	unsigned char *b = skb_tail_pointer(skb);
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
563
  	struct inet_diag_msg *r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
  	struct nlmsghdr *nlh;
  	long tmo;
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
566
  	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
  	nlh->nlmsg_flags = NLM_F_MULTI;
  	r = NLMSG_DATA(nlh);
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
569
570
571
572
  	r->idiag_family = sk->sk_family;
  	r->idiag_state = TCP_SYN_RECV;
  	r->idiag_timer = 1;
  	r->idiag_retrans = req->retrans;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573

73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
574
  	r->id.idiag_if = sk->sk_bound_dev_if;
f65c1b534   Pavel Emelyanov   sock_diag: Genera...
575
  	sock_diag_save_cookie(req, r->id.idiag_cookie);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
  
  	tmo = req->expires - jiffies;
  	if (tmo < 0)
  		tmo = 0;
c720c7e83   Eric Dumazet   inet: rename some...
580
  	r->id.idiag_sport = inet->inet_sport;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
581
582
583
584
585
586
587
588
  	r->id.idiag_dport = ireq->rmt_port;
  	r->id.idiag_src[0] = ireq->loc_addr;
  	r->id.idiag_dst[0] = ireq->rmt_addr;
  	r->idiag_expires = jiffies_to_msecs(tmo);
  	r->idiag_rqueue = 0;
  	r->idiag_wqueue = 0;
  	r->idiag_uid = sock_i_uid(sk);
  	r->idiag_inode = 0;
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
589
  #if IS_ENABLED(CONFIG_IPV6)
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
590
  	if (r->idiag_family == AF_INET6) {
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
591
592
  		*(struct in6_addr *)r->id.idiag_src = inet6_rsk(req)->loc_addr;
  		*(struct in6_addr *)r->id.idiag_dst = inet6_rsk(req)->rmt_addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
  	}
  #endif
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
595
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
  
  	return skb->len;
  
  nlmsg_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
600
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
  	return -1;
  }
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
603
  static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
37f352b5e   Pavel Emelyanov   inet_diag: Move b...
604
  			       struct netlink_callback *cb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
605
  			       struct inet_diag_req_v2 *r,
37f352b5e   Pavel Emelyanov   inet_diag: Move b...
606
  			       const struct nlattr *bc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
  {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
608
  	struct inet_diag_entry entry;
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
609
  	struct inet_connection_sock *icsk = inet_csk(sk);
2ad69c55a   Arnaldo Carvalho de Melo   [NET] rename stru...
610
  	struct listen_sock *lopt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
614
615
616
617
618
619
620
621
622
  	struct inet_sock *inet = inet_sk(sk);
  	int j, s_j;
  	int reqnum, s_reqnum;
  	int err = 0;
  
  	s_j = cb->args[3];
  	s_reqnum = cb->args[4];
  
  	if (s_j > 0)
  		s_j--;
  
  	entry.family = sk->sk_family;
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
623
  	read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624

463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
625
  	lopt = icsk->icsk_accept_queue.listen_opt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
627
  	if (!lopt || !lopt->qlen)
  		goto out;
37f352b5e   Pavel Emelyanov   inet_diag: Move b...
628
  	if (bc != NULL) {
c720c7e83   Eric Dumazet   inet: rename some...
629
  		entry.sport = inet->inet_num;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
  		entry.userlocks = sk->sk_userlocks;
  	}
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
632
  	for (j = s_j; j < lopt->nr_table_entries; j++) {
60236fdd0   Arnaldo Carvalho de Melo   [NET] Rename open...
633
  		struct request_sock *req, *head = lopt->syn_table[j];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
635
636
  
  		reqnum = 0;
  		for (req = head; req; reqnum++, req = req->dl_next) {
2e6599cb8   Arnaldo Carvalho de Melo   [NET] Generalise ...
637
  			struct inet_request_sock *ireq = inet_rsk(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
  			if (reqnum < s_reqnum)
  				continue;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
640
641
  			if (r->id.idiag_dport != ireq->rmt_port &&
  			    r->id.idiag_dport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
  				continue;
  
  			if (bc) {
  				entry.saddr =
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
646
  #if IS_ENABLED(CONFIG_IPV6)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  					(entry.family == AF_INET6) ?
ca304b610   Arnaldo Carvalho de Melo   [IPV6]: Introduce...
648
  					inet6_rsk(req)->loc_addr.s6_addr32 :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
  #endif
2e6599cb8   Arnaldo Carvalho de Melo   [NET] Generalise ...
650
  					&ireq->loc_addr;
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
651
  				entry.daddr =
dfd56b8b3   Eric Dumazet   net: use IS_ENABL...
652
  #if IS_ENABLED(CONFIG_IPV6)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
  					(entry.family == AF_INET6) ?
ca304b610   Arnaldo Carvalho de Melo   [IPV6]: Introduce...
654
  					inet6_rsk(req)->rmt_addr.s6_addr32 :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
  #endif
2e6599cb8   Arnaldo Carvalho de Melo   [NET] Generalise ...
656
657
  					&ireq->rmt_addr;
  				entry.dport = ntohs(ireq->rmt_port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658

87c22ea52   Pavel Emelyanov   inet_diag: Reduce...
659
  				if (!inet_diag_bc_run(bc, &entry))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
  					continue;
  			}
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
662
  			err = inet_diag_fill_req(skb, sk, req,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
  					       NETLINK_CB(cb->skb).pid,
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
664
  					       cb->nlh->nlmsg_seq, cb->nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
667
668
669
670
671
672
673
674
675
  			if (err < 0) {
  				cb->args[3] = j + 1;
  				cb->args[4] = reqnum;
  				goto out;
  			}
  		}
  
  		s_reqnum = 0;
  	}
  
  out:
463c84b97   Arnaldo Carvalho de Melo   [NET]: Introduce ...
676
  	read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
  
  	return err;
  }
1942c518c   Pavel Emelyanov   inet_diag: Genera...
680
  void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
681
  		struct netlink_callback *cb, struct inet_diag_req_v2 *r, struct nlattr *bc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682
683
684
  {
  	int i, num;
  	int s_i, s_num;
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
685

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
  	s_i = cb->args[1];
  	s_num = num = cb->args[2];
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
688

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
  	if (cb->args[0] == 0) {
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
690
  		if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  			goto skip_listen_ht;
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
692

0f7ff9274   Arnaldo Carvalho de Melo   [INET]: Just rena...
693
  		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
  			struct sock *sk;
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
695
  			struct hlist_nulls_node *node;
5caea4ea7   Eric Dumazet   net: listening_ha...
696
  			struct inet_listen_hashbucket *ilb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
698
  
  			num = 0;
5caea4ea7   Eric Dumazet   net: listening_ha...
699
700
  			ilb = &hashinfo->listening_hash[i];
  			spin_lock_bh(&ilb->lock);
c25eb3bfb   Eric Dumazet   net: Convert TCP/...
701
  			sk_nulls_for_each(sk, node, &ilb->head) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
703
704
705
706
707
  				struct inet_sock *inet = inet_sk(sk);
  
  				if (num < s_num) {
  					num++;
  					continue;
  				}
d23deaa07   Pavel Emelyanov   inet_diag: Introd...
708
709
710
  				if (r->sdiag_family != AF_UNSPEC &&
  						sk->sk_family != r->sdiag_family)
  					goto next_listen;
c720c7e83   Eric Dumazet   inet: rename some...
711
  				if (r->id.idiag_sport != inet->inet_sport &&
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
712
  				    r->id.idiag_sport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
  					goto next_listen;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
714
715
  				if (!(r->idiag_states & TCPF_LISTEN) ||
  				    r->id.idiag_dport ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
717
  				    cb->args[3] > 0)
  					goto syn_recv;
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
718
  				if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
5caea4ea7   Eric Dumazet   net: listening_ha...
719
  					spin_unlock_bh(&ilb->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
723
  					goto done;
  				}
  
  syn_recv:
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
724
  				if (!(r->idiag_states & TCPF_SYN_RECV))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
  					goto next_listen;
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
726
  				if (inet_diag_dump_reqs(skb, sk, cb, r, bc) < 0) {
5caea4ea7   Eric Dumazet   net: listening_ha...
727
  					spin_unlock_bh(&ilb->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
731
732
733
734
735
  					goto done;
  				}
  
  next_listen:
  				cb->args[3] = 0;
  				cb->args[4] = 0;
  				++num;
  			}
5caea4ea7   Eric Dumazet   net: listening_ha...
736
  			spin_unlock_bh(&ilb->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
738
739
740
741
  
  			s_num = 0;
  			cb->args[3] = 0;
  			cb->args[4] = 0;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
745
  skip_listen_ht:
  		cb->args[0] = 1;
  		s_i = num = s_num = 0;
  	}
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
746
  	if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
747
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748

f373b53b5   Eric Dumazet   tcp: replace ehas...
749
  	for (i = s_i; i <= hashinfo->ehash_mask; i++) {
540722ffc   Arnaldo Carvalho de Melo   [TCPDIAG]: Implem...
750
  		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
7e3aab4a9   David S. Miller   inet_diag: Missed...
751
  		spinlock_t *lock = inet_ehash_lockp(hashinfo, i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
  		struct sock *sk;
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
753
  		struct hlist_nulls_node *node;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754

6be547a61   Andi Kleen   inet_diag: Add em...
755
  		num = 0;
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
756
757
  		if (hlist_nulls_empty(&head->chain) &&
  			hlist_nulls_empty(&head->twchain))
6be547a61   Andi Kleen   inet_diag: Add em...
758
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
760
  		if (i > s_i)
  			s_num = 0;
7e3aab4a9   David S. Miller   inet_diag: Missed...
761
  		spin_lock_bh(lock);
3ab5aee7f   Eric Dumazet   net: Convert TCP ...
762
  		sk_nulls_for_each(sk, node, &head->chain) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
764
765
766
  			struct inet_sock *inet = inet_sk(sk);
  
  			if (num < s_num)
  				goto next_normal;
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
767
  			if (!(r->idiag_states & (1 << sk->sk_state)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
  				goto next_normal;
d23deaa07   Pavel Emelyanov   inet_diag: Introd...
769
770
771
  			if (r->sdiag_family != AF_UNSPEC &&
  					sk->sk_family != r->sdiag_family)
  				goto next_normal;
c720c7e83   Eric Dumazet   inet: rename some...
772
  			if (r->id.idiag_sport != inet->inet_sport &&
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
773
  			    r->id.idiag_sport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
  				goto next_normal;
c720c7e83   Eric Dumazet   inet: rename some...
775
  			if (r->id.idiag_dport != inet->inet_dport &&
4e852c027   Arnaldo Carvalho de Melo   [INET_DIAG]: whit...
776
  			    r->id.idiag_dport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
777
  				goto next_normal;
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
778
  			if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
7e3aab4a9   David S. Miller   inet_diag: Missed...
779
  				spin_unlock_bh(lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
780
781
782
783
784
  				goto done;
  			}
  next_normal:
  			++num;
  		}
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
785
  		if (r->idiag_states & TCPF_TIME_WAIT) {
c7d58aabd   Arnaldo Carvalho de Melo   [INET_DIAG]: Intr...
786
787
788
  			struct inet_timewait_sock *tw;
  
  			inet_twsk_for_each(tw, node,
dbca9b275   Eric Dumazet   [NET]: change lay...
789
  				    &head->twchain) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
791
792
  
  				if (num < s_num)
  					goto next_dying;
d23deaa07   Pavel Emelyanov   inet_diag: Introd...
793
794
795
  				if (r->sdiag_family != AF_UNSPEC &&
  						tw->tw_family != r->sdiag_family)
  					goto next_dying;
7dbf07552   Arnaldo Carvalho de Melo   [INET_DIAG]: Use ...
796
  				if (r->id.idiag_sport != tw->tw_sport &&
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
797
  				    r->id.idiag_sport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
  					goto next_dying;
7dbf07552   Arnaldo Carvalho de Melo   [INET_DIAG]: Use ...
799
  				if (r->id.idiag_dport != tw->tw_dport &&
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
800
  				    r->id.idiag_dport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
  					goto next_dying;
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
802
  				if (inet_twsk_diag_dump(tw, skb, cb, r, bc) < 0) {
7e3aab4a9   David S. Miller   inet_diag: Missed...
803
  					spin_unlock_bh(lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
807
808
809
  					goto done;
  				}
  next_dying:
  				++num;
  			}
  		}
7e3aab4a9   David S. Miller   inet_diag: Missed...
810
  		spin_unlock_bh(lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
812
813
814
815
  	}
  
  done:
  	cb->args[1] = i;
  	cb->args[2] = num;
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
816
817
818
  out:
  	;
  }
1942c518c   Pavel Emelyanov   inet_diag: Genera...
819
  EXPORT_SYMBOL_GPL(inet_diag_dump_icsk);
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
820
821
  
  static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
822
  		struct inet_diag_req_v2 *r, struct nlattr *bc)
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
823
824
825
826
827
  {
  	const struct inet_diag_handler *handler;
  
  	handler = inet_diag_lock_handler(r->sdiag_protocol);
  	if (!IS_ERR(handler))
1942c518c   Pavel Emelyanov   inet_diag: Genera...
828
  		handler->dump(skb, cb, r, bc);
d523a328f   Herbert Xu   [INET]: Fix inet_...
829
  	inet_diag_unlock_handler(handler);
efb3cb428   Pavel Emelyanov   inet_diag: Split ...
830

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
832
  	return skb->len;
  }
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
833
834
835
  static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
  {
  	struct nlattr *bc = NULL;
c8991362a   Pavel Emelyanov   inet_diag: Rename...
836
  	int hdrlen = sizeof(struct inet_diag_req_v2);
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
837
838
839
  
  	if (nlmsg_attrlen(cb->nlh, hdrlen))
  		bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE);
c8991362a   Pavel Emelyanov   inet_diag: Rename...
840
  	return __inet_diag_dump(skb, cb, (struct inet_diag_req_v2 *)NLMSG_DATA(cb->nlh), bc);
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
841
  }
a029fe26b   Pavel Emelyanov   inet_diag: Cleanu...
842
843
844
845
846
847
848
849
850
851
852
  static inline int inet_diag_type2proto(int type)
  {
  	switch (type) {
  	case TCPDIAG_GETSOCK:
  		return IPPROTO_TCP;
  	case DCCPDIAG_GETSOCK:
  		return IPPROTO_DCCP;
  	default:
  		return 0;
  	}
  }
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
853
854
  static int inet_diag_dump_compat(struct sk_buff *skb, struct netlink_callback *cb)
  {
3b09c84cb   Pavel Emelyanov   inet_diag: Rename...
855
  	struct inet_diag_req *rc = NLMSG_DATA(cb->nlh);
c8991362a   Pavel Emelyanov   inet_diag: Rename...
856
  	struct inet_diag_req_v2 req;
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
857
  	struct nlattr *bc = NULL;
3b09c84cb   Pavel Emelyanov   inet_diag: Rename...
858
  	int hdrlen = sizeof(struct inet_diag_req);
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
859

d23deaa07   Pavel Emelyanov   inet_diag: Introd...
860
  	req.sdiag_family = AF_UNSPEC; /* compatibility */
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
861
862
863
864
865
866
867
868
869
870
  	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;
  
  	if (nlmsg_attrlen(cb->nlh, hdrlen))
  		bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE);
  
  	return __inet_diag_dump(skb, cb, &req, bc);
  }
fe50ce284   Pavel Emelyanov   inet_diag: Switch...
871
872
873
  static int inet_diag_get_exact_compat(struct sk_buff *in_skb,
  			       const struct nlmsghdr *nlh)
  {
3b09c84cb   Pavel Emelyanov   inet_diag: Rename...
874
  	struct inet_diag_req *rc = NLMSG_DATA(nlh);
c8991362a   Pavel Emelyanov   inet_diag: Rename...
875
  	struct inet_diag_req_v2 req;
fe50ce284   Pavel Emelyanov   inet_diag: Switch...
876
877
878
879
880
881
882
883
884
  
  	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;
  
  	return inet_diag_get_exact(in_skb, nlh, &req);
  }
8d34172df   Pavel Emelyanov   sock_diag: Introd...
885
  static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
886
  {
3b09c84cb   Pavel Emelyanov   inet_diag: Rename...
887
  	int hdrlen = sizeof(struct inet_diag_req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888

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

b8f3ab429   David S. Miller   Revert "netlink: ...
893
  	if (nlh->nlmsg_flags & NLM_F_DUMP) {
ead592ba2   Thomas Graf   [IPv4] diag: Use ...
894
895
  		if (nlmsg_attrlen(nlh, hdrlen)) {
  			struct nlattr *attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896

ead592ba2   Thomas Graf   [IPv4] diag: Use ...
897
898
899
900
901
902
903
  			attr = nlmsg_find_attr(nlh, hdrlen,
  					       INET_DIAG_REQ_BYTECODE);
  			if (attr == NULL ||
  			    nla_len(attr) < sizeof(struct inet_diag_bc_op) ||
  			    inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
  				return -EINVAL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
904

8ef874bfc   Pavel Emelyanov   sock_diag: Move t...
905
  		return netlink_dump_start(sock_diag_nlsk, skb, nlh,
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
906
  					  inet_diag_dump_compat, NULL, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
  	}
ead592ba2   Thomas Graf   [IPv4] diag: Use ...
908

fe50ce284   Pavel Emelyanov   inet_diag: Switch...
909
  	return inet_diag_get_exact_compat(skb, nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910
  }
d366477a5   Pavel Emelyanov   sock_diag: Initia...
911
912
  static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
  {
c8991362a   Pavel Emelyanov   inet_diag: Rename...
913
  	int hdrlen = sizeof(struct inet_diag_req_v2);
d366477a5   Pavel Emelyanov   sock_diag: Initia...
914
915
916
917
918
  
  	if (nlmsg_len(h) < hdrlen)
  		return -EINVAL;
  
  	if (h->nlmsg_flags & NLM_F_DUMP) {
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
919
920
921
922
923
924
925
926
927
  		if (nlmsg_attrlen(h, hdrlen)) {
  			struct nlattr *attr;
  			attr = nlmsg_find_attr(h, hdrlen,
  					       INET_DIAG_REQ_BYTECODE);
  			if (attr == NULL ||
  			    nla_len(attr) < sizeof(struct inet_diag_bc_op) ||
  			    inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
  				return -EINVAL;
  		}
8ef874bfc   Pavel Emelyanov   sock_diag: Move t...
928
  		return netlink_dump_start(sock_diag_nlsk, skb, h,
25c4cd2b6   Pavel Emelyanov   inet_diag: Switch...
929
  					  inet_diag_dump, NULL, 0);
d366477a5   Pavel Emelyanov   sock_diag: Initia...
930
  	}
c8991362a   Pavel Emelyanov   inet_diag: Rename...
931
  	return inet_diag_get_exact(skb, h, (struct inet_diag_req_v2 *)NLMSG_DATA(h));
d366477a5   Pavel Emelyanov   sock_diag: Initia...
932
933
934
935
936
937
938
939
940
941
942
  }
  
  static struct sock_diag_handler inet_diag_handler = {
  	.family = AF_INET,
  	.dump = inet_diag_handler_dump,
  };
  
  static struct sock_diag_handler inet6_diag_handler = {
  	.family = AF_INET6,
  	.dump = inet_diag_handler_dump,
  };
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
943
944
945
946
  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...
947
  	if (type >= IPPROTO_MAX)
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
948
  		goto out;
d523a328f   Herbert Xu   [INET]: Fix inet_...
949
  	mutex_lock(&inet_diag_table_mutex);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
950
951
952
953
954
  	err = -EEXIST;
  	if (inet_diag_table[type] == NULL) {
  		inet_diag_table[type] = h;
  		err = 0;
  	}
d523a328f   Herbert Xu   [INET]: Fix inet_...
955
  	mutex_unlock(&inet_diag_table_mutex);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
956
957
958
959
960
961
962
963
  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...
964
  	if (type >= IPPROTO_MAX)
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
965
  		return;
d523a328f   Herbert Xu   [INET]: Fix inet_...
966
  	mutex_lock(&inet_diag_table_mutex);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
967
  	inet_diag_table[type] = NULL;
d523a328f   Herbert Xu   [INET]: Fix inet_...
968
  	mutex_unlock(&inet_diag_table_mutex);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
969
970
  }
  EXPORT_SYMBOL_GPL(inet_diag_unregister);
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
971
  static int __init inet_diag_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972
  {
f13c95f0e   Pavel Emelyanov   inet_diag: Switch...
973
  	const int inet_diag_table_size = (IPPROTO_MAX *
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
974
975
  					  sizeof(struct inet_diag_handler *));
  	int err = -ENOMEM;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
976
  	inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
977
978
  	if (!inet_diag_table)
  		goto out;
d366477a5   Pavel Emelyanov   sock_diag: Initia...
979
980
981
982
983
984
985
  	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...
986
  	sock_diag_register_inet_compat(inet_diag_rcv_msg_compat);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
987
988
  out:
  	return err;
d366477a5   Pavel Emelyanov   sock_diag: Initia...
989
990
991
992
  
  out_free_inet:
  	sock_diag_unregister(&inet_diag_handler);
  out_free_nl:
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
993
994
  	kfree(inet_diag_table);
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
  }
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
996
  static void __exit inet_diag_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
997
  {
d366477a5   Pavel Emelyanov   sock_diag: Initia...
998
999
  	sock_diag_unregister(&inet6_diag_handler);
  	sock_diag_unregister(&inet_diag_handler);
8ef874bfc   Pavel Emelyanov   sock_diag: Move t...
1000
  	sock_diag_unregister_inet_compat(inet_diag_rcv_msg_compat);
4f5736c4c   Arnaldo Carvalho de Melo   [TCPDIAG]: Introd...
1001
  	kfree(inet_diag_table);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002
  }
73c1f4a03   Arnaldo Carvalho de Melo   [TCPDIAG]: Just r...
1003
1004
  module_init(inet_diag_init);
  module_exit(inet_diag_exit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
  MODULE_LICENSE("GPL");
aec8dc62f   Pavel Emelyanov   sock_diag: Fix mo...
1006
1007
  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 */);