* [PATCH 2/7 nft v2] tunnel: add erspan support
2025-08-14 11:04 [PATCH 1/7 nft v2] src: add tunnel template support Fernando Fernandez Mancera
@ 2025-08-14 11:04 ` Fernando Fernandez Mancera
2025-08-14 11:04 ` [PATCH 3/7 nft v2] src: add tunnel statement and expression support Fernando Fernandez Mancera
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Fernando Fernandez Mancera @ 2025-08-14 11:04 UTC (permalink / raw)
To: netfilter-devel; +Cc: coreteam, pablo, fw
From: Pablo Neira Ayuso <pablo@netfilter.org>
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: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/rule.h | 18 ++++++++++++++++++
src/mnl.c | 43 +++++++++++++++++++++++++++++++++++++++++++
src/netlink.c | 32 ++++++++++++++++++++++++++++++++
src/parser_bison.y | 46 +++++++++++++++++++++++++++++++++++++++++++++-
src/rule.c | 26 ++++++++++++++++++++++++++
src/scanner.l | 5 +++++
6 files changed, 169 insertions(+), 1 deletion(-)
diff --git a/include/rule.h b/include/rule.h
index 0fa87b52..71e9a07e 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -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;
+ };
};
/**
diff --git a/src/mnl.c b/src/mnl.c
index c5a28c8d..722bfa2a 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1471,6 +1471,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)
{
@@ -1602,6 +1644,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);
diff --git a/src/netlink.c b/src/netlink.c
index f0a9c02b..ff81b185 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1808,6 +1808,35 @@ 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;
+ }
+
+ return 0;
+}
+
struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
struct nftnl_obj *nlo)
{
@@ -1938,6 +1967,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);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index b25765cb..183b7cc0 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -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
@@ -4957,6 +4960,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;
@@ -4985,6 +5025,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; }
diff --git a/src/rule.c b/src/rule.c
index 5b79facb..2557f4cc 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -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:
diff --git a/src/scanner.l b/src/scanner.l
index 5e848890..def0ac0e 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -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; }
--
2.50.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 3/7 nft v2] src: add tunnel statement and expression support
2025-08-14 11:04 [PATCH 1/7 nft v2] src: add tunnel template support Fernando Fernandez Mancera
2025-08-14 11:04 ` [PATCH 2/7 nft v2] tunnel: add erspan support Fernando Fernandez Mancera
@ 2025-08-14 11:04 ` Fernando Fernandez Mancera
2025-08-14 11:04 ` [PATCH 4/7 nft v2] tunnel: add vxlan support Fernando Fernandez Mancera
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Fernando Fernandez Mancera @ 2025-08-14 11:04 UTC (permalink / raw)
To: netfilter-devel; +Cc: coreteam, pablo, fw
From: Pablo Neira Ayuso <pablo@netfilter.org>
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: Pablo Neira Ayuso <pablo@netfilter.org>
---
Makefile.am | 2 ++
include/expression.h | 6 ++++
include/tunnel.h | 33 +++++++++++++++++
src/evaluate.c | 8 +++++
src/expression.c | 1 +
src/netlink_delinearize.c | 17 +++++++++
src/netlink_linearize.c | 14 ++++++++
src/parser_bison.y | 33 ++++++++++++++---
src/scanner.l | 3 +-
src/statement.c | 1 +
src/tunnel.c | 76 +++++++++++++++++++++++++++++++++++++++
11 files changed, 188 insertions(+), 6 deletions(-)
create mode 100644 include/tunnel.h
create mode 100644 src/tunnel.c
diff --git a/Makefile.am b/Makefile.am
index b5580b54..c0d14316 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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)
diff --git a/include/expression.h b/include/expression.h
index e483b7e7..7185ee66 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -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
index 00000000..9e6bd97a
--- /dev/null
+++ b/include/tunnel.h
@@ -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 */
diff --git a/src/evaluate.c b/src/evaluate.c
index 5eb076ff..f06ea1d1 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -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:
@@ -3049,6 +3050,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;
@@ -3166,6 +3172,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:
diff --git a/src/expression.c b/src/expression.c
index 8cb63979..e3c27a13 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -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;
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index b4d4a3da..8b134704 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -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;
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 8ac33d34..d01cadf8 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -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:
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 183b7cc0..e195c12a 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -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
@@ -3205,6 +3207,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(&@$);
@@ -3225,6 +3235,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
@@ -3903,6 +3914,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; }
@@ -4380,6 +4392,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; }
@@ -5480,6 +5493,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; }
;
diff --git a/src/scanner.l b/src/scanner.l
index def0ac0e..9695d710 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -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; }
diff --git a/src/statement.c b/src/statement.c
index 695b57a6..19bc7db4 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -289,6 +289,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
index 00000000..d03f853a
--- /dev/null
+++ b/src/tunnel.c
@@ -0,0 +1,76 @@
+/*
+ * 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)
+{
+ nft_print(octx, "tunnel %s",
+ tunnel_templates[expr->tunnel.key].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;
+}
--
2.50.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 4/7 nft v2] tunnel: add vxlan support
2025-08-14 11:04 [PATCH 1/7 nft v2] src: add tunnel template support Fernando Fernandez Mancera
2025-08-14 11:04 ` [PATCH 2/7 nft v2] tunnel: add erspan support Fernando Fernandez Mancera
2025-08-14 11:04 ` [PATCH 3/7 nft v2] src: add tunnel statement and expression support Fernando Fernandez Mancera
@ 2025-08-14 11:04 ` Fernando Fernandez Mancera
2025-08-14 11:04 ` [PATCH 5/7 nft v2] tunnel: add geneve support Fernando Fernandez Mancera
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Fernando Fernandez Mancera @ 2025-08-14 11:04 UTC (permalink / raw)
To: netfilter-devel; +Cc: coreteam, pablo, fw, Fernando Fernandez Mancera
This patch extends the tunnel metadata object to define vxlan 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
vxlan {
gbp 200
}
}
}
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
include/rule.h | 4 ++++
src/mnl.c | 16 ++++++++++++++++
src/netlink.c | 7 +++++++
src/parser_bison.y | 28 +++++++++++++++++++++++++++-
src/rule.c | 10 ++++++++++
src/scanner.l | 1 +
6 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/include/rule.h b/include/rule.h
index 71e9a07e..c52af2c4 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -495,6 +495,7 @@ struct secmark {
enum tunnel_type {
TUNNEL_UNSPEC = 0,
TUNNEL_ERSPAN,
+ TUNNEL_VXLAN,
};
struct tunnel {
@@ -517,6 +518,9 @@ struct tunnel {
uint8_t hwid;
} v2;
} erspan;
+ struct {
+ uint32_t gbp;
+ } vxlan;
};
};
diff --git a/src/mnl.c b/src/mnl.c
index 722bfa2a..0fcb8f6b 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1505,6 +1505,22 @@ static void obj_tunnel_add_opts(struct nftnl_obj *nlo, struct tunnel *tunnel)
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_VXLAN:
+ opts = nftnl_tunnel_opts_alloc(NFTNL_TUNNEL_TYPE_VXLAN);
+ if (!opts)
+ memory_allocation_error();
+
+ opt = nftnl_tunnel_opt_alloc(NFTNL_TUNNEL_TYPE_VXLAN);
+ if (!opt)
+ memory_allocation_error();
+
+ nftnl_tunnel_opt_set(opt, NFTNL_TUNNEL_VXLAN_GBP,
+ &tunnel->vxlan.gbp,
+ sizeof(tunnel->vxlan.gbp));
+
nftnl_tunnel_opts_add(opts, opt);
nftnl_obj_set_data(nlo, NFTNL_OBJ_TUNNEL_OPTS, &opts, sizeof(struct nftnl_tunnel_opts *));
break;
diff --git a/src/netlink.c b/src/netlink.c
index ff81b185..2a0b8f62 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1832,6 +1832,13 @@ static int tunnel_parse_opt_cb(struct nftnl_tunnel_opt *opt, void *data) {
opt,
NFTNL_TUNNEL_ERSPAN_V2_DIR);
break;
+ case NFTNL_TUNNEL_TYPE_VXLAN:
+ obj->tunnel.type = TUNNEL_VXLAN;
+ if (nftnl_tunnel_opt_get_flags(opt) & (1 << NFTNL_TUNNEL_VXLAN_GBP)) {
+ obj->tunnel.type = TUNNEL_VXLAN;
+ obj->tunnel.vxlan.gbp = nftnl_tunnel_opt_get_u32(opt, NFTNL_TUNNEL_VXLAN_GBP);
+ }
+ break;
}
return 0;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index e195c12a..df42c4aa 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -612,6 +612,7 @@ int nft_lex(void *, void *, void *);
%token ERSPAN "erspan"
%token EGRESS "egress"
%token INGRESS "ingress"
+%token GBP "gbp"
%token COUNTERS "counters"
%token QUOTAS "quotas"
@@ -770,7 +771,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
+%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
%destructor { obj_free($$); } obj_block_alloc
%type <list> stmt_list stateful_stmt_list set_elem_stmt_list
@@ -5010,6 +5011,27 @@ erspan_config : HDRVERSION NUM
}
;
+vxlan_block : /* empty */ { $$ = $<obj>-1; }
+ | vxlan_block common_block
+ | vxlan_block stmt_separator
+ | vxlan_block vxlan_config stmt_separator
+ {
+ $$ = $1;
+ }
+ ;
+
+vxlan_block_alloc : /* empty */
+ {
+ $$ = $<obj>-1;
+ }
+ ;
+
+vxlan_config : GBP NUM
+ {
+ $<obj>0->tunnel.vxlan.gbp = $2;
+ }
+ ;
+
tunnel_config : ID NUM
{
$<obj>0->tunnel.id = $2;
@@ -5042,6 +5064,10 @@ tunnel_config : ID NUM
{
$<obj>0->tunnel.type = TUNNEL_ERSPAN;
}
+ | VXLAN vxlan_block_alloc '{' vxlan_block '}'
+ {
+ $<obj>0->tunnel.type = TUNNEL_VXLAN;
+ }
;
tunnel_block : /* empty */ { $$ = $<obj>-1; }
diff --git a/src/rule.c b/src/rule.c
index 2557f4cc..0450851c 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -2043,6 +2043,16 @@ static void obj_print_data(const struct obj *obj,
}
nft_print(octx, "%s%s%s}",
opts->nl, opts->tab, opts->tab);
+ break;
+ case TUNNEL_VXLAN:
+ nft_print(octx, "%s%s%svxlan {",
+ opts->nl, opts->tab, opts->tab);
+ nft_print(octx, "%s%s%s%sgbp %u",
+ opts->nl, opts->tab, opts->tab, opts->tab,
+ obj->tunnel.vxlan.gbp);
+ nft_print(octx, "%s%s%s}",
+ opts->nl, opts->tab, opts->tab);
+ break;
default:
break;
}
diff --git a/src/scanner.l b/src/scanner.l
index 9695d710..74ebca3b 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -827,6 +827,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"egress" { return EGRESS; }
"ingress" { return INGRESS; }
"path" { return PATH; }
+ "gbp" { return GBP; }
}
"notrack" { return NOTRACK; }
--
2.50.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 5/7 nft v2] tunnel: add geneve support
2025-08-14 11:04 [PATCH 1/7 nft v2] src: add tunnel template support Fernando Fernandez Mancera
` (2 preceding siblings ...)
2025-08-14 11:04 ` [PATCH 4/7 nft v2] tunnel: add vxlan support Fernando Fernandez Mancera
@ 2025-08-14 11:04 ` Fernando Fernandez Mancera
2025-08-14 11:04 ` [PATCH 6/7 nft v2] tunnel: add tunnel object and statement json support Fernando Fernandez Mancera
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Fernando Fernandez Mancera @ 2025-08-14 11:04 UTC (permalink / raw)
To: netfilter-devel; +Cc: coreteam, pablo, fw, Fernando Fernandez Mancera
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>
---
include/rule.h | 14 ++++++++++
src/mnl.c | 25 +++++++++++++++++
src/netlink.c | 29 ++++++++++++++++++++
src/parser_bison.y | 43 ++++++++++++++++++++++++++++-
src/rule.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++
src/scanner.l | 3 +++
6 files changed, 180 insertions(+), 1 deletion(-)
diff --git a/include/rule.h b/include/rule.h
index c52af2c4..498a88bf 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -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
*
diff --git a/src/mnl.c b/src/mnl.c
index 0fcb8f6b..425ef51a 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1524,6 +1524,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;
}
diff --git a/src/netlink.c b/src/netlink.c
index 2a0b8f62..939a5d08 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1839,6 +1839,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 = NULL;
+
+ 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;
}
return 0;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index df42c4aa..daf718c7 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -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
@@ -5011,6 +5013,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
@@ -5068,6 +5108,7 @@ tunnel_config : ID NUM
{
$<obj>0->tunnel.type = TUNNEL_VXLAN;
}
+ | GENEVE geneve_block_alloc '{' geneve_block '}'
;
tunnel_block : /* empty */ { $$ = $<obj>-1; }
diff --git a/src/rule.c b/src/rule.c
index 0450851c..b9346759 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -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++) {
+ char *endptr;
+ uint32_t value;
+
+ 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;
}
diff --git a/src/scanner.l b/src/scanner.l
index 74ebca3b..8085c93b 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -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; }
--
2.50.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 6/7 nft v2] tunnel: add tunnel object and statement json support
2025-08-14 11:04 [PATCH 1/7 nft v2] src: add tunnel template support Fernando Fernandez Mancera
` (3 preceding siblings ...)
2025-08-14 11:04 ` [PATCH 5/7 nft v2] tunnel: add geneve support Fernando Fernandez Mancera
@ 2025-08-14 11:04 ` Fernando Fernandez Mancera
2025-08-14 11:04 ` [PATCH 7/7 nft v2] tests: add tunnel shell and python tests Fernando Fernandez Mancera
2025-08-19 12:47 ` [PATCH 1/7 nft v2] src: add tunnel template support Pablo Neira Ayuso
6 siblings, 0 replies; 10+ messages in thread
From: Fernando Fernandez Mancera @ 2025-08-14 11:04 UTC (permalink / raw)
To: netfilter-devel; +Cc: coreteam, pablo, fw, Fernando Fernandez Mancera
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
include/json.h | 2 +
include/tunnel.h | 4 +
src/json.c | 94 +++++++++++++++++++++--
src/parser_json.c | 190 +++++++++++++++++++++++++++++++++++++++++++++-
src/tunnel.c | 18 +++++
5 files changed, 300 insertions(+), 8 deletions(-)
diff --git a/include/json.h b/include/json.h
index b61eeafe..42e1c861 100644
--- a/include/json.h
+++ b/include/json.h
@@ -31,6 +31,7 @@ json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx);
json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx);
json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx);
json_t *meta_expr_json(const struct expr *expr, struct output_ctx *octx);
+json_t *tunnel_expr_json(const struct expr *expr, struct output_ctx *octx);
json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx);
json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx);
json_t *concat_expr_json(const struct expr *expr, struct output_ctx *octx);
@@ -160,6 +161,7 @@ EXPR_PRINT_STUB(fib_expr)
EXPR_PRINT_STUB(constant_expr)
EXPR_PRINT_STUB(socket_expr)
EXPR_PRINT_STUB(osf_expr)
+EXPR_PRINT_STUB(tunnel_expr)
EXPR_PRINT_STUB(xfrm_expr)
EXPR_PRINT_STUB(integer_type)
diff --git a/include/tunnel.h b/include/tunnel.h
index 9e6bd97a..e2d87d2c 100644
--- a/include/tunnel.h
+++ b/include/tunnel.h
@@ -25,6 +25,10 @@ extern const struct tunnel_template tunnel_templates[];
.byteorder = (__byteorder), \
}
+struct error_record *tunnel_key_parse(const struct location *loc,
+ const char *str,
+ unsigned int *value);
+
extern struct expr *tunnel_expr_alloc(const struct location *loc,
enum nft_tunnel_keys key);
diff --git a/src/json.c b/src/json.c
index e15a6761..88c713de 100644
--- a/src/json.c
+++ b/src/json.c
@@ -377,7 +377,31 @@ static json_t *timeout_policy_json(uint8_t l4, const uint32_t *timeout)
return root ? : json_null();
}
-static json_t *obj_print_json(const struct obj *obj)
+static json_t *tunnel_erspan_print_json(const struct obj *obj)
+{
+ json_t *tunnel;
+
+ switch (obj->tunnel.erspan.version) {
+ case 1:
+ tunnel = json_pack("{s:i, s:i}",
+ "version", obj->tunnel.erspan.version,
+ "index", obj->tunnel.erspan.v1.index);
+ break;
+ case 2:
+ tunnel = json_pack("{s:i, s:s, s:i}",
+ "version", obj->tunnel.erspan.version,
+ "direction", obj->tunnel.erspan.v2.direction
+ ? "egress" : "ingress",
+ "hwid", obj->tunnel.erspan.v2.hwid);
+ break;
+ default:
+ BUG("Unknown tunnel erspan version %d", obj->tunnel.erspan.version);
+ }
+
+ return tunnel;
+}
+
+static json_t *obj_print_json(struct output_ctx *octx, const struct obj *obj)
{
const char *rate_unit = NULL, *burst_unit = NULL;
const char *type = obj_type_name(obj->type);
@@ -492,7 +516,59 @@ static json_t *obj_print_json(const struct obj *obj)
json_decref(tmp);
break;
case NFT_OBJECT_TUNNEL:
- /* TODO */
+ tmp = json_pack("{s:i, s:o, s:o, s:i, s:i, s:i, s:i}",
+ "id", obj->tunnel.id,
+ "src", expr_print_json(obj->tunnel.src, octx),
+ "dst", expr_print_json(obj->tunnel.dst, octx),
+ "sport", obj->tunnel.sport,
+ "dport", obj->tunnel.dport,
+ "tos", obj->tunnel.tos,
+ "ttl", obj->tunnel.ttl);
+
+ switch (obj->tunnel.type) {
+ case TUNNEL_UNSPEC:
+ break;
+ case TUNNEL_ERSPAN:
+ json_object_set_new(tmp, "type", json_string("erspan"));
+ json_object_set_new(tmp, "tunnel",
+ tunnel_erspan_print_json(obj));
+ break;
+ case TUNNEL_VXLAN:
+ json_object_set_new(tmp, "type", json_string("vxlan"));
+ json_object_set_new(tmp, "tunnel",
+ json_pack("{s:i}",
+ "gbp",
+ obj->tunnel.vxlan.gbp));
+ break;
+ case TUNNEL_GENEVE:
+ struct tunnel_geneve *geneve;
+ json_t *opts = json_array();
+
+ list_for_each_entry(geneve, &obj->tunnel.geneve_opts, list) {
+ char data_str[256];
+ json_t *opt;
+ int offset;
+
+ data_str[0] = '0';
+ data_str[1] = 'x';
+ offset = 2;
+ for (uint32_t i = 0; i < geneve->data_len; i++)
+ offset += snprintf(data_str + offset,
+ 3, "%x", geneve->data[i]);
+
+ opt = json_pack("{s:i, s:i, s:s}",
+ "class", geneve->geneve_class,
+ "opt-type", geneve->type,
+ "data", data_str);
+ json_array_append_new(opts, opt);
+ }
+
+ json_object_set_new(tmp, "type", json_string("geneve"));
+ json_object_set_new(tmp, "tunnel", opts);
+ break;
+ }
+ json_object_update(root, tmp);
+ json_decref(tmp);
break;
}
@@ -1109,6 +1185,12 @@ json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx)
return nft_json_pack("{s:o}", "ipsec", root);
}
+json_t *tunnel_expr_json(const struct expr *expr, struct output_ctx *octx)
+{
+ return json_pack("{s:{s:s}}", "tunnel",
+ "key", tunnel_templates[expr->tunnel.key].token);
+}
+
json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx)
{
char buf[1024] = "0x";
@@ -1725,7 +1807,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
json_array_append_new(root, tmp);
}
list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
- tmp = obj_print_json(obj);
+ tmp = obj_print_json(&ctx->nft->output, obj);
json_array_append_new(root, tmp);
}
list_for_each_entry(set, &table->set_cache.list, cache.list) {
@@ -1900,7 +1982,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx,
strcmp(cmd->handle.obj.name, obj->handle.obj.name)))
continue;
- json_array_append_new(root, obj_print_json(obj));
+ json_array_append_new(root, obj_print_json(&ctx->nft->output, obj));
}
}
@@ -2119,7 +2201,9 @@ void monitor_print_element_json(struct netlink_mon_handler *monh,
void monitor_print_obj_json(struct netlink_mon_handler *monh,
const char *cmd, struct obj *o)
{
- monitor_print_json(monh, cmd, obj_print_json(o));
+ struct output_ctx *octx = &monh->ctx->nft->output;
+
+ monitor_print_json(monh, cmd, obj_print_json(octx, o));
}
void monitor_print_flowtable_json(struct netlink_mon_handler *monh,
diff --git a/src/parser_json.c b/src/parser_json.c
index ebb96d79..7764399a 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -439,6 +439,23 @@ static struct expr *json_parse_meta_expr(struct json_ctx *ctx,
return meta_expr_alloc(int_loc, key);
}
+static struct expr *json_parse_tunnel_expr(struct json_ctx *ctx,
+ const char *type, json_t *root)
+{
+ struct error_record *erec;
+ unsigned int key;
+ const char *name;
+
+ if (json_unpack_err(ctx, root, "{s:s}", "key", &name))
+ return NULL;
+ erec = tunnel_key_parse(int_loc, name, &key);
+ if (erec) {
+ erec_queue(erec, ctx->msgs);
+ return NULL;
+ }
+ return tunnel_expr_alloc(int_loc, key);
+}
+
static struct expr *json_parse_osf_expr(struct json_ctx *ctx,
const char *type, json_t *root)
{
@@ -1642,6 +1659,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
{ "rt", json_parse_rt_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "ct", json_parse_ct_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "numgen", json_parse_numgen_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "tunnel", json_parse_tunnel_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SES | CTX_F_MAP },
/* below two are hash expr */
{ "jhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "symhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
@@ -2202,6 +2220,23 @@ static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx,
return stmt;
}
+static struct stmt *json_parse_tunnel_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct stmt *stmt;
+
+ stmt = objref_stmt_alloc(int_loc);
+ stmt->objref.type = NFT_OBJECT_TUNNEL;
+ stmt->objref.expr = json_parse_stmt_expr(ctx, value);
+ if (!stmt->objref.expr) {
+ json_error(ctx, "Invalid tunnel reference.");
+ stmt_free(stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
static unsigned int json_parse_nat_flag(const char *flag)
{
const struct {
@@ -2870,6 +2905,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "synproxy", json_parse_synproxy_stmt },
{ "reset", json_parse_optstrip_stmt },
{ "secmark", json_parse_secmark_stmt },
+ { "tunnel", json_parse_tunnel_stmt },
};
const char *type;
unsigned int i;
@@ -3518,14 +3554,83 @@ static int json_parse_ct_timeout_policy(struct json_ctx *ctx,
return 0;
}
+static int json_parse_tunnel_erspan(struct json_ctx *ctx,
+ json_t *root, struct obj *obj)
+{
+ const char *dir;
+ json_t *tmp;
+ int i;
+
+ if (json_unpack_err(ctx, root, "{s:o}", "tunnel", &tmp))
+ return 1;
+
+ if (json_unpack_err(ctx, tmp, "{s:i}", "version", &obj->tunnel.erspan.version))
+ return 1;
+
+ switch (obj->tunnel.erspan.version) {
+ case 1:
+ if (json_unpack_err(ctx, tmp, "{s:i}",
+ "index", &obj->tunnel.erspan.v1.index))
+ return 1;
+ break;
+ case 2:
+ if (json_unpack_err(ctx, tmp, "{s:s, s:i}",
+ "direction", &dir,
+ "hwid", &i))
+ return 1;
+ obj->tunnel.erspan.v2.hwid = i;
+
+ if (!strcmp(dir, "ingress")) {
+ obj->tunnel.erspan.v2.direction = 0;
+ } else if (!strcmp(dir, "egress")) {
+ obj->tunnel.erspan.v2.direction = 1;
+ } else {
+ json_error(ctx, "Invalid direction '%s'.", dir);
+ return 1;
+ }
+ break;
+ default:
+ json_error(ctx, "Invalid erspan version %u" , obj->tunnel.erspan.version);
+ return 1;
+ }
+
+ return 0;
+}
+
+static enum tunnel_type json_parse_tunnel_type(struct json_ctx *ctx,
+ const char *type)
+{
+ const struct {
+ const char *type;
+ int val;
+ } type_tbl[] = {
+ { "erspan", TUNNEL_ERSPAN },
+ { "vxlan", TUNNEL_VXLAN },
+ { "geneve", TUNNEL_GENEVE },
+ };
+ unsigned int i;
+
+ if (!type)
+ return TUNNEL_UNSPEC;
+
+ for (i = 0; i < array_size(type_tbl); i++) {
+ if (!strcmp(type, type_tbl[i].type))
+ return type_tbl[i].val;
+ }
+
+ return TUNNEL_UNSPEC;
+}
+
static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
json_t *root, enum cmd_ops op,
enum cmd_obj cmd_obj)
{
- const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes";
+ const char *family, *tmp = NULL, *rate_unit = "packets", *burst_unit = "bytes";
uint32_t l3proto = NFPROTO_UNSPEC;
int inv = 0, flags = 0, i, j;
struct handle h = { 0 };
+ struct expr *expr;
+ json_t *tmp_json;
struct obj *obj;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
@@ -3713,8 +3818,87 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->synproxy.flags |= flags;
break;
- case CMD_OBJ_TUNNEL:
- /* TODO */
+ case NFT_OBJECT_TUNNEL:
+ cmd_obj = CMD_OBJ_TUNNEL;
+ obj->type = NFT_OBJECT_TUNNEL;
+ if (json_unpack_err(ctx, root, "{s:o}", "src", &tmp_json))
+ goto err_free_obj;
+ expr = json_parse_expr(ctx, tmp_json);
+ if (!expr)
+ goto err_free_obj;
+ obj->tunnel.src = expr;
+
+ if (json_unpack_err(ctx, root, "{s:o}", "dst", &tmp_json))
+ goto err_free_obj;
+ expr = json_parse_expr(ctx, tmp_json);
+ if (!expr)
+ goto err_free_obj;
+ obj->tunnel.dst = expr;
+
+ json_unpack(root, "{s:i}", "id", &obj->tunnel.id);
+ json_unpack(root, "{s:i}", "sport", &i);
+ obj->tunnel.sport = i;
+ json_unpack(root, "{s:i}", "dport", &i);
+ obj->tunnel.sport = i;
+ json_unpack(root, "{s:i}", "ttl", &i);
+ obj->tunnel.ttl = i;
+ json_unpack(root, "{s:i}", "tos", &i);
+ obj->tunnel.tos = i;
+ json_unpack(root, "{s:s}", "type", &tmp);
+
+ obj->tunnel.type = json_parse_tunnel_type(ctx, tmp);
+ switch (obj->tunnel.type) {
+ case TUNNEL_UNSPEC:
+ break;
+ case TUNNEL_ERSPAN:
+ if (json_parse_tunnel_erspan(ctx, root, obj))
+ goto err_free_obj;
+ break;
+ case TUNNEL_VXLAN:
+ if (json_unpack_err(ctx, root,
+ "{s:o}", "tunnel", &tmp_json))
+ goto err_free_obj;
+
+ json_unpack(tmp_json, "{s:i}",
+ "gbp", &obj->tunnel.vxlan.gbp);
+ break;
+ case TUNNEL_GENEVE:
+ json_t *value;
+ size_t index;
+
+ if (json_unpack_err(ctx, root,
+ "{s:o}", "tunnel", &tmp_json))
+ goto err_free_obj;
+
+ json_array_foreach(tmp_json, index, value) {
+ struct tunnel_geneve *geneve = xmalloc(sizeof(struct tunnel_geneve));
+ if (!geneve)
+ memory_allocation_error();
+
+ if (json_unpack_err(ctx, value, "{s:i, s:i, s:s}",
+ "class", &i,
+ "opt-type", &j,
+ "data", &tmp)) {
+ free(geneve);
+ goto err_free_obj;
+ }
+ geneve->geneve_class = i;
+ geneve->type = j;
+
+ if (tunnel_geneve_data_str2array(tmp,
+ geneve->data,
+ &geneve->data_len)) {
+ free(geneve);
+ goto err_free_obj;
+ }
+
+ if (index == 0)
+ init_list_head(&obj->tunnel.geneve_opts);
+
+ list_add_tail(&geneve->list, &obj->tunnel.geneve_opts);
+ }
+ break;
+ }
break;
default:
BUG("Invalid CMD '%d'", cmd_obj);
diff --git a/src/tunnel.c b/src/tunnel.c
index d03f853a..5685be2c 100644
--- a/src/tunnel.c
+++ b/src/tunnel.c
@@ -6,6 +6,8 @@
* published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <errno.h>
#include <limits.h>
#include <stddef.h>
@@ -38,6 +40,21 @@ const struct tunnel_template tunnel_templates[] = {
4 * 8, BYTEORDER_HOST_ENDIAN),
};
+struct error_record *tunnel_key_parse(const struct location *loc,
+ const char *str,
+ unsigned int *value)
+{
+ for (unsigned int i = 0; i < array_size(tunnel_templates); i++) {
+ if (!tunnel_templates[i].token || strcmp(tunnel_templates[i].token, str))
+ continue;
+
+ *value = i;
+ return NULL;
+ }
+
+ return error(loc, "syntax error, unexpected %s", str);
+}
+
static void tunnel_expr_print(const struct expr *expr, struct output_ctx *octx)
{
nft_print(octx, "tunnel %s",
@@ -58,6 +75,7 @@ const struct expr_ops tunnel_expr_ops = {
.type = EXPR_TUNNEL,
.name = "tunnel",
.print = tunnel_expr_print,
+ .json = tunnel_expr_json,
.cmp = tunnel_expr_cmp,
.clone = tunnel_expr_clone,
};
--
2.50.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 7/7 nft v2] tests: add tunnel shell and python tests
2025-08-14 11:04 [PATCH 1/7 nft v2] src: add tunnel template support Fernando Fernandez Mancera
` (4 preceding siblings ...)
2025-08-14 11:04 ` [PATCH 6/7 nft v2] tunnel: add tunnel object and statement json support Fernando Fernandez Mancera
@ 2025-08-14 11:04 ` Fernando Fernandez Mancera
2025-08-19 12:47 ` [PATCH 1/7 nft v2] src: add tunnel template support Pablo Neira Ayuso
6 siblings, 0 replies; 10+ messages in thread
From: Fernando Fernandez Mancera @ 2025-08-14 11:04 UTC (permalink / raw)
To: netfilter-devel; +Cc: coreteam, pablo, fw, Fernando Fernandez Mancera
Add tests for tunnel statement and object support. Shell and python
tests both cover standard nft output and json.
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
tests/py/netdev/tunnel.t | 7 +
tests/py/netdev/tunnel.t.json | 45 +++++
tests/py/netdev/tunnel.t.json.payload | 12 ++
tests/py/netdev/tunnel.t.payload | 12 ++
tests/shell/features/tunnel.nft | 17 ++
tests/shell/testcases/sets/0075tunnel_0 | 75 ++++++++
.../sets/dumps/0075tunnel_0.json-nft | 171 ++++++++++++++++++
.../testcases/sets/dumps/0075tunnel_0.nft | 63 +++++++
8 files changed, 402 insertions(+)
create mode 100644 tests/py/netdev/tunnel.t
create mode 100644 tests/py/netdev/tunnel.t.json
create mode 100644 tests/py/netdev/tunnel.t.json.payload
create mode 100644 tests/py/netdev/tunnel.t.payload
create mode 100644 tests/shell/features/tunnel.nft
create mode 100755 tests/shell/testcases/sets/0075tunnel_0
create mode 100644 tests/shell/testcases/sets/dumps/0075tunnel_0.json-nft
create mode 100644 tests/shell/testcases/sets/dumps/0075tunnel_0.nft
diff --git a/tests/py/netdev/tunnel.t b/tests/py/netdev/tunnel.t
new file mode 100644
index 00000000..920d21ff
--- /dev/null
+++ b/tests/py/netdev/tunnel.t
@@ -0,0 +1,7 @@
+:tunnelchain;type filter hook ingress device lo priority 0
+
+*netdev;test-netdev;tunnelchain
+
+tunnel path exists;ok
+tunnel path missing;ok
+tunnel id 10;ok
diff --git a/tests/py/netdev/tunnel.t.json b/tests/py/netdev/tunnel.t.json
new file mode 100644
index 00000000..3ca877d9
--- /dev/null
+++ b/tests/py/netdev/tunnel.t.json
@@ -0,0 +1,45 @@
+# tunnel path exists
+[
+ {
+ "match": {
+ "left": {
+ "tunnel": {
+ "key": "path"
+ }
+ },
+ "op": "==",
+ "right": true
+ }
+ }
+]
+
+# tunnel path missing
+[
+ {
+ "match": {
+ "left": {
+ "tunnel": {
+ "key": "path"
+ }
+ },
+ "op": "==",
+ "right": false
+ }
+ }
+]
+
+# tunnel id 10
+[
+ {
+ "match": {
+ "left": {
+ "tunnel": {
+ "key": "id"
+ }
+ },
+ "op": "==",
+ "right": 10
+ }
+ }
+]
+
diff --git a/tests/py/netdev/tunnel.t.json.payload b/tests/py/netdev/tunnel.t.json.payload
new file mode 100644
index 00000000..df127b6c
--- /dev/null
+++ b/tests/py/netdev/tunnel.t.json.payload
@@ -0,0 +1,12 @@
+# tunnel path exists
+netdev test-netdev tunnelchain
+ [ tunnel load path => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+# tunnel path missing
+netdev test-netdev tunnelchain
+ [ tunnel load path => reg 1 ]
+ [ cmp eq reg 1 0x00000000 ]
+# tunnel id 10
+netdev test-netdev tunnelchain
+ [ tunnel load id => reg 1 ]
+ [ cmp eq reg 1 0x0000000a ]
diff --git a/tests/py/netdev/tunnel.t.payload b/tests/py/netdev/tunnel.t.payload
new file mode 100644
index 00000000..df127b6c
--- /dev/null
+++ b/tests/py/netdev/tunnel.t.payload
@@ -0,0 +1,12 @@
+# tunnel path exists
+netdev test-netdev tunnelchain
+ [ tunnel load path => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+# tunnel path missing
+netdev test-netdev tunnelchain
+ [ tunnel load path => reg 1 ]
+ [ cmp eq reg 1 0x00000000 ]
+# tunnel id 10
+netdev test-netdev tunnelchain
+ [ tunnel load id => reg 1 ]
+ [ cmp eq reg 1 0x0000000a ]
diff --git a/tests/shell/features/tunnel.nft b/tests/shell/features/tunnel.nft
new file mode 100644
index 00000000..64b2f70b
--- /dev/null
+++ b/tests/shell/features/tunnel.nft
@@ -0,0 +1,17 @@
+# v5.7-rc1~146^2~137^2~26
+# 925d844696d9 ("netfilter: nft_tunnel: add support for geneve opts")
+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 0x2010 opt-type 0x2 data "0x87654321"
+ class 0x2020 opt-type 0x3 data "0x87654321abcdeffe"
+ }
+ }
+}
diff --git a/tests/shell/testcases/sets/0075tunnel_0 b/tests/shell/testcases/sets/0075tunnel_0
new file mode 100755
index 00000000..f8a8cf00
--- /dev/null
+++ b/tests/shell/testcases/sets/0075tunnel_0
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_tunnel)
+
+# * creating valid named objects
+# * referencing them from a valid rule
+
+RULESET="
+table netdev x {
+ tunnel geneve-t {
+ id 10
+ ip saddr 192.168.2.10
+ ip daddr 192.168.2.11
+ sport 10
+ dport 10
+ ttl 10
+ tos 10
+ geneve {
+ class 0x1 opt-type 0x1 data \"0x12345678\"
+ class 0x1010 opt-type 0x2 data \"0x87654321\"
+ class 0x2020 opt-type 0x3 data \"0x87654321abcdeffe\"
+ }
+ }
+
+ tunnel vxlan-t {
+ id 20
+ ip saddr 192.168.2.20
+ ip daddr 192.168.2.21
+ sport 20
+ dport 20
+ ttl 10
+ tos 10
+ vxlan {
+ gbp 200
+ }
+ }
+
+ tunnel erspan-tv1 {
+ id 30
+ ip saddr 192.168.2.30
+ ip daddr 192.168.2.31
+ sport 30
+ dport 30
+ ttl 10
+ tos 10
+ erspan {
+ version 1
+ index 5
+ }
+ }
+
+ tunnel erspan-tv2 {
+ id 40
+ ip saddr 192.168.2.40
+ ip daddr 192.168.2.41
+ sport 40
+ dport 40
+ ttl 10
+ tos 10
+ erspan {
+ version 2
+ direction ingress
+ id 10
+ }
+ }
+
+ chain x {
+ type filter hook ingress priority 0; policy accept;
+ tunnel name ip saddr map { 10.141.10.123 : "geneve-t", 10.141.10.124 : "vxlan-t", 10.141.10.125 : "erspan-tv1", 10.141.10.126 : "erspan-tv2" } counter
+ }
+}
+"
+
+set -e
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/sets/dumps/0075tunnel_0.json-nft b/tests/shell/testcases/sets/dumps/0075tunnel_0.json-nft
new file mode 100644
index 00000000..c3a7d522
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0075tunnel_0.json-nft
@@ -0,0 +1,171 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "x",
+ "handle": 0,
+ "type": "filter",
+ "hook": "ingress",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "tunnel": {
+ "family": "netdev",
+ "name": "geneve-t",
+ "table": "x",
+ "handle": 0,
+ "id": 10,
+ "src": "192.168.2.10",
+ "dst": "192.168.2.11",
+ "sport": 10,
+ "dport": 10,
+ "tos": 10,
+ "ttl": 10,
+ "type": "geneve",
+ "tunnel": [
+ {
+ "class": 1,
+ "opt-type": 1,
+ "data": "0x12345678"
+ },
+ {
+ "class": 4112,
+ "opt-type": 2,
+ "data": "0x87654321"
+ },
+ {
+ "class": 8224,
+ "opt-type": 3,
+ "data": "0x87654321abcdeffe"
+ }
+ ]
+ }
+ },
+ {
+ "tunnel": {
+ "family": "netdev",
+ "name": "vxlan-t",
+ "table": "x",
+ "handle": 0,
+ "id": 20,
+ "src": "192.168.2.20",
+ "dst": "192.168.2.21",
+ "sport": 20,
+ "dport": 20,
+ "tos": 10,
+ "ttl": 10,
+ "type": "vxlan",
+ "tunnel": {
+ "gbp": 200
+ }
+ }
+ },
+ {
+ "tunnel": {
+ "family": "netdev",
+ "name": "erspan-tv1",
+ "table": "x",
+ "handle": 0,
+ "id": 30,
+ "src": "192.168.2.30",
+ "dst": "192.168.2.31",
+ "sport": 30,
+ "dport": 30,
+ "tos": 10,
+ "ttl": 10,
+ "type": "erspan",
+ "tunnel": {
+ "version": 1,
+ "index": 5
+ }
+ }
+ },
+ {
+ "tunnel": {
+ "family": "netdev",
+ "name": "erspan-tv2",
+ "table": "x",
+ "handle": 0,
+ "id": 40,
+ "src": "192.168.2.40",
+ "dst": "192.168.2.41",
+ "sport": 40,
+ "dport": 40,
+ "tos": 10,
+ "ttl": 10,
+ "type": "erspan",
+ "tunnel": {
+ "version": 2,
+ "direction": "ingress",
+ "hwid": 10
+ }
+ }
+ },
+ {
+ "rule": {
+ "family": "netdev",
+ "table": "x",
+ "chain": "x",
+ "handle": 0,
+ "expr": [
+ {
+ "tunnel": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "10.141.10.123",
+ "geneve-t"
+ ],
+ [
+ "10.141.10.124",
+ "vxlan-t"
+ ],
+ [
+ "10.141.10.125",
+ "erspan-tv1"
+ ],
+ [
+ "10.141.10.126",
+ "erspan-tv2"
+ ]
+ ]
+ }
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0075tunnel_0.nft b/tests/shell/testcases/sets/dumps/0075tunnel_0.nft
new file mode 100644
index 00000000..9969124d
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0075tunnel_0.nft
@@ -0,0 +1,63 @@
+table netdev x {
+ tunnel geneve-t {
+ id 10
+ ip saddr 192.168.2.10
+ ip daddr 192.168.2.11
+ sport 10
+ dport 10
+ tos 10
+ ttl 10
+ geneve {
+ class 0x1 opt-type 0x1 data "0x12345678"
+ class 0x1010 opt-type 0x2 data "0x87654321"
+ class 0x2020 opt-type 0x3 data "0x87654321abcdeffe"
+ }
+ }
+
+ tunnel vxlan-t {
+ id 20
+ ip saddr 192.168.2.20
+ ip daddr 192.168.2.21
+ sport 20
+ dport 20
+ tos 10
+ ttl 10
+ vxlan {
+ gbp 200
+ }
+ }
+
+ tunnel erspan-tv1 {
+ id 30
+ ip saddr 192.168.2.30
+ ip daddr 192.168.2.31
+ sport 30
+ dport 30
+ tos 10
+ ttl 10
+ erspan {
+ version 1
+ index 5
+ }
+ }
+
+ tunnel erspan-tv2 {
+ id 40
+ ip saddr 192.168.2.40
+ ip daddr 192.168.2.41
+ sport 40
+ dport 40
+ tos 10
+ ttl 10
+ erspan {
+ version 2
+ direction ingress
+ id 10
+ }
+ }
+
+ chain x {
+ type filter hook ingress priority filter; policy accept;
+ tunnel name ip saddr map { 10.141.10.123 : "geneve-t", 10.141.10.124 : "vxlan-t", 10.141.10.125 : "erspan-tv1", 10.141.10.126 : "erspan-tv2" } counter packets 0 bytes 0
+ }
+}
--
2.50.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 1/7 nft v2] src: add tunnel template support
2025-08-14 11:04 [PATCH 1/7 nft v2] src: add tunnel template support Fernando Fernandez Mancera
` (5 preceding siblings ...)
2025-08-14 11:04 ` [PATCH 7/7 nft v2] tests: add tunnel shell and python tests Fernando Fernandez Mancera
@ 2025-08-19 12:47 ` Pablo Neira Ayuso
2025-08-19 15:57 ` Fernando Fernandez Mancera
6 siblings, 1 reply; 10+ messages in thread
From: Pablo Neira Ayuso @ 2025-08-19 12:47 UTC (permalink / raw)
To: Fernando Fernandez Mancera; +Cc: netfilter-devel, coreteam, fw
Hi,
I made a changes to this series and push it out to the 'tunnel' branch
under the nftables tree.
I added IPv6 support to this 1/7 patch.
However:
[PATCH 6/7] tunnel: add tunnel object and statement json support
still needs to be adjusted to also support IPv6.
I can see JSON representation uses "src" and "dst" as keys.
It is better to have keys that uniquely identify the datatype, I would
suggest:
"ip saddr"
"ip6 saddr"
(similar to ct expression)
or
"src-ipv4"
"src-ipv6"
else, anything you consider better for this.
I think this is the only remaining issue in this series IMO.
If you follow up, patch based your work on the two branches that I
have pushed out for libnftnl and nftables.
Thanks.
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 1/7 nft v2] src: add tunnel template support
2025-08-19 12:47 ` [PATCH 1/7 nft v2] src: add tunnel template support Pablo Neira Ayuso
@ 2025-08-19 15:57 ` Fernando Fernandez Mancera
2025-08-19 22:25 ` Pablo Neira Ayuso
0 siblings, 1 reply; 10+ messages in thread
From: Fernando Fernandez Mancera @ 2025-08-19 15:57 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, coreteam, fw
On 8/19/25 2:47 PM, Pablo Neira Ayuso wrote:
> Hi,
>
> I made a changes to this series and push it out to the 'tunnel' branch
> under the nftables tree.
>
> I added IPv6 support to this 1/7 patch.
>
> However:
>
> [PATCH 6/7] tunnel: add tunnel object and statement json support
>
> still needs to be adjusted to also support IPv6.
>
> I can see JSON representation uses "src" and "dst" as keys.
>
> It is better to have keys that uniquely identify the datatype, I would
> suggest:
>
> "ip saddr"
> "ip6 saddr"
>
> (similar to ct expression)
>
> or
>
> "src-ipv4"
> "src-ipv6"
>
Hi Pablo,
I would prefer the latter options "src-ipv4" and "src-ipv6" mainly
because adding spaces to keys in a JSON should be avoided.
I am sending a v3 with the requested changes, thank you!
> else, anything you consider better for this.
>
> I think this is the only remaining issue in this series IMO.
>
> If you follow up, patch based your work on the two branches that I
> have pushed out for libnftnl and nftables.
>
> Thanks.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/7 nft v2] src: add tunnel template support
2025-08-19 15:57 ` Fernando Fernandez Mancera
@ 2025-08-19 22:25 ` Pablo Neira Ayuso
0 siblings, 0 replies; 10+ messages in thread
From: Pablo Neira Ayuso @ 2025-08-19 22:25 UTC (permalink / raw)
To: Fernando Fernandez Mancera; +Cc: netfilter-devel, coreteam, fw
On Tue, Aug 19, 2025 at 05:57:55PM +0200, Fernando Fernandez Mancera wrote:
>
>
> On 8/19/25 2:47 PM, Pablo Neira Ayuso wrote:
> > Hi,
> >
> > I made a changes to this series and push it out to the 'tunnel' branch
> > under the nftables tree.
> >
> > I added IPv6 support to this 1/7 patch.
> >
> > However:
> >
> > [PATCH 6/7] tunnel: add tunnel object and statement json support
> >
> > still needs to be adjusted to also support IPv6.
> >
> > I can see JSON representation uses "src" and "dst" as keys.
> >
> > It is better to have keys that uniquely identify the datatype, I would
> > suggest:
> >
> > "ip saddr"
> > "ip6 saddr"
> >
> > (similar to ct expression)
> >
> > or
> >
> > "src-ipv4"
> > "src-ipv6"
> >
>
> Hi Pablo,
>
> I would prefer the latter options "src-ipv4" and "src-ipv6" mainly because
> adding spaces to keys in a JSON should be avoided.
>
> I am sending a v3 with the requested changes, thank you!
OK, thanks.
^ permalink raw reply [flat|nested] 10+ messages in thread