Branch data Line data Source code
1 : : /* Netfilter messages via netlink socket. Allows for user space
2 : : * protocol helpers and general trouble making from userspace.
3 : : *
4 : : * (C) 2001 by Jay Schulist <jschlst@samba.org>,
5 : : * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
6 : : * (C) 2005-2017 by Pablo Neira Ayuso <pablo@netfilter.org>
7 : : *
8 : : * Initial netfilter messages via netlink development funded and
9 : : * generally made possible by Network Robots, Inc. (www.networkrobots.com)
10 : : *
11 : : * Further development of this code funded by Astaro AG (http://www.astaro.com)
12 : : *
13 : : * This software may be used and distributed according to the terms
14 : : * of the GNU General Public License, incorporated herein by reference.
15 : : */
16 : :
17 : : #include <linux/module.h>
18 : : #include <linux/types.h>
19 : : #include <linux/socket.h>
20 : : #include <linux/kernel.h>
21 : : #include <linux/string.h>
22 : : #include <linux/sockios.h>
23 : : #include <linux/net.h>
24 : : #include <linux/skbuff.h>
25 : : #include <linux/uaccess.h>
26 : : #include <net/sock.h>
27 : : #include <linux/init.h>
28 : : #include <linux/sched/signal.h>
29 : :
30 : : #include <net/netlink.h>
31 : : #include <linux/netfilter/nfnetlink.h>
32 : :
33 : : MODULE_LICENSE("GPL");
34 : : MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
35 : : MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
36 : :
37 : : #define nfnl_dereference_protected(id) \
38 : : rcu_dereference_protected(table[(id)].subsys, \
39 : : lockdep_nfnl_is_held((id)))
40 : :
41 : : #define NFNL_MAX_ATTR_COUNT 32
42 : :
43 : : static struct {
44 : : struct mutex mutex;
45 : : const struct nfnetlink_subsystem __rcu *subsys;
46 : : } table[NFNL_SUBSYS_COUNT];
47 : :
48 : : static const int nfnl_group2type[NFNLGRP_MAX+1] = {
49 : : [NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK,
50 : : [NFNLGRP_CONNTRACK_UPDATE] = NFNL_SUBSYS_CTNETLINK,
51 : : [NFNLGRP_CONNTRACK_DESTROY] = NFNL_SUBSYS_CTNETLINK,
52 : : [NFNLGRP_CONNTRACK_EXP_NEW] = NFNL_SUBSYS_CTNETLINK_EXP,
53 : : [NFNLGRP_CONNTRACK_EXP_UPDATE] = NFNL_SUBSYS_CTNETLINK_EXP,
54 : : [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
55 : : [NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES,
56 : : [NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT,
57 : : [NFNLGRP_NFTRACE] = NFNL_SUBSYS_NFTABLES,
58 : : };
59 : :
60 : 234 : void nfnl_lock(__u8 subsys_id)
61 : : {
62 : 0 : mutex_lock(&table[subsys_id].mutex);
63 : 0 : }
64 : : EXPORT_SYMBOL_GPL(nfnl_lock);
65 : :
66 : 234 : void nfnl_unlock(__u8 subsys_id)
67 : : {
68 : 0 : mutex_unlock(&table[subsys_id].mutex);
69 : 0 : }
70 : : EXPORT_SYMBOL_GPL(nfnl_unlock);
71 : :
72 : : #ifdef CONFIG_PROVE_LOCKING
73 : : bool lockdep_nfnl_is_held(u8 subsys_id)
74 : : {
75 : : return lockdep_is_held(&table[subsys_id].mutex);
76 : : }
77 : : EXPORT_SYMBOL_GPL(lockdep_nfnl_is_held);
78 : : #endif
79 : :
80 : 234 : int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
81 : : {
82 : 234 : u8 cb_id;
83 : :
84 : : /* Sanity-check attr_count size to avoid stack buffer overflow. */
85 [ + + ]: 1326 : for (cb_id = 0; cb_id < n->cb_count; cb_id++)
86 [ - + + - ]: 1092 : if (WARN_ON(n->cb[cb_id].attr_count > NFNL_MAX_ATTR_COUNT))
87 : : return -EINVAL;
88 : :
89 : 234 : nfnl_lock(n->subsys_id);
90 [ - + ]: 234 : if (table[n->subsys_id].subsys) {
91 : 0 : nfnl_unlock(n->subsys_id);
92 : 0 : return -EBUSY;
93 : : }
94 : 234 : rcu_assign_pointer(table[n->subsys_id].subsys, n);
95 : 234 : nfnl_unlock(n->subsys_id);
96 : :
97 : 234 : return 0;
98 : : }
99 : : EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
100 : :
101 : 0 : int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
102 : : {
103 : 0 : nfnl_lock(n->subsys_id);
104 : 0 : table[n->subsys_id].subsys = NULL;
105 : 0 : nfnl_unlock(n->subsys_id);
106 : 0 : synchronize_rcu();
107 : 0 : return 0;
108 : : }
109 : : EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
110 : :
111 : 0 : static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u16 type)
112 : : {
113 : 0 : u8 subsys_id = NFNL_SUBSYS_ID(type);
114 : :
115 : 0 : if (subsys_id >= NFNL_SUBSYS_COUNT)
116 : : return NULL;
117 : :
118 [ # # # # ]: 0 : return rcu_dereference(table[subsys_id].subsys);
119 : : }
120 : :
121 : : static inline const struct nfnl_callback *
122 : 0 : nfnetlink_find_client(u16 type, const struct nfnetlink_subsystem *ss)
123 : : {
124 : 0 : u8 cb_id = NFNL_MSG_TYPE(type);
125 : :
126 [ # # ]: 0 : if (cb_id >= ss->cb_count)
127 : : return NULL;
128 : :
129 : 0 : return &ss->cb[cb_id];
130 : : }
131 : :
132 : 0 : int nfnetlink_has_listeners(struct net *net, unsigned int group)
133 : : {
134 : 0 : return netlink_has_listeners(net->nfnl, group);
135 : : }
136 : : EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
137 : :
138 : 0 : int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid,
139 : : unsigned int group, int echo, gfp_t flags)
140 : : {
141 : 0 : return nlmsg_notify(net->nfnl, skb, portid, group, echo, flags);
142 : : }
143 : : EXPORT_SYMBOL_GPL(nfnetlink_send);
144 : :
145 : 0 : int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error)
146 : : {
147 : 0 : return netlink_set_err(net->nfnl, portid, group, error);
148 : : }
149 : : EXPORT_SYMBOL_GPL(nfnetlink_set_err);
150 : :
151 : 0 : int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
152 : : int flags)
153 : : {
154 : 0 : return netlink_unicast(net->nfnl, skb, portid, flags);
155 : : }
156 : : EXPORT_SYMBOL_GPL(nfnetlink_unicast);
157 : :
158 : : /* Process one complete nfnetlink message. */
159 : 0 : static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
160 : : struct netlink_ext_ack *extack)
161 : : {
162 [ # # ]: 0 : struct net *net = sock_net(skb->sk);
163 : 0 : const struct nfnl_callback *nc;
164 : 0 : const struct nfnetlink_subsystem *ss;
165 : 0 : int type, err;
166 : :
167 : : /* All the messages must at least contain nfgenmsg */
168 [ # # ]: 0 : if (nlmsg_len(nlh) < sizeof(struct nfgenmsg))
169 : : return 0;
170 : :
171 : 0 : type = nlh->nlmsg_type;
172 : 0 : replay:
173 : 0 : rcu_read_lock();
174 [ # # ]: 0 : ss = nfnetlink_get_subsys(type);
175 [ # # ]: 0 : if (!ss) {
176 : : #ifdef CONFIG_MODULES
177 : 0 : rcu_read_unlock();
178 : 0 : request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
179 : 0 : rcu_read_lock();
180 [ # # ]: 0 : ss = nfnetlink_get_subsys(type);
181 [ # # ]: 0 : if (!ss)
182 : : #endif
183 : : {
184 : 0 : rcu_read_unlock();
185 : 0 : return -EINVAL;
186 : : }
187 : : }
188 : :
189 [ # # ]: 0 : nc = nfnetlink_find_client(type, ss);
190 [ # # ]: 0 : if (!nc) {
191 : 0 : rcu_read_unlock();
192 : 0 : return -EINVAL;
193 : : }
194 : :
195 : : {
196 [ # # ]: 0 : int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
197 : 0 : u8 cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
198 : 0 : struct nlattr *cda[NFNL_MAX_ATTR_COUNT + 1];
199 : 0 : struct nlattr *attr = (void *)nlh + min_len;
200 : 0 : int attrlen = nlh->nlmsg_len - min_len;
201 : 0 : __u8 subsys_id = NFNL_SUBSYS_ID(type);
202 : :
203 : : /* Sanity-check NFNL_MAX_ATTR_COUNT */
204 [ # # ]: 0 : if (ss->cb[cb_id].attr_count > NFNL_MAX_ATTR_COUNT) {
205 : 0 : rcu_read_unlock();
206 : 0 : return -ENOMEM;
207 : : }
208 : :
209 : 0 : err = nla_parse_deprecated(cda, ss->cb[cb_id].attr_count,
210 : : attr, attrlen,
211 : : ss->cb[cb_id].policy, extack);
212 [ # # ]: 0 : if (err < 0) {
213 : 0 : rcu_read_unlock();
214 : 0 : return err;
215 : : }
216 : :
217 [ # # ]: 0 : if (nc->call_rcu) {
218 : 0 : err = nc->call_rcu(net, net->nfnl, skb, nlh,
219 : : (const struct nlattr **)cda,
220 : : extack);
221 : 0 : rcu_read_unlock();
222 : : } else {
223 : 0 : rcu_read_unlock();
224 : 0 : nfnl_lock(subsys_id);
225 [ # # # # ]: 0 : if (nfnl_dereference_protected(subsys_id) != ss ||
226 : : nfnetlink_find_client(type, ss) != nc)
227 : : err = -EAGAIN;
228 [ # # ]: 0 : else if (nc->call)
229 : 0 : err = nc->call(net, net->nfnl, skb, nlh,
230 : : (const struct nlattr **)cda,
231 : : extack);
232 : : else
233 : : err = -EINVAL;
234 : 0 : nfnl_unlock(subsys_id);
235 : : }
236 [ # # ]: 0 : if (err == -EAGAIN)
237 : 0 : goto replay;
238 : : return err;
239 : : }
240 : : }
241 : :
242 : : struct nfnl_err {
243 : : struct list_head head;
244 : : struct nlmsghdr *nlh;
245 : : int err;
246 : : struct netlink_ext_ack extack;
247 : : };
248 : :
249 : 0 : static int nfnl_err_add(struct list_head *list, struct nlmsghdr *nlh, int err,
250 : : const struct netlink_ext_ack *extack)
251 : : {
252 : 0 : struct nfnl_err *nfnl_err;
253 : :
254 : 0 : nfnl_err = kmalloc(sizeof(struct nfnl_err), GFP_KERNEL);
255 [ # # ]: 0 : if (nfnl_err == NULL)
256 : : return -ENOMEM;
257 : :
258 : 0 : nfnl_err->nlh = nlh;
259 : 0 : nfnl_err->err = err;
260 : 0 : nfnl_err->extack = *extack;
261 : 0 : list_add_tail(&nfnl_err->head, list);
262 : :
263 : 0 : return 0;
264 : : }
265 : :
266 : 0 : static void nfnl_err_del(struct nfnl_err *nfnl_err)
267 : : {
268 : 0 : list_del(&nfnl_err->head);
269 : 0 : kfree(nfnl_err);
270 : : }
271 : :
272 : 0 : static void nfnl_err_reset(struct list_head *err_list)
273 : : {
274 : 0 : struct nfnl_err *nfnl_err, *next;
275 : :
276 [ # # ]: 0 : list_for_each_entry_safe(nfnl_err, next, err_list, head)
277 : 0 : nfnl_err_del(nfnl_err);
278 : 0 : }
279 : :
280 : 0 : static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
281 : : {
282 : 0 : struct nfnl_err *nfnl_err, *next;
283 : :
284 [ # # ]: 0 : list_for_each_entry_safe(nfnl_err, next, err_list, head) {
285 : 0 : netlink_ack(skb, nfnl_err->nlh, nfnl_err->err,
286 : 0 : &nfnl_err->extack);
287 : 0 : nfnl_err_del(nfnl_err);
288 : : }
289 : 0 : }
290 : :
291 : : enum {
292 : : NFNL_BATCH_FAILURE = (1 << 0),
293 : : NFNL_BATCH_DONE = (1 << 1),
294 : : NFNL_BATCH_REPLAY = (1 << 2),
295 : : };
296 : :
297 : 0 : static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
298 : : u16 subsys_id, u32 genid)
299 : : {
300 : 0 : struct sk_buff *oskb = skb;
301 [ # # ]: 0 : struct net *net = sock_net(skb->sk);
302 : 0 : const struct nfnetlink_subsystem *ss;
303 : 0 : const struct nfnl_callback *nc;
304 : 0 : struct netlink_ext_ack extack;
305 : 0 : LIST_HEAD(err_list);
306 : 0 : u32 status;
307 : 0 : int err;
308 : :
309 [ # # ]: 0 : if (subsys_id >= NFNL_SUBSYS_COUNT)
310 : 0 : return netlink_ack(skb, nlh, -EINVAL, NULL);
311 : 0 : replay:
312 : 0 : status = 0;
313 : :
314 : 0 : skb = netlink_skb_clone(oskb, GFP_KERNEL);
315 [ # # ]: 0 : if (!skb)
316 : 0 : return netlink_ack(oskb, nlh, -ENOMEM, NULL);
317 : :
318 : 0 : nfnl_lock(subsys_id);
319 : 0 : ss = nfnl_dereference_protected(subsys_id);
320 [ # # ]: 0 : if (!ss) {
321 : : #ifdef CONFIG_MODULES
322 : 0 : nfnl_unlock(subsys_id);
323 : 0 : request_module("nfnetlink-subsys-%d", subsys_id);
324 : 0 : nfnl_lock(subsys_id);
325 : 0 : ss = nfnl_dereference_protected(subsys_id);
326 [ # # ]: 0 : if (!ss)
327 : : #endif
328 : : {
329 : 0 : nfnl_unlock(subsys_id);
330 : 0 : netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
331 : 0 : return kfree_skb(skb);
332 : : }
333 : : }
334 : :
335 [ # # # # : 0 : if (!ss->valid_genid || !ss->commit || !ss->abort) {
# # ]
336 : 0 : nfnl_unlock(subsys_id);
337 : 0 : netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
338 : 0 : return kfree_skb(skb);
339 : : }
340 : :
341 [ # # ]: 0 : if (!try_module_get(ss->owner)) {
342 : 0 : nfnl_unlock(subsys_id);
343 : 0 : netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
344 : 0 : return kfree_skb(skb);
345 : : }
346 : :
347 [ # # ]: 0 : if (!ss->valid_genid(net, genid)) {
348 : 0 : module_put(ss->owner);
349 : 0 : nfnl_unlock(subsys_id);
350 : 0 : netlink_ack(oskb, nlh, -ERESTART, NULL);
351 : 0 : return kfree_skb(skb);
352 : : }
353 : :
354 : 0 : nfnl_unlock(subsys_id);
355 : :
356 [ # # ]: 0 : while (skb->len >= nlmsg_total_size(0)) {
357 : 0 : int msglen, type;
358 : :
359 [ # # ]: 0 : if (fatal_signal_pending(current)) {
360 : 0 : nfnl_err_reset(&err_list);
361 : 0 : err = -EINTR;
362 : 0 : status = NFNL_BATCH_FAILURE;
363 : 0 : goto done;
364 : : }
365 : :
366 : 0 : memset(&extack, 0, sizeof(extack));
367 [ # # ]: 0 : nlh = nlmsg_hdr(skb);
368 : 0 : err = 0;
369 : :
370 [ # # ]: 0 : if (nlh->nlmsg_len < NLMSG_HDRLEN ||
371 [ # # # # ]: 0 : skb->len < nlh->nlmsg_len ||
372 : : nlmsg_len(nlh) < sizeof(struct nfgenmsg)) {
373 : 0 : nfnl_err_reset(&err_list);
374 : 0 : status |= NFNL_BATCH_FAILURE;
375 : 0 : goto done;
376 : : }
377 : :
378 : : /* Only requests are handled by the kernel */
379 [ # # ]: 0 : if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
380 : 0 : err = -EINVAL;
381 : 0 : goto ack;
382 : : }
383 : :
384 : 0 : type = nlh->nlmsg_type;
385 [ # # ]: 0 : if (type == NFNL_MSG_BATCH_BEGIN) {
386 : : /* Malformed: Batch begin twice */
387 : 0 : nfnl_err_reset(&err_list);
388 : 0 : status |= NFNL_BATCH_FAILURE;
389 : 0 : goto done;
390 [ # # ]: 0 : } else if (type == NFNL_MSG_BATCH_END) {
391 : 0 : status |= NFNL_BATCH_DONE;
392 : 0 : goto done;
393 [ # # ]: 0 : } else if (type < NLMSG_MIN_TYPE) {
394 : 0 : err = -EINVAL;
395 : 0 : goto ack;
396 : : }
397 : :
398 : : /* We only accept a batch with messages for the same
399 : : * subsystem.
400 : : */
401 [ # # ]: 0 : if (NFNL_SUBSYS_ID(type) != subsys_id) {
402 : 0 : err = -EINVAL;
403 : 0 : goto ack;
404 : : }
405 : :
406 [ # # ]: 0 : nc = nfnetlink_find_client(type, ss);
407 [ # # ]: 0 : if (!nc) {
408 : 0 : err = -EINVAL;
409 : 0 : goto ack;
410 : : }
411 : :
412 : : {
413 [ # # ]: 0 : int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
414 : 0 : u8 cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
415 : 0 : struct nlattr *cda[NFNL_MAX_ATTR_COUNT + 1];
416 : 0 : struct nlattr *attr = (void *)nlh + min_len;
417 : 0 : int attrlen = nlh->nlmsg_len - min_len;
418 : :
419 : : /* Sanity-check NFTA_MAX_ATTR */
420 [ # # ]: 0 : if (ss->cb[cb_id].attr_count > NFNL_MAX_ATTR_COUNT) {
421 : 0 : err = -ENOMEM;
422 : 0 : goto ack;
423 : : }
424 : :
425 : 0 : err = nla_parse_deprecated(cda,
426 : : ss->cb[cb_id].attr_count,
427 : : attr, attrlen,
428 : : ss->cb[cb_id].policy, NULL);
429 [ # # ]: 0 : if (err < 0)
430 : 0 : goto ack;
431 : :
432 [ # # ]: 0 : if (nc->call_batch) {
433 : 0 : err = nc->call_batch(net, net->nfnl, skb, nlh,
434 : : (const struct nlattr **)cda,
435 : : &extack);
436 : : }
437 : :
438 : : /* The lock was released to autoload some module, we
439 : : * have to abort and start from scratch using the
440 : : * original skb.
441 : : */
442 [ # # ]: 0 : if (err == -EAGAIN) {
443 : 0 : status |= NFNL_BATCH_REPLAY;
444 : 0 : goto done;
445 : : }
446 : : }
447 : 0 : ack:
448 [ # # ]: 0 : if (nlh->nlmsg_flags & NLM_F_ACK || err) {
449 : : /* Errors are delivered once the full batch has been
450 : : * processed, this avoids that the same error is
451 : : * reported several times when replaying the batch.
452 : : */
453 [ # # ]: 0 : if (nfnl_err_add(&err_list, nlh, err, &extack) < 0) {
454 : : /* We failed to enqueue an error, reset the
455 : : * list of errors and send OOM to userspace
456 : : * pointing to the batch header.
457 : : */
458 : 0 : nfnl_err_reset(&err_list);
459 : 0 : netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM,
460 : : NULL);
461 : 0 : status |= NFNL_BATCH_FAILURE;
462 : 0 : goto done;
463 : : }
464 : : /* We don't stop processing the batch on errors, thus,
465 : : * userspace gets all the errors that the batch
466 : : * triggers.
467 : : */
468 [ # # ]: 0 : if (err)
469 : 0 : status |= NFNL_BATCH_FAILURE;
470 : : }
471 : :
472 : 0 : msglen = NLMSG_ALIGN(nlh->nlmsg_len);
473 [ # # ]: 0 : if (msglen > skb->len)
474 : 0 : msglen = skb->len;
475 : 0 : skb_pull(skb, msglen);
476 : : }
477 : 0 : done:
478 [ # # ]: 0 : if (status & NFNL_BATCH_REPLAY) {
479 : 0 : ss->abort(net, oskb, true);
480 : 0 : nfnl_err_reset(&err_list);
481 : 0 : kfree_skb(skb);
482 : 0 : module_put(ss->owner);
483 : 0 : goto replay;
484 [ # # ]: 0 : } else if (status == NFNL_BATCH_DONE) {
485 : 0 : err = ss->commit(net, oskb);
486 [ # # ]: 0 : if (err == -EAGAIN) {
487 : 0 : status |= NFNL_BATCH_REPLAY;
488 : 0 : goto done;
489 [ # # ]: 0 : } else if (err) {
490 : 0 : ss->abort(net, oskb, false);
491 : 0 : netlink_ack(oskb, nlmsg_hdr(oskb), err, NULL);
492 : : }
493 : : } else {
494 : 0 : ss->abort(net, oskb, false);
495 : : }
496 [ # # ]: 0 : if (ss->cleanup)
497 : 0 : ss->cleanup(net);
498 : :
499 : 0 : nfnl_err_deliver(&err_list, oskb);
500 : 0 : kfree_skb(skb);
501 : 0 : module_put(ss->owner);
502 : : }
503 : :
504 : : static const struct nla_policy nfnl_batch_policy[NFNL_BATCH_MAX + 1] = {
505 : : [NFNL_BATCH_GENID] = { .type = NLA_U32 },
506 : : };
507 : :
508 : 0 : static void nfnetlink_rcv_skb_batch(struct sk_buff *skb, struct nlmsghdr *nlh)
509 : : {
510 [ # # ]: 0 : int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
511 : 0 : struct nlattr *attr = (void *)nlh + min_len;
512 : 0 : struct nlattr *cda[NFNL_BATCH_MAX + 1];
513 : 0 : int attrlen = nlh->nlmsg_len - min_len;
514 : 0 : struct nfgenmsg *nfgenmsg;
515 : 0 : int msglen, err;
516 : 0 : u32 gen_id = 0;
517 : 0 : u16 res_id;
518 : :
519 : 0 : msglen = NLMSG_ALIGN(nlh->nlmsg_len);
520 [ # # ]: 0 : if (msglen > skb->len)
521 : 0 : msglen = skb->len;
522 : :
523 [ # # ]: 0 : if (skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg))
524 : 0 : return;
525 : :
526 : 0 : err = nla_parse_deprecated(cda, NFNL_BATCH_MAX, attr, attrlen,
527 : : nfnl_batch_policy, NULL);
528 [ # # ]: 0 : if (err < 0) {
529 : 0 : netlink_ack(skb, nlh, err, NULL);
530 : 0 : return;
531 : : }
532 [ # # ]: 0 : if (cda[NFNL_BATCH_GENID])
533 : 0 : gen_id = ntohl(nla_get_be32(cda[NFNL_BATCH_GENID]));
534 : :
535 : 0 : nfgenmsg = nlmsg_data(nlh);
536 : 0 : skb_pull(skb, msglen);
537 : : /* Work around old nft using host byte order */
538 [ # # ]: 0 : if (nfgenmsg->res_id == NFNL_SUBSYS_NFTABLES)
539 : : res_id = NFNL_SUBSYS_NFTABLES;
540 : : else
541 : 0 : res_id = ntohs(nfgenmsg->res_id);
542 : :
543 : 0 : nfnetlink_rcv_batch(skb, nlh, res_id, gen_id);
544 : : }
545 : :
546 : 0 : static void nfnetlink_rcv(struct sk_buff *skb)
547 : : {
548 [ # # ]: 0 : struct nlmsghdr *nlh = nlmsg_hdr(skb);
549 : :
550 [ # # ]: 0 : if (skb->len < NLMSG_HDRLEN ||
551 [ # # # # ]: 0 : nlh->nlmsg_len < NLMSG_HDRLEN ||
552 : : skb->len < nlh->nlmsg_len)
553 : : return;
554 : :
555 [ # # ]: 0 : if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
556 : 0 : netlink_ack(skb, nlh, -EPERM, NULL);
557 : 0 : return;
558 : : }
559 : :
560 [ # # ]: 0 : if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN)
561 : 0 : nfnetlink_rcv_skb_batch(skb, nlh);
562 : : else
563 : 0 : netlink_rcv_skb(skb, nfnetlink_rcv_msg);
564 : : }
565 : :
566 : : #ifdef CONFIG_MODULES
567 : 0 : static int nfnetlink_bind(struct net *net, int group)
568 : : {
569 : 0 : const struct nfnetlink_subsystem *ss;
570 : 0 : int type;
571 : :
572 [ # # ]: 0 : if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX)
573 : : return 0;
574 : :
575 : 0 : type = nfnl_group2type[group];
576 : :
577 : 0 : rcu_read_lock();
578 [ # # ]: 0 : ss = nfnetlink_get_subsys(type << 8);
579 : 0 : rcu_read_unlock();
580 [ # # ]: 0 : if (!ss)
581 : 0 : request_module_nowait("nfnetlink-subsys-%d", type);
582 : : return 0;
583 : : }
584 : : #endif
585 : :
586 : 78 : static int __net_init nfnetlink_net_init(struct net *net)
587 : : {
588 : 78 : struct sock *nfnl;
589 : 78 : struct netlink_kernel_cfg cfg = {
590 : : .groups = NFNLGRP_MAX,
591 : : .input = nfnetlink_rcv,
592 : : #ifdef CONFIG_MODULES
593 : : .bind = nfnetlink_bind,
594 : : #endif
595 : : };
596 : :
597 : 78 : nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg);
598 [ + - ]: 78 : if (!nfnl)
599 : : return -ENOMEM;
600 : 78 : net->nfnl_stash = nfnl;
601 : 78 : rcu_assign_pointer(net->nfnl, nfnl);
602 : 78 : return 0;
603 : : }
604 : :
605 : 0 : static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
606 : : {
607 : 0 : struct net *net;
608 : :
609 [ # # ]: 0 : list_for_each_entry(net, net_exit_list, exit_list)
610 : 0 : RCU_INIT_POINTER(net->nfnl, NULL);
611 : 0 : synchronize_net();
612 [ # # ]: 0 : list_for_each_entry(net, net_exit_list, exit_list)
613 : 0 : netlink_kernel_release(net->nfnl_stash);
614 : 0 : }
615 : :
616 : : static struct pernet_operations nfnetlink_net_ops = {
617 : : .init = nfnetlink_net_init,
618 : : .exit_batch = nfnetlink_net_exit_batch,
619 : : };
620 : :
621 : 78 : static int __init nfnetlink_init(void)
622 : : {
623 : 78 : int i;
624 : :
625 [ + + ]: 780 : for (i = NFNLGRP_NONE + 1; i <= NFNLGRP_MAX; i++)
626 [ - + ]: 702 : BUG_ON(nfnl_group2type[i] == NFNL_SUBSYS_NONE);
627 : :
628 [ + + ]: 1014 : for (i=0; i<NFNL_SUBSYS_COUNT; i++)
629 : 936 : mutex_init(&table[i].mutex);
630 : :
631 : 78 : return register_pernet_subsys(&nfnetlink_net_ops);
632 : : }
633 : :
634 : 0 : static void __exit nfnetlink_exit(void)
635 : : {
636 : 0 : unregister_pernet_subsys(&nfnetlink_net_ops);
637 : 0 : }
638 : : module_init(nfnetlink_init);
639 : : module_exit(nfnetlink_exit);
|