This patch adds support to attach tunnel metadata. Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> --- include/rule.h | 15 +++++++++++ src/evaluate.c | 23 ++++++++++++++++ src/mnl.c | 38 +++++++++++++++++++++++++++ src/netlink.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/parser_bison.y | 50 ++++++++++++++++++++++++++++++++--- src/rule.c | 21 +++++++++++++++ src/scanner.l | 4 ++- 7 files changed, 224 insertions(+), 4 deletions(-) diff --git a/include/rule.h b/include/rule.h index ee881b9ccd17..00c16e39e980 100644 --- a/include/rule.h +++ b/include/rule.h @@ -406,6 +406,18 @@ struct secmark { char ctx[NFT_SECMARK_CTX_MAXLEN]; }; +struct tunnel { + const char *type; + uint32_t id; + struct expr *src; + struct expr *dst; + uint32_t label; + uint16_t sport; + uint16_t dport; + uint8_t tos; + uint8_t ttl; +}; + /** * struct obj - nftables stateful object statement * @@ -426,6 +438,7 @@ struct obj { struct quota quota; struct ct_helper ct_helper; struct limit limit; + struct tunnel tunnel; struct ct_timeout ct_timeout; struct secmark secmark; struct ct_expect ct_expect; @@ -558,6 +571,8 @@ enum cmd_obj { CMD_OBJ_CT_HELPERS, CMD_OBJ_LIMIT, CMD_OBJ_LIMITS, + CMD_OBJ_TUNNEL, + CMD_OBJ_TUNNELS, CMD_OBJ_FLOWTABLE, CMD_OBJ_FLOWTABLES, CMD_OBJ_CT_TIMEOUT, diff --git a/src/evaluate.c b/src/evaluate.c index 48c65cd2f35a..0c2a8d0a9571 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -3520,9 +3520,31 @@ static int ct_timeout_evaluate(struct eval_ctx *ctx, struct obj *obj) return 0; } +static int tunnel_evaluate(struct eval_ctx *ctx, struct obj *obj) +{ + if (obj->tunnel.src != NULL) { + __expr_set_context(&ctx->ectx, &ipaddr_type, + BYTEORDER_BIG_ENDIAN, + sizeof(struct in_addr) * BITS_PER_BYTE, 0); + if (expr_evaluate(ctx, &obj->tunnel.src) < 0) + return -1; + } + if (obj->tunnel.dst != NULL) { + __expr_set_context(&ctx->ectx, &ipaddr_type, + BYTEORDER_BIG_ENDIAN, + sizeof(struct in_addr) * BITS_PER_BYTE, 0); + if (expr_evaluate(ctx, &obj->tunnel.dst) < 0) + return -1; + } + + return 0; +} + static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj) { switch (obj->type) { + case NFT_OBJECT_TUNNEL: + return tunnel_evaluate(ctx, obj); case NFT_OBJECT_CT_TIMEOUT: return ct_timeout_evaluate(ctx, obj); case NFT_OBJECT_CT_EXPECT: @@ -3604,6 +3626,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) case CMD_OBJ_QUOTA: case CMD_OBJ_CT_HELPER: case CMD_OBJ_LIMIT: + case CMD_OBJ_TUNNEL: case CMD_OBJ_CT_TIMEOUT: case CMD_OBJ_SECMARK: case CMD_OBJ_CT_EXPECT: diff --git a/src/mnl.c b/src/mnl.c index eab8d5486437..e9021e4515ae 100644 --- a/src/mnl.c +++ b/src/mnl.c @@ -951,6 +951,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, const struct cmd *cmd, unsigned int flags) { struct obj *obj = cmd->object; + struct nft_data_linearize nld; struct nftnl_obj *nlo; struct nlmsghdr *nlh; @@ -985,6 +986,43 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, const struct cmd *cmd, nftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_TYPE, obj->limit.type); nftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_FLAGS, obj->limit.flags); break; + case NFT_OBJECT_TUNNEL: + nftnl_obj_set_u64(nlo, NFTNL_OBJ_TUNNEL_ID, obj->tunnel.id); + if (obj->tunnel.sport) + nftnl_obj_set_u16(nlo, NFTNL_OBJ_TUNNEL_SPORT, + obj->tunnel.sport); + if (obj->tunnel.dport) + nftnl_obj_set_u16(nlo, NFTNL_OBJ_TUNNEL_DPORT, + obj->tunnel.dport); + if (obj->tunnel.tos) + nftnl_obj_set_u16(nlo, NFTNL_OBJ_TUNNEL_TOS, + obj->tunnel.tos); + if (obj->tunnel.ttl) + nftnl_obj_set_u16(nlo, NFTNL_OBJ_TUNNEL_TTL, + obj->tunnel.ttl); + if (obj->tunnel.src) { + netlink_gen_data(obj->tunnel.src, &nld); + if (nld.len == sizeof(struct in_addr)) { + nftnl_obj_set_u32(nlo, + NFTNL_OBJ_TUNNEL_IPV4_SRC, + nld.value[0]); + } + } else { + nftnl_obj_set(nlo, NFTNL_OBJ_TUNNEL_IPV6_SRC, + nld.value); + } + if (obj->tunnel.dst) { + netlink_gen_data(obj->tunnel.dst, &nld); + if (nld.len == sizeof(struct in_addr)) { + nftnl_obj_set_u32(nlo, + NFTNL_OBJ_TUNNEL_IPV4_DST, + nld.value[0]); + } else { + nftnl_obj_set(nlo, NFTNL_OBJ_TUNNEL_IPV6_DST, + nld.value); + } + } + break; case NFT_OBJECT_CT_HELPER: nftnl_obj_set_str(nlo, NFTNL_OBJ_CT_HELPER_NAME, obj->ct_helper.name); diff --git a/src/netlink.c b/src/netlink.c index 14b0df410726..bcb5ecc6bb32 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -936,6 +936,51 @@ void netlink_dump_obj(struct nftnl_obj *nln, struct netlink_ctx *ctx) fprintf(fp, "\n"); } +static struct in6_addr all_zeroes; + +static struct expr *netlink_obj_tunnel_parse_addr(struct nftnl_obj *nlo, + int attr) +{ + struct nft_data_delinearize nld; + const struct datatype *dtype; + const uint32_t *addr6; + struct expr *expr; + uint32_t addr; + + memset(&nld, 0, sizeof(nld)); + + switch (attr) { + case NFTNL_OBJ_TUNNEL_IPV4_SRC: + case NFTNL_OBJ_TUNNEL_IPV4_DST: + addr = nftnl_obj_get_u32(nlo, attr); + if (!addr) + return NULL; + + dtype = &ipaddr_type; + nld.value = &addr; + nld.len = sizeof(struct in_addr); + break; + case NFTNL_OBJ_TUNNEL_IPV6_SRC: + case NFTNL_OBJ_TUNNEL_IPV6_DST: + addr6 = nftnl_obj_get(nlo, attr); + if (!memcmp(addr6, &all_zeroes, sizeof(all_zeroes))) + return NULL; + + dtype = &ip6addr_type; + nld.value = addr6; + nld.len = sizeof(struct in6_addr); + break; + default: + return NULL; + } + + expr = netlink_alloc_value(&netlink_location, &nld); + expr->dtype = dtype; + expr->byteorder = BYTEORDER_BIG_ENDIAN; + + return expr; +} + struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, struct nftnl_obj *nlo) { @@ -1008,6 +1053,38 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, obj->ct_expect.size = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_EXPECT_SIZE); break; + case NFT_OBJECT_TUNNEL: + obj->tunnel.id = nftnl_obj_get_u64(nlo, NFTNL_OBJ_TUNNEL_ID); + obj->tunnel.sport = + nftnl_obj_get_u16(nlo, NFTNL_OBJ_TUNNEL_SPORT); + obj->tunnel.dport = + nftnl_obj_get_u16(nlo, NFTNL_OBJ_TUNNEL_DPORT); + obj->tunnel.tos = + nftnl_obj_get_u16(nlo, NFTNL_OBJ_TUNNEL_TOS) >> 2; + obj->tunnel.ttl = + nftnl_obj_get_u16(nlo, NFTNL_OBJ_TUNNEL_TTL); + + if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_IPV4_SRC)) { + obj->tunnel.src = + netlink_obj_tunnel_parse_addr(nlo, + NFTNL_OBJ_TUNNEL_IPV4_SRC); + } + if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_IPV4_DST)) { + obj->tunnel.dst = + netlink_obj_tunnel_parse_addr(nlo, + NFTNL_OBJ_TUNNEL_IPV4_DST); + } + if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_IPV6_SRC)) { + obj->tunnel.src = + netlink_obj_tunnel_parse_addr(nlo, + NFTNL_OBJ_TUNNEL_IPV6_SRC); + } + if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_IPV6_DST)) { + obj->tunnel.dst = + netlink_obj_tunnel_parse_addr(nlo, + NFTNL_OBJ_TUNNEL_IPV6_DST); + } + break; } obj->type = type; diff --git a/src/parser_bison.y b/src/parser_bison.y index 53e669521efa..d3b64b641700 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -459,6 +459,7 @@ int nft_lex(void *, void *, void *); %token COUNTERS "counters" %token QUOTAS "quotas" %token LIMITS "limits" +%token TUNNELS "tunnels" %token HELPERS "helpers" %token LOG "log" @@ -589,7 +590,7 @@ int nft_lex(void *, void *, void *); %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 ct_timeout_block ct_expect_block limit_block secmark_block +%type <obj> obj_block_alloc counter_block quota_block ct_helper_block ct_timeout_block ct_expect_block tunnel_block limit_block secmark_block %destructor { obj_free($$); } obj_block_alloc %type <list> stmt_list @@ -697,8 +698,8 @@ int nft_lex(void *, void *, void *); %type <expr> and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr %destructor { expr_free($$); } and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr -%type <obj> counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj -%destructor { obj_free($$); } counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj +%type <obj> counter_obj quota_obj ct_obj_alloc limit_obj tunnel_obj secmark_obj +%destructor { obj_free($$); } counter_obj quota_obj ct_obj_alloc limit_obj tunnel_obj secmark_obj %type <expr> relational_expr %destructor { expr_free($$); } relational_expr @@ -1009,6 +1010,10 @@ add_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3); } + | TUNNEL obj_spec tunnel_obj '{' tunnel_block '}' stmt_separator + { + $$ = cmd_alloc_obj_ct(CMD_ADD, CMD_OBJ_TUNNEL, &$2, &@$, $3); + } ; replace_cmd : RULE ruleid_spec rule @@ -1102,6 +1107,10 @@ create_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3); } + | TUNNEL obj_spec tunnel_obj '{' tunnel_block '}' stmt_separator + { + $$ = cmd_alloc_obj_ct(CMD_CREATE, CMD_OBJ_TUNNEL, &$2, &@$, $3); + } ; insert_cmd : RULE rule_position rule @@ -1589,6 +1598,15 @@ table_block : /* empty */ { $$ = $<table>-1; } list_add_tail(&$4->list, &$1->objs); $$ = $1; } + | table_block TUNNEL obj_identifier obj_block_alloc '{' tunnel_block '}' stmt_separator + { + $4->location = @3; + $4->type = NFT_OBJECT_TUNNEL; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->objs); + $$ = $1; + } ; chain_block_alloc : /* empty */ @@ -3698,6 +3716,32 @@ limit_obj : limit_config } ; +tunnel_config : TYPE string stmt_separator { $<obj>0->tunnel.type = $2; } + | ID NUM stmt_separator { $<obj>0->tunnel.id = $2; } + | IP SADDR expr stmt_separator { $<obj>0->tunnel.src = $3; } + | IP DADDR expr stmt_separator { $<obj>0->tunnel.dst = $3; } + | SPORT NUM stmt_separator { $<obj>0->tunnel.sport = $2; } + | DPORT NUM stmt_separator { $<obj>0->tunnel.dport = $2; } + | DSCP NUM stmt_separator { $<obj>0->tunnel.tos = $2 << 2; } + | TTL NUM stmt_separator { $<obj>0->tunnel.ttl = $2; } + ; + +tunnel_block : /* empty */ { $$ = $<obj>-1; } + | tunnel_block common_block + | tunnel_block stmt_separator + | tunnel_block tunnel_config + { + $$ = $1; + } + ; + +tunnel_obj : + { + $$ = obj_alloc(&@$); + $$->type = NFT_OBJECT_TUNNEL; + } + ; + relational_expr : expr /* implicit */ rhs_expr { $$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2); diff --git a/src/rule.c b/src/rule.c index 293606576044..b99786ba967a 100644 --- a/src/rule.c +++ b/src/rule.c @@ -1442,6 +1442,7 @@ void cmd_free(struct cmd *cmd) case CMD_OBJ_CT_TIMEOUT: case CMD_OBJ_CT_EXPECT: case CMD_OBJ_LIMIT: + case CMD_OBJ_TUNNEL: case CMD_OBJ_SECMARK: obj_free(cmd->object); break; @@ -1533,6 +1534,7 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl) case CMD_OBJ_CT_TIMEOUT: case CMD_OBJ_CT_EXPECT: case CMD_OBJ_LIMIT: + case CMD_OBJ_TUNNEL: case CMD_OBJ_SECMARK: return mnl_nft_obj_add(ctx, cmd, flags); case CMD_OBJ_FLOWTABLE: @@ -1617,6 +1619,8 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd) return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_CT_EXPECT); case CMD_OBJ_LIMIT: return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_TUNNEL: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_TUNNEL); case CMD_OBJ_SECMARK: return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SECMARK); case CMD_OBJ_FLOWTABLE: @@ -1903,6 +1907,18 @@ static void obj_print_data(const struct obj *obj, nft_print(octx, "%s", opts->nl); } break; + case NFT_OBJECT_TUNNEL: + nft_print(octx, " %s {%s", obj->handle.obj.name, opts->nl); + + nft_print(octx, "%s%sid %u%s", + opts->tab, opts->tab, obj->tunnel.id, opts->nl); + + if (obj->tunnel.dst) { + nft_print(octx, "%s%sip daddr ", + opts->tab, opts->tab); + expr_print(obj->tunnel.dst, octx); + } + break; default: nft_print(octx, " unknown {%s", opts->nl); break; @@ -1914,6 +1930,7 @@ static const char * const obj_type_name_array[] = { [NFT_OBJECT_QUOTA] = "quota", [NFT_OBJECT_CT_HELPER] = "ct helper", [NFT_OBJECT_LIMIT] = "limit", + [NFT_OBJECT_TUNNEL] = "tunnel", [NFT_OBJECT_CT_TIMEOUT] = "ct timeout", [NFT_OBJECT_SECMARK] = "secmark", [NFT_OBJECT_CT_EXPECT] = "ct expectation", @@ -1931,6 +1948,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = { [NFT_OBJECT_QUOTA] = CMD_OBJ_QUOTA, [NFT_OBJECT_CT_HELPER] = CMD_OBJ_CT_HELPER, [NFT_OBJECT_LIMIT] = CMD_OBJ_LIMIT, + [NFT_OBJECT_TUNNEL] = CMD_OBJ_TUNNEL, [NFT_OBJECT_CT_TIMEOUT] = CMD_OBJ_CT_TIMEOUT, [NFT_OBJECT_SECMARK] = CMD_OBJ_SECMARK, [NFT_OBJECT_CT_EXPECT] = CMD_OBJ_CT_EXPECT, @@ -2297,6 +2315,9 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_LIMIT: case CMD_OBJ_LIMITS: return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_TUNNEL: + case CMD_OBJ_TUNNELS: + return do_list_obj(ctx, cmd, NFT_OBJECT_TUNNEL); case CMD_OBJ_SECMARK: case CMD_OBJ_SECMARKS: return do_list_obj(ctx, cmd, NFT_OBJECT_SECMARK); diff --git a/src/scanner.l b/src/scanner.l index 4ed5f9241381..f8575638a47b 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -326,6 +326,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "quotas" { return QUOTAS; } "limits" { return LIMITS; } +"tunnel" { return TUNNEL; } +"tunnels" { return TUNNELS; } + "log" { return LOG; } "prefix" { return PREFIX; } "group" { return GROUP; } @@ -577,7 +580,6 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "reqid" { return REQID; } "spnum" { return SPNUM; } "transport" { return TRANSPORT; } -"tunnel" { return TUNNEL; } "in" { return IN; } "out" { return OUT; } -- 2.11.0