]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add EXPR_RANGE_VALUE expression and use it
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 3 Jan 2025 16:40:54 +0000 (17:40 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 10 Jan 2025 10:21:02 +0000 (11:21 +0100)
set element with range takes 4 instances of struct expr:

  EXPR_SET_ELEM -> EXPR_RANGE -> (2) EXPR_VALUE

where EXPR_RANGE represents two references to struct expr with constant
value.

This new EXPR_RANGE_VALUE trims it down to two expressions:

  EXPR_SET_ELEM -> EXPR_RANGE_VALUE

with two direct low and high values that represent the range:

  struct {
      mpz_t low;
      mpz_t high;
  };

this two new direct values in struct expr do not modify its size.

setelem_expr_to_range() translates EXPR_RANGE to EXPR_RANGE_VALUE, this
conversion happens at a later stage.

constant_range_expr_print() translates this structure to constant values
to reuse the existing datatype_print() which relies in singleton values.

The automerge routine has been updated to use EXPR_RANGE_VALUE.

This requires a follow up patch to rework the conversion from range
expression to singleton element to provide a noticeable memory
consumption reduction.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/expression.h
src/expression.c
src/intervals.c
src/mergesort.c

index 877887ff1978f47953a9f59e402bc864da4d0fb9..f2b45250872d19fe0fc1b8b23759810f5ea4447a 100644 (file)
@@ -48,6 +48,7 @@
  * @EXPR_XFRM          XFRM (ipsec) expression
  * @EXPR_SET_ELEM_CATCHALL catchall element expression
  * @EXPR_FLAGCMP       flagcmp expression
+ * @EXPR_RANGE_VALUE   constant range expression
  */
 enum expr_types {
        EXPR_INVALID,
@@ -80,6 +81,7 @@ enum expr_types {
        EXPR_XFRM,
        EXPR_SET_ELEM_CATCHALL,
        EXPR_FLAGCMP,
+       EXPR_RANGE_VALUE,
 
        EXPR_MAX = EXPR_FLAGCMP
 };
@@ -278,6 +280,11 @@ struct expr {
                        /* EXPR_VALUE */
                        mpz_t                   value;
                };
+               struct {
+                       /* EXPR_RANGE_VALUE */
+                       mpz_t                   low;
+                       mpz_t                   high;
+               } range;
                struct {
                        /* EXPR_PREFIX */
                        struct expr             *prefix;
@@ -473,6 +480,12 @@ extern struct expr *constant_expr_join(const struct expr *e1,
                                       const struct expr *e2);
 extern struct expr *constant_expr_splice(struct expr *expr, unsigned int len);
 
+extern struct expr *constant_range_expr_alloc(const struct location *loc,
+                                             const struct datatype *dtype,
+                                             enum byteorder byteorder,
+                                             unsigned int len,
+                                             mpz_t low, mpz_t high);
+
 extern struct expr *flag_expr_alloc(const struct location *loc,
                                    const struct datatype *dtype,
                                    enum byteorder byteorder,
index 62786f483eeda35b484dbab4e2a42921a6e66176..b98b3d960a5076e7e957e2b517ce1c07cc591be9 100644 (file)
@@ -542,6 +542,84 @@ struct expr *constant_expr_splice(struct expr *expr, unsigned int len)
        return slice;
 }
 
+static void constant_range_expr_print_one(const struct expr *expr,
+                                         const mpz_t value,
+                                         struct output_ctx *octx)
+{
+       unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+       unsigned char data[len];
+       struct expr *dummy;
+
+       /* create dummy temporary constant expression to print range. */
+       mpz_export_data(data, value, expr->byteorder, len);
+       dummy = constant_expr_alloc(&expr->location, expr->dtype,
+                                   expr->byteorder, expr->len, data);
+       expr_print(dummy, octx);
+       expr_free(dummy);
+}
+
+static void constant_range_expr_print(const struct expr *expr,
+                                     struct output_ctx *octx)
+{
+       unsigned int flags = octx->flags;
+
+       /* similar to range_expr_print(). */
+       octx->flags &= ~(NFT_CTX_OUTPUT_SERVICE |
+                        NFT_CTX_OUTPUT_REVERSEDNS |
+                        NFT_CTX_OUTPUT_GUID);
+       octx->flags |= NFT_CTX_OUTPUT_NUMERIC_ALL;
+
+       constant_range_expr_print_one(expr, expr->range.low, octx);
+       nft_print(octx, "-");
+       constant_range_expr_print_one(expr, expr->range.high, octx);
+
+       octx->flags = flags;
+}
+
+static bool constant_range_expr_cmp(const struct expr *e1, const struct expr *e2)
+{
+       return expr_basetype(e1) == expr_basetype(e2) &&
+              !mpz_cmp(e1->range.low, e2->range.low) &&
+              !mpz_cmp(e1->range.high, e2->range.high);
+}
+
+static void constant_range_expr_clone(struct expr *new, const struct expr *expr)
+{
+       mpz_init_set(new->range.low, expr->range.low);
+       mpz_init_set(new->range.high, expr->range.high);
+}
+
+static void constant_range_expr_destroy(struct expr *expr)
+{
+       mpz_clear(expr->range.low);
+       mpz_clear(expr->range.high);
+}
+
+static const struct expr_ops constant_range_expr_ops = {
+       .type           = EXPR_RANGE_VALUE,
+       .name           = "range_value",
+       .print          = constant_range_expr_print,
+       .cmp            = constant_range_expr_cmp,
+       .clone          = constant_range_expr_clone,
+       .destroy        = constant_range_expr_destroy,
+};
+
+struct expr *constant_range_expr_alloc(const struct location *loc,
+                                      const struct datatype *dtype,
+                                      enum byteorder byteorder,
+                                      unsigned int len, mpz_t low, mpz_t high)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, EXPR_RANGE_VALUE, dtype, byteorder, len);
+       expr->flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+
+       mpz_init_set(expr->range.low, low);
+       mpz_init_set(expr->range.high, high);
+
+       return expr;
+}
+
 /*
  * Allocate a constant expression with a single bit set at position n.
  */
@@ -1545,6 +1623,8 @@ void range_expr_value_low(mpz_t rop, const struct expr *expr)
        switch (expr->etype) {
        case EXPR_VALUE:
                return mpz_set(rop, expr->value);
+       case EXPR_RANGE_VALUE:
+               return mpz_set(rop, expr->range.low);
        case EXPR_PREFIX:
                return range_expr_value_low(rop, expr->prefix);
        case EXPR_RANGE:
@@ -1565,6 +1645,8 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
        switch (expr->etype) {
        case EXPR_VALUE:
                return mpz_set(rop, expr->value);
+       case EXPR_RANGE_VALUE:
+               return mpz_set(rop, expr->range.high);
        case EXPR_PREFIX:
                range_expr_value_low(rop, expr->prefix);
                assert(expr->len >= expr->prefix_len);
@@ -1616,6 +1698,7 @@ static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
        case EXPR_XFRM: return &xfrm_expr_ops;
        case EXPR_SET_ELEM_CATCHALL: return &set_elem_catchall_expr_ops;
        case EXPR_FLAGCMP: return &flagcmp_expr_ops;
+       case EXPR_RANGE_VALUE: return &constant_range_expr_ops;
        }
 
        return NULL;
index ffd474fd595e0926608c5bdc631a0f9fee162dbe..c46874d9a6ce600929cdc8918f55068af2442285 100644 (file)
@@ -17,15 +17,24 @@ static void set_to_range(struct expr *init);
 
 static void setelem_expr_to_range(struct expr *expr)
 {
-       unsigned char data[sizeof(struct in6_addr) * BITS_PER_BYTE];
-       struct expr *key, *value;
+       struct expr *key;
        mpz_t rop;
 
        assert(expr->etype == EXPR_SET_ELEM);
 
        switch (expr->key->etype) {
        case EXPR_SET_ELEM_CATCHALL:
+       case EXPR_RANGE_VALUE:
+               break;
        case EXPR_RANGE:
+               key = constant_range_expr_alloc(&expr->location,
+                                               expr->key->dtype,
+                                               expr->key->byteorder,
+                                               expr->key->len,
+                                               expr->key->left->value,
+                                               expr->key->right->value);
+               expr_free(expr->key);
+               expr->key = key;
                break;
        case EXPR_PREFIX:
                if (expr->key->prefix->etype != EXPR_VALUE)
@@ -37,16 +46,13 @@ static void setelem_expr_to_range(struct expr *expr)
                        mpz_switch_byteorder(expr->key->prefix->value, expr->len / BITS_PER_BYTE);
 
                mpz_ior(rop, rop, expr->key->prefix->value);
-               mpz_export_data(data, rop, expr->key->prefix->byteorder,
-                               expr->key->prefix->len / BITS_PER_BYTE);
+               key = constant_range_expr_alloc(&expr->location,
+                                               expr->key->dtype,
+                                               expr->key->byteorder,
+                                               expr->key->len,
+                                               expr->key->prefix->value,
+                                               rop);
                mpz_clear(rop);
-               value = constant_expr_alloc(&expr->location,
-                                           expr->key->prefix->dtype,
-                                           expr->key->prefix->byteorder,
-                                           expr->key->prefix->len, data);
-               key = range_expr_alloc(&expr->location,
-                                      expr_get(expr->key->prefix),
-                                      value);
                expr_free(expr->key);
                expr->key = key;
                break;
@@ -54,9 +60,12 @@ static void setelem_expr_to_range(struct expr *expr)
                if (expr_basetype(expr)->type == TYPE_STRING)
                        mpz_switch_byteorder(expr->key->value, expr->len / BITS_PER_BYTE);
 
-               key = range_expr_alloc(&expr->location,
-                                      expr_clone(expr->key),
-                                      expr_get(expr->key));
+               key = constant_range_expr_alloc(&expr->location,
+                                               expr->key->dtype,
+                                               expr->key->byteorder,
+                                               expr->key->len,
+                                               expr->key->value,
+                                               expr->key->value);
                expr_free(expr->key);
                expr->key = key;
                break;
@@ -76,8 +85,8 @@ static void purge_elem(struct set_automerge_ctx *ctx, struct expr *i)
 {
        if (ctx->debug_mask & NFT_DEBUG_SEGTREE) {
                pr_gmp_debug("remove: [%Zx-%Zx]\n",
-                            i->key->left->value,
-                            i->key->right->value);
+                            i->key->range.low,
+                            i->key->range.high);
        }
        list_move_tail(&i->list, &ctx->purge->expressions);
 }
@@ -107,19 +116,16 @@ static bool merge_ranges(struct set_automerge_ctx *ctx,
        if (prev->flags & EXPR_F_KERNEL) {
                prev->location = i->location;
                purge_elem(ctx, prev);
-               expr_free(i->key->left);
-               i->key->left = expr_get(prev->key->left);
+               mpz_set(i->key->range.low, prev->key->range.low);
                mpz_set(prev_range->high, range->high);
                return true;
        } else if (i->flags & EXPR_F_KERNEL) {
                i->location = prev->location;
                purge_elem(ctx, i);
-               expr_free(prev->key->right);
-               prev->key->right = expr_get(i->key->right);
+               mpz_set(prev->key->range.high, i->key->range.high);
                mpz_set(prev_range->high, range->high);
        } else {
-               expr_free(prev->key->right);
-               prev->key->right = expr_get(i->key->right);
+               mpz_set(prev->key->range.high, i->key->range.high);
                mpz_set(prev_range->high, range->high);
                list_del(&i->list);
                expr_free(i);
@@ -272,7 +278,7 @@ int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set,
                } else if (existing_set) {
                        if (debug_mask & NFT_DEBUG_SEGTREE) {
                                pr_gmp_debug("add: [%Zx-%Zx]\n",
-                                            i->key->left->value, i->key->right->value);
+                                            i->key->range.low, i->key->range.high);
                        }
                        clone = expr_clone(i);
                        clone->flags |= EXPR_F_KERNEL;
@@ -306,9 +312,8 @@ static void remove_elem(struct expr *prev, struct set *set, struct expr *purge)
 static void __adjust_elem_left(struct set *set, struct expr *prev, struct expr *i)
 {
        prev->flags &= ~EXPR_F_KERNEL;
-       expr_free(prev->key->left);
-       prev->key->left = expr_get(i->key->right);
-       mpz_add_ui(prev->key->left->value, prev->key->left->value, 1);
+       mpz_set(prev->key->range.low, i->key->range.high);
+       mpz_add_ui(prev->key->range.low, prev->key->range.low, 1);
        list_move(&prev->list, &set->existing_set->init->expressions);
 }
 
@@ -326,9 +331,8 @@ static void adjust_elem_left(struct set *set, struct expr *prev, struct expr *i,
 static void __adjust_elem_right(struct set *set, struct expr *prev, struct expr *i)
 {
        prev->flags &= ~EXPR_F_KERNEL;
-       expr_free(prev->key->right);
-       prev->key->right = expr_get(i->key->left);
-       mpz_sub_ui(prev->key->right->value, prev->key->right->value, 1);
+       mpz_set(prev->key->range.high, i->key->range.low);
+       mpz_sub_ui(prev->key->range.high, prev->key->range.high, 1);
        list_move(&prev->list, &set->existing_set->init->expressions);
 }
 
@@ -357,14 +361,12 @@ static void split_range(struct set *set, struct expr *prev, struct expr *i,
 
        prev->flags &= ~EXPR_F_KERNEL;
        clone = expr_clone(prev);
-       expr_free(clone->key->left);
-       clone->key->left = expr_get(i->key->right);
-       mpz_add_ui(clone->key->left->value, i->key->right->value, 1);
+       mpz_set(clone->key->range.low, i->key->range.high);
+       mpz_add_ui(clone->key->range.low, i->key->range.high, 1);
        list_add_tail(&clone->list, &set->existing_set->init->expressions);
 
-       expr_free(prev->key->right);
-       prev->key->right = expr_get(i->key->left);
-       mpz_sub_ui(prev->key->right->value, i->key->left->value, 1);
+       mpz_set(prev->key->range.high, i->key->range.low);
+       mpz_sub_ui(prev->key->range.high, i->key->range.low, 1);
        list_move(&prev->list, &set->existing_set->init->expressions);
 
        list_del(&i->list);
@@ -537,13 +539,13 @@ int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
        if (debug_mask & NFT_DEBUG_SEGTREE) {
                list_for_each_entry(i, &init->expressions, list)
                        pr_gmp_debug("remove: [%Zx-%Zx]\n",
-                                    i->key->left->value, i->key->right->value);
+                                    i->key->range.low, i->key->range.high);
                list_for_each_entry(i, &add->expressions, list)
                        pr_gmp_debug("add: [%Zx-%Zx]\n",
-                                    i->key->left->value, i->key->right->value);
+                                    i->key->range.low, i->key->range.high);
                list_for_each_entry(i, &existing_set->init->expressions, list)
                        pr_gmp_debug("existing: [%Zx-%Zx]\n",
-                                    i->key->left->value, i->key->right->value);
+                                    i->key->range.low, i->key->range.high);
        }
 
        if (list_empty(&add->expressions)) {
@@ -698,7 +700,7 @@ int set_to_intervals(const struct set *set, struct expr *init, bool add)
                        continue;
 
                if (!prev && segtree_needs_first_segment(set, init, add) &&
-                   mpz_cmp_ui(elem->key->left->value, 0)) {
+                   mpz_cmp_ui(elem->key->range.low, 0)) {
                        mpz_set_ui(p, 0);
                        expr = constant_expr_alloc(&internal_location,
                                                   set->key->dtype,
@@ -722,15 +724,15 @@ int set_to_intervals(const struct set *set, struct expr *init, bool add)
                                mpz_switch_byteorder(p, set->key->len / BITS_PER_BYTE);
 
                        if (!(set->flags & NFT_SET_ANONYMOUS) ||
-                           mpz_cmp(p, elem->key->left->value) != 0)
+                           mpz_cmp(p, elem->key->range.low) != 0)
                                list_add_tail(&newelem->list, &intervals);
                        else
                                expr_free(newelem);
                }
                newelem = NULL;
 
-               if (mpz_scan0(elem->key->right->value, 0) != set->key->len) {
-                       mpz_add_ui(p, elem->key->right->value, 1);
+               if (mpz_scan0(elem->key->range.high, 0) != set->key->len) {
+                       mpz_add_ui(p, elem->key->range.high, 1);
                        expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
                                                   set->key->byteorder, set->key->len,
                                                   NULL);
@@ -752,7 +754,7 @@ int set_to_intervals(const struct set *set, struct expr *init, bool add)
                expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
                                           set->key->byteorder, set->key->len, NULL);
 
-               mpz_set(expr->value, elem->key->left->value);
+               mpz_set(expr->value, elem->key->range.low);
                if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
                        mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
 
index 5e676be16369b59f51002e78ab4a7ec290192fb7..0452d60ad42bf44aa6b788ca468b88577e80123b 100644 (file)
@@ -38,6 +38,8 @@ static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value)
                return expr_msort_value(expr->left, value);
        case EXPR_VALUE:
                return expr->value;
+       case EXPR_RANGE_VALUE:
+               return expr->range.low;
        case EXPR_CONCAT:
                concat_expr_msort_value(expr, value);
                break;