netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH nftables 0/4] optimization updates
@ 2022-01-26 22:33 Pablo Neira Ayuso
  2022-01-26 22:33 ` [PATCH nftables 1/4] optimize: add __expr_cmp() Pablo Neira Ayuso
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Pablo Neira Ayuso @ 2022-01-26 22:33 UTC (permalink / raw)
  To: netfilter-devel

nft -o/--optimize crashes with verdict maps due to insufficient checks
on the expression type for verdict statements.

This patchset extends -o/--optimize to merge two rules with the same
verdict maps side by side.

This also prepares for allowing to merge raw expressions in
concatenation which is not possible yet due to the use of integer type.

Pablo Neira Ayuso (4):
  optimize: add __expr_cmp()
  optimize: merge verdict maps with same lookup key
  optimize: check for payload base and offset when searching for mergers
  optimize: do not merge raw payload expressions

 src/optimize.c                                | 210 +++++++++++++-----
 .../optimizations/dumps/merge_vmaps.nft       |  12 +
 .../shell/testcases/optimizations/merge_vmaps |  25 +++
 3 files changed, 189 insertions(+), 58 deletions(-)
 create mode 100644 tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
 create mode 100755 tests/shell/testcases/optimizations/merge_vmaps

-- 
2.30.2


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

* [PATCH nftables 1/4] optimize: add __expr_cmp()
  2022-01-26 22:33 [PATCH nftables 0/4] optimization updates Pablo Neira Ayuso
@ 2022-01-26 22:33 ` Pablo Neira Ayuso
  2022-01-26 22:33 ` [PATCH nftables 2/4] optimize: merge verdict maps with same lookup key Pablo Neira Ayuso
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Pablo Neira Ayuso @ 2022-01-26 22:33 UTC (permalink / raw)
  To: netfilter-devel

Add helper function to compare expression to allow for reuse.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 src/optimize.c | 98 ++++++++++++++++++++++++++------------------------
 1 file changed, 52 insertions(+), 46 deletions(-)

diff --git a/src/optimize.c b/src/optimize.c
index b5fb2c4179d0..c52966a86e2c 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -33,6 +33,57 @@ struct optimize_ctx {
 	uint32_t num_rules;
 };
 
+static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
+{
+	if (expr_a->etype != expr_b->etype)
+		return false;
+
+	switch (expr_a->etype) {
+	case EXPR_PAYLOAD:
+		if (expr_a->payload.desc != expr_b->payload.desc)
+			return false;
+		if (expr_a->payload.tmpl != expr_b->payload.tmpl)
+			return false;
+		break;
+	case EXPR_EXTHDR:
+		if (expr_a->exthdr.desc != expr_b->exthdr.desc)
+			return false;
+		if (expr_a->exthdr.tmpl != expr_b->exthdr.tmpl)
+			return false;
+		break;
+	case EXPR_META:
+		if (expr_a->meta.key != expr_b->meta.key)
+			return false;
+		if (expr_a->meta.base != expr_b->meta.base)
+			return false;
+		break;
+	case EXPR_CT:
+		if (expr_a->ct.key != expr_b->ct.key)
+			return false;
+		if (expr_a->ct.base != expr_b->ct.base)
+			return false;
+		if (expr_a->ct.direction != expr_b->ct.direction)
+			return false;
+		if (expr_a->ct.nfproto != expr_b->ct.nfproto)
+			return false;
+		break;
+	case EXPR_RT:
+		if (expr_a->rt.key != expr_b->rt.key)
+			return false;
+		break;
+	case EXPR_SOCKET:
+		if (expr_a->socket.key != expr_b->socket.key)
+			return false;
+		if (expr_a->socket.level != expr_b->socket.level)
+			return false;
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
 static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
 {
 	struct expr *expr_a, *expr_b;
@@ -45,52 +96,7 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
 		expr_a = stmt_a->expr;
 		expr_b = stmt_b->expr;
 
-		if (expr_a->left->etype != expr_b->left->etype)
-			return false;
-
-		switch (expr_a->left->etype) {
-		case EXPR_PAYLOAD:
-			if (expr_a->left->payload.desc != expr_b->left->payload.desc)
-				return false;
-			if (expr_a->left->payload.tmpl != expr_b->left->payload.tmpl)
-				return false;
-			break;
-		case EXPR_EXTHDR:
-			if (expr_a->left->exthdr.desc != expr_b->left->exthdr.desc)
-				return false;
-			if (expr_a->left->exthdr.tmpl != expr_b->left->exthdr.tmpl)
-				return false;
-			break;
-		case EXPR_META:
-			if (expr_a->left->meta.key != expr_b->left->meta.key)
-				return false;
-			if (expr_a->left->meta.base != expr_b->left->meta.base)
-				return false;
-			break;
-		case EXPR_CT:
-			if (expr_a->left->ct.key != expr_b->left->ct.key)
-				return false;
-			if (expr_a->left->ct.base != expr_b->left->ct.base)
-				return false;
-			if (expr_a->left->ct.direction != expr_b->left->ct.direction)
-				return false;
-			if (expr_a->left->ct.nfproto != expr_b->left->ct.nfproto)
-				return false;
-			break;
-		case EXPR_RT:
-			if (expr_a->left->rt.key != expr_b->left->rt.key)
-				return false;
-			break;
-		case EXPR_SOCKET:
-			if (expr_a->left->socket.key != expr_b->left->socket.key)
-				return false;
-			if (expr_a->left->socket.level != expr_b->left->socket.level)
-				return false;
-			break;
-		default:
-			return false;
-		}
-		break;
+		return __expr_cmp(expr_a->left, expr_b->left);
 	case STMT_COUNTER:
 	case STMT_NOTRACK:
 		break;
-- 
2.30.2


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

* [PATCH nftables 2/4] optimize: merge verdict maps with same lookup key
  2022-01-26 22:33 [PATCH nftables 0/4] optimization updates Pablo Neira Ayuso
  2022-01-26 22:33 ` [PATCH nftables 1/4] optimize: add __expr_cmp() Pablo Neira Ayuso
