aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-4.14/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/backport-4.14/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch')
-rw-r--r--target/linux/generic/backport-4.14/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch1079
1 files changed, 0 insertions, 1079 deletions
diff --git a/target/linux/generic/backport-4.14/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch b/target/linux/generic/backport-4.14/321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch
deleted file mode 100644
index f0109e4823..0000000000
--- a/target/linux/generic/backport-4.14/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
-@@ -939,6 +940,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)
-@@ -950,6 +952,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,
-@@ -1081,6 +1084,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
-@@ -1316,4 +1357,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
-@@ -389,6 +391,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
- */
-@@ -772,6 +808,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);
-@@ -793,10 +830,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))
-@@ -822,6 +860,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)
-@@ -4862,6 +4906,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)
- {
-@@ -4892,6 +5535,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)
- {
-@@ -5044,6 +5730,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)
-@@ -5092,6 +5793,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);
- }
-@@ -5211,6 +5915,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;
- }
- }
-
-@@ -5248,6 +5967,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);
- }
-@@ -5339,6 +6061,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;
- }
- }
-
-@@ -5889,6 +6622,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;
-@@ -5902,6 +6636,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) {
-@@ -5912,6 +6649,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--;
-@@ -5955,6 +6697,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:
-@@ -5969,6 +6713,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);