All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support
@ 2025-05-27 19:54 Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 1/7 nft] src: add tunnel template support Fernando Fernandez Mancera
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-27 19:54 UTC (permalink / raw)
  To: netfilter-devel; +Cc: coreteam, Fernando Fernandez Mancera

This patch series add nftables tunnel expression, statement and object
support. The implementation was splitted in multiple patches to ease the
review process. All the tests are passing and no memory leaks were
detected with valgrind enabled.

Please notice this series requires the following patches:

* libnftnl:
  - https://lore.kernel.org/netfilter-devel/20250527193420.9860-1-fmancera@suse.de/T/#t

* Linux kernel (already applied on net-next):
  - https://lore.kernel.org/netfilter-devel/20250521094108.23690-1-fmancera@suse.de/T/#u

Fernando Fernandez Mancera (4):
  tunnel: add vxlan support
  tunnel: add geneve support
  tunnel: add tunnel object and statement json support
  tests: add tunnel shell and python tests

Pablo Neira Ayuso (3):
  src: add tunnel template support
  tunnel: add erspan support
  src: add tunnel statement and expression support

 Makefile.am                                   |   2 +
 include/expression.h                          |   6 +
 include/json.h                                |   1 +
 include/parser.h                              |   1 +
 include/rule.h                                |  49 ++++
 include/tunnel.h                              |  37 +++
 src/cache.c                                   |   2 +
 src/evaluate.c                                |  30 +++
 src/expression.c                              |   1 +
 src/json.c                                    |  99 +++++++-
 src/mnl.c                                     |  77 ++++++
 src/netlink.c                                 | 121 +++++++++
 src/netlink_delinearize.c                     |  17 ++
 src/netlink_linearize.c                       |  14 ++
 src/parser_bison.y                            | 236 +++++++++++++++++-
 src/parser_json.c                             | 190 ++++++++++++++
 src/rule.c                                    | 178 ++++++++++++-
 src/scanner.l                                 |  24 +-
 src/statement.c                               |   1 +
 src/tunnel.c                                  |  94 +++++++
 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 +++++
 28 files changed, 1568 insertions(+), 14 deletions(-)
 create mode 100644 include/tunnel.h
 create mode 100644 src/tunnel.c
 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

