440 lines
15 KiB
Diff
440 lines
15 KiB
Diff
From a1d0fd8692380bf37bcfc8e404537cffa6155adc Mon Sep 17 00:00:00 2001
|
|
From: Phil Sutter <psutter@redhat.com>
|
|
Date: Fri, 17 Jul 2026 11:11:00 +0200
|
|
Subject: [PATCH] src: add EXPR_RANGE_VALUE expression and use it
|
|
|
|
JIRA: https://issues.redhat.com/browse/RHEL-190549
|
|
Upstream Status: nftables commit c9ee9032b0ee9e802f73dda90f5c84357aff8148
|
|
|
|
commit c9ee9032b0ee9e802f73dda90f5c84357aff8148
|
|
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
Date: Fri Jan 3 17:40:54 2025 +0100
|
|
|
|
src: add EXPR_RANGE_VALUE expression and use it
|
|
|
|
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>
|
|
|
|
Signed-off-by: Phil Sutter <psutter@redhat.com>
|
|
---
|
|
include/expression.h | 13 +++++++
|
|
src/expression.c | 83 +++++++++++++++++++++++++++++++++++++++++
|
|
src/intervals.c | 88 ++++++++++++++++++++++----------------------
|
|
src/mergesort.c | 2 +
|
|
4 files changed, 143 insertions(+), 43 deletions(-)
|
|
|
|
diff --git a/include/expression.h b/include/expression.h
|
|
index bf6df76..af9b0d5 100644
|
|
--- a/include/expression.h
|
|
+++ b/include/expression.h
|
|
@@ -47,6 +47,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,
|
|
@@ -79,6 +80,7 @@ enum expr_types {
|
|
EXPR_XFRM,
|
|
EXPR_SET_ELEM_CATCHALL,
|
|
EXPR_FLAGCMP,
|
|
+ EXPR_RANGE_VALUE,
|
|
|
|
EXPR_MAX = EXPR_FLAGCMP
|
|
};
|
|
@@ -276,6 +278,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;
|
|
@@ -472,6 +479,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,
|
|
diff --git a/src/expression.c b/src/expression.c
|
|
index 992f510..adfdf27 100644
|
|
--- a/src/expression.c
|
|
+++ b/src/expression.c
|
|
@@ -462,6 +462,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.
|
|
*/
|
|
@@ -1460,6 +1538,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:
|
|
@@ -1480,6 +1560,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);
|
|
@@ -1531,6 +1613,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;
|
|
diff --git a/src/intervals.c b/src/intervals.c
|
|
index a817cd0..825d9b5 100644
|
|
--- a/src/intervals.c
|
|
+++ b/src/intervals.c
|
|
@@ -15,15 +15,24 @@
|
|
|
|
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:
|
|
mpz_init(rop);
|
|
@@ -32,16 +41,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;
|
|
@@ -49,9 +55,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;
|
|
@@ -71,8 +80,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);
|
|
}
|
|
@@ -102,19 +111,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);
|
|
@@ -263,7 +269,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;
|
|
@@ -297,9 +303,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);
|
|
}
|
|
|
|
@@ -317,9 +322,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);
|
|
}
|
|
|
|
@@ -348,14 +352,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);
|
|
@@ -528,13 +530,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)) {
|
|
@@ -689,7 +691,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,
|
|
@@ -713,15 +715,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);
|
|
@@ -743,7 +745,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);
|
|
|
|
diff --git a/src/mergesort.c b/src/mergesort.c
|
|
index 5e676be..0452d60 100644
|
|
--- a/src/mergesort.c
|
|
+++ b/src/mergesort.c
|
|
@@ -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;
|