=== 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*::
*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]
|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
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
%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
}
;
-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
{
}
;
+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
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)
{
.table.location = *int_loc,
};
struct table *table = NULL;
+ enum table_flags flags = 0;
if (json_unpack_err(ctx, root, "{s:s}",
"family", &family))
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)) {
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)
static const char *table_flags_name[TABLE_FLAGS_MAX] = {
"dormant",
"owner",
+ "persist",
};
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)
{
--- /dev/null
+table t {
+ flags persist;
+}
--- /dev/null
+#!/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