-- 
2.49.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/7 nft] src: add tunnel template support
  2025-05-27 19:54 [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support Fernando Fernandez Mancera
@ 2025-05-27 19:54 ` Fernando Fernandez Mancera
  2025-05-28  0:38   ` Florian Westphal
  2025-05-27 19:54 ` [PATCH 2/7 nft] tunnel: add erspan support Fernando Fernandez Mancera
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-27 19:54 UTC (permalink / raw)
  To: netfilter-devel; +Cc: coreteam, Pablo Neira Ayuso

From: Pablo Neira Ayuso <pablo@netfilter.org>

This patch adds tunnel template support, this allows to attach a
metadata template that provides the configuration for the tunnel driver.

Example of generic tunnel configuration:

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

Joint work with Fernando.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/parser.h   |  1 +
 include/rule.h     | 13 +++++++
 src/cache.c        |  2 +
 src/evaluate.c     | 23 ++++++++++++
 src/json.c         |  7 ++++
 src/mnl.c          | 40 ++++++++++++++++++++
 src/netlink.c      | 81 ++++++++++++++++++++++++++++++++++++++++
 src/parser_bison.y | 92 ++++++++++++++++++++++++++++++++++++++++++++--
 src/parser_json.c  |  8 ++++
 src/rule.c         | 74 ++++++++++++++++++++++++++++++++++++-
 src/scanner.l      | 12 ++++++
 11 files changed, 349 insertions(+), 4 deletions(-)

diff --git a/include/parser.h b/include/parser.h
index 576e5e43..8cfd22e9 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -51,6 +51,7 @@ enum startcond_type {
 	PARSER_SC_SECMARK,
 	PARSER_SC_TCP,
 	PARSER_SC_TYPE,
+	PARSER_SC_TUNNEL,
 	PARSER_SC_VLAN,
 	PARSER_SC_XT,
 	PARSER_SC_CMD_DESTROY,
diff --git a/include/rule.h b/include/rule.h
index 85a0d9c0..c10db949 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -490,6 +490,16 @@ struct secmark {
 	char		ctx[NFT_SECMARK_CTX_MAXLEN];
 };
 
+struct tunnel {
+	uint32_t	id;
+	struct expr	*src;
+	struct expr	*dst;
+	uint16_t	sport;
+	uint16_t	dport;
+	uint8_t		tos;
+	uint8_t		ttl;
+};
+
 /**
  * struct obj - nftables stateful object statement
  *
@@ -516,6 +526,7 @@ struct obj {
 		struct secmark		secmark;
 		struct ct_expect	ct_expect;
 		struct synproxy		synproxy;
+		struct tunnel		tunnel;
 	};
 };
 
@@ -662,6 +673,8 @@ enum cmd_obj {
 	CMD_OBJ_CT_EXPECTATIONS,
 	CMD_OBJ_SYNPROXY,
 	CMD_OBJ_SYNPROXYS,
+	CMD_OBJ_TUNNEL,
+	CMD_OBJ_TUNNELS,
 	CMD_OBJ_HOOKS,
 };
 
diff --git a/src/cache.c b/src/cache.c
index 3ac819cf..b3984a24 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -443,6 +443,8 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
 	case CMD_OBJ_CT_TIMEOUTS:
 	case CMD_OBJ_CT_EXPECT:
 	case CMD_OBJ_CT_EXPECTATIONS:
+	case CMD_OBJ_TUNNEL:
+	case CMD_OBJ_TUNNELS:
 		if (h->table.name &&
 		    strlen(h->table.name) > NFT_NAME_MAXLEN) {
 			loc = &h->table.location;
diff --git a/src/evaluate.c b/src/evaluate.c
index 9c7f23cb..84c150d8 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -5640,6 +5640,26 @@ static int ct_timeout_evaluate(struct eval_ctx *ctx, struct obj *obj)
 	return 0;
 }
 
+static int tunnel_evaluate(struct eval_ctx *ctx, struct obj *obj)
+{
+	if (obj->tunnel.src != NULL) {
+		__expr_set_context(&ctx->ectx, &ipaddr_type,
+				   BYTEORDER_BIG_ENDIAN,
+				   sizeof(struct in_addr) * BITS_PER_BYTE, 0);
+		if (expr_evaluate(ctx, &obj->tunnel.src) < 0)
+			return -1;
+	}
+	if (obj->tunnel.dst != NULL) {
+		__expr_set_context(&ctx->ectx, &ipaddr_type,
+				   BYTEORDER_BIG_ENDIAN,
+				   sizeof(struct in_addr) * BITS_PER_BYTE, 0);
+		if (expr_evaluate(ctx, &obj->tunnel.dst) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
 static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj)
 {
 	struct table *table;
@@ -5658,6 +5678,8 @@ static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj)
 		return ct_timeout_evaluate(ctx, obj);
 	case NFT_OBJECT_CT_EXPECT:
 		return ct_expect_evaluate(ctx, obj);
+	case NFT_OBJECT_TUNNEL:
+		return tunnel_evaluate(ctx, obj);
 	default:
 		break;
 	}
@@ -5710,6 +5732,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_SECMARK:
 	case CMD_OBJ_CT_EXPECT:
 	case CMD_OBJ_SYNPROXY:
+	case CMD_OBJ_TUNNEL:
 		handle_merge(&cmd->object->handle, &cmd->handle);
 		return obj_evaluate(ctx, cmd->object);
 	default:
diff --git a/src/json.c b/src/json.c
index cbed9ce9..a8fc10c4 100644
--- a/src/json.c
+++ b/src/json.c
@@ -468,6 +468,9 @@ static json_t *obj_print_json(const struct obj *obj)
 		json_object_update(root, tmp);
 		json_decref(tmp);
 		break;
+	case NFT_OBJECT_TUNNEL:
+		/* TODO */
+		break;
 	}
 
 	return json_pack("{s:o}", type, root);
@@ -1997,6 +2000,10 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_SYNPROXYS:
 		root = do_list_obj_json(ctx, cmd, NFT_OBJECT_SYNPROXY);
 		break;
+	case CMD_OBJ_TUNNEL:
+	case CMD_OBJ_TUNNELS:
+		root = do_list_obj_json(ctx, cmd, NFT_OBJECT_TUNNEL);
+		break;
 	case CMD_OBJ_FLOWTABLE:
 		root = do_list_flowtable_json(ctx, cmd, table);
 		break;
diff --git a/src/mnl.c b/src/mnl.c
index 64b1aaed..ee46892e 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1453,6 +1453,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
 		    unsigned int flags)
 {
 	struct obj *obj = cmd->object;
+	struct nft_data_linearize nld;
 	struct nftnl_udata_buf *udbuf;
 	struct nftnl_obj *nlo;
 	struct nlmsghdr *nlh;
@@ -1541,6 +1542,45 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
 		nftnl_obj_set_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS,
 				  obj->synproxy.flags);
 		break;
+	case NFT_OBJECT_TUNNEL:
+		nftnl_obj_set_u32(nlo, NFTNL_OBJ_TUNNEL_ID, obj->tunnel.id);
+		if (obj->tunnel.sport)
+			nftnl_obj_set_u16(nlo, NFTNL_OBJ_TUNNEL_SPORT,
+					  obj->tunnel.sport);
+		if (obj->tunnel.dport)
+			nftnl_obj_set_u16(nlo, NFTNL_OBJ_TUNNEL_DPORT,
+					  obj->tunnel.dport);
+		if (obj->tunnel.tos)
+			nftnl_obj_set_u8(nlo, NFTNL_OBJ_TUNNEL_TOS,
+					  obj->tunnel.tos);
+		if (obj->tunnel.ttl)
+			nftnl_obj_set_u8(nlo, NFTNL_OBJ_TUNNEL_TTL,
+					  obj->tunnel.ttl);
+		if (obj->tunnel.src) {
+			netlink_gen_data(obj->tunnel.src, &nld);
+			if (nld.len == sizeof(struct in_addr)) {
+				nftnl_obj_set_u32(nlo,
+						  NFTNL_OBJ_TUNNEL_IPV4_SRC,
+						  nld.value[0]);
+			} else {
+				nftnl_obj_set_data(nlo,
+						   NFTNL_OBJ_TUNNEL_IPV6_SRC,
+						   nld.value, nld.len);
+			}
+		}
+		if (obj->tunnel.dst) {
+			netlink_gen_data(obj->tunnel.dst, &nld);
+			if (nld.len == sizeof(struct in_addr)) {
+				nftnl_obj_set_u32(nlo,
+						  NFTNL_OBJ_TUNNEL_IPV4_DST,
+						  nld.value[0]);
+		        } else {
+				nftnl_obj_set_data(nlo,
+						   NFTNL_OBJ_TUNNEL_IPV6_DST,
+						   nld.value, nld.len);
+			}
+		}
+		break;
 	default:
 		BUG("Unknown type %d\n", obj->type);
 		break;
diff --git a/src/netlink.c b/src/netlink.c
index bed816af..ccf43580 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1692,6 +1692,51 @@ void netlink_dump_obj(struct nftnl_obj *nln, struct netlink_ctx *ctx)
 	fprintf(fp, "\n");
 }
 
+static struct in6_addr all_zeroes;
+
+static struct expr *
+netlink_obj_tunnel_parse_addr(struct nftnl_obj *nlo, int attr)
+{
+	struct nft_data_delinearize nld;
+	const struct datatype *dtype;
+	const uint32_t *addr6;
+	struct expr *expr;
+	uint32_t addr;
+
+	memset(&nld, 0, sizeof(nld));
+
+	switch (attr) {
+	case NFTNL_OBJ_TUNNEL_IPV4_SRC:
+	case NFTNL_OBJ_TUNNEL_IPV4_DST:
+		addr = nftnl_obj_get_u32(nlo, attr);
+		if (!addr)
+			return NULL;
+
+		dtype = &ipaddr_type;
+		nld.value = &addr;
+		nld.len = sizeof(struct in_addr);
+		break;
+	case NFTNL_OBJ_TUNNEL_IPV6_SRC:
+	case NFTNL_OBJ_TUNNEL_IPV6_DST:
+		addr6 = nftnl_obj_get(nlo, attr);
+		if (!memcmp(addr6, &all_zeroes, sizeof(all_zeroes)))
+			return NULL;
+
+		dtype = &ip6addr_type;
+		nld.value = addr6;
+		nld.len = sizeof(struct in6_addr);
+		break;
+	default:
+		return NULL;
+	}
+
+	expr = netlink_alloc_value(&netlink_location, &nld);
+	expr->dtype     = dtype;
+	expr->byteorder = BYTEORDER_BIG_ENDIAN;
+
+	return expr;
+}
+
 static int obj_parse_udata_cb(const struct nftnl_udata *attr, void *data)
 {
 	unsigned char *value = nftnl_udata_get(attr);
@@ -1805,6 +1850,42 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
 		obj->synproxy.flags =
 			nftnl_obj_get_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS);
 		break;
+	case NFT_OBJECT_TUNNEL:
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_ID))
+			obj->tunnel.id = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TUNNEL_ID);
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_SPORT)) {
+			obj->tunnel.sport =
+				nftnl_obj_get_u16(nlo, NFTNL_OBJ_TUNNEL_SPORT);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_DPORT)) {
+			obj->tunnel.dport =
+				nftnl_obj_get_u16(nlo, NFTNL_OBJ_TUNNEL_DPORT);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_TOS)) {
+			obj->tunnel.tos =
+				nftnl_obj_get_u8(nlo, NFTNL_OBJ_TUNNEL_TOS);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_TTL)) {
+			obj->tunnel.ttl =
+				nftnl_obj_get_u8(nlo, NFTNL_OBJ_TUNNEL_TTL);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_IPV4_SRC)) {
+			obj->tunnel.src =
+				netlink_obj_tunnel_parse_addr(nlo, NFTNL_OBJ_TUNNEL_IPV4_SRC);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_IPV4_DST)) {
+			obj->tunnel.dst =
+				netlink_obj_tunnel_parse_addr(nlo, NFTNL_OBJ_TUNNEL_IPV4_DST);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_IPV6_SRC)) {
+			obj->tunnel.src =
+				netlink_obj_tunnel_parse_addr(nlo, NFTNL_OBJ_TUNNEL_IPV6_SRC);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_IPV6_DST)) {
+			obj->tunnel.dst =
+				netlink_obj_tunnel_parse_addr(nlo, NFTNL_OBJ_TUNNEL_IPV6_DST);
+		}
+		break;
 	default:
 		netlink_io_error(ctx, NULL, "Unknown object type %u", type);
 		obj_free(obj);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index ed6a24a1..c4694c77 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -410,6 +410,7 @@ int nft_lex(void *, void *, void *);
 %token LENGTH			"length"
 %token FRAG_OFF			"frag-off"
 %token TTL			"ttl"
+%token TOS			"tos"
 %token PROTOCOL			"protocol"
 %token CHECKSUM			"checksum"
 
@@ -604,9 +605,12 @@ int nft_lex(void *, void *, void *);
 %token LAST			"last"
 %token NEVER			"never"
 
+%token TUNNEL			"tunnel"
+
 %token COUNTERS			"counters"
 %token QUOTAS			"quotas"
 %token LIMITS			"limits"
+%token TUNNELS			"tunnels"
 %token SYNPROXYS		"synproxys"
 %token HELPERS			"helpers"
 
@@ -760,7 +764,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
+%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
 %destructor { obj_free($$); }	obj_block_alloc
 
 %type <list>			stmt_list stateful_stmt_list set_elem_stmt_list
@@ -879,8 +883,8 @@ int nft_lex(void *, void *, void *);
 %type <expr>			and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
 %destructor { expr_free($$); }	and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
 
-%type <obj>			counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj
-%destructor { obj_free($$); }	counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj
+%type <obj>			counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj tunnel_obj
+%destructor { obj_free($$); }	counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj tunnel_obj
 
 %type <expr>			relational_expr
 %destructor { expr_free($$); }	relational_expr
@@ -1083,6 +1087,7 @@ close_scope_udplite	: { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPL
 
 close_scope_log		: { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_LOG); }
 close_scope_synproxy	: { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_SYNPROXY); }
+close_scope_tunnel	: { scanner_pop_start_cond(nft->scanner, PARSER_SC_TUNNEL); }
 close_scope_xt		: { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); }
 
 common_block		:	INCLUDE		QUOTED_STRING	stmt_separator
@@ -1299,6 +1304,10 @@ add_cmd			:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
 			}
+			|	TUNNEL		obj_spec	tunnel_obj	'{' tunnel_block '}' close_scope_tunnel
+			{
+				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_TUNNEL, &$2, &@$, $3);
+			}
 			;
 
 replace_cmd		:	RULE		ruleid_spec	rule
@@ -1402,6 +1411,10 @@ create_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
 			}
+			|	TUNNEL		obj_spec	tunnel_obj	'{' tunnel_block '}'	close_scope_tunnel
+			{
+				$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_TUNNEL, &$2, &@$, $3);
+			}
 			;
 
 insert_cmd		:	RULE		rule_position	rule
@@ -1499,6 +1512,10 @@ delete_cmd		:	TABLE		table_or_id_spec
 			{
 				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
 			}
+			|	TUNNEL		obj_or_id_spec	close_scope_tunnel
+			{
+				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_TUNNEL, &$2, &@$, NULL);
+			}
 			;
 
 destroy_cmd		:	TABLE		table_or_id_spec
@@ -1566,6 +1583,10 @@ destroy_cmd		:	TABLE		table_or_id_spec
 			{
 				$$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
 			}
+			|	TUNNEL		obj_or_id_spec	close_scope_tunnel
+			{
+				$$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_TUNNEL, &$2, &@$, NULL);
+			}
 			;
 
 
@@ -2045,6 +2066,17 @@ table_block		:	/* empty */	{ $$ = $<table>-1; }
 				list_add_tail(&$4->list, &$1->objs);
 				$$ = $1;
 			}
+			|	table_block	TUNNEL	obj_identifier
+					obj_block_alloc '{'     tunnel_block     '}'
+					stmt_separator close_scope_tunnel
+			{
+				$4->location = @3;
+				$4->type = NFT_OBJECT_TUNNEL;
+				handle_merge(&$4->handle, &$3);
+				handle_free(&$3);
+				list_add_tail(&$4->list, &$1->objs);
+				$$ = $1;
+			}
 			;
 
 chain_block_alloc	:	/* empty */
@@ -4916,6 +4948,60 @@ limit_obj		:	/* empty */
 			}
 			;
 
+tunnel_config		:	ID	NUM
+			{
+				$<obj>0->tunnel.id = $2;
+			}
+			|	IP	SADDR	expr	close_scope_ip
+			{
+				$<obj>0->tunnel.src = $3;
+			}
+			|	IP	DADDR	expr	close_scope_ip
+			{
+				$<obj>0->tunnel.dst = $3;
+			}
+			|	SPORT	NUM
+			{
+				$<obj>0->tunnel.sport = $2;
+			}
+			|	DPORT	NUM
+			{
+				$<obj>0->tunnel.dport = $2;
+			}
+			|	TTL	NUM
+			{
+				$<obj>0->tunnel.ttl = $2;
+			}
+			|	TOS	NUM
+			{
+				$<obj>0->tunnel.tos = $2;
+			}
+			;
+
+tunnel_block		:	/* empty */	{ $$ = $<obj>-1; }
+			|       tunnel_block     common_block
+			|       tunnel_block     stmt_separator
+			|       tunnel_block     tunnel_config	stmt_separator
+			{
+				$$ = $1;
+			}
+			|       tunnel_block     comment_spec
+			{
+				if (already_set($<obj>1->comment, &@2, state)) {
+					free_const($2);
+					YYERROR;
+				}
+				$<obj>1->comment = $2;
+			}
+			;
+
+tunnel_obj		:	/* empty */
+			{
+				$$ = obj_alloc(&@$);
+				$$->type = NFT_OBJECT_TUNNEL;
+			}
+			;
+
 relational_expr		:	expr	/* implicit */	rhs_expr
 			{
 				$$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2);
diff --git a/src/parser_json.c b/src/parser_json.c
index e3dd14cd..c1bd7570 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3161,6 +3161,7 @@ static int string_to_nft_object(const char *str)
 		[NFT_OBJECT_SECMARK]	= "secmark",
 		[NFT_OBJECT_CT_EXPECT]	= "ct expectation",
 		[NFT_OBJECT_SYNPROXY]	= "synproxy",
+		[NFT_OBJECT_TUNNEL]	= "tunnel",
 	};
 	unsigned int i;
 
@@ -3661,6 +3662,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
 
 		obj->synproxy.flags |= flags;
 		break;
+	case CMD_OBJ_TUNNEL:
+		/* TODO */
+		break;
 	default:
 		BUG("Invalid CMD '%d'", cmd_obj);
 	}
@@ -3697,6 +3701,7 @@ static struct cmd *json_parse_cmd_add(struct json_ctx *ctx,
 		{ "ct helper", NFT_OBJECT_CT_HELPER, json_parse_cmd_add_object },
 		{ "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object },
 		{ "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object },
+		{ "tunnel", NFT_OBJECT_TUNNEL, json_parse_cmd_add_object },
 		{ "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object },
 		{ "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object },
 		{ "synproxy", CMD_OBJ_SYNPROXY, json_parse_cmd_add_object }
@@ -3832,6 +3837,7 @@ static struct cmd *json_parse_cmd_list_multiple(struct json_ctx *ctx,
 	case CMD_OBJ_SETS:
 	case CMD_OBJ_COUNTERS:
 	case CMD_OBJ_CT_HELPERS:
+	case CMD_OBJ_TUNNELS:
 		if (!json_unpack(root, "{s:s}", "table", &tmp))
 			h.table.name = xstrdup(tmp);
 		break;
@@ -3870,6 +3876,8 @@ static struct cmd *json_parse_cmd_list(struct json_ctx *ctx,
 		{ "ct helpers", CMD_OBJ_CT_HELPERS, json_parse_cmd_list_multiple },
 		{ "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object },
 		{ "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object },
+		{ "tunnel", NFT_OBJECT_TUNNEL, json_parse_cmd_add_object },
+		{ "tunnels", CMD_OBJ_TUNNELS, json_parse_cmd_list_multiple },
 		{ "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object },
 		{ "limits", CMD_OBJ_LIMIT, json_parse_cmd_list_multiple },
 		{ "ruleset", CMD_OBJ_RULESET, json_parse_cmd_list_multiple },
diff --git a/src/rule.c b/src/rule.c
index 80315837..1cea0d48 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1426,6 +1426,7 @@ void cmd_free(struct cmd *cmd)
 		case CMD_OBJ_LIMIT:
 		case CMD_OBJ_SECMARK:
 		case CMD_OBJ_SYNPROXY:
+		case CMD_OBJ_TUNNEL:
 			obj_free(cmd->object);
 			break;
 		case CMD_OBJ_FLOWTABLE:
@@ -1526,6 +1527,7 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
 	case CMD_OBJ_LIMIT:
 	case CMD_OBJ_SECMARK:
 	case CMD_OBJ_SYNPROXY:
+	case CMD_OBJ_TUNNEL:
 		return mnl_nft_obj_add(ctx, cmd, flags);
 	case CMD_OBJ_FLOWTABLE:
 		return mnl_nft_flowtable_add(ctx, cmd, flags);
@@ -1606,6 +1608,8 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
 		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SECMARK);
 	case CMD_OBJ_SYNPROXY:
 		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SYNPROXY);
+	case CMD_OBJ_TUNNEL:
+		return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_TUNNEL);
 	case CMD_OBJ_FLOWTABLE:
 		return mnl_nft_flowtable_del(ctx, cmd);
 	default:
@@ -1676,7 +1680,8 @@ void obj_free(struct obj *obj)
 		return;
 	free_const(obj->comment);
 	handle_free(&obj->handle);
-	if (obj->type == NFT_OBJECT_CT_TIMEOUT) {
+	switch (obj->type) {
+	case NFT_OBJECT_CT_TIMEOUT: {
 		struct timeout_state *ts, *next;
 
 		list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) {
@@ -1684,6 +1689,14 @@ void obj_free(struct obj *obj)
 			free_const(ts->timeout_str);
 			free(ts);
 		}
+		}
+		break;
+	case NFT_OBJECT_TUNNEL:
+		expr_free(obj->tunnel.src);
+		expr_free(obj->tunnel.dst);
+		break;
+	default:
+		break;
 	}
 	free(obj);
 }
@@ -1940,6 +1953,60 @@ static void obj_print_data(const struct obj *obj,
 		nft_print(octx, "%s", opts->stmt_separator);
 		}
 		break;
+	case NFT_OBJECT_TUNNEL:
+		nft_print(octx, " %s {", obj->handle.obj.name);
+		if (nft_output_handle(octx))
+			nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+		obj_print_comment(obj, opts, octx);
+
+		nft_print(octx, "%s%s%sid %u",
+			  opts->nl, opts->tab, opts->tab, obj->tunnel.id);
+
+		if (obj->tunnel.src) {
+			if (obj->tunnel.src->len == 32) {
+				nft_print(octx, "%s%s%sip saddr ",
+					  opts->nl, opts->tab, opts->tab);
+				expr_print(obj->tunnel.src, octx);
+			} else if (obj->tunnel.src->len == 128) {
+				nft_print(octx, "%s%s%sip6 saddr ",
+					  opts->nl, opts->tab, opts->tab);
+				expr_print(obj->tunnel.src, octx);
+			}
+		}
+		if (obj->tunnel.dst) {
+			if (obj->tunnel.dst->len == 32) {
+				nft_print(octx, "%s%s%sip daddr ",
+					  opts->nl, opts->tab, opts->tab);
+				expr_print(obj->tunnel.dst, octx);
+			} else if (obj->tunnel.dst->len == 128) {
+				nft_print(octx, "%s%s%sip6 daddr ",
+					  opts->nl, opts->tab, opts->tab);
+				expr_print(obj->tunnel.dst, octx);
+			}
+		}
+		if (obj->tunnel.sport) {
+			nft_print(octx, "%s%s%ssport %u",
+				  opts->nl, opts->tab, opts->tab,
+				  obj->tunnel.sport);
+		}
+		if (obj->tunnel.dport) {
+			nft_print(octx, "%s%s%sdport %u",
+				  opts->nl, opts->tab, opts->tab,
+				  obj->tunnel.dport);
+		}
+		if (obj->tunnel.tos) {
+			nft_print(octx, "%s%s%stos %u",
+				  opts->nl, opts->tab, opts->tab,
+				  obj->tunnel.tos);
+		}
+		if (obj->tunnel.ttl) {
+			nft_print(octx, "%s%s%sttl %u",
+				  opts->nl, opts->tab, opts->tab,
+				  obj->tunnel.ttl);
+		}
+		nft_print(octx, "%s", opts->stmt_separator);
+		break;
 	default:
 		nft_print(octx, " unknown {%s", opts->nl);
 		break;
@@ -1955,6 +2022,7 @@ static const char * const obj_type_name_array[] = {
 	[NFT_OBJECT_SECMARK]	= "secmark",
 	[NFT_OBJECT_SYNPROXY]	= "synproxy",
 	[NFT_OBJECT_CT_EXPECT]	= "ct expectation",
+	[NFT_OBJECT_TUNNEL]	= "tunnel",
 };
 
 const char *obj_type_name(unsigned int type)
@@ -1973,6 +2041,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = {
 	[NFT_OBJECT_SECMARK]	= CMD_OBJ_SECMARK,
 	[NFT_OBJECT_SYNPROXY]	= CMD_OBJ_SYNPROXY,
 	[NFT_OBJECT_CT_EXPECT]	= CMD_OBJ_CT_EXPECT,
+	[NFT_OBJECT_TUNNEL]	= CMD_OBJ_TUNNEL,
 };
 
 enum cmd_obj obj_type_to_cmd(uint32_t type)
@@ -2439,6 +2508,9 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_SYNPROXY:
 	case CMD_OBJ_SYNPROXYS:
 		return do_list_obj(ctx, cmd, NFT_OBJECT_SYNPROXY);
+	case CMD_OBJ_TUNNEL:
+	case CMD_OBJ_TUNNELS:
+		return do_list_obj(ctx, cmd, NFT_OBJECT_TUNNEL);
 	case CMD_OBJ_FLOWTABLE:
 		return do_list_flowtable(ctx, cmd, table);
 	case CMD_OBJ_FLOWTABLES:
diff --git a/src/scanner.l b/src/scanner.l
index 4787cc12..232bce67 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -223,6 +223,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 %s SCANSTATE_SECMARK
 %s SCANSTATE_TCP
 %s SCANSTATE_TYPE
+%s SCANSTATE_TUNNEL
 %s SCANSTATE_VLAN
 %s SCANSTATE_XT
 %s SCANSTATE_CMD_DESTROY
@@ -403,6 +404,8 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 	"maps"			{ return MAPS; }
 	"secmarks"		{ return SECMARKS; }
 	"synproxys"		{ return SYNPROXYS; }
+	"tunnel"		{ return TUNNEL; }
+	"tunnels"		{ return TUNNELS; }
 	"hooks"			{ return HOOKS; }
 }
 
@@ -807,6 +810,15 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 	"sack-perm"		{ return SACK_PERM; }
 }
 
+"tunnel"		{ scanner_push_start_cond(yyscanner, SCANSTATE_TUNNEL); return TUNNEL; }
+<SCANSTATE_TUNNEL>{
+	"id"			{ return ID; }
+	"sport"			{ return SPORT; }
+	"dport"			{ return DPORT; }
+	"ttl"			{ return TTL; }
+	"tos"			{ return TOS; }
+}
+
 "notrack"		{ return NOTRACK; }
 
 "all"			{ return ALL; }
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/7 nft] tunnel: add erspan support
  2025-05-27 19:54 [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 1/7 nft] src: add tunnel template support Fernando Fernandez Mancera
@ 2025-05-27 19:54 ` Fernando Fernandez Mancera
  2025-05-28  0:46   ` Florian Westphal
  2025-05-27 19:54 ` [PATCH 3/7 nft] src: add tunnel statement and expression support Fernando Fernandez Mancera
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-27 19:54 UTC (permalink / raw)
  To: netfilter-devel; +Cc: coreteam, Pablo Neira Ayuso

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          | 24 ++++++++++++++++++++++++
 src/netlink.c      | 16 ++++++++++++++++
 src/parser_bison.y | 46 +++++++++++++++++++++++++++++++++++++++++++++-
 src/rule.c         | 26 ++++++++++++++++++++++++++
 src/scanner.l      |  5 +++++
 6 files changed, 134 insertions(+), 1 deletion(-)

diff --git a/include/rule.h b/include/rule.h
index c10db949..2723af38 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -490,6 +490,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;
@@ -498,6 +503,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 ee46892e..34d919ea 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1580,6 +1580,30 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
 						   nld.value, nld.len);
 			}
 		}
