]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
optimize: merge several selectors with different verdict into verdict map
authorPablo Neira Ayuso <pablo@netfilter.org>
Sun, 2 Jan 2022 20:54:01 +0000 (21:54 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Sat, 15 Jan 2022 17:11:22 +0000 (18:11 +0100)
Transform:

  ip saddr 1.1.1.1 ip daddr 2.2.2.2 accept
  ip saddr 2.2.2.2 ip daddr 3.3.3.3 drop

into:

  ip saddr . ip daddr vmap { 1.1.1.1 . 2.2.2.2 : accept, 2.2.2.2 . 3.3.3.3 : drop }

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
src/expression.c
src/optimize.c
tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft [new file with mode: 0644]
tests/shell/testcases/optimizations/merge_stmts_concat_vmap [new file with mode: 0755]

index 34e0880be470a9ae1f00bd3c22f4b5db8b426549..ea999f2e546c48f4f3da5fe1dd4aad1d2f4f5589 100644 (file)
@@ -1185,14 +1185,40 @@ struct expr *mapping_expr_alloc(const struct location *loc,
        return expr;
 }
 
+static bool __set_expr_is_vmap(const struct expr *mappings)
+{
+       const struct expr *mapping;
+
+       if (list_empty(&mappings->expressions))
+               return false;
+
+       mapping = list_first_entry(&mappings->expressions, struct expr, list);
+       if (mapping->etype == EXPR_MAPPING &&
+           mapping->right->etype == EXPR_VERDICT)
+               return true;
+
+       return false;
+}
+
+static bool set_expr_is_vmap(const struct expr *expr)
+{
+
+       if (expr->mappings->etype == EXPR_SET)
+               return __set_expr_is_vmap(expr->mappings);
+
+       return false;
+}
+
 static void map_expr_print(const struct expr *expr, struct output_ctx *octx)
 {
        expr_print(expr->map, octx);
-       if (expr->mappings->etype == EXPR_SET_REF &&
-           expr->mappings->set->data->dtype->type == TYPE_VERDICT)
+       if ((expr->mappings->etype == EXPR_SET_REF &&
+            expr->mappings->set->data->dtype->type == TYPE_VERDICT) ||
+           set_expr_is_vmap(expr))
                nft_print(octx, " vmap ");
        else
                nft_print(octx, " map ");
+
        expr_print(expr->mappings, octx);
 }
 
index f1501aaf282e2f96c83d51f83c5483a58535af5d..76872caa841413af28b3d17d8dd4a304bc91e216 100644 (file)
@@ -413,6 +413,61 @@ static void merge_stmts_vmap(const struct optimize_ctx *ctx,
        stmt_free(verdict_a);
 }
 
+static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
+                                   uint32_t from, uint32_t to,
+                                   const struct merge *merge)
+{
+       struct stmt *orig_stmt = ctx->stmt_matrix[from][merge->stmt[0]];
+       struct expr *concat_a, *concat_b, *expr, *set;
+       struct stmt *stmt, *stmt_a, *stmt_b, *verdict;
+       uint32_t i, j;
+       int k;
+
+       k = stmt_verdict_find(ctx);
+       assert(k >= 0);
+
+       /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */
+       concat_a = concat_expr_alloc(&internal_location);
+       for (i = 0; i < merge->num_stmts; i++) {
+               stmt_a = ctx->stmt_matrix[from][merge->stmt[i]];
+               compound_expr_add(concat_a, expr_get(stmt_a->expr->left));
+       }
+
+       /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 : accept } */
+       set = set_expr_alloc(&internal_location, NULL);
+       set->set_flags |= NFT_SET_ANONYMOUS;
+
+       for (i = from; i <= to; i++) {
+               concat_b = concat_expr_alloc(&internal_location);
+               for (j = 0; j < merge->num_stmts; j++) {
+                       stmt_b = ctx->stmt_matrix[i][merge->stmt[j]];
+                       expr = stmt_b->expr->right;
+                       compound_expr_add(concat_b, expr_get(expr));
+               }
+               verdict = ctx->stmt_matrix[i][k];
+               build_verdict_map(concat_b, verdict, set);
+               expr_free(concat_b);
+       }
+
+       expr = map_expr_alloc(&internal_location, concat_a, set);
+       stmt = verdict_stmt_alloc(&internal_location, expr);
+
+       remove_counter(ctx, from);
+       list_add(&stmt->list, &orig_stmt->list);
+       list_del(&orig_stmt->list);
+       stmt_free(orig_stmt);
+
+       for (i = 1; i < merge->num_stmts; i++) {
+               stmt_a = ctx->stmt_matrix[from][merge->stmt[i]];
+               list_del(&stmt_a->list);
+               stmt_free(stmt_a);
+       }
+
+       verdict = ctx->stmt_matrix[from][k];
+       list_del(&verdict->list);
+       stmt_free(verdict);
+}
+
 static bool stmt_verdict_cmp(const struct optimize_ctx *ctx,
                             uint32_t from, uint32_t to)
 {
@@ -483,7 +538,7 @@ static void merge_rules(const struct optimize_ctx *ctx,
                if (same_verdict)
                        merge_concat_stmts(ctx, from, to, merge);
                else
-                       return;
+                       merge_concat_stmts_vmap(ctx, from, to, merge);
        } else {
                if (same_verdict)
                        merge_stmts(ctx, from, to, merge);
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft
new file mode 100644 (file)
index 0000000..c0f9ce0
--- /dev/null
@@ -0,0 +1,5 @@
+table ip x {
+       chain y {
+               ip saddr . ip daddr vmap { 1.1.1.1 . 2.2.2.2 : accept, 2.2.2.2 . 3.3.3.3 : drop, 4.4.4.4 . 5.5.5.5 : accept }
+       }
+}
diff --git a/tests/shell/testcases/optimizations/merge_stmts_concat_vmap b/tests/shell/testcases/optimizations/merge_stmts_concat_vmap
new file mode 100755 (executable)
index 0000000..f1ab028
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+       chain y {
+               ip saddr 1.1.1.1 ip daddr 2.2.2.2 accept
+               ip saddr 2.2.2.2 ip daddr 3.3.3.3 drop
+               ip saddr 4.4.4.4 ip daddr 5.5.5.5 accept
+       }
+}"
+
+$NFT -o -f - <<< $RULESET