338 lines
9.0 KiB
Diff
338 lines
9.0 KiB
Diff
From 450520649ac5ac6f983b40e15e54863aab9d5bd7 Mon Sep 17 00:00:00 2001
|
|
From: Phil Sutter <psutter@redhat.com>
|
|
Date: Fri, 14 Jun 2024 18:30:55 +0200
|
|
Subject: [PATCH] Add support for table's persist flag
|
|
|
|
JIRA: https://issues.redhat.com/browse/RHEL-32122
|
|
Upstream Status: nftables commit 4955ae1a81b73f9a61b7fbf1a73e11544513548e
|
|
Conflicts:
|
|
- Adjusted to missing commit ffd6b4790a728
|
|
("src: add free_const() and use it instead of xfree()")
|
|
|
|
commit 4955ae1a81b73f9a61b7fbf1a73e11544513548e
|
|
Author: Phil Sutter <phil@nwl.cc>
|
|
Date: Fri Dec 15 01:10:39 2023 +0100
|
|
|
|
Add support for table's persist flag
|
|
|
|
Bison parser lacked support for passing multiple flags, JSON parser
|
|
did not support table flags at all.
|
|
|
|
Document also 'owner' flag (and describe their relationship in nft.8.
|
|
|
|
Signed-off-by: Phil Sutter <phil@nwl.cc>
|
|
|
|
Signed-off-by: Phil Sutter <psutter@redhat.com>
|
|
---
|
|
doc/libnftables-json.adoc | 11 ++++-
|
|
doc/nft.txt | 9 ++++
|
|
include/rule.h | 4 +-
|
|
src/parser_bison.y | 35 +++++++++------
|
|
src/parser_json.c | 49 ++++++++++++++++++++-
|
|
src/rule.c | 12 +++++
|
|
tests/shell/features/table_flag_persist.nft | 3 ++
|
|
tests/shell/testcases/owner/0002-persist | 36 +++++++++++++++
|
|
8 files changed, 142 insertions(+), 17 deletions(-)
|
|
create mode 100644 tests/shell/features/table_flag_persist.nft
|
|
create mode 100755 tests/shell/testcases/owner/0002-persist
|
|
|
|
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
|
|
index 3e6e1db..0e424c2 100644
|
|
--- a/doc/libnftables-json.adoc
|
|
+++ b/doc/libnftables-json.adoc
|
|
@@ -202,12 +202,19 @@ Rename a chain. The new name is expected in a dedicated property named
|
|
|
|
=== TABLE
|
|
[verse]
|
|
+____
|
|
*{ "table": {
|
|
"family":* 'STRING'*,
|
|
"name":* 'STRING'*,
|
|
- "handle":* 'NUMBER'
|
|
+ "handle":* 'NUMBER'*,
|
|
+ "flags":* 'TABLE_FLAGS'
|
|
*}}*
|
|
|
|
+'TABLE_FLAGS' := 'TABLE_FLAG' | *[* 'TABLE_FLAG_LIST' *]*
|
|
+'TABLE_FLAG_LIST' := 'TABLE_FLAG' [*,* 'TABLE_FLAG_LIST' ]
|
|
+'TABLE_FLAG' := *"dormant"* | *"owner"* | *"persist"*
|
|
+____
|
|
+
|
|
This object describes a table.
|
|
|
|
*family*::
|
|
@@ -217,6 +224,8 @@ This object describes a table.
|
|
*handle*::
|
|
The table's handle. In input, it is used only in *delete* command as
|
|
alternative to *name*.
|
|
+*flags*::
|
|
+ The table's flags.
|
|
|
|
=== CHAIN
|
|
[verse]
|
|
diff --git a/doc/nft.txt b/doc/nft.txt
|
|
index b08e32f..dba1b60 100644
|
|
--- a/doc/nft.txt
|
|
+++ b/doc/nft.txt
|
|
@@ -343,8 +343,17 @@ return an error.
|
|
|Flag | Description
|
|
|dormant |
|
|
table is not evaluated any more (base chains are unregistered).
|
|
+|owner |
|
|
+table is owned by the creating process.
|
|
+|persist |
|
|
+table shall outlive the owning process.
|
|
|=================
|
|
|
|
+Creating a table with flag *owner* excludes other processes from manipulating
|
|
+it or its contents. By default, it will be removed when the process exits.
|
|
+Setting flag *persist* will prevent this and the resulting orphaned table will
|
|
+accept a new owner, e.g. a restarting daemon maintaining the table.
|
|
+
|
|
.*Add, change, delete a table*
|
|
---------------------------------------
|
|
# start nft in interactive mode
|
|
diff --git a/include/rule.h b/include/rule.h
|
|
index 6236d29..a8bb11f 100644
|
|
--- a/include/rule.h
|
|
+++ b/include/rule.h
|
|
@@ -130,10 +130,12 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier);
|
|
enum table_flags {
|
|
TABLE_F_DORMANT = (1 << 0),
|
|
TABLE_F_OWNER = (1 << 1),
|
|
+ TABLE_F_PERSIST = (1 << 2),
|
|
};
|
|
-#define TABLE_FLAGS_MAX 2
|
|
+#define TABLE_FLAGS_MAX 3
|
|
|
|
const char *table_flag_name(uint32_t flag);
|
|
+unsigned int parse_table_flag(const char *name);
|
|
|
|
/**
|
|
* struct table - nftables table
|
|
diff --git a/src/parser_bison.y b/src/parser_bison.y
|
|
index c517dc3..5ced6e1 100644
|
|
--- a/src/parser_bison.y
|
|
+++ b/src/parser_bison.y
|
|
@@ -720,6 +720,8 @@ int nft_lex(void *, void *, void *);
|
|
%type <rule> rule rule_alloc
|
|
%destructor { rule_free($$); } rule
|
|
|
|
+%type <val> table_flags table_flag
|
|
+
|
|
%type <val> set_flag_list set_flag
|
|
|
|
%type <val> set_policy_spec
|
|
@@ -1874,20 +1876,9 @@ table_block_alloc : /* empty */
|
|
}
|
|
;
|
|
|
|
-table_options : FLAGS STRING
|
|
+table_options : FLAGS table_flags
|
|
{
|
|
- if (strcmp($2, "dormant") == 0) {
|
|
- $<table>0->flags |= TABLE_F_DORMANT;
|
|
- xfree($2);
|
|
- } else if (strcmp($2, "owner") == 0) {
|
|
- $<table>0->flags |= TABLE_F_OWNER;
|
|
- xfree($2);
|
|
- } else {
|
|
- erec_queue(error(&@2, "unknown table option %s", $2),
|
|
- state->msgs);
|
|
- xfree($2);
|
|
- YYERROR;
|
|
- }
|
|
+ $<table>0->flags |= $2;
|
|
}
|
|
| comment_spec
|
|
{
|
|
@@ -1899,6 +1890,24 @@ table_options : FLAGS STRING
|
|
}
|
|
;
|
|
|
|
+table_flags : table_flag
|
|
+ | table_flags COMMA table_flag
|
|
+ {
|
|
+ $$ = $1 | $3;
|
|
+ }
|
|
+ ;
|
|
+table_flag : STRING
|
|
+ {
|
|
+ $$ = parse_table_flag($1);
|
|
+ xfree($1);
|
|
+ if ($$ == 0) {
|
|
+ erec_queue(error(&@1, "unknown table option %s", $1),
|
|
+ state->msgs);
|
|
+ YYERROR;
|
|
+ }
|
|
+ }
|
|
+ ;
|
|
+
|
|
table_block : /* empty */ { $$ = $<table>-1; }
|
|
| table_block common_block
|
|
| table_block stmt_separator
|
|
diff --git a/src/parser_json.c b/src/parser_json.c
|
|
index 199241a..9e5b656 100644
|
|
--- a/src/parser_json.c
|
|
+++ b/src/parser_json.c
|
|
@@ -2941,6 +2941,45 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
|
|
return NULL;
|
|
}
|
|
|
|
+static int json_parse_table_flags(struct json_ctx *ctx, json_t *root,
|
|
+ enum table_flags *flags)
|
|
+{
|
|
+ json_t *tmp, *tmp2;
|
|
+ size_t index;
|
|
+ int flag;
|
|
+
|
|
+ if (json_unpack(root, "{s:o}", "flags", &tmp))
|
|
+ return 0;
|
|
+
|
|
+ if (json_is_string(tmp)) {
|
|
+ flag = parse_table_flag(json_string_value(tmp));
|
|
+ if (flag) {
|
|
+ *flags = flag;
|
|
+ return 0;
|
|
+ }
|
|
+ json_error(ctx, "Invalid table flag '%s'.",
|
|
+ json_string_value(tmp));
|
|
+ return 1;
|
|
+ }
|
|
+ if (!json_is_array(tmp)) {
|
|
+ json_error(ctx, "Unexpected table flags value.");
|
|
+ return 1;
|
|
+ }
|
|
+ json_array_foreach(tmp, index, tmp2) {
|
|
+ if (json_is_string(tmp2)) {
|
|
+ flag = parse_table_flag(json_string_value(tmp2));
|
|
+
|
|
+ if (flag) {
|
|
+ *flags |= flag;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ json_error(ctx, "Invalid table flag at index %zu.", index);
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
|
|
enum cmd_ops op, enum cmd_obj obj)
|
|
{
|
|
@@ -2949,6 +2988,7 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
|
|
.table.location = *int_loc,
|
|
};
|
|
struct table *table = NULL;
|
|
+ enum table_flags flags = 0;
|
|
|
|
if (json_unpack_err(ctx, root, "{s:s}",
|
|
"family", &family))
|
|
@@ -2959,6 +2999,9 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
|
|
return NULL;
|
|
|
|
json_unpack(root, "{s:s}", "comment", &comment);
|
|
+ if (json_parse_table_flags(ctx, root, &flags))
|
|
+ return NULL;
|
|
+
|
|
} else if (op == CMD_DELETE &&
|
|
json_unpack(root, "{s:s}", "name", &h.table.name) &&
|
|
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
|
|
@@ -2972,10 +3015,12 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
|
|
if (h.table.name)
|
|
h.table.name = xstrdup(h.table.name);
|
|
|
|
- if (comment) {
|
|
+ if (comment || flags) {
|
|
table = table_alloc();
|
|
handle_merge(&table->handle, &h);
|
|
- table->comment = xstrdup(comment);
|
|
+ if (comment)
|
|
+ table->comment = xstrdup(comment);
|
|
+ table->flags = flags;
|
|
}
|
|
|
|
if (op == CMD_ADD)
|
|
diff --git a/src/rule.c b/src/rule.c
|
|
index 739b7a5..a0e151d 100644
|
|
--- a/src/rule.c
|
|
+++ b/src/rule.c
|
|
@@ -1208,6 +1208,7 @@ struct table *table_lookup_fuzzy(const struct handle *h,
|
|
static const char *table_flags_name[TABLE_FLAGS_MAX] = {
|
|
"dormant",
|
|
"owner",
|
|
+ "persist",
|
|
};
|
|
|
|
const char *table_flag_name(uint32_t flag)
|
|
@@ -1218,6 +1219,17 @@ const char *table_flag_name(uint32_t flag)
|
|
return table_flags_name[flag];
|
|
}
|
|
|
|
+unsigned int parse_table_flag(const char *name)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < TABLE_FLAGS_MAX; i++) {
|
|
+ if (!strcmp(name, table_flags_name[i]))
|
|
+ return 1 << i;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static void table_print_flags(const struct table *table, const char **delim,
|
|
struct output_ctx *octx)
|
|
{
|
|
diff --git a/tests/shell/features/table_flag_persist.nft b/tests/shell/features/table_flag_persist.nft
|
|
new file mode 100644
|
|
index 0000000..0da3e6d
|
|
--- /dev/null
|
|
+++ b/tests/shell/features/table_flag_persist.nft
|
|
@@ -0,0 +1,3 @@
|
|
+table t {
|
|
+ flags persist;
|
|
+}
|
|
diff --git a/tests/shell/testcases/owner/0002-persist b/tests/shell/testcases/owner/0002-persist
|
|
new file mode 100755
|
|
index 0000000..cf4b8f1
|
|
--- /dev/null
|
|
+++ b/tests/shell/testcases/owner/0002-persist
|
|
@@ -0,0 +1,36 @@
|
|
+#!/bin/bash
|
|
+
|
|
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_table_flag_owner)
|
|
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_table_flag_persist)
|
|
+
|
|
+die() {
|
|
+ echo "$@"
|
|
+ exit 1
|
|
+}
|
|
+
|
|
+$NFT -f - <<EOF
|
|
+table ip t {
|
|
+ flags owner, persist
|
|
+}
|
|
+EOF
|
|
+[[ $? -eq 0 ]] || {
|
|
+ die "table add failed"
|
|
+}
|
|
+
|
|
+$NFT list ruleset | grep -q 'table ip t' || {
|
|
+ die "table does not persist"
|
|
+}
|
|
+$NFT list ruleset | grep -q 'flags persist$' || {
|
|
+ die "unexpected flags in orphaned table"
|
|
+}
|
|
+
|
|
+$NFT -f - <<EOF
|
|
+table ip t {
|
|
+ flags owner, persist
|
|
+}
|
|
+EOF
|
|
+[[ $? -eq 0 ]] || {
|
|
+ die "retake ownership failed"
|
|
+}
|
|
+
|
|
+exit 0
|