+		switch (obj->tunnel.type) {
+		case TUNNEL_ERSPAN:
+			nftnl_obj_set_u32(nlo,
+					  NFTNL_OBJ_TUNNEL_ERSPAN_VERSION,
+					  obj->tunnel.erspan.version);
+			switch (obj->tunnel.erspan.version) {
+			case 1:
+				nftnl_obj_set_u32(nlo,
+						  NFTNL_OBJ_TUNNEL_ERSPAN_V1_INDEX,
+						  obj->tunnel.erspan.v1.index);
+				break;
+			case 2:
+				nftnl_obj_set_u8(nlo,
+						 NFTNL_OBJ_TUNNEL_ERSPAN_V2_DIR,
+						 obj->tunnel.erspan.v2.direction);
+				nftnl_obj_set_u8(nlo,
+						 NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID,
+						 obj->tunnel.erspan.v2.hwid);
+				break;
+			}
+			break;
+		case TUNNEL_UNSPEC:
+			break;
+		}
 		break;
 	default:
 		BUG("Unknown type %d\n", obj->type);
diff --git a/src/netlink.c b/src/netlink.c
index ccf43580..086846ce 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1885,6 +1885,22 @@ 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_ERSPAN_VERSION)) {
+			obj->tunnel.type = TUNNEL_ERSPAN;
+			obj->tunnel.erspan.version = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TUNNEL_ERSPAN_VERSION);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_ERSPAN_V1_INDEX)) {
+			obj->tunnel.type = TUNNEL_ERSPAN;
+			obj->tunnel.erspan.v1.index = nftnl_obj_get_u8(nlo, NFTNL_OBJ_TUNNEL_ERSPAN_V1_INDEX);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_ERSPAN_V2_DIR)) {
+			obj->tunnel.type = TUNNEL_ERSPAN;
+			obj->tunnel.erspan.v2.direction = nftnl_obj_get_u8(nlo, NFTNL_OBJ_TUNNEL_ERSPAN_V2_DIR);
+		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID)) {
+			obj->tunnel.type = TUNNEL_ERSPAN;
+			obj->tunnel.erspan.v2.hwid = nftnl_obj_get_u8(nlo, NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID);
+		}
 		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 c4694c77..9ba6e8f2 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -606,6 +606,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"
