[PATCH nft] src: evaluate: support prefix expression in statements

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



nft dumps core when a statement contains a prefix expression:
iifname ens3 snat to 10.0.0.0/28

yields:
BUG: unknown expression type prefix
nft: netlink_linearize.c:688: netlink_gen_expr: Assertion `0' failed.

This assertion is correct -- we can't linearize a prefix because kernel
doesn't know what that is.

For LHS, they get converted to a binary 'and' such as
'10.0.0.0 & 255.255.255.240'.  For RHS, we can convert them into a range:

iifname "ens3" snat to 10.0.0.0-10.0.0.15

Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1187
Signed-off-by: Florian Westphal <fw@xxxxxxxxx>
---
 src/evaluate.c                  | 61 +++++++++++++++++++++++++++++++++
 tests/py/ip6/dnat.t             |  2 ++
 tests/py/ip6/dnat.t.json        | 27 +++++++++++++++
 tests/py/ip6/dnat.t.payload.ip6 | 12 +++++++
 4 files changed, 102 insertions(+)

diff --git a/src/evaluate.c b/src/evaluate.c
index 8c1c82abed4e..d55fe8ebb78e 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1933,6 +1933,65 @@ static int stmt_evaluate_expr(struct eval_ctx *ctx, struct stmt *stmt)
 	return expr_evaluate(ctx, &stmt->expr);
 }
 
+static int stmt_prefix_conversion(struct eval_ctx *ctx, struct expr **expr,
+				  enum byteorder byteorder)
+{
+	struct expr *mask, *and, *or, *prefix, *base, *range;
+
+	prefix = *expr;
+	base = prefix->prefix;
+
+	if (base->etype != EXPR_VALUE)
+		return expr_error(ctx->msgs, prefix,
+				  "you cannot specify a prefix here, "
+				  "unknown type %s", base->dtype->name);
+
+	if (!expr_is_constant(base))
+		return expr_error(ctx->msgs, prefix,
+				  "Prefix expression is undefined for "
+				  "non-constant expressions");
+
+	if (expr_basetype(base)->type != TYPE_INTEGER)
+		return expr_error(ctx->msgs, prefix,
+				  "Prefix expression expected integer value");
+
+	mask = constant_expr_alloc(&prefix->location, expr_basetype(base),
+				   BYTEORDER_HOST_ENDIAN, base->len, NULL);
+
+	mpz_prefixmask(mask->value, base->len, prefix->prefix_len);
+	and = binop_expr_alloc(&prefix->location, OP_AND, expr_get(base), mask);
+
+	if (expr_evaluate(ctx, &and) < 0) {
+		expr_free(and);
+		goto err_evaluation;
+	}
+
+	mask = constant_expr_alloc(&prefix->location, expr_basetype(base),
+				   BYTEORDER_HOST_ENDIAN, base->len, NULL);
+	mpz_bitmask(mask->value, prefix->len - prefix->prefix_len);
+	or = binop_expr_alloc(&prefix->location, OP_OR, expr_get(base), mask);
+
+	if (expr_evaluate(ctx, &or) < 0) {
+		expr_free(and);
+		expr_free(or);
+		goto err_evaluation;
+	}
+
+	range = range_expr_alloc(&prefix->location, and, or);
+	if (expr_evaluate(ctx, &range) < 0) {
+		expr_free(range);
+		goto err_evaluation;
+	}
+
+	expr_free(*expr);
+	*expr = range;
+	return 0;
+
+err_evaluation:
+	return expr_error(ctx->msgs, prefix,
+			  "Could not expand prefix expression");
+}
+
 static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
 			     const struct datatype *dtype, unsigned int len,
 			     enum byteorder byteorder, struct expr **expr)
@@ -1969,6 +2028,8 @@ static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
 					 "unknown value to use");
 	case EXPR_RT:
 		return byteorder_conversion(ctx, expr, byteorder);
+	case EXPR_PREFIX:
+		return stmt_prefix_conversion(ctx, expr, byteorder);
 	default:
 		break;
 	}
diff --git a/tests/py/ip6/dnat.t b/tests/py/ip6/dnat.t
index 78d6d0ad382d..db5fde58e606 100644
--- a/tests/py/ip6/dnat.t
+++ b/tests/py/ip6/dnat.t
@@ -5,3 +5,5 @@
 tcp dport 80-90 dnat to [2001:838:35f:1::]-[2001:838:35f:2::]:80-100;ok
 tcp dport 80-90 dnat to [2001:838:35f:1::]-[2001:838:35f:2::]:100;ok;tcp dport 80-90 dnat to [2001:838:35f:1::]-[2001:838:35f:2::]:100
 tcp dport 80-90 dnat to [2001:838:35f:1::]:80;ok
+dnat to [2001:838:35f:1::]/64;ok;dnat to 2001:838:35f:1::-2001:838:35f:1:ffff:ffff:ffff:ffff
+dnat to 2001:838:35f:1::-2001:838:35f:1:ffff:ffff:ffff:ffff;ok
diff --git a/tests/py/ip6/dnat.t.json b/tests/py/ip6/dnat.t.json
index a5c01fd2d7a1..3419b60f5dd1 100644
--- a/tests/py/ip6/dnat.t.json
+++ b/tests/py/ip6/dnat.t.json
@@ -76,3 +76,30 @@
     }
 ]
 
+# dnat to [2001:838:35f:1::]/64
+[
+    {
+        "dnat": {
+            "addr": {
+                "range": [
+                    "2001:838:35f:1::",
+                    "2001:838:35f:1:ffff:ffff:ffff:ffff"
+                ]
+            }
+        }
+    }
+]
+
+# dnat to 2001:838:35f:1::-2001:838:35f:1:ffff:ffff:ffff:ffff
+[
+    {
+        "dnat": {
+            "addr": {
+                "range": [
+                    "2001:838:35f:1::",
+                    "2001:838:35f:1:ffff:ffff:ffff:ffff"
+                ]
+            }
+        }
+    }
+]
diff --git a/tests/py/ip6/dnat.t.payload.ip6 b/tests/py/ip6/dnat.t.payload.ip6
index 4d3fafe2bf02..985159e209c6 100644
--- a/tests/py/ip6/dnat.t.payload.ip6
+++ b/tests/py/ip6/dnat.t.payload.ip6
@@ -33,3 +33,15 @@ ip6 test-ip6 prerouting
   [ immediate reg 1 0x38080120 0x01005f03 0x00000000 0x00000000 ]
   [ immediate reg 2 0x00005000 ]
   [ nat dnat ip6 addr_min reg 1 addr_max reg 0 proto_min reg 2 proto_max reg 0 ]
+
+# dnat to [2001:838:35f:1::]/64
+ip6 test-ip6 prerouting
+  [ immediate reg 1 0x38080120 0x01005f03 0x00000000 0x00000000 ]
+  [ immediate reg 2 0x38080120 0x01005f03 0xffffffff 0xffffffff ]
+  [ nat dnat ip6 addr_min reg 1 addr_max reg 2 ]
+
+# dnat to 2001:838:35f:1::-2001:838:35f:1:ffff:ffff:ffff:ffff
+ip6 test-ip6 prerouting
+  [ immediate reg 1 0x38080120 0x01005f03 0x00000000 0x00000000 ]
+  [ immediate reg 2 0x38080120 0x01005f03 0xffffffff 0xffffffff ]
+  [ nat dnat ip6 addr_min reg 1 addr_max reg 2 ]
-- 
2.21.0




[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux