Blame view

net/ipv4/udp_diag.c 7.48 KB
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /*
   * udp_diag.c	Module for monitoring UDP transport protocols sockets.
   *
   * Authors:	Pavel Emelyanov, <xemul@parallels.com>
   *
   *	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.
   */
  
  
  #include <linux/module.h>
  #include <linux/inet_diag.h>
  #include <linux/udp.h>
  #include <net/udp.h>
  #include <net/udplite.h>
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
18
  #include <linux/sock_diag.h>
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
19
  static int sk_diag_dump(struct sock *sk, struct sk_buff *skb,
34160ea3f   Eric Dumazet   inet_diag: add co...
20
21
  			struct netlink_callback *cb,
  			const struct inet_diag_req_v2 *req,
d545caca8   Lorenzo Colitti   net: inet: diag: ...
22
  			struct nlattr *bc, bool net_admin)
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
23
24
25
  {
  	if (!inet_diag_bc_sk(bc, sk))
  		return 0;
d06ca9564   Eric W. Biederman   userns: Teach ine...
26
  	return inet_sk_diag_fill(sk, NULL, skb, req,
e32123e59   Patrick McHardy   netlink: rename s...
27
  			sk_user_ns(NETLINK_CB(cb->skb).sk),
15e473046   Eric W. Biederman   netlink: Rename p...
28
  			NETLINK_CB(cb->skb).portid,
d545caca8   Lorenzo Colitti   net: inet: diag: ...
29
  			cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh, net_admin);
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
30
  }
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
31
  static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
34160ea3f   Eric Dumazet   inet_diag: add co...
32
33
  			const struct nlmsghdr *nlh,
  			const struct inet_diag_req_v2 *req)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
34
  {
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
35
  	int err = -EINVAL;
ca065d0cf   Eric Dumazet   udp: no longer us...
36
  	struct sock *sk = NULL;
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
37
  	struct sk_buff *rep;
51d7cccf0   Andrey Vagin   net: make sock di...
38
  	struct net *net = sock_net(in_skb->sk);
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
39

ca065d0cf   Eric Dumazet   udp: no longer us...
40
  	rcu_read_lock();
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
41
  	if (req->sdiag_family == AF_INET)
51d7cccf0   Andrey Vagin   net: make sock di...
42
  		sk = __udp4_lib_lookup(net,
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
43
44
  				req->id.idiag_src[0], req->id.idiag_sport,
  				req->id.idiag_dst[0], req->id.idiag_dport,
538950a1b   Craig Gallek   soreuseport: sets...
45
  				req->id.idiag_if, tbl, NULL);
86e62ad6b   Pavel Emelyanov   udp_diag: Fix the...
46
  #if IS_ENABLED(CONFIG_IPV6)
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
47
  	else if (req->sdiag_family == AF_INET6)
51d7cccf0   Andrey Vagin   net: make sock di...
48
  		sk = __udp6_lib_lookup(net,
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
49
50
51
52
  				(struct in6_addr *)req->id.idiag_src,
  				req->id.idiag_sport,
  				(struct in6_addr *)req->id.idiag_dst,
  				req->id.idiag_dport,
538950a1b   Craig Gallek   soreuseport: sets...
53
  				req->id.idiag_if, tbl, NULL);
86e62ad6b   Pavel Emelyanov   udp_diag: Fix the...
54
  #endif
ca065d0cf   Eric Dumazet   udp: no longer us...
55
56
57
  	if (sk && !atomic_inc_not_zero(&sk->sk_refcnt))
  		sk = NULL;
  	rcu_read_unlock();
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
58
  	err = -ENOENT;
51456b291   Ian Morris   ipv4: coding styl...
59
  	if (!sk)
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
60
  		goto out_nosk;
f65c1b534   Pavel Emelyanov   sock_diag: Genera...
61
  	err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
62
63
64
65
  	if (err)
  		goto out;
  
  	err = -ENOMEM;
573ce260b   Hong zhi guo   net-next: replace...
66
67
68
  	rep = nlmsg_new(sizeof(struct inet_diag_msg) +
  			sizeof(struct inet_diag_meminfo) + 64,
  			GFP_KERNEL);
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
69
70
71
72
  	if (!rep)
  		goto out;
  
  	err = inet_sk_diag_fill(sk, NULL, rep, req,
e32123e59   Patrick McHardy   netlink: rename s...
73
  			   sk_user_ns(NETLINK_CB(in_skb).sk),
15e473046   Eric W. Biederman   netlink: Rename p...
74
  			   NETLINK_CB(in_skb).portid,
d545caca8   Lorenzo Colitti   net: inet: diag: ...
75
76
  			   nlh->nlmsg_seq, 0, nlh,
  			   netlink_net_capable(in_skb, CAP_NET_ADMIN));
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
77
78
79
80
81
  	if (err < 0) {
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(rep);
  		goto out;
  	}
15e473046   Eric W. Biederman   netlink: Rename p...
82
  	err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid,
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
83
84
85
86
87
88
89
90
  			      MSG_DONTWAIT);
  	if (err > 0)
  		err = 0;
  out:
  	if (sk)
  		sock_put(sk);
  out_nosk:
  	return err;
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
91
  }