@@ -764,7 +767,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
@@ -4948,6 +4951,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;
@@ -4976,6 +5016,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 1cea0d48..8acb6346 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -2005,6 +2005,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 232bce67..c5c394b7 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -817,6 +817,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.49.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/7 nft] src: add tunnel statement and expression support
  2025-05-27 19:54 [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 1/7 nft] src: add tunnel template support Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 2/7 nft] tunnel: add erspan support Fernando Fernandez Mancera
@ 2025-05-27 19:54 ` Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 4/7 nft] tunnel: add vxlan support Fernando Fernandez Mancera
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-27 19:54 UTC (permalink / raw)
  To: netfilter-devel; +Cc: coreteam, Pablo Neira Ayuso

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            |  7 ++++
 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, 187 insertions(+), 6 deletions(-)
 create mode 100644 include/tunnel.h
 create mode 100644 src/tunnel.c

diff --git a/Makefile.am b/Makefile.am
index fb64105d..092b130c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -99,6 +99,7 @@ noinst_HEADERS = \
 	include/socket.h \
 	include/statement.h \
 	include/tcpopt.h \
+	include/tunnel.h \
 	include/utils.h \
 	include/xfrm.h \
 	include/xt.h \
@@ -241,6 +242,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 f42a0c2b..96c9a512 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>
 
@@ -358,6 +360,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 84c150d8..323b11c1 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2936,6 +2936,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;
@@ -3037,6 +3042,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 dc9a4467..25388dd6 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -1761,6 +1761,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 48a3e33a..486a58ae 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 5f73183b..adb28402 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 9ba6e8f2..e533370a 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -320,6 +320,8 @@ int nft_lex(void *, void *, void *);
 %token RULESET			"ruleset"
 %token TRACE			"trace"
 
+%token PATH			"path"
+
 %token INET			"inet"
 %token NETDEV			"netdev"
 
@@ -778,8 +780,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
@@ -939,9 +941,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
@@ -3203,6 +3205,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(&@$);
@@ -3223,6 +3233,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
@@ -3901,6 +3912,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; }
@@ -4377,6 +4389,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; }
@@ -5471,6 +5484,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 c5c394b7..7d1fae0c 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; }
 
@@ -822,6 +822,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.49.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 4/7 nft] tunnel: add vxlan support
  2025-05-27 19:54 [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support Fernando Fernandez Mancera
                   ` (2 preceding siblings ...)
  2025-05-27 19:54 ` [PATCH 3/7 nft] src: add tunnel statement and expression support Fernando Fernandez Mancera
@ 2025-05-27 19:54 ` Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 5/7 nft] tunnel: add geneve support Fernando Fernandez Mancera
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-27 19:54 UTC (permalink / raw)
  To: netfilter-devel; +Cc: coreteam, 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          |  5 +++++
 src/netlink.c      |  4 ++++
 src/parser_bison.y | 28 +++++++++++++++++++++++++++-
 src/rule.c         | 10 ++++++++++
 src/scanner.l      |  1 +
 6 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/include/rule.h b/include/rule.h
