1 | #include <linux/module.h> |
2 | |
3 | #include <linux/inet_diag.h> |
4 | #include <linux/sock_diag.h> |
5 | |
6 | #include <net/inet_sock.h> |
7 | #include <net/raw.h> |
8 | #include <net/rawv6.h> |
9 | |
10 | #ifdef pr_fmt |
11 | # undef pr_fmt |
12 | #endif |
13 | |
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
15 | |
16 | static struct raw_hashinfo * |
17 | raw_get_hashinfo(const struct inet_diag_req_v2 *r) |
18 | { |
19 | if (r->sdiag_family == AF_INET) { |
20 | return &raw_v4_hashinfo; |
21 | #if IS_ENABLED(CONFIG_IPV6) |
22 | } else if (r->sdiag_family == AF_INET6) { |
23 | return &raw_v6_hashinfo; |
24 | #endif |
25 | } else { |
26 | pr_warn_once("Unexpected inet family %d\n" , |
27 | r->sdiag_family); |
28 | WARN_ON_ONCE(1); |
29 | return ERR_PTR(-EINVAL); |
30 | } |
31 | } |
32 | |
33 | /* |
34 | * Due to requirement of not breaking user API we can't simply |
35 | * rename @pad field in inet_diag_req_v2 structure, instead |
36 | * use helper to figure it out. |
37 | */ |
38 | |
39 | static struct sock *raw_lookup(struct net *net, struct sock *from, |
40 | const struct inet_diag_req_v2 *req) |
41 | { |
42 | struct inet_diag_req_raw *r = (void *)req; |
43 | struct sock *sk = NULL; |
44 | |
45 | if (r->sdiag_family == AF_INET) |
46 | sk = __raw_v4_lookup(net, from, r->sdiag_raw_protocol, |
47 | r->id.idiag_dst[0], |
48 | r->id.idiag_src[0], |
49 | r->id.idiag_if, 0); |
50 | #if IS_ENABLED(CONFIG_IPV6) |
51 | else |
52 | sk = __raw_v6_lookup(net, from, r->sdiag_raw_protocol, |
53 | (const struct in6_addr *)r->id.idiag_src, |
54 | (const struct in6_addr *)r->id.idiag_dst, |
55 | r->id.idiag_if, 0); |
56 | #endif |
57 | return sk; |
58 | } |
59 | |
60 | static struct sock *raw_sock_get(struct net *net, const struct inet_diag_req_v2 *r) |
61 | { |
62 | struct raw_hashinfo *hashinfo = raw_get_hashinfo(r); |
63 | struct sock *sk = NULL, *s; |
64 | int slot; |
65 | |
66 | if (IS_ERR(hashinfo)) |
67 | return ERR_CAST(hashinfo); |
68 | |
69 | read_lock(&hashinfo->lock); |
70 | for (slot = 0; slot < RAW_HTABLE_SIZE; slot++) { |
71 | sk_for_each(s, &hashinfo->ht[slot]) { |
72 | sk = raw_lookup(net, s, r); |
73 | if (sk) { |
74 | /* |
75 | * Grab it and keep until we fill |
76 | * diag meaage to be reported, so |
77 | * caller should call sock_put then. |
78 | * We can do that because we're keeping |
79 | * hashinfo->lock here. |
80 | */ |
81 | sock_hold(sk); |
82 | goto out_unlock; |
83 | } |
84 | } |
85 | } |
86 | out_unlock: |
87 | read_unlock(&hashinfo->lock); |
88 | |
89 | return sk ? sk : ERR_PTR(-ENOENT); |
90 | } |
91 | |
92 | static int raw_diag_dump_one(struct sk_buff *in_skb, |
93 | const struct nlmsghdr *nlh, |
94 | const struct inet_diag_req_v2 *r) |
95 | { |
96 | struct net *net = sock_net(in_skb->sk); |
97 | struct sk_buff *rep; |
98 | struct sock *sk; |
99 | int err; |
100 | |
101 | sk = raw_sock_get(net, r); |
102 | if (IS_ERR(sk)) |
103 | return PTR_ERR(sk); |
104 | |
105 | rep = nlmsg_new(sizeof(struct inet_diag_msg) + |
106 | sizeof(struct inet_diag_meminfo) + 64, |
107 | GFP_KERNEL); |
108 | if (!rep) { |
109 | sock_put(sk); |
110 | return -ENOMEM; |
111 | } |
112 | |
113 | err = inet_sk_diag_fill(sk, NULL, rep, r, |
114 | sk_user_ns(NETLINK_CB(in_skb).sk), |
115 | NETLINK_CB(in_skb).portid, |
116 | nlh->nlmsg_seq, 0, nlh, |
117 | netlink_net_capable(in_skb, CAP_NET_ADMIN)); |
118 | sock_put(sk); |
119 | |
120 | if (err < 0) { |
121 | kfree_skb(rep); |
122 | return err; |
123 | } |
124 | |
125 | err = netlink_unicast(net->diag_nlsk, rep, |
126 | NETLINK_CB(in_skb).portid, |
127 | MSG_DONTWAIT); |
128 | if (err > 0) |
129 | err = 0; |
130 | return err; |
131 | } |
132 | |
133 | static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, |
134 | struct netlink_callback *cb, |
135 | const struct inet_diag_req_v2 *r, |
136 | struct nlattr *bc, bool net_admin) |
137 | { |
138 | if (!inet_diag_bc_sk(bc, sk)) |
139 | return 0; |
140 | |
141 | return inet_sk_diag_fill(sk, NULL, skb, r, |
142 | sk_user_ns(NETLINK_CB(cb->skb).sk), |
143 | NETLINK_CB(cb->skb).portid, |
144 | cb->nlh->nlmsg_seq, NLM_F_MULTI, |
145 | cb->nlh, net_admin); |
146 | } |
147 | |
148 | static void raw_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, |
149 | const struct inet_diag_req_v2 *r, struct nlattr *bc) |
150 | { |
151 | bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); |
152 | struct raw_hashinfo *hashinfo = raw_get_hashinfo(r); |
153 | struct net *net = sock_net(skb->sk); |
154 | int num, s_num, slot, s_slot; |
155 | struct sock *sk = NULL; |
156 | |
157 | if (IS_ERR(hashinfo)) |
158 | return; |
159 | |
160 | s_slot = cb->args[0]; |
161 | num = s_num = cb->args[1]; |
162 | |
163 | read_lock(&hashinfo->lock); |
164 | for (slot = s_slot; slot < RAW_HTABLE_SIZE; s_num = 0, slot++) { |
165 | num = 0; |
166 | |
167 | sk_for_each(sk, &hashinfo->ht[slot]) { |
168 | struct inet_sock *inet = inet_sk(sk); |
169 | |
170 | if (!net_eq(sock_net(sk), net)) |
171 | continue; |
172 | if (num < s_num) |
173 | goto next; |
174 | if (sk->sk_family != r->sdiag_family) |
175 | goto next; |
176 | if (r->id.idiag_sport != inet->inet_sport && |
177 | r->id.idiag_sport) |
178 | goto next; |
179 | if (r->id.idiag_dport != inet->inet_dport && |
180 | r->id.idiag_dport) |
181 | goto next; |
182 | if (sk_diag_dump(sk, skb, cb, r, bc, net_admin) < 0) |
183 | goto out_unlock; |
184 | next: |
185 | num++; |
186 | } |
187 | } |
188 | |
189 | out_unlock: |
190 | read_unlock(&hashinfo->lock); |
191 | |
192 | cb->args[0] = slot; |
193 | cb->args[1] = num; |
194 | } |
195 | |
196 | static void raw_diag_get_info(struct sock *sk, struct inet_diag_msg *r, |
197 | void *info) |
198 | { |
199 | r->idiag_rqueue = sk_rmem_alloc_get(sk); |
200 | r->idiag_wqueue = sk_wmem_alloc_get(sk); |
201 | } |
202 | |
203 | #ifdef CONFIG_INET_DIAG_DESTROY |
204 | static int raw_diag_destroy(struct sk_buff *in_skb, |
205 | const struct inet_diag_req_v2 *r) |
206 | { |
207 | struct net *net = sock_net(in_skb->sk); |
208 | struct sock *sk; |
209 | int err; |
210 | |
211 | sk = raw_sock_get(net, r); |
212 | if (IS_ERR(sk)) |
213 | return PTR_ERR(sk); |
214 | err = sock_diag_destroy(sk, ECONNABORTED); |
215 | sock_put(sk); |
216 | return err; |
217 | } |
218 | #endif |
219 | |
220 | static const struct inet_diag_handler raw_diag_handler = { |
221 | .dump = raw_diag_dump, |
222 | .dump_one = raw_diag_dump_one, |
223 | .idiag_get_info = raw_diag_get_info, |
224 | .idiag_type = IPPROTO_RAW, |
225 | .idiag_info_size = 0, |
226 | #ifdef CONFIG_INET_DIAG_DESTROY |
227 | .destroy = raw_diag_destroy, |
228 | #endif |
229 | }; |
230 | |
231 | static void __always_unused __check_inet_diag_req_raw(void) |
232 | { |
233 | /* |
234 | * Make sure the two structures are identical, |
235 | * except the @pad field. |
236 | */ |
237 | #define __offset_mismatch(m1, m2) \ |
238 | (offsetof(struct inet_diag_req_v2, m1) != \ |
239 | offsetof(struct inet_diag_req_raw, m2)) |
240 | |
241 | BUILD_BUG_ON(sizeof(struct inet_diag_req_v2) != |
242 | sizeof(struct inet_diag_req_raw)); |
243 | BUILD_BUG_ON(__offset_mismatch(sdiag_family, sdiag_family)); |
244 | BUILD_BUG_ON(__offset_mismatch(sdiag_protocol, sdiag_protocol)); |
245 | BUILD_BUG_ON(__offset_mismatch(idiag_ext, idiag_ext)); |
246 | BUILD_BUG_ON(__offset_mismatch(pad, sdiag_raw_protocol)); |
247 | BUILD_BUG_ON(__offset_mismatch(idiag_states, idiag_states)); |
248 | BUILD_BUG_ON(__offset_mismatch(id, id)); |
249 | #undef __offset_mismatch |
250 | } |
251 | |
252 | static int __init raw_diag_init(void) |
253 | { |
254 | return inet_diag_register(&raw_diag_handler); |
255 | } |
256 | |
257 | static void __exit raw_diag_exit(void) |
258 | { |
259 | inet_diag_unregister(&raw_diag_handler); |
260 | } |
261 | |
262 | module_init(raw_diag_init); |
263 | module_exit(raw_diag_exit); |
264 | MODULE_LICENSE("GPL" ); |
265 | MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-255 /* AF_INET - IPPROTO_RAW */); |
266 | MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10-255 /* AF_INET6 - IPPROTO_RAW */); |
267 | |