34160ea3f   Eric Dumazet   inet_diag: add co...
92
93
94
  static void udp_dump(struct udp_table *table, struct sk_buff *skb,
  		     struct netlink_callback *cb,
  		     const struct inet_diag_req_v2 *r, struct nlattr *bc)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
95
  {
d545caca8   Lorenzo Colitti   net: inet: diag: ...
96
  	bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN);
51d7cccf0   Andrey Vagin   net: make sock di...
97
  	struct net *net = sock_net(skb->sk);
ca065d0cf   Eric Dumazet   udp: no longer us...
98
  	int num, s_num, slot, s_slot;
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
99
100
101
  
  	s_slot = cb->args[0];
  	num = s_num = cb->args[1];
86f3cddbc   Herbert Xu   udp_diag: Fix soc...
102
  	for (slot = s_slot; slot <= table->mask; s_num = 0, slot++) {
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
103
  		struct udp_hslot *hslot = &table->hash[slot];
ca065d0cf   Eric Dumazet   udp: no longer us...
104
  		struct sock *sk;
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
105

86f3cddbc   Herbert Xu   udp_diag: Fix soc...
106
  		num = 0;
ca065d0cf   Eric Dumazet   udp: no longer us...
107
  		if (hlist_empty(&hslot->head))
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
108
109
110
  			continue;
  
  		spin_lock_bh(&hslot->lock);
ca065d0cf   Eric Dumazet   udp: no longer us...
111
  		sk_for_each(sk, &hslot->head) {
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
112
  			struct inet_sock *inet = inet_sk(sk);
51d7cccf0   Andrey Vagin   net: make sock di...
113
114
  			if (!net_eq(sock_net(sk), net))
  				continue;
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
115
116
117
118
119
120
121
122
123
124
125
126
127
  			if (num < s_num)
  				goto next;
  			if (!(r->idiag_states & (1 << sk->sk_state)))
  				goto next;
  			if (r->sdiag_family != AF_UNSPEC &&
  					sk->sk_family != r->sdiag_family)
  				goto next;
  			if (r->id.idiag_sport != inet->inet_sport &&
  			    r->id.idiag_sport)
  				goto next;
  			if (r->id.idiag_dport != inet->inet_dport &&
  			    r->id.idiag_dport)
  				goto next;
d545caca8   Lorenzo Colitti   net: inet: diag: ...
128
  			if (sk_diag_dump(sk, skb, cb, r, bc, net_admin) < 0) {
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
129
130
131
132
133
134
135
136
137
138
139
  				spin_unlock_bh(&hslot->lock);
  				goto done;
  			}
  next:
  			num++;
  		}
  		spin_unlock_bh(&hslot->lock);
  	}
  done:
  	cb->args[0] = slot;
  	cb->args[1] = num;
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
140
141
142
  }
  
  static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
34160ea3f   Eric Dumazet   inet_diag: add co...
143
  			  const struct inet_diag_req_v2 *r, struct nlattr *bc)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
144
145
146
147
148
  {
  	udp_dump(&udp_table, skb, cb, r, bc);
  }
  
  static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh,
34160ea3f   Eric Dumazet   inet_diag: add co...
149
  			     const struct inet_diag_req_v2 *req)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
150
151
152
  {
  	return udp_dump_one(&udp_table, in_skb, nlh, req);
  }
62ad6fcd7   Shan Wei   udp_diag: impleme...
153
154
155
156
157
158
  static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
  		void *info)
  {
  	r->idiag_rqueue = sk_rmem_alloc_get(sk);
  	r->idiag_wqueue = sk_wmem_alloc_get(sk);
  }
