[openwrt/openwrt] nftables: update to 0.8.2, backport flowtable support

LEDE Commits lede-commits at lists.infradead.org
Wed Feb 21 11:13:09 PST 2018


nbd pushed a commit to openwrt/openwrt.git, branch master:
https://git.lede-project.org/bc3e0f6052547fbffa9fcf1a6c7e340a95aa9f46

commit bc3e0f6052547fbffa9fcf1a6c7e340a95aa9f46
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Mon Feb 5 14:02:13 2018 +0100

    nftables: update to 0.8.2, backport flowtable support
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 .../200-src-support-for-flowtable-listing.patch    | 515 +++++++++++++++++++++
 .../201-src-add-support-to-add-flowtables.patch    | 515 +++++++++++++++++++++
 .../patches/202-src-delete-flowtable.patch         | 122 +++++
 .../patches/203-src-flow-offload-support.patch     | 191 ++++++++
 .../204-tests-shell-add-flowtable-tests.patch      | 110 +++++
 .../patches/205-doc-nft-document-flowtable.patch   | 128 +++++
 6 files changed, 1581 insertions(+)

diff --git a/package/network/utils/nftables/patches/200-src-support-for-flowtable-listing.patch b/package/network/utils/nftables/patches/200-src-support-for-flowtable-listing.patch
new file mode 100644
index 0000000..259513d
--- /dev/null
+++ b/package/network/utils/nftables/patches/200-src-support-for-flowtable-listing.patch
@@ -0,0 +1,515 @@
+From: Pablo Neira Ayuso <pablo at netfilter.org>
+Date: Mon, 4 Dec 2017 13:28:25 +0100
+Subject: [PATCH] src: support for flowtable listing
+
+This patch allows you to dump existing flowtable.
+
+ # nft list ruleset
+ table ip x {
+        flowtable x {
+                hook ingress priority 10
+                devices = { eth0, tap0 }
+        }
+ }
+
+You can also list existing flowtables via:
+
+ # nft list flowtables
+ table ip x {
+        flowtable x {
+                hook ingress priority 10
+                devices = { eth0, tap0 }
+        }
+ }
+
+ You need a Linux kernel >= 4.16-rc to test this new feature.
+
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+
+--- a/include/linux/netfilter/nf_tables.h
++++ b/include/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,
+ };
+ 
+--- a/include/mnl.h
++++ b/include/mnl.h
+@@ -89,6 +89,9 @@ int mnl_nft_obj_batch_add(struct nftnl_o
+ int mnl_nft_obj_batch_del(struct nftnl_obj *nln, struct nftnl_batch *batch,
+ 			  unsigned int flags, uint32_t seqnum);
+ 
++struct nftnl_flowtable_list *
++mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table);
++
+ struct nftnl_ruleset *mnl_nft_ruleset_dump(struct netlink_ctx *ctx,
+ 					   uint32_t family);
+ int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
+--- a/include/netlink.h
++++ b/include/netlink.h
+@@ -179,6 +179,10 @@ extern int netlink_add_obj(struct netlin
+ extern int netlink_delete_obj(struct netlink_ctx *ctx, const struct handle *h,
+ 			      struct location *loc, uint32_t type);
+ 
++extern int netlink_list_flowtables(struct netlink_ctx *ctx,
++				   const struct handle *h,
++				   const struct location *loc);
++
+ extern void netlink_dump_chain(const struct nftnl_chain *nlc,
+ 			       struct netlink_ctx *ctx);
+ extern void netlink_dump_rule(const struct nftnl_rule *nlr,
+--- a/include/rule.h
++++ b/include/rule.h
+@@ -35,6 +35,7 @@ struct position_spec {
+  * @chain:	chain name (chains and rules only)
+  * @set:	set name (sets only)
+  * @obj:	stateful object name (stateful object only)
++ * @flowtable:	flow table name (flow table only)
+  * @handle:	rule handle (rules only)
+  * @position:	rule position (rules only)
+  * @set_id:	set ID (sets only)
+@@ -45,6 +46,7 @@ struct handle {
+ 	const char		*chain;
+ 	const char		*set;
+ 	const char		*obj;
++	const char		*flowtable;
+ 	struct handle_spec	handle;
+ 	struct position_spec	position;
+ 	uint32_t		set_id;
+@@ -98,6 +100,7 @@ enum table_flags {
+  * @chains:	chains contained in the table
+  * @sets:	sets contained in the table
+  * @objs:	stateful objects contained in the table
++ * @flowtables:	flow tables contained in the table
+  * @flags:	table flags
+  * @refcnt:	table reference counter
+  */
+@@ -109,6 +112,7 @@ struct table {
+ 	struct list_head	chains;
+ 	struct list_head	sets;
+ 	struct list_head	objs;
++	struct list_head	flowtables;
+ 	enum table_flags 	flags;
+ 	unsigned int		refcnt;
+ };
+@@ -315,6 +319,24 @@ void obj_print_plain(const struct obj *o
+ const char *obj_type_name(uint32_t type);
+ uint32_t obj_type_to_cmd(uint32_t type);
+ 
++struct flowtable {
++	struct list_head	list;
++	struct handle		handle;
++	struct location		location;
++	unsigned int		hooknum;
++	int			priority;
++	const char		**dev_array;
++	int			dev_array_len;
++	unsigned int		refcnt;
++};
++
++extern struct flowtable *flowtable_alloc(const struct location *loc);
++extern struct flowtable *flowtable_get(struct flowtable *flowtable);
++extern void flowtable_free(struct flowtable *flowtable);
++extern void flowtable_add_hash(struct flowtable *flowtable, struct table *table);
++
++void flowtable_print(const struct flowtable *n, struct output_ctx *octx);
++
+ /**
+  * enum cmd_ops - command operations
+  *
+@@ -373,6 +395,7 @@ enum cmd_ops {
+  * @CMD_OBJ_QUOTAS:	multiple quotas
+  * @CMD_OBJ_LIMIT:	limit
+  * @CMD_OBJ_LIMITS:	multiple limits
++ * @CMD_OBJ_FLOWTABLES:	flow tables
+  */
+ enum cmd_obj {
+ 	CMD_OBJ_INVALID,
+@@ -399,6 +422,7 @@ enum cmd_obj {
+ 	CMD_OBJ_CT_HELPERS,
+ 	CMD_OBJ_LIMIT,
+ 	CMD_OBJ_LIMITS,
++	CMD_OBJ_FLOWTABLES,
+ };
+ 
+ struct markup {
+--- a/src/evaluate.c
++++ b/src/evaluate.c
+@@ -3196,6 +3196,7 @@ static int cmd_evaluate_list(struct eval
+ 	case CMD_OBJ_CT_HELPERS:
+ 	case CMD_OBJ_LIMITS:
+ 	case CMD_OBJ_SETS:
++	case CMD_OBJ_FLOWTABLES:
+ 		if (cmd->handle.table == NULL)
+ 			return 0;
+ 		if (table_lookup(&cmd->handle, ctx->cache) == NULL)
+--- a/src/mnl.c
++++ b/src/mnl.c
+@@ -17,6 +17,7 @@
+ #include <libnftnl/expr.h>
+ #include <libnftnl/set.h>
+ #include <libnftnl/object.h>
++#include <libnftnl/flowtable.h>
+ #include <libnftnl/batch.h>
+ 
+ #include <linux/netfilter/nfnetlink.h>
+@@ -953,6 +954,63 @@ int mnl_nft_setelem_get(struct netlink_c
+ 	return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_elem_cb, nls);
+ }
+ 
++static int flowtable_cb(const struct nlmsghdr *nlh, void *data)
++{
++	struct nftnl_flowtable_list *nln_list = data;
++	struct nftnl_flowtable *n;
++
++	if (check_genid(nlh) < 0)
++		return MNL_CB_ERROR;
++
++	n = nftnl_flowtable_alloc();
++	if (n == NULL)
++		memory_allocation_error();
++
++	if (nftnl_flowtable_nlmsg_parse(nlh, n) < 0)
++		goto err_free;
++
++	nftnl_flowtable_list_add_tail(n, nln_list);
++	return MNL_CB_OK;
++
++err_free:
++	nftnl_flowtable_free(n);
++	return MNL_CB_OK;
++}
++
++struct nftnl_flowtable_list *
++mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
++{
++	struct nftnl_flowtable_list *nln_list;
++	char buf[MNL_SOCKET_BUFFER_SIZE];
++	struct nftnl_flowtable *n;
++	struct nlmsghdr *nlh;
++	int ret;
++
++	n = nftnl_flowtable_alloc();
++	if (n == NULL)
++		memory_allocation_error();
++
++	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family,
++				    NLM_F_DUMP | NLM_F_ACK, ctx->seqnum);
++	if (table != NULL)
++		nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_TABLE, table);
++	nftnl_flowtable_nlmsg_build_payload(nlh, n);
++	nftnl_flowtable_free(n);
++
++	nln_list = nftnl_flowtable_list_alloc();
++	if (nln_list == NULL)
++		memory_allocation_error();
++
++	ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, flowtable_cb, nln_list);
++	if (ret < 0)
++		goto err;
++
++	return nln_list;
++err:
++	nftnl_flowtable_list_free(nln_list);
++	return NULL;
++}
++
+ /*
+  * ruleset
+  */
+--- a/src/netlink.c
++++ b/src/netlink.c
+@@ -23,6 +23,7 @@
+ #include <libnftnl/expr.h>
+ #include <libnftnl/object.h>
+ #include <libnftnl/set.h>
++#include <libnftnl/flowtable.h>
+ #include <libnftnl/udata.h>
+ #include <libnftnl/ruleset.h>
+ #include <libnftnl/common.h>
+@@ -1826,6 +1827,70 @@ int netlink_reset_objs(struct netlink_ct
+ 	return err;
+ }
+ 
++static struct flowtable *
++netlink_delinearize_flowtable(struct netlink_ctx *ctx,
++			      struct nftnl_flowtable *nlo)
++{
++	struct flowtable *flowtable;
++	const char **dev_array;
++	int len = 0, i;
++
++	flowtable = flowtable_alloc(&netlink_location);
++	flowtable->handle.family =
++		nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FAMILY);
++	flowtable->handle.table =
++		xstrdup(nftnl_flowtable_get_str(nlo, NFTNL_FLOWTABLE_TABLE));
++	flowtable->handle.flowtable =
++		xstrdup(nftnl_flowtable_get_str(nlo, NFTNL_FLOWTABLE_NAME));
++	dev_array = nftnl_flowtable_get_array(nlo, NFTNL_FLOWTABLE_DEVICES);
++	while (dev_array[len] != '\0')
++		len++;
++
++	flowtable->dev_array = calloc(1, len * sizeof(char *));
++	for (i = 0; i < len; i++)
++		flowtable->dev_array[i] = xstrdup(dev_array[i]);
++
++	flowtable->dev_array_len = len;
++
++	flowtable->priority =
++		nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO);
++	flowtable->hooknum =
++		nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_HOOKNUM);
++
++	return flowtable;
++}
++
++static int list_flowtable_cb(struct nftnl_flowtable *nls, void *arg)
++{
++	struct netlink_ctx *ctx = arg;
++	struct flowtable *flowtable;
++
++	flowtable = netlink_delinearize_flowtable(ctx, nls);
++	if (flowtable == NULL)
++		return -1;
++	list_add_tail(&flowtable->list, &ctx->list);
++	return 0;
++}
++
++int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h,
++		      const struct location *loc)
++{
++	struct nftnl_flowtable_list *flowtable_cache;
++	int err;
++
++	flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family, h->table);
++	if (flowtable_cache == NULL) {
++		if (errno == EINTR)
++			return -1;
++
++		return 0;
++	}
++
++	err = nftnl_flowtable_list_foreach(flowtable_cache, list_flowtable_cb, ctx);
++	nftnl_flowtable_list_free(flowtable_cache);
++	return err;
++}
++
+ int netlink_batch_send(struct netlink_ctx *ctx, struct list_head *err_list)
+ {
+ 	return mnl_batch_talk(ctx, err_list);
+--- a/src/parser_bison.y
++++ b/src/parser_bison.y
+@@ -248,6 +248,8 @@ int nft_lex(void *, void *, void *);
+ %token METER			"meter"
+ %token METERS			"meters"
+ 
++%token FLOWTABLES		"flowtables"
++
+ %token <val> NUM		"number"
+ %token <string> STRING		"string"
+ %token <string> QUOTED_STRING	"quoted string"
+@@ -1104,6 +1106,10 @@ list_cmd		:	TABLE		table_spec
+ 			{
+ 				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &$2, &@$, NULL);
+ 			}
++			|       FLOWTABLES      ruleset_spec
++			{
++				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLES, &$2, &@$, NULL);
++			}
+ 			|	MAPS		ruleset_spec
+ 			{
+ 				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_MAPS, &$2, &@$, NULL);
+--- a/src/rule.c
++++ b/src/rule.c
+@@ -95,6 +95,11 @@ static int cache_init_objects(struct net
+ 			return -1;
+ 		list_splice_tail_init(&ctx->list, &table->chains);
+ 
++		ret = netlink_list_flowtables(ctx, &table->handle, &internal_location);
++		if (ret < 0)
++			return -1;
++		list_splice_tail_init(&ctx->list, &table->flowtables);
++
+ 		if (cmd != CMD_RESET) {
+ 			ret = netlink_list_objs(ctx, &table->handle, &internal_location);
+ 			if (ret < 0)
+@@ -722,6 +727,7 @@ struct table *table_alloc(void)
+ 	init_list_head(&table->chains);
+ 	init_list_head(&table->sets);
+ 	init_list_head(&table->objs);
++	init_list_head(&table->flowtables);
+ 	init_list_head(&table->scope.symbols);
+ 	table->refcnt = 1;
+ 
+@@ -797,6 +803,7 @@ static void table_print_options(const st
+ 
+ static void table_print(const struct table *table, struct output_ctx *octx)
+ {
++	struct flowtable *flowtable;
+ 	struct chain *chain;
+ 	struct obj *obj;
+ 	struct set *set;
+@@ -818,6 +825,11 @@ static void table_print(const struct tab
+ 		set_print(set, octx);
+ 		delim = "\n";
+ 	}
++	list_for_each_entry(flowtable, &table->flowtables, list) {
++		nft_print(octx, "%s", delim);
++		flowtable_print(flowtable, octx);
++		delim = "\n";
++	}
+ 	list_for_each_entry(chain, &table->chains, list) {
+ 		nft_print(octx, "%s", delim);
+ 		chain_print(chain, octx);
+@@ -1481,6 +1493,114 @@ static int do_list_obj(struct netlink_ct
+ 	return 0;
+ }
+ 
++struct flowtable *flowtable_alloc(const struct location *loc)
++{
++	struct flowtable *flowtable;
++
++	flowtable = xzalloc(sizeof(*flowtable));
++	if (loc != NULL)
++		flowtable->location = *loc;
++
++	flowtable->refcnt = 1;
++	return flowtable;
++}
++
++struct flowtable *flowtable_get(struct flowtable *flowtable)
++{
++	flowtable->refcnt++;
++	return flowtable;
++}
++
++void flowtable_free(struct flowtable *flowtable)
++{
++	if (--flowtable->refcnt > 0)
++		return;
++	handle_free(&flowtable->handle);
++	xfree(flowtable);
++}
++
++void flowtable_add_hash(struct flowtable *flowtable, struct table *table)
++{
++	list_add_tail(&flowtable->list, &table->flowtables);
++}
++
++static void flowtable_print_declaration(const struct flowtable *flowtable,
++					struct print_fmt_options *opts,
++					struct output_ctx *octx)
++{
++	int i;
++
++	nft_print(octx, "%sflowtable", opts->tab);
++
++	if (opts->family != NULL)
++		nft_print(octx, " %s", opts->family);
++
++	if (opts->table != NULL)
++		nft_print(octx, " %s", opts->table);
++
++	nft_print(octx, " %s {%s", flowtable->handle.flowtable, opts->nl);
++
++	nft_print(octx, "%s%shook %s priority %d%s",
++		  opts->tab, opts->tab, "ingress",
++		  flowtable->priority, opts->stmt_separator);
++
++	nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
++	for (i = 0; i < flowtable->dev_array_len; i++) {
++		nft_print(octx, "%s", flowtable->dev_array[i]);
++		if (i + 1 != flowtable->dev_array_len)
++			nft_print(octx, ", ");
++	}
++	nft_print(octx, " }%s", opts->stmt_separator);
++}
++
++static void do_flowtable_print(const struct flowtable *flowtable,
++			       struct print_fmt_options *opts,
++			       struct output_ctx *octx)
++{
++	flowtable_print_declaration(flowtable, opts, octx);
++	nft_print(octx, "%s}%s", opts->tab, opts->nl);
++}
++
++void flowtable_print(const struct flowtable *s, struct output_ctx *octx)
++{
++	struct print_fmt_options opts = {
++		.tab		= "\t",
++		.nl		= "\n",
++		.stmt_separator	= "\n",
++	};
++
++	do_flowtable_print(s, &opts, octx);
++}
++
++static int do_list_flowtables(struct netlink_ctx *ctx, struct cmd *cmd)
++{
++	struct print_fmt_options opts = {
++		.tab		= "\t",
++		.nl		= "\n",
++		.stmt_separator	= "\n",
++	};
++	struct flowtable *flowtable;
++	struct table *table;
++
++	list_for_each_entry(table, &ctx->cache->list, list) {
++		if (cmd->handle.family != NFPROTO_UNSPEC &&
++		    cmd->handle.family != table->handle.family)
++			continue;
++
++		nft_print(ctx->octx, "table %s %s {\n",
++			  family2str(table->handle.family),
++			  table->handle.table);
++
++		list_for_each_entry(flowtable, &table->flowtables, list) {
++			flowtable_print_declaration(flowtable, &opts, ctx->octx);
++			nft_print(ctx->octx, "%s}%s", opts.tab, opts.nl);
++		}
++
++		nft_print(ctx->octx, "}\n");
++	}
++	return 0;
++}
++
+ static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd)
+ {
+ 	unsigned int family = cmd->handle.family;
+@@ -1628,6 +1748,8 @@ static int do_command_list(struct netlin
+ 	case CMD_OBJ_LIMIT:
+ 	case CMD_OBJ_LIMITS:
+ 		return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);
++	case CMD_OBJ_FLOWTABLES:
++		return do_list_flowtables(ctx, cmd);
+ 	default:
+ 		BUG("invalid command object type %u\n", cmd->obj);
+ 	}
+--- a/src/scanner.l
++++ b/src/scanner.l
+@@ -297,6 +297,8 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr
+ "meter"			{ return METER; }
+ "meters"		{ return METERS; }
+ 
++"flowtables"		{ return FLOWTABLES; }
++
+ "counter"		{ return COUNTER; }
+ "name"			{ return NAME; }
+ "packets"		{ return PACKETS; }
diff --git a/package/network/utils/nftables/patches/201-src-add-support-to-add-flowtables.patch b/package/network/utils/nftables/patches/201-src-add-support-to-add-flowtables.patch
new file mode 100644
index 0000000..888a767
--- /dev/null
+++ b/package/network/utils/nftables/patches/201-src-add-support-to-add-flowtables.patch
@@ -0,0 +1,515 @@
+From: Pablo Neira Ayuso <pablo at netfilter.org>
+Date: Thu, 18 Jan 2018 08:43:23 +0100
+Subject: [PATCH] src: add support to add flowtables
+
+This patch allows you to create flowtable:
+
+ # nft add table x
+ # nft add flowtable x m { hook ingress priority 10\; devices = { eth0, wlan0 }\; }
+
+You have to specify hook and priority. So far, only the ingress hook is
+supported. The priority represents where this flowtable is placed in the
+ingress hook, which is registered to the devices that the user
+specifies.
+
+You can also use the 'create' command instead to bail out in case that
+there is an existing flowtable with this name.
+
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+
+--- a/include/expression.h
++++ b/include/expression.h
+@@ -407,6 +407,8 @@ extern struct expr *prefix_expr_alloc(co
+ extern struct expr *range_expr_alloc(const struct location *loc,
+ 				     struct expr *low, struct expr *high);
+ 
++extern struct expr *compound_expr_alloc(const struct location *loc,
++					const struct expr_ops *ops);
+ extern void compound_expr_add(struct expr *compound, struct expr *expr);
+ extern void compound_expr_remove(struct expr *compound, struct expr *expr);
+ extern void list_expr_sort(struct list_head *head);
+--- a/include/mnl.h
++++ b/include/mnl.h
+@@ -92,6 +92,10 @@ int mnl_nft_obj_batch_del(struct nftnl_o
+ struct nftnl_flowtable_list *
+ mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table);
+ 
++int mnl_nft_flowtable_batch_add(struct nftnl_flowtable *flo,
++				struct nftnl_batch *batch, unsigned int flags,
++				uint32_t seqnum);
++
+ struct nftnl_ruleset *mnl_nft_ruleset_dump(struct netlink_ctx *ctx,
+ 					   uint32_t family);
+ int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
+--- a/include/netlink.h
++++ b/include/netlink.h
+@@ -7,6 +7,7 @@
+ #include <libnftnl/expr.h>
+ #include <libnftnl/set.h>
+ #include <libnftnl/object.h>
++#include <libnftnl/flowtable.h>
+ 
+ #include <linux/netlink.h>
+ #include <linux/netfilter/nf_tables.h>
+@@ -182,6 +183,9 @@ extern int netlink_delete_obj(struct net
+ extern int netlink_list_flowtables(struct netlink_ctx *ctx,
+ 				   const struct handle *h,
+ 				   const struct location *loc);
++extern int netlink_add_flowtable(struct netlink_ctx *ctx,
++				 const struct handle *h, struct flowtable *ft,
++				 uint32_t flags);
+ 
+ extern void netlink_dump_chain(const struct nftnl_chain *nlc,
+ 			       struct netlink_ctx *ctx);
+--- a/include/rule.h
++++ b/include/rule.h
+@@ -322,10 +322,13 @@ uint32_t obj_type_to_cmd(uint32_t type);
+ struct flowtable {
+ 	struct list_head	list;
+ 	struct handle		handle;
++	struct scope		scope;
+ 	struct location		location;
++	const char *		hookstr;
+ 	unsigned int		hooknum;
+ 	int			priority;
+ 	const char		**dev_array;
++	struct expr		*dev_expr;
+ 	int			dev_array_len;
+ 	unsigned int		refcnt;
+ };
+@@ -383,6 +386,8 @@ enum cmd_ops {
+  * @CMD_OBJ_CHAIN:	chain
+  * @CMD_OBJ_CHAINS:	multiple chains
+  * @CMD_OBJ_TABLE:	table
++ * @CMD_OBJ_FLOWTABLE:	flowtable
++ * @CMD_OBJ_FLOWTABLES:	flowtables
+  * @CMD_OBJ_RULESET:	ruleset
+  * @CMD_OBJ_EXPR:	expression
+  * @CMD_OBJ_MONITOR:	monitor
+@@ -422,6 +427,7 @@ enum cmd_obj {
+ 	CMD_OBJ_CT_HELPERS,
+ 	CMD_OBJ_LIMIT,
+ 	CMD_OBJ_LIMITS,
++	CMD_OBJ_FLOWTABLE,
+ 	CMD_OBJ_FLOWTABLES,
+ };
+ 
+@@ -481,6 +487,7 @@ struct cmd {
+ 		struct rule	*rule;
+ 		struct chain	*chain;
+ 		struct table	*table;
++		struct flowtable *flowtable;
+ 		struct monitor	*monitor;
+ 		struct markup	*markup;
+ 		struct obj	*object;
+--- a/src/evaluate.c
++++ b/src/evaluate.c
+@@ -2897,6 +2897,24 @@ static int set_evaluate(struct eval_ctx
+ 	return 0;
+ }
+ 
++static uint32_t str2hooknum(uint32_t family, const char *hook);
++
++static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
++{
++	struct table *table;
++
++	table = table_lookup_global(ctx);
++	if (table == NULL)
++		return cmd_error(ctx, "Could not process rule: Table '%s' does not exist",
++				 ctx->cmd->handle.table);
++
++	ft->hooknum = str2hooknum(NFPROTO_NETDEV, ft->hookstr);
++	if (ft->hooknum == NF_INET_NUMHOOKS)
++		return chain_error(ctx, ft, "invalid hook %s", ft->hookstr);
++
++	return 0;
++}
++
+ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule)
+ {
+ 	struct stmt *stmt, *tstmt = NULL;
+@@ -3069,6 +3087,14 @@ static int cmd_evaluate_add(struct eval_
+ 		return chain_evaluate(ctx, cmd->chain);
+ 	case CMD_OBJ_TABLE:
+ 		return table_evaluate(ctx, cmd->table);
++	case CMD_OBJ_FLOWTABLE:
++		ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op,
++				   ctx->msgs, ctx->debug_mask & NFT_DEBUG_NETLINK, ctx->octx);
++		if (ret < 0)
++			return ret;
++
++		handle_merge(&cmd->flowtable->handle, &cmd->handle);
++		return flowtable_evaluate(ctx, cmd->flowtable);
+ 	case CMD_OBJ_COUNTER:
+ 	case CMD_OBJ_QUOTA:
+ 	case CMD_OBJ_CT_HELPER:
+--- a/src/expression.c
++++ b/src/expression.c
+@@ -663,8 +663,8 @@ struct expr *range_expr_alloc(const stru
+ 	return expr;
+ }
+ 
+-static struct expr *compound_expr_alloc(const struct location *loc,
+-					const struct expr_ops *ops)
++struct expr *compound_expr_alloc(const struct location *loc,
++				 const struct expr_ops *ops)
+ {
+ 	struct expr *expr;
+ 
+--- a/src/mnl.c
++++ b/src/mnl.c
+@@ -1011,6 +1011,22 @@ err:
+ 	return NULL;
+ }
+ 
++int mnl_nft_flowtable_batch_add(struct nftnl_flowtable *flo,
++				struct nftnl_batch *batch, unsigned int flags,
++				uint32_t seqnum)
++{
++	struct nlmsghdr *nlh;
++
++	nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch),
++				    NFT_MSG_NEWFLOWTABLE,
++				    nftnl_flowtable_get_u32(flo, NFTNL_FLOWTABLE_FAMILY),
++				    NLM_F_CREATE | flags, seqnum);
++	nftnl_flowtable_nlmsg_build_payload(nlh, flo);
++	mnl_nft_batch_continue(batch);
++
++	return 0;
++}
++
+ /*
+  * ruleset
+  */
+--- a/src/netlink.c
++++ b/src/netlink.c
+@@ -1773,6 +1773,64 @@ static struct obj *netlink_delinearize_o
+ 	return obj;
+ }
+ 
++static struct nftnl_flowtable *alloc_nftnl_flowtable(const struct handle *h,
++						     const struct flowtable *ft)
++{
++	struct nftnl_flowtable *flo;
++
++	flo = nftnl_flowtable_alloc();
++	if (flo == NULL)
++		memory_allocation_error();
++
++	nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY, h->family);
++	nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_TABLE, h->table);
++	if (h->flowtable != NULL)
++		nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_NAME, h->flowtable);
++
++	return flo;
++}
++
++static void netlink_dump_flowtable(struct nftnl_flowtable *flo,
++				   struct netlink_ctx *ctx)
++{
++	FILE *fp = ctx->octx->output_fp;
++
++	if (!(ctx->debug_mask & NFT_DEBUG_NETLINK) || !fp)
++		return;
++
++	nftnl_flowtable_fprintf(fp, flo, 0, 0);
++	fprintf(fp, "\n");
++}
++
++int netlink_add_flowtable(struct netlink_ctx *ctx, const struct handle *h,
++			  struct flowtable *ft, uint32_t flags)
++{
++	struct nftnl_flowtable *flo;
++	const char *dev_array[8];
++	struct expr *expr;
++	int i = 0, err;
++
++	flo = alloc_nftnl_flowtable(h, ft);
++	nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, ft->hooknum);
++	nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, ft->priority);
++
++	list_for_each_entry(expr, &ft->dev_expr->expressions, list)
++		dev_array[i++] = expr->identifier;
++
++	dev_array[i] = NULL;
++	nftnl_flowtable_set_array(flo, NFTNL_FLOWTABLE_DEVICES, dev_array);
++
++	netlink_dump_flowtable(flo, ctx);
++
++	err = mnl_nft_flowtable_batch_add(flo, ctx->batch, flags, ctx->seqnum);
++	if (err < 0)
++		netlink_io_error(ctx, &ft->location, "Could not add flowtable: %s",
++				 strerror(errno));
++	nftnl_flowtable_free(flo);
++
++	return err;
++}
++
+ static int list_obj_cb(struct nftnl_obj *nls, void *arg)
+ {
+ 	struct netlink_ctx *ctx = arg;
+--- a/src/parser_bison.y
++++ b/src/parser_bison.y
+@@ -145,6 +145,7 @@ int nft_lex(void *, void *, void *);
+ 	struct expr		*expr;
+ 	struct set		*set;
+ 	struct obj		*obj;
++	struct flowtable	*flowtable;
+ 	struct counter		*counter;
+ 	struct quota		*quota;
+ 	struct ct		*ct;
+@@ -189,6 +190,7 @@ int nft_lex(void *, void *, void *);
+ 
+ %token HOOK			"hook"
+ %token DEVICE			"device"
++%token DEVICES			"devices"
+ %token TABLE			"table"
+ %token TABLES			"tables"
+ %token CHAIN			"chain"
+@@ -200,6 +202,7 @@ int nft_lex(void *, void *, void *);
+ %token ELEMENT			"element"
+ %token MAP			"map"
+ %token MAPS			"maps"
++%token FLOWTABLE		"flowtable"
+ %token HANDLE			"handle"
+ %token RULESET			"ruleset"
+ %token TRACE			"trace"
+@@ -500,9 +503,9 @@ int nft_lex(void *, void *, void *);
+ %type <cmd>			base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
+ %destructor { cmd_free($$); }	base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
+ 
+-%type <handle>			table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
+-%destructor { handle_free(&$$); } table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
+-%type <handle>			set_spec set_identifier obj_spec obj_identifier
++%type <handle>			table_spec chain_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
++%destructor { handle_free(&$$); } table_spec chain_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
++%type <handle>			set_spec set_identifier flowtable_identifier obj_spec obj_identifier
+ %destructor { handle_free(&$$); } set_spec set_identifier obj_spec obj_identifier
+ %type <val>			family_spec family_spec_explicit chain_policy prio_spec
+ 
+@@ -526,6 +529,9 @@ int nft_lex(void *, void *, void *);
+ %type <set>			map_block_alloc map_block
+ %destructor { set_free($$); }	map_block_alloc
+ 
++%type <flowtable>		flowtable_block_alloc flowtable_block
++%destructor { flowtable_free($$); }	flowtable_block_alloc
++
+ %type <obj>			obj_block_alloc counter_block quota_block ct_helper_block limit_block
+ %destructor { obj_free($$); }	obj_block_alloc
+ 
+@@ -606,8 +612,8 @@ int nft_lex(void *, void *, void *);
+ %type <expr>			verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr
+ %destructor { expr_free($$); }	verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr
+ 
+-%type <expr>			set_expr set_block_expr set_list_expr set_list_member_expr
+-%destructor { expr_free($$); }	set_expr set_block_expr set_list_expr set_list_member_expr
++%type <expr>			set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member
++%destructor { expr_free($$); }	set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member
+ %type <expr>			set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
+ %destructor { expr_free($$); }	set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
+ %type <expr>			set_elem_expr_stmt set_elem_expr_stmt_alloc
+@@ -872,6 +878,13 @@ add_cmd			:	TABLE		table_spec
+ 			{
+ 				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEM, &$2, &@$, $3);
+ 			}
++			|	FLOWTABLE	flowtable_spec	flowtable_block_alloc
++						'{'	flowtable_block	'}'
++			{
++				$5->location = @5;
++				handle_merge(&$3->handle, &$2);
++				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
++			}
+ 			|	COUNTER		obj_spec
+ 			{
+ 				struct obj *obj;
+@@ -947,6 +960,13 @@ create_cmd		:	TABLE		table_spec
+ 			{
+ 				$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SETELEM, &$2, &@$, $3);
+ 			}
++			|	FLOWTABLE	flowtable_spec	flowtable_block_alloc
++						'{'	flowtable_block	'}'
++			{
++				$5->location = @5;
++				handle_merge(&$3->handle, &$2);
++				$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
++			}
+ 			|	COUNTER		obj_spec
+ 			{
+ 				struct obj *obj;
+@@ -1317,6 +1337,17 @@ table_block		:	/* empty */	{ $$ = $<tabl
+ 				list_add_tail(&$4->list, &$1->sets);
+ 				$$ = $1;
+ 			}
++
++			|	table_block	FLOWTABLE	flowtable_identifier
++					flowtable_block_alloc	'{'	flowtable_block	'}'
++					stmt_separator
++			{
++				$4->location = @3;
++				handle_merge(&$4->handle, &$3);
++				handle_free(&$3);
++				list_add_tail(&$4->list, &$1->flowtables);
++				$$ = $1;
++			}
+ 			|	table_block	COUNTER		obj_identifier
+ 					obj_block_alloc	'{'	counter_block	'}'
+ 					stmt_separator
+@@ -1512,6 +1543,62 @@ set_policy_spec		:	PERFORMANCE	{ $$ = NF
+ 			|	MEMORY		{ $$ = NFT_SET_POL_MEMORY; }
+ 			;
+ 
++flowtable_block_alloc	:	/* empty */
++			{
++				$$ = flowtable_alloc(NULL);
++			}
++			;
++
++flowtable_block		:	/* empty */	{ $$ = $<flowtable>-1; }
++			|	flowtable_block	common_block
++			|	flowtable_block	stmt_separator
++			|	flowtable_block	HOOK		STRING	PRIORITY        prio_spec	stmt_separator
++			{
++				$$->hookstr	= chain_hookname_lookup($3);
++				if ($$->hookstr == NULL) {
++					erec_queue(error(&@3, "unknown chain hook %s", $3),
++						   state->msgs);
++					xfree($3);
++					YYERROR;
++				}
++				xfree($3);
++
++				$$->priority = $5;
++			}
++			|	flowtable_block	DEVICES		'='	flowtable_expr	stmt_separator
++			{
++				$$->dev_expr = $4;
++			}
++			;
++
++flowtable_expr		:	'{'	flowtable_list_expr	'}'
++			{
++				$2->location = @$;
++				$$ = $2;
++			}
++			;
++
++flowtable_list_expr	:	flowtable_expr_member
++			{
++				$$ = compound_expr_alloc(&@$, NULL);
++				compound_expr_add($$, $1);
++			}
++			|	flowtable_list_expr	COMMA	flowtable_expr_member
++			{
++				compound_expr_add($1, $3);
++				$$ = $1;
++			}
++			|	flowtable_list_expr	COMMA	opt_newline
++			;
++
++flowtable_expr_member	:	STRING
++			{
++				$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
++						       current_scope(state),
++						       $1);
++			}
++			;
++
+ data_type_atom_expr	:	type_identifier
+ 			{
+ 				const struct datatype *dtype = datatype_lookup_byname($1);
+@@ -1720,6 +1807,21 @@ set_identifier		:	identifier
+ 			}
+ 			;
+ 
++
++flowtable_spec		:	table_spec	identifier
++			{
++				$$		= $1;
++				$$.flowtable	= $2;
++			}
++			;
++
++flowtable_identifier	:	identifier
++			{
++				memset(&$$, 0, sizeof($$));
++				$$.flowtable	= $1;
++			}
++			;
++
+ obj_spec		:	table_spec	identifier
+ 			{
+ 				$$		= $1;
+--- a/src/rule.c
++++ b/src/rule.c
+@@ -45,6 +45,8 @@ void handle_merge(struct handle *dst, co
+ 		dst->chain = xstrdup(src->chain);
+ 	if (dst->set == NULL && src->set != NULL)
+ 		dst->set = xstrdup(src->set);
++	if (dst->flowtable == NULL && src->flowtable != NULL)
++		dst->flowtable = xstrdup(src->flowtable);
+ 	if (dst->obj == NULL && src->obj != NULL)
+ 		dst->obj = xstrdup(src->obj);
+ 	if (dst->handle.id == 0)
+@@ -857,6 +859,7 @@ struct cmd *cmd_alloc(enum cmd_ops op, e
+ void nft_cmd_expand(struct cmd *cmd)
+ {
+ 	struct list_head new_cmds;
++	struct flowtable *ft;
+ 	struct table *table;
+ 	struct chain *chain;
+ 	struct rule *rule;
+@@ -896,6 +899,14 @@ void nft_cmd_expand(struct cmd *cmd)
+ 					&set->location, set_get(set));
+ 			list_add_tail(&new->list, &new_cmds);
+ 		}
++		list_for_each_entry(ft, &table->flowtables, list) {
++			handle_merge(&ft->handle, &table->handle);
++			memset(&h, 0, sizeof(h));
++			handle_merge(&h, &ft->handle);
++			new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h,
++					&ft->location, flowtable_get(ft));
++			list_add_tail(&new->list, &new_cmds);
++		}
+ 		list_for_each_entry(chain, &table->chains, list) {
+ 			list_for_each_entry(rule, &chain->rules, list) {
+ 				memset(&h, 0, sizeof(h));
+@@ -982,6 +993,9 @@ void cmd_free(struct cmd *cmd)
+ 		case CMD_OBJ_LIMIT:
+ 			obj_free(cmd->object);
+ 			break;
++		case CMD_OBJ_FLOWTABLE:
++			flowtable_free(cmd->flowtable);
++			break;
+ 		default:
+ 			BUG("invalid command object type %u\n", cmd->obj);
+ 		}
+@@ -1071,6 +1085,9 @@ static int do_command_add(struct netlink
+ 	case CMD_OBJ_CT_HELPER:
+ 	case CMD_OBJ_LIMIT:
+ 		return netlink_add_obj(ctx, &cmd->handle, cmd->object, flags);
++	case CMD_OBJ_FLOWTABLE:
++		return netlink_add_flowtable(ctx, &cmd->handle, cmd->flowtable,
++					     flags);
+ 	default:
+ 		BUG("invalid command object type %u\n", cmd->obj);
+ 	}
+--- a/src/scanner.l
++++ b/src/scanner.l
+@@ -238,6 +238,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr
+ 
+ "hook"			{ return HOOK; }
+ "device"		{ return DEVICE; }
++"devices"		{ return DEVICES; }
+ "table"			{ return TABLE; }
+ "tables"		{ return TABLES; }
+ "chain"			{ return CHAIN; }
+@@ -249,6 +250,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr
+ "element"		{ return ELEMENT; }
+ "map"			{ return MAP; }
+ "maps"			{ return MAPS; }
++"flowtable"		{ return FLOWTABLE; }
+ "handle"		{ return HANDLE; }
+ "ruleset"		{ return RULESET; }
+ "trace"			{ return TRACE; }
diff --git a/package/network/utils/nftables/patches/202-src-delete-flowtable.patch b/package/network/utils/nftables/patches/202-src-delete-flowtable.patch
new file mode 100644
index 0000000..32b7f96
--- /dev/null
+++ b/package/network/utils/nftables/patches/202-src-delete-flowtable.patch
@@ -0,0 +1,122 @@
+From: Pablo Neira Ayuso <pablo at netfilter.org>
+Date: Fri, 19 Jan 2018 01:41:38 +0100
+Subject: [PATCH] src: delete flowtable
+
+This patch allows you to delete an existing flowtable:
+
+ # nft delete flowtable x m
+
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+
+--- a/include/mnl.h
++++ b/include/mnl.h
+@@ -95,6 +95,9 @@ mnl_nft_flowtable_dump(struct netlink_ct
+ int mnl_nft_flowtable_batch_add(struct nftnl_flowtable *flo,
+ 				struct nftnl_batch *batch, unsigned int flags,
+ 				uint32_t seqnum);
++int mnl_nft_flowtable_batch_del(struct nftnl_flowtable *flow,
++				struct nftnl_batch *batch, unsigned int flags,
++				uint32_t seqnum);
+ 
+ struct nftnl_ruleset *mnl_nft_ruleset_dump(struct netlink_ctx *ctx,
+ 					   uint32_t family);
+--- a/include/netlink.h
++++ b/include/netlink.h
+@@ -186,6 +186,9 @@ extern int netlink_list_flowtables(struc
+ extern int netlink_add_flowtable(struct netlink_ctx *ctx,
+ 				 const struct handle *h, struct flowtable *ft,
+ 				 uint32_t flags);
++extern int netlink_delete_flowtable(struct netlink_ctx *ctx,
++				    const struct handle *h,
++				    struct location *loc);
+ 
+ extern void netlink_dump_chain(const struct nftnl_chain *nlc,
+ 			       struct netlink_ctx *ctx);
+--- a/src/evaluate.c
++++ b/src/evaluate.c
+@@ -3121,6 +3121,7 @@ static int cmd_evaluate_delete(struct ev
+ 	case CMD_OBJ_RULE:
+ 	case CMD_OBJ_CHAIN:
+ 	case CMD_OBJ_TABLE:
++	case CMD_OBJ_FLOWTABLE:
+ 	case CMD_OBJ_COUNTER:
+ 	case CMD_OBJ_QUOTA:
+ 	case CMD_OBJ_CT_HELPER:
+--- a/src/mnl.c
++++ b/src/mnl.c
+@@ -1027,6 +1027,22 @@ int mnl_nft_flowtable_batch_add(struct n
+ 	return 0;
+ }
+ 
++int mnl_nft_flowtable_batch_del(struct nftnl_flowtable *flo,
++				struct nftnl_batch *batch, unsigned int flags,
++				uint32_t seqnum)
++{
++	struct nlmsghdr *nlh;
++
++	nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch),
++				    NFT_MSG_DELFLOWTABLE,
++				    nftnl_flowtable_get_u32(flo, NFTNL_FLOWTABLE_FAMILY),
++				    flags, seqnum);
++	nftnl_flowtable_nlmsg_build_payload(nlh, flo);
++	mnl_nft_batch_continue(batch);
++
++	return 0;
++}
++
+ /*
+  * ruleset
+  */
+--- a/src/netlink.c
++++ b/src/netlink.c
+@@ -1831,6 +1831,24 @@ int netlink_add_flowtable(struct netlink
+ 	return err;
+ }
+ 
++int netlink_delete_flowtable(struct netlink_ctx *ctx, const struct handle *h,
++			     struct location *loc)
++{
++	struct nftnl_flowtable *flo;
++	int err;
++
++	flo = alloc_nftnl_flowtable(h, NULL);
++	netlink_dump_flowtable(flo, ctx);
++
++	err = mnl_nft_flowtable_batch_del(flo, ctx->batch, 0, ctx->seqnum);
++	if (err < 0)
++		netlink_io_error(ctx, loc, "Could not delete flowtable: %s",
++				 strerror(errno));
++	nftnl_flowtable_free(flo);
++
++	return err;
++}
++
+ static int list_obj_cb(struct nftnl_obj *nls, void *arg)
+ {
+ 	struct netlink_ctx *ctx = arg;
+--- a/src/parser_bison.y
++++ b/src/parser_bison.y
+@@ -1024,6 +1024,10 @@ delete_cmd		:	TABLE		table_spec
+ 			{
+ 				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SETELEM, &$2, &@$, $3);
+ 			}
++			|	FLOWTABLE	flowtable_spec
++			{
++				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
++			}
+ 			|	COUNTER		obj_spec
+ 			{
+ 				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+--- a/src/rule.c
++++ b/src/rule.c
+@@ -1177,6 +1177,9 @@ static int do_command_delete(struct netl
+ 	case CMD_OBJ_LIMIT:
+ 		return netlink_delete_obj(ctx, &cmd->handle, &cmd->location,
+ 					  NFT_OBJECT_LIMIT);
++	case CMD_OBJ_FLOWTABLE:
++		return netlink_delete_flowtable(ctx, &cmd->handle,
++						&cmd->location);
+ 	default:
+ 		BUG("invalid command object type %u\n", cmd->obj);
+ 	}
diff --git a/package/network/utils/nftables/patches/203-src-flow-offload-support.patch b/package/network/utils/nftables/patches/203-src-flow-offload-support.patch
new file mode 100644
index 0000000..86dfb1d
--- /dev/null
+++ b/package/network/utils/nftables/patches/203-src-flow-offload-support.patch
@@ -0,0 +1,191 @@
+From: Pablo Neira Ayuso <pablo at netfilter.org>
+Date: Sun, 3 Dec 2017 21:27:03 +0100
+Subject: [PATCH] src: flow offload support
+
+This patch allows us to refer to existing flowtables:
+
+ # nft add rule x x flow offload @m
+
+Packets matching this rule create an entry in the flow table 'm', hence,
+follow up packets that get to the flowtable at ingress bypass the
+classic forwarding path.
+
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+
+--- a/include/ct.h
++++ b/include/ct.h
+@@ -29,6 +29,8 @@ extern struct expr *ct_expr_alloc(const
+ extern void ct_expr_update_type(struct proto_ctx *ctx, struct expr *expr);
+ 
+ extern struct stmt *notrack_stmt_alloc(const struct location *loc);
++extern struct stmt *flow_offload_stmt_alloc(const struct location *loc,
++					    const char *table_name);
+ 
+ extern const struct datatype ct_dir_type;
+ extern const struct datatype ct_state_type;
+--- a/include/statement.h
++++ b/include/statement.h
+@@ -10,6 +10,12 @@ extern struct stmt *expr_stmt_alloc(cons
+ extern struct stmt *verdict_stmt_alloc(const struct location *loc,
+ 				       struct expr *expr);
+ 
++struct flow_stmt {
++	const char		*table_name;
++};
++
++struct stmt *flow_stmt_alloc(const struct location *loc, const char *name);
++
+ struct objref_stmt {
+ 	uint32_t		type;
+ 	struct expr		*expr;
+@@ -231,6 +237,7 @@ extern struct stmt *xt_stmt_alloc(const
+  * @STMT_NOTRACK:	notrack statement
+  * @STMT_OBJREF:	stateful object reference statement
+  * @STMT_EXTHDR:	extension header statement
++ * @STMT_FLOW_OFFLOAD:	flow offload statement
+  */
+ enum stmt_types {
+ 	STMT_INVALID,
+@@ -256,6 +263,7 @@ enum stmt_types {
+ 	STMT_NOTRACK,
+ 	STMT_OBJREF,
+ 	STMT_EXTHDR,
++	STMT_FLOW_OFFLOAD,
+ };
+ 
+ /**
+@@ -316,6 +324,7 @@ struct stmt {
+ 		struct fwd_stmt		fwd;
+ 		struct xt_stmt		xt;
+ 		struct objref_stmt	objref;
++		struct flow_stmt	flow;
+ 	};
+ };
+ 
+--- a/src/ct.c
++++ b/src/ct.c
+@@ -456,3 +456,26 @@ struct stmt *notrack_stmt_alloc(const st
+ {
+ 	return stmt_alloc(loc, &notrack_stmt_ops);
+ }
++
++static void flow_offload_stmt_print(const struct stmt *stmt,
++				    struct output_ctx *octx)
++{
++	printf("flow offload @%s", stmt->flow.table_name);
++}
++
++static const struct stmt_ops flow_offload_stmt_ops = {
++	.type		= STMT_FLOW_OFFLOAD,
++	.name		= "flow_offload",
++	.print		= flow_offload_stmt_print,
++};
++
++struct stmt *flow_offload_stmt_alloc(const struct location *loc,
++				     const char *table_name)
++{
++	struct stmt *stmt;
++
++	stmt = stmt_alloc(loc, &flow_offload_stmt_ops);
++	stmt->flow.table_name	= table_name;
++
++	return stmt;
++}
+--- a/src/evaluate.c
++++ b/src/evaluate.c
+@@ -2773,6 +2773,7 @@ int stmt_evaluate(struct eval_ctx *ctx,
+ 	case STMT_LIMIT:
+ 	case STMT_QUOTA:
+ 	case STMT_NOTRACK:
++	case STMT_FLOW_OFFLOAD:
+ 		return 0;
+ 	case STMT_EXPRESSION:
+ 		return stmt_evaluate_expr(ctx, stmt);
+--- a/src/netlink_delinearize.c
++++ b/src/netlink_delinearize.c
+@@ -680,6 +680,16 @@ static void netlink_parse_notrack(struct
+ 	ctx->stmt = notrack_stmt_alloc(loc);
+ }
+ 
++static void netlink_parse_flow_offload(struct netlink_parse_ctx *ctx,
++				       const struct location *loc,
++				       const struct nftnl_expr *nle)
++{
++	const char *table_name;
++
++	table_name = xstrdup(nftnl_expr_get_str(nle, NFTNL_EXPR_FLOW_TABLE_NAME));
++	ctx->stmt = flow_offload_stmt_alloc(loc, table_name);
++}
++
+ static void netlink_parse_ct_stmt(struct netlink_parse_ctx *ctx,
+ 				  const struct location *loc,
+ 				  const struct nftnl_expr *nle)
+@@ -1255,6 +1265,7 @@ static const struct {
+ 	{ .name = "hash",	.parse = netlink_parse_hash },
+ 	{ .name = "fib",	.parse = netlink_parse_fib },
+ 	{ .name = "tcpopt",	.parse = netlink_parse_exthdr },
++	{ .name = "flow_offload", .parse = netlink_parse_flow_offload },
+ };
+ 
+ static int netlink_parse_expr(const struct nftnl_expr *nle,
+--- a/src/netlink_linearize.c
++++ b/src/netlink_linearize.c
+@@ -1201,6 +1201,17 @@ static void netlink_gen_notrack_stmt(str
+ 	nftnl_rule_add_expr(ctx->nlr, nle);
+ }
+ 
++static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx,
++					  const struct stmt *stmt)
++{
++	struct nftnl_expr *nle;
++
++	nle = alloc_nft_expr("flow_offload");
++	nftnl_expr_set_str(nle, NFTNL_EXPR_FLOW_TABLE_NAME,
++			   stmt->flow.table_name);
++	nftnl_rule_add_expr(ctx->nlr, nle);
++}
++
+ static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
+ 				 const struct stmt *stmt)
+ {
+@@ -1300,6 +1311,8 @@ static void netlink_gen_stmt(struct netl
+ 		break;
+ 	case STMT_NOTRACK:
+ 		return netlink_gen_notrack_stmt(ctx, stmt);
++	case STMT_FLOW_OFFLOAD:
++		return netlink_gen_flow_offload_stmt(ctx, stmt);
+ 	case STMT_OBJREF:
+ 		return netlink_gen_objref_stmt(ctx, stmt);
+ 	default:
+--- a/src/parser_bison.y
++++ b/src/parser_bison.y
+@@ -248,6 +248,7 @@ int nft_lex(void *, void *, void *);
+ %token SIZE			"size"
+ 
+ %token FLOW			"flow"
++%token OFFLOAD			"offload"
+ %token METER			"meter"
+ %token METERS			"meters"
+ 
+@@ -3384,6 +3385,10 @@ meta_stmt		:	META	meta_key	SET	stmt_expr
+ 			{
+ 				$$ = notrack_stmt_alloc(&@$);
+ 			}
++			|	FLOW	OFFLOAD	AT string
++			{
++				$$ = flow_offload_stmt_alloc(&@$, $4);
++			}
+ 			;
+ 
+ offset_opt		:	/* empty */	{ $$ = 0; }
+--- a/src/scanner.l
++++ b/src/scanner.l
+@@ -296,6 +296,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr
+ "memory"		{ return MEMORY; }
+ 
+ "flow"			{ return FLOW; }
++"offload"		{ return OFFLOAD; }
+ "meter"			{ return METER; }
+ "meters"		{ return METERS; }
+ 
diff --git a/package/network/utils/nftables/patches/204-tests-shell-add-flowtable-tests.patch b/package/network/utils/nftables/patches/204-tests-shell-add-flowtable-tests.patch
new file mode 100644
index 0000000..e6dbf8f
--- /dev/null
+++ b/package/network/utils/nftables/patches/204-tests-shell-add-flowtable-tests.patch
@@ -0,0 +1,110 @@
+From: Pablo Neira Ayuso <pablo at netfilter.org>
+Date: Mon, 22 Jan 2018 19:54:36 +0100
+Subject: [PATCH] tests: shell: add flowtable tests
+
+Add basic flowtable tests.
+
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ create mode 100755 tests/shell/testcases/flowtable/0001flowtable_0
+ create mode 100755 tests/shell/testcases/flowtable/0002create_flowtable_0
+ create mode 100755 tests/shell/testcases/flowtable/0003add_after_flush_0
+ create mode 100755 tests/shell/testcases/flowtable/0004delete_after_add0
+ create mode 100755 tests/shell/testcases/flowtable/0005delete_in_use_1
+
+--- a/tests/shell/run-tests.sh
++++ b/tests/shell/run-tests.sh
+@@ -68,7 +68,9 @@ kernel_cleanup() {
+ 	nft_set_hash nft_set_rbtree nft_set_bitmap \
+ 	nft_chain_nat_ipv4 nft_chain_nat_ipv6 \
+ 	nf_tables_inet nf_tables_bridge nf_tables_arp \
+-	nf_tables_ipv4 nf_tables_ipv6 nf_tables
++	nf_tables_ipv4 nf_tables_ipv6 nf_tables \
++	nf_flow_table nf_flow_table_ipv4 nf_flow_tables_ipv6 \
++	nf_flow_table_inet nft_flow_offload
+ }
+ 
+ find_tests() {
+--- /dev/null
++++ b/tests/shell/testcases/flowtable/0001flowtable_0
+@@ -0,0 +1,33 @@
++#!/bin/bash
++
++tmpfile=$(mktemp)
++if [ ! -w $tmpfile ] ; then
++	echo "Failed to create tmp file" >&2
++	exit 0
++fi
++
++trap "rm -rf $tmpfile" EXIT # cleanup if aborted
++
++
++EXPECTED='table inet t {
++	flowtable f {
++		hook ingress priority 10
++		devices = { eth0, wlan0 }
++	}
++
++	chain c {
++		flow offload @f
++	}
++}'
++
++echo "$EXPECTED" > $tmpfile
++set -e
++$NFT -f $tmpfile
++
++GET="$($NFT list ruleset)"
++
++if [ "$EXPECTED" != "$GET" ] ; then
++	DIFF="$(which diff)"
++	[ -x $DIFF ] && $DIFF -u <(echo "$EXPECTED") <(echo "$GET")
++	exit 1
++fi
+--- /dev/null
++++ b/tests/shell/testcases/flowtable/0002create_flowtable_0
+@@ -0,0 +1,12 @@
++#!/bin/bash
++
++set -e
++$NFT add table t
++$NFT add flowtable t f { hook ingress priority 10 \; devices = { eth0, wlan0 }\; }
++if $NFT create flowtable t f { hook ingress priority 10 \; devices = { eth0, wlan0 }\; } 2>/dev/null ; then
++	echo "E: flowtable creation not failing on existing set" >&2
++	exit 1
++fi
++$NFT add flowtable t f { hook ingress priority 10 \; devices = { eth0, wlan0 }\; }
++
++exit 0
+--- /dev/null
++++ b/tests/shell/testcases/flowtable/0003add_after_flush_0
+@@ -0,0 +1,8 @@
++#!/bin/bash
++
++set -e
++$NFT add table x
++$NFT add flowtable x y { hook ingress priority 0\; devices = { eth0, wlan0 }\;}
++$NFT flush ruleset
++$NFT add table x
++$NFT add flowtable x y { hook ingress priority 0\; devices = { eth0, wlan0 }\;}
+--- /dev/null
++++ b/tests/shell/testcases/flowtable/0004delete_after_add0
+@@ -0,0 +1,6 @@
++#!/bin/bash
++
++set -e
++$NFT add table x
++$NFT add flowtable x y { hook ingress priority 0\; devices = { eth0, wlan0 }\;}
++$NFT delete flowtable x y
+--- /dev/null
++++ b/tests/shell/testcases/flowtable/0005delete_in_use_1
+@@ -0,0 +1,9 @@
++#!/bin/bash
++
++set -e
++$NFT add table x
++$NFT add chain x x
++$NFT add flowtable x y { hook ingress priority 0\; devices = { eth0, wlan0 }\;}
++$NFT add rule x x flow offload @y
++$NFT delete flowtable x y
++echo "E: delete flowtable in use"
diff --git a/package/network/utils/nftables/patches/205-doc-nft-document-flowtable.patch b/package/network/utils/nftables/patches/205-doc-nft-document-flowtable.patch
new file mode 100644
index 0000000..dd6faa5
--- /dev/null
+++ b/package/network/utils/nftables/patches/205-doc-nft-document-flowtable.patch
@@ -0,0 +1,128 @@
+From: Pablo Neira Ayuso <pablo at netfilter.org>
+Date: Tue, 23 Jan 2018 12:58:30 +0100
+Subject: [PATCH] doc: nft: document flowtable
+
+Document the new flowtable objects available since Linux kernel 4.16-rc.
+
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+
+--- a/doc/nft.xml
++++ b/doc/nft.xml
+@@ -1166,6 +1166,91 @@ filter input iif $int_ifs accept
+ 	</refsect1>
+ 
+ 	<refsect1>
++		<title>Flowtables</title>
++		<para>
++			<cmdsynopsis>
++				<group choice="req">
++					<arg>add</arg>
++					<arg>create</arg>
++				</group>
++				<command>flowtable</command>
++				<arg choice="opt"><replaceable>family</replaceable></arg>
++				<arg choice="plain"><replaceable>table</replaceable></arg>
++				<arg choice="plain"><replaceable>flowtable</replaceable></arg>
++				<arg choice="req">
++					hook <replaceable>hook</replaceable>
++					priority <replaceable>priority</replaceable> ;
++					devices = { <replaceable>device</replaceable>[,...] } ;
++				</arg>
++			</cmdsynopsis>
++			<cmdsynopsis>
++				<group choice="req">
++					<arg>delete</arg>
++					<arg>list</arg>
++				</group>
++				<command>flowtable</command>
++				<arg choice="opt"><replaceable>family</replaceable></arg>
++				<replaceable>table</replaceable>
++				<replaceable>flowtable</replaceable>
++			</cmdsynopsis>
++		</para>
++
++		<para>
++			Flowtables allow you to accelerate packet forwarding in software.
++			Flowtables entries are represented through a tuple that is composed of the
++			input interface, source and destination address, source and destination
++			port; and layer 3/4 protocols. Each entry also caches the destination
++			interface and the gateway address - to update the destination link-layer
++			address - to forward packets. The ttl and hoplimit fields are also
++			decremented. Hence, flowtables provides an alternative path that allow
++			packets to bypass the classic forwarding path. Flowtables reside in the
++			ingress hook, that is located before the prerouting hook. You can select
++			what flows you want to offload through the <literal>flow offload</literal>
++			expression from the <literal>forward</literal> chain. Flowtables are
++			identified by their address family and their name. The address family
++			must be one of
++
++			<simplelist type="inline">
++				<member><literal>ip</literal></member>
++				<member><literal>ip6</literal></member>
++				<member><literal>inet</literal></member>
++			</simplelist>.
++
++			The <literal>inet</literal> address family is a dummy family which is used to create
++			hybrid IPv4/IPv6 tables.
++
++			When no address family is specified, <literal>ip</literal> is used by default.
++		</para>
++
++		<variablelist>
++			<varlistentry>
++				<term><option>add</option></term>
++				<listitem>
++					<para>
++						Add a new flowtable for the given family with the given name.
++					</para>
++				</listitem>
++			</varlistentry>
++			<varlistentry>
++				<term><option>delete</option></term>
++				<listitem>
++					<para>
++						Delete the specified flowtable.
++					</para>
++				</listitem>
++			</varlistentry>
++			<varlistentry>
++				<term><option>list</option></term>
++				<listitem>
++					<para>
++						List all flowtables.
++					</para>
++				</listitem>
++			</varlistentry>
++		</variablelist>
++	</refsect1>
++
++	<refsect1>
+ 		<title>Stateful objects</title>
+ 		<para>
+ 			<cmdsynopsis>
+@@ -4923,6 +5008,24 @@ add rule nat prerouting tcp dport 22 red
+ 				</example>
+ 			</para>
+ 		</refsect2>
++
++		<refsect2>
++			<title>Flow offload statement</title>
++			<para>
++				A flow offload statement allows us to select what flows
++				you want to accelerate forwarding through layer 3 network
++				stack bypass. You have to specify the flowtable name where
++				you want to offload this flow.
++			</para>
++			<para>
++				<cmdsynopsis>
++					<command>flow offload</command>
++					<literal>@flowtable</literal>
++				</cmdsynopsis>
++			</para>
++
++		</refsect2>
++
+ 		<refsect2>
+ 			<title>Queue statement</title>
+ 			<para>



More information about the lede-commits mailing list