Blame view

net/ipv4/udp_diag.c 4.97 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
18
19
  /*
   * 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>
  #include <linux/inet_diag.h>
  #include <linux/sock_diag.h>
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
20
  static int sk_diag_dump(struct sock *sk, struct sk_buff *skb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
21
  		struct netlink_callback *cb, struct inet_diag_req_v2 *req,
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
22
23
24
25
26
27
28
29
  		struct nlattr *bc)
  {
  	if (!inet_diag_bc_sk(bc, sk))
  		return 0;
  
  	return inet_sk_diag_fill(sk, NULL, skb, req, NETLINK_CB(cb->skb).pid,
  			cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
  }
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
30
  static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
31
  		const struct nlmsghdr *nlh, struct inet_diag_req_v2 *req)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
32
  {
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
33
34
35
36
37
38
39
40
41
  	int err = -EINVAL;
  	struct sock *sk;
  	struct sk_buff *rep;
  
  	if (req->sdiag_family == AF_INET)
  		sk = __udp4_lib_lookup(&init_net,
  				req->id.idiag_src[0], req->id.idiag_sport,
  				req->id.idiag_dst[0], req->id.idiag_dport,
  				req->id.idiag_if, tbl);
86e62ad6b   Pavel Emelyanov   udp_diag: Fix the...
42
  #if IS_ENABLED(CONFIG_IPV6)
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
43
44
45
46
47
48
49
  	else if (req->sdiag_family == AF_INET6)
  		sk = __udp6_lib_lookup(&init_net,
  				(struct in6_addr *)req->id.idiag_src,
  				req->id.idiag_sport,
  				(struct in6_addr *)req->id.idiag_dst,
  				req->id.idiag_dport,
  				req->id.idiag_if, tbl);
86e62ad6b   Pavel Emelyanov   udp_diag: Fix the...
50
  #endif
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
51
52
53
54
55
56
  	else
  		goto out_nosk;
  
  	err = -ENOENT;
  	if (sk == NULL)
  		goto out_nosk;
f65c1b534   Pavel Emelyanov   sock_diag: Genera...
57
  	err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
a925aa00a   Pavel Emelyanov   udp_diag: Impleme...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  	if (err)
  		goto out;
  
  	err = -ENOMEM;
  	rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) +
  				     sizeof(struct inet_diag_meminfo) +
  				     64)), GFP_KERNEL);
  	if (!rep)
  		goto out;
  
  	err = inet_sk_diag_fill(sk, NULL, rep, req,
  			   NETLINK_CB(in_skb).pid,
  			   nlh->nlmsg_seq, 0, nlh);
  	if (err < 0) {
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(rep);
  		goto out;
  	}
  	err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid,
  			      MSG_DONTWAIT);
  	if (err > 0)
  		err = 0;
  out:
  	if (sk)
  		sock_put(sk);
  out_nosk:
  	return err;
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
85
86
87
  }
  
  static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
88
  		struct inet_diag_req_v2 *r, struct nlattr *bc)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
89
  {
b6d640c22   Pavel Emelyanov   udp_diag: Impleme...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  	int num, s_num, slot, s_slot;
  
  	s_slot = cb->args[0];
  	num = s_num = cb->args[1];
  
  	for (slot = s_slot; slot <= table->mask; num = s_num = 0, slot++) {
  		struct sock *sk;
  		struct hlist_nulls_node *node;
  		struct udp_hslot *hslot = &table->hash[slot];
  
  		if (hlist_nulls_empty(&hslot->head))
  			continue;
  
  		spin_lock_bh(&hslot->lock);
  		sk_nulls_for_each(sk, node, &hslot->head) {
  			struct inet_sock *inet = inet_sk(sk);
  
  			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;
  
  			if (sk_diag_dump(sk, skb, cb, r, bc) < 0) {
  				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...
133
134
135
  }
  
  static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
136
  		struct inet_diag_req_v2 *r, struct nlattr *bc)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
137
138
139
140
141
  {
  	udp_dump(&udp_table, skb, cb, r, bc);
  }
  
  static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
142
  		struct inet_diag_req_v2 *req)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
143
144
145
146
147
148
149
150
151
152
153
  {
  	return udp_dump_one(&udp_table, in_skb, nlh, req);
  }
  
  static const struct inet_diag_handler udp_diag_handler = {
  	.dump		 = udp_diag_dump,
  	.dump_one	 = udp_diag_dump_one,
  	.idiag_type	 = IPPROTO_UDP,
  };
  
  static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
154
  		struct inet_diag_req_v2 *r, struct nlattr *bc)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
155
156
157
158
159
  {
  	udp_dump(&udplite_table, skb, cb, r, bc);
  }
  
  static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh,
c8991362a   Pavel Emelyanov   inet_diag: Rename...
160
  		struct inet_diag_req_v2 *req)
52b7c59bc   Pavel Emelyanov   udp_diag: Basic s...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  {
  	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,
  	.idiag_type	 = IPPROTO_UDPLITE,
  };
  
  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...
197
198
  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 */);