nftables: update to 0.8.2, backport flowtable support
authorFelix Fietkau <nbd@nbd.name>
Mon, 5 Feb 2018 13:02:13 +0000 (14:02 +0100)
committerFelix Fietkau <nbd@nbd.name>
Wed, 21 Feb 2018 19:12:41 +0000 (20:12 +0100)
Signed-off-by: Felix Fietkau <nbd@nbd.name>
package/network/utils/nftables/patches/200-src-support-for-flowtable-listing.patch [new file with mode: 0644]
package/network/utils/nftables/patches/201-src-add-support-to-add-flowtables.patch [new file with mode: 0644]
package/network/utils/nftables/patches/202-src-delete-flowtable.patch [new file with mode: 0644]
package/network/utils/nftables/patches/203-src-flow-offload-support.patch [new file with mode: 0644]
package/network/utils/nftables/patches/204-tests-shell-add-flowtable-tests.patch [new file with mode: 0644]
package/network/utils/nftables/patches/205-doc-nft-document-flowtable.patch [new file with mode: 0644]

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 (file)
index 0000000..259513d
--- /dev/null
@@ -0,0 +1,515 @@
+From: Pablo Neira Ayuso <pablo@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@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 (file)
index 0000000..888a767
--- /dev/null
@@ -0,0 +1,515 @@
+From: Pablo Neira Ayuso <pablo@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@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 (file)
index 0000000..32b7f96
--- /dev/null
@@ -0,0 +1,122 @@
+From: Pablo Neira Ayuso <pablo@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@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 (file)
index 0000000..86dfb1d
--- /dev/null
@@ -0,0 +1,191 @@
+From: Pablo Neira Ayuso <pablo@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@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 (file)
index 0000000..e6dbf8f
--- /dev/null
@@ -0,0 +1,110 @@
+From: Pablo Neira Ayuso <pablo@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@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 (file)
index 0000000..dd6faa5
--- /dev/null
@@ -0,0 +1,128 @@
+From: Pablo Neira Ayuso <pablo@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@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>