]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add tunnel statement and expression support
authorPablo Neira Ayuso <pablo@netfilter.org>
Thu, 21 Aug 2025 09:12:58 +0000 (11:12 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 27 Aug 2025 21:51:03 +0000 (23:51 +0200)
This patch allows you to attach tunnel metadata through the tunnel
statement.

The following example shows how to redirect traffic to the erspan0
tunnel device which will take the tunnel configuration that is
specified by the ruleset.

     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
                    }
            }

    chain x {
    type filter hook ingress device veth0 priority 0;

    ip daddr 10.141.10.123 tunnel name y fwd to erspan0
    }
     }

This patch also allows to match on tunnel metadata via tunnel expression.

Joint work with Fernando.

Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Makefile.am
include/expression.h
include/tunnel.h [new file with mode: 0644]
src/evaluate.c
src/expression.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser_bison.y
src/scanner.l
src/statement.c
src/tunnel.c [new file with mode: 0644]

index e292d3b93d1024459dd364e05561f75098c498f7..51dadbe64649e3dd9c5364a50b187c936698df84 100644 (file)
@@ -100,6 +100,7 @@ noinst_HEADERS = \
        include/statement.h \
        include/tcpopt.h \
        include/trace.h \
+       include/tunnel.h \
        include/utils.h \
        include/xfrm.h \
        include/xt.h \
@@ -243,6 +244,7 @@ src_libnftables_la_SOURCES = \
        src/socket.c \
        src/statement.c \
        src/tcpopt.c \
+       src/tunnel.c \
        src/utils.c \
        src/xfrm.c \
        $(NULL)
