]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
tunnel: add erspan support
authorPablo Neira Ayuso <pablo@netfilter.org>
Thu, 21 Aug 2025 09:12:57 +0000 (11:12 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 27 Aug 2025 21:50:58 +0000 (23:50 +0200)
This patch extends the tunnel metadata object to define erspan 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
                erspan {
                        version 1
                        index 2
                }
        }
 }

Joint work with Fernando.

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 0fa87b5291036b08259abb7892a103caaf0c54dc..71e9a07ebf83c9511b91eec071a10f8215bddb46 100644 (file)
@@ -492,6 +492,11 @@ struct secmark {
        char            ctx[NFT_SECMARK_CTX_MAXLEN];
 };
 
+enum tunnel_type {
+       TUNNEL_UNSPEC = 0,
+       TUNNEL_ERSPAN,
+};
+
 struct tunnel {
        uint32_t        id;
        struct expr     *src;
@@ -500,6 +505,19 @@ struct tunnel {
        uint16_t        dport;
        uint8_t         tos;
        uint8_t         ttl;
+       enum tunnel_type type;
+       union {
+               struct {
+                       uint32_t        version;
+                       struct {
+                               uint32_t        index;
+                       } v1;
+                       struct {
+                               uint8_t         direction;
+                               uint8_t         hwid;
+                       } v2;
+               } erspan;
+       };
 };
 
 /**
index 18f2e25eeda5c43907dba9d7a7e936cc39af694a..4952a19464a82594ec264b59cfdc35c6f9177d2e 100644 (file)
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1479,6 +1479,48 @@ err:
        return NULL;
 }
 
+static void obj_tunnel_add_opts(struct nftnl_obj *nlo, struct tunnel *tunnel)
+{
+       struct nftnl_tunnel_opts *opts;
+       struct nftnl_tunnel_opt *opt;
+
+       switch (tunnel->type) {
+       case TUNNEL_ERSPAN:
+               opts = nftnl_tunnel_opts_alloc(NFTNL_TUNNEL_TYPE_ERSPAN);
+               if (!opts)
+                       memory_allocation_error();
+
+               opt = nftnl_tunnel_opt_alloc(NFTNL_TUNNEL_TYPE_ERSPAN);
+               if (!opt)
+                       memory_allocation_error();
+
+               nftnl_tunnel_opt_set(opt, NFTNL_TUNNEL_ERSPAN_VERSION,
+                                    &tunnel->erspan.version,
+                                    sizeof(tunnel->erspan.version));
+               switch (tunnel->erspan.version) {
+               case 1:
+                       nftnl_tunnel_opt_set(opt, NFTNL_TUNNEL_ERSPAN_V1_INDEX,
+                                            &tunnel->erspan.v1.index,
+                                            sizeof(tunnel->erspan.v1.index));
+                       break;
+               case 2:
+                       nftnl_tunnel_opt_set(opt, NFTNL_TUNNEL_ERSPAN_V2_DIR,
+                                            &tunnel->erspan.v2.direction,
+                                            sizeof(tunnel->erspan.v2.direction));
+                       nftnl_tunnel_opt_set(opt, NFTNL_TUNNEL_ERSPAN_V2_HWID,
+                                            &tunnel->erspan.v2.hwid,
+                                            sizeof(tunnel->erspan.v2.hwid));
+                       break;
+               }
+
+               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;
+       }
+}
+
 int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
                    unsigned int flags)
 {
@@ -1610,6 +1652,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
                                                   nld.value, nld.len);
                        }
                }
+               obj_tunnel_add_opts(nlo, &obj->tunnel);
                break;
        default:
                BUG("Unknown type %d\n", obj->type);
index f0a9c02bce4dfd45dda91d444d1578566e5f612f..4ef88402451b1ff2ae08c6e23d8f1547f87b7c11 100644 (file)
@@ -1808,6 +1808,41 @@ static int obj_parse_udata_cb(const struct nftnl_udata *attr, void *data)
        return 0;
 }
 
+static int tunnel_parse_opt_cb(struct nftnl_tunnel_opt *opt, void *data) {
+
+       struct obj *obj = data;
+
+       switch (nftnl_tunnel_opt_get_type(opt)) {
+       case NFTNL_TUNNEL_TYPE_ERSPAN:
+               obj->tunnel.type = TUNNEL_ERSPAN;
+               if (nftnl_tunnel_opt_get_flags(opt) & (1 << NFTNL_TUNNEL_ERSPAN_VERSION)) {
+                       obj->tunnel.erspan.version =
+                               nftnl_tunnel_opt_get_u32(opt,
+                                                        NFTNL_TUNNEL_ERSPAN_VERSION);
+               }
+               if (nftnl_tunnel_opt_get_flags(opt) & (1 << NFTNL_TUNNEL_ERSPAN_V1_INDEX)) {
+                       obj->tunnel.erspan.v1.index =
+                               nftnl_tunnel_opt_get_u32(opt,
+                                                        NFTNL_TUNNEL_ERSPAN_V1_INDEX);
+               }
+               if (nftnl_tunnel_opt_get_flags(opt) & (1 << NFTNL_TUNNEL_ERSPAN_V2_HWID)) {
+                       obj->tunnel.erspan.v2.hwid =
+                               nftnl_tunnel_opt_get_u8(opt,
+                                                       NFTNL_TUNNEL_ERSPAN_V2_HWID);
+               }
+               if (nftnl_tunnel_opt_get_flags(opt) & (1 << NFTNL_TUNNEL_ERSPAN_V2_DIR)) {
+                       obj->tunnel.erspan.v2.direction =
+                               nftnl_tunnel_opt_get_u8(opt,
+                                                       NFTNL_TUNNEL_ERSPAN_V2_DIR);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
                                    struct nftnl_obj *nlo)
 {
@@ -1938,6 +1973,9 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
                        obj->tunnel.dst =
                                netlink_obj_tunnel_parse_addr(nlo, NFTNL_OBJ_TUNNEL_IPV6_DST);
                }
+               if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_OPTS)) {
+                       nftnl_obj_tunnel_opts_foreach(nlo, tunnel_parse_opt_cb, obj);
+               }
                break;
        default:
                netlink_io_error(ctx, NULL, "Unknown object type %u", type);
index 367c6b3b87ce7210dbcb76f7f6b4c2b6e6a5c469..557977e2ed7a8057f652cd1078deed1d689c7972 100644 (file)
@@ -607,6 +607,9 @@ int nft_lex(void *, void *, void *);
 %token NEVER                   "never"
 
 %token TUNNEL                  "tunnel"
+%token ERSPAN                  "erspan"
+%token EGRESS                  "egress"
+%token INGRESS                 "ingress"
 
 %token COUNTERS                        "counters"
 %token QUOTAS                  "quotas"
@@ -765,7 +768,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
+%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
 %destructor { obj_free($$); }  obj_block_alloc
 
 %type <list>                   stmt_list stateful_stmt_list set_elem_stmt_list
@@ -4958,6 +4961,43 @@ limit_obj                :       /* empty */
                        }
                        ;
 
