From: Felix Fietkau Date: Mon, 5 Feb 2018 13:02:13 +0000 (+0100) Subject: nftables: update to 0.8.2, backport flowtable support X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=bc3e0f6052547fbffa9fcf1a6c7e340a95aa9f46;p=openwrt%2Fstaging%2Fyousong.git nftables: update to 0.8.2, backport flowtable support Signed-off-by: Felix Fietkau --- 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 0000000000..259513de8b --- /dev/null +++ b/package/network/utils/nftables/patches/200-src-support-for-flowtable-listing.patch @@ -0,0 +1,515 @@ +From: Pablo Neira Ayuso +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 +--- + +--- 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 + #include + #include ++#include + #include + + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 NUM "number" + %token STRING "string" + %token 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 0000000000..888a767160 --- /dev/null +++ b/package/network/utils/nftables/patches/201-src-add-support-to-add-flowtables.patch @@ -0,0 +1,515 @@ +From: Pablo Neira Ayuso +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 +--- + +--- 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 + #include + #include ++#include + + #include + #include +@@ -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 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 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 set_spec set_identifier obj_spec obj_identifier ++%type 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 set_spec set_identifier flowtable_identifier obj_spec obj_identifier + %destructor { handle_free(&$$); } set_spec set_identifier obj_spec obj_identifier + %type family_spec family_spec_explicit chain_policy prio_spec + +@@ -526,6 +529,9 @@ int nft_lex(void *, void *, void *); + %type map_block_alloc map_block + %destructor { set_free($$); } map_block_alloc + ++%type flowtable_block_alloc flowtable_block ++%destructor { flowtable_free($$); } flowtable_block_alloc ++ + %type 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 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 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 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 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 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 */ { $$ = $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 */ { $$ = $-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 0000000000..32b7f96bc5 --- /dev/null +++ b/package/network/utils/nftables/patches/202-src-delete-flowtable.patch @@ -0,0 +1,122 @@ +From: Pablo Neira Ayuso +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 +--- + +--- 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 0000000000..86dfb1d94a --- /dev/null +++ b/package/network/utils/nftables/patches/203-src-flow-offload-support.patch @@ -0,0 +1,191 @@ +From: Pablo Neira Ayuso +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 +--- + +--- 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, ¬rack_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 0000000000..e6dbf8fbec --- /dev/null +++ b/package/network/utils/nftables/patches/204-tests-shell-add-flowtable-tests.patch @@ -0,0 +1,110 @@ +From: Pablo Neira Ayuso +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 +--- + 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 0000000000..dd6faa5740 --- /dev/null +++ b/package/network/utils/nftables/patches/205-doc-nft-document-flowtable.patch @@ -0,0 +1,128 @@ +From: Pablo Neira Ayuso +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 +--- + +--- a/doc/nft.xml ++++ b/doc/nft.xml +@@ -1166,6 +1166,91 @@ filter input iif $int_ifs accept + + + ++ Flowtables ++ ++ ++ ++ add ++ create ++ ++ flowtable ++ family ++ table ++ flowtable ++ ++ hook hook ++ priority priority ; ++ devices = { device[,...] } ; ++ ++ ++ ++ ++ delete ++ list ++ ++ flowtable ++ family ++ table ++ flowtable ++ ++ ++ ++ ++ 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 flow offload ++ expression from the forward chain. Flowtables are ++ identified by their address family and their name. The address family ++ must be one of ++ ++ ++ ip ++ ip6 ++ inet ++ . ++ ++ The inet address family is a dummy family which is used to create ++ hybrid IPv4/IPv6 tables. ++ ++ When no address family is specified, ip is used by default. ++ ++ ++ ++ ++ ++ ++ ++ Add a new flowtable for the given family with the given name. ++ ++ ++ ++ ++ ++ ++ ++ Delete the specified flowtable. ++ ++ ++ ++ ++ ++ ++ ++ List all flowtables. ++ ++ ++ ++ ++ ++ ++ + Stateful objects + + +@@ -4923,6 +5008,24 @@ add rule nat prerouting tcp dport 22 red + + + ++ ++ ++ Flow offload statement ++ ++ 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. ++ ++ ++ ++ flow offload ++ @flowtable ++ ++ ++ ++ ++ + + Queue statement +