index 2723af38..f7872267 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -493,6 +493,7 @@ struct secmark {
 enum tunnel_type {
 	TUNNEL_UNSPEC = 0,
 	TUNNEL_ERSPAN,
+	TUNNEL_VXLAN,
 };
 
 struct tunnel {
@@ -515,6 +516,9 @@ struct tunnel {
 				uint8_t		hwid;
 			} v2;
 		} erspan;
+		struct {
+			uint32_t	gbp;
+		} vxlan;
 	};
 };
 
diff --git a/src/mnl.c b/src/mnl.c
index 34d919ea..302bb5ce 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1601,6 +1601,11 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
 				break;
 			}
 			break;
+		case TUNNEL_VXLAN:
+			nftnl_obj_set_u32(nlo,
+					  NFTNL_OBJ_TUNNEL_VXLAN_GBP,
+					  obj->tunnel.vxlan.gbp);
+			break;
 		case TUNNEL_UNSPEC:
 			break;
 		}
diff --git a/src/netlink.c b/src/netlink.c
index 086846ce..9d1984a7 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1901,6 +1901,10 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
 			obj->tunnel.type = TUNNEL_ERSPAN;
 			obj->tunnel.erspan.v2.hwid = nftnl_obj_get_u8(nlo, NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID);
 		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_VXLAN_GBP)) {
+			obj->tunnel.type = TUNNEL_VXLAN;
+			obj->tunnel.vxlan.gbp = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TUNNEL_VXLAN_GBP);
+		}
 		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 e533370a..53c2dc2b 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -611,6 +611,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"
