diff mbox series

[nft,2/3] exthdr: support ip/tcp options and sctp chunks in typeof expressions

Message ID 20211203160755.8720-3-fw@strlen.de
State Accepted, archived
Delegated to: Pablo Neira
Headers show
Series typeof fixes | expand

Commit Message

Florian Westphal Dec. 3, 2021, 4:07 p.m. UTC
This did not store the 'op' member and listing always treated this as ipv6
extension header.

Add test cases for this.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 src/exthdr.c                                  | 47 ++++++++++++++++---
 .../testcases/sets/dumps/typeof_sets_0.nft    | 27 +++++++++++
 tests/shell/testcases/sets/typeof_sets_0      | 27 +++++++++++
 3 files changed, 94 insertions(+), 7 deletions(-)
diff mbox series

Patch

diff --git a/src/exthdr.c b/src/exthdr.c
index 2357ab60648d..3e5f5cd8b73e 100644
--- a/src/exthdr.c
+++ b/src/exthdr.c
@@ -115,7 +115,8 @@  static void exthdr_expr_clone(struct expr *new, const struct expr *expr)
 
 #define NFTNL_UDATA_EXTHDR_DESC 0
 #define NFTNL_UDATA_EXTHDR_TYPE 1
-#define NFTNL_UDATA_EXTHDR_MAX 2
+#define NFTNL_UDATA_EXTHDR_OP	2
+#define NFTNL_UDATA_EXTHDR_MAX 3
 
 static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
 {
@@ -126,6 +127,7 @@  static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
 	switch (type) {
 	case NFTNL_UDATA_EXTHDR_DESC:
 	case NFTNL_UDATA_EXTHDR_TYPE:
+	case NFTNL_UDATA_EXTHDR_OP:
 		if (len != sizeof(uint32_t))
 			return -1;
 		break;
@@ -140,6 +142,7 @@  static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
 static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
 {
 	const struct nftnl_udata *ud[NFTNL_UDATA_EXTHDR_MAX + 1] = {};
+	enum nft_exthdr_op op = NFT_EXTHDR_OP_IPV6;
 	const struct exthdr_desc *desc;
 	unsigned int type;
 	uint32_t desc_id;
@@ -154,14 +157,31 @@  static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
 	    !ud[NFTNL_UDATA_EXTHDR_TYPE])
 		return NULL;
 
-	desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]);
-	desc = exthdr_find_desc(desc_id);
-	if (!desc)
-		return NULL;
+	if (ud[NFTNL_UDATA_EXTHDR_OP])
+		op = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_OP]);
 
+	desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]);
 	type = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_TYPE]);
 
-	return exthdr_expr_alloc(&internal_location, desc, type);
+	switch (op) {
+	case NFT_EXTHDR_OP_IPV6:
+		desc = exthdr_find_desc(desc_id);
+
+		return exthdr_expr_alloc(&internal_location, desc, type);
+	case NFT_EXTHDR_OP_TCPOPT:
+		return tcpopt_expr_alloc(&internal_location,
+					 desc_id, type);
+	case NFT_EXTHDR_OP_IPV4:
+		return ipopt_expr_alloc(&internal_location,
+					 desc_id, type);
+	case NFT_EXTHDR_OP_SCTP:
+		return sctp_chunk_expr_alloc(&internal_location,
+					     desc_id, type);
+	case __NFT_EXTHDR_OP_MAX:
+		return NULL;
+	}
+
+	return NULL;
 }
 
 static unsigned int expr_exthdr_type(const struct exthdr_desc *desc,
@@ -176,9 +196,22 @@  static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
 	const struct proto_hdr_template *tmpl = expr->exthdr.tmpl;
 	const struct exthdr_desc *desc = expr->exthdr.desc;
 	unsigned int type = expr_exthdr_type(desc, tmpl);
+	enum nft_exthdr_op op = expr->exthdr.op;
 
-	nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id);
 	nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_TYPE, type);
+	switch (op) {
+	case NFT_EXTHDR_OP_IPV6:
+		nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id);
+		break;
+	case NFT_EXTHDR_OP_TCPOPT:
+	case NFT_EXTHDR_OP_IPV4:
+	case NFT_EXTHDR_OP_SCTP:
+		nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_OP, op);
+		nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, expr->exthdr.raw_type);
+		break;
+	default:
+		return -1;
+	}
 
 	return 0;
 }
diff --git a/tests/shell/testcases/sets/dumps/typeof_sets_0.nft b/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
index 06d891e682b7..8f11b110552c 100644
--- a/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
+++ b/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
@@ -19,6 +19,21 @@  table inet t {
 		elements = { 1, 1024 }
 	}
 
+	set s5 {
+		typeof ip option ra value
+		elements = { 1, 1024 }
+	}
+
+	set s6 {
+		typeof tcp option maxseg size
+		elements = { 1, 1024 }
+	}
+
+	set s7 {
+		typeof sctp chunk init num-inbound-streams
+		elements = { 1, 4 }
+	}
+
 	chain c1 {
 		osf name @s1 accept
 	}
@@ -26,4 +41,16 @@  table inet t {
 	chain c2 {
 		vlan id @s2 accept
 	}
+
+	chain c5 {
+		ip option ra value @s5 accept
+	}
+
+	chain c6 {
+		tcp option maxseg size @s6 accept
+	}
+
+	chain c7 {
+		sctp chunk init num-inbound-streams @s7 accept
+	}
 }
diff --git a/tests/shell/testcases/sets/typeof_sets_0 b/tests/shell/testcases/sets/typeof_sets_0
index a6ff8ca772e2..1e99e2987733 100755
--- a/tests/shell/testcases/sets/typeof_sets_0
+++ b/tests/shell/testcases/sets/typeof_sets_0
@@ -25,6 +25,21 @@  EXPECTED="table inet t {
 		elements = { 1, 1024 }
 	}
 
+	set s5 {
+		typeof ip option ra value
+		elements = { 1, 1024 }
+	}
+
+	set s6 {
+		typeof tcp option maxseg size
+		elements = { 1, 1024 }
+	}
+
+	set s7 {
+		typeof sctp chunk init num-inbound-streams
+		elements = { 1, 4 }
+	}
+
 	chain c1 {
 		osf name @s1 accept
 	}
@@ -32,6 +47,18 @@  EXPECTED="table inet t {
 	chain c2 {
 		ether type vlan vlan id @s2 accept
 	}
+
+	chain c5 {
+		ip option ra value @s5 accept
+	}
+
+	chain c6 {
+		tcp option maxseg size @s6 accept
+	}
+
+	chain c7 {
+		sctp chunk init num-inbound-streams @s7 accept
+	}
 }"
 
 set -e