summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac8
-rw-r--r--doc/nft.txt94
-rw-r--r--doc/primary-expression.txt11
-rw-r--r--doc/statements.txt19
-rw-r--r--include/cache.h1
-rw-r--r--include/datatype.h2
-rw-r--r--include/expression.h5
-rw-r--r--include/linux/netfilter/nf_log.h3
-rw-r--r--include/linux/netfilter/nf_tables.h2
-rw-r--r--include/netlink.h4
-rw-r--r--include/parser.h2
-rw-r--r--include/rule.h13
-rw-r--r--include/statement.h13
-rw-r--r--src/cache.c2
-rw-r--r--src/cmd.c38
-rw-r--r--src/datatype.c21
-rw-r--r--src/evaluate.c324
-rw-r--r--src/expression.c9
-rw-r--r--src/json.c18
-rw-r--r--src/main.c136
-rw-r--r--src/meta.c2
-rw-r--r--src/mnl.c97
-rw-r--r--src/monitor.c4
-rw-r--r--src/netlink.c64
-rw-r--r--src/netlink_delinearize.c53
-rw-r--r--src/netlink_linearize.c21
-rw-r--r--src/parser_bison.y193
-rw-r--r--src/parser_json.c4
-rw-r--r--src/rule.c127
-rw-r--r--src/segtree.c47
-rw-r--r--src/statement.c56
-rw-r--r--tests/py/inet/sets.t2
-rw-r--r--tests/py/inet/sets.t.payload.bridge14
-rw-r--r--tests/py/inet/sets.t.payload.inet13
-rw-r--r--tests/py/inet/sets.t.payload.netdev13
-rwxr-xr-xtests/py/nft-test.py6
-rwxr-xr-xtests/shell/run-tests.sh19
-rwxr-xr-xtests/shell/testcases/chains/0012reject_in_prerouting_111
-rwxr-xr-x[-rw-r--r--]tests/shell/testcases/chains/0030create_00
-rwxr-xr-xtests/shell/testcases/chains/0032priority_variable_010
-rwxr-xr-xtests/shell/testcases/chains/0041chain_binding_018
-rwxr-xr-xtests/shell/testcases/chains/0042chain_variable_037
-rw-r--r--tests/shell/testcases/chains/dumps/0032priority_variable_0.nft13
-rw-r--r--tests/shell/testcases/chains/dumps/0041chain_binding_0.nft12
-rw-r--r--tests/shell/testcases/chains/dumps/0042chain_variable_0.nft15
-rwxr-xr-xtests/shell/testcases/flowtable/0006segfault_03
-rwxr-xr-xtests/shell/testcases/flowtable/0012flowtable_variable_029
-rw-r--r--tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft14
-rwxr-xr-xtests/shell/testcases/maps/0009vmap_019
-rw-r--r--tests/shell/testcases/maps/dumps/0009vmap_0.nft13
-rw-r--r--tests/shell/testcases/optionals/dumps/log_prefix_0.nft5
-rwxr-xr-xtests/shell/testcases/optionals/log_prefix_016
-rwxr-xr-xtests/shell/testcases/sets/0043concatenated_ranges_093
-rwxr-xr-xtests/shell/testcases/sets/0044interval_overlap_0136
-rwxr-xr-xtests/shell/testcases/sets/0048set_counters_018
-rwxr-xr-xtests/shell/testcases/sets/0049set_define_016
-rwxr-xr-xtests/shell/testcases/sets/0050set_define_117
-rwxr-xr-xtests/shell/testcases/sets/0051set_interval_counter_019
-rwxr-xr-xtests/shell/testcases/sets/0052overlap_016
-rwxr-xr-xtests/shell/testcases/sets/0053echo_016
-rwxr-xr-xtests/shell/testcases/sets/0054comments_set_011
-rw-r--r--tests/shell/testcases/sets/dumps/0048set_counters_0.nft13
-rw-r--r--tests/shell/testcases/sets/dumps/0049set_define_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0051set_interval_counter_0.nft13
-rw-r--r--tests/shell/testcases/sets/dumps/0052overlap_0.nft8
-rw-r--r--tests/shell/testcases/sets/dumps/0053echo_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0054comments_set_0.nft7
-rwxr-xr-xtests/shell/testcases/transactions/0002table_01
-rw-r--r--tests/shell/testcases/transactions/dumps/0002table_0.nft4
69 files changed, 1698 insertions, 377 deletions
diff --git a/configure.ac b/configure.ac
index 5a1f89a0..47f6a7a3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
-AC_INIT([nftables], [0.9.4], [netfilter-devel@vger.kernel.org])
-AC_DEFINE([RELEASE_NAME], ["Jive at Five"], [Release name])
+AC_INIT([nftables], [0.9.6], [netfilter-devel@vger.kernel.org])
+AC_DEFINE([RELEASE_NAME], ["Capital Idea #2"], [Release name])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
@@ -56,8 +56,8 @@ AS_IF([test "x$enable_man_doc" = "xyes"], [
[AC_MSG_ERROR([a2x not found, please install asciidoc])])
])
-PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3])
-PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.1.6])
+PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.4])
+PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.1.7])
AC_ARG_WITH([mini-gmp], [AS_HELP_STRING([--with-mini-gmp],
[Use builtin mini-gmp (for embedded builds)])],
diff --git a/doc/nft.txt b/doc/nft.txt
index ba0c8c0b..5326de16 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -22,7 +22,10 @@ for Netfilter.
OPTIONS
-------
-For a full summary of options, run *nft --help*.
+The command accepts several different options which are documented here in groups for better
+understanding of their meaning. You can get information about options by running *nft --help*.
+
+.General options:
*-h*::
*--help*::
@@ -32,42 +35,73 @@ For a full summary of options, run *nft --help*.
*--version*::
Show version.
-*-n*::
-*--numeric*::
- Print fully numerical output.
+*-V*::
+ Show long version information, including compile-time configuration.
+
+.Ruleset input handling options that specify to how to load rulesets:
+
+*-f*::
+*--file 'filename'*::
+ Read input from 'filename'. If 'filename' is -, read from stdin.
+
+*-i*::
+*--interactive*::
+ Read input from an interactive readline CLI. You can use quit to exit, or use the EOF marker,
+ normally this is CTRL-D.
+
+*-I*::
+*--includepath directory*::
+ Add the directory 'directory' to the list of directories to be searched for included files. This
+ option may be specified multiple times.
+
+*-c*::
+*--check*::
+ Check commands validity without actually applying the changes.
+
+.Ruleset list output formatting that modify the output of the list ruleset command:
+
+*-a*::
+*--handle*::
+ Show object handles in output.
*-s*::
*--stateless*::
Omit stateful information of rules and stateful objects.
-*-N*::
-*--reversedns*::
- Translate IP address to names via reverse DNS lookup. This may slow down
- your listing since it generates network traffic.
+*-t*::
+*--terse*::
+ Omit contents of sets from output.
*-S*::
*--service*::
Translate ports to service names as defined by /etc/services.
+*-N*::
+*--reversedns*::
+ Translate IP address to names via reverse DNS lookup. This may slow down
+ your listing since it generates network traffic.
+
*-u*::
*--guid*::
Translate numeric UID/GID to names as defined by /etc/passwd and /etc/group.
-*-p*::
-*--numeric-protocol*::
- Display layer 4 protocol numerically.
+*-n*::
+*--numeric*::
+ Print fully numerical output.
*-y*::
*--numeric-priority*::
Display base chain priority numerically.
-*-c*::
-*--check*::
- Check commands validity without actually applying the changes.
+*-p*::
+*--numeric-protocol*::
+ Display layer 4 protocol numerically.
-*-a*::
-*--handle*::
- Show object handles in output.
+*-T*::
+*--numeric-time*::
+ Show time, day and hour values in numeric format.
+
+.Command output formatting:
*-e*::
*--echo*::
@@ -78,27 +112,11 @@ For a full summary of options, run *nft --help*.
*--json*::
Format output in JSON. See libnftables-json(5) for a schema description.
-*-I*::
-*--includepath directory*::
- Add the directory 'directory' to the list of directories to be searched for included files. This
- option may be specified multiple times.
-
-*-f*::
-*--file 'filename'*::
- Read input from 'filename'. If 'filename' is -, read from stdin.
-
-*-i*::
-*--interactive*::
- Read input from an interactive readline CLI. You can use quit to exit, or use the EOF marker,
- normally this is CTRL-D.
-
-*-T*::
-*--numeric-time*::
- Show time, day and hour values in numeric format.
-
-*-t*::
-*--terse*::
- Omit contents of sets from output.
+*-d*::
+*--debug* 'level'::
+ Enable debugging output. The debug level can be any of *scanner*, *parser*, *eval*,
+ *netlink*, *mnl*, *proto-ctx*, *segtree*, *all*. You can combine more than one by
+ separating by the ',' symbol, for example '-d eval,mnl'.
INPUT FILE FORMATS
------------------
diff --git a/doc/primary-expression.txt b/doc/primary-expression.txt
index 48a7609d..a9c39cbb 100644
--- a/doc/primary-expression.txt
+++ b/doc/primary-expression.txt
@@ -123,7 +123,7 @@ integer (32 bit)
pseudo-random number|
integer (32 bit)
|ipsec|
-boolean|
+true if packet was ipsec encrypted |
boolean (1 bit)
|iifkind|
Input interface kind |
@@ -162,7 +162,7 @@ Device group (32 bit number). Can be specified numerically or as symbolic name d
Packet type: *host* (addressed to local host), *broadcast* (to all),
*multicast* (to group), *other* (addressed to another host).
|ifkind|
-Interface kind (16 byte string). Does not have to exist.
+Interface kind (16 byte string). See TYPES in ip-link(8) for a list.
|time|
Either an integer or a date in ISO format. For example: "2019-06-06 17:00".
Hour and seconds are optional and can be omitted if desired. If omitted,
@@ -183,11 +183,12 @@ For example, 17:00 and 17:00:00 would be equivalent.
-----------------------
# qualified meta expression
filter output meta oif eth0
+filter forward meta iifkind { "tun", "veth" }
# unqualified meta expression
filter output oif eth0
-# packet was subject to ipsec processing
+# incoming packet was subject to ipsec processing
raw prerouting meta ipsec exists accept
-----------------------
@@ -362,13 +363,15 @@ Routing Realm (32 bit number). Can be specified numerically or as symbolic name
--------------------------
# IP family independent rt expression
filter output rt classid 10
-filter output rt ipsec missing
# IP family dependent rt expressions
ip filter output rt nexthop 192.168.0.1
ip6 filter output rt nexthop fd00::1
inet filter output rt ip nexthop 192.168.0.1
inet filter output rt ip6 nexthop fd00::1
+
+# outgoing packet will be encapsulated/encrypted by ipsec
+filter output rt ipsec exists
--------------------------
IPSEC EXPRESSIONS
diff --git a/doc/statements.txt b/doc/statements.txt
index ced311cb..9155f286 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -218,6 +218,11 @@ has to be assigned before a conntrack lookup takes place, i.e. this has to be
done in prerouting and possibly output (if locally generated packets need to be
placed in a distinct zone), with a hook priority of -300.
+Unlike iptables, where the helper assignment happens in the raw table,
+the helper needs to be assigned after a conntrack entry has been
+found, i.e. it will not work when used with hook priorities equal or before
+-200.
+
.Conntrack statement types
[options="header"]
|==================
@@ -263,6 +268,20 @@ table inet raw {
ct event set new,related,destroy
--------------------------------------
+NOTRACK STATEMENT
+~~~~~~~~~~~~~~~~~
+The notrack statement allows to disable connection tracking for certain
+packets.
+
+[verse]
+*notrack*
+
+Note that for this statement to be effective, it has to be applied to packets
+before a conntrack lookup happens. Therefore, it needs to sit in a chain with
+either prerouting or output hook and a hook priority of -300 or less.
+
+See SYNPROXY STATEMENT for an example usage.
+
META STATEMENT
~~~~~~~~~~~~~~
A meta statement sets the value of a meta expression. The existing meta fields
diff --git a/include/cache.h b/include/cache.h
index 86a7eff7..213a6eaf 100644
--- a/include/cache.h
+++ b/include/cache.h
@@ -30,6 +30,7 @@ enum cache_level_flags {
NFT_CACHE_CHAIN_BIT |
NFT_CACHE_RULE_BIT,
NFT_CACHE_FULL = __NFT_CACHE_MAX_BIT - 1,
+ NFT_CACHE_REFRESH = (1 << 29),
NFT_CACHE_UPDATE = (1 << 30),
NFT_CACHE_FLUSHED = (1 << 31),
};
diff --git a/include/datatype.h b/include/datatype.h
index 04b4892b..1061a389 100644
--- a/include/datatype.h
+++ b/include/datatype.h
@@ -305,4 +305,6 @@ extern struct error_record *rate_parse(const struct location *loc,
extern struct error_record *data_unit_parse(const struct location *loc,
const char *str, uint64_t *rate);
+extern void expr_chain_export(const struct expr *e, char *chain);
+
#endif /* NFTABLES_DATATYPE_H */
diff --git a/include/expression.h b/include/expression.h
index 8135a516..130912a8 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -249,6 +249,7 @@ struct expr {
/* EXPR_VERDICT */
int verdict;
struct expr *chain;
+ uint32_t chain_id;
};
struct {
/* EXPR_VALUE */
@@ -381,6 +382,8 @@ extern const struct datatype *expr_basetype(const struct expr *expr);
extern void expr_set_type(struct expr *expr, const struct datatype *dtype,
enum byteorder byteorder);
+void expr_to_string(const struct expr *expr, char *string);
+
struct eval_ctx;
extern int expr_binary_error(struct list_head *msgs,
const struct expr *e1, const struct expr *e2,
@@ -476,7 +479,7 @@ extern void interval_map_decompose(struct expr *set);
extern struct expr *get_set_intervals(const struct set *set,
const struct expr *init);
struct table;
-extern int get_set_decompose(struct table *table, struct set *set);
+extern int get_set_decompose(struct set *cache_set, struct set *set);
extern struct expr *mapping_expr_alloc(const struct location *loc,
struct expr *from, struct expr *to);
diff --git a/include/linux/netfilter/nf_log.h b/include/linux/netfilter/nf_log.h
index 8be21e02..2ae00932 100644
--- a/include/linux/netfilter/nf_log.h
+++ b/include/linux/netfilter/nf_log.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _NETFILTER_NF_LOG_H
#define _NETFILTER_NF_LOG_H
@@ -9,4 +10,6 @@
#define NF_LOG_MACDECODE 0x20 /* Decode MAC header */
#define NF_LOG_MASK 0x2f
+#define NF_LOG_PREFIXLEN 128
+
#endif /* _NETFILTER_NF_LOG_H */
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 4565456c..1341b52f 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -209,6 +209,7 @@ enum nft_chain_attributes {
NFTA_CHAIN_COUNTERS,
NFTA_CHAIN_PAD,
NFTA_CHAIN_FLAGS,
+ NFTA_CHAIN_ID,
__NFTA_CHAIN_MAX
};
#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1)
@@ -238,6 +239,7 @@ enum nft_rule_attributes {
NFTA_RULE_PAD,
NFTA_RULE_ID,
NFTA_RULE_POSITION_ID,
+ NFTA_RULE_CHAIN_ID,
__NFTA_RULE_MAX
};
#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1)
diff --git a/include/netlink.h b/include/netlink.h
index 0a5fde3c..1077096e 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -64,6 +64,7 @@ struct netlink_ctx {
struct nft_ctx *nft;
struct list_head *msgs;
struct list_head list;
+ struct list_head list_bindings;
struct set *set;
const void *data;
uint32_t seqnum;
@@ -83,6 +84,7 @@ struct nft_data_linearize {
uint32_t len;
uint32_t value[4];
char chain[NFT_CHAIN_MAXNAMELEN];
+ uint32_t chain_id;
int verdict;
};
@@ -147,7 +149,7 @@ extern struct stmt *netlink_parse_set_expr(const struct set *set,
extern int netlink_list_setelems(struct netlink_ctx *ctx,
const struct handle *h, struct set *set);
extern int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
- const struct location *loc, struct table *table,
+ const struct location *loc, struct set *cache_set,
struct set *set, struct expr *init);
extern int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
struct set *set,
diff --git a/include/parser.h b/include/parser.h
index 636d1c88..9baa3a4d 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -11,7 +11,7 @@
#define YYLTYPE_IS_TRIVIAL 0
#define YYENABLE_NLS 0
-#define SCOPE_NEST_MAX 3
+#define SCOPE_NEST_MAX 4
struct parser_state {
struct input_descriptor *indesc;
diff --git a/include/rule.h b/include/rule.h
index cfb76b8a..caca63d0 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -79,6 +79,7 @@ struct handle {
struct position_spec position;
struct position_spec index;
uint32_t set_id;
+ uint32_t chain_id;
uint32_t rule_id;
uint32_t position_id;
};
@@ -155,6 +156,7 @@ struct table {
struct list_head sets;
struct list_head objs;
struct list_head flowtables;
+ struct list_head chain_bindings;
enum table_flags flags;
unsigned int refcnt;
};
@@ -176,6 +178,7 @@ extern struct table *table_lookup_fuzzy(const struct handle *h,
enum chain_flags {
CHAIN_F_BASECHAIN = 0x1,
CHAIN_F_HW_OFFLOAD = 0x2,
+ CHAIN_F_BINDING = 0x4,
};
/**
@@ -244,12 +247,16 @@ extern struct chain *chain_lookup(const struct table *table,
extern struct chain *chain_lookup_fuzzy(const struct handle *h,
const struct nft_cache *cache,
const struct table **table);
+extern struct chain *chain_binding_lookup(const struct table *table,
+ const char *chain_name);
extern const char *family2str(unsigned int family);
extern const char *hooknum2str(unsigned int family, unsigned int hooknum);
extern const char *chain_policy2str(uint32_t policy);
extern void chain_print_plain(const struct chain *chain,
struct output_ctx *octx);
+extern void chain_rules_print(const struct chain *chain,
+ struct output_ctx *octx, const char *indent);
/**
* struct rule - nftables rule
@@ -302,6 +309,7 @@ void rule_stmt_insert_at(struct rule *rule, struct stmt *nstmt,
* @rg_cache: cached range element (left)
* @policy: set mechanism policy
* @automerge: merge adjacents and overlapping elements, if possible
+ * @comment: comment
* @desc.size: count of set elements
* @desc.field_len: length of single concatenated fields, bytes
* @desc.field_count: count of concatenated fields
@@ -324,6 +332,7 @@ struct set {
bool root;
bool automerge;
bool key_typeof_valid;
+ const char *comment;
struct {
uint32_t size;
uint8_t field_len[NFT_REG32_COUNT];
@@ -676,6 +685,10 @@ struct cmd {
void *data;
struct expr *expr;
struct set *set;
+ struct {
+ struct expr *expr; /* same offset as cmd->expr */
+ struct set *set;
+ } elem;
struct rule *rule;
struct chain *chain;
struct table *table;
diff --git a/include/statement.h b/include/statement.h
index 7d96b394..f2fc6ade 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -11,6 +11,14 @@ extern struct stmt *expr_stmt_alloc(const struct location *loc,
extern struct stmt *verdict_stmt_alloc(const struct location *loc,
struct expr *expr);
+struct chain_stmt {
+ struct chain *chain;
+ struct expr *expr;
+};
+
+struct stmt *chain_stmt_alloc(const struct location *loc, struct chain *chain,
+ enum nft_verdicts verdict);
+
struct flow_stmt {
const char *table_name;
};
@@ -75,7 +83,7 @@ enum {
};
struct log_stmt {
- const char *prefix;
+ struct expr *prefix;
unsigned int snaplen;
uint16_t group;
uint16_t qthreshold;
@@ -287,6 +295,7 @@ extern struct stmt *xt_stmt_alloc(const struct location *loc);
* @STMT_CONNLIMIT: connection limit statement
* @STMT_MAP: map statement
* @STMT_SYNPROXY: synproxy statement
+ * @STMT_CHAIN: chain statement
*/
enum stmt_types {
STMT_INVALID,
@@ -315,6 +324,7 @@ enum stmt_types {
STMT_CONNLIMIT,
STMT_MAP,
STMT_SYNPROXY,
+ STMT_CHAIN,
};
/**
@@ -380,6 +390,7 @@ struct stmt {
struct flow_stmt flow;
struct map_stmt map;
struct synproxy_stmt synproxy;
+ struct chain_stmt chain;
};
};
diff --git a/src/cache.c b/src/cache.c
index a45111a7..7797ff6b 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -143,6 +143,8 @@ unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
break;
case CMD_LIST:
case CMD_EXPORT:
+ flags |= NFT_CACHE_FULL | NFT_CACHE_REFRESH;
+ break;
case CMD_MONITOR:
flags |= NFT_CACHE_FULL;
break;
diff --git a/src/cmd.c b/src/cmd.c
index c8ea4492..e0cf3e77 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -6,6 +6,7 @@
#include <iface.h>
#include <errno.h>
#include <stdlib.h>
+#include <cache.h>
#include <string.h>
static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
@@ -40,6 +41,40 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
return 1;
}
+static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
+ struct location *loc)
+{
+ unsigned int flags = NFT_CACHE_TABLE |
+ NFT_CACHE_CHAIN;
+ const struct table *table;
+ struct chain *chain;
+
+ if (cache_update(ctx->nft, flags, ctx->msgs) < 0)
+ return 0;
+
+ table = table_lookup_fuzzy(&cmd->handle, &ctx->nft->cache);
+ if (table && strcmp(cmd->handle.table.name, table->handle.table.name)) {
+ netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?",
+ strerror(ENOENT), table->handle.table.name,
+ family2str(table->handle.family));
+ return 1;
+ } else if (!table) {
+ return 0;
+ }
+
+ chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
+ if (chain && strcmp(cmd->handle.chain.name, chain->handle.chain.name)) {
+ netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ strerror(ENOENT),
+ chain->handle.chain.name,
+ family2str(table->handle.family),
+ table->handle.table.name);
+ return 1;
+ }
+
+ return 0;
+}
+
static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
struct location *loc)
{
@@ -109,6 +144,9 @@ static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
case CMD_OBJ_SET:
ret = nft_cmd_enoent_set(ctx, cmd, loc);
break;
+ case CMD_OBJ_RULE:
+ ret = nft_cmd_enoent_rule(ctx, cmd, loc);
+ break;
case CMD_OBJ_COUNTER:
case CMD_OBJ_QUOTA:
case CMD_OBJ_CT_HELPER:
diff --git a/src/datatype.c b/src/datatype.c
index 90905258..7382307e 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -247,20 +247,25 @@ const struct datatype invalid_type = {
.print = invalid_type_print,
};
+void expr_chain_export(const struct expr *e, char *chain_name)
+{
+ unsigned int len;
+
+ len = e->len / BITS_PER_BYTE;
+ if (len >= NFT_CHAIN_MAXNAMELEN)
+ BUG("verdict expression length %u is too large (%u bits max)",
+ e->len, NFT_CHAIN_MAXNAMELEN * BITS_PER_BYTE);
+
+ mpz_export_data(chain_name, e->value, BYTEORDER_HOST_ENDIAN, len);
+}
+
static void verdict_jump_chain_print(const char *what, const struct expr *e,
struct output_ctx *octx)
{
char chain[NFT_CHAIN_MAXNAMELEN];
- unsigned int len;
memset(chain, 0, sizeof(chain));
-
- len = e->len / BITS_PER_BYTE;
- if (len >= sizeof(chain))
- BUG("verdict expression length %u is too large (%lu bits max)",
- e->len, (unsigned long)sizeof(chain) * BITS_PER_BYTE);
-
- mpz_export_data(chain, e->value, BYTEORDER_HOST_ENDIAN, len);
+ expr_chain_export(e, chain);
nft_print(octx, "%s %s", what, chain);
}
diff --git a/src/evaluate.c b/src/evaluate.c
index 4156d896..b64ed3c0 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -19,6 +19,7 @@
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_synproxy.h>
#include <linux/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_log.h>
#include <linux/netfilter_ipv4.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
@@ -80,6 +81,7 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set);
static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
const char *name,
struct expr *key,
+ struct expr *data,
struct expr *expr)
{
struct cmd *cmd;
@@ -93,6 +95,7 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
set->flags = NFT_SET_ANONYMOUS | expr->set_flags;
set->handle.set.name = xstrdup(name);
set->key = key;
+ set->data = data;
set->init = expr;
set->automerge = set->flags & NFT_SET_INTERVAL;
@@ -1411,7 +1414,7 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
struct expr_ctx ectx = ctx->ectx;
struct expr *map = *expr, *mappings;
const struct datatype *dtype;
- struct expr *key;
+ struct expr *key, *data;
expr_set_context(&ctx->ectx, NULL, 0);
if (expr_evaluate(ctx, &map->map) < 0)
@@ -1430,15 +1433,14 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->ectx.byteorder,
ctx->ectx.len, NULL);
+ dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
+ data = constant_expr_alloc(&netlink_location, dtype,
+ dtype->byteorder, ectx.len, NULL);
+
mappings = implicit_set_declaration(ctx, "__map%d",
- key,
+ key, data,
mappings);
- dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
-
- mappings->set->data = constant_expr_alloc(&netlink_location,
- dtype, dtype->byteorder,
- ectx.len, NULL);
if (ectx.len && mappings->set->data->len != ectx.len)
BUG("%d vs %d\n", mappings->set->data->len, ectx.len);
@@ -1896,16 +1898,30 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
return -1;
break;
case EXPR_SET:
+ if (right->size == 0)
+ return expr_error(ctx->msgs, right, "Set is empty");
+
right = rel->right =
implicit_set_declaration(ctx, "__set%d",
- expr_get(left), right);
+ expr_get(left), NULL,
+ right);
/* fall through */
case EXPR_SET_REF:
+ if (rel->left->etype == EXPR_CT &&
+ (rel->left->ct.key == NFT_CT_SRC ||
+ rel->left->ct.key == NFT_CT_DST))
+ return expr_error(ctx->msgs, left,
+ "specify either ip or ip6 for address matching");
+
/* Data for range lookups needs to be in big endian order */
if (right->set->flags & NFT_SET_INTERVAL &&
byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0)
return -1;
break;
+ case EXPR_CONCAT:
+ return expr_binary_error(ctx->msgs, left, right,
+ "Use concatenations with sets and maps, not singleton values");
+ break;
default:
BUG("invalid expression type %s\n", expr_name(right));
}
@@ -2007,10 +2023,15 @@ static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *new = expr_clone((*exprp)->sym->expr);
+ if (expr_evaluate(ctx, &new) < 0) {
+ expr_free(new);
+ return -1;
+ }
+
expr_free(*exprp);
*exprp = new;
- return expr_evaluate(ctx, exprp);
+ return 0;
}
static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp)
@@ -2389,7 +2410,7 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
set->set_flags |= NFT_SET_TIMEOUT;
setref = implicit_set_declaration(ctx, stmt->meter.name,
- expr_get(key), set);
+ expr_get(key), NULL, set);
setref->set->desc.size = stmt->meter.size;
stmt->meter.set = setref;
@@ -3092,6 +3113,63 @@ static int stmt_evaluate_synproxy(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule,
+ enum cmd_ops op);
+
+static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct chain *chain = stmt->chain.chain;
+ struct cmd *cmd;
+
+ chain->flags |= CHAIN_F_BINDING;
+
+ if (ctx->table != NULL) {
+ list_add_tail(&chain->list, &ctx->table->chains);
+ } else {
+ struct rule *rule, *next;
+ struct handle h;
+
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &chain->handle);
+ h.family = ctx->rule->handle.family;
+ xfree(h.table.name);
+ h.table.name = xstrdup(ctx->rule->handle.table.name);
+ h.chain.location = stmt->location;
+ h.chain_id = chain->handle.chain_id;
+
+ cmd = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h, &stmt->location,
+ chain);
+ cmd->location = stmt->location;
+ list_add_tail(&cmd->list, &ctx->cmd->list);
+ h.chain_id = chain->handle.chain_id;
+
+ list_for_each_entry_safe(rule, next, &chain->rules, list) {
+ struct eval_ctx rule_ctx = {
+ .nft = ctx->nft,
+ .msgs = ctx->msgs,
+ };
+ struct handle h2 = {};
+
+ handle_merge(&rule->handle, &ctx->rule->handle);
+ xfree(rule->handle.table.name);
+ rule->handle.table.name = xstrdup(ctx->rule->handle.table.name);
+ xfree(rule->handle.chain.name);
+ rule->handle.chain.name = NULL;
+ rule->handle.chain_id = chain->handle.chain_id;
+ if (rule_evaluate(&rule_ctx, rule, CMD_INVALID) < 0)
+ return -1;
+
+ handle_merge(&h2, &rule->handle);
+ cmd = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h2,
+ &rule->location, rule);
+ list_add_tail(&cmd->list, &ctx->cmd->list);
+ list_del(&rule->list);
+ }
+ }
+
+ return 0;
+}
+
static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
{
int err;
@@ -3198,8 +3276,49 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ char prefix[NF_LOG_PREFIXLEN] = {}, tmp[NF_LOG_PREFIXLEN] = {};
+ int len = sizeof(prefix), offset = 0, ret;
+ struct expr *expr;
+ size_t size = 0;
+
+ if (stmt->log.prefix->etype != EXPR_LIST)
+ return 0;
+
+ list_for_each_entry(expr, &stmt->log.prefix->expressions, list) {
+ switch (expr->etype) {
+ case EXPR_VALUE:
+ expr_to_string(expr, tmp);
+ ret = snprintf(prefix + offset, len, "%s", tmp);
+ break;
+ case EXPR_VARIABLE:
+ ret = snprintf(prefix + offset, len, "%s",
+ expr->sym->expr->identifier);
+ break;
+ default:
+ BUG("unknown expresion type %s\n", expr_name(expr));
+ break;
+ }
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ if (len == NF_LOG_PREFIXLEN)
+ return stmt_error(ctx, stmt, "log prefix is too long");
+
+ expr = constant_expr_alloc(&stmt->log.prefix->location, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(prefix) * BITS_PER_BYTE, prefix);
+ expr_free(stmt->log.prefix);
+ stmt->log.prefix = expr;
+
+ return 0;
+}
+
static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
{
+ int ret = 0;
+
if (stmt->log.flags & (STMT_LOG_GROUP | STMT_LOG_SNAPLEN |
STMT_LOG_QTHRESHOLD)) {
if (stmt->log.flags & STMT_LOG_LEVEL)
@@ -3213,7 +3332,11 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
(stmt->log.flags & ~STMT_LOG_LEVEL || stmt->log.logflags))
return stmt_error(ctx, stmt,
"log level audit doesn't support any further options");
- return 0;
+
+ if (stmt->log.prefix)
+ ret = stmt_evaluate_log_prefix(ctx, stmt);
+
+ return ret;
}
static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
@@ -3318,7 +3441,7 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
ctx->ectx.len, NULL);
mappings = implicit_set_declaration(ctx, "__objmap%d",
- key, mappings);
+ key, NULL, mappings);
mappings->set->objtype = stmt->objref.type;
map->mappings = mappings;
@@ -3438,12 +3561,14 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_evaluate_map(ctx, stmt);
case STMT_SYNPROXY:
return stmt_evaluate_synproxy(ctx, stmt);
+ case STMT_CHAIN:
+ return stmt_evaluate_chain(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
}
-static int setelem_evaluate(struct eval_ctx *ctx, struct expr **expr)
+static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
{
struct table *table;
struct set *set;
@@ -3459,9 +3584,12 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct expr **expr)
ctx->set = set;
expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
- if (expr_evaluate(ctx, expr) < 0)
+ if (expr_evaluate(ctx, &cmd->expr) < 0)
return -1;
ctx->set = NULL;
+
+ cmd->elem.set = set_get(set);
+
return 0;
}
@@ -3530,11 +3658,6 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
return set_key_data_error(ctx, set,
set->data->dtype, type);
} else if (set_is_objmap(set->flags)) {
- if (set->data) {
- assert(set->data->etype == EXPR_VALUE);
- assert(set->data->dtype == &string_type);
- }
-
assert(set->data == NULL);
set->data = constant_expr_alloc(&netlink_location, &string_type,
BYTEORDER_HOST_ENDIAN,
@@ -3590,7 +3713,6 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
mpz_export_data(prio_str, prio->expr->value, BYTEORDER_HOST_ENDIAN,
NFT_NAME_MAXLEN);
loc = prio->expr->location;
- expr_free(prio->expr);
if (sscanf(prio_str, "%s %c %d", prio_fst, &op, &prio_snd) < 3) {
priority = std_prio_lookup(prio_str, family, hook);
@@ -3607,6 +3729,7 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
else
return false;
}
+ expr_free(prio->expr);
prio->expr = constant_expr_alloc(&loc, &integer_type,
BYTEORDER_HOST_ENDIAN,
sizeof(int) * BITS_PER_BYTE,
@@ -3614,6 +3737,69 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
return true;
}
+static bool evaluate_expr_variable(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct expr *expr;
+
+ if (expr_evaluate(ctx, exprp) < 0)
+ return false;
+
+ expr = *exprp;
+ if (expr->etype != EXPR_VALUE &&
+ expr->etype != EXPR_SET) {
+ expr_error(ctx->msgs, expr, "%s is not a valid "
+ "variable expression", expr_name(expr));
+ return false;
+ }
+
+ return true;
+}
+
+static bool evaluate_device_expr(struct eval_ctx *ctx, struct expr **dev_expr)
+{
+ struct expr *expr, *next, *key;
+ LIST_HEAD(tmp);
+
+ if ((*dev_expr)->etype == EXPR_VARIABLE) {
+ expr_set_context(&ctx->ectx, &ifname_type,
+ IFNAMSIZ * BITS_PER_BYTE);
+ if (!evaluate_expr_variable(ctx, dev_expr))
+ return false;
+ }
+
+ if ((*dev_expr)->etype != EXPR_SET &&
+ (*dev_expr)->etype != EXPR_LIST)
+ return true;
+
+ list_for_each_entry_safe(expr, next, &(*dev_expr)->expressions, list) {
+ list_del(&expr->list);
+
+ switch (expr->etype) {
+ case EXPR_VARIABLE:
+ expr_set_context(&ctx->ectx, &ifname_type,
+ IFNAMSIZ * BITS_PER_BYTE);
+ if (!evaluate_expr_variable(ctx, &expr))
+ return false;
+ break;
+ case EXPR_SET_ELEM:
+ key = expr_clone(expr->key);
+ expr_free(expr);
+ expr = key;
+ break;
+ case EXPR_VALUE:
+ break;
+ default:
+ BUG("invalid expresion type %s\n", expr_name(expr));
+ break;
+ }
+
+ list_add(&expr->list, &tmp);
+ }
+ list_splice_init(&tmp, &(*dev_expr)->expressions);
+
+ return true;
+}
+
static uint32_t str2hooknum(uint32_t family, const char *hook);
static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
@@ -3624,17 +3810,19 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
if (table == NULL)
return table_not_found(ctx);
- ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name);
- if (ft->hook.num == NF_INET_NUMHOOKS)
- return chain_error(ctx, ft, "invalid hook %s", ft->hook.name);
-
- if (!evaluate_priority(ctx, &ft->priority, NFPROTO_NETDEV, ft->hook.num))
- return __stmt_binary_error(ctx, &ft->priority.loc, NULL,
- "invalid priority expression %s.",
- expr_name(ft->priority.expr));
+ if (ft->hook.name) {
+ ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name);
+ if (ft->hook.num == NF_INET_NUMHOOKS)
+ return chain_error(ctx, ft, "invalid hook %s",
+ ft->hook.name);
+ if (!evaluate_priority(ctx, &ft->priority, NFPROTO_NETDEV, ft->hook.num))
+ return __stmt_binary_error(ctx, &ft->priority.loc, NULL,
+ "invalid priority expression %s.",
+ expr_name(ft->priority.expr));
+ }
- if (!ft->dev_expr)
- return chain_error(ctx, ft, "Unbound flowtable not allowed (must specify devices)");
+ if (ft->dev_expr && !evaluate_device_expr(ctx, &ft->dev_expr))
+ return -1;
return 0;
}
@@ -3798,25 +3986,6 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
return NF_INET_NUMHOOKS;
}
-static bool evaluate_policy(struct eval_ctx *ctx, struct expr **exprp)
-{
- struct expr *expr;
-
- ctx->ectx.dtype = &policy_type;
- ctx->ectx.len = NFT_NAME_MAXLEN * BITS_PER_BYTE;
- if (expr_evaluate(ctx, exprp) < 0)
- return false;
-
- expr = *exprp;
- if (expr->etype != EXPR_VALUE) {
- expr_error(ctx->msgs, expr, "%s is not a valid "
- "policy expression", expr_name(expr));
- return false;
- }
-
- return true;
-}
-
static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
{
struct table *table;
@@ -3833,7 +4002,7 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
chain_add_hash(chain, table);
}
return 0;
- } else {
+ } else if (!(chain->flags & CHAIN_F_BINDING)) {
if (chain_lookup(table, &chain->handle) == NULL)
chain_add_hash(chain_get(chain), table);
}
@@ -3852,7 +4021,9 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
"invalid priority expression %s in this context.",
expr_name(chain->priority.expr));
if (chain->policy) {
- if (!evaluate_policy(ctx, &chain->policy))
+ expr_set_context(&ctx->ectx, &policy_type,
+ NFT_NAME_MAXLEN * BITS_PER_BYTE);
+ if (!evaluate_expr_variable(ctx, &chain->policy))
return chain_error(ctx, chain, "invalid policy expression %s",
expr_name(chain->policy));
}
@@ -3861,6 +4032,9 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
if (!chain->dev_expr)
return __stmt_binary_error(ctx, &chain->loc, NULL,
"Missing `device' in this chain definition");
+
+ if (!evaluate_device_expr(ctx, &chain->dev_expr))
+ return -1;
} else if (chain->dev_expr) {
return __stmt_binary_error(ctx, &chain->dev_expr->location, NULL,
"This chain type cannot be bound to device");
@@ -3978,7 +4152,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- return setelem_evaluate(ctx, &cmd->expr);
+ return setelem_evaluate(ctx, cmd);
case CMD_OBJ_SET:
handle_merge(&cmd->set->handle, &cmd->handle);
return set_evaluate(ctx, cmd->set);
@@ -4006,15 +4180,30 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
}
}
+static void table_del_cache(struct eval_ctx *ctx, struct cmd *cmd)
+{
+ struct table *table;
+
+ table = table_lookup(&cmd->handle, &ctx->nft->cache);
+ if (!table)
+ return;
+
+ list_del(&table->list);
+ table_free(table);
+}
+
static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- return setelem_evaluate(ctx, &cmd->expr);
+ return setelem_evaluate(ctx, cmd);
case CMD_OBJ_SET:
case CMD_OBJ_RULE:
case CMD_OBJ_CHAIN:
+ return 0;
case CMD_OBJ_TABLE:
+ table_del_cache(ctx, cmd);
+ return 0;
case CMD_OBJ_FLOWTABLE:
case CMD_OBJ_COUNTER:
case CMD_OBJ_QUOTA:
@@ -4032,21 +4221,9 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
static int cmd_evaluate_get(struct eval_ctx *ctx, struct cmd *cmd)
{
- struct table *table;
- struct set *set;
-
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
- return table_not_found(ctx);
-
- set = set_lookup(table, cmd->handle.set.name);
- if (set == NULL || set_is_map(set->flags))
- return set_not_found(ctx, &ctx->cmd->handle.set.location,
- ctx->cmd->handle.set.name);
-
- return setelem_evaluate(ctx, &cmd->expr);
+ return setelem_evaluate(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -4224,6 +4401,14 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
}
}
+static void __flush_set_cache(struct set *set)
+{
+ if (set->init != NULL) {
+ expr_free(set->init);
+ set->init = NULL;
+ }
+}
+
static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
{
struct table *table;
@@ -4251,6 +4436,9 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
else if (!set_is_literal(set->flags))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+
+ __flush_set_cache(set);
+
return 0;
case CMD_OBJ_MAP:
table = table_lookup(&cmd->handle, &ctx->nft->cache);
@@ -4265,6 +4453,8 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+ __flush_set_cache(set);
+
return 0;
case CMD_OBJ_METER:
table = table_lookup(&cmd->handle, &ctx->nft->cache);
@@ -4279,6 +4469,8 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+ __flush_set_cache(set);
+
return 0;
default:
BUG("invalid command object type %u\n", cmd->obj);
diff --git a/src/expression.c b/src/expression.c
index a6bde70f..fe529f98 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -175,6 +175,15 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
}
}
+void expr_to_string(const struct expr *expr, char *string)
+{
+ int len = expr->len / BITS_PER_BYTE;
+
+ assert(expr->dtype == &string_type);
+
+ mpz_export_data(string, expr->value, BYTEORDER_HOST_ENDIAN, len);
+}
+
void expr_set_type(struct expr *expr, const struct datatype *dtype,
enum byteorder byteorder)
{
diff --git a/src/json.c b/src/json.c
index ed713181..a9f5000f 100644
--- a/src/json.c
+++ b/src/json.c
@@ -98,6 +98,9 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
"table", set->handle.table.name,
"type", set_dtype_json(set->key),
"handle", set->handle.handle.id);
+
+ if (set->comment)
+ json_object_set_new(root, "comment", json_string(set->comment));
if (datatype_ext)
json_object_set_new(root, "map", json_string(datatype_ext));
@@ -1224,9 +1227,12 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root = json_object(), *flags;
- if (stmt->log.flags & STMT_LOG_PREFIX)
- json_object_set_new(root, "prefix",
- json_string(stmt->log.prefix));
+ if (stmt->log.flags & STMT_LOG_PREFIX) {
+ char prefix[NF_LOG_PREFIXLEN] = {};
+
+ expr_to_string(stmt->log.prefix, prefix);
+ json_object_set_new(root, "prefix", json_string(prefix));
+ }
if (stmt->log.flags & STMT_LOG_GROUP)
json_object_set_new(root, "group",
json_integer(stmt->log.group));
@@ -1565,7 +1571,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
{
unsigned int family = cmd->handle.family;
- json_t *root = json_array();
+ json_t *root = json_array(), *tmp;
struct table *table;
list_for_each_entry(table, &ctx->nft->cache.list, list) {
@@ -1573,7 +1579,9 @@ static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
table->handle.family != family)
continue;
- json_array_extend(root, table_print_json_full(ctx, table));
+ tmp = table_print_json_full(ctx, table);
+ json_array_extend(root, tmp);
+ json_decref(tmp);
}
return root;
diff --git a/src/main.c b/src/main.c
index d830c7a2..3c26f510 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,17 +24,37 @@
static struct nft_ctx *nft;
-/*
- * These options are grouped separately in the help, so we give them named
- * indices for use there.
- */
enum opt_indices {
+ /* General options */
IDX_HELP,
IDX_VERSION,
IDX_VERSION_LONG,
- IDX_CHECK,
+ /* Ruleset input handling */
IDX_FILE,
+#define IDX_RULESET_INPUT_START IDX_FILE
IDX_INTERACTIVE,
+ IDX_INCLUDEPATH,
+ IDX_CHECK,
+#define IDX_RULESET_INPUT_END IDX_CHECK
+ /* Ruleset list formatting */
+ IDX_HANDLE,
+#define IDX_RULESET_LIST_START IDX_HANDLE
+ IDX_STATELESS,
+ IDX_TERSE,
+ IDX_SERVICE,
+ IDX_REVERSEDNS,
+ IDX_GUID,
+ IDX_NUMERIC,
+ IDX_NUMERIC_PRIO,
+ IDX_NUMERIC_PROTO,
+ IDX_NUMERIC_TIME,
+#define IDX_RULESET_LIST_END IDX_NUMERIC_TIME
+ /* Command output formatting */
+ IDX_ECHO,
+#define IDX_CMD_OUTPUT_START IDX_ECHO
+ IDX_JSON,
+ IDX_DEBUG,
+#define IDX_CMD_OUTPUT_END IDX_DEBUG
};
enum opt_vals {
@@ -72,46 +92,46 @@ struct nft_opt {
(struct nft_opt) { .name = n, .val = v, .arg = a, .help = h }
static const struct nft_opt nft_options[] = {
- NFT_OPT("help", OPT_HELP, NULL,
- "Show this help"),
- NFT_OPT("version", OPT_VERSION, NULL,
- "Show version information"),
- NFT_OPT(NULL, OPT_VERSION_LONG, NULL,
- "Show extended version information"),
- NFT_OPT("check", OPT_CHECK, NULL,
- "Check commands validity without actually applying the changes."),
- NFT_OPT("file", OPT_FILE, "<filename>",
- "Read input from <filename>"),
- NFT_OPT("interactive", OPT_INTERACTIVE, NULL,
- "Read input from interactive CLI"),
- NFT_OPT("numeric", OPT_NUMERIC, NULL,
- "Print fully numerical output."),
- NFT_OPT("stateless", OPT_STATELESS, NULL,
- "Omit stateful information of ruleset."),
- NFT_OPT("reversedns", OPT_IP2NAME, NULL,
- "Translate IP addresses to names."),
- NFT_OPT("service", OPT_SERVICE, NULL,
- "Translate ports to service names as described in /etc/services."),
- NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>",
- "Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH),
- NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>",
- "Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
- NFT_OPT("handle", OPT_HANDLE_OUTPUT, NULL,
- "Output rule handle."),
- NFT_OPT("echo", OPT_ECHO, NULL,
- "Echo what has been added, inserted or replaced."),
- NFT_OPT("json", OPT_JSON, NULL,
- "Format output in JSON"),
- NFT_OPT("guid", OPT_GUID, NULL,
- "Print UID/GID as defined in /etc/passwd and /etc/group."),
- NFT_OPT("numeric-priority", OPT_NUMERIC_PRIO, NULL,
- "Print chain priority numerically."),
- NFT_OPT("numeric-protocol", OPT_NUMERIC_PROTO, NULL,
- "Print layer 4 protocols numerically."),
- NFT_OPT("numeric-time", OPT_NUMERIC_TIME, NULL,
- "Print time values numerically."),
- NFT_OPT("terse", OPT_TERSE, NULL,
- "Omit contents of sets."),
+ [IDX_HELP] = NFT_OPT("help", OPT_HELP, NULL,
+ "Show this help"),
+ [IDX_VERSION] = NFT_OPT("version", OPT_VERSION, NULL,
+ "Show version information"),
+ [IDX_VERSION_LONG] = NFT_OPT(NULL, OPT_VERSION_LONG, NULL,
+ "Show extended version information"),
+ [IDX_FILE] = NFT_OPT("file", OPT_FILE, "<filename>",
+ "Read input from <filename>"),
+ [IDX_INTERACTIVE] = NFT_OPT("interactive", OPT_INTERACTIVE, NULL,
+ "Read input from interactive CLI"),
+ [IDX_INCLUDEPATH] = NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>",
+ "Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH),
+ [IDX_CHECK] = NFT_OPT("check", OPT_CHECK, NULL,
+ "Check commands validity without actually applying the changes."),
+ [IDX_HANDLE] = NFT_OPT("handle", OPT_HANDLE_OUTPUT, NULL,
+ "Output rule handle."),
+ [IDX_STATELESS] = NFT_OPT("stateless", OPT_STATELESS, NULL,
+ "Omit stateful information of ruleset."),
+ [IDX_TERSE] = NFT_OPT("terse", OPT_TERSE, NULL,
+ "Omit contents of sets."),
+ [IDX_SERVICE] = NFT_OPT("service", OPT_SERVICE, NULL,
+ "Translate ports to service names as described in /etc/services."),
+ [IDX_REVERSEDNS] = NFT_OPT("reversedns", OPT_IP2NAME, NULL,
+ "Translate IP addresses to names."),
+ [IDX_GUID] = NFT_OPT("guid", OPT_GUID, NULL,
+ "Print UID/GID as defined in /etc/passwd and /etc/group."),
+ [IDX_NUMERIC] = NFT_OPT("numeric", OPT_NUMERIC, NULL,
+ "Print fully numerical output."),
+ [IDX_NUMERIC_PRIO] = NFT_OPT("numeric-priority", OPT_NUMERIC_PRIO, NULL,
+ "Print chain priority numerically."),
+ [IDX_NUMERIC_PROTO] = NFT_OPT("numeric-protocol", OPT_NUMERIC_PROTO, NULL,
+ "Print layer 4 protocols numerically."),
+ [IDX_NUMERIC_TIME] = NFT_OPT("numeric-time", OPT_NUMERIC_TIME, NULL,
+ "Print time values numerically."),
+ [IDX_ECHO] = NFT_OPT("echo", OPT_ECHO, NULL,
+ "Echo what has been added, inserted or replaced."),
+ [IDX_JSON] = NFT_OPT("json", OPT_JSON, NULL,
+ "Format output in JSON"),
+ [IDX_DEBUG] = NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>",
+ "Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
};
#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0]))
@@ -169,23 +189,35 @@ static void print_option(const struct nft_opt *opt)
static void show_help(const char *name)
{
+ int i;
+
printf("Usage: %s [ options ] [ cmds... ]\n"
"\n"
- "Options:\n", name);
+ "Options (general):\n", name);
print_option(&nft_options[IDX_HELP]);
print_option(&nft_options[IDX_VERSION]);
print_option(&nft_options[IDX_VERSION_LONG]);
- fputs("\n", stdout);
+ printf("\n"
+ "Options (ruleset input handling):"
+ "\n");
- print_option(&nft_options[IDX_CHECK]);
- print_option(&nft_options[IDX_FILE]);
- print_option(&nft_options[IDX_INTERACTIVE]);
+ for (i = IDX_RULESET_INPUT_START; i <= IDX_RULESET_INPUT_END; i++)
+ print_option(&nft_options[i]);
- fputs("\n", stdout);
+ printf("\n"
+ "Options (ruleset list formatting):"
+ "\n");
+
+ for (i = IDX_RULESET_LIST_START; i <= IDX_RULESET_LIST_END; i++)
+ print_option(&nft_options[i]);
+
+ printf("\n"
+ "Options (command output formatting):"
+ "\n");
- for (size_t i = IDX_INTERACTIVE + 1; i < NR_NFT_OPTIONS; ++i)
+ for (i = IDX_CMD_OUTPUT_START; i <= IDX_CMD_OUTPUT_END; i++)
print_option(&nft_options[i]);
fputs("\n", stdout);
diff --git a/src/meta.c b/src/meta.c
index acc348eb..d92d0d32 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -73,7 +73,7 @@ static struct error_record *tchandle_type_parse(struct parse_ctx *ctx,
else if (strcmp(sym->identifier, "none") == 0)
handle = TC_H_UNSPEC;
else if (strchr(sym->identifier, ':')) {
- uint16_t tmp;
+ uint32_t tmp;
char *colon;
str = xstrdup(sym->identifier);
diff --git a/src/mnl.c b/src/mnl.c
index 94e80261..388eff8f 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -466,7 +466,11 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
- mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
+
+ if (h->chain_id)
+ mnl_attr_put_u32(nlh, NFTA_RULE_CHAIN_ID, htonl(h->chain_id));
+ else
+ mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
@@ -679,7 +683,18 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name);
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location);
- mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
+
+ if (!cmd->chain || !(cmd->chain->flags & CHAIN_F_BINDING)) {
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
+ } else {
+ if (cmd->handle.chain.name)
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME,
+ cmd->handle.chain.name);
+
+ mnl_attr_put_u32(nlh, NFTA_CHAIN_ID, htonl(cmd->handle.chain_id));
+ if (cmd->chain->flags)
+ nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS, cmd->chain->flags);
+ }
if (cmd->chain && cmd->chain->policy) {
mpz_export_data(&policy, cmd->chain->policy->value,
@@ -1027,6 +1042,11 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
sizeof(set->desc.field_len[0]));
}
+ if (set->comment) {
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_SET_COMMENT, set->comment))
+ memory_allocation_error();
+ }
+
nftnl_set_set_data(nls, NFTNL_SET_USERDATA, nftnl_udata_buf_data(udbuf),
nftnl_udata_buf_len(udbuf));
nftnl_udata_buf_free(udbuf);
@@ -1590,29 +1610,13 @@ err:
return NULL;
}
-int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
- unsigned int flags)
+static const char **nft_flowtable_dev_array(struct cmd *cmd)
{
- struct nftnl_flowtable *flo;
unsigned int ifname_len;
const char **dev_array;
char ifname[IFNAMSIZ];
- struct nlmsghdr *nlh;
int i = 0, len = 1;
struct expr *expr;
- int priority;
-
- flo = nftnl_flowtable_alloc();
- if (!flo)
- memory_allocation_error();
-
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
- cmd->handle.family);
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM,
- cmd->flowtable->hook.num);
- mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, priority);
list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list)
len++;
@@ -1628,14 +1632,52 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
}
dev_array[i] = NULL;
- nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
- dev_array, sizeof(char *) * len);
- i = 0;
+ return dev_array;
+}
+
+static void nft_flowtable_dev_array_free(const char **dev_array)
+{
+ int i = 0;
+
while (dev_array[i] != NULL)
xfree(dev_array[i++]);
free(dev_array);
+}
+
+int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ unsigned int flags)
+{
+ struct nftnl_flowtable *flo;
+ const char **dev_array;
+ struct nlmsghdr *nlh;
+ int priority;
+
+ flo = nftnl_flowtable_alloc();
+ if (!flo)
+ memory_allocation_error();
+
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
+ cmd->handle.family);
+
+ if (cmd->flowtable->hook.name) {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM,
+ cmd->flowtable->hook.num);
+ mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, priority);
+ } else {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, 0);
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, 0);
+ }
+
+ if (cmd->flowtable->dev_expr) {
+ dev_array = nft_flowtable_dev_array(cmd);
+ nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
+ dev_array, 0);
+ nft_flowtable_dev_array_free(dev_array);
+ }
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FLAGS,
cmd->flowtable->flags);
@@ -1662,6 +1704,7 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct nftnl_flowtable *flo;
+ const char **dev_array;
struct nlmsghdr *nlh;
flo = nftnl_flowtable_alloc();
@@ -1671,6 +1714,16 @@ int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
cmd->handle.family);
+ if (cmd->flowtable && cmd->flowtable->dev_expr) {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, 0);
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, 0);
+
+ dev_array = nft_flowtable_dev_array(cmd);
+ nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
+ dev_array, 0);
+ nft_flowtable_dev_array_free(dev_array);
+ }
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_DELFLOWTABLE, cmd->handle.family,
0, ctx->seqnum);
diff --git a/src/monitor.c b/src/monitor.c
index bb269c02..3872ebcf 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -214,6 +214,10 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
nft_mon_print(monh, "%s %s", family2str(t->handle.family),
t->handle.table.name);
+
+ if (t->flags & TABLE_F_DORMANT)
+ nft_mon_print(monh, " { flags dormant; }");
+
if (nft_output_handle(&monh->ctx->nft->output))
nft_mon_print(monh, " # handle %" PRIu64 "",
t->handle.handle.id);
diff --git a/src/netlink.c b/src/netlink.c
index fb0a17ba..20b3cdf5 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -123,7 +123,7 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
netlink_gen_data(key, &nld);
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
- if (set->set_flags & NFT_SET_INTERVAL && expr->key->field_count > 1) {
+ if (set->set_flags & NFT_SET_INTERVAL && key->field_count > 1) {
key->flags |= EXPR_F_INTERVAL_END;
netlink_gen_data(key, &nld);
key->flags &= ~EXPR_F_INTERVAL_END;
@@ -269,31 +269,41 @@ static void netlink_gen_constant_data(const struct expr *expr,
div_round_up(expr->len, BITS_PER_BYTE), data);
}
-static void netlink_gen_verdict(const struct expr *expr,
- struct nft_data_linearize *data)
+static void netlink_gen_chain(const struct expr *expr,
+ struct nft_data_linearize *data)
{
char chain[NFT_CHAIN_MAXNAMELEN];
unsigned int len;
- data->verdict = expr->verdict;
+ len = expr->chain->len / BITS_PER_BYTE;
- switch (expr->verdict) {
- case NFT_JUMP:
- case NFT_GOTO:
- len = expr->chain->len / BITS_PER_BYTE;
+ if (!len)
+ BUG("chain length is 0");
- if (!len)
- BUG("chain length is 0");
+ if (len > sizeof(chain))
+ BUG("chain is too large (%u, %u max)",
+ len, (unsigned int)sizeof(chain));
- if (len > sizeof(chain))
- BUG("chain is too large (%u, %u max)",
- len, (unsigned int)sizeof(chain));
+ memset(chain, 0, sizeof(chain));
- memset(chain, 0, sizeof(chain));
+ mpz_export_data(chain, expr->chain->value,
+ BYTEORDER_HOST_ENDIAN, len);
+ snprintf(data->chain, NFT_CHAIN_MAXNAMELEN, "%s", chain);
+}
+
+static void netlink_gen_verdict(const struct expr *expr,
+ struct nft_data_linearize *data)
+{
- mpz_export_data(chain, expr->chain->value,
- BYTEORDER_HOST_ENDIAN, len);
- snprintf(data->chain, NFT_CHAIN_MAXNAMELEN, "%s", chain);
+ data->verdict = expr->verdict;
+
+ switch (expr->verdict) {
+ case NFT_JUMP:
+ case NFT_GOTO:
+ if (expr->chain)
+ netlink_gen_chain(expr, data);
+ else
+ data->chain_id = expr->chain_id;
break;
}
}
@@ -546,7 +556,10 @@ static int list_chain_cb(struct nftnl_chain *nlc, void *arg)
return 0;
chain = netlink_delinearize_chain(ctx, nlc);
- list_add_tail(&chain->list, &ctx->list);
+ if (chain->flags & CHAIN_F_BINDING)
+ list_add_tail(&chain->list, &ctx->list_bindings);
+ else
+ list_add_tail(&chain->list, &ctx->list);
return 0;
}
@@ -648,6 +661,7 @@ void netlink_dump_set(const struct nftnl_set *nls, struct netlink_ctx *ctx)
static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data)
{
+ unsigned char *value = nftnl_udata_get(attr);
const struct nftnl_udata **tb = data;
uint8_t type = nftnl_udata_type(attr);
uint8_t len = nftnl_udata_len(attr);
@@ -665,6 +679,10 @@ static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data)
if (len < 3)
return -1;
break;
+ case NFTNL_UDATA_SET_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
default:
return 0;
}
@@ -738,11 +756,11 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
enum byteorder databyteorder = BYTEORDER_INVALID;
const struct datatype *keytype, *datatype = NULL;
struct expr *typeof_expr_key, *typeof_expr_data;
+ const char *udata, *comment = NULL;
uint32_t flags, key, objtype = 0;
const struct datatype *dtype;
uint32_t data_interval = 0;
bool automerge = false;
- const char *udata;
struct set *set;
uint32_t ulen;
uint32_t klen;
@@ -770,6 +788,8 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
typeof_expr_key = set_make_key(ud[NFTNL_UDATA_SET_KEY_TYPEOF]);
if (ud[NFTNL_UDATA_SET_DATA_TYPEOF])
typeof_expr_data = set_make_key(ud[NFTNL_UDATA_SET_DATA_TYPEOF]);
+ if (ud[NFTNL_UDATA_SET_COMMENT])
+ comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_SET_COMMENT]));
}
key = nftnl_set_get_u32(nls, NFTNL_SET_KEY_TYPE);
@@ -806,6 +826,8 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
set->handle.table.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_TABLE));
set->handle.set.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_NAME));
set->automerge = automerge;
+ if (comment)
+ set->comment = comment;
if (nftnl_set_is_set(nls, NFTNL_SET_EXPR)) {
const struct nftnl_expr *nle;
@@ -1215,7 +1237,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
}
int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
- const struct location *loc, struct table *table,
+ const struct location *loc, struct set *cache_set,
struct set *set, struct expr *init)
{
struct nftnl_set *nls, *nls_out = NULL;
@@ -1248,7 +1270,7 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1)
concat_range_aggregate(set->init);
else if (set->flags & NFT_SET_INTERVAL)
- err = get_set_decompose(table, set);
+ err = get_set_decompose(cache_set, set);
else
list_expr_sort(&ctx->set->init->expressions);
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 7f7ad262..9e3ed53d 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -163,6 +163,24 @@ err:
return NULL;
}
+static void netlink_parse_chain_verdict(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ struct expr *expr,
+ enum nft_verdicts verdict)
+{
+ char chain_name[NFT_CHAIN_MAXNAMELEN] = {};
+ struct chain *chain;
+
+ expr_chain_export(expr->chain, chain_name);
+ chain = chain_binding_lookup(ctx->table, chain_name);
+ if (chain) {
+ ctx->stmt = chain_stmt_alloc(loc, chain, verdict);
+ expr_free(expr);
+ } else {
+ ctx->stmt = verdict_stmt_alloc(loc, expr);
+ }
+}
+
static void netlink_parse_immediate(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -182,12 +200,23 @@ static void netlink_parse_immediate(struct netlink_parse_ctx *ctx,
}
dreg = netlink_parse_register(nle, NFTNL_EXPR_IMM_DREG);
-
expr = netlink_alloc_data(loc, &nld, dreg);
- if (dreg == NFT_REG_VERDICT)
- ctx->stmt = verdict_stmt_alloc(loc, expr);
- else
+
+ if (dreg == NFT_REG_VERDICT) {
+ switch (expr->verdict) {
+ case NFT_JUMP:
+ netlink_parse_chain_verdict(ctx, loc, expr, NFT_JUMP);
+ break;
+ case NFT_GOTO:
+ netlink_parse_chain_verdict(ctx, loc, expr, NFT_GOTO);
+ break;
+ default:
+ ctx->stmt = verdict_stmt_alloc(loc, expr);
+ break;
+ }
+ } else {
netlink_set_register(ctx, dreg, expr);
+ }
}
static void netlink_parse_xfrm(struct netlink_parse_ctx *ctx,
@@ -901,7 +930,11 @@ static void netlink_parse_log(struct netlink_parse_ctx *ctx,
stmt = log_stmt_alloc(loc);
prefix = nftnl_expr_get_str(nle, NFTNL_EXPR_LOG_PREFIX);
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_PREFIX)) {
- stmt->log.prefix = xstrdup(prefix);
+ stmt->log.prefix = constant_expr_alloc(&internal_location,
+ &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen(prefix) + 1) * BITS_PER_BYTE,
+ prefix);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_GROUP)) {
@@ -1682,13 +1715,19 @@ struct stmt *netlink_parse_set_expr(const struct set *set,
const struct nftnl_expr *nle)
{
struct netlink_parse_ctx ctx, *pctx = &ctx;
+ struct handle h = {};
- pctx->rule = rule_alloc(&netlink_location, &set->handle);
+ handle_merge(&h, &set->handle);
+ pctx->rule = rule_alloc(&netlink_location, &h);
pctx->table = table_lookup(&set->handle, cache);
assert(pctx->table != NULL);
if (netlink_parse_expr(nle, pctx) < 0)
return NULL;
+
+ init_list_head(&pctx->rule->stmts);
+ rule_free(pctx->rule);
+
return pctx->stmt;
}
@@ -2063,7 +2102,7 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *e
expr_free(binop);
} else if (binop->left->dtype->flags & DTYPE_F_PREFIX &&
- binop->op == OP_AND &&
+ binop->op == OP_AND && expr->right->etype == EXPR_VALUE &&
expr_mask_is_prefix(binop->right)) {
expr->left = expr_get(binop->left);
expr->right = prefix_expr_alloc(&expr->location,
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 08f7f89f..846df46b 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -10,6 +10,7 @@
*/
#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_log.h>
#include <string.h>
#include <rule.h>
@@ -712,10 +713,12 @@ static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
nftnl_expr_set(nle, NFTNL_EXPR_IMM_DATA, nld.value, nld.len);
break;
case EXPR_VERDICT:
- if ((expr->chain != NULL) &&
- !nftnl_expr_is_set(nle, NFTNL_EXPR_IMM_CHAIN)) {
+ if (expr->chain) {
nftnl_expr_set_str(nle, NFTNL_EXPR_IMM_CHAIN,
nld.chain);
+ } else if (expr->chain_id) {
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_CHAIN_ID,
+ nld.chain_id);
}
nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_VERDICT, nld.verdict);
break;
@@ -1006,8 +1009,10 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("log");
if (stmt->log.prefix != NULL) {
- nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX,
- stmt->log.prefix);
+ char prefix[NF_LOG_PREFIXLEN] = {};
+
+ expr_to_string(stmt->log.prefix, prefix);
+ nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, prefix);
}
if (stmt->log.flags & STMT_LOG_GROUP) {
nftnl_expr_set_u16(nle, NFTNL_EXPR_LOG_GROUP, stmt->log.group);
@@ -1442,6 +1447,12 @@ static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx,
nftnl_rule_add_expr(ctx->nlr, nle);
}
+static void netlink_gen_chain_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ return netlink_gen_expr(ctx, stmt->chain.expr, NFT_REG_VERDICT);
+}
+
static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
@@ -1495,6 +1506,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_objref_stmt(ctx, stmt);
case STMT_MAP:
return netlink_gen_map_stmt(ctx, stmt);
+ case STMT_CHAIN:
+ return netlink_gen_chain_stmt(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 8e937ca3..7e094ff6 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -594,8 +594,8 @@ int nft_lex(void *, void *, void *);
%type <table> table_block_alloc table_block
%destructor { close_scope(state); table_free($$); } table_block_alloc
-%type <chain> chain_block_alloc chain_block
-%destructor { close_scope(state); chain_free($$); } chain_block_alloc
+%type <chain> chain_block_alloc chain_block subchain_block
+%destructor { close_scope(state); chain_free($$); } chain_block_alloc subchain_block
%type <rule> rule rule_alloc
%destructor { rule_free($$); } rule
@@ -642,7 +642,9 @@ int nft_lex(void *, void *, void *);
%destructor { stmt_free($$); } tproxy_stmt
%type <stmt> synproxy_stmt synproxy_stmt_alloc
%destructor { stmt_free($$); } synproxy_stmt synproxy_stmt_alloc
-
+%type <stmt> chain_stmt
+%destructor { stmt_free($$); } chain_stmt
+%type <val> chain_stmt_type
%type <stmt> queue_stmt queue_stmt_alloc
%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc
@@ -860,6 +862,7 @@ common_block : INCLUDE QUOTED_STRING stmt_separator
if (symbol_lookup(scope, $2) != NULL) {
erec_queue(error(&@2, "redefinition of symbol '%s'", $2),
state->msgs);
+ expr_free($4);
xfree($2);
YYERROR;
}
@@ -1179,6 +1182,13 @@ delete_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
}
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
| COUNTER obj_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL);
@@ -1675,6 +1685,15 @@ chain_block : /* empty */ { $$ = $<chain>-1; }
}
;
+subchain_block : /* empty */ { $$ = $<chain>-1; }
+ | subchain_block stmt_separator
+ | subchain_block rule stmt_separator
+ {
+ list_add_tail(&$2->list, &$1->rules);
+ $$ = $1;
+ }
+ ;
+
typeof_expr : primary_expr
{
if (expr_ops($1)->build_udata == NULL) {
@@ -1749,6 +1768,11 @@ set_block : /* empty */ { $$ = $<set>-1; }
$$ = $1;
}
| set_block set_mechanism stmt_separator
+ | set_block comment_spec stmt_separator
+ {
+ $1->comment = $2;
+ $$ = $1;
+ }
;
set_block_expr : set_expr
@@ -1927,6 +1951,11 @@ flowtable_expr : '{' flowtable_list_expr '}'
$2->location = @$;
$$ = $2;
}
+ | variable_expr
+ {
+ $1->location = @$;
+ $$ = $1;
+ }
;
flowtable_list_expr : flowtable_expr_member
@@ -1949,6 +1978,11 @@ flowtable_expr_member : STRING
strlen($1) * BITS_PER_BYTE, $1);
xfree($1);
}
+ | variable_expr
+ {
+ datatype_set($1->sym->expr, &ifname_type);
+ $$ = $1;
+ }
;
data_type_atom_expr : type_identifier
@@ -2129,7 +2163,6 @@ extended_prio_spec : int_num
{
struct prio_spec spec = {0};
- datatype_set($1->sym->expr, &priority_type);
spec.expr = $1;
$$ = spec;
}
@@ -2188,6 +2221,12 @@ dev_spec : DEVICE string
compound_expr_add($$, expr);
}
+ | DEVICE variable_expr
+ {
+ datatype_set($2->sym->expr, &ifname_type);
+ $$ = compound_expr_alloc(&@$, EXPR_LIST);
+ compound_expr_add($$, $2);
+ }
| DEVICES '=' flowtable_expr
{
$$ = $3;
@@ -2520,6 +2559,20 @@ stmt : verdict_stmt
| set_stmt
| map_stmt
| synproxy_stmt
+ | chain_stmt
+ ;
+
+chain_stmt_type : JUMP { $$ = NFT_JUMP; }
+ | GOTO { $$ = NFT_GOTO; }
+ ;
+
+chain_stmt : chain_stmt_type chain_block_alloc '{' subchain_block '}'
+ {
+ $2->location = @2;
+ close_scope(state);
+ $4->location = @4;
+ $$ = chain_stmt_alloc(&@$, $4, $1);
+ }
;
verdict_stmt : verdict_expr
@@ -2629,7 +2682,127 @@ log_args : log_arg
log_arg : PREFIX string
{
- $<stmt>0->log.prefix = $2;
+ struct scope *scope = current_scope(state);
+ bool done = false, another_var = false;
+ char *start, *end, scratch = '\0';
+ struct expr *expr, *item;
+ struct symbol *sym;
+ enum {
+ PARSE_TEXT,
+ PARSE_VAR,
+ } prefix_state;
+
+ /* No variables in log prefix, skip. */
+ if (!strchr($2, '$')) {
+ expr = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen($2) + 1) * BITS_PER_BYTE, $2);
+ xfree($2);
+ $<stmt>0->log.prefix = expr;
+ $<stmt>0->log.flags |= STMT_LOG_PREFIX;
+ break;
+ }
+
+ /* Parse variables in log prefix string using a
+ * state machine parser with two states. This
+ * parser creates list of expressions composed
+ * of constant and variable expressions.
+ */
+ expr = compound_expr_alloc(&@$, EXPR_LIST);
+
+ start = (char *)$2;
+
+ if (*start != '$') {
+ prefix_state = PARSE_TEXT;
+ } else {
+ prefix_state = PARSE_VAR;
+ start++;
+ }
+ end = start;
+
+ /* Not nice, but works. */
+ while (!done) {
+ switch (prefix_state) {
+ case PARSE_TEXT:
+ while (*end != '\0' && *end != '$')
+ end++;
+
+ if (*end == '\0')
+ done = true;
+
+ *end = '\0';
+ item = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen(start) + 1) * BITS_PER_BYTE,
+ start);
+ compound_expr_add(expr, item);
+
+ if (done)
+ break;
+
+ start = end + 1;
+ end = start;
+
+ /* fall through */
+ case PARSE_VAR:
+ while (isalnum(*end) || *end == '_')
+ end++;
+
+ if (*end == '\0')
+ done = true;
+ else if (*end == '$')
+ another_var = true;
+ else
+ scratch = *end;
+
+ *end = '\0';
+
+ sym = symbol_get(scope, start);
+ if (!sym) {
+ sym = symbol_lookup_fuzzy(scope, start);
+ if (sym) {
+ erec_queue(error(&@2, "unknown identifier '%s'; "
+ "did you mean identifier ‘%s’?",
+ start, sym->identifier),
+ state->msgs);
+ } else {
+ erec_queue(error(&@2, "unknown identifier '%s'",
+ start),
+ state->msgs);
+ }
+ expr_free(expr);
+ xfree($2);
+ YYERROR;
+ }
+ item = variable_expr_alloc(&@$, scope, sym);
+ compound_expr_add(expr, item);
+
+ if (done)
+ break;
+
+ /* Restore original byte after
+ * symbol lookup.
+ */
+ if (scratch) {
+ *end = scratch;
+ scratch = '\0';
+ }
+
+ start = end;
+ if (another_var) {
+ another_var = false;
+ start++;
+ prefix_state = PARSE_VAR;
+ } else {
+ prefix_state = PARSE_TEXT;
+ }
+ end = start;
+ break;
+ }
+ }
+
+ xfree($2);
+ $<stmt>0->log.prefix = expr;
$<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
| GROUP NUM
@@ -3813,6 +3986,16 @@ set_rhs_expr : concat_rhs_expr
initializer_expr : rhs_expr
| list_rhs_expr
+ | '{' '}' { $$ = compound_expr_alloc(&@$, EXPR_SET); }
+ | DASH NUM
+ {
+ int32_t num = -$2;
+
+ $$ = constant_expr_alloc(&@$, &integer_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(num) * BITS_PER_BYTE,
+ &num);
+ }
;
counter_config : PACKETS NUM BYTES NUM
diff --git a/src/parser_json.c b/src/parser_json.c
index 9fdef691..59347168 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2159,7 +2159,9 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
stmt = log_stmt_alloc(int_loc);
if (!json_unpack(value, "{s:s}", "prefix", &tmpstr)) {
- stmt->log.prefix = xstrdup(tmpstr);
+ stmt->log.prefix = constant_expr_alloc(int_loc, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen(tmpstr) + 1) * BITS_PER_BYTE, tmpstr);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (!json_unpack(value, "{s:i}", "group", &tmp)) {
diff --git a/src/rule.c b/src/rule.c
index 1f56faeb..2b5685c2 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -177,7 +177,10 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
ret = netlink_list_chains(ctx, &table->handle);
if (ret < 0)
return -1;
+
list_splice_tail_init(&ctx->list, &table->chains);
+ list_splice_tail_init(&ctx->list_bindings,
+ &table->chain_bindings);
}
if (flags & NFT_CACHE_FLOWTABLE_BIT) {
ret = netlink_list_flowtables(ctx, &table->handle);
@@ -196,6 +199,9 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
ret = netlink_list_rules(ctx, &table->handle);
list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
chain = chain_lookup(table, &rule->handle);
+ if (!chain)
+ chain = chain_binding_lookup(table,
+ rule->handle.chain.name);
list_move_tail(&rule->list, &chain->rules);
}
if (ret < 0)
@@ -231,6 +237,11 @@ static bool cache_is_complete(struct nft_cache *cache, unsigned int flags)
return (cache->flags & flags) == flags;
}
+static bool cache_needs_refresh(struct nft_cache *cache)
+{
+ return cache->flags & NFT_CACHE_REFRESH;
+}
+
static bool cache_is_updated(struct nft_cache *cache, uint16_t genid)
{
return genid && genid == cache->genid;
@@ -245,6 +256,7 @@ int cache_update(struct nft_ctx *nft, unsigned int flags, struct list_head *msgs
{
struct netlink_ctx ctx = {
.list = LIST_HEAD_INIT(ctx.list),
+ .list_bindings = LIST_HEAD_INIT(ctx.list_bindings),
.nft = nft,
.msgs = msgs,
};
@@ -254,7 +266,8 @@ int cache_update(struct nft_ctx *nft, unsigned int flags, struct list_head *msgs
replay:
ctx.seqnum = cache->seqnum++;
genid = mnl_genid_get(&ctx);
- if (cache_is_complete(cache, flags) &&
+ if (!cache_needs_refresh(cache) &&
+ cache_is_complete(cache, flags) &&
cache_is_updated(cache, genid))
return 0;
@@ -354,6 +367,8 @@ void set_free(struct set *set)
return;
if (set->init != NULL)
expr_free(set->init);
+ if (set->comment)
+ xfree(set->comment);
handle_free(&set->handle);
stmt_free(set->stmt);
expr_free(set->key);
@@ -571,6 +586,13 @@ static void set_print_declaration(const struct set *set,
time_print(set->gc_int, octx);
nft_print(octx, "%s", opts->stmt_separator);
}
+
+ if (set->comment) {
+ nft_print(octx, "%s%scomment \"%s\"%s",
+ opts->tab, opts->tab,
+ set->comment,
+ opts->stmt_separator);
+ }
}
static void do_set_print(const struct set *set, struct print_fmt_options *opts,
@@ -858,12 +880,16 @@ const char *chain_hookname_lookup(const char *name)
return NULL;
}
+/* internal ID to uniquely identify a set in the batch */
+static uint32_t chain_id;
+
struct chain *chain_alloc(const char *name)
{
struct chain *chain;
chain = xzalloc(sizeof(*chain));
chain->refcnt = 1;
+ chain->handle.chain_id = ++chain_id;
init_list_head(&chain->rules);
init_list_head(&chain->scope.symbols);
if (name != NULL)
@@ -916,6 +942,18 @@ struct chain *chain_lookup(const struct table *table, const struct handle *h)
return NULL;
}
+struct chain *chain_binding_lookup(const struct table *table,
+ const char *chain_name)
+{
+ struct chain *chain;
+
+ list_for_each_entry(chain, &table->chain_bindings, list) {
+ if (!strcmp(chain->handle.chain.name, chain_name))
+ return chain;
+ }
+ return NULL;
+}
+
struct chain *chain_lookup_fuzzy(const struct handle *h,
const struct nft_cache *cache,
const struct table **t)
@@ -1175,6 +1213,9 @@ static void chain_print_declaration(const struct chain *chain,
char priobuf[STD_PRIO_BUFSIZE];
int policy, i;
+ if (chain->flags & CHAIN_F_BINDING)
+ return;
+
nft_print(octx, "\tchain %s {", chain->handle.chain.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id);
@@ -1210,17 +1251,22 @@ static void chain_print_declaration(const struct chain *chain,
}
}
-static void chain_print(const struct chain *chain, struct output_ctx *octx)
+void chain_rules_print(const struct chain *chain, struct output_ctx *octx,
+ const char *indent)
{
struct rule *rule;
- chain_print_declaration(chain, octx);
-
list_for_each_entry(rule, &chain->rules, list) {
- nft_print(octx, "\t\t");
+ nft_print(octx, "\t\t%s", indent ? : "");
rule_print(rule, octx);
nft_print(octx, "\n");
}
+}
+
+static void chain_print(const struct chain *chain, struct output_ctx *octx)
+{
+ chain_print_declaration(chain, octx);
+ chain_rules_print(chain, octx, NULL);
nft_print(octx, "\t}\n");
}
@@ -1255,6 +1301,7 @@ struct table *table_alloc(void)
init_list_head(&table->sets);
init_list_head(&table->objs);
init_list_head(&table->flowtables);
+ init_list_head(&table->chain_bindings);
init_list_head(&table->scope.symbols);
table->refcnt = 1;
@@ -1272,6 +1319,8 @@ void table_free(struct table *table)
return;
list_for_each_entry_safe(chain, next, &table->chains, list)
chain_free(chain);
+ list_for_each_entry_safe(chain, next, &table->chain_bindings, list)
+ chain_free(chain);
list_for_each_entry_safe(set, nset, &table->sets, list)
set_free(set);
list_for_each_entry_safe(ft, nft, &table->flowtables, list)
@@ -1437,6 +1486,7 @@ void nft_cmd_expand(struct cmd *cmd)
list_for_each_entry(chain, &table->chains, list) {
memset(&h, 0, sizeof(h));
handle_merge(&h, &chain->handle);
+ h.chain_id = chain->handle.chain_id;
new = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h,
&chain->location, chain_get(chain));
list_add_tail(&new->list, &new_cmds);
@@ -1469,6 +1519,12 @@ void nft_cmd_expand(struct cmd *cmd)
list_for_each_entry(rule, &chain->rules, list) {
memset(&h, 0, sizeof(h));
handle_merge(&h, &rule->handle);
+ if (chain->flags & CHAIN_F_BINDING) {
+ rule->handle.chain_id =
+ chain->handle.chain_id;
+ rule->handle.chain.location =
+ chain->location;
+ }
new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h,
&rule->location,
rule_get(rule));
@@ -1478,6 +1534,7 @@ void nft_cmd_expand(struct cmd *cmd)
list_splice(&new_cmds, &cmd->list);
break;
case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
set = cmd->set;
memset(&h, 0, sizeof(h));
handle_merge(&h, &set->handle);
@@ -1535,6 +1592,8 @@ void cmd_free(struct cmd *cmd)
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
expr_free(cmd->expr);
+ if (cmd->elem.set)
+ set_free(cmd->elem.set);
break;
case CMD_OBJ_SET:
case CMD_OBJ_SETELEMS:
@@ -1605,13 +1664,8 @@ static int __do_add_setelems(struct netlink_ctx *ctx, struct set *set,
static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
uint32_t flags)
{
- struct handle *h = &cmd->handle;
struct expr *init = cmd->expr;
- struct table *table;
- struct set *set;
-
- table = table_lookup(h, &ctx->nft->cache);
- set = set_lookup(table, h->set.name);
+ struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
set_to_intervals(ctx->msgs, set, init, true,
@@ -1708,13 +1762,8 @@ static int do_command_insert(struct netlink_ctx *ctx, struct cmd *cmd)
static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct handle *h = &cmd->handle;
- struct expr *expr = cmd->expr;
- struct table *table;
- struct set *set;
-
- table = table_lookup(h, &ctx->nft->cache);
- set = set_lookup(table, h->set.name);
+ struct expr *expr = cmd->elem.expr;
+ struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
set_to_intervals(ctx->msgs, set, expr, false,
@@ -2272,13 +2321,15 @@ static void flowtable_print_declaration(const struct flowtable *flowtable,
flowtable->hook.num, flowtable->priority.expr),
opts->stmt_separator);
- nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
- for (i = 0; i < flowtable->dev_array_len; i++) {
- nft_print(octx, "%s", flowtable->dev_array[i]);
- if (i + 1 != flowtable->dev_array_len)
- nft_print(octx, ", ");
+ if (flowtable->dev_array_len > 0) {
+ nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
+ for (i = 0; i < flowtable->dev_array_len; i++) {
+ nft_print(octx, "%s", flowtable->dev_array[i]);
+ if (i + 1 != flowtable->dev_array_len)
+ nft_print(octx, ", ");
+ }
+ nft_print(octx, " }%s", opts->stmt_separator);
}
- nft_print(octx, " }%s", opts->stmt_separator);
if (flowtable->flags & NFT_FLOWTABLE_COUNTER)
nft_print(octx, "%s%scounter%s", opts->tab, opts->tab,
@@ -2477,9 +2528,15 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd)
}
static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
- struct table *table, struct set *set)
+ struct set *set)
{
+ struct table *table = table_alloc();
+
+ table->handle.table.name = xstrdup(cmd->handle.table.name);
+ table->handle.family = cmd->handle.family;
table_print_declaration(table, &ctx->nft->output);
+ table_free(table);
+
set_print(set, &ctx->nft->output);
nft_print(&ctx->nft->output, "}\n");
}
@@ -2493,7 +2550,7 @@ static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
if (set == NULL)
return -1;
- __do_list_set(ctx, cmd, table, set);
+ __do_list_set(ctx, cmd, set);
return 0;
}
@@ -2564,14 +2621,13 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
return 0;
}
-static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
- struct table *table)
+static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct set *set, *new_set;
struct expr *init;
int err;
- set = set_lookup(table, cmd->handle.set.name);
+ set = cmd->elem.set;
/* Create a list of elements based of what we got from command line. */
if (set_is_non_concat_range(set))
@@ -2583,9 +2639,9 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
/* Fetch from kernel the elements that have been requested .*/
err = netlink_get_setelem(ctx, &cmd->handle, &cmd->location,
- table, new_set, init);
+ cmd->elem.set, new_set, init);
if (err >= 0)
- __do_list_set(ctx, cmd, table, new_set);
+ __do_list_set(ctx, cmd, new_set);
if (set_is_non_concat_range(set))
expr_free(init);
@@ -2597,14 +2653,9 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct table *table = NULL;
-
- if (cmd->handle.table.name != NULL)
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
-
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- return do_get_setelems(ctx, cmd, table);
+ return do_get_setelems(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -2646,7 +2697,7 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd)
if (ret < 0)
return ret;
- return do_list_obj(ctx, cmd, type);
+ return do_command_list(ctx, cmd);
}
static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd)
diff --git a/src/segtree.c b/src/segtree.c
index 266a2b4d..3a641bc5 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -383,8 +383,8 @@ static bool interval_overlap(const struct elementary_interval *e1,
static int set_overlap(struct list_head *msgs, const struct set *set,
struct expr *init, unsigned int keylen, bool add)
{
- struct elementary_interval *new_intervals[init->size];
- struct elementary_interval *intervals[set->init->size];
+ struct elementary_interval *new_intervals[init->size + 1];
+ struct elementary_interval *intervals[set->init->size + 1];
unsigned int n, m, i, j;
int ret = 0;
@@ -687,17 +687,15 @@ struct expr *get_set_intervals(const struct set *set, const struct expr *init)
return new_init;
}
-static struct expr *get_set_interval_find(const struct table *table,
- const char *set_name,
+static struct expr *get_set_interval_find(const struct set *cache_set,
struct expr *left,
struct expr *right)
{
+ const struct set *set = cache_set;
struct expr *range = NULL;
- struct set *set;
struct expr *i;
mpz_t val;
- set = set_lookup(table, set_name);
mpz_init2(val, set->key->len);
list_for_each_entry(i, &set->init->expressions, list) {
@@ -724,7 +722,7 @@ out:
return range;
}
-int get_set_decompose(struct table *table, struct set *set)
+int get_set_decompose(struct set *cache_set, struct set *set)
{
struct expr *i, *next, *range;
struct expr *left = NULL;
@@ -737,8 +735,7 @@ int get_set_decompose(struct table *table, struct set *set)
list_del(&left->list);
list_del(&i->list);
mpz_sub_ui(i->key->value, i->key->value, 1);
- range = get_set_interval_find(table, set->handle.set.name,
- left, i);
+ range = get_set_interval_find(cache_set, left, i);
if (!range) {
expr_free(new_init);
errno = ENOENT;
@@ -751,8 +748,7 @@ int get_set_decompose(struct table *table, struct set *set)
left = NULL;
} else {
if (left) {
- range = get_set_interval_find(table,
- set->handle.set.name,
+ range = get_set_interval_find(cache_set,
left, NULL);
if (range)
compound_expr_add(new_init, range);
@@ -764,8 +760,7 @@ int get_set_decompose(struct table *table, struct set *set)
}
}
if (left) {
- range = get_set_interval_find(table, set->handle.set.name,
- left, NULL);
+ range = get_set_interval_find(cache_set, left, NULL);
if (range)
compound_expr_add(new_init, range);
else
@@ -1027,6 +1022,10 @@ void interval_map_decompose(struct expr *set)
tmp->timeout = low->left->timeout;
if (low->left->expiration)
tmp->expiration = low->left->expiration;
+ if (low->left->stmt) {
+ tmp->stmt = low->left->stmt;
+ low->left->stmt = NULL;
+ }
tmp = mapping_expr_alloc(&tmp->location, tmp,
expr_clone(low->right));
@@ -1037,6 +1036,10 @@ void interval_map_decompose(struct expr *set)
tmp->timeout = low->timeout;
if (low->expiration)
tmp->expiration = low->expiration;
+ if (low->stmt) {
+ tmp->stmt = low->stmt;
+ low->stmt = NULL;
+ }
}
compound_expr_add(set, tmp);
@@ -1059,6 +1062,10 @@ void interval_map_decompose(struct expr *set)
prefix->timeout = low->left->timeout;
if (low->left->expiration)
prefix->expiration = low->left->expiration;
+ if (low->left->stmt) {
+ prefix->stmt = low->left->stmt;
+ low->left->stmt = NULL;
+ }
prefix = mapping_expr_alloc(&low->location, prefix,
expr_clone(low->right));
@@ -1069,6 +1076,10 @@ void interval_map_decompose(struct expr *set)
prefix->timeout = low->timeout;
if (low->expiration)
prefix->expiration = low->expiration;
+ if (low->stmt) {
+ prefix->stmt = low->stmt;
+ low->stmt = NULL;
+ }
}
compound_expr_add(set, prefix);
@@ -1086,16 +1097,20 @@ void interval_map_decompose(struct expr *set)
i = constant_expr_alloc(&low->location, low->dtype,
low->byteorder, expr_value(low)->len, NULL);
- mpz_init_bitmask(i->value, i->len);
+ mpz_bitmask(i->value, i->len);
if (!mpz_cmp(i->value, expr_value(low)->value)) {
expr_free(i);
i = low;
} else {
- i = range_expr_alloc(&low->location, expr_value(low), i);
+ i = range_expr_alloc(&low->location,
+ expr_clone(expr_value(low)), i);
i = set_elem_expr_alloc(&low->location, i);
if (low->etype == EXPR_MAPPING)
- i = mapping_expr_alloc(&i->location, i, low->right);
+ i = mapping_expr_alloc(&i->location, i,
+ expr_clone(low->right));
+
+ expr_free(low);
}
compound_expr_add(set, i);
diff --git a/src/statement.c b/src/statement.c
index 21a1bc8d..6fe8e9d9 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -15,9 +15,11 @@
#include <inttypes.h>
#include <string.h>
#include <syslog.h>
+#include <rule.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
+#include <linux/netfilter/nf_log.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <statement.h>
@@ -111,6 +113,50 @@ struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr)
return stmt;
}
+static const char *chain_verdict(const struct expr *expr)
+{
+ switch (expr->verdict) {
+ case NFT_JUMP:
+ return "jump";
+ case NFT_GOTO:
+ return "goto";
+ default:
+ BUG("unknown chain verdict");
+ }
+}
+
+static void chain_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ nft_print(octx, "%s {\n", chain_verdict(stmt->chain.expr));
+ chain_rules_print(stmt->chain.chain, octx, "\t");
+ nft_print(octx, "\t\t}");
+}
+
+static void chain_stmt_destroy(struct stmt *stmt)
+{
+ expr_free(stmt->chain.expr);
+}
+
+static const struct stmt_ops chain_stmt_ops = {
+ .type = STMT_CHAIN,
+ .name = "chain",
+ .print = chain_stmt_print,
+ .destroy = chain_stmt_destroy,
+};
+
+struct stmt *chain_stmt_alloc(const struct location *loc, struct chain *chain,
+ enum nft_verdicts verdict)
+{
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &chain_stmt_ops);
+ stmt->chain.chain = chain;
+ stmt->chain.expr = verdict_expr_alloc(loc, verdict, NULL);
+ stmt->chain.expr->chain_id = chain->handle.chain_id;
+
+ return stmt;
+}
+
static void meter_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
unsigned int flags = octx->flags;
@@ -300,8 +346,12 @@ int log_level_parse(const char *level)
static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
nft_print(octx, "log");
- if (stmt->log.flags & STMT_LOG_PREFIX)
- nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
+ if (stmt->log.flags & STMT_LOG_PREFIX) {
+ char prefix[NF_LOG_PREFIXLEN] = {};
+
+ expr_to_string(stmt->log.prefix, prefix);
+ nft_print(octx, " prefix \"%s\"", prefix);
+ }
if (stmt->log.flags & STMT_LOG_GROUP)
nft_print(octx, " group %u", stmt->log.group);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -338,7 +388,7 @@ static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void log_stmt_destroy(struct stmt *stmt)
{
- xfree(stmt->log.prefix);
+ expr_free(stmt->log.prefix);
}
static const struct stmt_ops log_stmt_ops = {
diff --git a/tests/py/inet/sets.t b/tests/py/inet/sets.t
index e0b0ee86..1c6f3235 100644
--- a/tests/py/inet/sets.t
+++ b/tests/py/inet/sets.t
@@ -21,4 +21,4 @@ ip6 daddr @set1 drop;fail
?set3 10.0.0.0/8 . 192.168.1.3-192.168.1.9 . 1024-65535;ok
ip saddr . ip daddr . tcp dport @set3 accept;ok
--ip daddr . tcp dport { 10.0.0.0/8 . 10-23, 192.168.1.1-192.168.3.8 . 80-443 } accept;ok
+ip daddr . tcp dport { 10.0.0.0/8 . 10-23, 192.168.1.1-192.168.3.8 . 80-443 } accept;ok
diff --git a/tests/py/inet/sets.t.payload.bridge b/tests/py/inet/sets.t.payload.bridge
index 089d9dd7..92f5417c 100644
--- a/tests/py/inet/sets.t.payload.bridge
+++ b/tests/py/inet/sets.t.payload.bridge
@@ -26,3 +26,17 @@ bridge
[ lookup reg 1 set set3 ]
[ immediate reg 0 accept ]
+# ip daddr . tcp dport { 10.0.0.0/8 . 10-23, 192.168.1.1-192.168.3.8 . 80-443 } accept
+__set%d test-inet 87
+__set%d test-inet 0
+ element 0000000a 00000a00 : 0 [end] element 0101a8c0 00005000 : 0 [end]
+bridge
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ payload load 2b @ transport header + 2 => reg 9 ]
+ [ lookup reg 1 set __set%d ]
+ [ immediate reg 0 accept ]
+
diff --git a/tests/py/inet/sets.t.payload.inet b/tests/py/inet/sets.t.payload.inet
index c5acd610..bd6e1b0f 100644
--- a/tests/py/inet/sets.t.payload.inet
+++ b/tests/py/inet/sets.t.payload.inet
@@ -26,3 +26,16 @@ inet
[ lookup reg 1 set set3 ]
[ immediate reg 0 accept ]
+# ip daddr . tcp dport { 10.0.0.0/8 . 10-23, 192.168.1.1-192.168.3.8 . 80-443 } accept
+__set%d test-inet 87
+__set%d test-inet 0
+ element 0000000a 00000a00 : 0 [end] element 0101a8c0 00005000 : 0 [end]
+inet
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ payload load 2b @ transport header + 2 => reg 9 ]
+ [ lookup reg 1 set __set%d ]
+ [ immediate reg 0 accept ]
diff --git a/tests/py/inet/sets.t.payload.netdev b/tests/py/inet/sets.t.payload.netdev
index 82994eab..f3032d8e 100644
--- a/tests/py/inet/sets.t.payload.netdev
+++ b/tests/py/inet/sets.t.payload.netdev
@@ -26,3 +26,16 @@ inet
[ lookup reg 1 set set3 ]
[ immediate reg 0 accept ]
+# ip daddr . tcp dport { 10.0.0.0/8 . 10-23, 192.168.1.1-192.168.3.8 . 80-443 } accept
+__set%d test-netdev 87
+__set%d test-netdev 0
+ element 0000000a 00000a00 : 0 [end] element 0101a8c0 00005000 : 0 [end]
+netdev
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ payload load 2b @ transport header + 2 => reg 9 ]
+ [ lookup reg 1 set __set%d ]
+ [ immediate reg 0 accept ]
diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py
index 01ee6c98..df97ed8e 100755
--- a/tests/py/nft-test.py
+++ b/tests/py/nft-test.py
@@ -1394,6 +1394,12 @@ def main():
# Change working directory to repository root
os.chdir(TESTS_PATH + "/../..")
+ try:
+ import unshare
+ unshare.unshare(unshare.CLONE_NEWNET)
+ except:
+ print_warning("cannot run in own namespace, connectivity might break")
+
check_lib_path = True
if args.library is None:
if args.host:
diff --git a/tests/shell/run-tests.sh b/tests/shell/run-tests.sh
index 26f8f46d..943f8877 100755
--- a/tests/shell/run-tests.sh
+++ b/tests/shell/run-tests.sh
@@ -22,11 +22,22 @@ if [ "$(id -u)" != "0" ] ; then
msg_error "this requires root!"
fi
+if [ "${1}" != "run" ]; then
+ if unshare -f -n true; then
+ unshare -n "${0}" run $@
+ exit $?
+ fi
+ msg_warn "cannot run in own namespace, connectivity might break"
+fi
+shift
+
[ -z "$NFT" ] && NFT=$SRC_NFT
-if [ ! -x "$NFT" ] ; then
- msg_error "no nft binary!"
+${NFT} > /dev/null 2>&1
+ret=$?
+if [ ${ret} -eq 126 ] || [ ${ret} -eq 127 ]; then
+ msg_error "cannot execute nft command: ${NFT}"
else
- msg_info "using nft binary $NFT"
+ msg_info "using nft command: ${NFT}"
fi
if [ ! -d "$TESTDIR" ] ; then
@@ -101,7 +112,7 @@ do
kernel_cleanup
msg_info "[EXECUTING] $testfile"
- test_output=$(NFT=$NFT DIFF=$DIFF ${testfile} 2>&1)
+ test_output=$(NFT="$NFT" DIFF=$DIFF ${testfile} 2>&1)
rc_got=$?
echo -en "\033[1A\033[K" # clean the [EXECUTING] foobar line
diff --git a/tests/shell/testcases/chains/0012reject_in_prerouting_1 b/tests/shell/testcases/chains/0012reject_in_prerouting_1
deleted file mode 100755
index 0ee86c11..00000000
--- a/tests/shell/testcases/chains/0012reject_in_prerouting_1
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-set -e
-
-$NFT add table t
-$NFT add chain t prerouting {type filter hook prerouting priority 0 \; }
-
-# wrong hook prerouting, only input/forward/output is valid
-$NFT add rule t prerouting reject 2>/dev/null || exit 0
-echo "E: accepted reject in prerouting hook" >&2
-exit 1
diff --git a/tests/shell/testcases/chains/0030create_0 b/tests/shell/testcases/chains/0030create_0
index 0b457f91..0b457f91 100644..100755
--- a/tests/shell/testcases/chains/0030create_0
+++ b/tests/shell/testcases/chains/0030create_0
diff --git a/tests/shell/testcases/chains/0032priority_variable_0 b/tests/shell/testcases/chains/0032priority_variable_0
index 51bc5eb1..8f2e57b9 100755
--- a/tests/shell/testcases/chains/0032priority_variable_0
+++ b/tests/shell/testcases/chains/0032priority_variable_0
@@ -6,12 +6,22 @@ set -e
RULESET="
define pri = 10
+define post = -10
+define for = \"filter - 100\"
table inet global {
chain prerouting {
type filter hook prerouting priority \$pri
policy accept
}
+ chain forward {
+ type filter hook prerouting priority \$for
+ policy accept
+ }
+ chain postrouting {
+ type filter hook postrouting priority \$post
+ policy accept
+ }
}"
$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/chains/0041chain_binding_0 b/tests/shell/testcases/chains/0041chain_binding_0
new file mode 100755
index 00000000..59bdbe9f
--- /dev/null
+++ b/tests/shell/testcases/chains/0041chain_binding_0
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="table inet x {
+ chain y {
+ type filter hook input priority 0;
+ meta l4proto { tcp, udp } th dport 53 jump {
+ ip saddr { 127.0.0.0/8, 172.23.0.0/16, 192.168.13.0/24 } counter accept
+ ip6 saddr ::1/128 counter accept
+ }
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+$NFT add rule inet x y meta l4proto icmpv6 jump { counter accept\; }
+$NFT add rule inet x y meta l4proto sctp jump { drop\; }
+$NFT delete rule inet x y handle 13
diff --git a/tests/shell/testcases/chains/0042chain_variable_0 b/tests/shell/testcases/chains/0042chain_variable_0
new file mode 100755
index 00000000..58535f76
--- /dev/null
+++ b/tests/shell/testcases/chains/0042chain_variable_0
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+set -e
+
+ip link add name dummy0 type dummy
+
+EXPECTED="define if_main = \"lo\"
+
+table netdev filter1 {
+ chain Main_Ingress1 {
+ type filter hook ingress device \$if_main priority -500; policy accept;
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+
+EXPECTED="define if_main = \"lo\"
+
+table netdev filter2 {
+ chain Main_Ingress2 {
+ type filter hook ingress devices = { \$if_main, dummy0 } priority -500; policy accept;
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+
+EXPECTED="define if_main = { lo, dummy0 }
+
+table netdev filter3 {
+ chain Main_Ingress3 {
+ type filter hook ingress devices = \$if_main priority -500; policy accept;
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+
+
diff --git a/tests/shell/testcases/chains/dumps/0032priority_variable_0.nft b/tests/shell/testcases/chains/dumps/0032priority_variable_0.nft
new file mode 100644
index 00000000..1a1b0794
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0032priority_variable_0.nft
@@ -0,0 +1,13 @@
+table inet global {
+ chain prerouting {
+ type filter hook prerouting priority filter + 10; policy accept;
+ }
+
+ chain forward {
+ type filter hook prerouting priority dstnat; policy accept;
+ }
+
+ chain postrouting {
+ type filter hook postrouting priority filter - 10; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0041chain_binding_0.nft b/tests/shell/testcases/chains/dumps/0041chain_binding_0.nft
new file mode 100644
index 00000000..520203d8
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0041chain_binding_0.nft
@@ -0,0 +1,12 @@
+table inet x {
+ chain y {
+ type filter hook input priority filter; policy accept;
+ meta l4proto { tcp, udp } th dport 53 jump {
+ ip saddr { 127.0.0.0/8, 172.23.0.0/16, 192.168.13.0/24 } counter packets 0 bytes 0 accept
+ ip6 saddr ::1 counter packets 0 bytes 0 accept
+ }
+ meta l4proto ipv6-icmp jump {
+ counter packets 0 bytes 0 accept
+ }
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
new file mode 100644
index 00000000..12931aad
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
@@ -0,0 +1,15 @@
+table netdev filter1 {
+ chain Main_Ingress1 {
+ type filter hook ingress device "lo" priority -500; policy accept;
+ }
+}
+table netdev filter2 {
+ chain Main_Ingress2 {
+ type filter hook ingress devices = { dummy0, lo } priority -500; policy accept;
+ }
+}
+table netdev filter3 {
+ chain Main_Ingress3 {
+ type filter hook ingress devices = { dummy0, lo } priority -500; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/flowtable/0006segfault_0 b/tests/shell/testcases/flowtable/0006segfault_0
index de590b77..fb7c52fe 100755
--- a/tests/shell/testcases/flowtable/0006segfault_0
+++ b/tests/shell/testcases/flowtable/0006segfault_0
@@ -9,6 +9,3 @@ $NFT add flowtable ip t f { hook ingress priority 10\; devices = { lo } }
$NFT add flowtable ip t f { hook ingress\; priority 10\; }
[[ $? -eq 1 ]] || exit 1
-
-$NFT add flowtable ip t f { hook ingress priority 10\; }
-[[ $? -eq 1 ]] || exit 1
diff --git a/tests/shell/testcases/flowtable/0012flowtable_variable_0 b/tests/shell/testcases/flowtable/0012flowtable_variable_0
new file mode 100755
index 00000000..8e334224
--- /dev/null
+++ b/tests/shell/testcases/flowtable/0012flowtable_variable_0
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+set -e
+
+ip link add name dummy1 type dummy
+
+EXPECTED="define if_main = { lo, dummy1 }
+
+table filter1 {
+ flowtable Main_ft1 {
+ hook ingress priority filter
+ counter
+ devices = \$if_main
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+
+EXPECTED="define if_main = \"lo\"
+
+table filter2 {
+ flowtable Main_ft2 {
+ hook ingress priority filter
+ counter
+ devices = { \$if_main, dummy1 }
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
diff --git a/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft
new file mode 100644
index 00000000..1cbb2f11
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft
@@ -0,0 +1,14 @@
+table ip filter1 {
+ flowtable Main_ft1 {
+ hook ingress priority filter
+ devices = { dummy1, lo }
+ counter
+ }
+}
+table ip filter2 {
+ flowtable Main_ft2 {
+ hook ingress priority filter
+ devices = { dummy1, lo }
+ counter
+ }
+}
diff --git a/tests/shell/testcases/maps/0009vmap_0 b/tests/shell/testcases/maps/0009vmap_0
new file mode 100755
index 00000000..7627c81d
--- /dev/null
+++ b/tests/shell/testcases/maps/0009vmap_0
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="table inet filter {
+ chain ssh_input {
+ }
+
+ chain wan_input {
+ tcp dport vmap { 22 : jump ssh_input }
+ }
+
+ chain prerouting {
+ type filter hook prerouting priority -300; policy accept;
+ iif vmap { "lo" : jump wan_input }
+ }
+}"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/maps/dumps/0009vmap_0.nft b/tests/shell/testcases/maps/dumps/0009vmap_0.nft
new file mode 100644
index 00000000..c556fece
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0009vmap_0.nft
@@ -0,0 +1,13 @@
+table inet filter {
+ chain ssh_input {
+ }
+
+ chain wan_input {
+ tcp dport vmap { 22 : jump ssh_input }
+ }
+
+ chain prerouting {
+ type filter hook prerouting priority raw; policy accept;
+ iif vmap { "lo" : jump wan_input }
+ }
+}
diff --git a/tests/shell/testcases/optionals/dumps/log_prefix_0.nft b/tests/shell/testcases/optionals/dumps/log_prefix_0.nft
new file mode 100644
index 00000000..8c11d697
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/log_prefix_0.nft
@@ -0,0 +1,5 @@
+table ip x {
+ chain y {
+ ct state invalid log prefix "invalid state match, logging:"
+ }
+}
diff --git a/tests/shell/testcases/optionals/log_prefix_0 b/tests/shell/testcases/optionals/log_prefix_0
new file mode 100755
index 00000000..513a9e74
--- /dev/null
+++ b/tests/shell/testcases/optionals/log_prefix_0
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -e
+
+TMP=$(mktemp)
+
+RULESET='define test = "state"
+define foo = "match, logging"
+
+table x {
+ chain y {
+ ct state invalid log prefix "invalid $test $foo:"
+ }
+}'
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/sets/0043concatenated_ranges_0 b/tests/shell/testcases/sets/0043concatenated_ranges_0
index a783dacc..11767373 100755
--- a/tests/shell/testcases/sets/0043concatenated_ranges_0
+++ b/tests/shell/testcases/sets/0043concatenated_ranges_0
@@ -6,11 +6,10 @@
# all possible permutations, and:
# - add entries to set
# - list them
-# - check that they can't be added again
# - get entries by specifying a value matching ranges for all fields
# - delete them
+# - check that they can't be deleted again
# - add them with 1s timeout
-# - check that they can't be added again right away
# - check that they are not listed after 1s, just once, for the first entry
# - delete them
# - make sure they can't be deleted again
@@ -63,27 +62,31 @@ cat <<'EOF' > "${tmp}"
flush ruleset
table inet filter {
- set test {
- type ${ta} . ${tb} . ${tc}
+ ${setmap} test {
+ type ${ta} . ${tb} . ${tc} ${mapt}
flags interval,timeout
- elements = { ${a1} . ${b1} . ${c1} ,
- ${a2} . ${b2} . ${c2} ,
- ${a3} . ${b3} . ${c3} }
+ elements = { ${a1} . ${b1} . ${c1} ${mapv},
+ ${a2} . ${b2} . ${c2} ${mapv},
+ ${a3} . ${b3} . ${c3} ${mapv}, }
}
chain output {
type filter hook output priority 0; policy accept;
- ${sa} . ${sb} . ${sc} @test counter
+ ${rule} @test counter
}
}
EOF
timeout_tested=0
+run_test()
+{
+setmap="$1"
for ta in ${TYPES}; do
eval a=\$ELEMS_${ta}
a1=${a%% *}; a2=$(expr "$a" : ".* \(.*\) .*"); a3=${a##* }
eval sa=\$RULESPEC_${ta}
+ mark=0
for tb in ${TYPES}; do
[ "${tb}" = "${ta}" ] && continue
if [ "${tb}" = "ipv6_addr" ]; then
@@ -107,41 +110,46 @@ for ta in ${TYPES}; do
[ "${tb}" = "ipv6_addr" ] && continue
fi
- echo "TYPE: ${ta} ${tb} ${tc}"
+ echo "$setmap TYPE: ${ta} ${tb} ${tc}"
eval c=\$ELEMS_${tc}
c1=${c%% *}; c2=$(expr "$c" : ".* \(.*\) .*"); c3=${c##* }
eval sc=\$RULESPEC_${tc}
- render ${tmp} | ${NFT} -f -
+ case "${setmap}" in
+ "set")
+ mapt=""
+ mapv=""
+ rule="${sa} . ${sb} . ${sc}"
+ ;;
+ "map")
+ mapt=": mark"
+ mark=42
+ mapv=$(printf " : 0x%08x" ${mark})
+ rule="meta mark set ${sa} . ${sb} . ${sc} map"
+ ;;
+ esac
- [ $(${NFT} list set inet filter test | \
- grep -c -e "${a1} . ${b1} . ${c1}" \
- -e "${a2} . ${b2} . ${c2}" \
- -e "${a3} . ${b3} . ${c3}") -eq 3 ]
+ render ${tmp} | ${NFT} -f -
- ! ${NFT} "add element inet filter test \
- { ${a1} . ${b1} . ${c1} };
- add element inet filter test \
- { ${a2} . ${b2} . ${c2} };
- add element inet filter test \
- { ${a3} . ${b3} . ${c3} }" 2>/dev/null
+ [ $(${NFT} list ${setmap} inet filter test | \
+ grep -c -e "${a1} . ${b1} . ${c1}${mapv}" \
+ -e "${a2} . ${b2} . ${c2}${mapv}" \
+ -e "${a3} . ${b3} . ${c3}${mapv}") -eq 3 ]
${NFT} delete element inet filter test \
- "{ ${a1} . ${b1} . ${c1} }"
- ! ${NFT} delete element inet filter test \
- "{ ${a1} . ${b1} . ${c1} }" 2>/dev/null
+ "{ ${a1} . ${b1} . ${c1}${mapv} }"
+ ${NFT} delete element inet filter test \
+ "{ ${a1} . ${b1} . ${c1}${mapv} }" \
+ 2>/dev/null && exit 1
eval add_a=\$ADD_${ta}
eval add_b=\$ADD_${tb}
eval add_c=\$ADD_${tc}
${NFT} add element inet filter test \
- "{ ${add_a} . ${add_b} . ${add_c} timeout 1s}"
- [ $(${NFT} list set inet filter test | \
+ "{ ${add_a} . ${add_b} . ${add_c} timeout 1s${mapv}}"
+ [ $(${NFT} list ${setmap} inet filter test | \
grep -c "${add_a} . ${add_b} . ${add_c}") -eq 1 ]
- ! ${NFT} add element inet filter test \
- "{ ${add_a} . ${add_b} . ${add_c} timeout 1s}" \
- 2>/dev/null
eval get_a=\$GET_${ta}
eval get_b=\$GET_${tb}
@@ -150,31 +158,36 @@ for ta in ${TYPES}; do
exp_b=${get_b##* }; get_b=${get_b%% *}
exp_c=${get_c##* }; get_c=${get_c%% *}
[ $(${NFT} get element inet filter test \
- "{ ${get_a} . ${get_b} . ${get_c} }" | \
+ "{ ${get_a} . ${get_b} . ${get_c}${mapv} }" | \
grep -c "${exp_a} . ${exp_b} . ${exp_c}") -eq 1 ]
${NFT} "delete element inet filter test \
- { ${a2} . ${b2} . ${c2} };
+ { ${a2} . ${b2} . ${c2}${mapv} };
delete element inet filter test \
- { ${a3} . ${b3} . ${c3} }"
- ! ${NFT} "delete element inet filter test \
- { ${a2} . ${b2} . ${c2} };
+ { ${a3} . ${b3} . ${c3}${mapv} }"
+ ${NFT} "delete element inet filter test \
+ { ${a2} . ${b2} . ${c2}${mapv} };
delete element inet filter test \
- { ${a3} . ${b3} . ${c3} }" 2>/dev/null
+ { ${a3} . ${b3} . ${c3} ${mapv} }" \
+ 2>/dev/null && exit 1
if [ ${timeout_tested} -eq 1 ]; then
${NFT} delete element inet filter test \
- "{ ${add_a} . ${add_b} . ${add_c} }"
- ! ${NFT} delete element inet filter test \
- "{ ${add_a} . ${add_b} . ${add_c} }" \
- 2>/dev/null
+ "{ ${add_a} . ${add_b} . ${add_c} ${mapv} }"
+ ${NFT} delete element inet filter test \
+ "{ ${add_a} . ${add_b} . ${add_c} ${mapv} }" \
+ 2>/dev/null && exit 1
continue
fi
sleep 1
- [ $(${NFT} list set inet filter test | \
- grep -c "${add_a} . ${add_b} . ${add_c}") -eq 0 ]
+ [ $(${NFT} list ${setmap} inet filter test | \
+ grep -c "${add_a} . ${add_b} . ${add_c} ${mapv}") -eq 0 ]
timeout_tested=1
done
done
done
+}
+
+run_test "set"
+run_test "map"
diff --git a/tests/shell/testcases/sets/0044interval_overlap_0 b/tests/shell/testcases/sets/0044interval_overlap_0
index fad92ddc..face90f2 100755
--- a/tests/shell/testcases/sets/0044interval_overlap_0
+++ b/tests/shell/testcases/sets/0044interval_overlap_0
@@ -7,6 +7,20 @@
# existing one
# - for concatenated ranges, the new element is less specific than any existing
# overlapping element, as elements are evaluated in order of insertion
+#
+# Then, repeat the test with a set configured with a timeout, checking that:
+# - we can insert all the elements as described above
+# - once the timeout has expired, we can insert all the elements again, and old
+# elements are not present
+# - before the timeout expires again, we can re-add elements that are not
+# expected to fail, but old elements might be present
+#
+# If $NFT points to a libtool wrapper, and we're running on a slow machine or
+# kernel (e.g. KASan enabled), it might not be possible to execute hundreds of
+# commands within an otherwise reasonable 1 second timeout. Estimate a usable
+# timeout first, by counting commands and measuring against one nft rule timeout
+# itself, so that we can keep this fast for a binary $NFT on a reasonably fast
+# kernel.
# Accept Interval List
intervals_simple="
@@ -32,35 +46,119 @@ intervals_concat="
y 0-2 . 0-3 0-2 . 0-3
n 0-1 . 0-2 0-2 . 0-3
y 10-20 . 30-40 0-2 . 0-3, 10-20 . 30-40
- n 15-20 . 50-60 0-2 . 0-3, 10-20 . 30-40, 15-20 . 50-60
+ y 15-20 . 50-60 0-2 . 0-3, 10-20 . 30-40, 15-20 . 50-60
y 3-9 . 4-29 0-2 . 0-3, 10-20 . 30-40, 15-20 . 50-60, 3-9 . 4-29
y 3-9 . 4-29 0-2 . 0-3, 10-20 . 30-40, 15-20 . 50-60, 3-9 . 4-29
n 11-19 . 30-40 0-2 . 0-3, 10-20 . 30-40, 15-20 . 50-60, 3-9 . 4-29
y 15-20 . 49-61 0-2 . 0-3, 10-20 . 30-40, 15-20 . 50-60, 3-9 . 4-29, 15-20 . 49-61
"
-$NFT add table t
-$NFT add set t s '{ type inet_service ; flags interval ; }'
-$NFT add set t c '{ type inet_service . inet_service ; flags interval ; }'
+count_elements() {
+ pass=
+ interval=
+ elements=0
+ for t in ${intervals_simple} ${intervals_concat}; do
+ [ -z "${pass}" ] && pass="${t}" && continue
+ [ -z "${interval}" ] && interval="${t}" && continue
+ unset IFS
+
+ elements=$((elements + 1))
-IFS='
+ IFS='
'
-set="s"
-for t in ${intervals_simple} switch ${intervals_concat}; do
- [ "${t}" = "switch" ] && set="c" && continue
- [ -z "${pass}" ] && pass="${t}" && continue
- [ -z "${interval}" ] && interval="${t}" && continue
-
- if [ "${pass}" = "y" ]; then
- $NFT add element t ${set} "{ ${interval} }"
- else
- ! $NFT add element t ${set} "{ ${interval} }" 2>/dev/null
+ done
+ unset IFS
+}
+
+match_elements() {
+ skip=0
+ n=0
+ out=
+ for a in $($NFT list set t ${1})}; do
+ [ ${n} -eq 0 ] && { [ "${a}" = "elements" ] && n=1; continue; }
+ [ ${n} -eq 1 ] && { [ "${a}" = "=" ] && n=2; continue; }
+ [ ${n} -eq 2 ] && { [ "${a}" = "{" ] && n=3; continue; }
+
+ [ "${a}" = "}" ] && break
+
+ [ ${skip} -eq 1 ] && skip=0 && out="${out}," && continue
+ [ "${a}" = "expires" ] && skip=1 && continue
+
+ [ -n "${out}" ] && out="${out} ${a}" || out="${a}"
+
+ done
+
+ if [ "${out%,}" != "${2}" ]; then
+ echo "Expected: ${2}, got: ${out%,}"
+ return 1
fi
- $NFT list set t ${set} | tr -d '\n\t' | tr -s ' ' | \
- grep -q "elements = { ${t} }"
+}
+
+estimate_timeout() {
+ count_elements
+ $NFT add table t
+ $NFT add set t s '{ type inet_service ; flags timeout; timeout 1s; gc-interval 1s; }'
+ execs_1s=1
+ $NFT add element t s "{ 0 }"
+ while match_elements s "0" >/dev/null; do
+ execs_1s=$((execs_1s + 1))
+ done
+
+ timeout="$((elements / execs_1s * 3 / 2 + 1))"
+}
+
+add_elements() {
+ set="s"
pass=
interval=
-done
+ IFS='
+'
+ for t in ${intervals_simple} switch ${intervals_concat}; do
+ [ "${t}" = "switch" ] && set="c" && continue
+ [ -z "${pass}" ] && pass="${t}" && continue
+ [ -z "${interval}" ] && interval="${t}" && continue
+ unset IFS
+
+ if [ "${pass}" = "y" ]; then
+ if ! $NFT add element t ${set} "{ ${interval} }"; then
+ echo "Failed to insert ${interval} given:"
+ $NFT list ruleset
+ exit 1
+ fi
+ else
+ if $NFT add element t ${set} "{ ${interval} }" 2>/dev/null; then
+ echo "Could insert ${interval} given:"
+ $NFT list ruleset
+ exit 1
+ fi
+ fi
+
+ [ "${1}" != "nomatch" ] && match_elements "${set}" "${t}"
+
+ pass=
+ interval=
+ IFS='
+'
+ done
+ unset IFS
+}
+
+$NFT add table t
+$NFT add set t s '{ type inet_service ; flags interval ; }'
+$NFT add set t c '{ type inet_service . inet_service ; flags interval ; }'
+add_elements
+
+$NFT flush ruleset
+estimate_timeout
+
+$NFT flush ruleset
+$NFT add table t
+$NFT add set t s "{ type inet_service ; flags interval,timeout; timeout ${timeout}s; gc-interval ${timeout}s; }"
+$NFT add set t c "{ type inet_service . inet_service ; flags interval,timeout ; timeout ${timeout}s; gc-interval ${timeout}s; }"
+add_elements
+
+sleep $((timeout * 3 / 2))
+add_elements
-unset IFS
+add_elements nomatch
diff --git a/tests/shell/testcases/sets/0048set_counters_0 b/tests/shell/testcases/sets/0048set_counters_0
new file mode 100755
index 00000000..e62d25df
--- /dev/null
+++ b/tests/shell/testcases/sets/0048set_counters_0
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="table ip x {
+ set y {
+ typeof ip saddr
+ counter
+ elements = { 192.168.10.35, 192.168.10.101, 192.168.10.135 }
+ }
+
+ chain z {
+ type filter hook output priority filter; policy accept;
+ ip daddr @y
+ }
+}"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/sets/0049set_define_0 b/tests/shell/testcases/sets/0049set_define_0
new file mode 100755
index 00000000..1d512f7b
--- /dev/null
+++ b/tests/shell/testcases/sets/0049set_define_0
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="define BASE_ALLOWED_INCOMING_TCP_PORTS = {22, 80, 443}
+define EXTRA_ALLOWED_INCOMING_TCP_PORTS = {}
+
+table inet filter {
+ chain input {
+ type filter hook input priority 0; policy drop;
+ tcp dport {\$BASE_ALLOWED_INCOMING_TCP_PORTS, \$EXTRA_ALLOWED_INCOMING_TCP_PORTS} ct state new counter accept
+ }
+}
+"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/sets/0050set_define_1 b/tests/shell/testcases/sets/0050set_define_1
new file mode 100755
index 00000000..c12de177
--- /dev/null
+++ b/tests/shell/testcases/sets/0050set_define_1
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="define BASE_ALLOWED_INCOMING_TCP_PORTS = {}
+
+table inet filter {
+ chain input {
+ type filter hook input priority 0; policy drop;
+ tcp dport {\$BASE_ALLOWED_INCOMING_TCP_PORTS} ct state new counter accept
+ }
+}
+"
+
+$NFT -f - <<< "$EXPECTED" &> /dev/null || exit 0
+echo "E: Accepted empty set" 1>&2
+exit 1
diff --git a/tests/shell/testcases/sets/0051set_interval_counter_0 b/tests/shell/testcases/sets/0051set_interval_counter_0
new file mode 100755
index 00000000..ea90e264
--- /dev/null
+++ b/tests/shell/testcases/sets/0051set_interval_counter_0
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="table ip x {
+ set s {
+ type ipv4_addr
+ flags interval
+ counter
+ elements = { 192.168.2.0/24 }
+ }
+
+ chain y {
+ type filter hook output priority filter; policy accept;
+ ip daddr @s
+ }
+}"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/sets/0052overlap_0 b/tests/shell/testcases/sets/0052overlap_0
new file mode 100755
index 00000000..c2960945
--- /dev/null
+++ b/tests/shell/testcases/sets/0052overlap_0
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="add table ip filter
+add set ip filter w_all {type ipv4_addr; flags interval; auto-merge}
+add element ip filter w_all {10.10.10.10, 10.10.10.11}
+"
+
+$NFT -f - <<< "$EXPECTED"
+
+EXPECTED="flush set ip filter w_all
+add element ip filter w_all {10.10.10.10, 10.10.10.253}
+"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/sets/0053echo_0 b/tests/shell/testcases/sets/0053echo_0
new file mode 100755
index 00000000..6bb03c28
--- /dev/null
+++ b/tests/shell/testcases/sets/0053echo_0
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="add table inet filter
+delete table inet filter
+
+table inet filter {
+ chain input {
+ type filter hook input priority filter; policy drop;
+ iifname { lo } ip saddr { 10.0.0.0/8 } ip daddr { 192.168.100.62 } tcp dport { 2001 } counter accept
+ }
+}
+"
+
+$NFT -ef - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/sets/0054comments_set_0 b/tests/shell/testcases/sets/0054comments_set_0
new file mode 100755
index 00000000..93a73f0d
--- /dev/null
+++ b/tests/shell/testcases/sets/0054comments_set_0
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# Test that comments are added to sets
+
+$NFT add table t
+$NFT add set t s {type ipv4_addr \; flags interval \; comment "test" \;}
+if ! $NFT list ruleset | grep test >/dev/null ; then
+ echo "E: missing comment in set" >&2
+ exit 1
+fi
+
diff --git a/tests/shell/testcases/sets/dumps/0048set_counters_0.nft b/tests/shell/testcases/sets/dumps/0048set_counters_0.nft
new file mode 100644
index 00000000..2145f6b1
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0048set_counters_0.nft
@@ -0,0 +1,13 @@
+table ip x {
+ set y {
+ typeof ip saddr
+ counter
+ elements = { 192.168.10.35 counter packets 0 bytes 0, 192.168.10.101 counter packets 0 bytes 0,
+ 192.168.10.135 counter packets 0 bytes 0 }
+ }
+
+ chain z {
+ type filter hook output priority filter; policy accept;
+ ip daddr @y
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0049set_define_0.nft b/tests/shell/testcases/sets/dumps/0049set_define_0.nft
new file mode 100644
index 00000000..998b387a
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0049set_define_0.nft
@@ -0,0 +1,6 @@
+table inet filter {
+ chain input {
+ type filter hook input priority filter; policy drop;
+ tcp dport { 22, 80, 443 } ct state new counter packets 0 bytes 0 accept
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0051set_interval_counter_0.nft b/tests/shell/testcases/sets/dumps/0051set_interval_counter_0.nft
new file mode 100644
index 00000000..fd488a76
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0051set_interval_counter_0.nft
@@ -0,0 +1,13 @@
+table ip x {
+ set s {
+ type ipv4_addr
+ flags interval
+ counter
+ elements = { 192.168.2.0/24 counter packets 0 bytes 0 }
+ }
+
+ chain y {
+ type filter hook output priority filter; policy accept;
+ ip daddr @s
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0052overlap_0.nft b/tests/shell/testcases/sets/dumps/0052overlap_0.nft
new file mode 100644
index 00000000..1cc02ada
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0052overlap_0.nft
@@ -0,0 +1,8 @@
+table ip filter {
+ set w_all {
+ type ipv4_addr
+ flags interval
+ auto-merge
+ elements = { 10.10.10.10, 10.10.10.253 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0053echo_0.nft b/tests/shell/testcases/sets/dumps/0053echo_0.nft
new file mode 100644
index 00000000..6a816636
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0053echo_0.nft
@@ -0,0 +1,6 @@
+table inet filter {
+ chain input {
+ type filter hook input priority filter; policy drop;
+ iifname { "lo" } ip saddr { 10.0.0.0/8 } ip daddr { 192.168.100.62 } tcp dport { 2001 } counter packets 0 bytes 0 accept
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0054comments_set_0.nft b/tests/shell/testcases/sets/dumps/0054comments_set_0.nft
new file mode 100644
index 00000000..2ad84003
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0054comments_set_0.nft
@@ -0,0 +1,7 @@
+table ip t {
+ set s {
+ type ipv4_addr
+ flags interval
+ comment "test"
+ }
+}
diff --git a/tests/shell/testcases/transactions/0002table_0 b/tests/shell/testcases/transactions/0002table_0
index 246b1092..c5f31a6f 100755
--- a/tests/shell/testcases/transactions/0002table_0
+++ b/tests/shell/testcases/transactions/0002table_0
@@ -5,6 +5,7 @@ set -e
RULESET="add table x
delete table x
add table x
+add chain x y { type nat hook prerouting priority 0; policy accept; }
add table x { flags dormant; }"
$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/transactions/dumps/0002table_0.nft b/tests/shell/testcases/transactions/dumps/0002table_0.nft
index 6eb70726..429cbc34 100644
--- a/tests/shell/testcases/transactions/dumps/0002table_0.nft
+++ b/tests/shell/testcases/transactions/dumps/0002table_0.nft
@@ -1,3 +1,7 @@
table ip x {
flags dormant
+
+ chain y {
+ type nat hook prerouting priority filter; policy accept;
+ }
}