+erspan_block           :       /* empty */     { $$ = $<obj>-1; }
+                       |       erspan_block     common_block
+                       |       erspan_block     stmt_separator
+                       |       erspan_block     erspan_config  stmt_separator
+                       {
+                               $$ = $1;
+                       }
+                       ;
+
+erspan_block_alloc     :       /* empty */
+                       {
+                               $$ = $<obj>-1;
+                       }
+                       ;
+
+erspan_config          :       HDRVERSION      NUM
+                       {
+                               $<obj>0->tunnel.erspan.version = $2;
+                       }
+                       |       INDEX           NUM
+                       {
+                               $<obj>0->tunnel.erspan.v1.index = $2;
+                       }
+                       |       DIRECTION       INGRESS
+                       {
+                               $<obj>0->tunnel.erspan.v2.direction = 0;
+                       }
+                       |       DIRECTION       EGRESS
+                       {
+                               $<obj>0->tunnel.erspan.v2.direction = 1;
+                       }
+                       |       ID              NUM
+                       {
+                               $<obj>0->tunnel.erspan.v2.hwid = $2;
+                       }
+                       ;
+
 tunnel_config          :       ID      NUM
                        {
                                $<obj>0->tunnel.id = $2;
@@ -4998,6 +5038,10 @@ tunnel_config            :       ID      NUM
                        {
                                $<obj>0->tunnel.tos = $2;
                        }
+                       |       ERSPAN  erspan_block_alloc '{' erspan_block '}'
+                       {
+                               $<obj>0->tunnel.type = TUNNEL_ERSPAN;
+                       }
                        ;
 
 tunnel_block           :       /* empty */     { $$ = $<obj>-1; }
index 5b79facbe3c7682424edabe8155bacf05556f365..2557f4cc1d7222a86476c37e4a360f023a5d0d31 100644 (file)
@@ -2021,6 +2021,32 @@ static void obj_print_data(const struct obj *obj,
                                  opts->nl, opts->tab, opts->tab,
                                  obj->tunnel.ttl);
                }
+               switch (obj->tunnel.type) {
+               case TUNNEL_ERSPAN:
+                       nft_print(octx, "%s%s%serspan {",
+                                 opts->nl, opts->tab, opts->tab);
+                       nft_print(octx, "%s%s%s%sversion %u",
+                                 opts->nl, opts->tab, opts->tab, opts->tab,
+                                 obj->tunnel.erspan.version);
+                       if (obj->tunnel.erspan.version == 1) {
+                               nft_print(octx, "%s%s%s%sindex %u",
+                                         opts->nl, opts->tab, opts->tab, opts->tab,
+                                         obj->tunnel.erspan.v1.index);
+                       } else {
+                               nft_print(octx, "%s%s%s%sdirection %s",
+                                         opts->nl, opts->tab, opts->tab, opts->tab,
+                                         obj->tunnel.erspan.v2.direction ? "egress"
+                                                                         : "ingress");
+                               nft_print(octx, "%s%s%s%sid %u",
+                                         opts->nl, opts->tab, opts->tab, opts->tab,
+                                         obj->tunnel.erspan.v2.hwid);
+                       }
+                       nft_print(octx, "%s%s%s}",
+                                 opts->nl, opts->tab, opts->tab);
+               default:
+                       break;
+               }
+
                nft_print(octx, "%s", opts->stmt_separator);
                break;
        default:
index 5e848890220fd48110aa3923a0195c62c3fcdb41..def0ac0e83681a10d895a6c5bc51ed59ce094228 100644 (file)
@@ -821,6 +821,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
        "dport"                 { return DPORT; }
        "ttl"                   { return TTL; }
        "tos"                   { return TOS; }
+       "version"               { return HDRVERSION; }
+       "direction"             { return DIRECTION; }
+       "erspan"                { return ERSPAN; }
+       "egress"                { return EGRESS; }
+       "ingress"               { return INGRESS; }
 }
 
 "notrack"              { return NOTRACK; }