]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
tunnel: add geneve support
authorPablo Neira Ayuso <pablo@netfilter.org>
Thu, 21 Aug 2025 09:13:00 +0000 (11:13 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 27 Aug 2025 21:51:10 +0000 (23:51 +0200)
This patch extends the tunnel metadata object to define geneve tunnel
specific configurations:

table netdev x {
tunnel y {
id 10
ip saddr 192.168.2.10
ip daddr 192.168.2.11
sport 10
dport 20
ttl 10
geneve {
class 0x1010 opt-type 0x1 data "0x12345678"
class 0x1020 opt-type 0x2 data "0x87654321"
class 0x2020 opt-type 0x3 data "0x87654321abcdeffe"
}
}
}

Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/rule.h
src/mnl.c
src/netlink.c
src/parser_bison.y
src/rule.c
src/scanner.l

index c52af2c49156fc03cb1b96893b90ad414670e308..498a88bf63a66dd6fc0d509535d41debd09835b0 100644 (file)
@@ -496,6 +496,15 @@ enum tunnel_type {
        TUNNEL_UNSPEC = 0,
        TUNNEL_ERSPAN,
        TUNNEL_VXLAN,
+       TUNNEL_GENEVE,
+};
+
+struct tunnel_geneve {
+       struct list_head        list;
+       uint16_t                geneve_class;
+       uint8_t                 type;
+       uint8_t                 data[NFTNL_TUNNEL_GENEVE_DATA_MAXLEN];
+       uint32_t                data_len;
 };
 
 struct tunnel {
@@ -521,9 +530,14 @@ struct tunnel {
                struct {
                        uint32_t        gbp;
                } vxlan;
+               struct list_head        geneve_opts;
        };
 };
 
+int tunnel_geneve_data_str2array(const char *hexstr,
+                                uint8_t *out_data,
+                                uint32_t *out_len);
+
 /**
  * struct obj - nftables stateful object statement
  *
index 0949a694b0b64ec8df1f9b8c977eccbdf0da3d9e..273cefe84e833b90efc75edd5d411c23f7ab9d2f 100644 (file)
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1532,6 +1532,31 @@ static void obj_tunnel_add_opts(struct nftnl_obj *nlo, struct tunnel *tunnel)
                nftnl_tunnel_opts_add(opts, opt);
                nftnl_obj_set_data(nlo, NFTNL_OBJ_TUNNEL_OPTS, &opts, sizeof(struct nftnl_tunnel_opts *));
                break;
+       case TUNNEL_GENEVE:
+               struct tunnel_geneve *geneve;
+
+               opts = nftnl_tunnel_opts_alloc(NFTNL_TUNNEL_TYPE_GENEVE);
+               if (!opts)
+                       memory_allocation_error();
+
+               list_for_each_entry(geneve, &tunnel->geneve_opts, list) {
+                       opt = nftnl_tunnel_opt_alloc(NFTNL_TUNNEL_TYPE_GENEVE);
+                       if (!opt)
+                               memory_allocation_error();
+
+                       nftnl_tunnel_opt_set(opt,
+                                            NFTNL_TUNNEL_GENEVE_TYPE,
+                                            &geneve->type, sizeof(geneve->type));
+                       nftnl_tunnel_opt_set(opt,
+                                            NFTNL_TUNNEL_GENEVE_CLASS,
+                                            &geneve->geneve_class, sizeof(geneve->geneve_class));
+                       nftnl_tunnel_opt_set(opt,
+                                            NFTNL_TUNNEL_GENEVE_DATA,
+                                            &geneve->data, geneve->data_len);
+                       nftnl_tunnel_opts_add(opts, opt);
+               }
+               nftnl_obj_set_data(nlo, NFTNL_OBJ_TUNNEL_OPTS, &opts, sizeof(struct nftnl_tunnel_opts *));
+               break;
        case TUNNEL_UNSPEC:
                break;
        }
index e132362bc93ffdcfea2405fb42523c9a51164b44..5bae3b82997443639b1f786f78f2a19752ad555b 100644 (file)
@@ -1843,6 +1843,35 @@ static int tunnel_parse_opt_cb(struct nftnl_tunnel_opt *opt, void *data) {
                        obj->tunnel.vxlan.gbp = nftnl_tunnel_opt_get_u32(opt, NFTNL_TUNNEL_VXLAN_GBP);
                }
                break;
+       case NFTNL_TUNNEL_TYPE_GENEVE:
+               struct tunnel_geneve *geneve;
+               const void *data;
+
+               if (!obj->tunnel.type) {
+                       init_list_head(&obj->tunnel.geneve_opts);
+                       obj->tunnel.type = TUNNEL_GENEVE;
+               }
+
+               geneve = xmalloc(sizeof(struct tunnel_geneve));
+               if (!geneve)
+                       memory_allocation_error();
+
+               if (nftnl_tunnel_opt_get_flags(opt) & (1 << NFTNL_TUNNEL_GENEVE_TYPE))
+                       geneve->type = nftnl_tunnel_opt_get_u8(opt, NFTNL_TUNNEL_GENEVE_TYPE);
+
+               if (nftnl_tunnel_opt_get_flags(opt) & (1 << NFTNL_TUNNEL_GENEVE_CLASS))
+                       geneve->geneve_class = nftnl_tunnel_opt_get_u16(opt, NFTNL_TUNNEL_GENEVE_CLASS);
+
+               if (nftnl_tunnel_opt_get_flags(opt) & (1 << NFTNL_TUNNEL_GENEVE_DATA)) {
+                       data = nftnl_tunnel_opt_get_data(opt, NFTNL_TUNNEL_GENEVE_DATA,
+                                                        &geneve->data_len);
+                       if (!data)
+                               return -1;
+                       memcpy(&geneve->data, data, geneve->data_len);
+               }
+
+               list_add_tail(&geneve->list, &obj->tunnel.geneve_opts);
+               break;
        default:
                break;
        }
index ca93a658a7a6825f1d83fe4112a9f5060376332b..13eb6027b8e8a2d429f15c35da1f24fda83872a6 100644 (file)
@@ -613,6 +613,8 @@ int nft_lex(void *, void *, void *);
 %token EGRESS                  "egress"
 %token INGRESS                 "ingress"
 %token GBP                     "gbp"
+%token CLASS                   "class"
+%token OPTTYPE                 "opt-type"
 
 %token COUNTERS                        "counters"
 %token QUOTAS                  "quotas"
@@ -771,7 +773,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 synproxy_block tunnel_block erspan_block erspan_block_alloc vxlan_block vxlan_block_alloc
+%type <obj>                    obj_block_alloc counter_block quota_block ct_helper_block ct_timeout_block ct_expect_block limit_block secmark_block synproxy_block tunnel_block erspan_block erspan_block_alloc vxlan_block vxlan_block_alloc geneve_block geneve_block_alloc
 %destructor { obj_free($$); }  obj_block_alloc
 
 %type <list>                   stmt_list stateful_stmt_list set_elem_stmt_list
@@ -5012,6 +5014,44 @@ erspan_config            :       HDRVERSION      NUM
                        }
                        ;
 
+geneve_block           :       /* empty */     { $$ = $<obj>-1; }
+                       |       geneve_block    common_block
+                       |       geneve_block    stmt_separator
+                       |       geneve_block    geneve_config   stmt_separator
+                       {
+                               $$ = $1;
+                       }
+                       ;
+
+geneve_block_alloc     :       /* empty */
+                       {
+                               $$ = $<obj>-1;
+                       }
+                       ;
+
+geneve_config          :       CLASS   NUM     OPTTYPE NUM     DATA    string
+                       {
+                               struct tunnel_geneve *geneve;
+
+                               geneve = xmalloc(sizeof(struct tunnel_geneve));
+                               geneve->geneve_class = $2;
+                               geneve->type = $4;
+                               if (tunnel_geneve_data_str2array($6, geneve->data, &geneve->data_len)) {
+                                       erec_queue(error(&@6, "Invalid data array %s\n", $6), state->msgs);
+                                       free_const($6);
+                                       free(geneve);
+                                       YYERROR;
+                               }
+
+                               if (!$<obj>0->tunnel.type) {
+                                       $<obj>0->tunnel.type = TUNNEL_GENEVE;
+                                       init_list_head(&$<obj>0->tunnel.geneve_opts);
+                               }
+                               list_add_tail(&geneve->list, &$<obj>0->tunnel.geneve_opts);
+                               free_const($6);
+                       }
+                       ;
+
 vxlan_block            :       /* empty */     { $$ = $<obj>-1; }
                        |       vxlan_block     common_block
                        |       vxlan_block     stmt_separator
@@ -5081,6 +5121,7 @@ tunnel_config             :       ID      NUM
                        {
                                $<obj>0->tunnel.type = TUNNEL_VXLAN;
                        }
+                       |       GENEVE  geneve_block_alloc '{' geneve_block '}'
                        ;
 
 tunnel_block           :       /* empty */     { $$ = $<obj>-1; }
index 0450851c6501a5802406544e66b90b6cf81e3bc3..e6216bcabf4072f351fbac9ebfe73aeca66da48e 100644 (file)
@@ -1707,6 +1707,14 @@ void obj_free(struct obj *obj)
        case NFT_OBJECT_TUNNEL:
                expr_free(obj->tunnel.src);
                expr_free(obj->tunnel.dst);
+               if (obj->tunnel.type == TUNNEL_GENEVE) {
+                       struct tunnel_geneve *geneve, *next;
+
+                       list_for_each_entry_safe(geneve, next, &obj->tunnel.geneve_opts, list) {
+                               list_del(&geneve->list);
+                               free(geneve);
+                       }
+               }
                break;
        default:
                break;
@@ -1787,6 +1795,44 @@ static const char *synproxy_timestamp_to_str(const uint32_t flags)
         return "";
 }
 