index e483b7e76f4cae3f18c6797155cb3d0668dd197d..7185ee66a018985348240a4ad72c7c2b41123ada 100644 (file)
@@ -77,6 +77,7 @@ enum expr_types {
        EXPR_NUMGEN,
        EXPR_HASH,
        EXPR_RT,
+       EXPR_TUNNEL,
        EXPR_FIB,
        EXPR_XFRM,
        EXPR_SET_ELEM_CATCHALL,
@@ -229,6 +230,7 @@ enum expr_flags {
 #include <hash.h>
 #include <ct.h>
 #include <socket.h>
+#include <tunnel.h>
 #include <osf.h>
 #include <xfrm.h>
 
@@ -368,6 +370,10 @@ struct expr {
                        enum nft_socket_keys    key;
                        uint32_t                level;
                } socket;
+               struct {
+                       /* EXPR_TUNNEL */
+                       enum nft_tunnel_keys    key;
+               } tunnel;
                struct {
                        /* EXPR_RT */
                        enum nft_rt_keys        key;
diff --git a/include/tunnel.h b/include/tunnel.h
new file mode 100644 (file)
index 0000000..9e6bd97
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef NFTABLES_TUNNEL_H
+#define NFTABLES_TUNNEL_H
+
+/**
+ * struct tunnel_template - template for tunnel expressions
+ *
+ * @token:     parser token for the expression
+ * @dtype:     data type of the expression
+ * @len:       length of the expression
+ * @byteorder: byteorder
+ */
+struct tunnel_template {
+       const char              *token;
+       const struct datatype   *dtype;
+       enum byteorder          byteorder;
+       unsigned int            len;
+};
+
+extern const struct tunnel_template tunnel_templates[];
+
+#define TUNNEL_TEMPLATE(__token, __dtype, __len, __byteorder) {        \
+       .token          = (__token),                            \
+       .dtype          = (__dtype),                            \
+       .len            = (__len),                              \
+       .byteorder      = (__byteorder),                        \
+}
+
+extern struct expr *tunnel_expr_alloc(const struct location *loc,
+                                     enum nft_tunnel_keys key);
+
+extern const struct expr_ops tunnel_expr_ops;
+
+#endif /* NFTABLES_TUNNEL_H */
index da8794dd412e61e7e058eaef7f8e56aacde564b3..6bf14b0cdba47fcf9aebc594383fda49af62aeb6 100644 (file)
@@ -1737,6 +1737,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
                case EXPR_SOCKET:
                case EXPR_OSF:
                case EXPR_XFRM:
+               case EXPR_TUNNEL:
                        break;
                case EXPR_RANGE:
                case EXPR_PREFIX:
@@ -3053,6 +3054,11 @@ static int expr_evaluate_osf(struct eval_ctx *ctx, struct expr **expr)
        return expr_evaluate_primary(ctx, expr);
 }
 
+static int expr_evaluate_tunnel(struct eval_ctx *ctx, struct expr **exprp)
+{
+       return expr_evaluate_primary(ctx, exprp);
+}
+
 static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
 {
        struct symbol *sym = (*exprp)->sym;
@@ -3170,6 +3176,8 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
                return expr_evaluate_meta(ctx, expr);
        case EXPR_SOCKET:
                return expr_evaluate_socket(ctx, expr);
+       case EXPR_TUNNEL:
+               return expr_evaluate_tunnel(ctx, expr);
        case EXPR_OSF:
                return expr_evaluate_osf(ctx, expr);
        case EXPR_FIB:
index 8cb639797284e896c1271066402423b65704f59d..e3c27a13f8dba49e157e49cd8f3b1a0d2c3e2326 100644 (file)
@@ -1762,6 +1762,7 @@ static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
        case EXPR_NUMGEN: return &numgen_expr_ops;
        case EXPR_HASH: return &hash_expr_ops;
        case EXPR_RT: return &rt_expr_ops;
+       case EXPR_TUNNEL: return &tunnel_expr_ops;
        case EXPR_FIB: return &fib_expr_ops;
        case EXPR_XFRM: return &xfrm_expr_ops;
        case EXPR_SET_ELEM_CATCHALL: return &set_elem_catchall_expr_ops;
index b97962a30ca24691afafd5041eacc5bbd0fb8d1d..5627826dfad06edd33be6422f0968fa5b96b034b 100644 (file)
@@ -940,6 +940,21 @@ static void netlink_parse_osf(struct netlink_parse_ctx *ctx,
        netlink_set_register(ctx, dreg, expr);
 }
 
+static void netlink_parse_tunnel(struct netlink_parse_ctx *ctx,
+                                const struct location *loc,
+                                const struct nftnl_expr *nle)
+{
+       enum nft_registers dreg;
+       struct expr * expr;
+       uint32_t key;
+
+       key = nftnl_expr_get_u32(nle, NFTNL_EXPR_TUNNEL_KEY);
+       expr = tunnel_expr_alloc(loc, key);
+
+       dreg = netlink_parse_register(nle, NFTNL_EXPR_TUNNEL_DREG);
+       netlink_set_register(ctx, dreg, expr);
+}
+
 static void netlink_parse_meta_stmt(struct netlink_parse_ctx *ctx,
                                    const struct location *loc,
                                    const struct nftnl_expr *nle)
@@ -1922,6 +1937,7 @@ static const struct expr_handler netlink_parsers[] = {
        { .name = "exthdr",     .parse = netlink_parse_exthdr },
        { .name = "meta",       .parse = netlink_parse_meta },
        { .name = "socket",     .parse = netlink_parse_socket },
+       { .name = "tunnel",     .parse = netlink_parse_tunnel },
        { .name = "osf",        .parse = netlink_parse_osf },
        { .name = "rt",         .parse = netlink_parse_rt },
        { .name = "ct",         .parse = netlink_parse_ct },
@@ -3023,6 +3039,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
        case EXPR_NUMGEN:
        case EXPR_FIB:
        case EXPR_SOCKET:
+       case EXPR_TUNNEL:
        case EXPR_OSF:
        case EXPR_XFRM:
                break;
index 8ac33d344de40023b315a0ae7bcb098b984fbf7c..d01cadf84faf0a2993f7b11899d73b5bde49356f 100644 (file)
@@ -334,6 +334,18 @@ static void netlink_gen_osf(struct netlink_linearize_ctx *ctx,
        nft_rule_add_expr(ctx, nle, &expr->location);
 }
 
+static void netlink_gen_tunnel(struct netlink_linearize_ctx *ctx,
+                              const struct expr *expr,
+                              enum nft_registers dreg)
+{
+       struct nftnl_expr *nle;
+
+       nle = alloc_nft_expr("tunnel");
+       netlink_put_register(nle, NFTNL_EXPR_TUNNEL_DREG, dreg);
+       nftnl_expr_set_u32(nle, NFTNL_EXPR_TUNNEL_KEY, expr->tunnel.key);
+       nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
 static void netlink_gen_numgen(struct netlink_linearize_ctx *ctx,
                            const struct expr *expr,
                            enum nft_registers dreg)
@@ -932,6 +944,8 @@ static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
                return netlink_gen_fib(ctx, expr, dreg);
        case EXPR_SOCKET:
                return netlink_gen_socket(ctx, expr, dreg);
+       case EXPR_TUNNEL:
+               return netlink_gen_tunnel(ctx, expr, dreg);
        case EXPR_OSF:
                return netlink_gen_osf(ctx, expr, dreg);
        case EXPR_XFRM:
index 557977e2ed7a8057f652cd1078deed1d689c7972..08d75dbba2cdab6abfcd9dd17d00d8e55b9904c8 100644 (file)
@@ -321,6 +321,8 @@ int nft_lex(void *, void *, void *);
 %token RULESET                 "ruleset"
 %token TRACE                   "trace"
 
+%token PATH                    "path"
+
 %token INET                    "inet"
 %token NETDEV                  "netdev"
 
@@ -779,8 +781,8 @@ int nft_lex(void *, void *, void *);
 %destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt last_stmt
 %type <stmt>                   limit_stmt_alloc quota_stmt_alloc last_stmt_alloc ct_limit_stmt_alloc
 %destructor { stmt_free($$); } limit_stmt_alloc quota_stmt_alloc last_stmt_alloc ct_limit_stmt_alloc
-%type <stmt>                   objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy
-%destructor { stmt_free($$); } objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy
+%type <stmt>                   objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy objref_stmt_tunnel
+%destructor { stmt_free($$); } objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy objref_stmt_tunnel
 
 %type <stmt>                   payload_stmt
 %destructor { stmt_free($$); } payload_stmt
@@ -940,9 +942,9 @@ int nft_lex(void *, void *, void *);
 %destructor { expr_free($$); } mh_hdr_expr
 %type <val>                    mh_hdr_field
 
-%type <expr>                   meta_expr
-%destructor { expr_free($$); } meta_expr
-%type <val>                    meta_key        meta_key_qualified      meta_key_unqualified    numgen_type
+%type <expr>                   meta_expr       tunnel_expr
+%destructor { expr_free($$); } meta_expr       tunnel_expr
+%type <val>                    meta_key        meta_key_qualified      meta_key_unqualified    numgen_type     tunnel_key
 
 %type <expr>                   socket_expr
 %destructor { expr_free($$); } socket_expr
@@ -3206,6 +3208,14 @@ objref_stmt_synproxy     :       SYNPROXY        NAME    stmt_expr close_scope_synproxy
                        }
                        ;
 
+objref_stmt_tunnel     :       TUNNEL  NAME    stmt_expr       close_scope_tunnel
+                       {
+                               $$ = objref_stmt_alloc(&@$);
+                               $$->objref.type = NFT_OBJECT_TUNNEL;
+                               $$->objref.expr = $3;
+                       }
+                       ;
+
 objref_stmt_ct         :       CT      TIMEOUT         SET     stmt_expr       close_scope_ct
                        {
                                $$ = objref_stmt_alloc(&@$);
@@ -3226,6 +3236,7 @@ objref_stmt               :       objref_stmt_counter
                        |       objref_stmt_quota
                        |       objref_stmt_synproxy
                        |       objref_stmt_ct
+                       |       objref_stmt_tunnel
                        ;
 
 stateful_stmt          :       counter_stmt    close_scope_counter
@@ -3904,6 +3915,7 @@ primary_stmt_expr :       symbol_expr                     { $$ = $1; }
                        |       boolean_expr                    { $$ = $1; }
                        |       meta_expr                       { $$ = $1; }
                        |       rt_expr                         { $$ = $1; }
+                       |       tunnel_expr                     { $$ = $1; }
                        |       ct_expr                         { $$ = $1; }
                        |       numgen_expr                     { $$ = $1; }
                        |       hash_expr                       { $$ = $1; }
@@ -4381,6 +4393,7 @@ selector_expr             :       payload_expr                    { $$ = $1; }
                        |       exthdr_expr                     { $$ = $1; }
                        |       exthdr_exists_expr              { $$ = $1; }
                        |       meta_expr                       { $$ = $1; }
+                       |       tunnel_expr                     { $$ = $1; }
                        |       socket_expr                     { $$ = $1; }
                        |       rt_expr                         { $$ = $1; }
                        |       ct_expr                         { $$ = $1; }
@@ -5493,6 +5506,16 @@ socket_key               :       TRANSPARENT     { $$ = NFT_SOCKET_TRANSPARENT; }
                        |       WILDCARD        { $$ = NFT_SOCKET_WILDCARD; }
                        ;
 
+tunnel_key             :       PATH            { $$ = NFT_TUNNEL_PATH; }
+                       |       ID              { $$ = NFT_TUNNEL_ID; }
+                       ;
+
+tunnel_expr            :       TUNNEL  tunnel_key
+                       {
+                               $$ = tunnel_expr_alloc(&@$, $2);
+                       }
+                       ;
+
 offset_opt             :       /* empty */     { $$ = 0; }
                        |       OFFSET  NUM     { $$ = $2; }
                        ;
index def0ac0e83681a10d895a6c5bc51ed59ce094228..9695d710a01c37006ae06085146bbafcd88f0331 100644 (file)
@@ -410,7 +410,7 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
 }
 
 "counter"              { scanner_push_start_cond(yyscanner, SCANSTATE_COUNTER); return COUNTER; }
-<SCANSTATE_COUNTER,SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_STMT_SYNPROXY,SCANSTATE_EXPR_OSF>"name"                   { return NAME; }
+<SCANSTATE_COUNTER,SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_STMT_SYNPROXY,SCANSTATE_EXPR_OSF,SCANSTATE_TUNNEL>"name"                  { return NAME; }
 <SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT>"packets"              { return PACKETS; }
 <SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"bytes"        { return BYTES; }
 
@@ -826,6 +826,7 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
        "erspan"                { return ERSPAN; }
        "egress"                { return EGRESS; }
        "ingress"               { return INGRESS; }
+       "path"                  { return PATH; }
 }
 
 "notrack"              { return NOTRACK; }
index 2bfed4ac982fc15ddb27fc51ccc1e43473083c6a..20241f6867a311b8cf7713ce93be05f5dcc34b19 100644 (file)
@@ -290,6 +290,7 @@ static const char *objref_type[NFT_OBJECT_MAX + 1] = {
        [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_SYNPROXY]   = "synproxy",
diff --git a/src/tunnel.c b/src/tunnel.c
new file mode 100644 (file)
index 0000000..cd92d03
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2018 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <arpa/inet.h>
+#include <linux/netfilter.h>
+#include <linux/pkt_sched.h>
+#include <linux/if_packet.h>
+
+#include <nftables.h>
+#include <expression.h>
+#include <datatype.h>
+#include <tunnel.h>
+#include <gmputil.h>
+#include <utils.h>
+#include <erec.h>
+
+const struct tunnel_template tunnel_templates[] = {
+       [NFT_TUNNEL_PATH]       = META_TEMPLATE("path", &boolean_type,
+                                               BITS_PER_BYTE, BYTEORDER_HOST_ENDIAN),
+       [NFT_TUNNEL_ID]         = META_TEMPLATE("id",  &integer_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+};
+
+static void tunnel_expr_print(const struct expr *expr, struct output_ctx *octx)
+{
+       uint32_t key = expr->tunnel.key;
+       const char *token = "unknown";
+
+       if (key < array_size(tunnel_templates))
+               token = tunnel_templates[key].token;
+
+       nft_print(octx, "tunnel %s", token);
+}
+
+static bool tunnel_expr_cmp(const struct expr *e1, const struct expr *e2)
+{
+       return e1->tunnel.key == e2->tunnel.key;
+}
+
+static void tunnel_expr_clone(struct expr *new, const struct expr *expr)
+{
+       new->tunnel.key = expr->tunnel.key;
+}
+
+const struct expr_ops tunnel_expr_ops = {
+       .type           = EXPR_TUNNEL,
+       .name           = "tunnel",
+       .print          = tunnel_expr_print,
+       .cmp            = tunnel_expr_cmp,
+       .clone          = tunnel_expr_clone,
+};
+
+struct expr *tunnel_expr_alloc(const struct location *loc,
+                              enum nft_tunnel_keys key)
+{
+       const struct tunnel_template *tmpl = &tunnel_templates[key];
+       struct expr *expr;
+
+       expr = expr_alloc(loc, EXPR_TUNNEL, tmpl->dtype, tmpl->byteorder,
+                         tmpl->len);
+       expr->tunnel.key = key;
+
+       return expr;
+}