5d77dca82   David Ahern   net: diag: suppor...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  #ifdef CONFIG_INET_DIAG_DESTROY
  static int __udp_diag_destroy(struct sk_buff *in_skb,
  			      const struct inet_diag_req_v2 *req,
  			      struct udp_table *tbl)
  {
  	struct net *net = sock_net(in_skb->sk);
  	struct sock *sk;
  	int err;
  
  	rcu_read_lock();
  
  	if (req->sdiag_family == AF_INET)
  		sk = __udp4_lib_lookup(net,
  				req->id.idiag_dst[0], req->id.idiag_dport,
  				req->id.idiag_src[0], req->id.idiag_sport,
  				req->id.idiag_if, tbl, NULL);
  #if IS_ENABLED(CONFIG_IPV6)
  	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))
  			sk = __udp4_lib_lookup(net,
f95bf3462   Lorenzo Colitti   net: diag: make u...
180
181
  					req->id.idiag_dst[3], req->id.idiag_dport,
  					req->id.idiag_src[3], req->id.idiag_sport,
5d77dca82   David Ahern   net: diag: suppor...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  					req->id.idiag_if, tbl, NULL);
  
  		else
  			sk = __udp6_lib_lookup(net,
  					(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, tbl, NULL);
  	}
  #endif
  	else {
  		rcu_read_unlock();
  		return -EINVAL;
  	}
  
  	if (sk && !atomic_inc_not_zero(&sk->sk_refcnt))
  		sk = NULL;
  
  	rcu_read_unlock();
  
  	if (!sk)
  		return -ENOENT;
  
  	if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) {
  		sock_put(sk);
  		return -ENOENT;
  	}
  
  	err = sock_diag_destroy(sk, ECONNABORTED);
  
  	sock_put(sk);
  
  	return err;
  }
  
  static int udp_diag_destroy(struct sk_buff *in_skb,
  			    const struct inet_diag_req_v2 *req)
  {
  	return __udp_diag_destroy(in_skb, req, &udp_table);
  }
  
  static int udplite_diag_destroy(struct sk_buff *in_skb,
  				const struct inet_diag_req_v2 *req)
  {
  	return __udp_diag_destroy(in_skb, req, &udplite_table);
  }
  
  #endif
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
231
232
233
  static const struct inet_diag_handler udp_diag_handler = {
  	.dump		 = udp_diag_dump,
  	.dump_one	 = udp_diag_dump_one,
62ad6fcd7   Shan Wei   udp_diag: impleme...
234
  	.idiag_get_info  = udp_diag_get_info,
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
235
  	.idiag_type	 = IPPROTO_UDP,
3fd22af80   Craig Gallek   sock_diag: specif...
236
  	.idiag_info_size = 0,
5d77dca82   David Ahern   net: diag: suppor...
237
238
239
  #ifdef CONFIG_INET_DIAG_DESTROY
  	.destroy	 = udp_diag_destroy,
  #endif
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
240
241
242
  };
  
  static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
34160ea3f   Eric Dumazet   inet_diag: add co...
243
244
  			      const struct inet_diag_req_v2 *r,
  			      struct nlattr *bc)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
245
246
247
248
249
  {
  	udp_dump(&udplite_table, skb, cb, r, bc);
  }
  
  static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh,
34160ea3f   Eric Dumazet   inet_diag: add co...
250
  				 const struct inet_diag_req_v2 *req)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
251
252
253
254
255
256
257
  {
  	return udp_dump_one(&udplite_table, in_skb, nlh, req);
  }
  
  static const struct inet_diag_handler udplite_diag_handler = {
  	.dump		 = udplite_diag_dump,
  	.dump_one	 = udplite_diag_dump_one,
62ad6fcd7   Shan Wei   udp_diag: impleme...
258
  	.idiag_get_info  = udp_diag_get_info,
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
259
  	.idiag_type	 = IPPROTO_UDPLITE,
3fd22af80   Craig Gallek   sock_diag: specif...
260
  	.idiag_info_size = 0,
5d77dca82   David Ahern   net: diag: suppor...
261
262
263
  #ifdef CONFIG_INET_DIAG_DESTROY
  	.destroy	 = udplite_diag_destroy,
  #endif
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  };
  
  static int __init udp_diag_init(void)
  {
  	int err;
  
  	err = inet_diag_register(&udp_diag_handler);
  	if (err)
  		goto out;
  	err = inet_diag_register(&udplite_diag_handler);
  	if (err)
  		goto out_lite;
  out:
  	return err;
  out_lite:
  	inet_diag_unregister(&udp_diag_handler);
  	goto out;
  }
  
  static void __exit udp_diag_exit(void)
  {
  	inet_diag_unregister(&udplite_diag_handler);
  	inet_diag_unregister(&udp_diag_handler);
  }
  
  module_init(udp_diag_init);
  module_exit(udp_diag_exit);
  MODULE_LICENSE("GPL");
aec8dc62f   Pavel Emelyanov   sock_diag: Fix mo...
292
293
  MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-17 /* AF_INET - IPPROTO_UDP */);
  MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-136 /* AF_INET - IPPROTO_UDPLITE */);