]>
Commit | Line | Data |
---|---|---|
96518518 PM |
1 | /* |
2 | * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * Development of this code funded by Astaro AG (http://www.astaro.com/) | |
9 | */ | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/list.h> | |
14 | #include <linux/skbuff.h> | |
15 | #include <linux/netlink.h> | |
16 | #include <linux/netfilter.h> | |
17 | #include <linux/netfilter/nfnetlink.h> | |
18 | #include <linux/netfilter/nf_tables.h> | |
19 | #include <net/netfilter/nf_tables_core.h> | |
20 | #include <net/netfilter/nf_tables.h> | |
21 | #include <net/sock.h> | |
22 | ||
23 | static LIST_HEAD(nf_tables_afinfo); | |
24 | static LIST_HEAD(nf_tables_expressions); | |
25 | ||
26 | /** | |
27 | * nft_register_afinfo - register nf_tables address family info | |
28 | * | |
29 | * @afi: address family info to register | |
30 | * | |
31 | * Register the address family for use with nf_tables. Returns zero on | |
32 | * success or a negative errno code otherwise. | |
33 | */ | |
34 | int nft_register_afinfo(struct nft_af_info *afi) | |
35 | { | |
36 | INIT_LIST_HEAD(&afi->tables); | |
37 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
38 | list_add_tail(&afi->list, &nf_tables_afinfo); | |
39 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
40 | return 0; | |
41 | } | |
42 | EXPORT_SYMBOL_GPL(nft_register_afinfo); | |
43 | ||
44 | /** | |
45 | * nft_unregister_afinfo - unregister nf_tables address family info | |
46 | * | |
47 | * @afi: address family info to unregister | |
48 | * | |
49 | * Unregister the address family for use with nf_tables. | |
50 | */ | |
51 | void nft_unregister_afinfo(struct nft_af_info *afi) | |
52 | { | |
53 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
54 | list_del(&afi->list); | |
55 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
56 | } | |
57 | EXPORT_SYMBOL_GPL(nft_unregister_afinfo); | |
58 | ||
59 | static struct nft_af_info *nft_afinfo_lookup(int family) | |
60 | { | |
61 | struct nft_af_info *afi; | |
62 | ||
63 | list_for_each_entry(afi, &nf_tables_afinfo, list) { | |
64 | if (afi->family == family) | |
65 | return afi; | |
66 | } | |
67 | return NULL; | |
68 | } | |
69 | ||
70 | static struct nft_af_info *nf_tables_afinfo_lookup(int family, bool autoload) | |
71 | { | |
72 | struct nft_af_info *afi; | |
73 | ||
74 | afi = nft_afinfo_lookup(family); | |
75 | if (afi != NULL) | |
76 | return afi; | |
77 | #ifdef CONFIG_MODULES | |
78 | if (autoload) { | |
79 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
80 | request_module("nft-afinfo-%u", family); | |
81 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
82 | afi = nft_afinfo_lookup(family); | |
83 | if (afi != NULL) | |
84 | return ERR_PTR(-EAGAIN); | |
85 | } | |
86 | #endif | |
87 | return ERR_PTR(-EAFNOSUPPORT); | |
88 | } | |
89 | ||
90 | /* | |
91 | * Tables | |
92 | */ | |
93 | ||
94 | static struct nft_table *nft_table_lookup(const struct nft_af_info *afi, | |
95 | const struct nlattr *nla) | |
96 | { | |
97 | struct nft_table *table; | |
98 | ||
99 | list_for_each_entry(table, &afi->tables, list) { | |
100 | if (!nla_strcmp(nla, table->name)) | |
101 | return table; | |
102 | } | |
103 | return NULL; | |
104 | } | |
105 | ||
106 | static struct nft_table *nf_tables_table_lookup(const struct nft_af_info *afi, | |
107 | const struct nlattr *nla, | |
108 | bool autoload) | |
109 | { | |
110 | struct nft_table *table; | |
111 | ||
112 | if (nla == NULL) | |
113 | return ERR_PTR(-EINVAL); | |
114 | ||
115 | table = nft_table_lookup(afi, nla); | |
116 | if (table != NULL) | |
117 | return table; | |
118 | ||
119 | #ifdef CONFIG_MODULES | |
120 | if (autoload) { | |
121 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
122 | request_module("nft-table-%u-%*.s", afi->family, | |
123 | nla_len(nla)-1, (const char *)nla_data(nla)); | |
124 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
125 | if (nft_table_lookup(afi, nla)) | |
126 | return ERR_PTR(-EAGAIN); | |
127 | } | |
128 | #endif | |
129 | return ERR_PTR(-ENOENT); | |
130 | } | |
131 | ||
132 | static inline u64 nf_tables_alloc_handle(struct nft_table *table) | |
133 | { | |
134 | return ++table->hgenerator; | |
135 | } | |
136 | ||
137 | static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { | |
138 | [NFTA_TABLE_NAME] = { .type = NLA_STRING }, | |
139 | }; | |
140 | ||
141 | static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq, | |
142 | int event, u32 flags, int family, | |
143 | const struct nft_table *table) | |
144 | { | |
145 | struct nlmsghdr *nlh; | |
146 | struct nfgenmsg *nfmsg; | |
147 | ||
148 | event |= NFNL_SUBSYS_NFTABLES << 8; | |
149 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); | |
150 | if (nlh == NULL) | |
151 | goto nla_put_failure; | |
152 | ||
153 | nfmsg = nlmsg_data(nlh); | |
154 | nfmsg->nfgen_family = family; | |
155 | nfmsg->version = NFNETLINK_V0; | |
156 | nfmsg->res_id = 0; | |
157 | ||
158 | if (nla_put_string(skb, NFTA_TABLE_NAME, table->name)) | |
159 | goto nla_put_failure; | |
160 | ||
161 | return nlmsg_end(skb, nlh); | |
162 | ||
163 | nla_put_failure: | |
164 | nlmsg_trim(skb, nlh); | |
165 | return -1; | |
166 | } | |
167 | ||
168 | static int nf_tables_table_notify(const struct sk_buff *oskb, | |
169 | const struct nlmsghdr *nlh, | |
170 | const struct nft_table *table, | |
171 | int event, int family) | |
172 | { | |
173 | struct sk_buff *skb; | |
174 | u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; | |
175 | u32 seq = nlh ? nlh->nlmsg_seq : 0; | |
176 | struct net *net = oskb ? sock_net(oskb->sk) : &init_net; | |
177 | bool report; | |
178 | int err; | |
179 | ||
180 | report = nlh ? nlmsg_report(nlh) : false; | |
181 | if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) | |
182 | return 0; | |
183 | ||
184 | err = -ENOBUFS; | |
185 | skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | |
186 | if (skb == NULL) | |
187 | goto err; | |
188 | ||
189 | err = nf_tables_fill_table_info(skb, portid, seq, event, 0, | |
190 | family, table); | |
191 | if (err < 0) { | |
192 | kfree_skb(skb); | |
193 | goto err; | |
194 | } | |
195 | ||
196 | err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, | |
197 | GFP_KERNEL); | |
198 | err: | |
199 | if (err < 0) | |
200 | nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); | |
201 | return err; | |
202 | } | |
203 | ||
204 | static int nf_tables_dump_tables(struct sk_buff *skb, | |
205 | struct netlink_callback *cb) | |
206 | { | |
207 | const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); | |
208 | const struct nft_af_info *afi; | |
209 | const struct nft_table *table; | |
210 | unsigned int idx = 0, s_idx = cb->args[0]; | |
211 | int family = nfmsg->nfgen_family; | |
212 | ||
213 | list_for_each_entry(afi, &nf_tables_afinfo, list) { | |
214 | if (family != NFPROTO_UNSPEC && family != afi->family) | |
215 | continue; | |
216 | ||
217 | list_for_each_entry(table, &afi->tables, list) { | |
218 | if (idx < s_idx) | |
219 | goto cont; | |
220 | if (idx > s_idx) | |
221 | memset(&cb->args[1], 0, | |
222 | sizeof(cb->args) - sizeof(cb->args[0])); | |
223 | if (nf_tables_fill_table_info(skb, | |
224 | NETLINK_CB(cb->skb).portid, | |
225 | cb->nlh->nlmsg_seq, | |
226 | NFT_MSG_NEWTABLE, | |
227 | NLM_F_MULTI, | |
228 | afi->family, table) < 0) | |
229 | goto done; | |
230 | cont: | |
231 | idx++; | |
232 | } | |
233 | } | |
234 | done: | |
235 | cb->args[0] = idx; | |
236 | return skb->len; | |
237 | } | |
238 | ||
239 | static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, | |
240 | const struct nlmsghdr *nlh, | |
241 | const struct nlattr * const nla[]) | |
242 | { | |
243 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
244 | const struct nft_af_info *afi; | |
245 | const struct nft_table *table; | |
246 | struct sk_buff *skb2; | |
247 | int family = nfmsg->nfgen_family; | |
248 | int err; | |
249 | ||
250 | if (nlh->nlmsg_flags & NLM_F_DUMP) { | |
251 | struct netlink_dump_control c = { | |
252 | .dump = nf_tables_dump_tables, | |
253 | }; | |
254 | return netlink_dump_start(nlsk, skb, nlh, &c); | |
255 | } | |
256 | ||
257 | afi = nf_tables_afinfo_lookup(family, false); | |
258 | if (IS_ERR(afi)) | |
259 | return PTR_ERR(afi); | |
260 | ||
261 | table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME], false); | |
262 | if (IS_ERR(table)) | |
263 | return PTR_ERR(table); | |
264 | ||
265 | skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | |
266 | if (!skb2) | |
267 | return -ENOMEM; | |
268 | ||
269 | err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid, | |
270 | nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0, | |
271 | family, table); | |
272 | if (err < 0) | |
273 | goto err; | |
274 | ||
275 | return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); | |
276 | ||
277 | err: | |
278 | kfree_skb(skb2); | |
279 | return err; | |
280 | } | |
281 | ||
282 | static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, | |
283 | const struct nlmsghdr *nlh, | |
284 | const struct nlattr * const nla[]) | |
285 | { | |
286 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
287 | const struct nlattr *name; | |
288 | struct nft_af_info *afi; | |
289 | struct nft_table *table; | |
290 | int family = nfmsg->nfgen_family; | |
291 | ||
292 | afi = nf_tables_afinfo_lookup(family, true); | |
293 | if (IS_ERR(afi)) | |
294 | return PTR_ERR(afi); | |
295 | ||
296 | name = nla[NFTA_TABLE_NAME]; | |
297 | table = nf_tables_table_lookup(afi, name, false); | |
298 | if (IS_ERR(table)) { | |
299 | if (PTR_ERR(table) != -ENOENT) | |
300 | return PTR_ERR(table); | |
301 | table = NULL; | |
302 | } | |
303 | ||
304 | if (table != NULL) { | |
305 | if (nlh->nlmsg_flags & NLM_F_EXCL) | |
306 | return -EEXIST; | |
307 | if (nlh->nlmsg_flags & NLM_F_REPLACE) | |
308 | return -EOPNOTSUPP; | |
309 | return 0; | |
310 | } | |
311 | ||
312 | table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL); | |
313 | if (table == NULL) | |
314 | return -ENOMEM; | |
315 | ||
316 | nla_strlcpy(table->name, name, nla_len(name)); | |
317 | INIT_LIST_HEAD(&table->chains); | |
318 | ||
319 | list_add_tail(&table->list, &afi->tables); | |
320 | nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); | |
321 | return 0; | |
322 | } | |
323 | ||
324 | static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, | |
325 | const struct nlmsghdr *nlh, | |
326 | const struct nlattr * const nla[]) | |
327 | { | |
328 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
329 | struct nft_af_info *afi; | |
330 | struct nft_table *table; | |
331 | int family = nfmsg->nfgen_family; | |
332 | ||
333 | afi = nf_tables_afinfo_lookup(family, false); | |
334 | if (IS_ERR(afi)) | |
335 | return PTR_ERR(afi); | |
336 | ||
337 | table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME], false); | |
338 | if (IS_ERR(table)) | |
339 | return PTR_ERR(table); | |
340 | ||
341 | if (table->flags & NFT_TABLE_BUILTIN) | |
342 | return -EOPNOTSUPP; | |
343 | ||
344 | if (table->use) | |
345 | return -EBUSY; | |
346 | ||
347 | list_del(&table->list); | |
348 | nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family); | |
349 | kfree(table); | |
350 | return 0; | |
351 | } | |
352 | ||
353 | static struct nft_table *__nf_tables_table_lookup(const struct nft_af_info *afi, | |
354 | const char *name) | |
355 | { | |
356 | struct nft_table *table; | |
357 | ||
358 | list_for_each_entry(table, &afi->tables, list) { | |
359 | if (!strcmp(name, table->name)) | |
360 | return table; | |
361 | } | |
362 | ||
363 | return ERR_PTR(-ENOENT); | |
364 | } | |
365 | ||
366 | static int nf_tables_chain_notify(const struct sk_buff *oskb, | |
367 | const struct nlmsghdr *nlh, | |
368 | const struct nft_table *table, | |
369 | const struct nft_chain *chain, | |
370 | int event, int family); | |
371 | ||
372 | /** | |
373 | * nft_register_table - register a built-in table | |
374 | * | |
375 | * @table: the table to register | |
376 | * @family: protocol family to register table with | |
377 | * | |
378 | * Register a built-in table for use with nf_tables. Returns zero on | |
379 | * success or a negative errno code otherwise. | |
380 | */ | |
381 | int nft_register_table(struct nft_table *table, int family) | |
382 | { | |
383 | struct nft_af_info *afi; | |
384 | struct nft_table *t; | |
385 | struct nft_chain *chain; | |
386 | int err; | |
387 | ||
388 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
389 | again: | |
390 | afi = nf_tables_afinfo_lookup(family, true); | |
391 | if (IS_ERR(afi)) { | |
392 | err = PTR_ERR(afi); | |
393 | if (err == -EAGAIN) | |
394 | goto again; | |
395 | goto err; | |
396 | } | |
397 | ||
398 | t = __nf_tables_table_lookup(afi, table->name); | |
399 | if (IS_ERR(t)) { | |
400 | err = PTR_ERR(t); | |
401 | if (err != -ENOENT) | |
402 | goto err; | |
403 | t = NULL; | |
404 | } | |
405 | ||
406 | if (t != NULL) { | |
407 | err = -EEXIST; | |
408 | goto err; | |
409 | } | |
410 | ||
411 | table->flags |= NFT_TABLE_BUILTIN; | |
412 | list_add_tail(&table->list, &afi->tables); | |
413 | nf_tables_table_notify(NULL, NULL, table, NFT_MSG_NEWTABLE, family); | |
414 | list_for_each_entry(chain, &table->chains, list) | |
415 | nf_tables_chain_notify(NULL, NULL, table, chain, | |
416 | NFT_MSG_NEWCHAIN, family); | |
417 | err = 0; | |
418 | err: | |
419 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
420 | return err; | |
421 | } | |
422 | EXPORT_SYMBOL_GPL(nft_register_table); | |
423 | ||
424 | /** | |
425 | * nft_unregister_table - unregister a built-in table | |
426 | * | |
427 | * @table: the table to unregister | |
428 | * @family: protocol family to unregister table with | |
429 | * | |
430 | * Unregister a built-in table for use with nf_tables. | |
431 | */ | |
432 | void nft_unregister_table(struct nft_table *table, int family) | |
433 | { | |
434 | struct nft_chain *chain; | |
435 | ||
436 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
437 | list_del(&table->list); | |
438 | list_for_each_entry(chain, &table->chains, list) | |
439 | nf_tables_chain_notify(NULL, NULL, table, chain, | |
440 | NFT_MSG_DELCHAIN, family); | |
441 | nf_tables_table_notify(NULL, NULL, table, NFT_MSG_DELTABLE, family); | |
442 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
443 | } | |
444 | EXPORT_SYMBOL_GPL(nft_unregister_table); | |
445 | ||
446 | /* | |
447 | * Chains | |
448 | */ | |
449 | ||
450 | static struct nft_chain * | |
451 | nf_tables_chain_lookup_byhandle(const struct nft_table *table, u64 handle) | |
452 | { | |
453 | struct nft_chain *chain; | |
454 | ||
455 | list_for_each_entry(chain, &table->chains, list) { | |
456 | if (chain->handle == handle) | |
457 | return chain; | |
458 | } | |
459 | ||
460 | return ERR_PTR(-ENOENT); | |
461 | } | |
462 | ||
463 | static struct nft_chain *nf_tables_chain_lookup(const struct nft_table *table, | |
464 | const struct nlattr *nla) | |
465 | { | |
466 | struct nft_chain *chain; | |
467 | ||
468 | if (nla == NULL) | |
469 | return ERR_PTR(-EINVAL); | |
470 | ||
471 | list_for_each_entry(chain, &table->chains, list) { | |
472 | if (!nla_strcmp(nla, chain->name)) | |
473 | return chain; | |
474 | } | |
475 | ||
476 | return ERR_PTR(-ENOENT); | |
477 | } | |
478 | ||
479 | static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { | |
480 | [NFTA_CHAIN_TABLE] = { .type = NLA_STRING }, | |
481 | [NFTA_CHAIN_HANDLE] = { .type = NLA_U64 }, | |
482 | [NFTA_CHAIN_NAME] = { .type = NLA_STRING, | |
483 | .len = NFT_CHAIN_MAXNAMELEN - 1 }, | |
484 | [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED }, | |
485 | }; | |
486 | ||
487 | static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = { | |
488 | [NFTA_HOOK_HOOKNUM] = { .type = NLA_U32 }, | |
489 | [NFTA_HOOK_PRIORITY] = { .type = NLA_U32 }, | |
490 | }; | |
491 | ||
492 | static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, | |
493 | int event, u32 flags, int family, | |
494 | const struct nft_table *table, | |
495 | const struct nft_chain *chain) | |
496 | { | |
497 | struct nlmsghdr *nlh; | |
498 | struct nfgenmsg *nfmsg; | |
499 | ||
500 | event |= NFNL_SUBSYS_NFTABLES << 8; | |
501 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); | |
502 | if (nlh == NULL) | |
503 | goto nla_put_failure; | |
504 | ||
505 | nfmsg = nlmsg_data(nlh); | |
506 | nfmsg->nfgen_family = family; | |
507 | nfmsg->version = NFNETLINK_V0; | |
508 | nfmsg->res_id = 0; | |
509 | ||
510 | if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name)) | |
511 | goto nla_put_failure; | |
512 | if (nla_put_be64(skb, NFTA_CHAIN_HANDLE, cpu_to_be64(chain->handle))) | |
513 | goto nla_put_failure; | |
514 | if (nla_put_string(skb, NFTA_CHAIN_NAME, chain->name)) | |
515 | goto nla_put_failure; | |
516 | ||
517 | if (chain->flags & NFT_BASE_CHAIN) { | |
518 | const struct nf_hook_ops *ops = &nft_base_chain(chain)->ops; | |
519 | struct nlattr *nest = nla_nest_start(skb, NFTA_CHAIN_HOOK); | |
520 | if (nest == NULL) | |
521 | goto nla_put_failure; | |
522 | if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum))) | |
523 | goto nla_put_failure; | |
524 | if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority))) | |
525 | goto nla_put_failure; | |
526 | nla_nest_end(skb, nest); | |
527 | } | |
528 | ||
529 | return nlmsg_end(skb, nlh); | |
530 | ||
531 | nla_put_failure: | |
532 | nlmsg_trim(skb, nlh); | |
533 | return -1; | |
534 | } | |
535 | ||
536 | static int nf_tables_chain_notify(const struct sk_buff *oskb, | |
537 | const struct nlmsghdr *nlh, | |
538 | const struct nft_table *table, | |
539 | const struct nft_chain *chain, | |
540 | int event, int family) | |
541 | { | |
542 | struct sk_buff *skb; | |
543 | u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; | |
544 | struct net *net = oskb ? sock_net(oskb->sk) : &init_net; | |
545 | u32 seq = nlh ? nlh->nlmsg_seq : 0; | |
546 | bool report; | |
547 | int err; | |
548 | ||
549 | report = nlh ? nlmsg_report(nlh) : false; | |
550 | if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) | |
551 | return 0; | |
552 | ||
553 | err = -ENOBUFS; | |
554 | skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | |
555 | if (skb == NULL) | |
556 | goto err; | |
557 | ||
558 | err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family, | |
559 | table, chain); | |
560 | if (err < 0) { | |
561 | kfree_skb(skb); | |
562 | goto err; | |
563 | } | |
564 | ||
565 | err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, | |
566 | GFP_KERNEL); | |
567 | err: | |
568 | if (err < 0) | |
569 | nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); | |
570 | return err; | |
571 | } | |
572 | ||
573 | static int nf_tables_dump_chains(struct sk_buff *skb, | |
574 | struct netlink_callback *cb) | |
575 | { | |
576 | const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); | |
577 | const struct nft_af_info *afi; | |
578 | const struct nft_table *table; | |
579 | const struct nft_chain *chain; | |
580 | unsigned int idx = 0, s_idx = cb->args[0]; | |
581 | int family = nfmsg->nfgen_family; | |
582 | ||
583 | list_for_each_entry(afi, &nf_tables_afinfo, list) { | |
584 | if (family != NFPROTO_UNSPEC && family != afi->family) | |
585 | continue; | |
586 | ||
587 | list_for_each_entry(table, &afi->tables, list) { | |
588 | list_for_each_entry(chain, &table->chains, list) { | |
589 | if (idx < s_idx) | |
590 | goto cont; | |
591 | if (idx > s_idx) | |
592 | memset(&cb->args[1], 0, | |
593 | sizeof(cb->args) - sizeof(cb->args[0])); | |
594 | if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid, | |
595 | cb->nlh->nlmsg_seq, | |
596 | NFT_MSG_NEWCHAIN, | |
597 | NLM_F_MULTI, | |
598 | afi->family, table, chain) < 0) | |
599 | goto done; | |
600 | cont: | |
601 | idx++; | |
602 | } | |
603 | } | |
604 | } | |
605 | done: | |
606 | cb->args[0] = idx; | |
607 | return skb->len; | |
608 | } | |
609 | ||
610 | ||
611 | static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb, | |
612 | const struct nlmsghdr *nlh, | |
613 | const struct nlattr * const nla[]) | |
614 | { | |
615 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
616 | const struct nft_af_info *afi; | |
617 | const struct nft_table *table; | |
618 | const struct nft_chain *chain; | |
619 | struct sk_buff *skb2; | |
620 | int family = nfmsg->nfgen_family; | |
621 | int err; | |
622 | ||
623 | if (nlh->nlmsg_flags & NLM_F_DUMP) { | |
624 | struct netlink_dump_control c = { | |
625 | .dump = nf_tables_dump_chains, | |
626 | }; | |
627 | return netlink_dump_start(nlsk, skb, nlh, &c); | |
628 | } | |
629 | ||
630 | afi = nf_tables_afinfo_lookup(family, false); | |
631 | if (IS_ERR(afi)) | |
632 | return PTR_ERR(afi); | |
633 | ||
634 | table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE], false); | |
635 | if (IS_ERR(table)) | |
636 | return PTR_ERR(table); | |
637 | ||
638 | chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); | |
639 | if (IS_ERR(chain)) | |
640 | return PTR_ERR(chain); | |
641 | ||
642 | skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | |
643 | if (!skb2) | |
644 | return -ENOMEM; | |
645 | ||
646 | err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid, | |
647 | nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0, | |
648 | family, table, chain); | |
649 | if (err < 0) | |
650 | goto err; | |
651 | ||
652 | return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); | |
653 | ||
654 | err: | |
655 | kfree_skb(skb2); | |
656 | return err; | |
657 | } | |
658 | ||
659 | static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, | |
660 | const struct nlmsghdr *nlh, | |
661 | const struct nlattr * const nla[]) | |
662 | { | |
663 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
664 | const struct nlattr * uninitialized_var(name); | |
665 | const struct nft_af_info *afi; | |
666 | struct nft_table *table; | |
667 | struct nft_chain *chain; | |
668 | struct nft_base_chain *basechain; | |
669 | struct nlattr *ha[NFTA_HOOK_MAX + 1]; | |
670 | int family = nfmsg->nfgen_family; | |
671 | u64 handle = 0; | |
672 | int err; | |
673 | bool create; | |
674 | ||
675 | create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; | |
676 | ||
677 | afi = nf_tables_afinfo_lookup(family, true); | |
678 | if (IS_ERR(afi)) | |
679 | return PTR_ERR(afi); | |
680 | ||
681 | table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE], create); | |
682 | if (IS_ERR(table)) | |
683 | return PTR_ERR(table); | |
684 | ||
685 | if (table->use == UINT_MAX) | |
686 | return -EOVERFLOW; | |
687 | ||
688 | chain = NULL; | |
689 | name = nla[NFTA_CHAIN_NAME]; | |
690 | ||
691 | if (nla[NFTA_CHAIN_HANDLE]) { | |
692 | handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE])); | |
693 | chain = nf_tables_chain_lookup_byhandle(table, handle); | |
694 | if (IS_ERR(chain)) | |
695 | return PTR_ERR(chain); | |
696 | } else { | |
697 | chain = nf_tables_chain_lookup(table, name); | |
698 | if (IS_ERR(chain)) { | |
699 | if (PTR_ERR(chain) != -ENOENT) | |
700 | return PTR_ERR(chain); | |
701 | chain = NULL; | |
702 | } | |
703 | } | |
704 | ||
705 | if (chain != NULL) { | |
706 | if (nlh->nlmsg_flags & NLM_F_EXCL) | |
707 | return -EEXIST; | |
708 | if (nlh->nlmsg_flags & NLM_F_REPLACE) | |
709 | return -EOPNOTSUPP; | |
710 | ||
711 | if (nla[NFTA_CHAIN_HANDLE] && name && | |
712 | !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]))) | |
713 | return -EEXIST; | |
714 | ||
715 | if (nla[NFTA_CHAIN_HANDLE] && name) | |
716 | nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); | |
717 | ||
718 | goto notify; | |
719 | } | |
720 | ||
721 | if (nla[NFTA_CHAIN_HOOK]) { | |
722 | struct nf_hook_ops *ops; | |
723 | ||
724 | err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK], | |
725 | nft_hook_policy); | |
726 | if (err < 0) | |
727 | return err; | |
728 | if (ha[NFTA_HOOK_HOOKNUM] == NULL || | |
729 | ha[NFTA_HOOK_PRIORITY] == NULL) | |
730 | return -EINVAL; | |
731 | if (ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])) >= afi->nhooks) | |
732 | return -EINVAL; | |
733 | ||
734 | basechain = kzalloc(sizeof(*basechain), GFP_KERNEL); | |
735 | if (basechain == NULL) | |
736 | return -ENOMEM; | |
737 | chain = &basechain->chain; | |
738 | ||
739 | ops = &basechain->ops; | |
740 | ops->pf = family; | |
741 | ops->owner = afi->owner; | |
742 | ops->hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); | |
743 | ops->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); | |
744 | ops->priv = chain; | |
745 | ops->hook = nft_do_chain; | |
746 | if (afi->hooks[ops->hooknum]) | |
747 | ops->hook = afi->hooks[ops->hooknum]; | |
748 | ||
749 | chain->flags |= NFT_BASE_CHAIN; | |
750 | } else { | |
751 | chain = kzalloc(sizeof(*chain), GFP_KERNEL); | |
752 | if (chain == NULL) | |
753 | return -ENOMEM; | |
754 | } | |
755 | ||
756 | INIT_LIST_HEAD(&chain->rules); | |
757 | chain->handle = nf_tables_alloc_handle(table); | |
758 | nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); | |
759 | ||
760 | list_add_tail(&chain->list, &table->chains); | |
761 | table->use++; | |
762 | notify: | |
763 | nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, | |
764 | family); | |
765 | return 0; | |
766 | } | |
767 | ||
768 | static void nf_tables_rcu_chain_destroy(struct rcu_head *head) | |
769 | { | |
770 | struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head); | |
771 | ||
772 | BUG_ON(chain->use > 0); | |
773 | ||
774 | if (chain->flags & NFT_BASE_CHAIN) | |
775 | kfree(nft_base_chain(chain)); | |
776 | else | |
777 | kfree(chain); | |
778 | } | |
779 | ||
780 | static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, | |
781 | const struct nlmsghdr *nlh, | |
782 | const struct nlattr * const nla[]) | |
783 | { | |
784 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
785 | const struct nft_af_info *afi; | |
786 | struct nft_table *table; | |
787 | struct nft_chain *chain; | |
788 | int family = nfmsg->nfgen_family; | |
789 | ||
790 | afi = nf_tables_afinfo_lookup(family, false); | |
791 | if (IS_ERR(afi)) | |
792 | return PTR_ERR(afi); | |
793 | ||
794 | table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE], false); | |
795 | if (IS_ERR(table)) | |
796 | return PTR_ERR(table); | |
797 | ||
798 | chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); | |
799 | if (IS_ERR(chain)) | |
800 | return PTR_ERR(chain); | |
801 | ||
802 | if (chain->flags & NFT_CHAIN_BUILTIN) | |
803 | return -EOPNOTSUPP; | |
804 | ||
805 | if (!list_empty(&chain->rules)) | |
806 | return -EBUSY; | |
807 | ||
808 | list_del(&chain->list); | |
809 | table->use--; | |
810 | ||
811 | if (chain->flags & NFT_BASE_CHAIN) | |
812 | nf_unregister_hook(&nft_base_chain(chain)->ops); | |
813 | ||
814 | nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN, | |
815 | family); | |
816 | ||
817 | /* Make sure all rule references are gone before this is released */ | |
818 | call_rcu(&chain->rcu_head, nf_tables_rcu_chain_destroy); | |
819 | return 0; | |
820 | } | |
821 | ||
822 | static void nft_ctx_init(struct nft_ctx *ctx, | |
823 | const struct nft_af_info *afi, | |
824 | const struct nft_table *table, | |
825 | const struct nft_chain *chain) | |
826 | { | |
827 | ctx->afi = afi; | |
828 | ctx->table = table; | |
829 | ctx->chain = chain; | |
830 | } | |
831 | ||
832 | /* | |
833 | * Expressions | |
834 | */ | |
835 | ||
836 | /** | |
837 | * nft_register_expr - register nf_tables expr operations | |
838 | * @ops: expr operations | |
839 | * | |
840 | * Registers the expr operations for use with nf_tables. Returns zero on | |
841 | * success or a negative errno code otherwise. | |
842 | */ | |
843 | int nft_register_expr(struct nft_expr_ops *ops) | |
844 | { | |
845 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
846 | list_add_tail(&ops->list, &nf_tables_expressions); | |
847 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
848 | return 0; | |
849 | } | |
850 | EXPORT_SYMBOL_GPL(nft_register_expr); | |
851 | ||
852 | /** | |
853 | * nft_unregister_expr - unregister nf_tables expr operations | |
854 | * @ops: expr operations | |
855 | * | |
856 | * Unregisters the expr operations for use with nf_tables. | |
857 | */ | |
858 | void nft_unregister_expr(struct nft_expr_ops *ops) | |
859 | { | |
860 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
861 | list_del(&ops->list); | |
862 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
863 | } | |
864 | EXPORT_SYMBOL_GPL(nft_unregister_expr); | |
865 | ||
866 | static const struct nft_expr_ops *__nft_expr_ops_get(struct nlattr *nla) | |
867 | { | |
868 | const struct nft_expr_ops *ops; | |
869 | ||
870 | list_for_each_entry(ops, &nf_tables_expressions, list) { | |
871 | if (!nla_strcmp(nla, ops->name)) | |
872 | return ops; | |
873 | } | |
874 | return NULL; | |
875 | } | |
876 | ||
877 | static const struct nft_expr_ops *nft_expr_ops_get(struct nlattr *nla) | |
878 | { | |
879 | const struct nft_expr_ops *ops; | |
880 | ||
881 | if (nla == NULL) | |
882 | return ERR_PTR(-EINVAL); | |
883 | ||
884 | ops = __nft_expr_ops_get(nla); | |
885 | if (ops != NULL && try_module_get(ops->owner)) | |
886 | return ops; | |
887 | ||
888 | #ifdef CONFIG_MODULES | |
889 | if (ops == NULL) { | |
890 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
891 | request_module("nft-expr-%.*s", | |
892 | nla_len(nla), (char *)nla_data(nla)); | |
893 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
894 | if (__nft_expr_ops_get(nla)) | |
895 | return ERR_PTR(-EAGAIN); | |
896 | } | |
897 | #endif | |
898 | return ERR_PTR(-ENOENT); | |
899 | } | |
900 | ||
901 | static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = { | |
902 | [NFTA_EXPR_NAME] = { .type = NLA_STRING }, | |
903 | [NFTA_EXPR_DATA] = { .type = NLA_NESTED }, | |
904 | }; | |
905 | ||
906 | static int nf_tables_fill_expr_info(struct sk_buff *skb, | |
907 | const struct nft_expr *expr) | |
908 | { | |
909 | if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->name)) | |
910 | goto nla_put_failure; | |
911 | ||
912 | if (expr->ops->dump) { | |
913 | struct nlattr *data = nla_nest_start(skb, NFTA_EXPR_DATA); | |
914 | if (data == NULL) | |
915 | goto nla_put_failure; | |
916 | if (expr->ops->dump(skb, expr) < 0) | |
917 | goto nla_put_failure; | |
918 | nla_nest_end(skb, data); | |
919 | } | |
920 | ||
921 | return skb->len; | |
922 | ||
923 | nla_put_failure: | |
924 | return -1; | |
925 | }; | |
926 | ||
927 | struct nft_expr_info { | |
928 | const struct nft_expr_ops *ops; | |
929 | struct nlattr *tb[NFTA_EXPR_MAX + 1]; | |
930 | }; | |
931 | ||
932 | static int nf_tables_expr_parse(const struct nlattr *nla, | |
933 | struct nft_expr_info *info) | |
934 | { | |
935 | const struct nft_expr_ops *ops; | |
936 | int err; | |
937 | ||
938 | err = nla_parse_nested(info->tb, NFTA_EXPR_MAX, nla, nft_expr_policy); | |
939 | if (err < 0) | |
940 | return err; | |
941 | ||
942 | ops = nft_expr_ops_get(info->tb[NFTA_EXPR_NAME]); | |
943 | if (IS_ERR(ops)) | |
944 | return PTR_ERR(ops); | |
945 | info->ops = ops; | |
946 | return 0; | |
947 | } | |
948 | ||
949 | static int nf_tables_newexpr(const struct nft_ctx *ctx, | |
950 | struct nft_expr_info *info, | |
951 | struct nft_expr *expr) | |
952 | { | |
953 | const struct nft_expr_ops *ops = info->ops; | |
954 | int err; | |
955 | ||
956 | expr->ops = ops; | |
957 | if (ops->init) { | |
958 | struct nlattr *ma[ops->maxattr + 1]; | |
959 | ||
960 | if (info->tb[NFTA_EXPR_DATA]) { | |
961 | err = nla_parse_nested(ma, ops->maxattr, | |
962 | info->tb[NFTA_EXPR_DATA], | |
963 | ops->policy); | |
964 | if (err < 0) | |
965 | goto err1; | |
966 | } else | |
967 | memset(ma, 0, sizeof(ma[0]) * (ops->maxattr + 1)); | |
968 | ||
969 | err = ops->init(ctx, expr, (const struct nlattr **)ma); | |
970 | if (err < 0) | |
971 | goto err1; | |
972 | } | |
973 | ||
974 | info->ops = NULL; | |
975 | return 0; | |
976 | ||
977 | err1: | |
978 | expr->ops = NULL; | |
979 | return err; | |
980 | } | |
981 | ||
982 | static void nf_tables_expr_destroy(struct nft_expr *expr) | |
983 | { | |
984 | if (expr->ops->destroy) | |
985 | expr->ops->destroy(expr); | |
986 | module_put(expr->ops->owner); | |
987 | } | |
988 | ||
989 | /* | |
990 | * Rules | |
991 | */ | |
992 | ||
993 | static struct nft_rule *__nf_tables_rule_lookup(const struct nft_chain *chain, | |
994 | u64 handle) | |
995 | { | |
996 | struct nft_rule *rule; | |
997 | ||
998 | // FIXME: this sucks | |
999 | list_for_each_entry(rule, &chain->rules, list) { | |
1000 | if (handle == rule->handle) | |
1001 | return rule; | |
1002 | } | |
1003 | ||
1004 | return ERR_PTR(-ENOENT); | |
1005 | } | |
1006 | ||
1007 | static struct nft_rule *nf_tables_rule_lookup(const struct nft_chain *chain, | |
1008 | const struct nlattr *nla) | |
1009 | { | |
1010 | if (nla == NULL) | |
1011 | return ERR_PTR(-EINVAL); | |
1012 | ||
1013 | return __nf_tables_rule_lookup(chain, be64_to_cpu(nla_get_be64(nla))); | |
1014 | } | |
1015 | ||
1016 | static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = { | |
1017 | [NFTA_RULE_TABLE] = { .type = NLA_STRING }, | |
1018 | [NFTA_RULE_CHAIN] = { .type = NLA_STRING, | |
1019 | .len = NFT_CHAIN_MAXNAMELEN - 1 }, | |
1020 | [NFTA_RULE_HANDLE] = { .type = NLA_U64 }, | |
1021 | [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED }, | |
1022 | }; | |
1023 | ||
1024 | static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, | |
1025 | int event, u32 flags, int family, | |
1026 | const struct nft_table *table, | |
1027 | const struct nft_chain *chain, | |
1028 | const struct nft_rule *rule) | |
1029 | { | |
1030 | struct nlmsghdr *nlh; | |
1031 | struct nfgenmsg *nfmsg; | |
1032 | const struct nft_expr *expr, *next; | |
1033 | struct nlattr *list; | |
1034 | ||
1035 | event |= NFNL_SUBSYS_NFTABLES << 8; | |
1036 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), | |
1037 | flags); | |
1038 | if (nlh == NULL) | |
1039 | goto nla_put_failure; | |
1040 | ||
1041 | nfmsg = nlmsg_data(nlh); | |
1042 | nfmsg->nfgen_family = family; | |
1043 | nfmsg->version = NFNETLINK_V0; | |
1044 | nfmsg->res_id = 0; | |
1045 | ||
1046 | if (nla_put_string(skb, NFTA_RULE_TABLE, table->name)) | |
1047 | goto nla_put_failure; | |
1048 | if (nla_put_string(skb, NFTA_RULE_CHAIN, chain->name)) | |
1049 | goto nla_put_failure; | |
1050 | if (nla_put_be64(skb, NFTA_RULE_HANDLE, cpu_to_be64(rule->handle))) | |
1051 | goto nla_put_failure; | |
1052 | ||
1053 | list = nla_nest_start(skb, NFTA_RULE_EXPRESSIONS); | |
1054 | if (list == NULL) | |
1055 | goto nla_put_failure; | |
1056 | nft_rule_for_each_expr(expr, next, rule) { | |
1057 | struct nlattr *elem = nla_nest_start(skb, NFTA_LIST_ELEM); | |
1058 | if (elem == NULL) | |
1059 | goto nla_put_failure; | |
1060 | if (nf_tables_fill_expr_info(skb, expr) < 0) | |
1061 | goto nla_put_failure; | |
1062 | nla_nest_end(skb, elem); | |
1063 | } | |
1064 | nla_nest_end(skb, list); | |
1065 | ||
1066 | return nlmsg_end(skb, nlh); | |
1067 | ||
1068 | nla_put_failure: | |
1069 | nlmsg_trim(skb, nlh); | |
1070 | return -1; | |
1071 | } | |
1072 | ||
1073 | static int nf_tables_rule_notify(const struct sk_buff *oskb, | |
1074 | const struct nlmsghdr *nlh, | |
1075 | const struct nft_table *table, | |
1076 | const struct nft_chain *chain, | |
1077 | const struct nft_rule *rule, | |
1078 | int event, u32 flags, int family) | |
1079 | { | |
1080 | struct sk_buff *skb; | |
1081 | u32 portid = NETLINK_CB(oskb).portid; | |
1082 | struct net *net = oskb ? sock_net(oskb->sk) : &init_net; | |
1083 | u32 seq = nlh->nlmsg_seq; | |
1084 | bool report; | |
1085 | int err; | |
1086 | ||
1087 | report = nlmsg_report(nlh); | |
1088 | if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) | |
1089 | return 0; | |
1090 | ||
1091 | err = -ENOBUFS; | |
1092 | skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | |
1093 | if (skb == NULL) | |
1094 | goto err; | |
1095 | ||
1096 | err = nf_tables_fill_rule_info(skb, portid, seq, event, flags, | |
1097 | family, table, chain, rule); | |
1098 | if (err < 0) { | |
1099 | kfree_skb(skb); | |
1100 | goto err; | |
1101 | } | |
1102 | ||
1103 | err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, | |
1104 | GFP_KERNEL); | |
1105 | err: | |
1106 | if (err < 0) | |
1107 | nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); | |
1108 | return err; | |
1109 | } | |
1110 | ||
1111 | static int nf_tables_dump_rules(struct sk_buff *skb, | |
1112 | struct netlink_callback *cb) | |
1113 | { | |
1114 | const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); | |
1115 | const struct nft_af_info *afi; | |
1116 | const struct nft_table *table; | |
1117 | const struct nft_chain *chain; | |
1118 | const struct nft_rule *rule; | |
1119 | unsigned int idx = 0, s_idx = cb->args[0]; | |
1120 | int family = nfmsg->nfgen_family; | |
1121 | ||
1122 | list_for_each_entry(afi, &nf_tables_afinfo, list) { | |
1123 | if (family != NFPROTO_UNSPEC && family != afi->family) | |
1124 | continue; | |
1125 | ||
1126 | list_for_each_entry(table, &afi->tables, list) { | |
1127 | list_for_each_entry(chain, &table->chains, list) { | |
1128 | list_for_each_entry(rule, &chain->rules, list) { | |
1129 | if (idx < s_idx) | |
1130 | goto cont; | |
1131 | if (idx > s_idx) | |
1132 | memset(&cb->args[1], 0, | |
1133 | sizeof(cb->args) - sizeof(cb->args[0])); | |
1134 | if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid, | |
1135 | cb->nlh->nlmsg_seq, | |
1136 | NFT_MSG_NEWRULE, | |
1137 | NLM_F_MULTI | NLM_F_APPEND, | |
1138 | afi->family, table, chain, rule) < 0) | |
1139 | goto done; | |
1140 | cont: | |
1141 | idx++; | |
1142 | } | |
1143 | } | |
1144 | } | |
1145 | } | |
1146 | done: | |
1147 | cb->args[0] = idx; | |
1148 | return skb->len; | |
1149 | } | |
1150 | ||
1151 | static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb, | |
1152 | const struct nlmsghdr *nlh, | |
1153 | const struct nlattr * const nla[]) | |
1154 | { | |
1155 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
1156 | const struct nft_af_info *afi; | |
1157 | const struct nft_table *table; | |
1158 | const struct nft_chain *chain; | |
1159 | const struct nft_rule *rule; | |
1160 | struct sk_buff *skb2; | |
1161 | int family = nfmsg->nfgen_family; | |
1162 | int err; | |
1163 | ||
1164 | if (nlh->nlmsg_flags & NLM_F_DUMP) { | |
1165 | struct netlink_dump_control c = { | |
1166 | .dump = nf_tables_dump_rules, | |
1167 | }; | |
1168 | return netlink_dump_start(nlsk, skb, nlh, &c); | |
1169 | } | |
1170 | ||
1171 | afi = nf_tables_afinfo_lookup(family, false); | |
1172 | if (IS_ERR(afi)) | |
1173 | return PTR_ERR(afi); | |
1174 | ||
1175 | table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE], false); | |
1176 | if (IS_ERR(table)) | |
1177 | return PTR_ERR(table); | |
1178 | ||
1179 | chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); | |
1180 | if (IS_ERR(chain)) | |
1181 | return PTR_ERR(chain); | |
1182 | ||
1183 | rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); | |
1184 | if (IS_ERR(rule)) | |
1185 | return PTR_ERR(rule); | |
1186 | ||
1187 | skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | |
1188 | if (!skb2) | |
1189 | return -ENOMEM; | |
1190 | ||
1191 | err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid, | |
1192 | nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0, | |
1193 | family, table, chain, rule); | |
1194 | if (err < 0) | |
1195 | goto err; | |
1196 | ||
1197 | return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); | |
1198 | ||
1199 | err: | |
1200 | kfree_skb(skb2); | |
1201 | return err; | |
1202 | } | |
1203 | ||
1204 | static void nf_tables_rcu_rule_destroy(struct rcu_head *head) | |
1205 | { | |
1206 | struct nft_rule *rule = container_of(head, struct nft_rule, rcu_head); | |
1207 | struct nft_expr *expr; | |
1208 | ||
1209 | /* | |
1210 | * Careful: some expressions might not be initialized in case this | |
1211 | * is called on error from nf_tables_newrule(). | |
1212 | */ | |
1213 | expr = nft_expr_first(rule); | |
1214 | while (expr->ops && expr != nft_expr_last(rule)) { | |
1215 | nf_tables_expr_destroy(expr); | |
1216 | expr = nft_expr_next(expr); | |
1217 | } | |
1218 | kfree(rule); | |
1219 | } | |
1220 | ||
1221 | static void nf_tables_rule_destroy(struct nft_rule *rule) | |
1222 | { | |
1223 | call_rcu(&rule->rcu_head, nf_tables_rcu_rule_destroy); | |
1224 | } | |
1225 | ||
1226 | #define NFT_RULE_MAXEXPRS 128 | |
1227 | ||
1228 | static struct nft_expr_info *info; | |
1229 | ||
1230 | static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, | |
1231 | const struct nlmsghdr *nlh, | |
1232 | const struct nlattr * const nla[]) | |
1233 | { | |
1234 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
1235 | const struct nft_af_info *afi; | |
1236 | struct nft_table *table; | |
1237 | struct nft_chain *chain; | |
1238 | struct nft_rule *rule, *old_rule = NULL; | |
1239 | struct nft_expr *expr; | |
1240 | struct nft_ctx ctx; | |
1241 | struct nlattr *tmp; | |
1242 | unsigned int size, i, n; | |
1243 | int err, rem; | |
1244 | bool create; | |
1245 | u64 handle; | |
1246 | ||
1247 | create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; | |
1248 | ||
1249 | afi = nf_tables_afinfo_lookup(nfmsg->nfgen_family, create); | |
1250 | if (IS_ERR(afi)) | |
1251 | return PTR_ERR(afi); | |
1252 | ||
1253 | table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE], create); | |
1254 | if (IS_ERR(table)) | |
1255 | return PTR_ERR(table); | |
1256 | ||
1257 | chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); | |
1258 | if (IS_ERR(chain)) | |
1259 | return PTR_ERR(chain); | |
1260 | ||
1261 | if (nla[NFTA_RULE_HANDLE]) { | |
1262 | handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE])); | |
1263 | rule = __nf_tables_rule_lookup(chain, handle); | |
1264 | if (IS_ERR(rule)) | |
1265 | return PTR_ERR(rule); | |
1266 | ||
1267 | if (nlh->nlmsg_flags & NLM_F_EXCL) | |
1268 | return -EEXIST; | |
1269 | if (nlh->nlmsg_flags & NLM_F_REPLACE) | |
1270 | old_rule = rule; | |
1271 | else | |
1272 | return -EOPNOTSUPP; | |
1273 | } else { | |
1274 | if (!create || nlh->nlmsg_flags & NLM_F_REPLACE) | |
1275 | return -EINVAL; | |
1276 | handle = nf_tables_alloc_handle(table); | |
1277 | } | |
1278 | ||
1279 | n = 0; | |
1280 | size = 0; | |
1281 | if (nla[NFTA_RULE_EXPRESSIONS]) { | |
1282 | nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) { | |
1283 | err = -EINVAL; | |
1284 | if (nla_type(tmp) != NFTA_LIST_ELEM) | |
1285 | goto err1; | |
1286 | if (n == NFT_RULE_MAXEXPRS) | |
1287 | goto err1; | |
1288 | err = nf_tables_expr_parse(tmp, &info[n]); | |
1289 | if (err < 0) | |
1290 | goto err1; | |
1291 | size += info[n].ops->size; | |
1292 | n++; | |
1293 | } | |
1294 | } | |
1295 | ||
1296 | err = -ENOMEM; | |
1297 | rule = kzalloc(sizeof(*rule) + size, GFP_KERNEL); | |
1298 | if (rule == NULL) | |
1299 | goto err1; | |
1300 | ||
1301 | rule->handle = handle; | |
1302 | rule->dlen = size; | |
1303 | ||
1304 | nft_ctx_init(&ctx, afi, table, chain); | |
1305 | expr = nft_expr_first(rule); | |
1306 | for (i = 0; i < n; i++) { | |
1307 | err = nf_tables_newexpr(&ctx, &info[i], expr); | |
1308 | if (err < 0) | |
1309 | goto err2; | |
1310 | expr = nft_expr_next(expr); | |
1311 | } | |
1312 | ||
1313 | /* Register hook when first rule is inserted into a base chain */ | |
1314 | if (list_empty(&chain->rules) && chain->flags & NFT_BASE_CHAIN) { | |
1315 | err = nf_register_hook(&nft_base_chain(chain)->ops); | |
1316 | if (err < 0) | |
1317 | goto err2; | |
1318 | } | |
1319 | ||
1320 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { | |
1321 | list_replace_rcu(&old_rule->list, &rule->list); | |
1322 | nf_tables_rule_destroy(old_rule); | |
1323 | } else if (nlh->nlmsg_flags & NLM_F_APPEND) | |
1324 | list_add_tail_rcu(&rule->list, &chain->rules); | |
1325 | else | |
1326 | list_add_rcu(&rule->list, &chain->rules); | |
1327 | ||
1328 | nf_tables_rule_notify(skb, nlh, table, chain, rule, NFT_MSG_NEWRULE, | |
1329 | nlh->nlmsg_flags & (NLM_F_APPEND | NLM_F_REPLACE), | |
1330 | nfmsg->nfgen_family); | |
1331 | return 0; | |
1332 | ||
1333 | err2: | |
1334 | nf_tables_rule_destroy(rule); | |
1335 | err1: | |
1336 | for (i = 0; i < n; i++) { | |
1337 | if (info[i].ops != NULL) | |
1338 | module_put(info[i].ops->owner); | |
1339 | } | |
1340 | return err; | |
1341 | } | |
1342 | ||
1343 | static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, | |
1344 | const struct nlmsghdr *nlh, | |
1345 | const struct nlattr * const nla[]) | |
1346 | { | |
1347 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | |
1348 | const struct nft_af_info *afi; | |
1349 | const struct nft_table *table; | |
1350 | struct nft_chain *chain; | |
1351 | struct nft_rule *rule, *tmp; | |
1352 | int family = nfmsg->nfgen_family; | |
1353 | ||
1354 | afi = nf_tables_afinfo_lookup(family, false); | |
1355 | if (IS_ERR(afi)) | |
1356 | return PTR_ERR(afi); | |
1357 | ||
1358 | table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE], false); | |
1359 | if (IS_ERR(table)) | |
1360 | return PTR_ERR(table); | |
1361 | ||
1362 | chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); | |
1363 | if (IS_ERR(chain)) | |
1364 | return PTR_ERR(chain); | |
1365 | ||
1366 | if (nla[NFTA_RULE_HANDLE]) { | |
1367 | rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); | |
1368 | if (IS_ERR(rule)) | |
1369 | return PTR_ERR(rule); | |
1370 | ||
1371 | /* List removal must be visible before destroying expressions */ | |
1372 | list_del_rcu(&rule->list); | |
1373 | ||
1374 | nf_tables_rule_notify(skb, nlh, table, chain, rule, | |
1375 | NFT_MSG_DELRULE, 0, family); | |
1376 | nf_tables_rule_destroy(rule); | |
1377 | } else { | |
1378 | /* Remove all rules in this chain */ | |
1379 | list_for_each_entry_safe(rule, tmp, &chain->rules, list) { | |
1380 | list_del_rcu(&rule->list); | |
1381 | ||
1382 | nf_tables_rule_notify(skb, nlh, table, chain, rule, | |
1383 | NFT_MSG_DELRULE, 0, family); | |
1384 | nf_tables_rule_destroy(rule); | |
1385 | } | |
1386 | } | |
1387 | ||
1388 | /* Unregister hook when last rule from base chain is deleted */ | |
1389 | if (list_empty(&chain->rules) && chain->flags & NFT_BASE_CHAIN) | |
1390 | nf_unregister_hook(&nft_base_chain(chain)->ops); | |
1391 | ||
1392 | return 0; | |
1393 | } | |
1394 | ||
1395 | static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { | |
1396 | [NFT_MSG_NEWTABLE] = { | |
1397 | .call = nf_tables_newtable, | |
1398 | .attr_count = NFTA_TABLE_MAX, | |
1399 | .policy = nft_table_policy, | |
1400 | }, | |
1401 | [NFT_MSG_GETTABLE] = { | |
1402 | .call = nf_tables_gettable, | |
1403 | .attr_count = NFTA_TABLE_MAX, | |
1404 | .policy = nft_table_policy, | |
1405 | }, | |
1406 | [NFT_MSG_DELTABLE] = { | |
1407 | .call = nf_tables_deltable, | |
1408 | .attr_count = NFTA_TABLE_MAX, | |
1409 | .policy = nft_table_policy, | |
1410 | }, | |
1411 | [NFT_MSG_NEWCHAIN] = { | |
1412 | .call = nf_tables_newchain, | |
1413 | .attr_count = NFTA_CHAIN_MAX, | |
1414 | .policy = nft_chain_policy, | |
1415 | }, | |
1416 | [NFT_MSG_GETCHAIN] = { | |
1417 | .call = nf_tables_getchain, | |
1418 | .attr_count = NFTA_CHAIN_MAX, | |
1419 | .policy = nft_chain_policy, | |
1420 | }, | |
1421 | [NFT_MSG_DELCHAIN] = { | |
1422 | .call = nf_tables_delchain, | |
1423 | .attr_count = NFTA_CHAIN_MAX, | |
1424 | .policy = nft_chain_policy, | |
1425 | }, | |
1426 | [NFT_MSG_NEWRULE] = { | |
1427 | .call = nf_tables_newrule, | |
1428 | .attr_count = NFTA_RULE_MAX, | |
1429 | .policy = nft_rule_policy, | |
1430 | }, | |
1431 | [NFT_MSG_GETRULE] = { | |
1432 | .call = nf_tables_getrule, | |
1433 | .attr_count = NFTA_RULE_MAX, | |
1434 | .policy = nft_rule_policy, | |
1435 | }, | |
1436 | [NFT_MSG_DELRULE] = { | |
1437 | .call = nf_tables_delrule, | |
1438 | .attr_count = NFTA_RULE_MAX, | |
1439 | .policy = nft_rule_policy, | |
1440 | }, | |
1441 | }; | |
1442 | ||
1443 | static const struct nfnetlink_subsystem nf_tables_subsys = { | |
1444 | .name = "nf_tables", | |
1445 | .subsys_id = NFNL_SUBSYS_NFTABLES, | |
1446 | .cb_count = NFT_MSG_MAX, | |
1447 | .cb = nf_tables_cb, | |
1448 | }; | |
1449 | ||
1450 | /** | |
1451 | * nft_validate_input_register - validate an expressions' input register | |
1452 | * | |
1453 | * @reg: the register number | |
1454 | * | |
1455 | * Validate that the input register is one of the general purpose | |
1456 | * registers. | |
1457 | */ | |
1458 | int nft_validate_input_register(enum nft_registers reg) | |
1459 | { | |
1460 | if (reg <= NFT_REG_VERDICT) | |
1461 | return -EINVAL; | |
1462 | if (reg > NFT_REG_MAX) | |
1463 | return -ERANGE; | |
1464 | return 0; | |
1465 | } | |
1466 | EXPORT_SYMBOL_GPL(nft_validate_input_register); | |
1467 | ||
1468 | /** | |
1469 | * nft_validate_output_register - validate an expressions' output register | |
1470 | * | |
1471 | * @reg: the register number | |
1472 | * | |
1473 | * Validate that the output register is one of the general purpose | |
1474 | * registers or the verdict register. | |
1475 | */ | |
1476 | int nft_validate_output_register(enum nft_registers reg) | |
1477 | { | |
1478 | if (reg < NFT_REG_VERDICT) | |
1479 | return -EINVAL; | |
1480 | if (reg > NFT_REG_MAX) | |
1481 | return -ERANGE; | |
1482 | return 0; | |
1483 | } | |
1484 | EXPORT_SYMBOL_GPL(nft_validate_output_register); | |
1485 | ||
1486 | /** | |
1487 | * nft_validate_data_load - validate an expressions' data load | |
1488 | * | |
1489 | * @ctx: context of the expression performing the load | |
1490 | * @reg: the destination register number | |
1491 | * @data: the data to load | |
1492 | * @type: the data type | |
1493 | * | |
1494 | * Validate that a data load uses the appropriate data type for | |
1495 | * the destination register. A value of NULL for the data means | |
1496 | * that its runtime gathered data, which is always of type | |
1497 | * NFT_DATA_VALUE. | |
1498 | */ | |
1499 | int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg, | |
1500 | const struct nft_data *data, | |
1501 | enum nft_data_types type) | |
1502 | { | |
1503 | switch (reg) { | |
1504 | case NFT_REG_VERDICT: | |
1505 | if (data == NULL || type != NFT_DATA_VERDICT) | |
1506 | return -EINVAL; | |
1507 | // FIXME: do loop detection | |
1508 | return 0; | |
1509 | default: | |
1510 | if (data != NULL && type != NFT_DATA_VALUE) | |
1511 | return -EINVAL; | |
1512 | return 0; | |
1513 | } | |
1514 | } | |
1515 | EXPORT_SYMBOL_GPL(nft_validate_data_load); | |
1516 | ||
1517 | static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = { | |
1518 | [NFTA_VERDICT_CODE] = { .type = NLA_U32 }, | |
1519 | [NFTA_VERDICT_CHAIN] = { .type = NLA_STRING, | |
1520 | .len = NFT_CHAIN_MAXNAMELEN - 1 }, | |
1521 | }; | |
1522 | ||
1523 | static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, | |
1524 | struct nft_data_desc *desc, const struct nlattr *nla) | |
1525 | { | |
1526 | struct nlattr *tb[NFTA_VERDICT_MAX + 1]; | |
1527 | struct nft_chain *chain; | |
1528 | int err; | |
1529 | ||
1530 | err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy); | |
1531 | if (err < 0) | |
1532 | return err; | |
1533 | ||
1534 | if (!tb[NFTA_VERDICT_CODE]) | |
1535 | return -EINVAL; | |
1536 | data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE])); | |
1537 | ||
1538 | switch (data->verdict) { | |
1539 | case NF_ACCEPT: | |
1540 | case NF_DROP: | |
1541 | case NF_QUEUE: | |
1542 | case NFT_CONTINUE: | |
1543 | case NFT_BREAK: | |
1544 | case NFT_RETURN: | |
1545 | desc->len = sizeof(data->verdict); | |
1546 | break; | |
1547 | case NFT_JUMP: | |
1548 | case NFT_GOTO: | |
1549 | if (!tb[NFTA_VERDICT_CHAIN]) | |
1550 | return -EINVAL; | |
1551 | chain = nf_tables_chain_lookup(ctx->table, | |
1552 | tb[NFTA_VERDICT_CHAIN]); | |
1553 | if (IS_ERR(chain)) | |
1554 | return PTR_ERR(chain); | |
1555 | if (chain->flags & NFT_BASE_CHAIN) | |
1556 | return -EOPNOTSUPP; | |
1557 | ||
1558 | if (ctx->chain->level + 1 > chain->level) { | |
1559 | if (ctx->chain->level + 1 == 16) | |
1560 | return -EMLINK; | |
1561 | chain->level = ctx->chain->level + 1; | |
1562 | } | |
1563 | chain->use++; | |
1564 | data->chain = chain; | |
1565 | desc->len = sizeof(data); | |
1566 | break; | |
1567 | default: | |
1568 | return -EINVAL; | |
1569 | } | |
1570 | ||
1571 | desc->type = NFT_DATA_VERDICT; | |
1572 | return 0; | |
1573 | } | |
1574 | ||
1575 | static void nft_verdict_uninit(const struct nft_data *data) | |
1576 | { | |
1577 | switch (data->verdict) { | |
1578 | case NFT_JUMP: | |
1579 | case NFT_GOTO: | |
1580 | data->chain->use--; | |
1581 | break; | |
1582 | } | |
1583 | } | |
1584 | ||
1585 | static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data) | |
1586 | { | |
1587 | struct nlattr *nest; | |
1588 | ||
1589 | nest = nla_nest_start(skb, NFTA_DATA_VERDICT); | |
1590 | if (!nest) | |
1591 | goto nla_put_failure; | |
1592 | ||
1593 | if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict))) | |
1594 | goto nla_put_failure; | |
1595 | ||
1596 | switch (data->verdict) { | |
1597 | case NFT_JUMP: | |
1598 | case NFT_GOTO: | |
1599 | if (nla_put_string(skb, NFTA_VERDICT_CHAIN, data->chain->name)) | |
1600 | goto nla_put_failure; | |
1601 | } | |
1602 | nla_nest_end(skb, nest); | |
1603 | return 0; | |
1604 | ||
1605 | nla_put_failure: | |
1606 | return -1; | |
1607 | } | |
1608 | ||
1609 | static int nft_value_init(const struct nft_ctx *ctx, struct nft_data *data, | |
1610 | struct nft_data_desc *desc, const struct nlattr *nla) | |
1611 | { | |
1612 | unsigned int len; | |
1613 | ||
1614 | len = nla_len(nla); | |
1615 | if (len == 0) | |
1616 | return -EINVAL; | |
1617 | if (len > sizeof(data->data)) | |
1618 | return -EOVERFLOW; | |
1619 | ||
1620 | nla_memcpy(data->data, nla, sizeof(data->data)); | |
1621 | desc->type = NFT_DATA_VALUE; | |
1622 | desc->len = len; | |
1623 | return 0; | |
1624 | } | |
1625 | ||
1626 | static int nft_value_dump(struct sk_buff *skb, const struct nft_data *data, | |
1627 | unsigned int len) | |
1628 | { | |
1629 | return nla_put(skb, NFTA_DATA_VALUE, len, data->data); | |
1630 | } | |
1631 | ||
1632 | static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { | |
1633 | [NFTA_DATA_VALUE] = { .type = NLA_BINARY, | |
1634 | .len = FIELD_SIZEOF(struct nft_data, data) }, | |
1635 | [NFTA_DATA_VERDICT] = { .type = NLA_NESTED }, | |
1636 | }; | |
1637 | ||
1638 | /** | |
1639 | * nft_data_init - parse nf_tables data netlink attributes | |
1640 | * | |
1641 | * @ctx: context of the expression using the data | |
1642 | * @data: destination struct nft_data | |
1643 | * @desc: data description | |
1644 | * @nla: netlink attribute containing data | |
1645 | * | |
1646 | * Parse the netlink data attributes and initialize a struct nft_data. | |
1647 | * The type and length of data are returned in the data description. | |
1648 | * | |
1649 | * The caller can indicate that it only wants to accept data of type | |
1650 | * NFT_DATA_VALUE by passing NULL for the ctx argument. | |
1651 | */ | |
1652 | int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, | |
1653 | struct nft_data_desc *desc, const struct nlattr *nla) | |
1654 | { | |
1655 | struct nlattr *tb[NFTA_DATA_MAX + 1]; | |
1656 | int err; | |
1657 | ||
1658 | err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy); | |
1659 | if (err < 0) | |
1660 | return err; | |
1661 | ||
1662 | if (tb[NFTA_DATA_VALUE]) | |
1663 | return nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]); | |
1664 | if (tb[NFTA_DATA_VERDICT] && ctx != NULL) | |
1665 | return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]); | |
1666 | return -EINVAL; | |
1667 | } | |
1668 | EXPORT_SYMBOL_GPL(nft_data_init); | |
1669 | ||
1670 | /** | |
1671 | * nft_data_uninit - release a nft_data item | |
1672 | * | |
1673 | * @data: struct nft_data to release | |
1674 | * @type: type of data | |
1675 | * | |
1676 | * Release a nft_data item. NFT_DATA_VALUE types can be silently discarded, | |
1677 | * all others need to be released by calling this function. | |
1678 | */ | |
1679 | void nft_data_uninit(const struct nft_data *data, enum nft_data_types type) | |
1680 | { | |
1681 | switch (type) { | |
1682 | case NFT_DATA_VALUE: | |
1683 | return; | |
1684 | case NFT_DATA_VERDICT: | |
1685 | return nft_verdict_uninit(data); | |
1686 | default: | |
1687 | WARN_ON(1); | |
1688 | } | |
1689 | } | |
1690 | EXPORT_SYMBOL_GPL(nft_data_uninit); | |
1691 | ||
1692 | int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data, | |
1693 | enum nft_data_types type, unsigned int len) | |
1694 | { | |
1695 | struct nlattr *nest; | |
1696 | int err; | |
1697 | ||
1698 | nest = nla_nest_start(skb, attr); | |
1699 | if (nest == NULL) | |
1700 | return -1; | |
1701 | ||
1702 | switch (type) { | |
1703 | case NFT_DATA_VALUE: | |
1704 | err = nft_value_dump(skb, data, len); | |
1705 | break; | |
1706 | case NFT_DATA_VERDICT: | |
1707 | err = nft_verdict_dump(skb, data); | |
1708 | break; | |
1709 | default: | |
1710 | err = -EINVAL; | |
1711 | WARN_ON(1); | |
1712 | } | |
1713 | ||
1714 | nla_nest_end(skb, nest); | |
1715 | return err; | |
1716 | } | |
1717 | EXPORT_SYMBOL_GPL(nft_data_dump); | |
1718 | ||
1719 | static int __init nf_tables_module_init(void) | |
1720 | { | |
1721 | int err; | |
1722 | ||
1723 | info = kmalloc(sizeof(struct nft_expr_info) * NFT_RULE_MAXEXPRS, | |
1724 | GFP_KERNEL); | |
1725 | if (info == NULL) { | |
1726 | err = -ENOMEM; | |
1727 | goto err1; | |
1728 | } | |
1729 | ||
1730 | err = nf_tables_core_module_init(); | |
1731 | if (err < 0) | |
1732 | goto err2; | |
1733 | ||
1734 | err = nfnetlink_subsys_register(&nf_tables_subsys); | |
1735 | if (err < 0) | |
1736 | goto err3; | |
1737 | ||
1738 | pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n"); | |
1739 | return 0; | |
1740 | err3: | |
1741 | nf_tables_core_module_exit(); | |
1742 | err2: | |
1743 | kfree(info); | |
1744 | err1: | |
1745 | return err; | |
1746 | } | |
1747 | ||
1748 | static void __exit nf_tables_module_exit(void) | |
1749 | { | |
1750 | nfnetlink_subsys_unregister(&nf_tables_subsys); | |
1751 | nf_tables_core_module_exit(); | |
1752 | kfree(info); | |
1753 | } | |
1754 | ||
1755 | module_init(nf_tables_module_init); | |
1756 | module_exit(nf_tables_module_exit); | |
1757 | ||
1758 | MODULE_LICENSE("GPL"); | |
1759 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | |
1760 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFTABLES); |