diff options
Diffstat (limited to 'target/linux/generic/backport-4.19/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch')
-rw-r--r-- | target/linux/generic/backport-4.19/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch | 1079 |
1 files changed, 0 insertions, 1079 deletions
diff --git a/target/linux/generic/backport-4.19/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch b/target/linux/generic/backport-4.19/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch deleted file mode 100644 index 1c16bbae4e..0000000000 --- a/target/linux/generic/backport-4.19/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch +++ /dev/null @@ -1,1079 +0,0 @@ -From: Pablo Neira Ayuso <pablo@netfilter.org> -Date: Sun, 7 Jan 2018 01:04:07 +0100 -Subject: [PATCH] netfilter: nf_tables: add flow table netlink frontend - -This patch introduces a netlink control plane to create, delete and dump -flow tables. Flow tables are identified by name, this name is used from -rules to refer to an specific flow table. Flow tables use the rhashtable -class and a generic garbage collector to remove expired entries. - -This also adds the infrastructure to add different flow table types, so -we can add one for each layer 3 protocol family. - -Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> ---- - create mode 100644 include/net/netfilter/nf_flow_table.h - ---- /dev/null -+++ b/include/net/netfilter/nf_flow_table.h -@@ -0,0 +1,23 @@ -+#ifndef _NF_FLOW_TABLE_H -+#define _NF_FLOW_TABLE_H -+ -+#include <linux/rhashtable.h> -+ -+struct nf_flowtable; -+ -+struct nf_flowtable_type { -+ struct list_head list; -+ int family; -+ void (*gc)(struct work_struct *work); -+ const struct rhashtable_params *params; -+ nf_hookfn *hook; -+ struct module *owner; -+}; -+ -+struct nf_flowtable { -+ struct rhashtable rhashtable; -+ const struct nf_flowtable_type *type; -+ struct delayed_work gc_work; -+}; -+ -+#endif /* _FLOW_OFFLOAD_H */ ---- a/include/net/netfilter/nf_tables.h -+++ b/include/net/netfilter/nf_tables.h -@@ -9,6 +9,7 @@ - #include <linux/netfilter/x_tables.h> - #include <linux/netfilter/nf_tables.h> - #include <linux/u64_stats_sync.h> -+#include <net/netfilter/nf_flow_table.h> - #include <net/netlink.h> - - #define NFT_JUMP_STACK_SIZE 16 -@@ -938,6 +939,7 @@ unsigned int nft_do_chain(struct nft_pkt - * @chains: chains in the table - * @sets: sets in the table - * @objects: stateful objects in the table -+ * @flowtables: flow tables in the table - * @hgenerator: handle generator state - * @use: number of chain references to this table - * @flags: table flag (see enum nft_table_flags) -@@ -949,6 +951,7 @@ struct nft_table { - struct list_head chains; - struct list_head sets; - struct list_head objects; -+ struct list_head flowtables; - u64 hgenerator; - u32 use; - u16 flags:14, -@@ -1080,6 +1083,44 @@ int nft_register_obj(struct nft_object_t - void nft_unregister_obj(struct nft_object_type *obj_type); - - /** -+ * struct nft_flowtable - nf_tables flow table -+ * -+ * @list: flow table list node in table list -+ * @table: the table the flow table is contained in -+ * @name: name of this flow table -+ * @hooknum: hook number -+ * @priority: hook priority -+ * @ops_len: number of hooks in array -+ * @genmask: generation mask -+ * @use: number of references to this flow table -+ * @data: rhashtable and garbage collector -+ * @ops: array of hooks -+ */ -+struct nft_flowtable { -+ struct list_head list; -+ struct nft_table *table; -+ char *name; -+ int hooknum; -+ int priority; -+ int ops_len; -+ u32 genmask:2, -+ use:30; -+ /* runtime data below here */ -+ struct nf_hook_ops *ops ____cacheline_aligned; -+ struct nf_flowtable data; -+}; -+ -+struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table, -+ const struct nlattr *nla, -+ u8 genmask); -+void nft_flow_table_iterate(struct net *net, -+ void (*iter)(struct nf_flowtable *flowtable, void *data), -+ void *data); -+ -+void nft_register_flowtable_type(struct nf_flowtable_type *type); -+void nft_unregister_flowtable_type(struct nf_flowtable_type *type); -+ -+/** - * struct nft_traceinfo - nft tracing information and state - * - * @pkt: pktinfo currently processed -@@ -1315,4 +1356,11 @@ struct nft_trans_obj { - #define nft_trans_obj(trans) \ - (((struct nft_trans_obj *)trans->data)->obj) - -+struct nft_trans_flowtable { -+ struct nft_flowtable *flowtable; -+}; -+ -+#define nft_trans_flowtable(trans) \ -+ (((struct nft_trans_flowtable *)trans->data)->flowtable) -+ - #endif /* _NET_NF_TABLES_H */ ---- a/include/uapi/linux/netfilter/nf_tables.h -+++ b/include/uapi/linux/netfilter/nf_tables.h -@@ -92,6 +92,9 @@ enum nft_verdicts { - * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes) - * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes) - * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes) -+ * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes) -+ * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes) -+ * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes) - */ - enum nf_tables_msg_types { - NFT_MSG_NEWTABLE, -@@ -116,6 +119,9 @@ enum nf_tables_msg_types { - NFT_MSG_GETOBJ, - NFT_MSG_DELOBJ, - NFT_MSG_GETOBJ_RESET, -+ NFT_MSG_NEWFLOWTABLE, -+ NFT_MSG_GETFLOWTABLE, -+ NFT_MSG_DELFLOWTABLE, - NFT_MSG_MAX, - }; - -@@ -1310,6 +1316,53 @@ enum nft_object_attributes { - #define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1) - - /** -+ * enum nft_flowtable_attributes - nf_tables flow table netlink attributes -+ * -+ * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING) -+ * @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING) -+ * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32) -+ * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32) -+ */ -+enum nft_flowtable_attributes { -+ NFTA_FLOWTABLE_UNSPEC, -+ NFTA_FLOWTABLE_TABLE, -+ NFTA_FLOWTABLE_NAME, -+ NFTA_FLOWTABLE_HOOK, -+ NFTA_FLOWTABLE_USE, -+ __NFTA_FLOWTABLE_MAX -+}; -+#define NFTA_FLOWTABLE_MAX (__NFTA_FLOWTABLE_MAX - 1) -+ -+/** -+ * enum nft_flowtable_hook_attributes - nf_tables flow table hook netlink attributes -+ * -+ * @NFTA_FLOWTABLE_HOOK_NUM: netfilter hook number (NLA_U32) -+ * @NFTA_FLOWTABLE_HOOK_PRIORITY: netfilter hook priority (NLA_U32) -+ * @NFTA_FLOWTABLE_HOOK_DEVS: input devices this flow table is bound to (NLA_NESTED) -+ */ -+enum nft_flowtable_hook_attributes { -+ NFTA_FLOWTABLE_HOOK_UNSPEC, -+ NFTA_FLOWTABLE_HOOK_NUM, -+ NFTA_FLOWTABLE_HOOK_PRIORITY, -+ NFTA_FLOWTABLE_HOOK_DEVS, -+ __NFTA_FLOWTABLE_HOOK_MAX -+}; -+#define NFTA_FLOWTABLE_HOOK_MAX (__NFTA_FLOWTABLE_HOOK_MAX - 1) -+ -+/** -+ * enum nft_device_attributes - nf_tables device netlink attributes -+ * -+ * @NFTA_DEVICE_NAME: name of this device (NLA_STRING) -+ */ -+enum nft_devices_attributes { -+ NFTA_DEVICE_UNSPEC, -+ NFTA_DEVICE_NAME, -+ __NFTA_DEVICE_MAX -+}; -+#define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1) -+ -+ -+/** - * enum nft_trace_attributes - nf_tables trace netlink attributes - * - * @NFTA_TRACE_TABLE: name of the table (NLA_STRING) ---- a/net/netfilter/nf_tables_api.c -+++ b/net/netfilter/nf_tables_api.c -@@ -17,6 +17,7 @@ - #include <linux/netfilter.h> - #include <linux/netfilter/nfnetlink.h> - #include <linux/netfilter/nf_tables.h> -+#include <net/netfilter/nf_flow_table.h> - #include <net/netfilter/nf_tables_core.h> - #include <net/netfilter/nf_tables.h> - #include <net/net_namespace.h> -@@ -24,6 +25,7 @@ - - static LIST_HEAD(nf_tables_expressions); - static LIST_HEAD(nf_tables_objects); -+static LIST_HEAD(nf_tables_flowtables); - - /** - * nft_register_afinfo - register nf_tables address family info -@@ -374,6 +376,40 @@ static int nft_delobj(struct nft_ctx *ct - return err; - } - -+static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type, -+ struct nft_flowtable *flowtable) -+{ -+ struct nft_trans *trans; -+ -+ trans = nft_trans_alloc(ctx, msg_type, -+ sizeof(struct nft_trans_flowtable)); -+ if (trans == NULL) -+ return -ENOMEM; -+ -+ if (msg_type == NFT_MSG_NEWFLOWTABLE) -+ nft_activate_next(ctx->net, flowtable); -+ -+ nft_trans_flowtable(trans) = flowtable; -+ list_add_tail(&trans->list, &ctx->net->nft.commit_list); -+ -+ return 0; -+} -+ -+static int nft_delflowtable(struct nft_ctx *ctx, -+ struct nft_flowtable *flowtable) -+{ -+ int err; -+ -+ err = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable); -+ if (err < 0) -+ return err; -+ -+ nft_deactivate_next(ctx->net, flowtable); -+ ctx->table->use--; -+ -+ return err; -+} -+ - /* - * Tables - */ -@@ -757,6 +793,7 @@ static int nf_tables_newtable(struct net - INIT_LIST_HEAD(&table->chains); - INIT_LIST_HEAD(&table->sets); - INIT_LIST_HEAD(&table->objects); -+ INIT_LIST_HEAD(&table->flowtables); - table->flags = flags; - - nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); -@@ -778,10 +815,11 @@ err1: - - static int nft_flush_table(struct nft_ctx *ctx) - { -- int err; -+ struct nft_flowtable *flowtable, *nft; - struct nft_chain *chain, *nc; - struct nft_object *obj, *ne; - struct nft_set *set, *ns; -+ int err; - - list_for_each_entry(chain, &ctx->table->chains, list) { - if (!nft_is_active_next(ctx->net, chain)) -@@ -807,6 +845,12 @@ static int nft_flush_table(struct nft_ct - goto out; - } - -+ list_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) { -+ err = nft_delflowtable(ctx, flowtable); -+ if (err < 0) -+ goto out; -+ } -+ - list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) { - err = nft_delobj(ctx, obj); - if (err < 0) -@@ -4835,6 +4879,605 @@ static void nf_tables_obj_notify(const s - ctx->afi->family, ctx->report, GFP_KERNEL); - } - -+/* -+ * Flow tables -+ */ -+void nft_register_flowtable_type(struct nf_flowtable_type *type) -+{ -+ nfnl_lock(NFNL_SUBSYS_NFTABLES); -+ list_add_tail_rcu(&type->list, &nf_tables_flowtables); -+ nfnl_unlock(NFNL_SUBSYS_NFTABLES); -+} -+EXPORT_SYMBOL_GPL(nft_register_flowtable_type); -+ -+void nft_unregister_flowtable_type(struct nf_flowtable_type *type) -+{ -+ nfnl_lock(NFNL_SUBSYS_NFTABLES); -+ list_del_rcu(&type->list); -+ nfnl_unlock(NFNL_SUBSYS_NFTABLES); -+} -+EXPORT_SYMBOL_GPL(nft_unregister_flowtable_type); -+ -+static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = { -+ [NFTA_FLOWTABLE_TABLE] = { .type = NLA_STRING, -+ .len = NFT_NAME_MAXLEN - 1 }, -+ [NFTA_FLOWTABLE_NAME] = { .type = NLA_STRING, -+ .len = NFT_NAME_MAXLEN - 1 }, -+ [NFTA_FLOWTABLE_HOOK] = { .type = NLA_NESTED }, -+}; -+ -+struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table, -+ const struct nlattr *nla, -+ u8 genmask) -+{ -+ struct nft_flowtable *flowtable; -+ -+ list_for_each_entry(flowtable, &table->flowtables, list) { -+ if (!nla_strcmp(nla, flowtable->name) && -+ nft_active_genmask(flowtable, genmask)) -+ return flowtable; -+ } -+ return ERR_PTR(-ENOENT); -+} -+EXPORT_SYMBOL_GPL(nf_tables_flowtable_lookup); -+ -+#define NFT_FLOWTABLE_DEVICE_MAX 8 -+ -+static int nf_tables_parse_devices(const struct nft_ctx *ctx, -+ const struct nlattr *attr, -+ struct net_device *dev_array[], int *len) -+{ -+ const struct nlattr *tmp; -+ struct net_device *dev; -+ char ifname[IFNAMSIZ]; -+ int rem, n = 0, err; -+ -+ nla_for_each_nested(tmp, attr, rem) { -+ if (nla_type(tmp) != NFTA_DEVICE_NAME) { -+ err = -EINVAL; -+ goto err1; -+ } -+ -+ nla_strlcpy(ifname, tmp, IFNAMSIZ); -+ dev = dev_get_by_name(ctx->net, ifname); -+ if (!dev) { -+ err = -ENOENT; -+ goto err1; -+ } -+ -+ dev_array[n++] = dev; -+ if (n == NFT_FLOWTABLE_DEVICE_MAX) { -+ err = -EFBIG; -+ goto err1; -+ } -+ } -+ if (!len) -+ return -EINVAL; -+ -+ err = 0; -+err1: -+ *len = n; -+ return err; -+} -+ -+static const struct nla_policy nft_flowtable_hook_policy[NFTA_FLOWTABLE_HOOK_MAX + 1] = { -+ [NFTA_FLOWTABLE_HOOK_NUM] = { .type = NLA_U32 }, -+ [NFTA_FLOWTABLE_HOOK_PRIORITY] = { .type = NLA_U32 }, -+ [NFTA_FLOWTABLE_HOOK_DEVS] = { .type = NLA_NESTED }, -+}; -+ -+static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx, -+ const struct nlattr *attr, -+ struct nft_flowtable *flowtable) -+{ -+ struct net_device *dev_array[NFT_FLOWTABLE_DEVICE_MAX]; -+ struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1]; -+ struct nf_hook_ops *ops; -+ int hooknum, priority; -+ int err, n = 0, i; -+ -+ err = nla_parse_nested(tb, NFTA_FLOWTABLE_HOOK_MAX, attr, -+ nft_flowtable_hook_policy, NULL); -+ if (err < 0) -+ return err; -+ -+ if (!tb[NFTA_FLOWTABLE_HOOK_NUM] || -+ !tb[NFTA_FLOWTABLE_HOOK_PRIORITY] || -+ !tb[NFTA_FLOWTABLE_HOOK_DEVS]) -+ return -EINVAL; -+ -+ hooknum = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_NUM])); -+ if (hooknum >= ctx->afi->nhooks) -+ return -EINVAL; -+ -+ priority = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY])); -+ -+ err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS], -+ dev_array, &n); -+ if (err < 0) -+ goto err1; -+ -+ ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL); -+ if (!ops) { -+ err = -ENOMEM; -+ goto err1; -+ } -+ -+ flowtable->ops = ops; -+ flowtable->ops_len = n; -+ -+ for (i = 0; i < n; i++) { -+ flowtable->ops[i].pf = NFPROTO_NETDEV; -+ flowtable->ops[i].hooknum = hooknum; -+ flowtable->ops[i].priority = priority; -+ flowtable->ops[i].priv = &flowtable->data.rhashtable; -+ flowtable->ops[i].hook = flowtable->data.type->hook; -+ flowtable->ops[i].dev = dev_array[i]; -+ } -+ -+ err = 0; -+err1: -+ for (i = 0; i < n; i++) -+ dev_put(dev_array[i]); -+ -+ return err; -+} -+ -+static const struct nf_flowtable_type * -+__nft_flowtable_type_get(const struct nft_af_info *afi) -+{ -+ const struct nf_flowtable_type *type; -+ -+ list_for_each_entry(type, &nf_tables_flowtables, list) { -+ if (afi->family == type->family) -+ return type; -+ } -+ return NULL; -+} -+ -+static const struct nf_flowtable_type * -+nft_flowtable_type_get(const struct nft_af_info *afi) -+{ -+ const struct nf_flowtable_type *type; -+ -+ type = __nft_flowtable_type_get(afi); -+ if (type != NULL && try_module_get(type->owner)) -+ return type; -+ -+#ifdef CONFIG_MODULES -+ if (type == NULL) { -+ nfnl_unlock(NFNL_SUBSYS_NFTABLES); -+ request_module("nf-flowtable-%u", afi->family); -+ nfnl_lock(NFNL_SUBSYS_NFTABLES); -+ if (__nft_flowtable_type_get(afi)) -+ return ERR_PTR(-EAGAIN); -+ } -+#endif -+ return ERR_PTR(-ENOENT); -+} -+ -+void nft_flow_table_iterate(struct net *net, -+ void (*iter)(struct nf_flowtable *flowtable, void *data), -+ void *data) -+{ -+ struct nft_flowtable *flowtable; -+ const struct nft_af_info *afi; -+ const struct nft_table *table; -+ -+ rcu_read_lock(); -+ list_for_each_entry_rcu(afi, &net->nft.af_info, list) { -+ list_for_each_entry_rcu(table, &afi->tables, list) { -+ list_for_each_entry_rcu(flowtable, &table->flowtables, list) { -+ iter(&flowtable->data, data); -+ } -+ } -+ } -+ rcu_read_unlock(); -+} -+EXPORT_SYMBOL_GPL(nft_flow_table_iterate); -+ -+static void nft_unregister_flowtable_net_hooks(struct net *net, -+ struct nft_flowtable *flowtable) -+{ -+ int i; -+ -+ for (i = 0; i < flowtable->ops_len; i++) { -+ if (!flowtable->ops[i].dev) -+ continue; -+ -+ nf_unregister_net_hook(net, &flowtable->ops[i]); -+ } -+} -+ -+static int nf_tables_newflowtable(struct net *net, struct sock *nlsk, -+ struct sk_buff *skb, -+ const struct nlmsghdr *nlh, -+ const struct nlattr * const nla[], -+ struct netlink_ext_ack *extack) -+{ -+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh); -+ const struct nf_flowtable_type *type; -+ u8 genmask = nft_genmask_next(net); -+ int family = nfmsg->nfgen_family; -+ struct nft_flowtable *flowtable; -+ struct nft_af_info *afi; -+ struct nft_table *table; -+ struct nft_ctx ctx; -+ int err, i, k; -+ -+ if (!nla[NFTA_FLOWTABLE_TABLE] || -+ !nla[NFTA_FLOWTABLE_NAME] || -+ !nla[NFTA_FLOWTABLE_HOOK]) -+ return -EINVAL; -+ -+ afi = nf_tables_afinfo_lookup(net, family, true); -+ if (IS_ERR(afi)) -+ return PTR_ERR(afi); -+ -+ table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask); -+ if (IS_ERR(table)) -+ return PTR_ERR(table); -+ -+ flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME], -+ genmask); -+ if (IS_ERR(flowtable)) { -+ err = PTR_ERR(flowtable); -+ if (err != -ENOENT) -+ return err; -+ } else { -+ if (nlh->nlmsg_flags & NLM_F_EXCL) -+ return -EEXIST; -+ -+ return 0; -+ } -+ -+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); -+ -+ flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL); -+ if (!flowtable) -+ return -ENOMEM; -+ -+ flowtable->table = table; -+ flowtable->name = nla_strdup(nla[NFTA_FLOWTABLE_NAME], GFP_KERNEL); -+ if (!flowtable->name) { -+ err = -ENOMEM; -+ goto err1; -+ } -+ -+ type = nft_flowtable_type_get(afi); -+ if (IS_ERR(type)) { -+ err = PTR_ERR(type); -+ goto err2; -+ } -+ -+ flowtable->data.type = type; -+ err = rhashtable_init(&flowtable->data.rhashtable, type->params); -+ if (err < 0) -+ goto err3; -+ -+ err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK], -+ flowtable); -+ if (err < 0) -+ goto err3; -+ -+ for (i = 0; i < flowtable->ops_len; i++) { -+ err = nf_register_net_hook(net, &flowtable->ops[i]); -+ if (err < 0) -+ goto err4; -+ } -+ -+ err = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable); -+ if (err < 0) -+ goto err5; -+ -+ INIT_DEFERRABLE_WORK(&flowtable->data.gc_work, type->gc); -+ queue_delayed_work(system_power_efficient_wq, -+ &flowtable->data.gc_work, HZ); -+ -+ list_add_tail_rcu(&flowtable->list, &table->flowtables); -+ table->use++; -+ -+ return 0; -+err5: -+ i = flowtable->ops_len; -+err4: -+ for (k = i - 1; k >= 0; k--) -+ nf_unregister_net_hook(net, &flowtable->ops[i]); -+ -+ kfree(flowtable->ops); -+err3: -+ module_put(type->owner); -+err2: -+ kfree(flowtable->name); -+err1: -+ kfree(flowtable); -+ return err; -+} -+ -+static int nf_tables_delflowtable(struct net *net, struct sock *nlsk, -+ struct sk_buff *skb, -+ const struct nlmsghdr *nlh, -+ const struct nlattr * const nla[], -+ struct netlink_ext_ack *extack) -+{ -+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh); -+ u8 genmask = nft_genmask_next(net); -+ int family = nfmsg->nfgen_family; -+ struct nft_flowtable *flowtable; -+ struct nft_af_info *afi; -+ struct nft_table *table; -+ struct nft_ctx ctx; -+ -+ afi = nf_tables_afinfo_lookup(net, family, true); -+ if (IS_ERR(afi)) -+ return PTR_ERR(afi); -+ -+ table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask); -+ if (IS_ERR(table)) -+ return PTR_ERR(table); -+ -+ flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME], -+ genmask); -+ if (IS_ERR(flowtable)) -+ return PTR_ERR(flowtable); -+ if (flowtable->use > 0) -+ return -EBUSY; -+ -+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); -+ -+ return nft_delflowtable(&ctx, flowtable); -+} -+ -+static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net, -+ u32 portid, u32 seq, int event, -+ u32 flags, int family, -+ struct nft_flowtable *flowtable) -+{ -+ struct nlattr *nest, *nest_devs; -+ struct nfgenmsg *nfmsg; -+ struct nlmsghdr *nlh; -+ int i; -+ -+ event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); -+ nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); -+ if (nlh == NULL) -+ goto nla_put_failure; -+ -+ nfmsg = nlmsg_data(nlh); -+ nfmsg->nfgen_family = family; -+ nfmsg->version = NFNETLINK_V0; -+ nfmsg->res_id = htons(net->nft.base_seq & 0xffff); -+ -+ if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) || -+ nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) || -+ nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use))) -+ goto nla_put_failure; -+ -+ nest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK); -+ if (nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_NUM, htonl(flowtable->hooknum)) || -+ nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(flowtable->priority))) -+ goto nla_put_failure; -+ -+ nest_devs = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK_DEVS); -+ if (!nest_devs) -+ goto nla_put_failure; -+ -+ for (i = 0; i < flowtable->ops_len; i++) { -+ if (flowtable->ops[i].dev && -+ nla_put_string(skb, NFTA_DEVICE_NAME, -+ flowtable->ops[i].dev->name)) -+ goto nla_put_failure; -+ } -+ nla_nest_end(skb, nest_devs); -+ nla_nest_end(skb, nest); -+ -+ nlmsg_end(skb, nlh); -+ return 0; -+ -+nla_put_failure: -+ nlmsg_trim(skb, nlh); -+ return -1; -+} -+ -+struct nft_flowtable_filter { -+ char *table; -+}; -+ -+static int nf_tables_dump_flowtable(struct sk_buff *skb, -+ struct netlink_callback *cb) -+{ -+ const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); -+ struct nft_flowtable_filter *filter = cb->data; -+ unsigned int idx = 0, s_idx = cb->args[0]; -+ struct net *net = sock_net(skb->sk); -+ int family = nfmsg->nfgen_family; -+ struct nft_flowtable *flowtable; -+ const struct nft_af_info *afi; -+ const struct nft_table *table; -+ -+ rcu_read_lock(); -+ cb->seq = net->nft.base_seq; -+ -+ list_for_each_entry_rcu(afi, &net->nft.af_info, list) { -+ if (family != NFPROTO_UNSPEC && family != afi->family) -+ continue; -+ -+ list_for_each_entry_rcu(table, &afi->tables, list) { -+ list_for_each_entry_rcu(flowtable, &table->flowtables, list) { -+ if (!nft_is_active(net, flowtable)) -+ goto cont; -+ if (idx < s_idx) -+ goto cont; -+ if (idx > s_idx) -+ memset(&cb->args[1], 0, -+ sizeof(cb->args) - sizeof(cb->args[0])); -+ if (filter && filter->table[0] && -+ strcmp(filter->table, table->name)) -+ goto cont; -+ -+ if (nf_tables_fill_flowtable_info(skb, net, NETLINK_CB(cb->skb).portid, -+ cb->nlh->nlmsg_seq, -+ NFT_MSG_NEWFLOWTABLE, -+ NLM_F_MULTI | NLM_F_APPEND, -+ afi->family, flowtable) < 0) -+ goto done; -+ -+ nl_dump_check_consistent(cb, nlmsg_hdr(skb)); -+cont: -+ idx++; -+ } -+ } -+ } -+done: -+ rcu_read_unlock(); -+ -+ cb->args[0] = idx; -+ return skb->len; -+} -+ -+static int nf_tables_dump_flowtable_done(struct netlink_callback *cb) -+{ -+ struct nft_flowtable_filter *filter = cb->data; -+ -+ if (!filter) -+ return 0; -+ -+ kfree(filter->table); -+ kfree(filter); -+ -+ return 0; -+} -+ -+static struct nft_flowtable_filter * -+nft_flowtable_filter_alloc(const struct nlattr * const nla[]) -+{ -+ struct nft_flowtable_filter *filter; -+ -+ filter = kzalloc(sizeof(*filter), GFP_KERNEL); -+ if (!filter) -+ return ERR_PTR(-ENOMEM); -+ -+ if (nla[NFTA_FLOWTABLE_TABLE]) { -+ filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE], -+ GFP_KERNEL); -+ if (!filter->table) { -+ kfree(filter); -+ return ERR_PTR(-ENOMEM); -+ } -+ } -+ return filter; -+} -+ -+static int nf_tables_getflowtable(struct net *net, struct sock *nlsk, -+ struct sk_buff *skb, -+ const struct nlmsghdr *nlh, -+ const struct nlattr * const nla[], -+ struct netlink_ext_ack *extack) -+{ -+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh); -+ u8 genmask = nft_genmask_cur(net); -+ int family = nfmsg->nfgen_family; -+ struct nft_flowtable *flowtable; -+ const struct nft_af_info *afi; -+ const struct nft_table *table; -+ struct sk_buff *skb2; -+ int err; -+ -+ if (nlh->nlmsg_flags & NLM_F_DUMP) { -+ struct netlink_dump_control c = { -+ .dump = nf_tables_dump_flowtable, -+ .done = nf_tables_dump_flowtable_done, -+ }; -+ -+ if (nla[NFTA_FLOWTABLE_TABLE]) { -+ struct nft_flowtable_filter *filter; -+ -+ filter = nft_flowtable_filter_alloc(nla); -+ if (IS_ERR(filter)) -+ return -ENOMEM; -+ -+ c.data = filter; -+ } -+ return netlink_dump_start(nlsk, skb, nlh, &c); -+ } -+ -+ if (!nla[NFTA_FLOWTABLE_NAME]) -+ return -EINVAL; -+ -+ afi = nf_tables_afinfo_lookup(net, family, false); -+ if (IS_ERR(afi)) -+ return PTR_ERR(afi); -+ -+ table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask); -+ if (IS_ERR(table)) -+ return PTR_ERR(table); -+ -+ flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME], -+ genmask); -+ if (IS_ERR(table)) -+ return PTR_ERR(flowtable); -+ -+ skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); -+ if (!skb2) -+ return -ENOMEM; -+ -+ err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid, -+ nlh->nlmsg_seq, -+ NFT_MSG_NEWFLOWTABLE, 0, family, -+ flowtable); -+ if (err < 0) -+ goto err; -+ -+ return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); -+err: -+ kfree_skb(skb2); -+ return err; -+} -+ -+static void nf_tables_flowtable_notify(struct nft_ctx *ctx, -+ struct nft_flowtable *flowtable, -+ int event) -+{ -+ struct sk_buff *skb; -+ int err; -+ -+ if (ctx->report && -+ !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) -+ return; -+ -+ skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); -+ if (skb == NULL) -+ goto err; -+ -+ err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid, -+ ctx->seq, event, 0, -+ ctx->afi->family, flowtable); -+ if (err < 0) { -+ kfree_skb(skb); -+ goto err; -+ } -+ -+ nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, -+ ctx->report, GFP_KERNEL); -+ return; -+err: -+ nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); -+} -+ -+static void nft_flowtable_destroy(void *ptr, void *arg) -+{ -+ kfree(ptr); -+} -+ -+static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable) -+{ -+ cancel_delayed_work_sync(&flowtable->data.gc_work); -+ kfree(flowtable->name); -+ rhashtable_free_and_destroy(&flowtable->data.rhashtable, -+ nft_flowtable_destroy, NULL); -+ module_put(flowtable->data.type->owner); -+} -+ - static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, - u32 portid, u32 seq) - { -@@ -4865,6 +5508,49 @@ nla_put_failure: - return -EMSGSIZE; - } - -+static void nft_flowtable_event(unsigned long event, struct net_device *dev, -+ struct nft_flowtable *flowtable) -+{ -+ int i; -+ -+ for (i = 0; i < flowtable->ops_len; i++) { -+ if (flowtable->ops[i].dev != dev) -+ continue; -+ -+ nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]); -+ flowtable->ops[i].dev = NULL; -+ break; -+ } -+} -+ -+static int nf_tables_flowtable_event(struct notifier_block *this, -+ unsigned long event, void *ptr) -+{ -+ struct net_device *dev = netdev_notifier_info_to_dev(ptr); -+ struct nft_flowtable *flowtable; -+ struct nft_table *table; -+ struct nft_af_info *afi; -+ -+ if (event != NETDEV_UNREGISTER) -+ return 0; -+ -+ nfnl_lock(NFNL_SUBSYS_NFTABLES); -+ list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) { -+ list_for_each_entry(table, &afi->tables, list) { -+ list_for_each_entry(flowtable, &table->flowtables, list) { -+ nft_flowtable_event(event, dev, flowtable); -+ } -+ } -+ } -+ nfnl_unlock(NFNL_SUBSYS_NFTABLES); -+ -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block nf_tables_flowtable_notifier = { -+ .notifier_call = nf_tables_flowtable_event, -+}; -+ - static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb, - int event) - { -@@ -5017,6 +5703,21 @@ static const struct nfnl_callback nf_tab - .attr_count = NFTA_OBJ_MAX, - .policy = nft_obj_policy, - }, -+ [NFT_MSG_NEWFLOWTABLE] = { -+ .call_batch = nf_tables_newflowtable, -+ .attr_count = NFTA_FLOWTABLE_MAX, -+ .policy = nft_flowtable_policy, -+ }, -+ [NFT_MSG_GETFLOWTABLE] = { -+ .call = nf_tables_getflowtable, -+ .attr_count = NFTA_FLOWTABLE_MAX, -+ .policy = nft_flowtable_policy, -+ }, -+ [NFT_MSG_DELFLOWTABLE] = { -+ .call_batch = nf_tables_delflowtable, -+ .attr_count = NFTA_FLOWTABLE_MAX, -+ .policy = nft_flowtable_policy, -+ }, - }; - - static void nft_chain_commit_update(struct nft_trans *trans) -@@ -5065,6 +5766,9 @@ static void nf_tables_commit_release(str - case NFT_MSG_DELOBJ: - nft_obj_destroy(nft_trans_obj(trans)); - break; -+ case NFT_MSG_DELFLOWTABLE: -+ nf_tables_flowtable_destroy(nft_trans_flowtable(trans)); -+ break; - } - kfree(trans); - } -@@ -5184,6 +5888,21 @@ static int nf_tables_commit(struct net * - nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans), - NFT_MSG_DELOBJ); - break; -+ case NFT_MSG_NEWFLOWTABLE: -+ nft_clear(net, nft_trans_flowtable(trans)); -+ nf_tables_flowtable_notify(&trans->ctx, -+ nft_trans_flowtable(trans), -+ NFT_MSG_NEWFLOWTABLE); -+ nft_trans_destroy(trans); -+ break; -+ case NFT_MSG_DELFLOWTABLE: -+ list_del_rcu(&nft_trans_flowtable(trans)->list); -+ nf_tables_flowtable_notify(&trans->ctx, -+ nft_trans_flowtable(trans), -+ NFT_MSG_DELFLOWTABLE); -+ nft_unregister_flowtable_net_hooks(net, -+ nft_trans_flowtable(trans)); -+ break; - } - } - -@@ -5221,6 +5940,9 @@ static void nf_tables_abort_release(stru - case NFT_MSG_NEWOBJ: - nft_obj_destroy(nft_trans_obj(trans)); - break; -+ case NFT_MSG_NEWFLOWTABLE: -+ nf_tables_flowtable_destroy(nft_trans_flowtable(trans)); -+ break; - } - kfree(trans); - } -@@ -5312,6 +6034,17 @@ static int nf_tables_abort(struct net *n - nft_clear(trans->ctx.net, nft_trans_obj(trans)); - nft_trans_destroy(trans); - break; -+ case NFT_MSG_NEWFLOWTABLE: -+ trans->ctx.table->use--; -+ list_del_rcu(&nft_trans_flowtable(trans)->list); -+ nft_unregister_flowtable_net_hooks(net, -+ nft_trans_flowtable(trans)); -+ break; -+ case NFT_MSG_DELFLOWTABLE: -+ trans->ctx.table->use++; -+ nft_clear(trans->ctx.net, nft_trans_flowtable(trans)); -+ nft_trans_destroy(trans); -+ break; - } - } - -@@ -5862,6 +6595,7 @@ EXPORT_SYMBOL_GPL(__nft_release_basechai - /* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */ - static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi) - { -+ struct nft_flowtable *flowtable, *nf; - struct nft_table *table, *nt; - struct nft_chain *chain, *nc; - struct nft_object *obj, *ne; -@@ -5875,6 +6609,9 @@ static void __nft_release_afinfo(struct - list_for_each_entry_safe(table, nt, &afi->tables, list) { - list_for_each_entry(chain, &table->chains, list) - nf_tables_unregister_hook(net, table, chain); -+ list_for_each_entry(flowtable, &table->flowtables, list) -+ nf_unregister_net_hooks(net, flowtable->ops, -+ flowtable->ops_len); - /* No packets are walking on these chains anymore. */ - ctx.table = table; - list_for_each_entry(chain, &table->chains, list) { -@@ -5885,6 +6622,11 @@ static void __nft_release_afinfo(struct - nf_tables_rule_release(&ctx, rule); - } - } -+ list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { -+ list_del(&flowtable->list); -+ table->use--; -+ nf_tables_flowtable_destroy(flowtable); -+ } - list_for_each_entry_safe(set, ns, &table->sets, list) { - list_del(&set->list); - table->use--; -@@ -5928,6 +6670,8 @@ static int __init nf_tables_module_init( - if (err < 0) - goto err3; - -+ register_netdevice_notifier(&nf_tables_flowtable_notifier); -+ - pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n"); - return register_pernet_subsys(&nf_tables_net_ops); - err3: -@@ -5942,6 +6686,7 @@ static void __exit nf_tables_module_exit - { - unregister_pernet_subsys(&nf_tables_net_ops); - nfnetlink_subsys_unregister(&nf_tables_subsys); -+ unregister_netdevice_notifier(&nf_tables_flowtable_notifier); - rcu_barrier(); - nf_tables_core_module_exit(); - kfree(info); |