|
|
f1d1c0 |
From 7b1f98e90a32865faca9a97f4348f20c753cd2f3 Mon Sep 17 00:00:00 2001
|
|
|
f1d1c0 |
From: Phil Sutter <psutter@redhat.com>
|
|
|
f1d1c0 |
Date: Fri, 14 Feb 2020 14:51:33 +0100
|
|
|
f1d1c0 |
Subject: [PATCH] src: Add support for concatenated set ranges
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1795224
|
|
|
f1d1c0 |
Upstream Status: nftables commit 8ac2f3b2fca38
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
commit 8ac2f3b2fca38b6533043b0678730c10ba4dc5ef
|
|
|
f1d1c0 |
Author: Stefano Brivio <sbrivio@redhat.com>
|
|
|
f1d1c0 |
Date: Thu Jan 30 01:16:57 2020 +0100
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
src: Add support for concatenated set ranges
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
After exporting field lengths via NFTNL_SET_DESC_CONCAT attributes,
|
|
|
f1d1c0 |
we now need to adjust parsing of user input and generation of
|
|
|
f1d1c0 |
netlink key data to complete support for concatenation of set
|
|
|
f1d1c0 |
ranges.
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
Instead of using separate elements for start and end of a range,
|
|
|
f1d1c0 |
denoting the end element by the NFT_SET_ELEM_INTERVAL_END flag,
|
|
|
f1d1c0 |
as it's currently done for ranges without concatenation, we'll use
|
|
|
f1d1c0 |
the new attribute NFTNL_SET_ELEM_KEY_END as suggested by Pablo. It
|
|
|
f1d1c0 |
behaves in the same way as NFTNL_SET_ELEM_KEY, but it indicates
|
|
|
f1d1c0 |
that the included key represents the upper bound of a range.
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
For example, "packets with an IPv4 address between 192.0.2.0 and
|
|
|
f1d1c0 |
192.0.2.42, with destination port between 22 and 25", needs to be
|
|
|
f1d1c0 |
expressed as a single element with two keys:
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
NFTA_SET_ELEM_KEY: 192.0.2.0 . 22
|
|
|
f1d1c0 |
NFTA_SET_ELEM_KEY_END: 192.0.2.42 . 25
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
To achieve this, we need to:
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- adjust the lexer rules to allow multiton expressions as elements
|
|
|
f1d1c0 |
of a concatenation. As wildcards are not allowed (semantics would
|
|
|
f1d1c0 |
be ambiguous), exclude wildcards expressions from the set of
|
|
|
f1d1c0 |
possible multiton expressions, and allow them directly where
|
|
|
f1d1c0 |
needed. Concatenations now admit prefixes and ranges
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- generate, for each element in a range concatenation, a second key
|
|
|
f1d1c0 |
attribute, that includes the upper bound for the range
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- also expand prefixes and non-ranged values in the concatenation
|
|
|
f1d1c0 |
to ranges: given a set with interval and concatenation support,
|
|
|
f1d1c0 |
the kernel has no way to tell which elements are ranged, so they
|
|
|
f1d1c0 |
all need to be. For example, 192.0.2.0 . 192.0.2.9 : 1024 is
|
|
|
f1d1c0 |
sent as:
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
NFTA_SET_ELEM_KEY: 192.0.2.0 . 1024
|
|
|
f1d1c0 |
NFTA_SET_ELEM_KEY_END: 192.0.2.9 . 1024
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- aggregate ranges when elements received by the kernel represent
|
|
|
f1d1c0 |
concatenated ranges, see concat_range_aggregate()
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- perform a few minor adjustments where interval expressions
|
|
|
f1d1c0 |
are already handled: we have intervals in these sets, but
|
|
|
f1d1c0 |
the set specification isn't just an interval, so we can't
|
|
|
f1d1c0 |
just aggregate and deaggregate interval ranges linearly
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
v4: No changes
|
|
|
f1d1c0 |
v3:
|
|
|
f1d1c0 |
- rework to use a separate key for closing element of range instead of
|
|
|
f1d1c0 |
a separate element with EXPR_F_INTERVAL_END set (Pablo Neira Ayuso)
|
|
|
f1d1c0 |
v2:
|
|
|
f1d1c0 |
- reworked netlink_gen_concat_data(), moved loop body to a new function,
|
|
|
f1d1c0 |
netlink_gen_concat_data_expr() (Phil Sutter)
|
|
|
f1d1c0 |
- dropped repeated pattern in bison file, replaced by a new helper,
|
|
|
f1d1c0 |
compound_expr_alloc_or_add() (Phil Sutter)
|
|
|
f1d1c0 |
- added set_is_nonconcat_range() helper (Phil Sutter)
|
|
|
f1d1c0 |
- in expr_evaluate_set(), we need to set NFT_SET_SUBKEY also on empty
|
|
|
f1d1c0 |
sets where the set in the context already has the flag
|
|
|
f1d1c0 |
- dropped additional 'end' parameter from netlink_gen_data(),
|
|
|
f1d1c0 |
temporarily set EXPR_F_INTERVAL_END on expressions and use that from
|
|
|
f1d1c0 |
netlink_gen_concat_data() to figure out we need to add the 'end'
|
|
|
f1d1c0 |
element (Phil Sutter)
|
|
|
f1d1c0 |
- replace range_mask_len() by a simplified version, as we don't need
|
|
|
f1d1c0 |
to actually store the composing masks of a range (Phil Sutter)
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
|
|
|
f1d1c0 |
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
|
f1d1c0 |
---
|
|
|
f1d1c0 |
include/expression.h | 1 +
|
|
|
bfbb76 |
include/rule.h | 5 ++
|
|
|
bfbb76 |
src/evaluate.c | 5 ++
|
|
|
bfbb76 |
src/netlink.c | 109 +++++++++++++++++++++++++++++-----------
|
|
|
bfbb76 |
src/parser_bison.y | 17 +++++--
|
|
|
bfbb76 |
src/rule.c | 13 ++---
|
|
|
bfbb76 |
src/segtree.c | 117 +++++++++++++++++++++++++++++++++++++++++++
|
|
|
f1d1c0 |
7 files changed, 229 insertions(+), 38 deletions(-)
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
diff --git a/include/expression.h b/include/expression.h
|
|
|
f1d1c0 |
index ee726aa..2e41aa0 100644
|
|
|
f1d1c0 |
--- a/include/expression.h
|
|
|
f1d1c0 |
+++ b/include/expression.h
|
|
|
f1d1c0 |
@@ -460,6 +460,7 @@ extern int set_to_intervals(struct list_head *msgs, struct set *set,
|
|
|
f1d1c0 |
struct expr *init, bool add,
|
|
|
f1d1c0 |
unsigned int debug_mask, bool merge,
|
|
|
f1d1c0 |
struct output_ctx *octx);
|
|
|
f1d1c0 |
+extern void concat_range_aggregate(struct expr *set);
|
|
|
f1d1c0 |
extern void interval_map_decompose(struct expr *set);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
extern struct expr *get_set_intervals(const struct set *set,
|
|
|
f1d1c0 |
diff --git a/include/rule.h b/include/rule.h
|
|
|
f1d1c0 |
index c03b0b8..626973e 100644
|
|
|
f1d1c0 |
--- a/include/rule.h
|
|
|
f1d1c0 |
+++ b/include/rule.h
|
|
|
f1d1c0 |
@@ -372,6 +372,11 @@ static inline bool set_is_interval(uint32_t set_flags)
|
|
|
f1d1c0 |
return set_flags & NFT_SET_INTERVAL;
|
|
|
f1d1c0 |
}
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
+static inline bool set_is_non_concat_range(struct set *s)
|
|
|
f1d1c0 |
+{
|
|
|
f1d1c0 |
+ return (s->flags & NFT_SET_INTERVAL) && s->desc.field_count <= 1;
|
|
|
f1d1c0 |
+}
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
#include <statement.h>
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
struct counter {
|
|
|
f1d1c0 |
diff --git a/src/evaluate.c b/src/evaluate.c
|
|
|
f1d1c0 |
index 58f458d..0c84816 100644
|
|
|
f1d1c0 |
--- a/src/evaluate.c
|
|
|
f1d1c0 |
+++ b/src/evaluate.c
|
|
|
f1d1c0 |
@@ -136,6 +136,11 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
if ((*expr)->byteorder == byteorder)
|
|
|
f1d1c0 |
return 0;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ /* Conversion for EXPR_CONCAT is handled for single composing ranges */
|
|
|
f1d1c0 |
+ if ((*expr)->etype == EXPR_CONCAT)
|
|
|
f1d1c0 |
+ return 0;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
if (expr_basetype(*expr)->type != TYPE_INTEGER)
|
|
|
f1d1c0 |
return expr_error(ctx->msgs, *expr,
|
|
|
f1d1c0 |
"Byteorder mismatch: expected %s, got %s",
|
|
|
f1d1c0 |
diff --git a/src/netlink.c b/src/netlink.c
|
|
|
f1d1c0 |
index 83d863c..e0ba903 100644
|
|
|
f1d1c0 |
--- a/src/netlink.c
|
|
|
f1d1c0 |
+++ b/src/netlink.c
|
|
|
f1d1c0 |
@@ -98,10 +98,11 @@ struct nftnl_expr *alloc_nft_expr(const char *name)
|
|
|
f1d1c0 |
static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
|
|
|
f1d1c0 |
const struct expr *expr)
|
|
|
f1d1c0 |
{
|
|
|
f1d1c0 |
- const struct expr *elem, *key, *data;
|
|
|
f1d1c0 |
+ const struct expr *elem, *data;
|
|
|
f1d1c0 |
struct nftnl_set_elem *nlse;
|
|
|
f1d1c0 |
struct nft_data_linearize nld;
|
|
|
f1d1c0 |
struct nftnl_udata_buf *udbuf = NULL;
|
|
|
f1d1c0 |
+ struct expr *key;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
nlse = nftnl_set_elem_alloc();
|
|
|
f1d1c0 |
if (nlse == NULL)
|
|
|
f1d1c0 |
@@ -119,6 +120,16 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
netlink_gen_data(key, &nld);
|
|
|
f1d1c0 |
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ if (set->set_flags & NFT_SET_INTERVAL && expr->key->field_count > 1) {
|
|
|
f1d1c0 |
+ key->flags |= EXPR_F_INTERVAL_END;
|
|
|
f1d1c0 |
+ netlink_gen_data(key, &nld);
|
|
|
f1d1c0 |
+ key->flags &= ~EXPR_F_INTERVAL_END;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY_END, &nld.value,
|
|
|
f1d1c0 |
+ nld.len);
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
if (elem->timeout)
|
|
|
f1d1c0 |
nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_TIMEOUT,
|
|
|
f1d1c0 |
elem->timeout);
|
|
|
f1d1c0 |
@@ -186,28 +197,58 @@ void netlink_gen_raw_data(const mpz_t value, enum byteorder byteorder,
|
|
|
f1d1c0 |
data->len = len;
|
|
|
f1d1c0 |
}
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
+static int netlink_export_pad(unsigned char *data, const mpz_t v,
|
|
|
f1d1c0 |
+ const struct expr *i)
|
|
|
f1d1c0 |
+{
|
|
|
f1d1c0 |
+ mpz_export_data(data, v, i->byteorder,
|
|
|
f1d1c0 |
+ div_round_up(i->len, BITS_PER_BYTE));
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ return netlink_padded_len(i->len) / BITS_PER_BYTE;
|
|
|
f1d1c0 |
+}
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+static int netlink_gen_concat_data_expr(int end, const struct expr *i,
|
|
|
f1d1c0 |
+ unsigned char *data)
|
|
|
f1d1c0 |
+{
|
|
|
f1d1c0 |
+ switch (i->etype) {
|
|
|
f1d1c0 |
+ case EXPR_RANGE:
|
|
|
f1d1c0 |
+ i = end ? i->right : i->left;
|
|
|
f1d1c0 |
+ break;
|
|
|
f1d1c0 |
+ case EXPR_PREFIX:
|
|
|
f1d1c0 |
+ if (end) {
|
|
|
f1d1c0 |
+ int count;
|
|
|
f1d1c0 |
+ mpz_t v;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ mpz_init_bitmask(v, i->len - i->prefix_len);
|
|
|
f1d1c0 |
+ mpz_add(v, i->prefix->value, v);
|
|
|
f1d1c0 |
+ count = netlink_export_pad(data, v, i);
|
|
|
f1d1c0 |
+ mpz_clear(v);
|
|
|
f1d1c0 |
+ return count;
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+ return netlink_export_pad(data, i->prefix->value, i);
|
|
|
f1d1c0 |
+ case EXPR_VALUE:
|
|
|
f1d1c0 |
+ break;
|
|
|
f1d1c0 |
+ default:
|
|
|
f1d1c0 |
+ BUG("invalid expression type '%s' in set", expr_ops(i)->name);
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ return netlink_export_pad(data, i->value, i);
|
|
|
f1d1c0 |
+}
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
static void netlink_gen_concat_data(const struct expr *expr,
|
|
|
f1d1c0 |
struct nft_data_linearize *nld)
|
|
|
f1d1c0 |
{
|
|
|
f1d1c0 |
+ unsigned int len = expr->len / BITS_PER_BYTE, offset = 0;
|
|
|
f1d1c0 |
+ int end = expr->flags & EXPR_F_INTERVAL_END;
|
|
|
f1d1c0 |
+ unsigned char data[len];
|
|
|
f1d1c0 |
const struct expr *i;
|
|
|
f1d1c0 |
- unsigned int len, offset;
|
|
|
f1d1c0 |
-
|
|
|
f1d1c0 |
- len = expr->len / BITS_PER_BYTE;
|
|
|
f1d1c0 |
- if (1) {
|
|
|
f1d1c0 |
- unsigned char data[len];
|
|
|
f1d1c0 |
-
|
|
|
f1d1c0 |
- memset(data, 0, sizeof(data));
|
|
|
f1d1c0 |
- offset = 0;
|
|
|
f1d1c0 |
- list_for_each_entry(i, &expr->expressions, list) {
|
|
|
f1d1c0 |
- assert(i->etype == EXPR_VALUE);
|
|
|
f1d1c0 |
- mpz_export_data(data + offset, i->value, i->byteorder,
|
|
|
f1d1c0 |
- div_round_up(i->len, BITS_PER_BYTE));
|
|
|
f1d1c0 |
- offset += netlink_padded_len(i->len) / BITS_PER_BYTE;
|
|
|
f1d1c0 |
- }
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- memcpy(nld->value, data, len);
|
|
|
f1d1c0 |
- nld->len = len;
|
|
|
f1d1c0 |
- }
|
|
|
f1d1c0 |
+ memset(data, 0, len);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ list_for_each_entry(i, &expr->expressions, list)
|
|
|
f1d1c0 |
+ offset += netlink_gen_concat_data_expr(end, i, data + offset);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ memcpy(nld->value, data, len);
|
|
|
f1d1c0 |
+ nld->len = len;
|
|
|
f1d1c0 |
}
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
static void netlink_gen_constant_data(const struct expr *expr,
|
|
|
f1d1c0 |
@@ -812,6 +853,7 @@ int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
|
|
|
f1d1c0 |
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_FLAGS))
|
|
|
f1d1c0 |
flags = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_FLAGS);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
+key_end:
|
|
|
f1d1c0 |
key = netlink_alloc_value(&netlink_location, &nld);
|
|
|
f1d1c0 |
datatype_set(key, set->key->dtype);
|
|
|
f1d1c0 |
key->byteorder = set->key->byteorder;
|
|
|
f1d1c0 |
@@ -880,6 +922,15 @@ int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
|
|
|
f1d1c0 |
}
|
|
|
f1d1c0 |
out:
|
|
|
f1d1c0 |
compound_expr_add(set->init, expr);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ if (!(flags & NFT_SET_ELEM_INTERVAL_END) &&
|
|
|
f1d1c0 |
+ nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_KEY_END)) {
|
|
|
f1d1c0 |
+ flags |= NFT_SET_ELEM_INTERVAL_END;
|
|
|
f1d1c0 |
+ nld.value = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_KEY_END,
|
|
|
f1d1c0 |
+ &nld.len);
|
|
|
f1d1c0 |
+ goto key_end;
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
return 0;
|
|
|
f1d1c0 |
}
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
@@ -918,15 +969,16 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
|
|
|
f1d1c0 |
set->init = set_expr_alloc(&internal_location, set);
|
|
|
f1d1c0 |
nftnl_set_elem_foreach(nls, list_setelem_cb, ctx);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- if (!(set->flags & NFT_SET_INTERVAL))
|
|
|
f1d1c0 |
+ if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1)
|
|
|
f1d1c0 |
+ concat_range_aggregate(set->init);
|
|
|
f1d1c0 |
+ else if (set->flags & NFT_SET_INTERVAL)
|
|
|
f1d1c0 |
+ interval_map_decompose(set->init);
|
|
|
f1d1c0 |
+ else
|
|
|
f1d1c0 |
list_expr_sort(&ctx->set->init->expressions);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
nftnl_set_free(nls);
|
|
|
f1d1c0 |
ctx->set = NULL;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- if (set->flags & NFT_SET_INTERVAL)
|
|
|
f1d1c0 |
- interval_map_decompose(set->init);
|
|
|
f1d1c0 |
-
|
|
|
f1d1c0 |
return 0;
|
|
|
f1d1c0 |
}
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
@@ -935,6 +987,7 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
|
|
|
f1d1c0 |
struct set *set, struct expr *init)
|
|
|
f1d1c0 |
{
|
|
|
f1d1c0 |
struct nftnl_set *nls, *nls_out = NULL;
|
|
|
f1d1c0 |
+ int err = 0;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
nls = nftnl_set_alloc();
|
|
|
f1d1c0 |
if (nls == NULL)
|
|
|
f1d1c0 |
@@ -958,18 +1011,18 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
|
|
|
f1d1c0 |
set->init = set_expr_alloc(loc, set);
|
|
|
f1d1c0 |
nftnl_set_elem_foreach(nls_out, list_setelem_cb, ctx);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- if (!(set->flags & NFT_SET_INTERVAL))
|
|
|
f1d1c0 |
+ if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1)
|
|
|
f1d1c0 |
+ concat_range_aggregate(set->init);
|
|
|
f1d1c0 |
+ else if (set->flags & NFT_SET_INTERVAL)
|
|
|
f1d1c0 |
+ err = get_set_decompose(table, set);
|
|
|
f1d1c0 |
+ else
|
|
|
f1d1c0 |
list_expr_sort(&ctx->set->init->expressions);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
nftnl_set_free(nls);
|
|
|
f1d1c0 |
nftnl_set_free(nls_out);
|
|
|
f1d1c0 |
ctx->set = NULL;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- if (set->flags & NFT_SET_INTERVAL &&
|
|
|
f1d1c0 |
- get_set_decompose(table, set) < 0)
|
|
|
f1d1c0 |
- return -1;
|
|
|
f1d1c0 |
-
|
|
|
f1d1c0 |
- return 0;
|
|
|
f1d1c0 |
+ return err;
|
|
|
f1d1c0 |
}
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
void netlink_dump_obj(struct nftnl_obj *nln, struct netlink_ctx *ctx)
|
|
|
f1d1c0 |
diff --git a/src/parser_bison.y b/src/parser_bison.y
|
|
|
f1d1c0 |
index 0fd9b94..ea83f52 100644
|
|
|
f1d1c0 |
--- a/src/parser_bison.y
|
|
|
f1d1c0 |
+++ b/src/parser_bison.y
|
|
|
f1d1c0 |
@@ -3551,7 +3551,6 @@ range_rhs_expr : basic_rhs_expr DASH basic_rhs_expr
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
multiton_rhs_expr : prefix_rhs_expr
|
|
|
f1d1c0 |
| range_rhs_expr
|
|
|
f1d1c0 |
- | wildcard_expr
|
|
|
f1d1c0 |
;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
map_expr : concat_expr MAP rhs_expr
|
|
|
f1d1c0 |
@@ -3645,7 +3644,7 @@ set_elem_option : TIMEOUT time_spec
|
|
|
f1d1c0 |
;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
set_lhs_expr : concat_rhs_expr
|
|
|
f1d1c0 |
- | multiton_rhs_expr
|
|
|
f1d1c0 |
+ | wildcard_expr
|
|
|
f1d1c0 |
;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
set_rhs_expr : concat_rhs_expr
|
|
|
f1d1c0 |
@@ -3898,7 +3897,7 @@ list_rhs_expr : basic_rhs_expr COMMA basic_rhs_expr
|
|
|
f1d1c0 |
;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
rhs_expr : concat_rhs_expr { $$ = $1; }
|
|
|
f1d1c0 |
- | multiton_rhs_expr { $$ = $1; }
|
|
|
f1d1c0 |
+ | wildcard_expr { $$ = $1; }
|
|
|
f1d1c0 |
| set_expr { $$ = $1; }
|
|
|
f1d1c0 |
| set_ref_symbol_expr { $$ = $1; }
|
|
|
f1d1c0 |
;
|
|
|
f1d1c0 |
@@ -3939,7 +3938,17 @@ basic_rhs_expr : inclusive_or_rhs_expr
|
|
|
f1d1c0 |
;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
concat_rhs_expr : basic_rhs_expr
|
|
|
f1d1c0 |
- | concat_rhs_expr DOT basic_rhs_expr
|
|
|
f1d1c0 |
+ | multiton_rhs_expr
|
|
|
f1d1c0 |
+ | concat_rhs_expr DOT multiton_rhs_expr
|
|
|
f1d1c0 |
+ {
|
|
|
f1d1c0 |
+ struct location rhs[] = {
|
|
|
f1d1c0 |
+ [1] = @2,
|
|
|
f1d1c0 |
+ [2] = @3,
|
|
|
f1d1c0 |
+ };
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+ | concat_rhs_expr DOT basic_rhs_expr
|
|
|
f1d1c0 |
{
|
|
|
f1d1c0 |
struct location rhs[] = {
|
|
|
f1d1c0 |
[1] = @2,
|
|
|
f1d1c0 |
diff --git a/src/rule.c b/src/rule.c
|
|
|
f1d1c0 |
index 4669577..e18237b 100644
|
|
|
f1d1c0 |
--- a/src/rule.c
|
|
|
f1d1c0 |
+++ b/src/rule.c
|
|
|
f1d1c0 |
@@ -1512,7 +1512,8 @@ static int __do_add_setelems(struct netlink_ctx *ctx, struct set *set,
|
|
|
f1d1c0 |
return -1;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
if (set->init != NULL &&
|
|
|
f1d1c0 |
- set->flags & NFT_SET_INTERVAL) {
|
|
|
f1d1c0 |
+ set->flags & NFT_SET_INTERVAL &&
|
|
|
f1d1c0 |
+ set->desc.field_count <= 1) {
|
|
|
f1d1c0 |
interval_map_decompose(expr);
|
|
|
f1d1c0 |
list_splice_tail_init(&expr->expressions, &set->init->expressions);
|
|
|
f1d1c0 |
set->init->size += expr->size;
|
|
|
f1d1c0 |
@@ -1533,7 +1534,7 @@ static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
|
|
|
f1d1c0 |
table = table_lookup(h, &ctx->nft->cache);
|
|
|
f1d1c0 |
set = set_lookup(table, h->set.name);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- if (set->flags & NFT_SET_INTERVAL &&
|
|
|
f1d1c0 |
+ if (set_is_non_concat_range(set) &&
|
|
|
f1d1c0 |
set_to_intervals(ctx->msgs, set, init, true,
|
|
|
f1d1c0 |
ctx->nft->debug_mask, set->automerge,
|
|
|
f1d1c0 |
&ctx->nft->output) < 0)
|
|
|
f1d1c0 |
@@ -1548,7 +1549,7 @@ static int do_add_set(struct netlink_ctx *ctx, const struct cmd *cmd,
|
|
|
f1d1c0 |
struct set *set = cmd->set;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
if (set->init != NULL) {
|
|
|
f1d1c0 |
- if (set->flags & NFT_SET_INTERVAL &&
|
|
|
f1d1c0 |
+ if (set_is_non_concat_range(set) &&
|
|
|
f1d1c0 |
set_to_intervals(ctx->msgs, set, set->init, true,
|
|
|
f1d1c0 |
ctx->nft->debug_mask, set->automerge,
|
|
|
f1d1c0 |
&ctx->nft->output) < 0)
|
|
|
f1d1c0 |
@@ -1634,7 +1635,7 @@ static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
|
|
|
f1d1c0 |
table = table_lookup(h, &ctx->nft->cache);
|
|
|
f1d1c0 |
set = set_lookup(table, h->set.name);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- if (set->flags & NFT_SET_INTERVAL &&
|
|
|
f1d1c0 |
+ if (set_is_non_concat_range(set) &&
|
|
|
f1d1c0 |
set_to_intervals(ctx->msgs, set, expr, false,
|
|
|
f1d1c0 |
ctx->nft->debug_mask, set->automerge,
|
|
|
f1d1c0 |
&ctx->nft->output) < 0)
|
|
|
f1d1c0 |
@@ -2488,7 +2489,7 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
|
|
|
f1d1c0 |
set = set_lookup(table, cmd->handle.set.name);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
/* Create a list of elements based of what we got from command line. */
|
|
|
f1d1c0 |
- if (set->flags & NFT_SET_INTERVAL)
|
|
|
f1d1c0 |
+ if (set_is_non_concat_range(set))
|
|
|
f1d1c0 |
init = get_set_intervals(set, cmd->expr);
|
|
|
f1d1c0 |
else
|
|
|
f1d1c0 |
init = cmd->expr;
|
|
|
f1d1c0 |
@@ -2501,7 +2502,7 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
|
|
|
f1d1c0 |
if (err >= 0)
|
|
|
f1d1c0 |
__do_list_set(ctx, cmd, table, new_set);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
- if (set->flags & NFT_SET_INTERVAL)
|
|
|
f1d1c0 |
+ if (set_is_non_concat_range(set))
|
|
|
f1d1c0 |
expr_free(init);
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
set_free(new_set);
|
|
|
f1d1c0 |
diff --git a/src/segtree.c b/src/segtree.c
|
|
|
f1d1c0 |
index 7217dbc..e859f84 100644
|
|
|
f1d1c0 |
--- a/src/segtree.c
|
|
|
f1d1c0 |
+++ b/src/segtree.c
|
|
|
f1d1c0 |
@@ -652,6 +652,11 @@ struct expr *get_set_intervals(const struct set *set, const struct expr *init)
|
|
|
f1d1c0 |
set_elem_add(set, new_init, i->key->value,
|
|
|
f1d1c0 |
i->flags, i->byteorder);
|
|
|
f1d1c0 |
break;
|
|
|
f1d1c0 |
+ case EXPR_CONCAT:
|
|
|
f1d1c0 |
+ compound_expr_add(new_init, expr_clone(i));
|
|
|
f1d1c0 |
+ i->flags |= EXPR_F_INTERVAL_END;
|
|
|
f1d1c0 |
+ compound_expr_add(new_init, expr_clone(i));
|
|
|
f1d1c0 |
+ break;
|
|
|
f1d1c0 |
default:
|
|
|
f1d1c0 |
range_expr_value_low(low, i);
|
|
|
f1d1c0 |
set_elem_add(set, new_init, low, 0, i->byteorder);
|
|
|
f1d1c0 |
@@ -823,6 +828,9 @@ static int expr_value_cmp(const void *p1, const void *p2)
|
|
|
f1d1c0 |
struct expr *e2 = *(void * const *)p2;
|
|
|
f1d1c0 |
int ret;
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
+ if (expr_value(e1)->etype == EXPR_CONCAT)
|
|
|
f1d1c0 |
+ return -1;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
ret = mpz_cmp(expr_value(e1)->value, expr_value(e2)->value);
|
|
|
f1d1c0 |
if (ret == 0) {
|
|
|
f1d1c0 |
if (e1->flags & EXPR_F_INTERVAL_END)
|
|
|
f1d1c0 |
@@ -834,6 +842,115 @@ static int expr_value_cmp(const void *p1, const void *p2)
|
|
|
f1d1c0 |
return ret;
|
|
|
f1d1c0 |
}
|
|
|
f1d1c0 |
|
|
|
f1d1c0 |
+/* Given start and end elements of a range, check if it can be represented as
|
|
|
f1d1c0 |
+ * a single netmask, and if so, how long, by returning zero or a positive value.
|
|
|
f1d1c0 |
+ */
|
|
|
f1d1c0 |
+static int range_mask_len(const mpz_t start, const mpz_t end, unsigned int len)
|
|
|
f1d1c0 |
+{
|
|
|
f1d1c0 |
+ mpz_t tmp_start, tmp_end;
|
|
|
f1d1c0 |
+ int ret;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ mpz_init_set_ui(tmp_start, mpz_get_ui(start));
|
|
|
f1d1c0 |
+ mpz_init_set_ui(tmp_end, mpz_get_ui(end));
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ while (mpz_cmp(tmp_start, tmp_end) <= 0 &&
|
|
|
f1d1c0 |
+ !mpz_tstbit(tmp_start, 0) && mpz_tstbit(tmp_end, 0) &&
|
|
|
f1d1c0 |
+ len--) {
|
|
|
f1d1c0 |
+ mpz_fdiv_q_2exp(tmp_start, tmp_start, 1);
|
|
|
f1d1c0 |
+ mpz_fdiv_q_2exp(tmp_end, tmp_end, 1);
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ ret = !mpz_cmp(tmp_start, tmp_end) ? (int)len : -1;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ mpz_clear(tmp_start);
|
|
|
f1d1c0 |
+ mpz_clear(tmp_end);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ return ret;
|
|
|
f1d1c0 |
+}
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+/* Given a set with two elements (start and end), transform them into a
|
|
|
f1d1c0 |
+ * concatenation of ranges. That is, from a list of start expressions and a list
|
|
|
f1d1c0 |
+ * of end expressions, form a list of start - end expressions.
|
|
|
f1d1c0 |
+ */
|
|
|
f1d1c0 |
+void concat_range_aggregate(struct expr *set)
|
|
|
f1d1c0 |
+{
|
|
|
f1d1c0 |
+ struct expr *i, *start = NULL, *end, *r1, *r2, *next, *r1_next, *tmp;
|
|
|
f1d1c0 |
+ struct list_head *r2_next;
|
|
|
f1d1c0 |
+ int prefix_len, free_r1;
|
|
|
f1d1c0 |
+ mpz_t range, p;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ list_for_each_entry_safe(i, next, &set->expressions, list) {
|
|
|
f1d1c0 |
+ if (!start) {
|
|
|
f1d1c0 |
+ start = i;
|
|
|
f1d1c0 |
+ continue;
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+ end = i;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ /* Walk over r1 (start expression) and r2 (end) in parallel,
|
|
|
f1d1c0 |
+ * form ranges between corresponding r1 and r2 expressions,
|
|
|
f1d1c0 |
+ * store them by replacing r2 expressions, and free r1
|
|
|
f1d1c0 |
+ * expressions.
|
|
|
f1d1c0 |
+ */
|
|
|
f1d1c0 |
+ r2 = list_first_entry(&expr_value(end)->expressions,
|
|
|
f1d1c0 |
+ struct expr, list);
|
|
|
f1d1c0 |
+ list_for_each_entry_safe(r1, r1_next,
|
|
|
f1d1c0 |
+ &expr_value(start)->expressions,
|
|
|
f1d1c0 |
+ list) {
|
|
|
f1d1c0 |
+ mpz_init(range);
|
|
|
f1d1c0 |
+ mpz_init(p);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ r2_next = r2->list.next;
|
|
|
f1d1c0 |
+ free_r1 = 0;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ if (!mpz_cmp(r1->value, r2->value)) {
|
|
|
f1d1c0 |
+ free_r1 = 1;
|
|
|
f1d1c0 |
+ goto next;
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ mpz_sub(range, r2->value, r1->value);
|
|
|
f1d1c0 |
+ mpz_sub_ui(range, range, 1);
|
|
|
f1d1c0 |
+ mpz_and(p, r1->value, range);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ /* Check if we are forced, or if it's anyway preferable,
|
|
|
f1d1c0 |
+ * to express the range as two points instead of a
|
|
|
f1d1c0 |
+ * netmask.
|
|
|
f1d1c0 |
+ */
|
|
|
f1d1c0 |
+ prefix_len = range_mask_len(r1->value, r2->value,
|
|
|
f1d1c0 |
+ r1->len);
|
|
|
f1d1c0 |
+ if (prefix_len < 0 ||
|
|
|
f1d1c0 |
+ !(r1->dtype->flags & DTYPE_F_PREFIX)) {
|
|
|
f1d1c0 |
+ tmp = range_expr_alloc(&r1->location, r1,
|
|
|
f1d1c0 |
+ r2);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ list_replace(&r2->list, &tmp->list);
|
|
|
f1d1c0 |
+ r2_next = tmp->list.next;
|
|
|
f1d1c0 |
+ } else {
|
|
|
f1d1c0 |
+ tmp = prefix_expr_alloc(&r1->location, r1,
|
|
|
f1d1c0 |
+ prefix_len);
|
|
|
f1d1c0 |
+ tmp->len = r2->len;
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ list_replace(&r2->list, &tmp->list);
|
|
|
f1d1c0 |
+ r2_next = tmp->list.next;
|
|
|
f1d1c0 |
+ expr_free(r2);
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+next:
|
|
|
f1d1c0 |
+ mpz_clear(p);
|
|
|
f1d1c0 |
+ mpz_clear(range);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ r2 = list_entry(r2_next, typeof(*r2), list);
|
|
|
f1d1c0 |
+ compound_expr_remove(start, r1);
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ if (free_r1)
|
|
|
f1d1c0 |
+ expr_free(r1);
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
+ compound_expr_remove(set, start);
|
|
|
f1d1c0 |
+ expr_free(start);
|
|
|
f1d1c0 |
+ start = NULL;
|
|
|
f1d1c0 |
+ }
|
|
|
f1d1c0 |
+}
|
|
|
f1d1c0 |
+
|
|
|
f1d1c0 |
void interval_map_decompose(struct expr *set)
|
|
|
f1d1c0 |
{
|
|
|
f1d1c0 |
struct expr **elements, **ranges;
|
|
|
f1d1c0 |
--
|
|
|
bfbb76 |
2.31.1
|
|
|
f1d1c0 |
|