@ 2022-01-26 22:33 ` Pablo Neira Ayuso
  2022-01-26 22:33 ` [PATCH nftables 3/4] optimize: check for payload base and offset when searching for mergers Pablo Neira Ayuso
  2022-01-26 22:33 ` [PATCH nftables 4/4] optimize: do not merge raw payload expressions Pablo Neira Ayuso
  3 siblings, 0 replies; 5+ messages in thread
From: Pablo Neira Ayuso @ 2022-01-26 22:33 UTC (permalink / raw)
  To: netfilter-devel

Merge two consecutive verdict maps with the same lookup key.

For instance, merge the following:

 table inet x {
        chain filter_in_tcp {
                tcp dport vmap {
                           80 : accept,
                           81 : accept,
                          443 : accept,
                          931 : accept,
                         5001 : accept,
                         5201 : accept,
                }
                tcp dport vmap {
                         6800-6999  : accept,
                        33434-33499 : accept,
                }
        }
 }

into:

 table inet x {
        chain filter_in_tcp {
                tcp dport vmap {
                           80 : accept,
                           81 : accept,
                          443 : accept,
                          931 : accept,
                         5001 : accept,
                         5201 : accept,
                         6800-6999  : accept,
                        33434-33499 : accept,
                }
	}
 }

This patch updates statement comparison routine to inspect the verdict
expression type to detect possible merger.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 src/optimize.c                                | 105 ++++++++++++++++--
 .../optimizations/dumps/merge_vmaps.nft       |  12 ++
 .../shell/testcases/optimizations/merge_vmaps |  25 +++++
 3 files changed, 130 insertions(+), 12 deletions(-)
 create mode 100644 tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
 create mode 100755 tests/shell/testcases/optimizations/merge_vmaps

diff --git a/src/optimize.c b/src/optimize.c
index c52966a86e2c..9a93e3b8d296 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -101,6 +101,15 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
 	case STMT_NOTRACK:
 		break;
 	case STMT_VERDICT:
+		expr_a = stmt_a->expr;
+		expr_b = stmt_b->expr;
+
+		if (expr_a->etype != expr_b->etype)
+			return false;
+
+		if (expr_a->etype == EXPR_MAP &&
+		    expr_b->etype == EXPR_MAP)
+			return __expr_cmp(expr_a->map, expr_b->map);
 		break;
 	case STMT_LIMIT:
 		if (stmt_a->limit.rate != stmt_b->limit.rate ||
@@ -139,14 +148,8 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
 	return true;
 }
 
-static bool stmt_verdict_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+static bool expr_verdict_eq(const struct expr *expr_a, const struct expr *expr_b)
 {
-	struct expr *expr_a, *expr_b;
-
-	assert (stmt_a->ops->type == STMT_VERDICT);
-
-	expr_a = stmt_a->expr;
-	expr_b = stmt_b->expr;
 	if (expr_a->verdict != expr_b->verdict)
 		return false;
 	if (expr_a->chain && expr_b->chain) {
@@ -162,6 +165,25 @@ static bool stmt_verdict_eq(const struct stmt *stmt_a, const struct stmt *stmt_b
 	return true;
 }
 
+static bool stmt_verdict_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+	struct expr *expr_a, *expr_b;
+
+	assert (stmt_a->ops->type == STMT_VERDICT);
+
+	expr_a = stmt_a->expr;
+	expr_b = stmt_b->expr;
+	if (expr_a->etype == EXPR_VERDICT &&
+	    expr_b->etype == EXPR_VERDICT)
+		return expr_verdict_eq(expr_a, expr_b);
+
+	if (expr_a->etype == EXPR_MAP &&
+	    expr_b->etype == EXPR_MAP)
+		return __expr_cmp(expr_a->map, expr_b->map);
+
+	return false;
+}
+
 static bool stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
 {
 	if (!stmt_a && !stmt_b)
@@ -194,6 +216,10 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
 		if (stmt_type_find(ctx, stmt))
 			continue;
 
+		if (stmt->ops->type == STMT_VERDICT &&
+		    stmt->expr->etype == EXPR_MAP)
+			continue;
+
 		/* No refcounter available in statement objects, clone it to
 		 * to store in the array of selectors.
 		 */
@@ -273,16 +299,15 @@ struct merge {
 	uint32_t	num_stmts;
 };
 
-static void merge_stmts(const struct optimize_ctx *ctx,
-			uint32_t from, uint32_t to, const struct merge *merge)
+static void merge_expr_stmts(const struct optimize_ctx *ctx,
+			     uint32_t from, uint32_t to,
+			     const struct merge *merge,
+			     struct stmt *stmt_a)
 {
-	struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
 	struct expr *expr_a, *expr_b, *set, *elem;
 	struct stmt *stmt_b;
 	uint32_t i;
 
-	assert (stmt_a->ops->type == STMT_EXPRESSION);
-
 	set = set_expr_alloc(&internal_location, NULL);
 	set->set_flags |= NFT_SET_ANONYMOUS;
 
@@ -301,6 +326,62 @@ static void merge_stmts(const struct optimize_ctx *ctx,
 	stmt_a->expr->right = set;
 }
 
+static void merge_vmap(const struct optimize_ctx *ctx,
+		       struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+	struct expr *mappings, *mapping, *expr;
+
+	mappings = stmt_b->expr->mappings;
+	list_for_each_entry(expr, &mappings->expressions, list) {
+		mapping = expr_clone(expr);
+		compound_expr_add(stmt_a->expr->mappings, mapping);
+	}
+}
+
+static void merge_verdict_stmts(const struct optimize_ctx *ctx,
+				uint32_t from, uint32_t to,
+				const struct merge *merge,
+				struct stmt *stmt_a)
+{
+	struct stmt *stmt_b;
+	uint32_t i;
+
+	for (i = from + 1; i <= to; i++) {
+		stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+		switch (stmt_b->ops->type) {
+		case STMT_VERDICT:
+			switch (stmt_b->expr->etype) {
+			case EXPR_MAP:
+				merge_vmap(ctx, stmt_a, stmt_b);
+				break;
+			default:
+				assert(1);
+			}
+			break;
+		default:
+			assert(1);
+			break;
+		}
+	}
+}
+
+static void merge_stmts(const struct optimize_ctx *ctx,
+			uint32_t from, uint32_t to, const struct merge *merge)
+{
+	struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
+
+	switch (stmt_a->ops->type) {
+	case STMT_EXPRESSION:
+		merge_expr_stmts(ctx, from, to, merge, stmt_a);
+		break;
+	case STMT_VERDICT:
+		merge_verdict_stmts(ctx, from, to, merge, stmt_a);
+		break;
+	default:
+		assert(1);
+	}
+}
+
 static void merge_concat_stmts(const struct optimize_ctx *ctx,
 			       uint32_t from, uint32_t to,
 			       const struct merge *merge)
diff --git a/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft b/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
new file mode 100644
index 000000000000..c1c9743b9f8c
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
@@ -0,0 +1,12 @@
+table ip x {
+	chain filter_in_tcp {
+	}
+
+	chain filter_in_udp {
+	}
+
+	chain y {
+		tcp dport vmap { 80 : accept, 81 : accept, 443 : accept, 8000-8100 : accept, 24000-25000 : accept }
+		meta l4proto vmap { tcp : goto filter_in_tcp, udp : goto filter_in_udp }
+	}
+}
diff --git a/tests/shell/testcases/optimizations/merge_vmaps b/tests/shell/testcases/optimizations/merge_vmaps
new file mode 100755
index 000000000000..7b7a2723be4b
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_vmaps
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+	chain filter_in_tcp {
+	}
+	chain filter_in_udp {
+	}
+	chain y {
+		tcp dport vmap {
+			80 : accept,
+			81 : accept,
+			443 : accept,
+		}
+		tcp dport vmap {
+			8000-8100 : accept,
+			24000-25000 : accept,
+		}
+		meta l4proto tcp goto filter_in_tcp
+		meta l4proto udp goto filter_in_udp
+	}
+}"
+
+$NFT -o -f - <<< $RULESET
-- 
2.30.2


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

* [PATCH nftables 3/4] optimize: check for payload base and offset when searching for mergers
  2022-01-26 22:33 [PATCH nftables 0/4] optimization updates Pablo Neira Ayuso
  2022-01-26 22:33 ` [PATCH nftables 1/4] optimize: add __expr_cmp() Pablo Neira Ayuso
  2022-01-26 22:33 ` [PATCH nftables 2/4] optimize: merge verdict maps with same lookup key Pablo Neira Ayuso
@ 2022-01-26 22:33 ` Pablo Neira Ayuso
  2022-01-26 22:33 ` [PATCH nftables 4/4] optimize: do not merge raw payload expressions Pablo Neira Ayuso
  3 siblings, 0 replies; 5+ messages in thread
From: Pablo Neira Ayuso @ 2022-01-26 22:33 UTC (permalink / raw)
  To: netfilter-devel

Extend the existing checks to cover the payload base and offset.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 src/optimize.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/optimize.c b/src/optimize.c
index 9a93e3b8d296..5882f3bd005d 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -40,6 +40,10 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
 
 	switch (expr_a->etype) {
 	case EXPR_PAYLOAD:
+		if (expr_a->payload.base != expr_b->payload.base)
+			return false;
+		if (expr_a->payload.offset != expr_b->payload.offset)
+			return false;
 		if (expr_a->payload.desc != expr_b->payload.desc)
 			return false;
 		if (expr_a->payload.tmpl != expr_b->payload.tmpl)
-- 
2.30.2


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

* [PATCH nftables 4/4] optimize: do not merge raw payload expressions
  2022-01-26 22:33 [PATCH nftables 0/4] optimization updates Pablo Neira Ayuso
                   ` (2 preceding siblings ...)
  2022-01-26 22:33 ` [PATCH nftables 3/4] optimize: check for payload base and offset when searching for mergers Pablo Neira Ayuso
@ 2022-01-26 22:33 ` Pablo Neira Ayuso
  3 siblings, 0 replies; 5+ messages in thread
