This patch adds support to attach tunnel metadata.

Signed-off-by: Pablo Neira Ayuso <[email protected]>
---
 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

Reply via email to