@@ -769,7 +770,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
@@ -5001,6 +5002,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;
@@ -5033,6 +5055,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 8acb6346..e020cfb9 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -2027,6 +2027,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 7d1fae0c..77c84923 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -823,6 +823,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 	"egress"		{ return EGRESS; }
 	"ingress"		{ return INGRESS; }
 	"path"			{ return PATH; }
+	"gbp"			{ return GBP; }
 }
 
 "notrack"		{ return NOTRACK; }
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 5/7 nft] tunnel: add geneve support
  2025-05-27 19:54 [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support Fernando Fernandez Mancera
                   ` (3 preceding siblings ...)
  2025-05-27 19:54 ` [PATCH 4/7 nft] tunnel: add vxlan support Fernando Fernandez Mancera
@ 2025-05-27 19:54 ` Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 6/7 nft] tunnel: add tunnel object and statement json support Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 7/7 nft] tests: add tunnel shell and python tests Fernando Fernandez Mancera
  6 siblings, 0 replies; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-27 19:54 UTC (permalink / raw)
  To: netfilter-devel; +Cc: coreteam, 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          |  8 ++++++
 src/netlink.c      | 20 ++++++++++++++
 src/parser_bison.y | 43 ++++++++++++++++++++++++++++-
 src/rule.c         | 68 ++++++++++++++++++++++++++++++++++++++++++++++
 src/scanner.l      |  3 ++
 6 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/include/rule.h b/include/rule.h
index f7872267..86239c38 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -494,6 +494,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 {
@@ -519,9 +528,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 302bb5ce..6d8e4da0 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1455,6 +1455,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
 	struct obj *obj = cmd->object;
 	struct nft_data_linearize nld;
 	struct nftnl_udata_buf *udbuf;
+	struct tunnel_geneve *geneve;
 	struct nftnl_obj *nlo;
 	struct nlmsghdr *nlh;
 
@@ -1606,6 +1607,13 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
 					  NFTNL_OBJ_TUNNEL_VXLAN_GBP,
 					  obj->tunnel.vxlan.gbp);
 			break;
+		case TUNNEL_GENEVE:
+			list_for_each_entry(geneve, &obj->tunnel.geneve_opts, list) {
+				nftnl_obj_set_data(nlo,
+						   NFTNL_OBJ_TUNNEL_GENEVE_OPTS,
+						   geneve, sizeof(struct tunnel_geneve));
+			}
+			break;
 		case TUNNEL_UNSPEC:
 			break;
 		}
diff --git a/src/netlink.c b/src/netlink.c
index 9d1984a7..53feabfb 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1756,6 +1756,23 @@ static int obj_parse_udata_cb(const struct nftnl_udata *attr, void *data)
 	return 0;
 }
 
+static int netlink_parse_obj_tunnel_geneve(struct nftnl_obj_tunnel_geneve *e, void *data)
+{
+	struct tunnel_geneve *geneve;
+	struct obj *obj = data;
+
+	if (!obj->tunnel.type) {
+		init_list_head(&obj->tunnel.geneve_opts);
+		obj->tunnel.type = TUNNEL_GENEVE;
+	}
+
+	geneve = xmalloc(sizeof(struct tunnel_geneve));
+	memcpy(geneve, e, sizeof(struct tunnel_geneve));
+	list_add_tail(&geneve->list, &obj->tunnel.geneve_opts);
+
+	return 0;
+}
+
 struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
 				    struct nftnl_obj *nlo)
 {
@@ -1905,6 +1922,9 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
 			obj->tunnel.type = TUNNEL_VXLAN;
 			obj->tunnel.vxlan.gbp = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TUNNEL_VXLAN_GBP);
 		}
+		if (nftnl_obj_is_set(nlo, NFTNL_OBJ_TUNNEL_GENEVE_OPTS)) {
+			nftnl_obj_tunnel_geneve_opts_foreach(nlo, netlink_parse_obj_tunnel_geneve, 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 53c2dc2b..8e0e27e9 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -612,6 +612,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"
@@ -770,7 +772,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
@@ -5002,6 +5004,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
@@ -5059,6 +5099,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 e020cfb9..588c01b1 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1694,6 +1694,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;
@@ -1771,6 +1779,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)
@@ -2005,6 +2051,7 @@ 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 {",
@@ -2037,6 +2084,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 77c84923..6eacfcb0 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -824,6 +824,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.49.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 6/7 nft] tunnel: add tunnel object and statement json support
  2025-05-27 19:54 [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support Fernando Fernandez Mancera
                   ` (4 preceding siblings ...)
  2025-05-27 19:54 ` [PATCH 5/7 nft] tunnel: add geneve support Fernando Fernandez Mancera
@ 2025-05-27 19:54 ` Fernando Fernandez Mancera
  2025-05-27 19:54 ` [PATCH 7/7 nft] tests: add tunnel shell and python tests Fernando Fernandez Mancera
  6 siblings, 0 replies; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-27 19:54 UTC (permalink / raw)
  To: netfilter-devel; +Cc: coreteam, Fernando Fernandez Mancera

Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
 include/json.h    |   1 +
 include/tunnel.h  |   4 +
 src/json.c        |  94 +++++++++++++++++++++--
 src/parser_json.c | 186 +++++++++++++++++++++++++++++++++++++++++++++-
 src/tunnel.c      |  18 +++++
 5 files changed, 296 insertions(+), 7 deletions(-)

diff --git a/include/json.h b/include/json.h
index b61eeafe..1caeb8f5 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);
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 a8fc10c4..8ba129c6 100644
--- a/src/json.c
+++ b/src/json.c
@@ -354,7 +354,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);
@@ -469,7 +493,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;
 	}
 
@@ -1077,6 +1153,12 @@ json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx)
 	return 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";
@@ -1693,7 +1775,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) {
@@ -1868,7 +1950,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));
 		}
 	}
 
@@ -2087,7 +2169,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 c1bd7570..6d614c99 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)
 {
@@ -1616,6 +1633,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 },
@@ -2176,6 +2194,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 {
@@ -2844,6 +2879,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;
@@ -3467,6 +3503,73 @@ 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)
@@ -3475,6 +3578,8 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
 	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}",
@@ -3662,8 +3767,85 @@ 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 (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.49.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 7/7 nft] tests: add tunnel shell and python tests
  2025-05-27 19:54 [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support Fernando Fernandez Mancera
                   ` (5 preceding siblings ...)
  2025-05-27 19:54 ` [PATCH 6/7 nft] tunnel: add tunnel object and statement json support Fernando Fernandez Mancera
@ 2025-05-27 19:54 ` Fernando Fernandez Mancera
  6 siblings, 0 replies; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-27 19:54 UTC (permalink / raw)
  To: netfilter-devel; +Cc: coreteam, 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.49.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/7 nft] src: add tunnel template support
  2025-05-27 19:54 ` [PATCH 1/7 nft] src: add tunnel template support Fernando Fernandez Mancera
@ 2025-05-28  0:38   ` Florian Westphal
  0 siblings, 0 replies; 12+ messages in thread
