Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/852534/?format=api
{ "id": 852534, "url": "http://patchwork.ozlabs.org/api/patches/852534/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/20171222192732.13188-7-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-7-pablo@netfilter.org>", "list_archive_url": null, "date": "2017-12-22T19:27:31", "name": "[nf-next,v3,6/7] netfilter: nf_tables: flow offload expression", "commit_ref": null, "pull_url": null, "state": "rfc", "archived": true, "hash": "66eac256e1a69a7a3c377fceb5c8cfac8eee30e8", "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-7-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/852534/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/852534/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 3z3JTF1Yrkz9sBd\n\tfor <patchwork-incoming@ozlabs.org>;\n\tSat, 23 Dec 2017 06:28:33 +1100 (AEDT)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1756852AbdLVT2b (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tFri, 22 Dec 2017 14:28:31 -0500", "from mail.us.es ([193.147.175.20]:42380 \"EHLO mail.us.es\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1756787AbdLVT2K (ORCPT <rfc822;netdev@vger.kernel.org>);\n\tFri, 22 Dec 2017 14:28:10 -0500", "from antivirus1-rhel7.int (unknown [192.168.2.11])\n\tby mail.us.es (Postfix) with ESMTP id F312CEBAD8\n\tfor <netdev@vger.kernel.org>; Fri, 22 Dec 2017 20:28:08 +0100 (CET)", "from antivirus1-rhel7.int (localhost [127.0.0.1])\n\tby antivirus1-rhel7.int (Postfix) with ESMTP id DF8D4F7312\n\tfor <netdev@vger.kernel.org>; Fri, 22 Dec 2017 20:28:08 +0100 (CET)", "by antivirus1-rhel7.int (Postfix, from userid 99)\n\tid D6AECF731E; Fri, 22 Dec 2017 20:28:08 +0100 (CET)", "from antivirus1-rhel7.int (localhost [127.0.0.1])\n\tby antivirus1-rhel7.int (Postfix) with ESMTP id 70058F7306;\n\tFri, 22 Dec 2017 20:28:06 +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:28:06 +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 91D054265A31;\n\tFri, 22 Dec 2017 20:28:05 +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 6/7] netfilter: nf_tables: flow offload expression", "Date": "Fri, 22 Dec 2017 20:27:31 +0100", "Message-Id": "<20171222192732.13188-7-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": "Add new instruction for the nf_tables VM that allows us to specify what\nflows are offloaded into a given flow table via name. This new\ninstruction creates the flow entry and adds it to the flow table.\n\nOnly established flows, ie. we have seen traffic in both directions, are\nadded to the flow table. You can still decide to offload entries at a\nlater stage via packet counting or checking the ct status in case you\nwant to offload assured conntracks.\n\nThis new extension depends on the conntrack subsystem.\n\nSigned-off-by: Pablo Neira Ayuso <pablo@netfilter.org>\n---\n include/uapi/linux/netfilter/nf_tables.h | 11 ++\n net/netfilter/Kconfig | 7 +\n net/netfilter/Makefile | 1 +\n net/netfilter/nft_flow_offload.c | 268 +++++++++++++++++++++++++++++++\n 4 files changed, 287 insertions(+)\n create mode 100644 net/netfilter/nft_flow_offload.c", "diff": "diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h\nindex 9ba0f4c13de6..528d832fefb4 100644\n--- a/include/uapi/linux/netfilter/nf_tables.h\n+++ b/include/uapi/linux/netfilter/nf_tables.h\n@@ -954,6 +954,17 @@ enum nft_ct_attributes {\n };\n #define NFTA_CT_MAX\t\t(__NFTA_CT_MAX - 1)\n \n+/**\n+ * enum nft_flow_attributes - ct offload expression attributes\n+ * @NFTA_FLOW_TABLE_NAME: flow table name (NLA_STRING)\n+ */\n+enum nft_offload_attributes {\n+\tNFTA_FLOW_UNSPEC,\n+\tNFTA_FLOW_TABLE_NAME,\n+\t__NFTA_FLOW_MAX,\n+};\n+#define NFTA_FLOW_MAX\t\t(__NFTA_FLOW_MAX - 1)\n+\n enum nft_limit_type {\n \tNFT_LIMIT_PKTS,\n \tNFT_LIMIT_PKT_BYTES\ndiff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig\nindex 0c6256db5a6c..1ada46345f3c 100644\n--- a/net/netfilter/Kconfig\n+++ b/net/netfilter/Kconfig\n@@ -497,6 +497,13 @@ config NFT_CT\n \t This option adds the \"ct\" expression that you can use to match\n \t connection tracking information such as the flow state.\n \n+config NFT_FLOW_OFFLOAD\n+\tdepends on NF_CONNTRACK\n+\ttristate \"Netfilter nf_tables hardware flow offload module\"\n+\thelp\n+\t This option adds the \"flow_offload\" expression that you can use to\n+\t choose what flows are placed into the hardware.\n+\n config NFT_SET_RBTREE\n \ttristate \"Netfilter nf_tables rbtree set module\"\n \thelp\ndiff --git a/net/netfilter/Makefile b/net/netfilter/Makefile\nindex 1f7d92bd571a..2c1b8de922f2 100644\n--- a/net/netfilter/Makefile\n+++ b/net/netfilter/Makefile\n@@ -83,6 +83,7 @@ obj-$(CONFIG_NFT_META)\t\t+= nft_meta.o\n obj-$(CONFIG_NFT_RT)\t\t+= nft_rt.o\n obj-$(CONFIG_NFT_NUMGEN)\t+= nft_numgen.o\n obj-$(CONFIG_NFT_CT)\t\t+= nft_ct.o\n+obj-$(CONFIG_NFT_FLOW_OFFLOAD)\t+= nft_flow_offload.o\n obj-$(CONFIG_NFT_LIMIT)\t\t+= nft_limit.o\n obj-$(CONFIG_NFT_NAT)\t\t+= nft_nat.o\n obj-$(CONFIG_NFT_OBJREF)\t+= nft_objref.o\ndiff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c\nnew file mode 100644\nindex 000000000000..4f16c37acaa3\n--- /dev/null\n+++ b/net/netfilter/nft_flow_offload.c\n@@ -0,0 +1,268 @@\n+#include <linux/kernel.h>\n+#include <linux/module.h>\n+#include <linux/init.h>\n+#include <linux/netlink.h>\n+#include <linux/netfilter.h>\n+#include <linux/workqueue.h>\n+#include <linux/spinlock.h>\n+#include <linux/netfilter/nf_tables.h>\n+#include <net/ip.h> /* for ipv4 options. */\n+#include <net/netfilter/nf_tables.h>\n+#include <net/netfilter/nf_tables_core.h>\n+#include <net/netfilter/nf_conntrack_core.h>\n+#include <linux/netfilter/nf_conntrack_common.h>\n+#include <net/netfilter/nf_flow_table.h>\n+\n+struct nft_flow_offload {\n+\tstruct nft_flowtable\t*flowtable;\n+};\n+\n+static int nft_flow_route(const struct nft_pktinfo *pkt,\n+\t\t\t const struct nf_conn *ct,\n+\t\t\t struct nf_flow_route *route,\n+\t\t\t enum ip_conntrack_dir dir)\n+{\n+\tstruct dst_entry *this_dst = skb_dst(pkt->skb);\n+\tstruct dst_entry *other_dst;\n+\tconst struct nf_afinfo *ai;\n+\tstruct flowi fl;\n+\n+\tmemset(&fl, 0, sizeof(fl));\n+\tswitch (nft_pf(pkt)) {\n+\tcase NFPROTO_IPV4:\n+\t\tfl.u.ip4.daddr = ct->tuplehash[!dir].tuple.dst.u3.ip;\n+\t\tbreak;\n+\tcase NFPROTO_IPV6:\n+\t\tfl.u.ip6.daddr = ct->tuplehash[!dir].tuple.dst.u3.in6;\n+\t\tbreak;\n+\t}\n+\n+\tai = nf_get_afinfo(nft_pf(pkt));\n+\tif (ai) {\n+\t\tai->route(nft_net(pkt), &other_dst, &fl, false);\n+\t\tif (!other_dst)\n+\t\t\treturn -ENOENT;\n+\t}\n+\n+\troute->tuple[dir].dst\t\t= this_dst;\n+\troute->tuple[dir].ifindex\t= nft_in(pkt)->ifindex;\n+\troute->tuple[!dir].dst\t\t= other_dst;\n+\troute->tuple[!dir].ifindex\t= nft_out(pkt)->ifindex;\n+\n+\treturn 0;\n+}\n+\n+static bool nft_flow_offload_skip(struct sk_buff *skb)\n+{\n+\tstruct ip_options *opt = &(IPCB(skb)->opt);\n+\n+\tif (unlikely(opt->optlen))\n+\t\treturn true;\n+\tif (skb_sec_path(skb))\n+\t\treturn true;\n+\n+\treturn false;\n+}\n+\n+static void nft_flow_offload_eval(const struct nft_expr *expr,\n+\t\t\t\t struct nft_regs *regs,\n+\t\t\t\t const struct nft_pktinfo *pkt)\n+{\n+\tstruct nft_flow_offload *priv = nft_expr_priv(expr);\n+\tstruct nf_flowtable *flowtable = &priv->flowtable->data;\n+\tenum ip_conntrack_info ctinfo;\n+\tstruct nf_flow_route route;\n+\tstruct flow_offload *flow;\n+\tenum ip_conntrack_dir dir;\n+\tstruct nf_conn *ct;\n+\tint ret;\n+\n+\tif (nft_flow_offload_skip(pkt->skb))\n+\t\tgoto out;\n+\n+\tct = nf_ct_get(pkt->skb, &ctinfo);\n+\tif (!ct)\n+\t\tgoto out;\n+\n+\tswitch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {\n+\tcase IPPROTO_TCP:\n+\tcase IPPROTO_UDP:\n+\t\tbreak;\n+\tdefault:\n+\t\tgoto out;\n+\t}\n+\n+\tif (test_bit(IPS_HELPER_BIT, &ct->status))\n+\t\tgoto out;\n+\n+\tif (ctinfo == IP_CT_NEW ||\n+\t ctinfo == IP_CT_RELATED)\n+\t\tgoto out;\n+\n+\tif (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))\n+\t\tgoto out;\n+\n+\tdir = CTINFO2DIR(ctinfo);\n+\tif (nft_flow_route(pkt, ct, &route, dir) < 0)\n+\t\tgoto err_flow_route;\n+\n+\tflow = flow_offload_alloc(ct, &route);\n+\tif (!flow)\n+\t\tgoto err_flow_alloc;\n+\n+\tret = flow_offload_add(flowtable, flow);\n+\tif (ret < 0)\n+\t\tgoto err_flow_add;\n+\n+\treturn;\n+\n+err_flow_add:\n+\tflow_offload_free(flow);\n+err_flow_alloc:\n+\tdst_release(route.tuple[!dir].dst);\n+err_flow_route:\n+\tclear_bit(IPS_OFFLOAD_BIT, &ct->status);\n+out:\n+\tregs->verdict.code = NFT_BREAK;\n+}\n+\n+static int nft_flow_offload_validate(const struct nft_ctx *ctx,\n+\t\t\t\t const struct nft_expr *expr,\n+\t\t\t\t const struct nft_data **data)\n+{\n+\tunsigned int hook_mask = (1 << NF_INET_FORWARD);\n+\n+\treturn nft_chain_validate_hooks(ctx->chain, hook_mask);\n+}\n+\n+static int nft_flow_offload_init(const struct nft_ctx *ctx,\n+\t\t\t\t const struct nft_expr *expr,\n+\t\t\t\t const struct nlattr * const tb[])\n+{\n+\tstruct nft_flow_offload *priv = nft_expr_priv(expr);\n+\tu8 genmask = nft_genmask_next(ctx->net);\n+\tstruct nft_flowtable *flowtable;\n+\n+\tif (!tb[NFTA_FLOW_TABLE_NAME])\n+\t\treturn -EINVAL;\n+\n+\tflowtable = nf_tables_flowtable_lookup(ctx->table,\n+\t\t\t\t\t tb[NFTA_FLOW_TABLE_NAME],\n+\t\t\t\t\t genmask);\n+\tif (IS_ERR(flowtable))\n+\t\treturn PTR_ERR(flowtable);\n+\n+\tpriv->flowtable = flowtable;\n+\tflowtable->use++;\n+\n+\treturn nf_ct_netns_get(ctx->net, ctx->afi->family);\n+}\n+\n+static void nft_flow_offload_destroy(const struct nft_ctx *ctx,\n+\t\t\t\t const struct nft_expr *expr)\n+{\n+\tstruct nft_flow_offload *priv = nft_expr_priv(expr);\n+\n+\tpriv->flowtable->use--;\n+\tnf_ct_netns_put(ctx->net, ctx->afi->family);\n+}\n+\n+static int nft_flow_offload_dump(struct sk_buff *skb, const struct nft_expr *expr)\n+{\n+\tstruct nft_flow_offload *priv = nft_expr_priv(expr);\n+\n+\tif (nla_put_string(skb, NFTA_FLOW_TABLE_NAME, priv->flowtable->name))\n+\t\tgoto nla_put_failure;\n+\n+\treturn 0;\n+\n+nla_put_failure:\n+\treturn -1;\n+}\n+\n+struct nft_expr_type nft_flow_offload_type;\n+static const struct nft_expr_ops nft_flow_offload_ops = {\n+\t.type\t\t= &nft_flow_offload_type,\n+\t.size\t\t= NFT_EXPR_SIZE(sizeof(struct nft_flow_offload)),\n+\t.eval\t\t= nft_flow_offload_eval,\n+\t.init\t\t= nft_flow_offload_init,\n+\t.destroy\t= nft_flow_offload_destroy,\n+\t.validate\t= nft_flow_offload_validate,\n+\t.dump\t\t= nft_flow_offload_dump,\n+};\n+\n+struct nft_expr_type nft_flow_offload_type __read_mostly = {\n+\t.name\t\t= \"flow_offload\",\n+\t.ops\t\t= &nft_flow_offload_ops,\n+\t.maxattr\t= NFTA_FLOW_MAX,\n+\t.owner\t\t= THIS_MODULE,\n+};\n+\n+static void flow_offload_iterate_cleanup(struct flow_offload *flow, void *data)\n+{\n+\tstruct net_device *dev = data;\n+\n+\tif (dev && flow->tuplehash[0].tuple.iifidx != dev->ifindex)\n+\t\treturn;\n+\n+\tflow_offload_dead(flow);\n+}\n+\n+static void nft_flow_offload_iterate_cleanup(struct nf_flowtable *flowtable,\n+\t\t\t\t\t void *data)\n+{\n+\tnf_flow_table_iterate(flowtable, flow_offload_iterate_cleanup, data);\n+}\n+\n+static int flow_offload_netdev_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+\n+\tif (event != NETDEV_DOWN)\n+\t\treturn NOTIFY_DONE;\n+\n+\tnft_flow_table_iterate(dev_net(dev), nft_flow_offload_iterate_cleanup, dev);\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block flow_offload_netdev_notifier = {\n+\t.notifier_call\t= flow_offload_netdev_event,\n+};\n+\n+static int __init nft_flow_offload_module_init(void)\n+{\n+\tint err;\n+\n+\tregister_netdevice_notifier(&flow_offload_netdev_notifier);\n+\n+\terr = nft_register_expr(&nft_flow_offload_type);\n+\tif (err < 0)\n+\t\tgoto register_expr;\n+\n+\treturn 0;\n+\n+register_expr:\n+\tunregister_netdevice_notifier(&flow_offload_netdev_notifier);\n+\treturn err;\n+}\n+\n+static void __exit nft_flow_offload_module_exit(void)\n+{\n+\tstruct net *net;\n+\n+\tnft_unregister_expr(&nft_flow_offload_type);\n+\tunregister_netdevice_notifier(&flow_offload_netdev_notifier);\n+\trtnl_lock();\n+\tfor_each_net(net)\n+\t\tnft_flow_table_iterate(net, nft_flow_offload_iterate_cleanup, NULL);\n+\trtnl_unlock();\n+}\n+\n+module_init(nft_flow_offload_module_init);\n+module_exit(nft_flow_offload_module_exit);\n+\n+MODULE_LICENSE(\"GPL\");\n+MODULE_AUTHOR(\"Pablo Neira Ayuso <pablo@netfilter.org>\");\n+MODULE_ALIAS_NFT_EXPR(\"flow_offload\");\n", "prefixes": [ "nf-next", "v3", "6/7" ] }