|
|
ddb7af |
From 0fb0e506d01f99548dbb9cabfef713bea7e447b5 Mon Sep 17 00:00:00 2001
|
|
|
ddb7af |
From: Phil Sutter <psutter@redhat.com>
|
|
|
ddb7af |
Date: Fri, 24 Jun 2022 16:02:59 +0200
|
|
|
ddb7af |
Subject: [PATCH] rule: collapse set element commands
|
|
|
ddb7af |
|
|
|
ddb7af |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1917398
|
|
|
ddb7af |
Upstream Status: nftables commit 498a5f0c219d8
|
|
|
ddb7af |
|
|
|
ddb7af |
commit 498a5f0c219d8a118af4f172f248647d9b077101
|
|
|
ddb7af |
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
|
ddb7af |
Date: Mon Jun 13 17:22:44 2022 +0200
|
|
|
ddb7af |
|
|
|
ddb7af |
rule: collapse set element commands
|
|
|
ddb7af |
|
|
|
ddb7af |
Robots might generate a long list of singleton element commands such as:
|
|
|
ddb7af |
|
|
|
ddb7af |
add element t s { 1.0.1.0/24 }
|
|
|
ddb7af |
...
|
|
|
ddb7af |
add element t s { 1.0.2.0/23 }
|
|
|
ddb7af |
|
|
|
ddb7af |
collapse them into one single command before the evaluation step, ie.
|
|
|
ddb7af |
|
|
|
ddb7af |
add element t s { 1.0.1.0/24, ..., 1.0.2.0/23 }
|
|
|
ddb7af |
|
|
|
ddb7af |
this speeds up overlap detection and set element automerge operations in
|
|
|
ddb7af |
this worst case scenario.
|
|
|
ddb7af |
|
|
|
ddb7af |
Since 3da9643fb9ff9 ("intervals: add support to automerge with kernel
|
|
|
ddb7af |
elements"), the new interval tracking relies on mergesort. The pattern
|
|
|
ddb7af |
above triggers the set sorting for each element.
|
|
|
ddb7af |
|
|
|
ddb7af |
This patch adds a list to cmd objects that store collapsed commands.
|
|
|
ddb7af |
Moreover, expressions also contain a reference to the original command,
|
|
|
ddb7af |
to uncollapse the commands after the evaluation step.
|
|
|
ddb7af |
|
|
|
ddb7af |
These commands are uncollapsed after the evaluation step to ensure error
|
|
|
ddb7af |
reporting works as expected (command and netlink message are mapped
|
|
|
ddb7af |
1:1).
|
|
|
ddb7af |
|
|
|
ddb7af |
For the record:
|
|
|
ddb7af |
|
|
|
ddb7af |
- nftables versions <= 1.0.2 did not perform any kind of overlap
|
|
|
ddb7af |
check for the described scenario above (because set cache only contained
|
|
|
ddb7af |
elements in the kernel in this case). This is a problem for kernels < 5.7
|
|
|
ddb7af |
which rely on userspace to detect overlaps.
|
|
|
ddb7af |
|
|
|
ddb7af |
- the overlap detection could be skipped for kernels >= 5.7.
|
|
|
ddb7af |
|
|
|
ddb7af |
- The extended netlink error reporting available for set elements
|
|
|
ddb7af |
since 5.19-rc might allow to remove the uncollapse step, in this case,
|
|
|
ddb7af |
error reporting does not rely on the netlink sequence to refer to the
|
|
|
ddb7af |
command triggering the problem.
|
|
|
ddb7af |
|
|
|
ddb7af |
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
|
ddb7af |
|
|
|
ddb7af |
Signed-off-by: Phil Sutter <psutter@redhat.com>
|
|
|
ddb7af |
---
|
|
|
ddb7af |
include/expression.h | 1 +
|
|
|
ddb7af |
include/rule.h | 3 ++
|
|
|
ddb7af |
src/libnftables.c | 17 ++++++++--
|
|
|
ddb7af |
src/rule.c | 75 ++++++++++++++++++++++++++++++++++++++++++++
|
|
|
ddb7af |
4 files changed, 93 insertions(+), 3 deletions(-)
|
|
|
ddb7af |
|
|
|
ddb7af |
diff --git a/include/expression.h b/include/expression.h
|
|
|
ddb7af |
index 2c3818e..53194c9 100644
|
|
|
ddb7af |
--- a/include/expression.h
|
|
|
ddb7af |
+++ b/include/expression.h
|
|
|
ddb7af |
@@ -243,6 +243,7 @@ struct expr {
|
|
|
ddb7af |
enum expr_types etype:8;
|
|
|
ddb7af |
enum ops op:8;
|
|
|
ddb7af |
unsigned int len;
|
|
|
ddb7af |
+ struct cmd *cmd;
|
|
|
ddb7af |
|
|
|
ddb7af |
union {
|
|
|
ddb7af |
struct {
|
|
|
ddb7af |
diff --git a/include/rule.h b/include/rule.h
|
|
|
ddb7af |
index e232b97..9081225 100644
|
|
|
ddb7af |
--- a/include/rule.h
|
|
|
ddb7af |
+++ b/include/rule.h
|
|
|
ddb7af |
@@ -700,6 +700,7 @@ struct cmd {
|
|
|
ddb7af |
enum cmd_obj obj;
|
|
|
ddb7af |
struct handle handle;
|
|
|
ddb7af |
uint32_t seqnum;
|
|
|
ddb7af |
+ struct list_head collapse_list;
|
|
|
ddb7af |
union {
|
|
|
ddb7af |
void *data;
|
|
|
ddb7af |
struct expr *expr;
|
|
|
ddb7af |
@@ -728,6 +729,8 @@ extern struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
|
|
|
ddb7af |
const struct handle *h, const struct location *loc,
|
|
|
ddb7af |
void *data);
|
|
|
ddb7af |
extern void nft_cmd_expand(struct cmd *cmd);
|
|
|
ddb7af |
+extern bool nft_cmd_collapse(struct list_head *cmds);
|
|
|
ddb7af |
+extern void nft_cmd_uncollapse(struct list_head *cmds);
|
|
|
ddb7af |
extern struct cmd *cmd_alloc_obj_ct(enum cmd_ops op, int type,
|
|
|
ddb7af |
const struct handle *h,
|
|
|
ddb7af |
const struct location *loc, struct obj *obj);
|
|
|
ddb7af |
diff --git a/src/libnftables.c b/src/libnftables.c
|
|
|
ddb7af |
index 6a22ea0..aac682b 100644
|
|
|
ddb7af |
--- a/src/libnftables.c
|
|
|
ddb7af |
+++ b/src/libnftables.c
|
|
|
ddb7af |
@@ -501,7 +501,9 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
|
|
|
ddb7af |
{
|
|
|
ddb7af |
struct nft_cache_filter *filter;
|
|
|
ddb7af |
struct cmd *cmd, *next;
|
|
|
ddb7af |
+ bool collapsed = false;
|
|
|
ddb7af |
unsigned int flags;
|
|
|
ddb7af |
+ int err = 0;
|
|
|
ddb7af |
|
|
|
ddb7af |
filter = nft_cache_filter_init();
|
|
|
ddb7af |
flags = nft_cache_evaluate(nft, cmds, filter);
|
|
|
ddb7af |
@@ -512,17 +514,26 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
|
|
|
ddb7af |
|
|
|
ddb7af |
nft_cache_filter_fini(filter);
|
|
|
ddb7af |
|
|
|
ddb7af |
+ if (nft_cmd_collapse(cmds))
|
|
|
ddb7af |
+ collapsed = true;
|
|
|
ddb7af |
+
|
|
|
ddb7af |
list_for_each_entry_safe(cmd, next, cmds, list) {
|
|
|
ddb7af |
struct eval_ctx ectx = {
|
|
|
ddb7af |
.nft = nft,
|
|
|
ddb7af |
.msgs = msgs,
|
|
|
ddb7af |
};
|
|
|
ddb7af |
+
|
|
|
ddb7af |
if (cmd_evaluate(&ectx, cmd) < 0 &&
|
|
|
ddb7af |
- ++nft->state->nerrs == nft->parser_max_errors)
|
|
|
ddb7af |
- return -1;
|
|
|
ddb7af |
+ ++nft->state->nerrs == nft->parser_max_errors) {
|
|
|
ddb7af |
+ err = -1;
|
|
|
ddb7af |
+ break;
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
}
|
|
|
ddb7af |
|
|
|
ddb7af |
- if (nft->state->nerrs)
|
|
|
ddb7af |
+ if (collapsed)
|
|
|
ddb7af |
+ nft_cmd_uncollapse(cmds);
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ if (err < 0 || nft->state->nerrs)
|
|
|
ddb7af |
return -1;
|
|
|
ddb7af |
|
|
|
ddb7af |
list_for_each_entry(cmd, cmds, list) {
|
|
|
ddb7af |
diff --git a/src/rule.c b/src/rule.c
|
|
|
ddb7af |
index 7f61bdc..0526a14 100644
|
|
|
ddb7af |
--- a/src/rule.c
|
|
|
ddb7af |
+++ b/src/rule.c
|
|
|
ddb7af |
@@ -1279,6 +1279,8 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
|
|
|
ddb7af |
cmd->handle = *h;
|
|
|
ddb7af |
cmd->location = *loc;
|
|
|
ddb7af |
cmd->data = data;
|
|
|
ddb7af |
+ init_list_head(&cmd->collapse_list);
|
|
|
ddb7af |
+
|
|
|
ddb7af |
return cmd;
|
|
|
ddb7af |
}
|
|
|
ddb7af |
|
|
|
ddb7af |
@@ -1379,6 +1381,79 @@ void nft_cmd_expand(struct cmd *cmd)
|
|
|
ddb7af |
}
|
|
|
ddb7af |
}
|
|
|
ddb7af |
|
|
|
ddb7af |
+bool nft_cmd_collapse(struct list_head *cmds)
|
|
|
ddb7af |
+{
|
|
|
ddb7af |
+ struct cmd *cmd, *next, *elems = NULL;
|
|
|
ddb7af |
+ struct expr *expr, *enext;
|
|
|
ddb7af |
+ bool collapse = false;
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ list_for_each_entry_safe(cmd, next, cmds, list) {
|
|
|
ddb7af |
+ if (cmd->op != CMD_ADD &&
|
|
|
ddb7af |
+ cmd->op != CMD_CREATE) {
|
|
|
ddb7af |
+ elems = NULL;
|
|
|
ddb7af |
+ continue;
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ if (cmd->obj != CMD_OBJ_ELEMENTS) {
|
|
|
ddb7af |
+ elems = NULL;
|
|
|
ddb7af |
+ continue;
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ if (!elems) {
|
|
|
ddb7af |
+ elems = cmd;
|
|
|
ddb7af |
+ continue;
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ if (cmd->op != elems->op) {
|
|
|
ddb7af |
+ elems = cmd;
|
|
|
ddb7af |
+ continue;
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ if (strcmp(elems->handle.table.name, cmd->handle.table.name) ||
|
|
|
ddb7af |
+ strcmp(elems->handle.set.name, cmd->handle.set.name)) {
|
|
|
ddb7af |
+ elems = cmd;
|
|
|
ddb7af |
+ continue;
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ collapse = true;
|
|
|
ddb7af |
+ list_for_each_entry_safe(expr, enext, &cmd->expr->expressions, list) {
|
|
|
ddb7af |
+ expr->cmd = cmd;
|
|
|
ddb7af |
+ list_move_tail(&expr->list, &elems->expr->expressions);
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+ elems->expr->size += cmd->expr->size;
|
|
|
ddb7af |
+ list_move_tail(&cmd->list, &elems->collapse_list);
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ return collapse;
|
|
|
ddb7af |
+}
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+void nft_cmd_uncollapse(struct list_head *cmds)
|
|
|
ddb7af |
+{
|
|
|
ddb7af |
+ struct cmd *cmd, *cmd_next, *collapse_cmd, *collapse_cmd_next;
|
|
|
ddb7af |
+ struct expr *expr, *next;
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ list_for_each_entry_safe(cmd, cmd_next, cmds, list) {
|
|
|
ddb7af |
+ if (list_empty(&cmd->collapse_list))
|
|
|
ddb7af |
+ continue;
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ assert(cmd->obj == CMD_OBJ_ELEMENTS);
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ list_for_each_entry_safe(expr, next, &cmd->expr->expressions, list) {
|
|
|
ddb7af |
+ if (!expr->cmd)
|
|
|
ddb7af |
+ continue;
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ list_move_tail(&expr->list, &expr->cmd->expr->expressions);
|
|
|
ddb7af |
+ cmd->expr->size--;
|
|
|
ddb7af |
+ expr->cmd = NULL;
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+
|
|
|
ddb7af |
+ list_for_each_entry_safe(collapse_cmd, collapse_cmd_next, &cmd->collapse_list, list) {
|
|
|
ddb7af |
+ collapse_cmd->elem.set = set_get(cmd->elem.set);
|
|
|
ddb7af |
+ list_add(&collapse_cmd->list, &cmd->list);
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+ }
|
|
|
ddb7af |
+}
|
|
|
ddb7af |
+
|
|
|
ddb7af |
struct markup *markup_alloc(uint32_t format)
|
|
|
ddb7af |
{
|
|
|
ddb7af |
struct markup *markup;
|
|
|
ddb7af |
--
|
|
|
ddb7af |
2.36.1
|
|
|
ddb7af |
|