+int tunnel_geneve_data_str2array(const char *hexstr,
+                                uint8_t *out_data,
+                                uint32_t *out_len)
+{
+       char bytestr[3] = {0};
+       size_t len;
+
+       if (hexstr[0] == '0' && (hexstr[1] == 'x' || hexstr[1] == 'X'))
+               hexstr += 2;
+       else
+               return -1;
+
+       len = strlen(hexstr);
+       if (len % 4 != 0)
+               return -1;
+
+       len = len / 2;
+       if (len > NFTNL_TUNNEL_GENEVE_DATA_MAXLEN)
+               return -1;
+
+       for (size_t i = 0; i < len; i++) {
+               uint32_t value;
+               char *endptr;
+
+               bytestr[0] = hexstr[i * 2];
+               bytestr[1] = hexstr[i * 2 + 1];
+
+               value = strtoul(bytestr, &endptr, 16);
+               if (*endptr != '\0')
+                       return -1;
+
+               out_data[i] = (uint8_t) value;
+       }
+       *out_len = (uint8_t) len;
+
+       return 0;
+}
+
 static void obj_print_comment(const struct obj *obj,
                              struct print_fmt_options *opts,
                              struct output_ctx *octx)
@@ -2053,6 +2099,27 @@ static void obj_print_data(const struct obj *obj,
                        nft_print(octx, "%s%s%s}",
                                  opts->nl, opts->tab, opts->tab);
                        break;
+               case TUNNEL_GENEVE:
+                       struct tunnel_geneve *geneve;
+
+                       nft_print(octx, "%s%s%sgeneve {", opts->nl, opts->tab, opts->tab);
+                       list_for_each_entry(geneve, &obj->tunnel.geneve_opts, list) {
+                               char data_str[256];
+                               int offset = 0;
+
+                               for (uint32_t i = 0; i < geneve->data_len; i++) {
+                                       offset += snprintf(data_str + offset,
+                                                          geneve->data_len,
+                                                          "%x",
+                                                          geneve->data[i]);
+                               }
+                               nft_print(octx, "%s%s%s%sclass 0x%x opt-type 0x%x data \"0x%s\"",
+                                         opts->nl, opts->tab, opts->tab, opts->tab,
+                                         geneve->geneve_class, geneve->type, data_str);
+
+                       }
+                       nft_print(octx, "%s%s%s}", opts->nl, opts->tab, opts->tab);
+                       break;
                default:
                        break;
                }
index 74ebca3b133f05620cb52a6b9700b6e9e139f157..8085c93b903284029c9d6929453be0f022470a75 100644 (file)
@@ -828,6 +828,9 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
        "ingress"               { return INGRESS; }
        "path"                  { return PATH; }
        "gbp"                   { return GBP; }
+       "class"                 { return CLASS; }
+       "opt-type"              { return OPTTYPE; }
+       "data"                  { return DATA; }
 }
 
 "notrack"              { return NOTRACK; }