From: Florian Westphal @ 2025-05-28  0:38 UTC (permalink / raw)
  To: Fernando Fernandez Mancera; +Cc: netfilter-devel, coreteam, Pablo Neira Ayuso

Fernando Fernandez Mancera <fmancera@suse.de> wrote:
> From: Pablo Neira Ayuso <pablo@netfilter.org>
> 
> This patch adds tunnel template support, this allows to attach a
> metadata template that provides the configuration for the tunnel driver.
> 
> Example of generic tunnel configuration:
> 
>  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
>         }
>  }

Dumb question, how would such a 'generic' tunnel be used?
Or is this just an intermediate step that adds the common code?

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/7 nft] tunnel: add erspan support
  2025-05-27 19:54 ` [PATCH 2/7 nft] tunnel: add erspan support Fernando Fernandez Mancera
@ 2025-05-28  0:46   ` Florian Westphal
  2025-05-28  9:59     ` Fernando Fernandez Mancera
  0 siblings, 1 reply; 12+ messages in thread
From: Florian Westphal @ 2025-05-28  0:46 UTC (permalink / raw)
  To: Fernando Fernandez Mancera; +Cc: netfilter-devel, coreteam, Pablo Neira Ayuso

Fernando Fernandez Mancera <fmancera@suse.de> wrote:
> 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
>                 }
>         }
>  }

Would it make sense to make this

tunnel erspan y {
                 id 10
                 ip saddr 192.168.2.10
                 ip daddr 192.168.2.11
                 sport 10
                 dport 20
                 ttl 10
                 version 1
                 index 2
}

Or was the sub-section intentional to cleanly separate the common parts
from the tunnel specific knobs?

In that case, maybe 'tunnel y {
	...
	type erspan { ... '?

Or do you think its unecessarily verbose?

I think it might be good to make it clear that this is an either-or thing
and multiple 'type' declarations aren't permitted.

Or are there plans to support

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
	       }
	       geneve {
		...
?

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/7 nft] tunnel: add erspan support
  2025-05-28  0:46   ` Florian Westphal
