1 From: Pablo Neira Ayuso <pablo@netfilter.org>
2 Date: Sun, 7 Jan 2018 01:04:07 +0100
3 Subject: [PATCH] netfilter: nf_tables: add flow table netlink frontend
5 This patch introduces a netlink control plane to create, delete and dump
6 flow tables. Flow tables are identified by name, this name is used from
7 rules to refer to an specific flow table. Flow tables use the rhashtable
8 class and a generic garbage collector to remove expired entries.
10 This also adds the infrastructure to add different flow table types, so
11 we can add one for each layer 3 protocol family.
13 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
15 create mode 100644 include/net/netfilter/nf_flow_table.h
18 +++ b/include/net/netfilter/nf_flow_table.h
20 +#ifndef _NF_FLOW_TABLE_H
21 +#define _NF_FLOW_TABLE_H
23 +#include <linux/rhashtable.h>
27 +struct nf_flowtable_type {
28 + struct list_head list;
30 + void (*gc)(struct work_struct *work);
31 + const struct rhashtable_params *params;
33 + struct module *owner;
36 +struct nf_flowtable {
37 + struct rhashtable rhashtable;
38 + const struct nf_flowtable_type *type;
39 + struct delayed_work gc_work;
42 +#endif /* _FLOW_OFFLOAD_H */
43 --- a/include/net/netfilter/nf_tables.h
44 +++ b/include/net/netfilter/nf_tables.h
46 #include <linux/netfilter/x_tables.h>
47 #include <linux/netfilter/nf_tables.h>
48 #include <linux/u64_stats_sync.h>
49 +#include <net/netfilter/nf_flow_table.h>
50 #include <net/netlink.h>
52 #define NFT_JUMP_STACK_SIZE 16
53 @@ -941,6 +942,7 @@ unsigned int nft_do_chain(struct nft_pkt
54 * @chains: chains in the table
55 * @sets: sets in the table
56 * @objects: stateful objects in the table
57 + * @flowtables: flow tables in the table
58 * @hgenerator: handle generator state
59 * @use: number of chain references to this table
60 * @flags: table flag (see enum nft_table_flags)
61 @@ -952,6 +954,7 @@ struct nft_table {
62 struct list_head chains;
63 struct list_head sets;
64 struct list_head objects;
65 + struct list_head flowtables;
69 @@ -1083,6 +1086,44 @@ int nft_register_obj(struct nft_object_t
70 void nft_unregister_obj(struct nft_object_type *obj_type);
73 + * struct nft_flowtable - nf_tables flow table
75 + * @list: flow table list node in table list
76 + * @table: the table the flow table is contained in
77 + * @name: name of this flow table
78 + * @hooknum: hook number
79 + * @priority: hook priority
80 + * @ops_len: number of hooks in array
81 + * @genmask: generation mask
82 + * @use: number of references to this flow table
83 + * @data: rhashtable and garbage collector
84 + * @ops: array of hooks
86 +struct nft_flowtable {
87 + struct list_head list;
88 + struct nft_table *table;
95 + /* runtime data below here */
96 + struct nf_hook_ops *ops ____cacheline_aligned;
97 + struct nf_flowtable data;
100 +struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
101 + const struct nlattr *nla,
103 +void nft_flow_table_iterate(struct net *net,
104 + void (*iter)(struct nf_flowtable *flowtable, void *data),
107 +void nft_register_flowtable_type(struct nf_flowtable_type *type);
108 +void nft_unregister_flowtable_type(struct nf_flowtable_type *type);
111 * struct nft_traceinfo - nft tracing information and state
113 * @pkt: pktinfo currently processed
114 @@ -1318,4 +1359,11 @@ struct nft_trans_obj {
115 #define nft_trans_obj(trans) \
116 (((struct nft_trans_obj *)trans->data)->obj)
118 +struct nft_trans_flowtable {
119 + struct nft_flowtable *flowtable;
122 +#define nft_trans_flowtable(trans) \
123 + (((struct nft_trans_flowtable *)trans->data)->flowtable)
125 #endif /* _NET_NF_TABLES_H */
126 --- a/include/uapi/linux/netfilter/nf_tables.h
127 +++ b/include/uapi/linux/netfilter/nf_tables.h
128 @@ -92,6 +92,9 @@ enum nft_verdicts {
129 * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)
130 * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)
131 * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)
132 + * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
133 + * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
134 + * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes)
136 enum nf_tables_msg_types {
138 @@ -116,6 +119,9 @@ enum nf_tables_msg_types {
141 NFT_MSG_GETOBJ_RESET,
142 + NFT_MSG_NEWFLOWTABLE,
143 + NFT_MSG_GETFLOWTABLE,
144 + NFT_MSG_DELFLOWTABLE,
148 @@ -1310,6 +1316,53 @@ enum nft_object_attributes {
149 #define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1)
152 + * enum nft_flowtable_attributes - nf_tables flow table netlink attributes
154 + * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING)
155 + * @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING)
156 + * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)
157 + * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)
159 +enum nft_flowtable_attributes {
160 + NFTA_FLOWTABLE_UNSPEC,
161 + NFTA_FLOWTABLE_TABLE,
162 + NFTA_FLOWTABLE_NAME,
163 + NFTA_FLOWTABLE_HOOK,
164 + NFTA_FLOWTABLE_USE,
165 + __NFTA_FLOWTABLE_MAX
167 +#define NFTA_FLOWTABLE_MAX (__NFTA_FLOWTABLE_MAX - 1)
170 + * enum nft_flowtable_hook_attributes - nf_tables flow table hook netlink attributes
172 + * @NFTA_FLOWTABLE_HOOK_NUM: netfilter hook number (NLA_U32)
173 + * @NFTA_FLOWTABLE_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
174 + * @NFTA_FLOWTABLE_HOOK_DEVS: input devices this flow table is bound to (NLA_NESTED)
176 +enum nft_flowtable_hook_attributes {
177 + NFTA_FLOWTABLE_HOOK_UNSPEC,
178 + NFTA_FLOWTABLE_HOOK_NUM,
179 + NFTA_FLOWTABLE_HOOK_PRIORITY,
180 + NFTA_FLOWTABLE_HOOK_DEVS,
181 + __NFTA_FLOWTABLE_HOOK_MAX
183 +#define NFTA_FLOWTABLE_HOOK_MAX (__NFTA_FLOWTABLE_HOOK_MAX - 1)
186 + * enum nft_device_attributes - nf_tables device netlink attributes
188 + * @NFTA_DEVICE_NAME: name of this device (NLA_STRING)
190 +enum nft_devices_attributes {
191 + NFTA_DEVICE_UNSPEC,
195 +#define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1)
199 * enum nft_trace_attributes - nf_tables trace netlink attributes
201 * @NFTA_TRACE_TABLE: name of the table (NLA_STRING)
202 --- a/net/netfilter/nf_tables_api.c
203 +++ b/net/netfilter/nf_tables_api.c
205 #include <linux/netfilter.h>
206 #include <linux/netfilter/nfnetlink.h>
207 #include <linux/netfilter/nf_tables.h>
208 +#include <net/netfilter/nf_flow_table.h>
209 #include <net/netfilter/nf_tables_core.h>
210 #include <net/netfilter/nf_tables.h>
211 #include <net/net_namespace.h>
214 static LIST_HEAD(nf_tables_expressions);
215 static LIST_HEAD(nf_tables_objects);
216 +static LIST_HEAD(nf_tables_flowtables);
219 * nft_register_afinfo - register nf_tables address family info
220 @@ -389,6 +391,40 @@ static int nft_delobj(struct nft_ctx *ct
224 +static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
225 + struct nft_flowtable *flowtable)
227 + struct nft_trans *trans;
229 + trans = nft_trans_alloc(ctx, msg_type,
230 + sizeof(struct nft_trans_flowtable));
234 + if (msg_type == NFT_MSG_NEWFLOWTABLE)
235 + nft_activate_next(ctx->net, flowtable);
237 + nft_trans_flowtable(trans) = flowtable;
238 + list_add_tail(&trans->list, &ctx->net->nft.commit_list);
243 +static int nft_delflowtable(struct nft_ctx *ctx,
244 + struct nft_flowtable *flowtable)
248 + err = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable);
252 + nft_deactivate_next(ctx->net, flowtable);
261 @@ -772,6 +808,7 @@ static int nf_tables_newtable(struct net
262 INIT_LIST_HEAD(&table->chains);
263 INIT_LIST_HEAD(&table->sets);
264 INIT_LIST_HEAD(&table->objects);
265 + INIT_LIST_HEAD(&table->flowtables);
266 table->flags = flags;
268 nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
269 @@ -793,10 +830,11 @@ err1:
271 static int nft_flush_table(struct nft_ctx *ctx)
274 + struct nft_flowtable *flowtable, *nft;
275 struct nft_chain *chain, *nc;
276 struct nft_object *obj, *ne;
277 struct nft_set *set, *ns;
280 list_for_each_entry(chain, &ctx->table->chains, list) {
281 if (!nft_is_active_next(ctx->net, chain))
282 @@ -822,6 +860,12 @@ static int nft_flush_table(struct nft_ct
286 + list_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) {
287 + err = nft_delflowtable(ctx, flowtable);
292 list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) {
293 err = nft_delobj(ctx, obj);
295 @@ -4863,6 +4907,605 @@ static void nf_tables_obj_notify(const s
296 ctx->afi->family, ctx->report, GFP_KERNEL);
302 +void nft_register_flowtable_type(struct nf_flowtable_type *type)
304 + nfnl_lock(NFNL_SUBSYS_NFTABLES);
305 + list_add_tail_rcu(&type->list, &nf_tables_flowtables);
306 + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
308 +EXPORT_SYMBOL_GPL(nft_register_flowtable_type);
310 +void nft_unregister_flowtable_type(struct nf_flowtable_type *type)
312 + nfnl_lock(NFNL_SUBSYS_NFTABLES);
313 + list_del_rcu(&type->list);
314 + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
316 +EXPORT_SYMBOL_GPL(nft_unregister_flowtable_type);
318 +static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = {
319 + [NFTA_FLOWTABLE_TABLE] = { .type = NLA_STRING,
320 + .len = NFT_NAME_MAXLEN - 1 },
321 + [NFTA_FLOWTABLE_NAME] = { .type = NLA_STRING,
322 + .len = NFT_NAME_MAXLEN - 1 },
323 + [NFTA_FLOWTABLE_HOOK] = { .type = NLA_NESTED },
326 +struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
327 + const struct nlattr *nla,
330 + struct nft_flowtable *flowtable;
332 + list_for_each_entry(flowtable, &table->flowtables, list) {
333 + if (!nla_strcmp(nla, flowtable->name) &&
334 + nft_active_genmask(flowtable, genmask))
337 + return ERR_PTR(-ENOENT);
339 +EXPORT_SYMBOL_GPL(nf_tables_flowtable_lookup);
341 +#define NFT_FLOWTABLE_DEVICE_MAX 8
343 +static int nf_tables_parse_devices(const struct nft_ctx *ctx,
344 + const struct nlattr *attr,
345 + struct net_device *dev_array[], int *len)
347 + const struct nlattr *tmp;
348 + struct net_device *dev;
349 + char ifname[IFNAMSIZ];
350 + int rem, n = 0, err;
352 + nla_for_each_nested(tmp, attr, rem) {
353 + if (nla_type(tmp) != NFTA_DEVICE_NAME) {
358 + nla_strlcpy(ifname, tmp, IFNAMSIZ);
359 + dev = dev_get_by_name(ctx->net, ifname);
365 + dev_array[n++] = dev;
366 + if (n == NFT_FLOWTABLE_DEVICE_MAX) {
380 +static const struct nla_policy nft_flowtable_hook_policy[NFTA_FLOWTABLE_HOOK_MAX + 1] = {
381 + [NFTA_FLOWTABLE_HOOK_NUM] = { .type = NLA_U32 },
382 + [NFTA_FLOWTABLE_HOOK_PRIORITY] = { .type = NLA_U32 },
383 + [NFTA_FLOWTABLE_HOOK_DEVS] = { .type = NLA_NESTED },
386 +static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,
387 + const struct nlattr *attr,
388 + struct nft_flowtable *flowtable)
390 + struct net_device *dev_array[NFT_FLOWTABLE_DEVICE_MAX];
391 + struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1];
392 + struct nf_hook_ops *ops;
393 + int hooknum, priority;
396 + err = nla_parse_nested(tb, NFTA_FLOWTABLE_HOOK_MAX, attr,
397 + nft_flowtable_hook_policy, NULL);
401 + if (!tb[NFTA_FLOWTABLE_HOOK_NUM] ||
402 + !tb[NFTA_FLOWTABLE_HOOK_PRIORITY] ||
403 + !tb[NFTA_FLOWTABLE_HOOK_DEVS])
406 + hooknum = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_NUM]));
407 + if (hooknum >= ctx->afi->nhooks)
410 + priority = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY]));
412 + err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS],
417 + ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL);
423 + flowtable->ops = ops;
424 + flowtable->ops_len = n;
426 + for (i = 0; i < n; i++) {
427 + flowtable->ops[i].pf = NFPROTO_NETDEV;
428 + flowtable->ops[i].hooknum = hooknum;
429 + flowtable->ops[i].priority = priority;
430 + flowtable->ops[i].priv = &flowtable->data.rhashtable;
431 + flowtable->ops[i].hook = flowtable->data.type->hook;
432 + flowtable->ops[i].dev = dev_array[i];
437 + for (i = 0; i < n; i++)
438 + dev_put(dev_array[i]);
443 +static const struct nf_flowtable_type *
444 +__nft_flowtable_type_get(const struct nft_af_info *afi)
446 + const struct nf_flowtable_type *type;
448 + list_for_each_entry(type, &nf_tables_flowtables, list) {
449 + if (afi->family == type->family)
455 +static const struct nf_flowtable_type *
456 +nft_flowtable_type_get(const struct nft_af_info *afi)
458 + const struct nf_flowtable_type *type;
460 + type = __nft_flowtable_type_get(afi);
461 + if (type != NULL && try_module_get(type->owner))
464 +#ifdef CONFIG_MODULES
465 + if (type == NULL) {
466 + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
467 + request_module("nf-flowtable-%u", afi->family);
468 + nfnl_lock(NFNL_SUBSYS_NFTABLES);
469 + if (__nft_flowtable_type_get(afi))
470 + return ERR_PTR(-EAGAIN);
473 + return ERR_PTR(-ENOENT);
476 +void nft_flow_table_iterate(struct net *net,
477 + void (*iter)(struct nf_flowtable *flowtable, void *data),
480 + struct nft_flowtable *flowtable;
481 + const struct nft_af_info *afi;
482 + const struct nft_table *table;
485 + list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
486 + list_for_each_entry_rcu(table, &afi->tables, list) {
487 + list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
488 + iter(&flowtable->data, data);
494 +EXPORT_SYMBOL_GPL(nft_flow_table_iterate);
496 +static void nft_unregister_flowtable_net_hooks(struct net *net,
497 + struct nft_flowtable *flowtable)
501 + for (i = 0; i < flowtable->ops_len; i++) {
502 + if (!flowtable->ops[i].dev)
505 + nf_unregister_net_hook(net, &flowtable->ops[i]);
509 +static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
510 + struct sk_buff *skb,
511 + const struct nlmsghdr *nlh,
512 + const struct nlattr * const nla[],
513 + struct netlink_ext_ack *extack)
515 + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
516 + const struct nf_flowtable_type *type;
517 + u8 genmask = nft_genmask_next(net);
518 + int family = nfmsg->nfgen_family;
519 + struct nft_flowtable *flowtable;
520 + struct nft_af_info *afi;
521 + struct nft_table *table;
522 + struct nft_ctx ctx;
525 + if (!nla[NFTA_FLOWTABLE_TABLE] ||
526 + !nla[NFTA_FLOWTABLE_NAME] ||
527 + !nla[NFTA_FLOWTABLE_HOOK])
530 + afi = nf_tables_afinfo_lookup(net, family, true);
532 + return PTR_ERR(afi);
534 + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
536 + return PTR_ERR(table);
538 + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
540 + if (IS_ERR(flowtable)) {
541 + err = PTR_ERR(flowtable);
542 + if (err != -ENOENT)
545 + if (nlh->nlmsg_flags & NLM_F_EXCL)
551 + nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
553 + flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL);
557 + flowtable->table = table;
558 + flowtable->name = nla_strdup(nla[NFTA_FLOWTABLE_NAME], GFP_KERNEL);
559 + if (!flowtable->name) {
564 + type = nft_flowtable_type_get(afi);
565 + if (IS_ERR(type)) {
566 + err = PTR_ERR(type);
570 + flowtable->data.type = type;
571 + err = rhashtable_init(&flowtable->data.rhashtable, type->params);
575 + err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],
580 + for (i = 0; i < flowtable->ops_len; i++) {
581 + err = nf_register_net_hook(net, &flowtable->ops[i]);
586 + err = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable);
590 + INIT_DEFERRABLE_WORK(&flowtable->data.gc_work, type->gc);
591 + queue_delayed_work(system_power_efficient_wq,
592 + &flowtable->data.gc_work, HZ);
594 + list_add_tail_rcu(&flowtable->list, &table->flowtables);
599 + i = flowtable->ops_len;
601 + for (k = i - 1; k >= 0; k--)
602 + nf_unregister_net_hook(net, &flowtable->ops[i]);
604 + kfree(flowtable->ops);
606 + module_put(type->owner);
608 + kfree(flowtable->name);
614 +static int nf_tables_delflowtable(struct net *net, struct sock *nlsk,
615 + struct sk_buff *skb,
616 + const struct nlmsghdr *nlh,
617 + const struct nlattr * const nla[],
618 + struct netlink_ext_ack *extack)
620 + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
621 + u8 genmask = nft_genmask_next(net);
622 + int family = nfmsg->nfgen_family;
623 + struct nft_flowtable *flowtable;
624 + struct nft_af_info *afi;
625 + struct nft_table *table;
626 + struct nft_ctx ctx;
628 + afi = nf_tables_afinfo_lookup(net, family, true);
630 + return PTR_ERR(afi);
632 + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
634 + return PTR_ERR(table);
636 + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
638 + if (IS_ERR(flowtable))
639 + return PTR_ERR(flowtable);
640 + if (flowtable->use > 0)
643 + nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
645 + return nft_delflowtable(&ctx, flowtable);
648 +static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
649 + u32 portid, u32 seq, int event,
650 + u32 flags, int family,
651 + struct nft_flowtable *flowtable)
653 + struct nlattr *nest, *nest_devs;
654 + struct nfgenmsg *nfmsg;
655 + struct nlmsghdr *nlh;
658 + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
659 + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
661 + goto nla_put_failure;
663 + nfmsg = nlmsg_data(nlh);
664 + nfmsg->nfgen_family = family;
665 + nfmsg->version = NFNETLINK_V0;
666 + nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
668 + if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) ||
669 + nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||
670 + nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)))
671 + goto nla_put_failure;
673 + nest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK);
674 + if (nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_NUM, htonl(flowtable->hooknum)) ||
675 + nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(flowtable->priority)))
676 + goto nla_put_failure;
678 + nest_devs = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK_DEVS);
680 + goto nla_put_failure;
682 + for (i = 0; i < flowtable->ops_len; i++) {
683 + if (flowtable->ops[i].dev &&
684 + nla_put_string(skb, NFTA_DEVICE_NAME,
685 + flowtable->ops[i].dev->name))
686 + goto nla_put_failure;
688 + nla_nest_end(skb, nest_devs);
689 + nla_nest_end(skb, nest);
691 + nlmsg_end(skb, nlh);
695 + nlmsg_trim(skb, nlh);
699 +struct nft_flowtable_filter {
703 +static int nf_tables_dump_flowtable(struct sk_buff *skb,
704 + struct netlink_callback *cb)
706 + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
707 + struct nft_flowtable_filter *filter = cb->data;
708 + unsigned int idx = 0, s_idx = cb->args[0];
709 + struct net *net = sock_net(skb->sk);
710 + int family = nfmsg->nfgen_family;
711 + struct nft_flowtable *flowtable;
712 + const struct nft_af_info *afi;
713 + const struct nft_table *table;
716 + cb->seq = net->nft.base_seq;
718 + list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
719 + if (family != NFPROTO_UNSPEC && family != afi->family)
722 + list_for_each_entry_rcu(table, &afi->tables, list) {
723 + list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
724 + if (!nft_is_active(net, flowtable))
729 + memset(&cb->args[1], 0,
730 + sizeof(cb->args) - sizeof(cb->args[0]));
731 + if (filter && filter->table[0] &&
732 + strcmp(filter->table, table->name))
735 + if (nf_tables_fill_flowtable_info(skb, net, NETLINK_CB(cb->skb).portid,
736 + cb->nlh->nlmsg_seq,
737 + NFT_MSG_NEWFLOWTABLE,
738 + NLM_F_MULTI | NLM_F_APPEND,
739 + afi->family, flowtable) < 0)
742 + nl_dump_check_consistent(cb, nlmsg_hdr(skb));
755 +static int nf_tables_dump_flowtable_done(struct netlink_callback *cb)
757 + struct nft_flowtable_filter *filter = cb->data;
762 + kfree(filter->table);
768 +static struct nft_flowtable_filter *
769 +nft_flowtable_filter_alloc(const struct nlattr * const nla[])
771 + struct nft_flowtable_filter *filter;
773 + filter = kzalloc(sizeof(*filter), GFP_KERNEL);
775 + return ERR_PTR(-ENOMEM);
777 + if (nla[NFTA_FLOWTABLE_TABLE]) {
778 + filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE],
780 + if (!filter->table) {
782 + return ERR_PTR(-ENOMEM);
788 +static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
789 + struct sk_buff *skb,
790 + const struct nlmsghdr *nlh,
791 + const struct nlattr * const nla[],
792 + struct netlink_ext_ack *extack)
794 + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
795 + u8 genmask = nft_genmask_cur(net);
796 + int family = nfmsg->nfgen_family;
797 + struct nft_flowtable *flowtable;
798 + const struct nft_af_info *afi;
799 + const struct nft_table *table;
800 + struct sk_buff *skb2;
803 + if (nlh->nlmsg_flags & NLM_F_DUMP) {
804 + struct netlink_dump_control c = {
805 + .dump = nf_tables_dump_flowtable,
806 + .done = nf_tables_dump_flowtable_done,
809 + if (nla[NFTA_FLOWTABLE_TABLE]) {
810 + struct nft_flowtable_filter *filter;
812 + filter = nft_flowtable_filter_alloc(nla);
813 + if (IS_ERR(filter))
818 + return netlink_dump_start(nlsk, skb, nlh, &c);
821 + if (!nla[NFTA_FLOWTABLE_NAME])
824 + afi = nf_tables_afinfo_lookup(net, family, false);
826 + return PTR_ERR(afi);
828 + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
830 + return PTR_ERR(table);
832 + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
835 + return PTR_ERR(flowtable);
837 + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
841 + err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,
843 + NFT_MSG_NEWFLOWTABLE, 0, family,
848 + return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
854 +static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
855 + struct nft_flowtable *flowtable,
858 + struct sk_buff *skb;
862 + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
865 + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
869 + err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid,
870 + ctx->seq, event, 0,
871 + ctx->afi->family, flowtable);
877 + nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
878 + ctx->report, GFP_KERNEL);
881 + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
884 +static void nft_flowtable_destroy(void *ptr, void *arg)
889 +static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
891 + cancel_delayed_work_sync(&flowtable->data.gc_work);
892 + kfree(flowtable->name);
893 + rhashtable_free_and_destroy(&flowtable->data.rhashtable,
894 + nft_flowtable_destroy, NULL);
895 + module_put(flowtable->data.type->owner);
898 static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
901 @@ -4893,6 +5536,49 @@ nla_put_failure:
905 +static void nft_flowtable_event(unsigned long event, struct net_device *dev,
906 + struct nft_flowtable *flowtable)
910 + for (i = 0; i < flowtable->ops_len; i++) {
911 + if (flowtable->ops[i].dev != dev)
914 + nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]);
915 + flowtable->ops[i].dev = NULL;
920 +static int nf_tables_flowtable_event(struct notifier_block *this,
921 + unsigned long event, void *ptr)
923 + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
924 + struct nft_flowtable *flowtable;
925 + struct nft_table *table;
926 + struct nft_af_info *afi;
928 + if (event != NETDEV_UNREGISTER)
931 + nfnl_lock(NFNL_SUBSYS_NFTABLES);
932 + list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {
933 + list_for_each_entry(table, &afi->tables, list) {
934 + list_for_each_entry(flowtable, &table->flowtables, list) {
935 + nft_flowtable_event(event, dev, flowtable);
939 + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
941 + return NOTIFY_DONE;
944 +static struct notifier_block nf_tables_flowtable_notifier = {
945 + .notifier_call = nf_tables_flowtable_event,
948 static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb,
951 @@ -5045,6 +5731,21 @@ static const struct nfnl_callback nf_tab
952 .attr_count = NFTA_OBJ_MAX,
953 .policy = nft_obj_policy,
955 + [NFT_MSG_NEWFLOWTABLE] = {
956 + .call_batch = nf_tables_newflowtable,
957 + .attr_count = NFTA_FLOWTABLE_MAX,
958 + .policy = nft_flowtable_policy,
960 + [NFT_MSG_GETFLOWTABLE] = {
961 + .call = nf_tables_getflowtable,
962 + .attr_count = NFTA_FLOWTABLE_MAX,
963 + .policy = nft_flowtable_policy,
965 + [NFT_MSG_DELFLOWTABLE] = {
966 + .call_batch = nf_tables_delflowtable,
967 + .attr_count = NFTA_FLOWTABLE_MAX,
968 + .policy = nft_flowtable_policy,
972 static void nft_chain_commit_update(struct nft_trans *trans)
973 @@ -5093,6 +5794,9 @@ static void nf_tables_commit_release(str
975 nft_obj_destroy(nft_trans_obj(trans));
977 + case NFT_MSG_DELFLOWTABLE:
978 + nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
983 @@ -5212,6 +5916,21 @@ static int nf_tables_commit(struct net *
984 nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans),
987 + case NFT_MSG_NEWFLOWTABLE:
988 + nft_clear(net, nft_trans_flowtable(trans));
989 + nf_tables_flowtable_notify(&trans->ctx,
990 + nft_trans_flowtable(trans),
991 + NFT_MSG_NEWFLOWTABLE);
992 + nft_trans_destroy(trans);
994 + case NFT_MSG_DELFLOWTABLE:
995 + list_del_rcu(&nft_trans_flowtable(trans)->list);
996 + nf_tables_flowtable_notify(&trans->ctx,
997 + nft_trans_flowtable(trans),
998 + NFT_MSG_DELFLOWTABLE);
999 + nft_unregister_flowtable_net_hooks(net,
1000 + nft_trans_flowtable(trans));
1005 @@ -5249,6 +5968,9 @@ static void nf_tables_abort_release(stru
1006 case NFT_MSG_NEWOBJ:
1007 nft_obj_destroy(nft_trans_obj(trans));
1009 + case NFT_MSG_NEWFLOWTABLE:
1010 + nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
1015 @@ -5340,6 +6062,17 @@ static int nf_tables_abort(struct net *n
1016 nft_clear(trans->ctx.net, nft_trans_obj(trans));
1017 nft_trans_destroy(trans);
1019 + case NFT_MSG_NEWFLOWTABLE:
1020 + trans->ctx.table->use--;
1021 + list_del_rcu(&nft_trans_flowtable(trans)->list);
1022 + nft_unregister_flowtable_net_hooks(net,
1023 + nft_trans_flowtable(trans));
1025 + case NFT_MSG_DELFLOWTABLE:
1026 + trans->ctx.table->use++;
1027 + nft_clear(trans->ctx.net, nft_trans_flowtable(trans));
1028 + nft_trans_destroy(trans);
1033 @@ -5890,6 +6623,7 @@ EXPORT_SYMBOL_GPL(__nft_release_basechai
1034 /* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */
1035 static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
1037 + struct nft_flowtable *flowtable, *nf;
1038 struct nft_table *table, *nt;
1039 struct nft_chain *chain, *nc;
1040 struct nft_object *obj, *ne;
1041 @@ -5903,6 +6637,9 @@ static void __nft_release_afinfo(struct
1042 list_for_each_entry_safe(table, nt, &afi->tables, list) {
1043 list_for_each_entry(chain, &table->chains, list)
1044 nf_tables_unregister_hook(net, table, chain);
1045 + list_for_each_entry(flowtable, &table->flowtables, list)
1046 + nf_unregister_net_hooks(net, flowtable->ops,
1047 + flowtable->ops_len);
1048 /* No packets are walking on these chains anymore. */
1050 list_for_each_entry(chain, &table->chains, list) {
1051 @@ -5913,6 +6650,11 @@ static void __nft_release_afinfo(struct
1052 nf_tables_rule_release(&ctx, rule);
1055 + list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
1056 + list_del(&flowtable->list);
1058 + nf_tables_flowtable_destroy(flowtable);
1060 list_for_each_entry_safe(set, ns, &table->sets, list) {
1061 list_del(&set->list);
1063 @@ -5956,6 +6698,8 @@ static int __init nf_tables_module_init(
1067 + register_netdevice_notifier(&nf_tables_flowtable_notifier);
1069 pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
1070 return register_pernet_subsys(&nf_tables_net_ops);
1072 @@ -5970,6 +6714,7 @@ static void __exit nf_tables_module_exit
1074 unregister_pernet_subsys(&nf_tables_net_ops);
1075 nfnetlink_subsys_unregister(&nf_tables_subsys);
1076 + unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
1078 nf_tables_core_module_exit();