diff mbox series

[nft] evaluate: fix maps with key and data concatenations

Message ID 20210622193818.138030-1-pablo@netfilter.org
State Accepted
Delegated to: Pablo Neira
Headers show
Series [nft] evaluate: fix maps with key and data concatenations | expand

Commit Message

Pablo Neira Ayuso June 22, 2021, 7:38 p.m. UTC
expr_evaluate_concat() is overloaded, it deals with two cases:

 #1 set key and data definitions, this case uses the special
    dynamically created concatenation datatype which is taken
    from the context.
 #2 set elements, this case iterates over the set key and data
    expressions that are components of the concatenation tuple,
    to fetch the corresponding datatype.

Add a new function to deal with case #1 specifically.

This patch is implicitly fixing up map that include arbitrary
concatenations. This is failing with a spurious error report such as:

 # cat bug.nft
 table x {
        map test {
                type ipv4_addr . inet_proto . inet_service : ipv4_addr . inet_service
        }
 }

 # nft -f bug.nft
 bug.nft:3:48-71: Error: datatype mismatch, expected concatenation of (IPv4 address, Internet protocol, internet network service), expression has type concatenation of (IPv4 address, internet network service)
                type ipv4_addr . inet_proto . inet_service : ipv4_addr . inet_service
                                                             ^^^^^^^^^^^^^^^^^^^^^^^^

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 src/evaluate.c                                | 50 ++++++++++++++++---
 tests/shell/testcases/maps/0010concat_map_0   | 19 +++++++
 .../testcases/maps/dumps/0010concat_map_0.nft | 11 ++++
 3 files changed, 74 insertions(+), 6 deletions(-)
 create mode 100755 tests/shell/testcases/maps/0010concat_map_0
 create mode 100644 tests/shell/testcases/maps/dumps/0010concat_map_0.nft
diff mbox series

Patch

diff --git a/src/evaluate.c b/src/evaluate.c
index 35ef8a376170..ffb6b3a6af1a 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1241,8 +1241,7 @@  static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr)
 	return err;
 }
 
-static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr,
-				bool eval)
+static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
 {
 	const struct datatype *dtype = ctx->ectx.dtype, *tmp;
 	uint32_t type = dtype ? dtype->type : 0, ntype = 0;
@@ -1271,7 +1270,7 @@  static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr,
 			tmp = concat_subtype_lookup(type, --off);
 		expr_set_context(&ctx->ectx, tmp, tmp->size);
 
-		if (eval && list_member_evaluate(ctx, &i) < 0)
+		if (list_member_evaluate(ctx, &i) < 0)
 			return -1;
 		flags &= i->flags;
 
@@ -2237,7 +2236,7 @@  static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
 	case EXPR_BINOP:
 		return expr_evaluate_binop(ctx, expr);
 	case EXPR_CONCAT:
-		return expr_evaluate_concat(ctx, expr, true);
+		return expr_evaluate_concat(ctx, expr);
 	case EXPR_LIST:
 		return expr_evaluate_list(ctx, expr);
 	case EXPR_SET:
@@ -3792,6 +3791,45 @@  static int set_key_data_error(struct eval_ctx *ctx, const struct set *set,
 			 dtype->name, name, hint);
 }
 
+static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
+{
+	unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+	struct expr *i, *next;
+	uint32_t ntype = 0;
+
+	list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
+		unsigned dsize_bytes;
+
+		if (i->etype == EXPR_CT &&
+		    (i->ct.key == NFT_CT_SRC ||
+		     i->ct.key == NFT_CT_DST))
+			return expr_error(ctx->msgs, i,
+					  "specify either ip or ip6 for address matching");
+
+		if (i->dtype->size == 0)
+			return expr_binary_error(ctx->msgs, i, *expr,
+						 "can not use variable sized "
+						 "data types (%s) in concat "
+						 "expressions",
+						 i->dtype->name);
+
+		flags &= i->flags;
+
+		ntype = concat_subtype_add(ntype, i->dtype->type);
+
+		dsize_bytes = div_round_up(i->dtype->size, BITS_PER_BYTE);
+		(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
+	}
+
+	(*expr)->flags |= flags;
+	datatype_set(*expr, concat_type_alloc(ntype));
+	(*expr)->len   = (*expr)->dtype->size;
+
+	expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
+
+	return 0;
+}
+
 static int set_evaluate(struct eval_ctx *ctx, struct set *set)
 {
 	unsigned int num_stmts = 0;
@@ -3821,7 +3859,7 @@  static int set_evaluate(struct eval_ctx *ctx, struct set *set)
 
 	if (set->key->len == 0) {
 		if (set->key->etype == EXPR_CONCAT &&
-		    expr_evaluate_concat(ctx, &set->key, false) < 0)
+		    set_expr_evaluate_concat(ctx, &set->key) < 0)
 			return -1;
 
 		if (set->key->len == 0)
@@ -3845,7 +3883,7 @@  static int set_evaluate(struct eval_ctx *ctx, struct set *set)
 			set->data->len *= 2;
 
 		if (set->data->etype == EXPR_CONCAT &&
-		    expr_evaluate_concat(ctx, &set->data, false) < 0)
+		    set_expr_evaluate_concat(ctx, &set->data) < 0)
 			return -1;
 
 		if (set->data->len == 0 && set->data->dtype->type != TYPE_VERDICT)
diff --git a/tests/shell/testcases/maps/0010concat_map_0 b/tests/shell/testcases/maps/0010concat_map_0
new file mode 100755
index 000000000000..4848d97212fd
--- /dev/null
+++ b/tests/shell/testcases/maps/0010concat_map_0
@@ -0,0 +1,19 @@ 
+#!/bin/bash
+
+set -e
+
+EXPECTED="table inet x {
+	map z {
+		type ipv4_addr . inet_proto . inet_service : ipv4_addr . inet_service
+		elements = {
+			1.1.1.1 . tcp . 20 : 2.2.2.2 . 30
+		}
+	}
+
+	chain y {
+		type nat hook prerouting priority dstnat;
+		dnat ip addr . port to ip saddr . ip protocol . tcp dport map @z
+	}
+}"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/maps/dumps/0010concat_map_0.nft b/tests/shell/testcases/maps/dumps/0010concat_map_0.nft
new file mode 100644
index 000000000000..328c653c9913
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0010concat_map_0.nft
@@ -0,0 +1,11 @@ 
+table inet x {
+	map z {
+		type ipv4_addr . inet_proto . inet_service : ipv4_addr . inet_service
+		elements = { 1.1.1.1 . tcp . 20 : 2.2.2.2 . 30 }
+	}
+
+	chain y {
+		type nat hook prerouting priority dstnat; policy accept;
+		meta nfproto ipv4 dnat ip addr . port to ip saddr . ip protocol . tcp dport map @z
+	}
+}