]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
Add support for table's persist flag
authorPhil Sutter <phil@nwl.cc>
Fri, 15 Dec 2023 00:10:39 +0000 (01:10 +0100)
committerPhil Sutter <phil@nwl.cc>
Fri, 19 Apr 2024 17:21:21 +0000 (19:21 +0200)
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>
doc/libnftables-json.adoc
doc/nft.txt
include/rule.h
src/parser_bison.y
src/parser_json.c
src/rule.c
tests/shell/features/table_flag_persist.nft [new file with mode: 0644]
tests/shell/testcases/owner/0002-persist [new file with mode: 0755]

index e3b24cc4ed60de8c5e3100204d315d1f44352dd8..a8a6165fde59d419f1ec7a1fa904e1491fe15284 100644 (file)
@@ -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]
index 248b29af369ad6dfa67c9994f507f4dd5a6b5d91..2080c07350f6d6ceb16945812103c900a33c7d5c 100644 (file)
@@ -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
index 3a833cf3a458831ecefb24776418907b35f100a0..5b3e12b5d7dcff762a741e7ff6bf106f556ef72b 100644 (file)
@@ -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
index 61bed761b0a93c755d38a1897ee27cb8d4a48c45..53f45315ef469e079fa0ddd90d9242f0a34f4a6e 100644 (file)
@@ -742,6 +742,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
@@ -1905,20 +1907,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;
-                                       free_const($2);
-                               } else if (strcmp($2, "owner") == 0) {
-                                       $<table>0->flags |= TABLE_F_OWNER;
-                                       free_const($2);
-                               } else {
-                                       erec_queue(error(&@2, "unknown table option %s", $2),
-                                                  state->msgs);
-                                       free_const($2);
-                                       YYERROR;
-                               }
+                               $<table>0->flags |= $2;
                        }
                        |       comment_spec
                        {
@@ -1930,6 +1921,24 @@ table_options            :       FLAGS           STRING
                        }
                        ;
 
+table_flags            :       table_flag
+                       |       table_flags     COMMA   table_flag
+                       {
+                               $$ = $1 | $3;
+                       }
+                       ;
+table_flag             :       STRING
+                       {
+                               $$ = parse_table_flag($1);
+                               free_const($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
index 418d4ad7f60779085bac1d6bfb1d0ca592d4da57..8b7efaf226627f5853b0fe440da51954f9fad048 100644 (file)
@@ -2966,6 +2966,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)
 {
@@ -2974,6 +3013,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))
@@ -2984,6 +3024,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)) {
@@ -2997,10 +3040,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)
index 45289cc01dce8be6af849d2d844f0551eda99e40..65ff0fbbe21f1fc49149686eb85bdaee1f373d47 100644 (file)
@@ -1215,6 +1215,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)
@@ -1225,6 +1226,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 (file)
index 0000000..0da3e6d
--- /dev/null
@@ -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 (executable)
index 0000000..cf4b8f1
--- /dev/null
@@ -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