]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
segtree: fix decomposition of unclosed intervals containing address prefixes
authorJeremy Sowden <jeremy@azazel.net>
Sun, 18 Sep 2022 17:22:12 +0000 (18:22 +0100)
committerFlorian Westphal <fw@strlen.de>
Wed, 21 Sep 2022 11:57:09 +0000 (13:57 +0200)
The code which decomposes unclosed intervals doesn't check for prefixes.  This
leads to incorrect output for sets which contain these.  For example,

  # nft -f - <<END
  table ip t {
    chain c {
      ip saddr 192.0.0.0/2 drop
      ip saddr 10.0.0.0/8 drop
      ip saddr { 192.0.0.0/2, 10.0.0.0/8 } drop
    }
  }
  table ip6 t {
    chain c {
      ip6 saddr ff00::/8 drop
      ip6 saddr fe80::/10 drop
      ip6 saddr { ff00::/8, fe80::/10 } drop
    }
  }
  END
  # nft list table ip6 t
  table ip6 t {
    chain c {
      ip6 saddr ff00::/8 drop
      ip6 saddr fe80::/10 drop
      ip6 saddr { fe80::/10, ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff } drop
    }
  }
  # nft list table ip t
  table ip t {
    chain c {
      ip saddr 192.0.0.0/2 drop
      ip saddr 10.0.0.0/8 drop
      ip saddr { 10.0.0.0/8, 192.0.0.0-255.255.255.255 } drop
    }
  }

Instead of treating the final unclosed interval as a special case, reuse the
code which correctly handles closed intervals.

Add a shell test-case.

Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1018156
Fixes: 86b965bdab8d ("segtree: fix decomposition of unclosed intervals")
Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
Signed-off-by: Florian Westphal <fw@strlen.de>
src/segtree.c
tests/shell/testcases/sets/0071unclosed_prefix_interval_0 [new file with mode: 0755]
tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft [new file with mode: 0644]

index d15c39f31f3a4c776bd12ab98bb43305dc79c192..0e3d111fb7aba4a91a2a1af99e4ce5e3ecd4cf28 100644 (file)
@@ -158,6 +158,8 @@ static struct expr *expr_value(struct expr *expr)
                return expr->left->key;
        case EXPR_SET_ELEM:
                return expr->key;
+       case EXPR_VALUE:
+               return expr;
        default:
                BUG("invalid expression type %s\n", expr_name(expr));
        }
@@ -503,7 +505,8 @@ add_interval(struct expr *set, struct expr *low, struct expr *i)
        mpz_init(p);
 
        mpz_sub(range, expr_value(i)->value, expr_value(low)->value);
-       mpz_sub_ui(range, range, 1);
+       if (i->etype != EXPR_VALUE)
+               mpz_sub_ui(range, range, 1);
 
        mpz_and(p, expr_value(low)->value, range);
 
@@ -618,25 +621,14 @@ void interval_map_decompose(struct expr *set)
        mpz_bitmask(i->value, i->len);
 
        if (!mpz_cmp(i->value, expr_value(low)->value)) {
-               expr_free(i);
-               i = low;
+               compound_expr_add(set, low);
        } else {
-               i = range_expr_alloc(&low->location,
-                                    expr_clone(expr_value(low)), i);
-               i = set_elem_expr_alloc(&low->location, i);
-               if (low->etype == EXPR_MAPPING) {
-                       i = mapping_expr_alloc(&i->location, i,
-                                              expr_clone(low->right));
-                       interval_expr_copy(i->left, low->left);
-               } else {
-                       interval_expr_copy(i, low);
-               }
-               i->flags |= EXPR_F_KERNEL;
-
+               add_interval(set, low, i);
                expr_free(low);
        }
 
-       compound_expr_add(set, i);
+       expr_free(i);
+
 out:
        if (catchall)
                compound_expr_add(set, catchall);
diff --git a/tests/shell/testcases/sets/0071unclosed_prefix_interval_0 b/tests/shell/testcases/sets/0071unclosed_prefix_interval_0
new file mode 100755 (executable)
index 0000000..79e3ca7
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+set -e
+
+RULESET="
+table inet t {
+       set s1 {
+               type ipv4_addr
+               flags interval
+               elements = { 192.0.0.0/2, 10.0.0.0/8 }
+       }
+       set s2 {
+               type ipv6_addr
+               flags interval
+               elements = { ff00::/8, fe80::/10 }
+       }
+       chain c {
+               ip saddr @s1 accept
+               ip6 daddr @s2 accept
+       }
+}"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft b/tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft
new file mode 100644 (file)
index 0000000..4eed94c
--- /dev/null
@@ -0,0 +1,19 @@
+table inet t {
+       set s1 {
+               type ipv4_addr
+               flags interval
+               elements = { 10.0.0.0/8, 192.0.0.0/2 }
+       }
+
+       set s2 {
+               type ipv6_addr
+               flags interval
+               elements = { fe80::/10,
+                            ff00::/8 }
+       }
+
+       chain c {
+               ip saddr @s1 accept
+               ip6 daddr @s2 accept
+       }
+}