@ 2025-05-28  9:59     ` Fernando Fernandez Mancera
  2025-05-28 10:39       ` Florian Westphal
  0 siblings, 1 reply; 12+ messages in thread
From: Fernando Fernandez Mancera @ 2025-05-28  9:59 UTC (permalink / raw)
  To: Florian Westphal, Fernando Fernandez Mancera
  Cc: netfilter-devel, coreteam, Pablo Neira Ayuso



On 5/28/25 2:46 AM, Florian Westphal wrote:
> Fernando Fernandez Mancera <fmancera@suse.de> wrote:
>> 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
>>                  }
>>          }
>>   }
> 
> Would it make sense to make this
> 
> tunnel erspan y {
>                   id 10
>                   ip saddr 192.168.2.10
>                   ip daddr 192.168.2.11
>                   sport 10
>                   dport 20
>                   ttl 10
>                   version 1
>                   index 2
> }
> 
> Or was the sub-section intentional to cleanly separate the common parts
> from the tunnel specific knobs?
> 

The sub-section was to cleanly separate it and easily understand what 
are the tunnel specific options configured.

> In that case, maybe 'tunnel y {
> 	...
> 	type erspan { ... '?
> 
> Or do you think its unecessarily verbose?
> 
> I think it might be good to make it clear that this is an either-or thing
> and multiple 'type' declarations aren't permitted.
> 

IMHO, adding "type erspan {" won't hurt but I thought it was clear 
enough. If you think adding the "type" keyword makes it clearer, I can 
do it for sure.

Please, notice that if more than one specific sub-section is set, the 
bison parser will complain.

> Or are there plans to support
> 
> 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
> 	       }
> 	       geneve {
> 		...
> ?

I do not think there are plans to support this at all.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/7 nft] tunnel: add erspan support
  2025-05-28  9:59     ` Fernando Fernandez Mancera
@ 2025-05-28 10:39       ` Florian Westphal
  0 siblings, 0 replies; 12+ messages in thread
From: Florian Westphal @ 2025-05-28 10:39 UTC (permalink / raw)
  To: Fernando Fernandez Mancera
  Cc: Fernando Fernandez Mancera, netfilter-devel, coreteam,
	Pablo Neira Ayuso

Fernando Fernandez Mancera <ffmancera@riseup.net> wrote:
> > tunnel erspan y {
> >                   id 10
> >                   ip saddr 192.168.2.10
> >                   ip daddr 192.168.2.11
> >                   sport 10
> >                   dport 20
> >                   ttl 10
> >                   version 1
> >                   index 2
> > }
> > 
> > Or was the sub-section intentional to cleanly separate the common parts
> > from the tunnel specific knobs?
> > 
> 
> The sub-section was to cleanly separate it and easily understand what 
> are the tunnel specific options configured.

Makes sense to me, thanks.

> > In that case, maybe 'tunnel y {
> > 	...
> > 	type erspan { ... '?
> > 
> > Or do you think its unecessarily verbose?
> > 
> > I think it might be good to make it clear that this is an either-or thing
> > and multiple 'type' declarations aren't permitted.
> > 
> 
> IMHO, adding "type erspan {" won't hurt but I thought it was clear 
> enough. If you think adding the "type" keyword makes it clearer, I can 
> do it for sure.

I think its fine as long as we never add additional, common subsections.
Else it will be confusing, e.g. if this is illegal:

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
 }
 vxlan { ..
}

thats fine.  But if we also allow something like this in the future:
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
 }
 extraops {
   ...
 }
}

Then I think it would be confusing as to what kind of 'nested { bla }'
are allowed to co-exist and whioch ones are mutually exclusive.

Thats why I mentioned the extra keyword.  But I admit that as-is its
not necessary.

> Please, notice that if more than one specific sub-section is set, the 
> bison parser will complain.

Good, thanks for explaining.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2025-05-28 10:39 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-27 19:54 [PATCH 0/7 nft] Add nftables tunnel expr, stmt and object support Fernando Fernandez Mancera
2025-05-27 19:54 ` [PATCH 1/7 nft] src: add tunnel template support Fernando Fernandez Mancera
2025-05-28  0:38   ` Florian Westphal
2025-05-27 19:54 ` [PATCH 2/7 nft] tunnel: add erspan support Fernando Fernandez Mancera
2025-05-28  0:46   ` Florian Westphal
2025-05-28  9:59     ` Fernando Fernandez Mancera
2025-05-28 10:39       ` Florian Westphal
2025-05-27 19:54 ` [PATCH 3/7 nft] src: add tunnel statement and expression support Fernando Fernandez Mancera
2025-05-27 19:54 ` [PATCH 4/7 nft] tunnel: add vxlan support Fernando Fernandez Mancera
2025-05-27 19:54 ` [PATCH 5/7 nft] tunnel: add geneve support Fernando Fernandez Mancera
2025-05-27 19:54 ` [PATCH 6/7 nft] tunnel: add tunnel object and statement json support Fernando Fernandez Mancera
2025-05-27 19:54 ` [PATCH 7/7 nft] tests: add tunnel shell and python tests Fernando Fernandez Mancera

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.