get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/852526/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 852526,
    "url": "http://patchwork.ozlabs.org/api/patches/852526/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/20171222192732.13188-2-pablo@netfilter.org/",
    "project": {
        "id": 7,
        "url": "http://patchwork.ozlabs.org/api/projects/7/?format=api",
        "name": "Linux network development",
        "link_name": "netdev",
        "list_id": "netdev.vger.kernel.org",
        "list_email": "netdev@vger.kernel.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20171222192732.13188-2-pablo@netfilter.org>",
    "list_archive_url": null,
    "date": "2017-12-22T19:27:26",
    "name": "[nf-next,v3,1/7] netfilter: nf_tables: add flow table netlink frontend",
    "commit_ref": null,
    "pull_url": null,
    "state": "rfc",
    "archived": true,
    "hash": "32ecafd88093e155a625941ecd69b3d12d6ebd20",
    "submitter": {
        "id": 1315,
        "url": "http://patchwork.ozlabs.org/api/people/1315/?format=api",
        "name": "Pablo Neira Ayuso",
        "email": "pablo@netfilter.org"
    },
    "delegate": {
        "id": 34,
        "url": "http://patchwork.ozlabs.org/api/users/34/?format=api",
        "username": "davem",
        "first_name": "David",
        "last_name": "Miller",
        "email": "davem@davemloft.net"
    },
    "mbox": "http://patchwork.ozlabs.org/project/netdev/patch/20171222192732.13188-2-pablo@netfilter.org/mbox/",
    "series": [
        {
            "id": 20090,
            "url": "http://patchwork.ozlabs.org/api/series/20090/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/netdev/list/?series=20090",
            "date": "2017-12-22T19:27:25",
            "name": "Flow offload infrastructure",
            "version": 3,
            "mbox": "http://patchwork.ozlabs.org/series/20090/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/852526/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/852526/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<netdev-owner@vger.kernel.org>",
        "X-Original-To": "patchwork-incoming@ozlabs.org",
        "Delivered-To": "patchwork-incoming@ozlabs.org",
        "Authentication-Results": "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)",
        "Received": [
            "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3z3JSr5KYYz9s7n\n\tfor <patchwork-incoming@ozlabs.org>;\n\tSat, 23 Dec 2017 06:28:12 +1100 (AEDT)",
            "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1756790AbdLVT2J (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tFri, 22 Dec 2017 14:28:09 -0500",
            "from mail.us.es ([193.147.175.20]:42302 \"EHLO mail.us.es\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1756065AbdLVT2F (ORCPT <rfc822;netdev@vger.kernel.org>);\n\tFri, 22 Dec 2017 14:28:05 -0500",
            "from antivirus1-rhel7.int (unknown [192.168.2.11])\n\tby mail.us.es (Postfix) with ESMTP id 0187FEBAD2\n\tfor <netdev@vger.kernel.org>; Fri, 22 Dec 2017 20:28:03 +0100 (CET)",
            "from antivirus1-rhel7.int (localhost [127.0.0.1])\n\tby antivirus1-rhel7.int (Postfix) with ESMTP id D7C65F731E\n\tfor <netdev@vger.kernel.org>; Fri, 22 Dec 2017 20:28:02 +0100 (CET)",
            "by antivirus1-rhel7.int (Postfix, from userid 99)\n\tid C3285F7316; Fri, 22 Dec 2017 20:28:02 +0100 (CET)",
            "from antivirus1-rhel7.int (localhost [127.0.0.1])\n\tby antivirus1-rhel7.int (Postfix) with ESMTP id A775EF7306;\n\tFri, 22 Dec 2017 20:27:59 +0100 (CET)",
            "from 192.168.1.97 (192.168.1.97) by antivirus1-rhel7.int\n\t(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int); \n\tFri, 22 Dec 2017 20:27:59 +0100 (CET)",
            "from salvia.here (129.166.216.87.static.jazztel.es\n\t[87.216.166.129]) (Authenticated sender: pneira@us.es)\n\tby entrada.int (Postfix) with ESMTPA id 9CC794265A31;\n\tFri, 22 Dec 2017 20:27:58 +0100 (CET)"
        ],
        "X-Spam-Checker-Version": "SpamAssassin 3.4.1 (2015-04-28) on\n\tantivirus1-rhel7.int",
        "X-Spam-Level": "",
        "X-Spam-Status": "No, score=-108.2 required=7.5 tests=ALL_TRUSTED,BAYES_50,\n\tSMTPAUTH_US2,USER_IN_WHITELIST autolearn=disabled version=3.4.1",
        "X-Virus-Status": "clean(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int)",
        "X-SMTPAUTHUS": "auth mail.us.es",
        "From": "Pablo Neira Ayuso <pablo@netfilter.org>",
        "To": "netfilter-devel@vger.kernel.org",
        "Cc": "netdev@vger.kernel.org, f.fainelli@gmail.com,\n\tsimon.horman@netronome.com, ronye@mellanox.com, jiri@mellanox.com,\n\tnbd@nbd.name, john@phrozen.org, kubakici@wp.pl, fw@strlen.de",
        "Subject": "[PATCH nf-next,\n\tv3 1/7] netfilter: nf_tables: add flow table netlink frontend",
        "Date": "Fri, 22 Dec 2017 20:27:26 +0100",
        "Message-Id": "<20171222192732.13188-2-pablo@netfilter.org>",
        "X-Mailer": "git-send-email 2.11.0",
        "In-Reply-To": "<20171222192732.13188-1-pablo@netfilter.org>",
        "References": "<20171222192732.13188-1-pablo@netfilter.org>",
        "X-Virus-Scanned": "ClamAV using ClamSMTP",
        "Sender": "netdev-owner@vger.kernel.org",
        "Precedence": "bulk",
        "List-ID": "<netdev.vger.kernel.org>",
        "X-Mailing-List": "netdev@vger.kernel.org"
    },
    "content": "This patch introduces a netlink control plane to create, delete and dump\nflow tables. Flow tables are identified by name, this name is used from\nrules to refer to an specific flow table. Flow tables use the rhashtable\nclass and a generic garbage collector to remove expired entries.\n\nThis also adds the infrastructure to add different flow table types, so\nwe can add one for each layer 3 protocol family.\n\nSigned-off-by: Pablo Neira Ayuso <pablo@netfilter.org>\n---\n include/net/netfilter/nf_flow_table.h    |  23 +\n include/net/netfilter/nf_tables.h        |  48 ++\n include/uapi/linux/netfilter/nf_tables.h |  53 +++\n net/netfilter/nf_tables_api.c            | 747 ++++++++++++++++++++++++++++++-\n 4 files changed, 870 insertions(+), 1 deletion(-)\n create mode 100644 include/net/netfilter/nf_flow_table.h",
    "diff": "diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h\nnew file mode 100644\nindex 000000000000..3a0779589281\n--- /dev/null\n+++ b/include/net/netfilter/nf_flow_table.h\n@@ -0,0 +1,23 @@\n+#ifndef _NF_FLOW_TABLE_H\n+#define _NF_FLOW_TABLE_H\n+\n+#include <linux/rhashtable.h>\n+\n+struct nf_flowtable;\n+\n+struct nf_flowtable_type {\n+\tstruct list_head\t\tlist;\n+\tint\t\t\t\tfamily;\n+\tvoid\t\t\t\t(*gc)(struct work_struct *work);\n+\tconst struct rhashtable_params\t*params;\n+\tnf_hookfn\t\t\t*hook;\n+\tstruct module\t\t\t*owner;\n+};\n+\n+struct nf_flowtable {\n+\tstruct rhashtable\t\trhashtable;\n+\tconst struct nf_flowtable_type\t*type;\n+\tstruct delayed_work\t\tgc_work;\n+};\n+\n+#endif /* _FLOW_OFFLOAD_H */\ndiff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h\nindex 0f5b12a4ad09..624928d22589 100644\n--- a/include/net/netfilter/nf_tables.h\n+++ b/include/net/netfilter/nf_tables.h\n@@ -8,6 +8,7 @@\n #include <linux/netfilter/x_tables.h>\n #include <linux/netfilter/nf_tables.h>\n #include <linux/u64_stats_sync.h>\n+#include <net/netfilter/nf_flow_table.h>\n #include <net/netlink.h>\n \n #define NFT_JUMP_STACK_SIZE\t16\n@@ -942,6 +943,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv);\n  *\t@chains: chains in the table\n  *\t@sets: sets in the table\n  *\t@objects: stateful objects in the table\n+ *\t@flowtables: flow tables in the table\n  *\t@hgenerator: handle generator state\n  *\t@use: number of chain references to this table\n  *\t@flags: table flag (see enum nft_table_flags)\n@@ -953,6 +955,7 @@ struct nft_table {\n \tstruct list_head\t\tchains;\n \tstruct list_head\t\tsets;\n \tstruct list_head\t\tobjects;\n+\tstruct list_head\t\tflowtables;\n \tu64\t\t\t\thgenerator;\n \tu32\t\t\t\tuse;\n \tu16\t\t\t\tflags:14,\n@@ -1091,6 +1094,44 @@ int nft_register_obj(struct nft_object_type *obj_type);\n void nft_unregister_obj(struct nft_object_type *obj_type);\n \n /**\n+ *\tstruct nft_flowtable - nf_tables flow table\n+ *\n+ *\t@list: flow table list node in table list\n+ * \t@table: the table the flow table is contained in\n+ *\t@name: name of this flow table\n+ *\t@hooknum: hook number\n+ *\t@priority: hook priority\n+ *\t@ops_len: number of hooks in array\n+ *\t@genmask: generation mask\n+ *\t@use: number of references to this flow table\n+ *\t@data: rhashtable and garbage collector\n+ * \t@ops: array of hooks\n+ */\n+struct nft_flowtable {\n+\tstruct list_head\t\tlist;\n+\tstruct nft_table\t\t*table;\n+\tchar\t\t\t\t*name;\n+\tint\t\t\t\thooknum;\n+\tint\t\t\t\tpriority;\n+\tint\t\t\t\tops_len;\n+\tu32\t\t\t\tgenmask:2,\n+\t\t\t\t\tuse:30;\n+\t/* runtime data below here */\n+\tstruct nf_hook_ops\t\t*ops ____cacheline_aligned;\n+\tstruct nf_flowtable\t\tdata;\n+};\n+\n+struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,\n+\t\t\t\t\t\t const struct nlattr *nla,\n+\t\t\t\t\t\t u8 genmask);\n+void nft_flow_table_iterate(struct net *net,\n+\t\t\t    void (*iter)(struct nf_flowtable *flowtable, void *data),\n+\t\t\t    void *data);\n+\n+void nft_register_flowtable_type(struct nf_flowtable_type *type);\n+void nft_unregister_flowtable_type(struct nf_flowtable_type *type);\n+\n+/**\n  *\tstruct nft_traceinfo - nft tracing information and state\n  *\n  *\t@pkt: pktinfo currently processed\n@@ -1326,4 +1367,11 @@ struct nft_trans_obj {\n #define nft_trans_obj(trans)\t\\\n \t(((struct nft_trans_obj *)trans->data)->obj)\n \n+struct nft_trans_flowtable {\n+\tstruct nft_flowtable\t\t*flowtable;\n+};\n+\n+#define nft_trans_flowtable(trans)\t\\\n+\t(((struct nft_trans_flowtable *)trans->data)->flowtable)\n+\n #endif /* _NET_NF_TABLES_H */\ndiff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h\nindex 871afa4871bf..9ba0f4c13de6 100644\n--- a/include/uapi/linux/netfilter/nf_tables.h\n+++ b/include/uapi/linux/netfilter/nf_tables.h\n@@ -91,6 +91,9 @@ enum nft_verdicts {\n  * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)\n  * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)\n  * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)\n+ * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)\n+ * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)\n+ * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes)\n  */\n enum nf_tables_msg_types {\n \tNFT_MSG_NEWTABLE,\n@@ -115,6 +118,9 @@ enum nf_tables_msg_types {\n \tNFT_MSG_GETOBJ,\n \tNFT_MSG_DELOBJ,\n \tNFT_MSG_GETOBJ_RESET,\n+\tNFT_MSG_NEWFLOWTABLE,\n+\tNFT_MSG_GETFLOWTABLE,\n+\tNFT_MSG_DELFLOWTABLE,\n \tNFT_MSG_MAX,\n };\n \n@@ -1307,6 +1313,53 @@ enum nft_object_attributes {\n #define NFTA_OBJ_MAX\t\t(__NFTA_OBJ_MAX - 1)\n \n /**\n+ * enum nft_flowtable_attributes - nf_tables flow table netlink attributes\n+ *\n+ * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING)\n+ * @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING)\n+ * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)\n+ * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)\n+ */\n+enum nft_flowtable_attributes {\n+\tNFTA_FLOWTABLE_UNSPEC,\n+\tNFTA_FLOWTABLE_TABLE,\n+\tNFTA_FLOWTABLE_NAME,\n+\tNFTA_FLOWTABLE_HOOK,\n+\tNFTA_FLOWTABLE_USE,\n+\t__NFTA_FLOWTABLE_MAX\n+};\n+#define NFTA_FLOWTABLE_MAX\t(__NFTA_FLOWTABLE_MAX - 1)\n+\n+/**\n+ * enum nft_flowtable_hook_attributes - nf_tables flow table hook netlink attributes\n+ *\n+ * @NFTA_FLOWTABLE_HOOK_NUM: netfilter hook number (NLA_U32)\n+ * @NFTA_FLOWTABLE_HOOK_PRIORITY: netfilter hook priority (NLA_U32)\n+ * @NFTA_FLOWTABLE_HOOK_DEVS: input devices this flow table is bound to (NLA_NESTED)\n+ */\n+enum nft_flowtable_hook_attributes {\n+\tNFTA_FLOWTABLE_HOOK_UNSPEC,\n+\tNFTA_FLOWTABLE_HOOK_NUM,\n+\tNFTA_FLOWTABLE_HOOK_PRIORITY,\n+\tNFTA_FLOWTABLE_HOOK_DEVS,\n+\t__NFTA_FLOWTABLE_HOOK_MAX\n+};\n+#define NFTA_FLOWTABLE_HOOK_MAX\t(__NFTA_FLOWTABLE_HOOK_MAX - 1)\n+\n+/**\n+ * enum nft_device_attributes - nf_tables device netlink attributes\n+ *\n+ * @NFTA_DEVICE_NAME: name of this device (NLA_STRING)\n+ */\n+enum nft_devices_attributes {\n+\tNFTA_DEVICE_UNSPEC,\n+\tNFTA_DEVICE_NAME,\n+\t__NFTA_DEVICE_MAX\n+};\n+#define NFTA_DEVICE_MAX\t\t(__NFTA_DEVICE_MAX - 1)\n+\n+\n+/**\n  * enum nft_trace_attributes - nf_tables trace netlink attributes\n  *\n  * @NFTA_TRACE_TABLE: name of the table (NLA_STRING)\ndiff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c\nindex 64e1ee091225..efd9405a8a5e 100644\n--- a/net/netfilter/nf_tables_api.c\n+++ b/net/netfilter/nf_tables_api.c\n@@ -17,6 +17,7 @@\n #include <linux/netfilter.h>\n #include <linux/netfilter/nfnetlink.h>\n #include <linux/netfilter/nf_tables.h>\n+#include <net/netfilter/nf_flow_table.h>\n #include <net/netfilter/nf_tables_core.h>\n #include <net/netfilter/nf_tables.h>\n #include <net/net_namespace.h>\n@@ -24,6 +25,7 @@\n \n static LIST_HEAD(nf_tables_expressions);\n static LIST_HEAD(nf_tables_objects);\n+static LIST_HEAD(nf_tables_flowtables);\n \n /**\n  *\tnft_register_afinfo - register nf_tables address family info\n@@ -348,6 +350,40 @@ static int nft_delobj(struct nft_ctx *ctx, struct nft_object *obj)\n \treturn err;\n }\n \n+static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,\n+\t\t\t\t   struct nft_flowtable *flowtable)\n+{\n+\tstruct nft_trans *trans;\n+\n+\ttrans = nft_trans_alloc(ctx, msg_type,\n+\t\t\t\tsizeof(struct nft_trans_flowtable));\n+\tif (trans == NULL)\n+\t\treturn -ENOMEM;\n+\n+\tif (msg_type == NFT_MSG_NEWFLOWTABLE)\n+\t\tnft_activate_next(ctx->net, flowtable);\n+\n+\tnft_trans_flowtable(trans) = flowtable;\n+\tlist_add_tail(&trans->list, &ctx->net->nft.commit_list);\n+\n+\treturn 0;\n+}\n+\n+static int nft_delflowtable(struct nft_ctx *ctx,\n+\t\t\t    struct nft_flowtable *flowtable)\n+{\n+\tint err;\n+\n+\terr = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable);\n+\tif (err < 0)\n+\t\treturn err;\n+\n+\tnft_deactivate_next(ctx->net, flowtable);\n+\tctx->table->use--;\n+\n+\treturn err;\n+}\n+\n /*\n  * Tables\n  */\n@@ -733,6 +769,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,\n \tINIT_LIST_HEAD(&table->chains);\n \tINIT_LIST_HEAD(&table->sets);\n \tINIT_LIST_HEAD(&table->objects);\n+\tINIT_LIST_HEAD(&table->flowtables);\n \ttable->flags = flags;\n \n \tnft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);\n@@ -754,10 +791,11 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,\n \n static int nft_flush_table(struct nft_ctx *ctx)\n {\n-\tint err;\n+\tstruct nft_flowtable *flowtable, *nft;\n \tstruct nft_chain *chain, *nc;\n \tstruct nft_object *obj, *ne;\n \tstruct nft_set *set, *ns;\n+\tint err;\n \n \tlist_for_each_entry(chain, &ctx->table->chains, list) {\n \t\tif (!nft_is_active_next(ctx->net, chain))\n@@ -783,6 +821,12 @@ static int nft_flush_table(struct nft_ctx *ctx)\n \t\t\tgoto out;\n \t}\n \n+\tlist_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) {\n+\t\terr = nft_delflowtable(ctx, flowtable);\n+\t\tif (err < 0)\n+\t\t\tgoto out;\n+\t}\n+\n \tlist_for_each_entry_safe(obj, ne, &ctx->table->objects, list) {\n \t\terr = nft_delobj(ctx, obj);\n \t\tif (err < 0)\n@@ -4779,6 +4823,605 @@ static void nf_tables_obj_notify(const struct nft_ctx *ctx,\n \t\t       ctx->afi->family, ctx->report, GFP_KERNEL);\n }\n \n+/*\n+ * Flow tables\n+ */\n+void nft_register_flowtable_type(struct nf_flowtable_type *type)\n+{\n+\tnfnl_lock(NFNL_SUBSYS_NFTABLES);\n+\tlist_add_tail_rcu(&type->list, &nf_tables_flowtables);\n+\tnfnl_unlock(NFNL_SUBSYS_NFTABLES);\n+}\n+EXPORT_SYMBOL_GPL(nft_register_flowtable_type);\n+\n+void nft_unregister_flowtable_type(struct nf_flowtable_type *type)\n+{\n+\tnfnl_lock(NFNL_SUBSYS_NFTABLES);\n+\tlist_del_rcu(&type->list);\n+\tnfnl_unlock(NFNL_SUBSYS_NFTABLES);\n+}\n+EXPORT_SYMBOL_GPL(nft_unregister_flowtable_type);\n+\n+static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = {\n+\t[NFTA_FLOWTABLE_TABLE]\t\t= { .type = NLA_STRING,\n+\t\t\t\t\t    .len = NFT_NAME_MAXLEN - 1 },\n+\t[NFTA_FLOWTABLE_NAME]\t\t= { .type = NLA_STRING,\n+\t\t\t\t\t    .len = NFT_NAME_MAXLEN - 1 },\n+\t[NFTA_FLOWTABLE_HOOK]\t\t= { .type = NLA_NESTED },\n+};\n+\n+struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,\n+\t\t\t\t\t\t const struct nlattr *nla,\n+\t\t\t\t\t\t u8 genmask)\n+{\n+\tstruct nft_flowtable *flowtable;\n+\n+\tlist_for_each_entry(flowtable, &table->flowtables, list) {\n+\t\tif (!nla_strcmp(nla, flowtable->name) &&\n+\t\t    nft_active_genmask(flowtable, genmask))\n+\t\t\treturn flowtable;\n+\t}\n+\treturn ERR_PTR(-ENOENT);\n+}\n+EXPORT_SYMBOL_GPL(nf_tables_flowtable_lookup);\n+\n+#define NFT_FLOWTABLE_DEVICE_MAX\t8\n+\n+static int nf_tables_parse_devices(const struct nft_ctx *ctx,\n+\t\t\t\t   const struct nlattr *attr,\n+\t\t\t\t   struct net_device *dev_array[], int *len)\n+{\n+\tconst struct nlattr *tmp;\n+\tstruct net_device *dev;\n+\tchar ifname[IFNAMSIZ];\n+\tint rem, n = 0, err;\n+\n+\tnla_for_each_nested(tmp, attr, rem) {\n+\t\tif (nla_type(tmp) != NFTA_DEVICE_NAME) {\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto err1;\n+\t\t}\n+\n+\t\tnla_strlcpy(ifname, tmp, IFNAMSIZ);\n+\t\tdev = dev_get_by_name(ctx->net, ifname);\n+\t\tif (!dev) {\n+\t\t\terr = -ENOENT;\n+\t\t\tgoto err1;\n+\t\t}\n+\n+\t\tdev_array[n++] = dev;\n+\t\tif (n == NFT_FLOWTABLE_DEVICE_MAX) {\n+\t\t\terr = -EFBIG;\n+\t\t\tgoto err1;\n+\t\t}\n+\t}\n+\tif (!len)\n+\t\treturn -EINVAL;\n+\n+\terr = 0;\n+err1:\n+\t*len = n;\n+\treturn err;\n+}\n+\n+static const struct nla_policy nft_flowtable_hook_policy[NFTA_FLOWTABLE_HOOK_MAX + 1] = {\n+\t[NFTA_FLOWTABLE_HOOK_NUM]\t= { .type = NLA_U32 },\n+\t[NFTA_FLOWTABLE_HOOK_PRIORITY]\t= { .type = NLA_U32 },\n+\t[NFTA_FLOWTABLE_HOOK_DEVS]\t= { .type = NLA_NESTED },\n+};\n+\n+static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,\n+\t\t\t\t\t  const struct nlattr *attr,\n+\t\t\t\t\t  struct nft_flowtable *flowtable)\n+{\n+\tstruct net_device *dev_array[NFT_FLOWTABLE_DEVICE_MAX];\n+\tstruct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1];\n+\tstruct nf_hook_ops *ops;\n+\tint hooknum, priority;\n+\tint err, n = 0, i;\n+\n+\terr = nla_parse_nested(tb, NFTA_FLOWTABLE_HOOK_MAX, attr,\n+\t\t\t       nft_flowtable_hook_policy, NULL);\n+\tif (err < 0)\n+\t\treturn err;\n+\n+\tif (!tb[NFTA_FLOWTABLE_HOOK_NUM] ||\n+\t    !tb[NFTA_FLOWTABLE_HOOK_PRIORITY] ||\n+\t    !tb[NFTA_FLOWTABLE_HOOK_DEVS])\n+\t\treturn -EINVAL;\n+\n+\thooknum = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_NUM]));\n+\tif (hooknum >= ctx->afi->nhooks)\n+\t\treturn -EINVAL;\n+\n+\tpriority = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY]));\n+\n+\terr = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS],\n+\t\t\t\t      dev_array, &n);\n+\tif (err < 0)\n+\t\tgoto err1;\n+\n+\tops = kmalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL);\n+\tif (!ops) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err1;\n+\t}\n+\n+\tflowtable->ops\t\t= ops;\n+\tflowtable->ops_len\t= n;\n+\n+\tfor (i = 0; i < n; i++) {\n+\t\tflowtable->ops[i].pf\t\t= NFPROTO_NETDEV;\n+\t\tflowtable->ops[i].hooknum\t= hooknum;\n+\t\tflowtable->ops[i].priority\t= priority;\n+\t\tflowtable->ops[i].priv\t\t= &flowtable->data.rhashtable;\n+\t\tflowtable->ops[i].hook\t\t= flowtable->data.type->hook;\n+\t\tflowtable->ops[i].dev\t\t= dev_array[i];\n+\t}\n+\n+\terr = 0;\n+err1:\n+\tfor (i = 0; i < n; i++)\n+\t\tdev_put(dev_array[i]);\n+\n+\treturn err;\n+}\n+\n+static const struct nf_flowtable_type *\n+__nft_flowtable_type_get(const struct nft_af_info *afi)\n+{\n+\tconst struct nf_flowtable_type *type;\n+\n+\tlist_for_each_entry(type, &nf_tables_flowtables, list) {\n+\t\tif (afi->family == type->family)\n+\t\t\treturn type;\n+\t}\n+\treturn NULL;\n+}\n+\n+static const struct nf_flowtable_type *\n+nft_flowtable_type_get(const struct nft_af_info *afi)\n+{\n+\tconst struct nf_flowtable_type *type;\n+\n+\ttype = __nft_flowtable_type_get(afi);\n+\tif (type != NULL && try_module_get(type->owner))\n+\t\treturn type;\n+\n+#ifdef CONFIG_MODULES\n+\tif (type == NULL) {\n+\t\tnfnl_unlock(NFNL_SUBSYS_NFTABLES);\n+\t\trequest_module(\"nf-flowtable-%u\", afi->family);\n+\t\tnfnl_lock(NFNL_SUBSYS_NFTABLES);\n+\t\tif (__nft_flowtable_type_get(afi))\n+\t\t\treturn ERR_PTR(-EAGAIN);\n+\t}\n+#endif\n+\treturn ERR_PTR(-ENOENT);\n+}\n+\n+void nft_flow_table_iterate(struct net *net,\n+\t\t\t    void (*iter)(struct nf_flowtable *flowtable, void *data),\n+\t\t\t    void *data)\n+{\n+\tstruct nft_flowtable *flowtable;\n+\tconst struct nft_af_info *afi;\n+\tconst struct nft_table *table;\n+\n+\trcu_read_lock();\n+\tlist_for_each_entry_rcu(afi, &net->nft.af_info, list) {\n+\t\tlist_for_each_entry_rcu(table, &afi->tables, list) {\n+\t\t\tlist_for_each_entry_rcu(flowtable, &table->flowtables, list) {\n+\t\t\t\titer(&flowtable->data, data);\n+\t\t\t}\n+\t\t}\n+\t}\n+\trcu_read_unlock();\n+}\n+EXPORT_SYMBOL_GPL(nft_flow_table_iterate);\n+\n+static void nft_unregister_flowtable_net_hooks(struct net *net,\n+\t\t\t\t\t       struct nft_flowtable *flowtable)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < flowtable->ops_len; i++) {\n+\t\tif (!flowtable->ops[i].dev)\n+\t\t\tcontinue;\n+\n+\t\tnf_unregister_net_hook(net, &flowtable->ops[i]);\n+\t}\n+}\n+\n+static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,\n+\t\t\t\t  struct sk_buff *skb,\n+\t\t\t\t  const struct nlmsghdr *nlh,\n+\t\t\t\t  const struct nlattr * const nla[],\n+\t\t\t\t  struct netlink_ext_ack *extack)\n+{\n+\tconst struct nfgenmsg *nfmsg = nlmsg_data(nlh);\n+\tconst struct nf_flowtable_type *type;\n+\tu8 genmask = nft_genmask_next(net);\n+\tint family = nfmsg->nfgen_family;\n+\tstruct nft_flowtable *flowtable;\n+\tstruct nft_af_info *afi;\n+\tstruct nft_table *table;\n+\tstruct nft_ctx ctx;\n+\tint err, i, k;\n+\n+\tif (!nla[NFTA_FLOWTABLE_TABLE] ||\n+\t    !nla[NFTA_FLOWTABLE_NAME] ||\n+\t    !nla[NFTA_FLOWTABLE_HOOK])\n+\t\treturn -EINVAL;\n+\n+\tafi = nf_tables_afinfo_lookup(net, family, true);\n+\tif (IS_ERR(afi))\n+\t\treturn PTR_ERR(afi);\n+\n+\ttable = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);\n+\tif (IS_ERR(table))\n+\t\treturn PTR_ERR(table);\n+\n+\tflowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],\n+\t\t\t\t\t       genmask);\n+\tif (IS_ERR(flowtable)) {\n+\t\terr = PTR_ERR(flowtable);\n+\t\tif (err != -ENOENT)\n+\t\t\treturn err;\n+\t} else {\n+\t\tif (nlh->nlmsg_flags & NLM_F_EXCL)\n+\t\t\treturn -EEXIST;\n+\n+\t\treturn 0;\n+\t}\n+\n+\tnft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);\n+\n+\tflowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL);\n+\tif (!flowtable)\n+\t\treturn -ENOMEM;\n+\n+\tflowtable->table = table;\n+\tflowtable->name = nla_strdup(nla[NFTA_FLOWTABLE_NAME], GFP_KERNEL);\n+\tif (!flowtable->name) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err1;\n+\t}\n+\n+\ttype = nft_flowtable_type_get(afi);\n+\tif (IS_ERR(type)) {\n+\t\terr = PTR_ERR(type);\n+\t\tgoto err2;\n+\t}\n+\n+\tflowtable->data.type = type;\n+\terr = rhashtable_init(&flowtable->data.rhashtable, type->params);\n+\tif (err < 0)\n+\t\tgoto err3;\n+\n+\terr = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],\n+\t\t\t\t\t     flowtable);\n+\tif (err < 0)\n+\t\tgoto err3;\n+\n+\tfor (i = 0; i < flowtable->ops_len; i++) {\n+\t\terr = nf_register_net_hook(net, &flowtable->ops[i]);\n+\t\tif (err < 0)\n+\t\t\tgoto err4;\n+\t}\n+\n+\terr = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable);\n+\tif (err < 0)\n+\t\tgoto err5;\n+\n+\tINIT_DEFERRABLE_WORK(&flowtable->data.gc_work, type->gc);\n+\tqueue_delayed_work(system_power_efficient_wq,\n+\t\t\t   &flowtable->data.gc_work, HZ);\n+\n+\tlist_add_tail_rcu(&flowtable->list, &table->flowtables);\n+\ttable->use++;\n+\n+\treturn 0;\n+err5:\n+\ti = flowtable->ops_len - 1;\n+err4:\n+\tfor (k = i; k >= 0; k--)\n+\t\tnf_unregister_net_hook(net, &flowtable->ops[i]);\n+\n+\tkfree(flowtable->ops);\n+err3:\n+\tmodule_put(type->owner);\n+err2:\n+\tkfree(flowtable->name);\n+err1:\n+\tkfree(flowtable);\n+\treturn err;\n+}\n+\n+static int nf_tables_delflowtable(struct net *net, struct sock *nlsk,\n+\t\t\t\t  struct sk_buff *skb,\n+\t\t\t\t  const struct nlmsghdr *nlh,\n+\t\t\t\t  const struct nlattr * const nla[],\n+\t\t\t\t  struct netlink_ext_ack *extack)\n+{\n+\tconst struct nfgenmsg *nfmsg = nlmsg_data(nlh);\n+\tu8 genmask = nft_genmask_next(net);\n+\tint family = nfmsg->nfgen_family;\n+\tstruct nft_flowtable *flowtable;\n+\tstruct nft_af_info *afi;\n+\tstruct nft_table *table;\n+\tstruct nft_ctx ctx;\n+\n+\tafi = nf_tables_afinfo_lookup(net, family, true);\n+\tif (IS_ERR(afi))\n+\t\treturn PTR_ERR(afi);\n+\n+\ttable = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);\n+\tif (IS_ERR(table))\n+\t\treturn PTR_ERR(table);\n+\n+\tflowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],\n+\t\t\t\t\t       genmask);\n+\tif (IS_ERR(flowtable))\n+                return PTR_ERR(flowtable);\n+\tif (flowtable->use > 0)\n+\t\treturn -EBUSY;\n+\n+\tnft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);\n+\n+\treturn nft_delflowtable(&ctx, flowtable);\n+}\n+\n+static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,\n+\t\t\t\t\t u32 portid, u32 seq, int event,\n+\t\t\t\t\t u32 flags, int family,\n+\t\t\t\t\t struct nft_flowtable *flowtable)\n+{\n+\tstruct nlattr *nest, *nest_devs;\n+\tstruct nfgenmsg *nfmsg;\n+\tstruct nlmsghdr *nlh;\n+\tint i;\n+\n+\tevent = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);\n+\tnlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);\n+\tif (nlh == NULL)\n+\t\tgoto nla_put_failure;\n+\n+\tnfmsg = nlmsg_data(nlh);\n+\tnfmsg->nfgen_family\t= family;\n+\tnfmsg->version\t\t= NFNETLINK_V0;\n+\tnfmsg->res_id\t\t= htons(net->nft.base_seq & 0xffff);\n+\n+\tif (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) ||\n+\t    nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||\n+\t    nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)))\n+\t\tgoto nla_put_failure;\n+\n+\tnest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK);\n+\tif (nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_NUM, htonl(flowtable->hooknum)) ||\n+\t    nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(flowtable->priority)))\n+\t\tgoto nla_put_failure;\n+\n+\tnest_devs = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK_DEVS);\n+\tif (!nest_devs)\n+\t\tgoto nla_put_failure;\n+\n+\tfor (i = 0; i < flowtable->ops_len; i++) {\n+\t\tif (flowtable->ops[i].dev &&\n+\t\t    nla_put_string(skb, NFTA_DEVICE_NAME,\n+\t\t\t\t   flowtable->ops[i].dev->name))\n+\t\t\tgoto nla_put_failure;\n+\t}\n+\tnla_nest_end(skb, nest_devs);\n+\tnla_nest_end(skb, nest);\n+\n+\tnlmsg_end(skb, nlh);\n+\treturn 0;\n+\n+nla_put_failure:\n+\tnlmsg_trim(skb, nlh);\n+\treturn -1;\n+}\n+\n+struct nft_flowtable_filter {\n+\tchar\t\t*table;\n+};\n+\n+static int nf_tables_dump_flowtable(struct sk_buff *skb,\n+\t\t\t\t    struct netlink_callback *cb)\n+{\n+\tconst struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);\n+\tstruct nft_flowtable_filter *filter = cb->data;\n+\tunsigned int idx = 0, s_idx = cb->args[0];\n+\tstruct net *net = sock_net(skb->sk);\n+\tint family = nfmsg->nfgen_family;\n+\tstruct nft_flowtable *flowtable;\n+\tconst struct nft_af_info *afi;\n+\tconst struct nft_table *table;\n+\n+\trcu_read_lock();\n+\tcb->seq = net->nft.base_seq;\n+\n+\tlist_for_each_entry_rcu(afi, &net->nft.af_info, list) {\n+\t\tif (family != NFPROTO_UNSPEC && family != afi->family)\n+\t\t\tcontinue;\n+\n+\t\tlist_for_each_entry_rcu(table, &afi->tables, list) {\n+\t\t\tlist_for_each_entry_rcu(flowtable, &table->flowtables, list) {\n+\t\t\t\tif (!nft_is_active(net, flowtable))\n+\t\t\t\t\tgoto cont;\n+\t\t\t\tif (idx < s_idx)\n+\t\t\t\t\tgoto cont;\n+\t\t\t\tif (idx > s_idx)\n+\t\t\t\t\tmemset(&cb->args[1], 0,\n+\t\t\t\t\t       sizeof(cb->args) - sizeof(cb->args[0]));\n+\t\t\t\tif (filter && filter->table[0] &&\n+\t\t\t\t    strcmp(filter->table, table->name))\n+\t\t\t\t\tgoto cont;\n+\n+\t\t\t\tif (nf_tables_fill_flowtable_info(skb, net, NETLINK_CB(cb->skb).portid,\n+\t\t\t\t\t\t\t\t  cb->nlh->nlmsg_seq,\n+\t\t\t\t\t\t\t\t  NFT_MSG_NEWFLOWTABLE,\n+\t\t\t\t\t\t\t\t  NLM_F_MULTI | NLM_F_APPEND,\n+\t\t\t\t\t\t\t\t  afi->family, flowtable) < 0)\n+\t\t\t\t\tgoto done;\n+\n+\t\t\t\tnl_dump_check_consistent(cb, nlmsg_hdr(skb));\n+cont:\n+\t\t\t\tidx++;\n+\t\t\t}\n+\t\t}\n+\t}\n+done:\n+\trcu_read_unlock();\n+\n+\tcb->args[0] = idx;\n+\treturn skb->len;\n+}\n+\n+static int nf_tables_dump_flowtable_done(struct netlink_callback *cb)\n+{\n+\tstruct nft_flowtable_filter *filter = cb->data;\n+\n+\tif (!filter)\n+\t\treturn 0;\n+\n+\tkfree(filter->table);\n+\tkfree(filter);\n+\n+\treturn 0;\n+}\n+\n+static struct nft_flowtable_filter *\n+nft_flowtable_filter_alloc(const struct nlattr * const nla[])\n+{\n+\tstruct nft_flowtable_filter *filter;\n+\n+\tfilter = kzalloc(sizeof(*filter), GFP_KERNEL);\n+\tif (!filter)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tif (nla[NFTA_FLOWTABLE_TABLE]) {\n+\t\tfilter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE],\n+\t\t\t\t\t   GFP_KERNEL);\n+\t\tif (!filter->table) {\n+\t\t\tkfree(filter);\n+\t\t\treturn ERR_PTR(-ENOMEM);\n+\t\t}\n+\t}\n+\treturn filter;\n+}\n+\n+static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,\n+\t\t\t\t  struct sk_buff *skb,\n+\t\t\t\t  const struct nlmsghdr *nlh,\n+\t\t\t\t  const struct nlattr * const nla[],\n+\t\t\t\t  struct netlink_ext_ack *extack)\n+{\n+\tconst struct nfgenmsg *nfmsg = nlmsg_data(nlh);\n+\tu8 genmask = nft_genmask_cur(net);\n+\tint family = nfmsg->nfgen_family;\n+\tstruct nft_flowtable *flowtable;\n+\tconst struct nft_af_info *afi;\n+\tconst struct nft_table *table;\n+\tstruct sk_buff *skb2;\n+\tint err;\n+\n+\tif (nlh->nlmsg_flags & NLM_F_DUMP) {\n+\t\tstruct netlink_dump_control c = {\n+\t\t\t.dump = nf_tables_dump_flowtable,\n+\t\t\t.done = nf_tables_dump_flowtable_done,\n+\t\t};\n+\n+\t\tif (nla[NFTA_FLOWTABLE_TABLE]) {\n+\t\t\tstruct nft_flowtable_filter *filter;\n+\n+\t\t\tfilter = nft_flowtable_filter_alloc(nla);\n+\t\t\tif (IS_ERR(filter))\n+\t\t\t\treturn -ENOMEM;\n+\n+\t\t\tc.data = filter;\n+\t\t}\n+\t\treturn netlink_dump_start(nlsk, skb, nlh, &c);\n+\t}\n+\n+\tif (!nla[NFTA_FLOWTABLE_NAME])\n+\t\treturn -EINVAL;\n+\n+\tafi = nf_tables_afinfo_lookup(net, family, false);\n+\tif (IS_ERR(afi))\n+\t\treturn PTR_ERR(afi);\n+\n+\ttable = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);\n+\tif (IS_ERR(table))\n+\t\treturn PTR_ERR(table);\n+\n+\tflowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],\n+\t\t\t\t\t       genmask);\n+\tif (IS_ERR(table))\n+\t\treturn PTR_ERR(flowtable);\n+\n+\tskb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);\n+\tif (!skb2)\n+\t\treturn -ENOMEM;\n+\n+\terr = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,\n+\t\t\t\t\t    nlh->nlmsg_seq,\n+\t\t\t\t\t    NFT_MSG_NEWFLOWTABLE, 0, family,\n+\t\t\t\t\t    flowtable);\n+\tif (err < 0)\n+\t\tgoto err;\n+\n+\treturn nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);\n+err:\n+\tkfree_skb(skb2);\n+\treturn err;\n+}\n+\n+static void nf_tables_flowtable_notify(struct nft_ctx *ctx,\n+\t\t\t\t       struct nft_flowtable *flowtable,\n+\t\t\t\t       int event)\n+{\n+\tstruct sk_buff *skb;\n+\tint err;\n+\n+\tif (ctx->report &&\n+\t    !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))\n+\t\treturn;\n+\n+\tskb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);\n+\tif (skb == NULL)\n+\t\tgoto err;\n+\n+\terr = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid,\n+\t\t\t\t\t    ctx->seq, event, 0,\n+\t\t\t\t\t    ctx->afi->family, flowtable);\n+\tif (err < 0) {\n+\t\tkfree_skb(skb);\n+\t\tgoto err;\n+\t}\n+\n+\tnfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,\n+\t\t       ctx->report, GFP_KERNEL);\n+\treturn;\n+err:\n+\tnfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);\n+}\n+\n+static void nft_flowtable_destroy(void *ptr, void *arg)\n+{\n+\tkfree(ptr);\n+}\n+\n+static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)\n+{\n+\tcancel_delayed_work_sync(&flowtable->data.gc_work);\n+\tkfree(flowtable->name);\n+\trhashtable_free_and_destroy(&flowtable->data.rhashtable,\n+\t\t\t\t    nft_flowtable_destroy, NULL);\n+\tmodule_put(flowtable->data.type->owner);\n+}\n+\n static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,\n \t\t\t\t   u32 portid, u32 seq)\n {\n@@ -4809,6 +5452,49 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,\n \treturn -EMSGSIZE;\n }\n \n+static void nft_flowtable_event(unsigned long event, struct net_device *dev,\n+\t\t\t\tstruct nft_flowtable *flowtable)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < flowtable->ops_len; i++) {\n+\t\tif (flowtable->ops[i].dev != dev)\n+\t\t\tcontinue;\n+\n+\t\tnf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]);\n+\t\tflowtable->ops[i].dev = NULL;\n+\t\tbreak;\n+\t}\n+}\n+\n+static int nf_tables_flowtable_event(struct notifier_block *this,\n+\t\t\t\t     unsigned long event, void *ptr)\n+{\n+\tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n+\tstruct nft_flowtable *flowtable;\n+\tstruct nft_table *table;\n+\tstruct nft_af_info *afi;\n+\n+\tif (event != NETDEV_UNREGISTER)\n+\t\treturn 0;\n+\n+\tnfnl_lock(NFNL_SUBSYS_NFTABLES);\n+\tlist_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {\n+\t\tlist_for_each_entry(table, &afi->tables, list) {\n+\t\t\tlist_for_each_entry(flowtable, &table->flowtables, list) {\n+\t\t\t\tnft_flowtable_event(event, dev, flowtable);\n+\t\t\t}\n+\t\t}\n+\t}\n+\tnfnl_unlock(NFNL_SUBSYS_NFTABLES);\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block nf_tables_flowtable_notifier = {\n+\t.notifier_call\t= nf_tables_flowtable_event,\n+};\n+\n static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb,\n \t\t\t\t int event)\n {\n@@ -4961,6 +5647,21 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {\n \t\t.attr_count\t= NFTA_OBJ_MAX,\n \t\t.policy\t\t= nft_obj_policy,\n \t},\n+\t[NFT_MSG_NEWFLOWTABLE] = {\n+\t\t.call_batch\t= nf_tables_newflowtable,\n+\t\t.attr_count\t= NFTA_FLOWTABLE_MAX,\n+\t\t.policy\t\t= nft_flowtable_policy,\n+\t},\n+\t[NFT_MSG_GETFLOWTABLE] = {\n+\t\t.call\t\t= nf_tables_getflowtable,\n+\t\t.attr_count\t= NFTA_FLOWTABLE_MAX,\n+\t\t.policy\t\t= nft_flowtable_policy,\n+\t},\n+\t[NFT_MSG_DELFLOWTABLE] = {\n+\t\t.call_batch\t= nf_tables_delflowtable,\n+\t\t.attr_count\t= NFTA_FLOWTABLE_MAX,\n+\t\t.policy\t\t= nft_flowtable_policy,\n+\t},\n };\n \n static void nft_chain_commit_update(struct nft_trans *trans)\n@@ -5006,6 +5707,9 @@ static void nf_tables_commit_release(struct nft_trans *trans)\n \tcase NFT_MSG_DELOBJ:\n \t\tnft_obj_destroy(nft_trans_obj(trans));\n \t\tbreak;\n+\tcase NFT_MSG_DELFLOWTABLE:\n+\t\tnf_tables_flowtable_destroy(nft_trans_flowtable(trans));\n+\t\tbreak;\n \t}\n \tkfree(trans);\n }\n@@ -5124,6 +5828,21 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)\n \t\t\tnf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans),\n \t\t\t\t\t     NFT_MSG_DELOBJ);\n \t\t\tbreak;\n+\t\tcase NFT_MSG_NEWFLOWTABLE:\n+\t\t\tnft_clear(net, nft_trans_flowtable(trans));\n+\t\t\tnf_tables_flowtable_notify(&trans->ctx,\n+\t\t\t\t\t\t   nft_trans_flowtable(trans),\n+\t\t\t\t\t\t   NFT_MSG_NEWFLOWTABLE);\n+\t\t\tnft_trans_destroy(trans);\n+\t\t\tbreak;\n+\t\tcase NFT_MSG_DELFLOWTABLE:\n+\t\t\tlist_del_rcu(&nft_trans_flowtable(trans)->list);\n+\t\t\tnf_tables_flowtable_notify(&trans->ctx,\n+\t\t\t\t\t\t   nft_trans_flowtable(trans),\n+\t\t\t\t\t\t   NFT_MSG_DELFLOWTABLE);\n+\t\t\tnft_unregister_flowtable_net_hooks(net,\n+\t\t\t\t\tnft_trans_flowtable(trans));\n+\t\t\tbreak;\n \t\t}\n \t}\n \n@@ -5161,6 +5880,9 @@ static void nf_tables_abort_release(struct nft_trans *trans)\n \tcase NFT_MSG_NEWOBJ:\n \t\tnft_obj_destroy(nft_trans_obj(trans));\n \t\tbreak;\n+\tcase NFT_MSG_NEWFLOWTABLE:\n+\t\tnf_tables_flowtable_destroy(nft_trans_flowtable(trans));\n+\t\tbreak;\n \t}\n \tkfree(trans);\n }\n@@ -5251,6 +5973,17 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)\n \t\t\tnft_clear(trans->ctx.net, nft_trans_obj(trans));\n \t\t\tnft_trans_destroy(trans);\n \t\t\tbreak;\n+\t\tcase NFT_MSG_NEWFLOWTABLE:\n+\t\t\ttrans->ctx.table->use--;\n+\t\t\tlist_del_rcu(&nft_trans_flowtable(trans)->list);\n+\t\t\tnft_unregister_flowtable_net_hooks(net,\n+\t\t\t\t\tnft_trans_flowtable(trans));\n+\t\t\tbreak;\n+\t\tcase NFT_MSG_DELFLOWTABLE:\n+\t\t\ttrans->ctx.table->use++;\n+\t\t\tnft_clear(trans->ctx.net, nft_trans_flowtable(trans));\n+\t\t\tnft_trans_destroy(trans);\n+\t\t\tbreak;\n \t\t}\n \t}\n \n@@ -5802,6 +6535,7 @@ EXPORT_SYMBOL_GPL(__nft_release_basechain);\n /* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */\n static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)\n {\n+\tstruct nft_flowtable *flowtable, *nf;\n \tstruct nft_table *table, *nt;\n \tstruct nft_chain *chain, *nc;\n \tstruct nft_object *obj, *ne;\n@@ -5816,6 +6550,9 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)\n \t\tlist_for_each_entry(chain, &table->chains, list)\n \t\t\tnf_tables_unregister_hooks(net, table, chain,\n \t\t\t\t\t\t   afi->nops);\n+\t\tlist_for_each_entry(flowtable, &table->flowtables, list)\n+\t\t\tnf_unregister_net_hooks(net, flowtable->ops,\n+\t\t\t\t\t\tflowtable->ops_len);\n \t\t/* No packets are walking on these chains anymore. */\n \t\tctx.table = table;\n \t\tlist_for_each_entry(chain, &table->chains, list) {\n@@ -5826,6 +6563,11 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)\n \t\t\t\tnf_tables_rule_destroy(&ctx, rule);\n \t\t\t}\n \t\t}\n+\t\tlist_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {\n+\t\t\tlist_del(&flowtable->list);\n+\t\t\ttable->use--;\n+\t\t\tnf_tables_flowtable_destroy(flowtable);\n+\t\t}\n \t\tlist_for_each_entry_safe(set, ns, &table->sets, list) {\n \t\t\tlist_del(&set->list);\n \t\t\ttable->use--;\n@@ -5869,6 +6611,8 @@ static int __init nf_tables_module_init(void)\n \tif (err < 0)\n \t\tgoto err3;\n \n+\tregister_netdevice_notifier(&nf_tables_flowtable_notifier);\n+\n \tpr_info(\"nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\\n\");\n \treturn register_pernet_subsys(&nf_tables_net_ops);\n err3:\n@@ -5883,6 +6627,7 @@ static void __exit nf_tables_module_exit(void)\n {\n \tunregister_pernet_subsys(&nf_tables_net_ops);\n \tnfnetlink_subsys_unregister(&nf_tables_subsys);\n+\tunregister_netdevice_notifier(&nf_tables_flowtable_notifier);\n \trcu_barrier();\n \tnf_tables_core_module_exit();\n \tkfree(info);\n",
    "prefixes": [
        "nf-next",
        "v3",
        "1/7"
    ]
}