From: Pablo Neira Ayuso @ 2022-01-26 22:33 UTC (permalink / raw)
  To: netfilter-devel

Merging raw expressions results in a valid concatenation which throws:

 Error: can not use variable sized data types (integer) in concat expressions

Disable merging raw expressions until this is supported by skipping raw
expressions.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 src/optimize.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/optimize.c b/src/optimize.c
index 5882f3bd005d..04523edb795b 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -40,6 +40,9 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
 
 	switch (expr_a->etype) {
 	case EXPR_PAYLOAD:
+		/* disable until concatenation with integer works. */
+		if (expr_a->payload.is_raw || expr_b->payload.is_raw)
+			return false;
 		if (expr_a->payload.base != expr_b->payload.base)
 			return false;
 		if (expr_a->payload.offset != expr_b->payload.offset)
-- 
2.30.2


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

end of thread, other threads:[~2022-01-26 22:33 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-01-26 22:33 [PATCH nftables 0/4] optimization updates Pablo Neira Ayuso
2022-01-26 22:33 ` [PATCH nftables 1/4] optimize: add __expr_cmp() Pablo Neira Ayuso
2022-01-26 22:33 ` [PATCH nftables 2/4] optimize: merge verdict maps with same lookup key Pablo Neira Ayuso
2022-01-26 22:33 ` [PATCH nftables 3/4] optimize: check for payload base and offset when searching for mergers Pablo Neira Ayuso
2022-01-26 22:33 ` [PATCH nftables 4/4] optimize: do not merge raw payload expressions Pablo Neira Ayuso

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).