aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/netfilter/nf_flow_table.h23
-rw-r--r--include/net/netfilter/nf_tables.h48
-rw-r--r--include/uapi/linux/netfilter/nf_tables.h53
-rw-r--r--net/netfilter/nf_tables_api.c747
4 files changed, 870 insertions, 1 deletions
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
new file mode 100644
index 000000000000..3a0779589281
--- /dev/null
+++ b/include/net/netfilter/nf_flow_table.h
@@ -0,0 +1,23 @@
1#ifndef _NF_FLOW_TABLE_H
2#define _NF_FLOW_TABLE_H
3
4#include <linux/rhashtable.h>
5
6struct nf_flowtable;
7
8struct nf_flowtable_type {
9 struct list_head list;
10 int family;
11 void (*gc)(struct work_struct *work);
12 const struct rhashtable_params *params;
13 nf_hookfn *hook;
14 struct module *owner;
15};
16
17struct nf_flowtable {
18 struct rhashtable rhashtable;
19 const struct nf_flowtable_type *type;
20 struct delayed_work gc_work;
21};
22
23#endif /* _FLOW_OFFLOAD_H */
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index e3ec02fd0f67..dd238950df81 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -9,6 +9,7 @@
9#include <linux/netfilter/x_tables.h> 9#include <linux/netfilter/x_tables.h>
10#include <linux/netfilter/nf_tables.h> 10#include <linux/netfilter/nf_tables.h>
11#include <linux/u64_stats_sync.h> 11#include <linux/u64_stats_sync.h>
12#include <net/netfilter/nf_flow_table.h>
12#include <net/netlink.h> 13#include <net/netlink.h>
13 14
14#define NFT_JUMP_STACK_SIZE 16 15#define NFT_JUMP_STACK_SIZE 16
@@ -943,6 +944,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv);
943 * @chains: chains in the table 944 * @chains: chains in the table
944 * @sets: sets in the table 945 * @sets: sets in the table
945 * @objects: stateful objects in the table 946 * @objects: stateful objects in the table
947 * @flowtables: flow tables in the table
946 * @hgenerator: handle generator state 948 * @hgenerator: handle generator state
947 * @use: number of chain references to this table 949 * @use: number of chain references to this table
948 * @flags: table flag (see enum nft_table_flags) 950 * @flags: table flag (see enum nft_table_flags)
@@ -954,6 +956,7 @@ struct nft_table {
954 struct list_head chains; 956 struct list_head chains;
955 struct list_head sets; 957 struct list_head sets;
956 struct list_head objects; 958 struct list_head objects;
959 struct list_head flowtables;
957 u64 hgenerator; 960 u64 hgenerator;
958 u32 use; 961 u32 use;
959 u16 flags:14, 962 u16 flags:14,
@@ -1085,6 +1088,44 @@ int nft_register_obj(struct nft_object_type *obj_type);
1085void nft_unregister_obj(struct nft_object_type *obj_type); 1088void nft_unregister_obj(struct nft_object_type *obj_type);
1086 1089
1087/** 1090/**
1091 * struct nft_flowtable - nf_tables flow table
1092 *
1093 * @list: flow table list node in table list
1094 * @table: the table the flow table is contained in
1095 * @name: name of this flow table
1096 * @hooknum: hook number
1097 * @priority: hook priority
1098 * @ops_len: number of hooks in array
1099 * @genmask: generation mask
1100 * @use: number of references to this flow table
1101 * @data: rhashtable and garbage collector
1102 * @ops: array of hooks
1103 */
1104struct nft_flowtable {
1105 struct list_head list;
1106 struct nft_table *table;
1107 char *name;
1108 int hooknum;
1109 int priority;
1110 int ops_len;
1111 u32 genmask:2,
1112 use:30;
1113 /* runtime data below here */
1114 struct nf_hook_ops *ops ____cacheline_aligned;
1115 struct nf_flowtable data;
1116};
1117
1118struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
1119 const struct nlattr *nla,
1120 u8 genmask);
1121void nft_flow_table_iterate(struct net *net,
1122 void (*iter)(struct nf_flowtable *flowtable, void *data),
1123 void *data);
1124
1125void nft_register_flowtable_type(struct nf_flowtable_type *type);
1126void nft_unregister_flowtable_type(struct nf_flowtable_type *type);
1127
1128/**
1088 * struct nft_traceinfo - nft tracing information and state 1129 * struct nft_traceinfo - nft tracing information and state
1089 * 1130 *
1090 * @pkt: pktinfo currently processed 1131 * @pkt: pktinfo currently processed
@@ -1317,4 +1358,11 @@ struct nft_trans_obj {
1317#define nft_trans_obj(trans) \ 1358#define nft_trans_obj(trans) \
1318 (((struct nft_trans_obj *)trans->data)->obj) 1359 (((struct nft_trans_obj *)trans->data)->obj)
1319 1360
1361struct nft_trans_flowtable {
1362 struct nft_flowtable *flowtable;
1363};
1364
1365#define nft_trans_flowtable(trans) \
1366 (((struct nft_trans_flowtable *)trans->data)->flowtable)
1367
1320#endif /* _NET_NF_TABLES_H */ 1368#endif /* _NET_NF_TABLES_H */
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 2efbf9744c2a..591b53bce070 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -92,6 +92,9 @@ enum nft_verdicts {
92 * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes) 92 * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)
93 * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes) 93 * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)
94 * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes) 94 * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)
95 * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
96 * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
97 * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes)
95 */ 98 */
96enum nf_tables_msg_types { 99enum nf_tables_msg_types {
97 NFT_MSG_NEWTABLE, 100 NFT_MSG_NEWTABLE,
@@ -116,6 +119,9 @@ enum nf_tables_msg_types {
116 NFT_MSG_GETOBJ, 119 NFT_MSG_GETOBJ,
117 NFT_MSG_DELOBJ, 120 NFT_MSG_DELOBJ,
118 NFT_MSG_GETOBJ_RESET, 121 NFT_MSG_GETOBJ_RESET,
122 NFT_MSG_NEWFLOWTABLE,
123 NFT_MSG_GETFLOWTABLE,
124 NFT_MSG_DELFLOWTABLE,
119 NFT_MSG_MAX, 125 NFT_MSG_MAX,
120}; 126};
121 127
@@ -1310,6 +1316,53 @@ enum nft_object_attributes {
1310#define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1) 1316#define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1)
1311 1317
1312/** 1318/**
1319 * enum nft_flowtable_attributes - nf_tables flow table netlink attributes
1320 *
1321 * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING)
1322 * @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING)
1323 * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)
1324 * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)
1325 */
1326enum nft_flowtable_attributes {
1327 NFTA_FLOWTABLE_UNSPEC,
1328 NFTA_FLOWTABLE_TABLE,
1329 NFTA_FLOWTABLE_NAME,
1330 NFTA_FLOWTABLE_HOOK,
1331 NFTA_FLOWTABLE_USE,
1332 __NFTA_FLOWTABLE_MAX
1333};
1334#define NFTA_FLOWTABLE_MAX (__NFTA_FLOWTABLE_MAX - 1)
1335
1336/**
1337 * enum nft_flowtable_hook_attributes - nf_tables flow table hook netlink attributes
1338 *
1339 * @NFTA_FLOWTABLE_HOOK_NUM: netfilter hook number (NLA_U32)
1340 * @NFTA_FLOWTABLE_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
1341 * @NFTA_FLOWTABLE_HOOK_DEVS: input devices this flow table is bound to (NLA_NESTED)
1342 */
1343enum nft_flowtable_hook_attributes {
1344 NFTA_FLOWTABLE_HOOK_UNSPEC,
1345 NFTA_FLOWTABLE_HOOK_NUM,
1346 NFTA_FLOWTABLE_HOOK_PRIORITY,
1347 NFTA_FLOWTABLE_HOOK_DEVS,
1348 __NFTA_FLOWTABLE_HOOK_MAX
1349};
1350#define NFTA_FLOWTABLE_HOOK_MAX (__NFTA_FLOWTABLE_HOOK_MAX - 1)
1351
1352/**
1353 * enum nft_device_attributes - nf_tables device netlink attributes
1354 *
1355 * @NFTA_DEVICE_NAME: name of this device (NLA_STRING)
1356 */
1357enum nft_devices_attributes {
1358 NFTA_DEVICE_UNSPEC,
1359 NFTA_DEVICE_NAME,
1360 __NFTA_DEVICE_MAX
1361};
1362#define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1)
1363
1364
1365/**
1313 * enum nft_trace_attributes - nf_tables trace netlink attributes 1366 * enum nft_trace_attributes - nf_tables trace netlink attributes
1314 * 1367 *
1315 * @NFTA_TRACE_TABLE: name of the table (NLA_STRING) 1368 * @NFTA_TRACE_TABLE: name of the table (NLA_STRING)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index fa564dac66a2..db0933256ec9 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -17,6 +17,7 @@
17#include <linux/netfilter.h> 17#include <linux/netfilter.h>
18#include <linux/netfilter/nfnetlink.h> 18#include <linux/netfilter/nfnetlink.h>
19#include <linux/netfilter/nf_tables.h> 19#include <linux/netfilter/nf_tables.h>
20#include <net/netfilter/nf_flow_table.h>
20#include <net/netfilter/nf_tables_core.h> 21#include <net/netfilter/nf_tables_core.h>
21#include <net/netfilter/nf_tables.h> 22#include <net/netfilter/nf_tables.h>
22#include <net/net_namespace.h> 23#include <net/net_namespace.h>
@@ -24,6 +25,7 @@
24 25
25static LIST_HEAD(nf_tables_expressions); 26static LIST_HEAD(nf_tables_expressions);
26static LIST_HEAD(nf_tables_objects); 27static LIST_HEAD(nf_tables_objects);
28static LIST_HEAD(nf_tables_flowtables);
27 29
28/** 30/**
29 * nft_register_afinfo - register nf_tables address family info 31 * nft_register_afinfo - register nf_tables address family info
@@ -345,6 +347,40 @@ static int nft_delobj(struct nft_ctx *ctx, struct nft_object *obj)
345 return err; 347 return err;
346} 348}
347 349
350static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
351 struct nft_flowtable *flowtable)
352{
353 struct nft_trans *trans;
354
355 trans = nft_trans_alloc(ctx, msg_type,
356 sizeof(struct nft_trans_flowtable));
357 if (trans == NULL)
358 return -ENOMEM;
359
360 if (msg_type == NFT_MSG_NEWFLOWTABLE)
361 nft_activate_next(ctx->net, flowtable);
362
363 nft_trans_flowtable(trans) = flowtable;
364 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
365
366 return 0;
367}
368
369static int nft_delflowtable(struct nft_ctx *ctx,
370 struct nft_flowtable *flowtable)
371{
372 int err;
373
374 err = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable);
375 if (err < 0)
376 return err;
377
378 nft_deactivate_next(ctx->net, flowtable);
379 ctx->table->use--;
380
381 return err;
382}
383
348/* 384/*
349 * Tables 385 * Tables
350 */ 386 */
@@ -728,6 +764,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
728 INIT_LIST_HEAD(&table->chains); 764 INIT_LIST_HEAD(&table->chains);
729 INIT_LIST_HEAD(&table->sets); 765 INIT_LIST_HEAD(&table->sets);
730 INIT_LIST_HEAD(&table->objects); 766 INIT_LIST_HEAD(&table->objects);
767 INIT_LIST_HEAD(&table->flowtables);
731 table->flags = flags; 768 table->flags = flags;
732 769
733 nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); 770 nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
@@ -749,10 +786,11 @@ err1:
749 786
750static int nft_flush_table(struct nft_ctx *ctx) 787static int nft_flush_table(struct nft_ctx *ctx)
751{ 788{
752 int err; 789 struct nft_flowtable *flowtable, *nft;
753 struct nft_chain *chain, *nc; 790 struct nft_chain *chain, *nc;
754 struct nft_object *obj, *ne; 791 struct nft_object *obj, *ne;
755 struct nft_set *set, *ns; 792 struct nft_set *set, *ns;
793 int err;
756 794
757 list_for_each_entry(chain, &ctx->table->chains, list) { 795 list_for_each_entry(chain, &ctx->table->chains, list) {
758 if (!nft_is_active_next(ctx->net, chain)) 796 if (!nft_is_active_next(ctx->net, chain))
@@ -778,6 +816,12 @@ static int nft_flush_table(struct nft_ctx *ctx)
778 goto out; 816 goto out;
779 } 817 }
780 818
819 list_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) {
820 err = nft_delflowtable(ctx, flowtable);
821 if (err < 0)
822 goto out;
823 }
824
781 list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) { 825 list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) {
782 err = nft_delobj(ctx, obj); 826 err = nft_delobj(ctx, obj);
783 if (err < 0) 827 if (err < 0)
@@ -4839,6 +4883,605 @@ static void nf_tables_obj_notify(const struct nft_ctx *ctx,
4839 ctx->afi->family, ctx->report, GFP_KERNEL); 4883 ctx->afi->family, ctx->report, GFP_KERNEL);
4840} 4884}
4841 4885
4886/*
4887 * Flow tables
4888 */
4889void nft_register_flowtable_type(struct nf_flowtable_type *type)
4890{
4891 nfnl_lock(NFNL_SUBSYS_NFTABLES);
4892 list_add_tail_rcu(&type->list, &nf_tables_flowtables);
4893 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
4894}
4895EXPORT_SYMBOL_GPL(nft_register_flowtable_type);
4896
4897void nft_unregister_flowtable_type(struct nf_flowtable_type *type)
4898{
4899 nfnl_lock(NFNL_SUBSYS_NFTABLES);
4900 list_del_rcu(&type->list);
4901 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
4902}
4903EXPORT_SYMBOL_GPL(nft_unregister_flowtable_type);
4904
4905static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = {
4906 [NFTA_FLOWTABLE_TABLE] = { .type = NLA_STRING,
4907 .len = NFT_NAME_MAXLEN - 1 },
4908 [NFTA_FLOWTABLE_NAME] = { .type = NLA_STRING,
4909 .len = NFT_NAME_MAXLEN - 1 },
4910 [NFTA_FLOWTABLE_HOOK] = { .type = NLA_NESTED },
4911};
4912
4913struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
4914 const struct nlattr *nla,
4915 u8 genmask)
4916{
4917 struct nft_flowtable *flowtable;
4918
4919 list_for_each_entry(flowtable, &table->flowtables, list) {
4920 if (!nla_strcmp(nla, flowtable->name) &&
4921 nft_active_genmask(flowtable, genmask))
4922 return flowtable;
4923 }
4924 return ERR_PTR(-ENOENT);
4925}
4926EXPORT_SYMBOL_GPL(nf_tables_flowtable_lookup);
4927
4928#define NFT_FLOWTABLE_DEVICE_MAX 8
4929
4930static int nf_tables_parse_devices(const struct nft_ctx *ctx,
4931 const struct nlattr *attr,
4932 struct net_device *dev_array[], int *len)
4933{
4934 const struct nlattr *tmp;
4935 struct net_device *dev;
4936 char ifname[IFNAMSIZ];
4937 int rem, n = 0, err;
4938
4939 nla_for_each_nested(tmp, attr, rem) {
4940 if (nla_type(tmp) != NFTA_DEVICE_NAME) {
4941 err = -EINVAL;
4942 goto err1;
4943 }
4944
4945 nla_strlcpy(ifname, tmp, IFNAMSIZ);
4946 dev = dev_get_by_name(ctx->net, ifname);
4947 if (!dev) {
4948 err = -ENOENT;
4949 goto err1;
4950 }
4951
4952 dev_array[n++] = dev;
4953 if (n == NFT_FLOWTABLE_DEVICE_MAX) {
4954 err = -EFBIG;
4955 goto err1;
4956 }
4957 }
4958 if (!len)
4959 return -EINVAL;
4960
4961 err = 0;
4962err1:
4963 *len = n;
4964 return err;
4965}
4966
4967static const struct nla_policy nft_flowtable_hook_policy[NFTA_FLOWTABLE_HOOK_MAX + 1] = {
4968 [NFTA_FLOWTABLE_HOOK_NUM] = { .type = NLA_U32 },
4969 [NFTA_FLOWTABLE_HOOK_PRIORITY] = { .type = NLA_U32 },
4970 [NFTA_FLOWTABLE_HOOK_DEVS] = { .type = NLA_NESTED },
4971};
4972
4973static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,
4974 const struct nlattr *attr,
4975 struct nft_flowtable *flowtable)
4976{
4977 struct net_device *dev_array[NFT_FLOWTABLE_DEVICE_MAX];
4978 struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1];
4979 struct nf_hook_ops *ops;
4980 int hooknum, priority;
4981 int err, n = 0, i;
4982
4983 err = nla_parse_nested(tb, NFTA_FLOWTABLE_HOOK_MAX, attr,
4984 nft_flowtable_hook_policy, NULL);
4985 if (err < 0)
4986 return err;
4987
4988 if (!tb[NFTA_FLOWTABLE_HOOK_NUM] ||
4989 !tb[NFTA_FLOWTABLE_HOOK_PRIORITY] ||
4990 !tb[NFTA_FLOWTABLE_HOOK_DEVS])
4991 return -EINVAL;
4992
4993 hooknum = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_NUM]));
4994 if (hooknum >= ctx->afi->nhooks)
4995 return -EINVAL;
4996
4997 priority = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY]));
4998
4999 err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS],
5000 dev_array, &n);
5001 if (err < 0)
5002 goto err1;
5003
5004 ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL);
5005 if (!ops) {
5006 err = -ENOMEM;
5007 goto err1;
5008 }
5009
5010 flowtable->ops = ops;
5011 flowtable->ops_len = n;
5012
5013 for (i = 0; i < n; i++) {
5014 flowtable->ops[i].pf = NFPROTO_NETDEV;
5015 flowtable->ops[i].hooknum = hooknum;
5016 flowtable->ops[i].priority = priority;
5017 flowtable->ops[i].priv = &flowtable->data.rhashtable;
5018 flowtable->ops[i].hook = flowtable->data.type->hook;
5019 flowtable->ops[i].dev = dev_array[i];
5020 }
5021
5022 err = 0;
5023err1:
5024 for (i = 0; i < n; i++)
5025 dev_put(dev_array[i]);
5026
5027 return err;
5028}
5029
5030static const struct nf_flowtable_type *
5031__nft_flowtable_type_get(const struct nft_af_info *afi)
5032{
5033 const struct nf_flowtable_type *type;
5034
5035 list_for_each_entry(type, &nf_tables_flowtables, list) {
5036 if (afi->family == type->family)
5037 return type;
5038 }
5039 return NULL;
5040}
5041
5042static const struct nf_flowtable_type *
5043nft_flowtable_type_get(const struct nft_af_info *afi)
5044{
5045 const struct nf_flowtable_type *type;
5046
5047 type = __nft_flowtable_type_get(afi);
5048 if (type != NULL && try_module_get(type->owner))
5049 return type;
5050
5051#ifdef CONFIG_MODULES
5052 if (type == NULL) {
5053 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
5054 request_module("nf-flowtable-%u", afi->family);
5055 nfnl_lock(NFNL_SUBSYS_NFTABLES);
5056 if (__nft_flowtable_type_get(afi))
5057 return ERR_PTR(-EAGAIN);
5058 }
5059#endif
5060 return ERR_PTR(-ENOENT);
5061}
5062
5063void nft_flow_table_iterate(struct net *net,
5064 void (*iter)(struct nf_flowtable *flowtable, void *data),
5065 void *data)
5066{
5067 struct nft_flowtable *flowtable;
5068 const struct nft_af_info *afi;
5069 const struct nft_table *table;
5070
5071 rcu_read_lock();
5072 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
5073 list_for_each_entry_rcu(table, &afi->tables, list) {
5074 list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
5075 iter(&flowtable->data, data);
5076 }
5077 }
5078 }
5079 rcu_read_unlock();
5080}
5081EXPORT_SYMBOL_GPL(nft_flow_table_iterate);
5082
5083static void nft_unregister_flowtable_net_hooks(struct net *net,
5084 struct nft_flowtable *flowtable)
5085{
5086 int i;
5087
5088 for (i = 0; i < flowtable->ops_len; i++) {
5089 if (!flowtable->ops[i].dev)
5090 continue;
5091
5092 nf_unregister_net_hook(net, &flowtable->ops[i]);
5093 }
5094}
5095
5096static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
5097 struct sk_buff *skb,
5098 const struct nlmsghdr *nlh,
5099 const struct nlattr * const nla[],
5100 struct netlink_ext_ack *extack)
5101{
5102 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
5103 const struct nf_flowtable_type *type;
5104 u8 genmask = nft_genmask_next(net);
5105 int family = nfmsg->nfgen_family;
5106 struct nft_flowtable *flowtable;
5107 struct nft_af_info *afi;
5108 struct nft_table *table;
5109 struct nft_ctx ctx;
5110 int err, i, k;
5111
5112 if (!nla[NFTA_FLOWTABLE_TABLE] ||
5113 !nla[NFTA_FLOWTABLE_NAME] ||
5114 !nla[NFTA_FLOWTABLE_HOOK])
5115 return -EINVAL;
5116
5117 afi = nf_tables_afinfo_lookup(net, family, true);
5118 if (IS_ERR(afi))
5119 return PTR_ERR(afi);
5120
5121 table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
5122 if (IS_ERR(table))
5123 return PTR_ERR(table);
5124
5125 flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
5126 genmask);
5127 if (IS_ERR(flowtable)) {
5128 err = PTR_ERR(flowtable);
5129 if (err != -ENOENT)
5130 return err;
5131 } else {
5132 if (nlh->nlmsg_flags & NLM_F_EXCL)
5133 return -EEXIST;
5134
5135 return 0;
5136 }
5137
5138 nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
5139
5140 flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL);
5141 if (!flowtable)
5142 return -ENOMEM;
5143
5144 flowtable->table = table;
5145 flowtable->name = nla_strdup(nla[NFTA_FLOWTABLE_NAME], GFP_KERNEL);
5146 if (!flowtable->name) {
5147 err = -ENOMEM;
5148 goto err1;
5149 }
5150
5151 type = nft_flowtable_type_get(afi);
5152 if (IS_ERR(type)) {
5153 err = PTR_ERR(type);
5154 goto err2;
5155 }
5156
5157 flowtable->data.type = type;
5158 err = rhashtable_init(&flowtable->data.rhashtable, type->params);
5159 if (err < 0)
5160 goto err3;
5161
5162 err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],
5163 flowtable);
5164 if (err < 0)
5165 goto err3;
5166
5167 for (i = 0; i < flowtable->ops_len; i++) {
5168 err = nf_register_net_hook(net, &flowtable->ops[i]);
5169 if (err < 0)
5170 goto err4;
5171 }
5172
5173 err = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable);
5174 if (err < 0)
5175 goto err5;
5176
5177 INIT_DEFERRABLE_WORK(&flowtable->data.gc_work, type->gc);
5178 queue_delayed_work(system_power_efficient_wq,
5179 &flowtable->data.gc_work, HZ);
5180
5181 list_add_tail_rcu(&flowtable->list, &table->flowtables);
5182 table->use++;
5183
5184 return 0;
5185err5:
5186 i = flowtable->ops_len;
5187err4:
5188 for (k = i - 1; k >= 0; k--)
5189 nf_unregister_net_hook(net, &flowtable->ops[i]);
5190
5191 kfree(flowtable->ops);
5192err3:
5193 module_put(type->owner);
5194err2:
5195 kfree(flowtable->name);
5196err1:
5197 kfree(flowtable);
5198 return err;
5199}
5200
5201static int nf_tables_delflowtable(struct net *net, struct sock *nlsk,
5202 struct sk_buff *skb,
5203 const struct nlmsghdr *nlh,
5204 const struct nlattr * const nla[],
5205 struct netlink_ext_ack *extack)
5206{
5207 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
5208 u8 genmask = nft_genmask_next(net);
5209 int family = nfmsg->nfgen_family;
5210 struct nft_flowtable *flowtable;
5211 struct nft_af_info *afi;
5212 struct nft_table *table;
5213 struct nft_ctx ctx;
5214
5215 afi = nf_tables_afinfo_lookup(net, family, true);
5216 if (IS_ERR(afi))
5217 return PTR_ERR(afi);
5218
5219 table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
5220 if (IS_ERR(table))
5221 return PTR_ERR(table);
5222
5223 flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
5224 genmask);
5225 if (IS_ERR(flowtable))
5226 return PTR_ERR(flowtable);
5227 if (flowtable->use > 0)
5228 return -EBUSY;
5229
5230 nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
5231
5232 return nft_delflowtable(&ctx, flowtable);
5233}
5234
5235static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
5236 u32 portid, u32 seq, int event,
5237 u32 flags, int family,
5238 struct nft_flowtable *flowtable)
5239{
5240 struct nlattr *nest, *nest_devs;
5241 struct nfgenmsg *nfmsg;
5242 struct nlmsghdr *nlh;
5243 int i;
5244
5245 event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
5246 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
5247 if (nlh == NULL)
5248 goto nla_put_failure;
5249
5250 nfmsg = nlmsg_data(nlh);
5251 nfmsg->nfgen_family = family;
5252 nfmsg->version = NFNETLINK_V0;
5253 nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
5254
5255 if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) ||
5256 nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||
5257 nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)))
5258 goto nla_put_failure;
5259
5260 nest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK);
5261 if (nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_NUM, htonl(flowtable->hooknum)) ||
5262 nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(flowtable->priority)))
5263 goto nla_put_failure;
5264
5265 nest_devs = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK_DEVS);
5266 if (!nest_devs)
5267 goto nla_put_failure;
5268
5269 for (i = 0; i < flowtable->ops_len; i++) {
5270 if (flowtable->ops[i].dev &&
5271 nla_put_string(skb, NFTA_DEVICE_NAME,
5272 flowtable->ops[i].dev->name))
5273 goto nla_put_failure;
5274 }
5275 nla_nest_end(skb, nest_devs);
5276 nla_nest_end(skb, nest);
5277
5278 nlmsg_end(skb, nlh);
5279 return 0;
5280
5281nla_put_failure:
5282 nlmsg_trim(skb, nlh);
5283 return -1;
5284}
5285
5286struct nft_flowtable_filter {
5287 char *table;
5288};
5289
5290static int nf_tables_dump_flowtable(struct sk_buff *skb,
5291 struct netlink_callback *cb)
5292{
5293 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
5294 struct nft_flowtable_filter *filter = cb->data;
5295 unsigned int idx = 0, s_idx = cb->args[0];
5296 struct net *net = sock_net(skb->sk);
5297 int family = nfmsg->nfgen_family;
5298 struct nft_flowtable *flowtable;
5299 const struct nft_af_info *afi;
5300 const struct nft_table *table;
5301
5302 rcu_read_lock();
5303 cb->seq = net->nft.base_seq;
5304
5305 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
5306 if (family != NFPROTO_UNSPEC && family != afi->family)
5307 continue;
5308
5309 list_for_each_entry_rcu(table, &afi->tables, list) {
5310 list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
5311 if (!nft_is_active(net, flowtable))
5312 goto cont;
5313 if (idx < s_idx)
5314 goto cont;
5315 if (idx > s_idx)
5316 memset(&cb->args[1], 0,
5317 sizeof(cb->args) - sizeof(cb->args[0]));
5318 if (filter && filter->table[0] &&
5319 strcmp(filter->table, table->name))
5320 goto cont;
5321
5322 if (nf_tables_fill_flowtable_info(skb, net, NETLINK_CB(cb->skb).portid,
5323 cb->nlh->nlmsg_seq,
5324 NFT_MSG_NEWFLOWTABLE,
5325 NLM_F_MULTI | NLM_F_APPEND,
5326 afi->family, flowtable) < 0)
5327 goto done;
5328
5329 nl_dump_check_consistent(cb, nlmsg_hdr(skb));
5330cont:
5331 idx++;
5332 }
5333 }
5334 }
5335done:
5336 rcu_read_unlock();
5337
5338 cb->args[0] = idx;
5339 return skb->len;
5340}
5341
5342static int nf_tables_dump_flowtable_done(struct netlink_callback *cb)
5343{
5344 struct nft_flowtable_filter *filter = cb->data;
5345
5346 if (!filter)
5347 return 0;
5348
5349 kfree(filter->table);
5350 kfree(filter);
5351
5352 return 0;
5353}
5354
5355static struct nft_flowtable_filter *
5356nft_flowtable_filter_alloc(const struct nlattr * const nla[])
5357{
5358 struct nft_flowtable_filter *filter;
5359
5360 filter = kzalloc(sizeof(*filter), GFP_KERNEL);
5361 if (!filter)
5362 return ERR_PTR(-ENOMEM);
5363
5364 if (nla[NFTA_FLOWTABLE_TABLE]) {
5365 filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE],
5366 GFP_KERNEL);
5367 if (!filter->table) {
5368 kfree(filter);
5369 return ERR_PTR(-ENOMEM);
5370 }
5371 }
5372 return filter;
5373}
5374
5375static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
5376 struct sk_buff *skb,
5377 const struct nlmsghdr *nlh,
5378 const struct nlattr * const nla[],
5379 struct netlink_ext_ack *extack)
5380{
5381 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
5382 u8 genmask = nft_genmask_cur(net);
5383 int family = nfmsg->nfgen_family;
5384 struct nft_flowtable *flowtable;
5385 const struct nft_af_info *afi;
5386 const struct nft_table *table;
5387 struct sk_buff *skb2;
5388 int err;
5389
5390 if (nlh->nlmsg_flags & NLM_F_DUMP) {
5391 struct netlink_dump_control c = {
5392 .dump = nf_tables_dump_flowtable,
5393 .done = nf_tables_dump_flowtable_done,
5394 };
5395
5396 if (nla[NFTA_FLOWTABLE_TABLE]) {
5397 struct nft_flowtable_filter *filter;
5398
5399 filter = nft_flowtable_filter_alloc(nla);
5400 if (IS_ERR(filter))
5401 return -ENOMEM;
5402
5403 c.data = filter;
5404 }
5405 return netlink_dump_start(nlsk, skb, nlh, &c);
5406 }
5407
5408 if (!nla[NFTA_FLOWTABLE_NAME])
5409 return -EINVAL;
5410
5411 afi = nf_tables_afinfo_lookup(net, family, false);
5412 if (IS_ERR(afi))
5413 return PTR_ERR(afi);
5414
5415 table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
5416 if (IS_ERR(table))
5417 return PTR_ERR(table);
5418
5419 flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
5420 genmask);
5421 if (IS_ERR(table))
5422 return PTR_ERR(flowtable);
5423
5424 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
5425 if (!skb2)
5426 return -ENOMEM;
5427
5428 err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,
5429 nlh->nlmsg_seq,
5430 NFT_MSG_NEWFLOWTABLE, 0, family,
5431 flowtable);
5432 if (err < 0)
5433 goto err;
5434
5435 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
5436err:
5437 kfree_skb(skb2);
5438 return err;
5439}
5440
5441static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
5442 struct nft_flowtable *flowtable,
5443 int event)
5444{
5445 struct sk_buff *skb;
5446 int err;
5447
5448 if (ctx->report &&
5449 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
5450 return;
5451
5452 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
5453 if (skb == NULL)
5454 goto err;
5455
5456 err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid,
5457 ctx->seq, event, 0,
5458 ctx->afi->family, flowtable);
5459 if (err < 0) {
5460 kfree_skb(skb);
5461 goto err;
5462 }
5463
5464 nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
5465 ctx->report, GFP_KERNEL);
5466 return;
5467err:
5468 nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
5469}
5470
5471static void nft_flowtable_destroy(void *ptr, void *arg)
5472{
5473 kfree(ptr);
5474}
5475
5476static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
5477{
5478 cancel_delayed_work_sync(&flowtable->data.gc_work);
5479 kfree(flowtable->name);
5480 rhashtable_free_and_destroy(&flowtable->data.rhashtable,
5481 nft_flowtable_destroy, NULL);
5482 module_put(flowtable->data.type->owner);
5483}
5484
4842static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, 5485static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
4843 u32 portid, u32 seq) 5486 u32 portid, u32 seq)
4844{ 5487{
@@ -4869,6 +5512,49 @@ nla_put_failure:
4869 return -EMSGSIZE; 5512 return -EMSGSIZE;
4870} 5513}
4871 5514
5515static void nft_flowtable_event(unsigned long event, struct net_device *dev,
5516 struct nft_flowtable *flowtable)
5517{
5518 int i;
5519
5520 for (i = 0; i < flowtable->ops_len; i++) {
5521 if (flowtable->ops[i].dev != dev)
5522 continue;
5523
5524 nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]);
5525 flowtable->ops[i].dev = NULL;
5526 break;
5527 }
5528}
5529
5530static int nf_tables_flowtable_event(struct notifier_block *this,
5531 unsigned long event, void *ptr)
5532{
5533 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
5534 struct nft_flowtable *flowtable;
5535 struct nft_table *table;
5536 struct nft_af_info *afi;
5537
5538 if (event != NETDEV_UNREGISTER)
5539 return 0;
5540
5541 nfnl_lock(NFNL_SUBSYS_NFTABLES);
5542 list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {
5543 list_for_each_entry(table, &afi->tables, list) {
5544 list_for_each_entry(flowtable, &table->flowtables, list) {
5545 nft_flowtable_event(event, dev, flowtable);
5546 }
5547 }
5548 }
5549 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
5550
5551 return NOTIFY_DONE;
5552}
5553
5554static struct notifier_block nf_tables_flowtable_notifier = {
5555 .notifier_call = nf_tables_flowtable_event,
5556};
5557
4872static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb, 5558static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb,
4873 int event) 5559 int event)
4874{ 5560{
@@ -5021,6 +5707,21 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
5021 .attr_count = NFTA_OBJ_MAX, 5707 .attr_count = NFTA_OBJ_MAX,
5022 .policy = nft_obj_policy, 5708 .policy = nft_obj_policy,
5023 }, 5709 },
5710 [NFT_MSG_NEWFLOWTABLE] = {
5711 .call_batch = nf_tables_newflowtable,
5712 .attr_count = NFTA_FLOWTABLE_MAX,
5713 .policy = nft_flowtable_policy,
5714 },
5715 [NFT_MSG_GETFLOWTABLE] = {
5716 .call = nf_tables_getflowtable,
5717 .attr_count = NFTA_FLOWTABLE_MAX,
5718 .policy = nft_flowtable_policy,
5719 },
5720 [NFT_MSG_DELFLOWTABLE] = {
5721 .call_batch = nf_tables_delflowtable,
5722 .attr_count = NFTA_FLOWTABLE_MAX,
5723 .policy = nft_flowtable_policy,
5724 },
5024}; 5725};
5025 5726
5026static void nft_chain_commit_update(struct nft_trans *trans) 5727static void nft_chain_commit_update(struct nft_trans *trans)
@@ -5066,6 +5767,9 @@ static void nf_tables_commit_release(struct nft_trans *trans)
5066 case NFT_MSG_DELOBJ: 5767 case NFT_MSG_DELOBJ:
5067 nft_obj_destroy(nft_trans_obj(trans)); 5768 nft_obj_destroy(nft_trans_obj(trans));
5068 break; 5769 break;
5770 case NFT_MSG_DELFLOWTABLE:
5771 nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
5772 break;
5069 } 5773 }
5070 kfree(trans); 5774 kfree(trans);
5071} 5775}
@@ -5183,6 +5887,21 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
5183 nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans), 5887 nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans),
5184 NFT_MSG_DELOBJ); 5888 NFT_MSG_DELOBJ);
5185 break; 5889 break;
5890 case NFT_MSG_NEWFLOWTABLE:
5891 nft_clear(net, nft_trans_flowtable(trans));
5892 nf_tables_flowtable_notify(&trans->ctx,
5893 nft_trans_flowtable(trans),
5894 NFT_MSG_NEWFLOWTABLE);
5895 nft_trans_destroy(trans);
5896 break;
5897 case NFT_MSG_DELFLOWTABLE:
5898 list_del_rcu(&nft_trans_flowtable(trans)->list);
5899 nf_tables_flowtable_notify(&trans->ctx,
5900 nft_trans_flowtable(trans),
5901 NFT_MSG_DELFLOWTABLE);
5902 nft_unregister_flowtable_net_hooks(net,
5903 nft_trans_flowtable(trans));
5904 break;
5186 } 5905 }
5187 } 5906 }
5188 5907
@@ -5220,6 +5939,9 @@ static void nf_tables_abort_release(struct nft_trans *trans)
5220 case NFT_MSG_NEWOBJ: 5939 case NFT_MSG_NEWOBJ:
5221 nft_obj_destroy(nft_trans_obj(trans)); 5940 nft_obj_destroy(nft_trans_obj(trans));
5222 break; 5941 break;
5942 case NFT_MSG_NEWFLOWTABLE:
5943 nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
5944 break;
5223 } 5945 }
5224 kfree(trans); 5946 kfree(trans);
5225} 5947}
@@ -5309,6 +6031,17 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
5309 nft_clear(trans->ctx.net, nft_trans_obj(trans)); 6031 nft_clear(trans->ctx.net, nft_trans_obj(trans));
5310 nft_trans_destroy(trans); 6032 nft_trans_destroy(trans);
5311 break; 6033 break;
6034 case NFT_MSG_NEWFLOWTABLE:
6035 trans->ctx.table->use--;
6036 list_del_rcu(&nft_trans_flowtable(trans)->list);
6037 nft_unregister_flowtable_net_hooks(net,
6038 nft_trans_flowtable(trans));
6039 break;
6040 case NFT_MSG_DELFLOWTABLE:
6041 trans->ctx.table->use++;
6042 nft_clear(trans->ctx.net, nft_trans_flowtable(trans));
6043 nft_trans_destroy(trans);
6044 break;
5312 } 6045 }
5313 } 6046 }
5314 6047
@@ -5865,6 +6598,7 @@ EXPORT_SYMBOL_GPL(__nft_release_basechain);
5865/* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */ 6598/* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */
5866static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi) 6599static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
5867{ 6600{
6601 struct nft_flowtable *flowtable, *nf;
5868 struct nft_table *table, *nt; 6602 struct nft_table *table, *nt;
5869 struct nft_chain *chain, *nc; 6603 struct nft_chain *chain, *nc;
5870 struct nft_object *obj, *ne; 6604 struct nft_object *obj, *ne;
@@ -5878,6 +6612,9 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
5878 list_for_each_entry_safe(table, nt, &afi->tables, list) { 6612 list_for_each_entry_safe(table, nt, &afi->tables, list) {
5879 list_for_each_entry(chain, &table->chains, list) 6613 list_for_each_entry(chain, &table->chains, list)
5880 nf_tables_unregister_hook(net, table, chain); 6614 nf_tables_unregister_hook(net, table, chain);
6615 list_for_each_entry(flowtable, &table->flowtables, list)
6616 nf_unregister_net_hooks(net, flowtable->ops,
6617 flowtable->ops_len);
5881 /* No packets are walking on these chains anymore. */ 6618 /* No packets are walking on these chains anymore. */
5882 ctx.table = table; 6619 ctx.table = table;
5883 list_for_each_entry(chain, &table->chains, list) { 6620 list_for_each_entry(chain, &table->chains, list) {
@@ -5888,6 +6625,11 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
5888 nf_tables_rule_destroy(&ctx, rule); 6625 nf_tables_rule_destroy(&ctx, rule);
5889 } 6626 }
5890 } 6627 }
6628 list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
6629 list_del(&flowtable->list);
6630 table->use--;
6631 nf_tables_flowtable_destroy(flowtable);
6632 }
5891 list_for_each_entry_safe(set, ns, &table->sets, list) { 6633 list_for_each_entry_safe(set, ns, &table->sets, list) {
5892 list_del(&set->list); 6634 list_del(&set->list);
5893 table->use--; 6635 table->use--;
@@ -5932,6 +6674,8 @@ static int __init nf_tables_module_init(void)
5932 if (err < 0) 6674 if (err < 0)
5933 goto err3; 6675 goto err3;
5934 6676
6677 register_netdevice_notifier(&nf_tables_flowtable_notifier);
6678
5935 pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n"); 6679 pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
5936 return register_pernet_subsys(&nf_tables_net_ops); 6680 return register_pernet_subsys(&nf_tables_net_ops);
5937err3: 6681err3:
@@ -5946,6 +6690,7 @@ static void __exit nf_tables_module_exit(void)
5946{ 6690{
5947 unregister_pernet_subsys(&nf_tables_net_ops); 6691 unregister_pernet_subsys(&nf_tables_net_ops);
5948 nfnetlink_subsys_unregister(&nf_tables_subsys); 6692 nfnetlink_subsys_unregister(&nf_tables_subsys);
6693 unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
5949 rcu_barrier(); 6694 rcu_barrier();
5950 nf_tables_core_module_exit(); 6695 nf_tables_core_module_exit();
5951 kfree(info); 6696 kfree(info);