1 From: Pablo Neira Ayuso <pablo@netfilter.org>
2 Date: Thu, 18 Jan 2018 08:43:23 +0100
3 Subject: [PATCH] src: add support to add flowtables
5 This patch allows you to create flowtable:
8 # nft add flowtable x m { hook ingress priority 10\; devices = { eth0, wlan0 }\; }
10 You have to specify hook and priority. So far, only the ingress hook is
11 supported. The priority represents where this flowtable is placed in the
12 ingress hook, which is registered to the devices that the user
15 You can also use the 'create' command instead to bail out in case that
16 there is an existing flowtable with this name.
18 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
21 --- a/include/expression.h
22 +++ b/include/expression.h
23 @@ -407,6 +407,8 @@ extern struct expr *prefix_expr_alloc(co
24 extern struct expr *range_expr_alloc(const struct location *loc,
25 struct expr *low, struct expr *high);
27 +extern struct expr *compound_expr_alloc(const struct location *loc,
28 + const struct expr_ops *ops);
29 extern void compound_expr_add(struct expr *compound, struct expr *expr);
30 extern void compound_expr_remove(struct expr *compound, struct expr *expr);
31 extern void list_expr_sort(struct list_head *head);
34 @@ -92,6 +92,10 @@ int mnl_nft_obj_batch_del(struct nftnl_o
35 struct nftnl_flowtable_list *
36 mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table);
38 +int mnl_nft_flowtable_batch_add(struct nftnl_flowtable *flo,
39 + struct nftnl_batch *batch, unsigned int flags,
42 struct nftnl_ruleset *mnl_nft_ruleset_dump(struct netlink_ctx *ctx,
44 int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
45 --- a/include/netlink.h
46 +++ b/include/netlink.h
48 #include <libnftnl/expr.h>
49 #include <libnftnl/set.h>
50 #include <libnftnl/object.h>
51 +#include <libnftnl/flowtable.h>
53 #include <linux/netlink.h>
54 #include <linux/netfilter/nf_tables.h>
55 @@ -182,6 +183,9 @@ extern int netlink_delete_obj(struct net
56 extern int netlink_list_flowtables(struct netlink_ctx *ctx,
57 const struct handle *h,
58 const struct location *loc);
59 +extern int netlink_add_flowtable(struct netlink_ctx *ctx,
60 + const struct handle *h, struct flowtable *ft,
63 extern void netlink_dump_chain(const struct nftnl_chain *nlc,
64 struct netlink_ctx *ctx);
67 @@ -322,10 +322,13 @@ uint32_t obj_type_to_cmd(uint32_t type);
69 struct list_head list;
72 struct location location;
73 + const char * hookstr;
76 const char **dev_array;
77 + struct expr *dev_expr;
81 @@ -383,6 +386,8 @@ enum cmd_ops {
82 * @CMD_OBJ_CHAIN: chain
83 * @CMD_OBJ_CHAINS: multiple chains
84 * @CMD_OBJ_TABLE: table
85 + * @CMD_OBJ_FLOWTABLE: flowtable
86 + * @CMD_OBJ_FLOWTABLES: flowtables
87 * @CMD_OBJ_RULESET: ruleset
88 * @CMD_OBJ_EXPR: expression
89 * @CMD_OBJ_MONITOR: monitor
90 @@ -422,6 +427,7 @@ enum cmd_obj {
98 @@ -481,6 +487,7 @@ struct cmd {
102 + struct flowtable *flowtable;
103 struct monitor *monitor;
104 struct markup *markup;
108 @@ -2897,6 +2897,24 @@ static int set_evaluate(struct eval_ctx
112 +static uint32_t str2hooknum(uint32_t family, const char *hook);
114 +static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
116 + struct table *table;
118 + table = table_lookup_global(ctx);
120 + return cmd_error(ctx, "Could not process rule: Table '%s' does not exist",
121 + ctx->cmd->handle.table);
123 + ft->hooknum = str2hooknum(NFPROTO_NETDEV, ft->hookstr);
124 + if (ft->hooknum == NF_INET_NUMHOOKS)
125 + return chain_error(ctx, ft, "invalid hook %s", ft->hookstr);
130 static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule)
132 struct stmt *stmt, *tstmt = NULL;
133 @@ -3069,6 +3087,14 @@ static int cmd_evaluate_add(struct eval_
134 return chain_evaluate(ctx, cmd->chain);
136 return table_evaluate(ctx, cmd->table);
137 + case CMD_OBJ_FLOWTABLE:
138 + ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op,
139 + ctx->msgs, ctx->debug_mask & NFT_DEBUG_NETLINK, ctx->octx);
143 + handle_merge(&cmd->flowtable->handle, &cmd->handle);
144 + return flowtable_evaluate(ctx, cmd->flowtable);
145 case CMD_OBJ_COUNTER:
147 case CMD_OBJ_CT_HELPER:
148 --- a/src/expression.c
149 +++ b/src/expression.c
150 @@ -663,8 +663,8 @@ struct expr *range_expr_alloc(const stru
154 -static struct expr *compound_expr_alloc(const struct location *loc,
155 - const struct expr_ops *ops)
156 +struct expr *compound_expr_alloc(const struct location *loc,
157 + const struct expr_ops *ops)
163 @@ -1011,6 +1011,22 @@ err:
167 +int mnl_nft_flowtable_batch_add(struct nftnl_flowtable *flo,
168 + struct nftnl_batch *batch, unsigned int flags,
171 + struct nlmsghdr *nlh;
173 + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch),
174 + NFT_MSG_NEWFLOWTABLE,
175 + nftnl_flowtable_get_u32(flo, NFTNL_FLOWTABLE_FAMILY),
176 + NLM_F_CREATE | flags, seqnum);
177 + nftnl_flowtable_nlmsg_build_payload(nlh, flo);
178 + mnl_nft_batch_continue(batch);
188 @@ -1773,6 +1773,64 @@ static struct obj *netlink_delinearize_o
192 +static struct nftnl_flowtable *alloc_nftnl_flowtable(const struct handle *h,
193 + const struct flowtable *ft)
195 + struct nftnl_flowtable *flo;
197 + flo = nftnl_flowtable_alloc();
199 + memory_allocation_error();
201 + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY, h->family);
202 + nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_TABLE, h->table);
203 + if (h->flowtable != NULL)
204 + nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_NAME, h->flowtable);
209 +static void netlink_dump_flowtable(struct nftnl_flowtable *flo,
210 + struct netlink_ctx *ctx)
212 + FILE *fp = ctx->octx->output_fp;
214 + if (!(ctx->debug_mask & NFT_DEBUG_NETLINK) || !fp)
217 + nftnl_flowtable_fprintf(fp, flo, 0, 0);
221 +int netlink_add_flowtable(struct netlink_ctx *ctx, const struct handle *h,
222 + struct flowtable *ft, uint32_t flags)
224 + struct nftnl_flowtable *flo;
225 + const char *dev_array[8];
229 + flo = alloc_nftnl_flowtable(h, ft);
230 + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, ft->hooknum);
231 + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, ft->priority);
233 + list_for_each_entry(expr, &ft->dev_expr->expressions, list)
234 + dev_array[i++] = expr->identifier;
236 + dev_array[i] = NULL;
237 + nftnl_flowtable_set_array(flo, NFTNL_FLOWTABLE_DEVICES, dev_array);
239 + netlink_dump_flowtable(flo, ctx);
241 + err = mnl_nft_flowtable_batch_add(flo, ctx->batch, flags, ctx->seqnum);
243 + netlink_io_error(ctx, &ft->location, "Could not add flowtable: %s",
245 + nftnl_flowtable_free(flo);
250 static int list_obj_cb(struct nftnl_obj *nls, void *arg)
252 struct netlink_ctx *ctx = arg;
253 --- a/src/parser_bison.y
254 +++ b/src/parser_bison.y
255 @@ -145,6 +145,7 @@ int nft_lex(void *, void *, void *);
259 + struct flowtable *flowtable;
260 struct counter *counter;
263 @@ -189,6 +190,7 @@ int nft_lex(void *, void *, void *);
266 %token DEVICE "device"
267 +%token DEVICES "devices"
269 %token TABLES "tables"
271 @@ -200,6 +202,7 @@ int nft_lex(void *, void *, void *);
272 %token ELEMENT "element"
275 +%token FLOWTABLE "flowtable"
276 %token HANDLE "handle"
277 %token RULESET "ruleset"
279 @@ -500,9 +503,9 @@ int nft_lex(void *, void *, void *);
280 %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
281 %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
283 -%type <handle> table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
284 -%destructor { handle_free(&$$); } table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
285 -%type <handle> set_spec set_identifier obj_spec obj_identifier
286 +%type <handle> table_spec chain_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
287 +%destructor { handle_free(&$$); } table_spec chain_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
288 +%type <handle> set_spec set_identifier flowtable_identifier obj_spec obj_identifier
289 %destructor { handle_free(&$$); } set_spec set_identifier obj_spec obj_identifier
290 %type <val> family_spec family_spec_explicit chain_policy prio_spec
292 @@ -526,6 +529,9 @@ int nft_lex(void *, void *, void *);
293 %type <set> map_block_alloc map_block
294 %destructor { set_free($$); } map_block_alloc
296 +%type <flowtable> flowtable_block_alloc flowtable_block
297 +%destructor { flowtable_free($$); } flowtable_block_alloc
299 %type <obj> obj_block_alloc counter_block quota_block ct_helper_block limit_block
300 %destructor { obj_free($$); } obj_block_alloc
302 @@ -606,8 +612,8 @@ int nft_lex(void *, void *, void *);
303 %type <expr> verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr
304 %destructor { expr_free($$); } verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr
306 -%type <expr> set_expr set_block_expr set_list_expr set_list_member_expr
307 -%destructor { expr_free($$); } set_expr set_block_expr set_list_expr set_list_member_expr
308 +%type <expr> set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member
309 +%destructor { expr_free($$); } set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member
310 %type <expr> set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
311 %destructor { expr_free($$); } set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
312 %type <expr> set_elem_expr_stmt set_elem_expr_stmt_alloc
313 @@ -872,6 +878,13 @@ add_cmd : TABLE table_spec
315 $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEM, &$2, &@$, $3);
317 + | FLOWTABLE flowtable_spec flowtable_block_alloc
318 + '{' flowtable_block '}'
321 + handle_merge(&$3->handle, &$2);
322 + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
327 @@ -947,6 +960,13 @@ create_cmd : TABLE table_spec
329 $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SETELEM, &$2, &@$, $3);
331 + | FLOWTABLE flowtable_spec flowtable_block_alloc
332 + '{' flowtable_block '}'
335 + handle_merge(&$3->handle, &$2);
336 + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
341 @@ -1317,6 +1337,17 @@ table_block : /* empty */ { $$ = $<tabl
342 list_add_tail(&$4->list, &$1->sets);
346 + | table_block FLOWTABLE flowtable_identifier
347 + flowtable_block_alloc '{' flowtable_block '}'
351 + handle_merge(&$4->handle, &$3);
353 + list_add_tail(&$4->list, &$1->flowtables);
356 | table_block COUNTER obj_identifier
357 obj_block_alloc '{' counter_block '}'
359 @@ -1512,6 +1543,62 @@ set_policy_spec : PERFORMANCE { $$ = NF
360 | MEMORY { $$ = NFT_SET_POL_MEMORY; }
363 +flowtable_block_alloc : /* empty */
365 + $$ = flowtable_alloc(NULL);
369 +flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
370 + | flowtable_block common_block
371 + | flowtable_block stmt_separator
372 + | flowtable_block HOOK STRING PRIORITY prio_spec stmt_separator
374 + $$->hookstr = chain_hookname_lookup($3);
375 + if ($$->hookstr == NULL) {
376 + erec_queue(error(&@3, "unknown chain hook %s", $3),
385 + | flowtable_block DEVICES '=' flowtable_expr stmt_separator
391 +flowtable_expr : '{' flowtable_list_expr '}'
398 +flowtable_list_expr : flowtable_expr_member
400 + $$ = compound_expr_alloc(&@$, NULL);
401 + compound_expr_add($$, $1);
403 + | flowtable_list_expr COMMA flowtable_expr_member
405 + compound_expr_add($1, $3);
408 + | flowtable_list_expr COMMA opt_newline
411 +flowtable_expr_member : STRING
413 + $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
414 + current_scope(state),
419 data_type_atom_expr : type_identifier
421 const struct datatype *dtype = datatype_lookup_byname($1);
422 @@ -1720,6 +1807,21 @@ set_identifier : identifier
427 +flowtable_spec : table_spec identifier
434 +flowtable_identifier : identifier
436 + memset(&$$, 0, sizeof($$));
441 obj_spec : table_spec identifier
446 @@ -45,6 +45,8 @@ void handle_merge(struct handle *dst, co
447 dst->chain = xstrdup(src->chain);
448 if (dst->set == NULL && src->set != NULL)
449 dst->set = xstrdup(src->set);
450 + if (dst->flowtable == NULL && src->flowtable != NULL)
451 + dst->flowtable = xstrdup(src->flowtable);
452 if (dst->obj == NULL && src->obj != NULL)
453 dst->obj = xstrdup(src->obj);
454 if (dst->handle.id == 0)
455 @@ -857,6 +859,7 @@ struct cmd *cmd_alloc(enum cmd_ops op, e
456 void nft_cmd_expand(struct cmd *cmd)
458 struct list_head new_cmds;
459 + struct flowtable *ft;
463 @@ -896,6 +899,14 @@ void nft_cmd_expand(struct cmd *cmd)
464 &set->location, set_get(set));
465 list_add_tail(&new->list, &new_cmds);
467 + list_for_each_entry(ft, &table->flowtables, list) {
468 + handle_merge(&ft->handle, &table->handle);
469 + memset(&h, 0, sizeof(h));
470 + handle_merge(&h, &ft->handle);
471 + new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h,
472 + &ft->location, flowtable_get(ft));
473 + list_add_tail(&new->list, &new_cmds);
475 list_for_each_entry(chain, &table->chains, list) {
476 list_for_each_entry(rule, &chain->rules, list) {
477 memset(&h, 0, sizeof(h));
478 @@ -982,6 +993,9 @@ void cmd_free(struct cmd *cmd)
480 obj_free(cmd->object);
482 + case CMD_OBJ_FLOWTABLE:
483 + flowtable_free(cmd->flowtable);
486 BUG("invalid command object type %u\n", cmd->obj);
488 @@ -1071,6 +1085,9 @@ static int do_command_add(struct netlink
489 case CMD_OBJ_CT_HELPER:
491 return netlink_add_obj(ctx, &cmd->handle, cmd->object, flags);
492 + case CMD_OBJ_FLOWTABLE:
493 + return netlink_add_flowtable(ctx, &cmd->handle, cmd->flowtable,
496 BUG("invalid command object type %u\n", cmd->obj);
500 @@ -238,6 +238,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr
502 "hook" { return HOOK; }
503 "device" { return DEVICE; }
504 +"devices" { return DEVICES; }
505 "table" { return TABLE; }
506 "tables" { return TABLES; }
507 "chain" { return CHAIN; }
508 @@ -249,6 +250,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr
509 "element" { return ELEMENT; }
510 "map" { return MAP; }
511 "maps" { return MAPS; }
512 +"flowtable" { return FLOWTABLE; }
513 "handle" { return HANDLE; }
514 "ruleset" { return RULESET; }
515 "trace" { return TRACE; }