]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: Don't merge adjacent/overlapping ranges
authorPhil Sutter <phil@nwl.cc>
Wed, 10 Jan 2018 20:32:04 +0000 (21:32 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 11 Jan 2018 12:20:38 +0000 (13:20 +0100)
Previously, when adding multiple ranges to a set they were merged if
overlapping or adjacent. This might cause inconvenience though since it
is afterwards not easily possible anymore to remove one of the merged
ranges again while keeping the others in place.

Since it is not possible to have overlapping ranges, this patch adds a
check for newly added ranges to make sure they don't overlap if merging
is turned off.

Note that it is not possible (yet?) to enable range merging using nft
tool.

Testsuite had to be adjusted as well: One test in tests/py changed avoid
adding overlapping ranges and the test in tests/shell which explicitly
tests for this feature dropped.

Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/expression.h
include/netlink.h
include/nftables.h
src/libnftables.c
src/rule.c
src/segtree.c
tests/py/any/meta.t
tests/py/any/meta.t.payload
tests/shell/testcases/sets/0002named_interval_automerging_0 [deleted file]

index 915ce0bad04dfd68e2ad563a96073a1003f2fe6b..0a0e178fe4680d73bec70d5b46cdfd61eb8fb39a 100644 (file)
@@ -419,7 +419,7 @@ extern struct expr *set_expr_alloc(const struct location *loc,
                                   const struct set *set);
 extern int set_to_intervals(struct list_head *msgs, struct set *set,
                            struct expr *init, bool add,
-                           unsigned int debug_mask);
+                           unsigned int debug_mask, bool merge);
 extern void interval_map_decompose(struct expr *set);
 
 extern struct expr *mapping_expr_alloc(const struct location *loc,
index 51cd5c9d1b94d75e3db21e6822f6f238f0cdc41e..4ec215da1dcde7574e30045e767610604c7d649b 100644 (file)
@@ -42,6 +42,7 @@ extern const struct location netlink_location;
  * @octx:      output context
  * @debug_mask:        display debugging information
  * @cache:     cache context
+ * @range_merge: merge adjacent/overlapping ranges in new set elements
  */
 struct netlink_ctx {
        struct mnl_socket       *nf_sock;
@@ -55,6 +56,7 @@ struct netlink_ctx {
        unsigned int            debug_mask;
        struct output_ctx       *octx;
        struct nft_cache        *cache;
+       bool                    range_merge;
 };
 
 extern struct nftnl_table *alloc_nftnl_table(const struct handle *h);
index 3bfa33e5cb336751663fda080d42247dd1058735..f22df0d1ddc15cfb10f4569159bdc037151bf61e 100644 (file)
@@ -31,6 +31,7 @@ struct nft_ctx {
        unsigned int            debug_mask;
        struct output_ctx       output;
        bool                    check;
+       bool                    range_merge;
        struct nft_cache        cache;
        uint32_t                flags;
 };
index c86d89477e778ae6aa483d1026fa71966bc20b40..8a18bb78f0d92c149ac3007a8f0c6554468fb6e0 100644 (file)
@@ -43,6 +43,7 @@ static int nft_netlink(struct nft_ctx *nft,
                ctx.nf_sock = nf_sock;
                ctx.cache = &nft->cache;
                ctx.debug_mask = nft->debug_mask;
+               ctx.range_merge = nft->range_merge;
                init_list_head(&ctx.list);
                ret = do_command(&ctx, cmd);
                if (ret < 0)
index bb9add07efaf5cbbeafda5ed397aab6b0ef707e5..edd0ff6f322c571804d431d612f9f84699c02fac 100644 (file)
@@ -997,7 +997,8 @@ static int do_add_setelems(struct netlink_ctx *ctx, const struct handle *h,
        set = set_lookup(table, h->set);
 
        if (set->flags & NFT_SET_INTERVAL &&
-           set_to_intervals(ctx->msgs, set, init, true, ctx->debug_mask) < 0)
+           set_to_intervals(ctx->msgs, set, init, true,
+                            ctx->debug_mask, ctx->range_merge) < 0)
                return -1;
 
        return __do_add_setelems(ctx, h, set, init, flags);
@@ -1009,7 +1010,7 @@ static int do_add_set(struct netlink_ctx *ctx, const struct handle *h,
        if (set->init != NULL) {
                if (set->flags & NFT_SET_INTERVAL &&
                    set_to_intervals(ctx->msgs, set, set->init, true,
-                                    ctx->debug_mask) < 0)
+                                    ctx->debug_mask, ctx->range_merge) < 0)
                        return -1;
        }
        if (netlink_add_set(ctx, h, set, flags) < 0)
@@ -1108,7 +1109,8 @@ static int do_delete_setelems(struct netlink_ctx *ctx, const struct handle *h,
        set = set_lookup(table, h->set);
 
        if (set->flags & NFT_SET_INTERVAL &&
-           set_to_intervals(ctx->msgs, set, expr, false, ctx->debug_mask) < 0)
+           set_to_intervals(ctx->msgs, set, expr, false,
+                            ctx->debug_mask, ctx->range_merge) < 0)
                return -1;
 
        if (netlink_delete_setelems(ctx, h, expr) < 0)
index 8d36cc9b0d65e017d9aef1b9019b05c95f4d906c..d2c4efaae2f0a4d1b799b99b85d4cf2347903ebe 100644 (file)
@@ -375,8 +375,26 @@ static int set_overlap(struct list_head *msgs, const struct set *set,
        return 0;
 }
 
+static int intervals_overlap(struct list_head *msgs,
+                            struct elementary_interval **intervals,
+                            unsigned int keylen)
+{
+       unsigned int i, j;
+
+       for (i = 0; i < keylen - 1; i++) {
+               for (j = i + 1; j < keylen; j++) {
+                       if (interval_overlap(intervals[i], intervals[j]))
+                               return expr_error(msgs, intervals[j]->expr,
+                                       "interval overlaps with previous one");
+               }
+       }
+
+       return 0;
+}
+
 static int set_to_segtree(struct list_head *msgs, struct set *set,
-                         struct expr *init, struct seg_tree *tree, bool add)
+                         struct expr *init, struct seg_tree *tree,
+                         bool add, bool merge)
 {
        struct elementary_interval *intervals[init->size];
        struct expr *i, *next;
@@ -394,6 +412,12 @@ static int set_to_segtree(struct list_head *msgs, struct set *set,
 
        n = expr_to_intervals(init, tree->keylen, intervals);
 
+       if (add && !merge) {
+               err = intervals_overlap(msgs, intervals, n);
+               if (err < 0)
+                       return err;
+       }
+
        list_for_each_entry_safe(i, next, &init->expressions, list) {
                list_del(&i->list);
                expr_free(i);
@@ -450,7 +474,7 @@ static bool segtree_needs_first_segment(const struct set *set,
 
 static void segtree_linearize(struct list_head *list, const struct set *set,
                              const struct expr *init, struct seg_tree *tree,
-                             bool add)
+                             bool add, bool merge)
 {
        bool needs_first_segment = segtree_needs_first_segment(set, init, add);
        struct elementary_interval *ei, *nei, *prev = NULL;
@@ -489,7 +513,8 @@ static void segtree_linearize(struct list_head *list, const struct set *set,
                                mpz_sub_ui(q, ei->left, 1);
                                nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
                                list_add_tail(&nei->list, list);
-                       } else if (add && ei->expr->ops->type != EXPR_MAPPING) {
+                       } else if (add && merge &&
+                                  ei->expr->ops->type != EXPR_MAPPING) {
                                /* Merge contiguous segments only in case of
                                 * new additions.
                                 */
@@ -550,16 +575,17 @@ static void set_insert_interval(struct expr *set, struct seg_tree *tree,
 }
 
 int set_to_intervals(struct list_head *errs, struct set *set,
-                    struct expr *init, bool add, unsigned int debug_mask)
+                    struct expr *init, bool add, unsigned int debug_mask,
+                    bool merge)
 {
        struct elementary_interval *ei, *next;
        struct seg_tree tree;
        LIST_HEAD(list);
 
        seg_tree_init(&tree, set, init, debug_mask);
-       if (set_to_segtree(errs, set, init, &tree, add) < 0)
+       if (set_to_segtree(errs, set, init, &tree, add, merge) < 0)
                return -1;
-       segtree_linearize(&list, set, init, &tree, add);
+       segtree_linearize(&list, set, init, &tree, add, merge);
 
        init->size = 0;
        list_for_each_entry_safe(ei, next, &list, list) {
index a38c5011f70821dcb3fbecf529485dd7ae539355..3158e8877357af83d72fd36a33cf1775fbe27d3a 100644 (file)
@@ -15,7 +15,7 @@ meta length 33-45;ok
 meta length != 33-45;ok
 meta length { 33, 55, 67, 88};ok
 meta length { 33-55, 67-88};ok
-meta length { 33-55, 55-88, 100-120};ok;meta length { 33-88, 100-120}
+meta length { 33-55, 56-88, 100-120};ok;meta length { 33-55, 56-88, 100-120}
 meta length != { 33, 55, 67, 88};ok
 meta length { 33-55};ok
 meta length != { 33-55};ok
index b2065f3d920b4487831554975f0b424ec2936744..d0199b8ae510be203d99115f11410fba48d6992f 100644 (file)
@@ -52,10 +52,10 @@ ip test-ip4 input
   [ byteorder reg 1 = hton(reg 1, 4, 4) ]
   [ lookup reg 1 set __set%d ]
 
-# meta length { 33-55, 55-88, 100-120}
+# meta length { 33-55, 56-88, 100-120}
 __set%d test-ip4 7
 __set%d test-ip4 0
-       element 00000000  : 1 [end]     element 21000000  : 0 [end]     element 59000000  : 1 [end]     element 64000000  : 0 [end]     element 79000000  : 1 [end]
+       element 00000000  : 1 [end]     element 21000000  : 0 [end]     element 38000000  : 0 [end]     element 59000000  : 1 [end]     element 64000000  : 0 [end]     element 79000000  : 1 [end]
 ip test-ip4 input
   [ meta load len => reg 1 ]
   [ byteorder reg 1 = hton(reg 1, 4, 4) ]
diff --git a/tests/shell/testcases/sets/0002named_interval_automerging_0 b/tests/shell/testcases/sets/0002named_interval_automerging_0
deleted file mode 100755 (executable)
index b07e0b0..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-# This testscase checks the automerging of adjacent intervals
-
-set -e
-
-$NFT add table t
-$NFT add set t s { type ipv4_addr \; flags interval \; }
-$NFT add element t s { 192.168.0.0/24, 192.168.1.0/24 }
-$NFT list ruleset | grep "192.168.0.0/23" >/dev/null && exit 0
-echo "E: automerging of adjavect intervals failed in named set" >&2
-exit 1