summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am119
-rw-r--r--src/cache.c357
-rw-r--r--src/cli.c68
-rw-r--r--src/cmd.c251
-rw-r--r--src/ct.c31
-rw-r--r--src/datatype.c532
-rw-r--r--src/dccpopt.c277
-rw-r--r--src/erec.c14
-rw-r--r--src/evaluate.c2114
-rw-r--r--src/expression.c425
-rw-r--r--src/exthdr.c36
-rw-r--r--src/fib.c21
-rw-r--r--src/gmputil.c27
-rw-r--r--src/hash.c6
-rw-r--r--src/iface.c25
-rw-r--r--src/intervals.c353
-rw-r--r--src/ipopt.c9
-rw-r--r--src/json.c717
-rw-r--r--src/libnftables.c216
-rw-r--r--src/libnftables.map5
-rw-r--r--src/main.c89
-rw-r--r--src/mergesort.c42
-rw-r--r--src/meta.c176
-rw-r--r--src/mini-gmp.c4
-rw-r--r--src/misspell.c14
-rw-r--r--src/mnl.c861
-rw-r--r--src/monitor.c111
-rw-r--r--src/netlink.c482
-rw-r--r--src/netlink_delinearize.c894
-rw-r--r--src/netlink_linearize.c239
-rw-r--r--src/nfnl_osf.c4
-rw-r--r--src/nftutils.c100
-rw-r--r--src/nftutils.h20
-rw-r--r--src/numgen.c6
-rw-r--r--src/optimize.c560
-rw-r--r--src/osf.c11
-rw-r--r--src/owner.c13
-rw-r--r--src/parser_bison.y1447
-rw-r--r--src/parser_json.c1238
-rw-r--r--src/payload.c520
-rw-r--r--src/preprocess.c168
-rw-r--r--src/print.c7
-rw-r--r--src/proto.c161
-rw-r--r--src/rt.c15
-rw-r--r--src/rule.c596
-rw-r--r--src/scanner.l263
-rw-r--r--src/sctp_chunk.c3
-rw-r--r--src/segtree.c185
-rw-r--r--src/socket.c6
-rw-r--r--src/statement.c133
-rw-r--r--src/tcpopt.c81
-rw-r--r--src/utils.c14
-rw-r--r--src/xfrm.c9
-rw-r--r--src/xt.c246
54 files changed, 10042 insertions, 4279 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index 264d981e..00000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,119 +0,0 @@
-include $(top_srcdir)/Make_global.am
-
-sbin_PROGRAMS = nft
-
-AM_CPPFLAGS = -I$(top_srcdir)/include
-AM_CPPFLAGS += -DDEFAULT_INCLUDE_PATH="\"${sysconfdir}\"" \
- ${LIBMNL_CFLAGS} ${LIBNFTNL_CFLAGS}
-if BUILD_DEBUG
-AM_CPPFLAGS += -g -DDEBUG
-endif
-if BUILD_XTABLES
-AM_CPPFLAGS += ${XTABLES_CFLAGS}
-endif
-if BUILD_MINIGMP
-AM_CPPFLAGS += -DHAVE_MINIGMP
-endif
-if BUILD_JSON
-AM_CPPFLAGS += -DHAVE_JSON
-endif
-if BUILD_XTABLES
-AM_CPPFLAGS += -DHAVE_XTABLES
-endif
-
-AM_CFLAGS = -Wall \
- -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \
- -Wdeclaration-after-statement -Wsign-compare -Winit-self \
- -Wformat-nonliteral -Wformat-security -Wmissing-format-attribute \
- -Wcast-align -Wundef -Wbad-function-cast \
- -Waggregate-return -Wunused -Wwrite-strings ${GCC_FVISIBILITY_HIDDEN}
-
-
-AM_YFLAGS = -d -Wno-yacc
-
-BUILT_SOURCES = parser_bison.h
-
-lib_LTLIBRARIES = libnftables.la
-
-libnftables_la_SOURCES = \
- rule.c \
- statement.c \
- cache.c \
- cmd.c \
- datatype.c \
- expression.c \
- evaluate.c \
- proto.c \
- payload.c \
- exthdr.c \
- fib.c \
- hash.c \
- intervals.c \
- ipopt.c \
- meta.c \
- rt.c \
- numgen.c \
- ct.c \
- xfrm.c \
- netlink.c \
- netlink_linearize.c \
- netlink_delinearize.c \
- misspell.c \
- monitor.c \
- owner.c \
- segtree.c \
- gmputil.c \
- utils.c \
- erec.c \
- mnl.c \
- iface.c \
- mergesort.c \
- optimize.c \
- osf.c \
- nfnl_osf.c \
- tcpopt.c \
- socket.c \
- print.c \
- sctp_chunk.c \
- libnftables.c \
- libnftables.map
-
-# yacc and lex generate dirty code
-noinst_LTLIBRARIES = libparser.la
-libparser_la_SOURCES = parser_bison.y scanner.l
-libparser_la_CFLAGS = ${AM_CFLAGS} \
- -Wno-missing-prototypes \
- -Wno-missing-declarations \
- -Wno-implicit-function-declaration \
- -Wno-nested-externs \
- -Wno-undef \
- -Wno-redundant-decls
-
-libnftables_la_LIBADD = ${LIBMNL_LIBS} ${LIBNFTNL_LIBS} libparser.la
-libnftables_la_LDFLAGS = -version-info ${libnftables_LIBVERSION} \
- -Wl,--version-script=$(srcdir)/libnftables.map
-
-if BUILD_MINIGMP
-noinst_LTLIBRARIES += libminigmp.la
-libminigmp_la_SOURCES = mini-gmp.c
-libminigmp_la_CFLAGS = ${AM_CFLAGS} -Wno-sign-compare
-libnftables_la_LIBADD += libminigmp.la
-endif
-
-libnftables_la_SOURCES += xt.c
-if BUILD_XTABLES
-libnftables_la_LIBADD += ${XTABLES_LIBS}
-endif
-
-nft_SOURCES = main.c
-
-if BUILD_CLI
-nft_SOURCES += cli.c
-endif
-
-if BUILD_JSON
-libnftables_la_SOURCES += json.c parser_json.c
-libnftables_la_LIBADD += ${JANSSON_LIBS}
-endif
-
-nft_LDADD = libnftables.la
diff --git a/src/cache.c b/src/cache.c
index f790f995..d58fb59f 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -6,6 +6,8 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <expression.h>
#include <statement.h>
#include <rule.h>
@@ -28,10 +30,7 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
break;
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
- NFT_CACHE_FLOWTABLE;
+ NFT_CACHE_SET;
list_for_each_entry(set, &cmd->table->sets, list) {
if (set->automerge)
flags |= NFT_CACHE_SETELEM_MAYBE;
@@ -52,21 +51,15 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
break;
case CMD_OBJ_ELEMENTS:
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
NFT_CACHE_SETELEM_MAYBE;
break;
case CMD_OBJ_RULE:
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
- NFT_CACHE_FLOWTABLE;
+ NFT_CACHE_SET;
- if (cmd->handle.index.id ||
- cmd->handle.position.id)
- flags |= NFT_CACHE_RULE | NFT_CACHE_UPDATE;
+ if (cmd->handle.index.id)
+ flags |= NFT_CACHE_FULL | NFT_CACHE_UPDATE;
break;
default:
break;
@@ -79,9 +72,11 @@ static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- flags |= NFT_CACHE_SETELEM_MAYBE;
+ flags |= NFT_CACHE_SET |
+ NFT_CACHE_SETELEM_MAYBE;
break;
default:
+ flags = NFT_CACHE_TABLE;
break;
}
@@ -124,9 +119,9 @@ void nft_cache_filter_fini(struct nft_cache_filter *filter)
struct nft_filter_obj *obj, *next;
list_for_each_entry_safe(obj, next, &filter->obj[i].head, list)
- xfree(obj);
+ free(obj);
}
- xfree(filter);
+ free(filter);
}
static void cache_filter_add(struct nft_cache_filter *filter,
@@ -195,37 +190,58 @@ static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags)
return flags;
}
+static void obj_filter_setup(const struct cmd *cmd, unsigned int *flags,
+ struct nft_cache_filter *filter, int type)
+{
+ assert(filter);
+
+ if (cmd->handle.family)
+ filter->list.family = cmd->handle.family;
+ if (cmd->handle.table.name)
+ filter->list.table = cmd->handle.table.name;
+ if (cmd->handle.obj.name)
+ filter->list.obj = cmd->handle.obj.name;
+
+ filter->list.obj_type = type;
+ *flags |= NFT_CACHE_TABLE | NFT_CACHE_OBJECT;
+}
+
static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
unsigned int flags,
struct nft_cache_filter *filter)
{
switch (cmd->obj) {
case CMD_OBJ_TABLE:
- if (filter && cmd->handle.table.name) {
- filter->list.family = cmd->handle.family;
+ filter->list.family = cmd->handle.family;
+ if (!cmd->handle.table.name) {
+ flags |= NFT_CACHE_TABLE;
+ break;
+ } else {
filter->list.table = cmd->handle.table.name;
}
flags |= NFT_CACHE_FULL;
break;
case CMD_OBJ_CHAIN:
- if (filter && cmd->handle.chain.name) {
+ if (cmd->handle.chain.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
filter->list.chain = cmd->handle.chain.name;
+ /* implicit terse listing to fetch content of anonymous
+ * sets only when chain name is specified.
+ */
+ flags |= NFT_CACHE_TERSE;
}
flags |= NFT_CACHE_FULL;
break;
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
- if (filter && cmd->handle.table.name && cmd->handle.set.name) {
+ if (cmd->handle.table.name && cmd->handle.set.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
filter->list.set = cmd->handle.set.name;
}
if (filter->list.table && filter->list.set)
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM;
- else if (nft_output_terse(&nft->output))
- flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
else
flags |= NFT_CACHE_FULL;
break;
@@ -235,10 +251,11 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
case CMD_OBJ_SETS:
case CMD_OBJ_MAPS:
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET;
+ if (!nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_SETELEM;
break;
case CMD_OBJ_FLOWTABLE:
- if (filter &&
- cmd->handle.table.name &&
+ if (cmd->handle.table.name &&
cmd->handle.flowtable.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
@@ -248,11 +265,91 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
case CMD_OBJ_FLOWTABLES:
flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_COUNTER);
+ break;
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_QUOTA);
+ break;
+ case CMD_OBJ_CT_HELPER:
+ case CMD_OBJ_CT_HELPERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_HELPER);
+ break;
+ case CMD_OBJ_LIMIT:
+ case CMD_OBJ_LIMITS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_LIMIT);
+ break;
+ case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_TIMEOUT);
+ break;
+ case CMD_OBJ_SECMARK:
+ case CMD_OBJ_SECMARKS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_SECMARK);
+ break;
+ case CMD_OBJ_CT_EXPECT:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_EXPECT);
+ break;
+ case CMD_OBJ_SYNPROXY:
+ case CMD_OBJ_SYNPROXYS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_SYNPROXY);
+ break;
case CMD_OBJ_RULESET:
- if (nft_output_terse(&nft->output))
- flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
- else
- flags |= NFT_CACHE_FULL;
+ default:
+ flags |= NFT_CACHE_FULL;
+ break;
+ }
+ flags |= NFT_CACHE_REFRESH;
+
+ if (nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_TERSE;
+
+ return flags;
+}
+
+static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags,
+ struct nft_cache_filter *filter)
+{
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ case CMD_OBJ_CHAIN:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
+ if (cmd->handle.table.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ }
+ if (cmd->handle.chain.name)
+ filter->list.chain = cmd->handle.chain.name;
+ if (cmd->handle.family)
+ filter->list.family = cmd->handle.family;
+ if (cmd->handle.handle.id)
+ filter->list.rule_handle = cmd->handle.handle.id;
+
+ filter->reset.rule = true;
+ flags |= NFT_CACHE_FULL;
+ break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_COUNTER);
+ filter->reset.obj = true;
+ break;
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_QUOTA);
+ filter->reset.obj = true;
+ break;
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ if (cmd->handle.table.name && cmd->handle.set.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.set = cmd->handle.set.name;
+ }
+ flags |= NFT_CACHE_SETELEM;
+ filter->reset.elem = true;
break;
default:
flags |= NFT_CACHE_FULL;
@@ -277,6 +374,7 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
}
break;
case CMD_OBJ_RULE:
+ case CMD_OBJ_RULES:
case CMD_OBJ_CHAIN:
case CMD_OBJ_CHAINS:
if (h->table.name &&
@@ -342,7 +440,9 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
case CMD_OBJ_CT_HELPER:
case CMD_OBJ_CT_HELPERS:
case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_CT_EXPECTATIONS:
if (h->table.name &&
strlen(h->table.name) > NFT_NAME_MAXLEN) {
loc = &h->table.location;
@@ -365,51 +465,52 @@ err_name_too_long:
return -1;
}
+static void reset_filter(struct nft_cache_filter *filter)
+{
+ memset(&filter->list, 0, sizeof(filter->list));
+ memset(&filter->reset, 0, sizeof(filter->reset));
+}
+
int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
struct list_head *msgs, struct nft_cache_filter *filter,
unsigned int *pflags)
{
- unsigned int flags = NFT_CACHE_EMPTY;
+ unsigned int flags, batch_flags = NFT_CACHE_EMPTY;
struct cmd *cmd;
+ assert(filter);
+
list_for_each_entry(cmd, cmds, list) {
if (nft_handle_validate(cmd, msgs) < 0)
return -1;
- if (filter->list.table && cmd->op != CMD_LIST)
- memset(&filter->list, 0, sizeof(filter->list));
+ flags = NFT_CACHE_EMPTY;
+ reset_filter(filter);
switch (cmd->op) {
case CMD_ADD:
case CMD_INSERT:
case CMD_CREATE:
flags = evaluate_cache_add(cmd, flags);
- if (nft_output_echo(&nft->output))
- flags |= NFT_CACHE_FULL;
break;
- case CMD_REPLACE:
- flags = NFT_CACHE_FULL;
+ case CMD_REPLACE: /* only for rule */
+ flags = NFT_CACHE_TABLE | NFT_CACHE_SET;
break;
case CMD_DELETE:
- flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_FLOWTABLE |
- NFT_CACHE_OBJECT;
-
+ case CMD_DESTROY:
flags = evaluate_cache_del(cmd, flags);
break;
case CMD_GET:
flags = evaluate_cache_get(cmd, flags);
break;
case CMD_RESET:
- flags |= NFT_CACHE_TABLE;
+ flags = evaluate_cache_reset(cmd, flags, filter);
break;
case CMD_LIST:
- flags |= evaluate_cache_list(nft, cmd, flags, filter);
+ flags = evaluate_cache_list(nft, cmd, flags, filter);
break;
case CMD_MONITOR:
- flags |= NFT_CACHE_FULL;
+ flags = NFT_CACHE_FULL;
break;
case CMD_FLUSH:
flags = evaluate_cache_flush(cmd, flags, filter);
@@ -424,18 +525,16 @@ int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
default:
break;
}
+ batch_flags |= flags;
}
- *pflags = flags;
+ *pflags = batch_flags;
return 0;
}
void table_cache_add(struct table *table, struct nft_cache *cache)
{
- uint32_t hash;
-
- hash = djb_hash(table->handle.table.name) % NFT_CACHE_HSIZE;
- cache_add(&table->cache, &cache->table_cache, hash);
+ cache_add(&table->cache, &cache->table_cache, table->handle.table.name);
}
void table_cache_del(struct table *table)
@@ -449,8 +548,7 @@ struct table *table_cache_find(const struct cache *cache,
struct table *table;
uint32_t hash;
- if (!name)
- return NULL;
+ assert(name);
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(table, &cache->ht[hash], cache.hlist) {
@@ -471,8 +569,8 @@ static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
{
struct chain_cache_dump_ctx *ctx = arg;
const char *chain_name, *table_name;
- uint32_t hash, family;
struct chain *chain;
+ uint32_t family;
table_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE);
family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
@@ -482,13 +580,12 @@ static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
return 0;
chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME);
- hash = djb_hash(chain_name) % NFT_CACHE_HSIZE;
- chain = netlink_delinearize_chain(ctx->nlctx, nlc);
+ chain = netlink_delinearize_chain(ctx->nlctx, nlc);
if (chain->flags & CHAIN_F_BINDING) {
list_add_tail(&chain->cache.list, &ctx->table->chain_bindings);
} else {
- cache_add(&chain->cache, &ctx->table->chain_cache, hash);
+ cache_add(&chain->cache, &ctx->table->chain_cache, chain_name);
}
nftnl_chain_list_del(nlc);
@@ -554,10 +651,7 @@ void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table,
void chain_cache_add(struct chain *chain, struct table *table)
{
- uint32_t hash;
-
- hash = djb_hash(chain->handle.chain.name) % NFT_CACHE_HSIZE;
- cache_add(&chain->cache, &table->chain_cache, hash);
+ cache_add(&chain->cache, &table->chain_cache, chain->handle.chain.name);
}
void chain_cache_del(struct chain *chain)
@@ -570,6 +664,8 @@ struct chain *chain_cache_find(const struct table *table, const char *name)
struct chain *chain;
uint32_t hash;
+ assert(name);
+
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(chain, &table->chain_cache.ht[hash], cache.hlist) {
if (!strcmp(chain->handle.chain.name, name))
@@ -591,13 +687,14 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data)
table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
- if (h->family != family ||
- strcmp(table, h->table.name) != 0 ||
+ if ((h->family != NFPROTO_UNSPEC && h->family != family) ||
+ (h->table.name && strcmp(table, h->table.name) != 0) ||
(h->chain.name && strcmp(chain, h->chain.name) != 0))
return 0;
netlink_dump_rule(nlr, ctx);
rule = netlink_delinearize_rule(ctx, nlr);
+ assert(rule);
list_add_tail(&rule->list, &ctx->list);
return 0;
@@ -607,15 +704,30 @@ static int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
const struct nft_cache_filter *filter)
{
struct nftnl_rule_list *rule_cache;
- const char *table = NULL;
+ const char *table = h->table.name;
const char *chain = NULL;
+ uint64_t rule_handle = 0;
+ int family = h->family;
+ bool reset = false;
+ bool dump = true;
if (filter) {
- table = filter->list.table;
- chain = filter->list.chain;
+ if (filter->list.table)
+ table = filter->list.table;
+ if (filter->list.chain)
+ chain = filter->list.chain;
+ if (filter->list.rule_handle) {
+ rule_handle = filter->list.rule_handle;
+ dump = false;
+ }
+ if (filter->list.family)
+ family = filter->list.family;
+
+ reset = filter->reset.rule;
}
- rule_cache = mnl_nft_rule_dump(ctx, h->family, table, chain);
+ rule_cache = mnl_nft_rule_dump(ctx, family,
+ table, chain, rule_handle, dump, reset);
if (rule_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -641,7 +753,6 @@ static int set_cache_cb(struct nftnl_set *nls, void *arg)
const char *set_name;
uint32_t set_family;
struct set *set;
- uint32_t hash;
set_table = nftnl_set_get_str(nls, NFTNL_SET_TABLE);
set_family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY);
@@ -655,8 +766,7 @@ static int set_cache_cb(struct nftnl_set *nls, void *arg)
return -1;
set_name = nftnl_set_get_str(nls, NFTNL_SET_NAME);
- hash = djb_hash(set_name) % NFT_CACHE_HSIZE;
- cache_add(&set->cache, &ctx->table->set_cache, hash);
+ cache_add(&set->cache, &ctx->table->set_cache, set_name);
nftnl_set_list_del(nls);
nftnl_set_free(nls);
@@ -706,10 +816,7 @@ set_cache_dump(struct netlink_ctx *ctx,
void set_cache_add(struct set *set, struct table *table)
{
- uint32_t hash;
-
- hash = djb_hash(set->handle.set.name) % NFT_CACHE_HSIZE;
- cache_add(&set->cache, &table->set_cache, hash);
+ cache_add(&set->cache, &table->set_cache, set->handle.set.name);
}
void set_cache_del(struct set *set)
@@ -722,6 +829,8 @@ struct set *set_cache_find(const struct table *table, const char *name)
struct set *set;
uint32_t hash;
+ assert(name);
+
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(set, &table->set_cache.ht[hash], cache.hlist) {
if (!strcmp(set->handle.set.name, name))
@@ -739,17 +848,26 @@ struct obj_cache_dump_ctx {
static int obj_cache_cb(struct nftnl_obj *nlo, void *arg)
{
struct obj_cache_dump_ctx *ctx = arg;
+ const char *obj_table;
const char *obj_name;
+ uint32_t obj_family;
struct obj *obj;
- uint32_t hash;
+
+ obj_table = nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE);
+ obj_family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY);
+
+ if (obj_family != ctx->table->handle.family ||
+ strcmp(obj_table, ctx->table->handle.table.name))
+ return 0;
obj = netlink_delinearize_obj(ctx->nlctx, nlo);
- if (!obj)
- return -1;
+ if (obj) {
+ obj_name = nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME);
+ cache_add(&obj->cache, &ctx->table->obj_cache, obj_name);
+ }
- obj_name = nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME);
- hash = djb_hash(obj_name) % NFT_CACHE_HSIZE;
- cache_add(&obj->cache, &ctx->table->obj_cache, hash);
+ nftnl_obj_list_del(nlo);
+ nftnl_obj_free(nlo);
return 0;
}
@@ -767,13 +885,30 @@ static int obj_cache_init(struct netlink_ctx *ctx, struct table *table,
}
static struct nftnl_obj_list *obj_cache_dump(struct netlink_ctx *ctx,
- const struct table *table)
+ const struct nft_cache_filter *filter)
{
struct nftnl_obj_list *obj_list;
+ int type = NFT_OBJECT_UNSPEC;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *obj = NULL;
+ bool reset = false;
+ bool dump = true;
- obj_list = mnl_nft_obj_dump(ctx, table->handle.family,
- table->handle.table.name, NULL,
- 0, true, false);
+ if (filter) {
+ family = filter->list.family;
+ if (filter->list.table)
+ table = filter->list.table;
+ if (filter->list.obj) {
+ obj = filter->list.obj;
+ dump = false;
+ }
+ if (filter->list.obj_type)
+ type = filter->list.obj_type;
+
+ reset = filter->reset.obj;
+ }
+ obj_list = mnl_nft_obj_dump(ctx, family, table, obj, type, dump, reset);
if (!obj_list) {
if (errno == EINTR)
return NULL;
@@ -791,10 +926,7 @@ static struct nftnl_obj_list *obj_cache_dump(struct netlink_ctx *ctx,
void obj_cache_add(struct obj *obj, struct table *table)
{
- uint32_t hash;
-
- hash = djb_hash(obj->handle.obj.name) % NFT_CACHE_HSIZE;
- cache_add(&obj->cache, &table->obj_cache, hash);
+ cache_add(&obj->cache, &table->obj_cache, obj->handle.obj.name);
}
void obj_cache_del(struct obj *obj)
@@ -808,6 +940,8 @@ struct obj *obj_cache_find(const struct table *table, const char *name,
struct obj *obj;
uint32_t hash;
+ assert(name);
+
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(obj, &table->obj_cache.ht[hash], cache.hlist) {
if (!strcmp(obj->handle.obj.name, name) &&
@@ -830,7 +964,6 @@ static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg)
const char *ft_table;
const char *ft_name;
uint32_t ft_family;
- uint32_t hash;
ft_family = nftnl_flowtable_get_u32(nlf, NFTNL_FLOWTABLE_FAMILY);
ft_table = nftnl_flowtable_get_str(nlf, NFTNL_FLOWTABLE_TABLE);
@@ -844,8 +977,7 @@ static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg)
return -1;
ft_name = nftnl_flowtable_get_str(nlf, NFTNL_FLOWTABLE_NAME);
- hash = djb_hash(ft_name) % NFT_CACHE_HSIZE;
- cache_add(&ft->cache, &ctx->table->ft_cache, hash);
+ cache_add(&ft->cache, &ctx->table->ft_cache, ft_name);
nftnl_flowtable_list_del(nlf);
nftnl_flowtable_free(nlf);
@@ -896,10 +1028,7 @@ ft_cache_dump(struct netlink_ctx *ctx, const struct nft_cache_filter *filter)
void ft_cache_add(struct flowtable *ft, struct table *table)
{
- uint32_t hash;
-
- hash = djb_hash(ft->handle.flowtable.name) % NFT_CACHE_HSIZE;
- cache_add(&ft->cache, &table->ft_cache, hash);
+ cache_add(&ft->cache, &table->ft_cache, ft->handle.flowtable.name);
}
void ft_cache_del(struct flowtable *ft)
@@ -912,6 +1041,8 @@ struct flowtable *ft_cache_find(const struct table *table, const char *name)
struct flowtable *ft;
uint32_t hash;
+ assert(name);
+
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(ft, &table->ft_cache.ht[hash], cache.hlist) {
if (!strcmp(ft->handle.flowtable.name, name))
@@ -975,13 +1106,14 @@ err_ctx_list:
static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table,
const char *chain_name)
{
- struct nft_cache_filter filter;
+ struct nft_cache_filter filter = {};
struct chain *chain;
int ret = 0;
list_for_each_entry(chain, &table->chain_bindings, cache.list) {
filter.list.table = table->handle.table.name;
filter.list.chain = chain->handle.chain.name;
+
ret = rule_init_cache(ctx, table, &filter);
}
@@ -994,7 +1126,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
struct nftnl_flowtable_list *ft_list = NULL;
struct nftnl_chain_list *chain_list = NULL;
struct nftnl_set_list *set_list = NULL;
- struct nftnl_obj_list *obj_list;
+ struct nftnl_obj_list *obj_list = NULL;
struct table *table;
struct set *set;
int ret = 0;
@@ -1011,6 +1143,13 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
goto cache_fails;
}
}
+ if (flags & NFT_CACHE_OBJECT_BIT) {
+ obj_list = obj_cache_dump(ctx, filter);
+ if (!obj_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
if (flags & NFT_CACHE_FLOWTABLE_BIT) {
ft_list = ft_cache_dump(ctx, filter);
if (!ft_list) {
@@ -1034,7 +1173,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
+ set, filter->reset.elem);
if (ret < 0)
goto cache_fails;
}
@@ -1047,7 +1186,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
+ set, filter->reset.elem);
if (ret < 0)
goto cache_fails;
}
@@ -1063,15 +1202,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
goto cache_fails;
}
if (flags & NFT_CACHE_OBJECT_BIT) {
- obj_list = obj_cache_dump(ctx, table);
- if (!obj_list) {
- ret = -1;
- goto cache_fails;
- }
ret = obj_cache_init(ctx, table, obj_list);
-
- nftnl_obj_list_free(obj_list);
-
if (ret < 0)
goto cache_fails;
}
@@ -1092,6 +1223,8 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
cache_fails:
if (set_list)
nftnl_set_list_free(set_list);
+ if (obj_list)
+ nftnl_obj_list_free(obj_list);
if (ft_list)
nftnl_flowtable_list_free(ft_list);
@@ -1128,9 +1261,10 @@ static bool nft_cache_is_complete(struct nft_cache *cache, unsigned int flags)
return (cache->flags & flags) == flags;
}
-static bool nft_cache_needs_refresh(struct nft_cache *cache)
+static bool nft_cache_needs_refresh(struct nft_cache *cache, unsigned int flags)
{
- return cache->flags & NFT_CACHE_REFRESH;
+ return (cache->flags & NFT_CACHE_REFRESH) ||
+ (flags & NFT_CACHE_REFRESH);
}
static bool nft_cache_is_updated(struct nft_cache *cache, uint16_t genid)
@@ -1158,7 +1292,7 @@ int nft_cache_update(struct nft_ctx *nft, unsigned int flags,
replay:
ctx.seqnum = cache->seqnum++;
genid = mnl_genid_get(&ctx);
- if (!nft_cache_needs_refresh(cache) &&
+ if (!nft_cache_needs_refresh(cache, flags) &&
nft_cache_is_complete(cache, flags) &&
nft_cache_is_updated(cache, genid))
return 0;
@@ -1230,11 +1364,14 @@ void cache_init(struct cache *cache)
void cache_free(struct cache *cache)
{
- xfree(cache->ht);
+ free(cache->ht);
}
-void cache_add(struct cache_item *item, struct cache *cache, uint32_t hash)
+void cache_add(struct cache_item *item, struct cache *cache, const char *name)
{
+ uint32_t hash;
+
+ hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_add_tail(&item->hlist, &cache->ht[hash]);
list_add_tail(&item->list, &cache->list);
}
diff --git a/src/cli.c b/src/cli.c
index 11fc85ab..448c25c2 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -12,13 +12,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <config.h>
-#include <stdlib.h>
+#include <nft.h>
+
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
-#include <string.h>
#include <ctype.h>
#include <limits.h>
#ifdef HAVE_LIBREADLINE
@@ -37,6 +36,15 @@
#define CMDLINE_PROMPT "nft> "
#define CMDLINE_QUIT "quit"
+static bool cli_quit;
+static int cli_rc;
+
+static void __cli_exit(int rc)
+{
+ cli_quit = true;
+ cli_rc = rc;
+}
+
static char histfile[PATH_MAX];
static void
@@ -100,8 +108,8 @@ static char *cli_append_multiline(char *line)
if (!s) {
fprintf(stderr, "%s:%u: Memory allocation failure\n",
__FILE__, __LINE__);
- cli_exit();
- exit(EXIT_FAILURE);
+ cli_exit(EXIT_FAILURE);
+ return NULL;
}
snprintf(s, len + 1, "%s%s", multiline, line);
free(multiline);
@@ -125,8 +133,7 @@ static void cli_complete(char *line)
if (line == NULL) {
printf("\n");
- cli_exit();
- exit(0);
+ return cli_exit(0);
}
line = cli_append_multiline(line);
@@ -139,10 +146,8 @@ static void cli_complete(char *line)
if (*c == '\0')
return;
- if (!strcmp(line, CMDLINE_QUIT)) {
- cli_exit();
- exit(0);
- }
+ if (!strcmp(line, CMDLINE_QUIT))
+ return cli_exit(0);
/* avoid duplicate history entries */
hist = history_get(history_length);
@@ -176,16 +181,19 @@ int cli_init(struct nft_ctx *nft)
read_history(histfile);
history_set_pos(history_length);
- while (true)
+ while (!cli_quit)
rl_callback_read_char();
- return 0;
+
+ return cli_rc;
}
-void cli_exit(void)
+void cli_exit(int rc)
{
rl_callback_handler_remove();
rl_deprep_terminal();
write_history(histfile);
+
+ __cli_exit(rc);
}
#elif defined(HAVE_LIBEDIT)
@@ -205,51 +213,63 @@ int cli_init(struct nft_ctx *nft)
history_set_pos(history_length);
rl_set_prompt(CMDLINE_PROMPT);
- while ((line = readline(rl_prompt)) != NULL) {
+ while (!cli_quit) {
+ line = readline(rl_prompt);
+ if (!line) {
+ cli_exit(0);
+ break;
+ }
line = cli_append_multiline(line);
if (!line)
continue;
cli_complete(line);
}
- cli_exit();
- return 0;
+ return cli_rc;
}
-void cli_exit(void)
+void cli_exit(int rc)
{
rl_deprep_terminal();
write_history(histfile);
+
+ __cli_exit(rc);
}
#else /* HAVE_LINENOISE */
int cli_init(struct nft_ctx *nft)
{
- int quit = 0;
char *line;
init_histfile();
linenoiseHistoryLoad(histfile);
linenoiseSetMultiLine(1);
- while (!quit && (line = linenoise(CMDLINE_PROMPT)) != NULL) {
+ while (!cli_quit) {
+ line = linenoise(CMDLINE_PROMPT);
+ if (!line) {
+ cli_exit(0);
+ break;
+ }
if (strcmp(line, CMDLINE_QUIT) == 0) {
- quit = 1;
+ cli_exit(0);
} else if (line[0] != '\0') {
linenoiseHistoryAdd(line);
nft_run_cmd_from_buffer(nft, line);
}
linenoiseFree(line);
}
- cli_exit();
- exit(0);
+
+ return cli_rc;
}
-void cli_exit(void)
+void cli_exit(int rc)
{
linenoiseHistorySave(histfile);
+
+ __cli_exit(rc);
}
#endif /* HAVE_LINENOISE */
diff --git a/src/cmd.c b/src/cmd.c
index 63692422..eb44b986 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -1,3 +1,13 @@
+/*
+ * Copyright (c) 2020 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <erec.h>
#include <mnl.h>
#include <cmd.h>
@@ -5,9 +15,20 @@
#include <utils.h>
#include <iface.h>
#include <errno.h>
-#include <stdlib.h>
#include <cache.h>
-#include <string.h>
+
+void cmd_add_loc(struct cmd *cmd, const struct nlmsghdr *nlh, const struct location *loc)
+{
+ if (cmd->num_attrs >= cmd->attr_array_len) {
+ cmd->attr_array_len *= 2;
+ cmd->attr = xrealloc(cmd->attr, sizeof(struct nlerr_loc) * cmd->attr_array_len);
+ }
+
+ cmd->attr[cmd->num_attrs].seqnum = nlh->nlmsg_seq;
+ cmd->attr[cmd->num_attrs].offset = nlh->nlmsg_len;
+ cmd->attr[cmd->num_attrs].location = loc;
+ cmd->num_attrs++;
+}
static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
const struct location *loc)
@@ -21,7 +42,7 @@ static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!table)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?",
+ 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;
@@ -37,7 +58,7 @@ static int table_fuzzy_check(struct netlink_ctx *ctx, const struct cmd *cmd,
if (strcmp(cmd->handle.table.name, table->handle.table.name) ||
cmd->handle.family != table->handle.family) {
netlink_io_error(ctx, &cmd->handle.table.location,
- "%s; did you mean table ‘%s’ in family %s?",
+ "%s; did you mean table '%s' in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
return 1;
@@ -55,6 +76,10 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!cmd->handle.chain.name)
return 0;
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_CHAIN,
+ ctx->msgs, NULL) < 0)
+ return 0;
+
chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
/* check table first. */
if (!table)
@@ -66,7 +91,7 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!chain)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ 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);
@@ -96,7 +121,7 @@ static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
if (strcmp(cmd->handle.chain.name, chain->handle.chain.name)) {
- netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ 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),
@@ -116,6 +141,10 @@ static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!cmd->handle.set.name)
return 0;
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_SET,
+ ctx->msgs, NULL) < 0)
+ return 0;
+
set = set_lookup_fuzzy(cmd->handle.set.name, &ctx->nft->cache, &table);
/* check table first. */
if (!table)
@@ -127,7 +156,7 @@ static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!set)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean %s ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean %s '%s' in table %s '%s'?",
strerror(ENOENT),
set_is_map(set->flags) ? "map" : "set",
set->handle.set.name,
@@ -145,6 +174,10 @@ static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!cmd->handle.obj.name)
return 0;
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_OBJECT,
+ ctx->msgs, NULL) < 0)
+ return 0;
+
obj = obj_lookup_fuzzy(cmd->handle.obj.name, &ctx->nft->cache, &table);
/* check table first. */
if (!table)
@@ -156,7 +189,7 @@ static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!obj)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean obj ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean obj '%s' in table %s '%s'?",
strerror(ENOENT), obj->handle.obj.name,
family2str(obj->handle.family),
table->handle.table.name);
@@ -173,6 +206,10 @@ static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
if (!cmd->handle.flowtable.name)
return 0;
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE,
+ ctx->msgs, NULL) < 0)
+ return 0;
+
ft = flowtable_lookup_fuzzy(cmd->handle.flowtable.name,
&ctx->nft->cache, &table);
/* check table first. */
@@ -185,7 +222,7 @@ static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
if (!ft)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean flowtable '%s' in table %s '%s'?",
strerror(ENOENT), ft->handle.flowtable.name,
family2str(ft->handle.family),
table->handle.table.name);
@@ -233,6 +270,53 @@ static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
netlink_io_error(ctx, loc, "Could not process rule: %s", strerror(err));
}
+static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd,
+ struct mnl_err *err)
+{
+ struct chain *chain = cmd->chain, *existing_chain;
+ const struct table *table;
+ int priority;
+
+ switch (err->err) {
+ case EOPNOTSUPP:
+ if (!(chain->flags & CHAIN_F_BASECHAIN))
+ break;
+
+ mpz_export_data(&priority, chain->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ if (priority <= -200 && !strcmp(chain->type.str, "nat"))
+ return netlink_io_error(ctx, &chain->priority.loc,
+ "Chains of type \"nat\" must have a priority value above -200");
+
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_CHAIN,
+ ctx->msgs, NULL) < 0) {
+ return netlink_io_error(ctx, &chain->loc,
+ "Chain of type \"%s\" is not supported, perhaps kernel support is missing?",
+ chain->type.str);
+ }
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name, cmd->handle.family);
+ if (table) {
+ existing_chain = chain_cache_find(table, cmd->handle.chain.name);
+ if (existing_chain && existing_chain != chain &&
+ !strcmp(existing_chain->handle.chain.name, chain->handle.chain.name))
+ return netlink_io_error(ctx, &chain->loc,
+ "Chain \"%s\" already exists in table %s '%s' with different declaration",
+ chain->handle.chain.name,
+ family2str(table->handle.family), table->handle.table.name);
+ }
+
+ return netlink_io_error(ctx, &chain->loc,
+ "Chain of type \"%s\" is not supported, perhaps kernel support is missing?",
+ chain->type.str);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err)
{
@@ -240,9 +324,8 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
uint32_t i;
for (i = 0; i < cmd->num_attrs; i++) {
- if (!cmd->attr[i].offset)
- break;
- if (cmd->attr[i].offset == err->offset)
+ if (cmd->attr[i].seqnum == err->seqnum &&
+ cmd->attr[i].offset == err->offset)
loc = cmd->attr[i].location;
}
@@ -255,6 +338,150 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
loc = &cmd->location;
}
+ switch (cmd->obj) {
+ case CMD_OBJ_CHAIN:
+ if (nft_cmd_chain_error(ctx, cmd, err) < 0)
+ return;
+ break;
+ default:
+ break;
+ }
+
+ if (cmd->op == CMD_DESTROY && err->err == EINVAL) {
+ netlink_io_error(ctx, loc,
+ "\"destroy\" command is not supported, perhaps kernel support is missing?");
+ return;
+ }
+
netlink_io_error(ctx, loc, "Could not process rule: %s",
strerror(err->err));
}
+
+static void nft_cmd_expand_chain(struct chain *chain, struct list_head *new_cmds)
+{
+ struct rule *rule, *next;
+ struct handle h;
+ struct cmd *new;
+
+ list_for_each_entry_safe(rule, next, &chain->rules, list) {
+ list_del(&rule->list);
+ handle_merge(&rule->handle, &chain->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &chain->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);
+ list_add_tail(&new->list, new_cmds);
+ }
+}
+
+bool nft_cmd_collapse_elems(enum cmd_ops op, struct list_head *cmds,
+ struct handle *handle, struct expr *init)
+{
+ struct cmd *last_cmd;
+
+ if (list_empty(cmds))
+ return false;
+
+ if (init->etype == EXPR_VARIABLE)
+ return false;
+
+ last_cmd = list_last_entry(cmds, struct cmd, list);
+ if (last_cmd->op != op ||
+ last_cmd->obj != CMD_OBJ_ELEMENTS ||
+ last_cmd->expr->etype == EXPR_VARIABLE ||
+ last_cmd->handle.family != handle->family ||
+ strcmp(last_cmd->handle.table.name, handle->table.name) ||
+ strcmp(last_cmd->handle.set.name, handle->set.name))
+ return false;
+
+ list_splice_tail_init(&init->expressions, &last_cmd->expr->expressions);
+ last_cmd->expr->size += init->size;
+
+ return true;
+}
+
+void nft_cmd_expand(struct cmd *cmd)
+{
+ struct list_head new_cmds;
+ struct flowtable *ft;
+ struct table *table;
+ struct chain *chain;
+ struct set *set;
+ struct obj *obj;
+ struct cmd *new;
+ struct handle h;
+
+ init_list_head(&new_cmds);
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ table = cmd->table;
+ if (!table)
+ return;
+
+ list_for_each_entry(chain, &table->chains, list) {
+ handle_merge(&chain->handle, &table->handle);
+ 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);
+ }
+ list_for_each_entry(obj, &table->objs, list) {
+ handle_merge(&obj->handle, &table->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &obj->handle);
+ new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h,
+ &obj->location, obj_get(obj));
+ list_add_tail(&new->list, &new_cmds);
+ }
+ list_for_each_entry(set, &table->sets, list) {
+ handle_merge(&set->handle, &table->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &set->handle);
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h,
+ &set->location, set_get(set));
+ list_add_tail(&new->list, &new_cmds);
+ }
+ list_for_each_entry(ft, &table->flowtables, list) {
+ handle_merge(&ft->handle, &table->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &ft->handle);
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h,
+ &ft->location, flowtable_get(ft));
+ list_add_tail(&new->list, &new_cmds);
+ }
+ list_for_each_entry(chain, &table->chains, list)
+ nft_cmd_expand_chain(chain, &new_cmds);
+
+ list_splice(&new_cmds, &cmd->list);
+ break;
+ case CMD_OBJ_CHAIN:
+ chain = cmd->chain;
+ if (!chain || list_empty(&chain->rules))
+ break;
+
+ nft_cmd_expand_chain(chain, &new_cmds);
+ list_splice(&new_cmds, &cmd->list);
+ break;
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ set = cmd->set;
+ if (!set->init)
+ break;
+
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &set->handle);
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h,
+ &set->location, set_get(set));
+ list_add(&new->list, &cmd->list);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/ct.c b/src/ct.c
index e246d303..71ebb948 100644
--- a/src/ct.c
+++ b/src/ct.c
@@ -10,11 +10,11 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
-#include <string.h>
#include <netinet/ip.h>
#include <linux/netfilter.h>
@@ -131,7 +131,7 @@ static const struct symbol_table ct_events_tbl = {
},
};
-static const struct datatype ct_event_type = {
+const struct datatype ct_event_type = {
.type = TYPE_CT_EVENTBIT,
.name = "ct_event",
.desc = "conntrack event bits",
@@ -216,10 +216,17 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
return NULL;
}
-static const struct datatype ct_label_type = {
+static void ct_label_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, CONNLABEL_CONF,
+ octx->tbl.ct_label, &ct_label_type);
+}
+
+const struct datatype ct_label_type = {
.type = TYPE_CT_LABEL,
.name = "ct_label",
.desc = "conntrack label",
+ .describe = ct_label_type_describe,
.byteorder = BYTEORDER_HOST_ENDIAN,
.size = CT_LABEL_BIT_SIZE,
.basetype = &bitmask_type,
@@ -271,10 +278,10 @@ const struct ct_template ct_templates[__NFT_CT_MAX] = {
[NFT_CT_PROTOCOL] = CT_TEMPLATE("protocol", &inet_protocol_type,
BYTEORDER_BIG_ENDIAN,
BITS_PER_BYTE),
- [NFT_CT_PROTO_SRC] = CT_TEMPLATE("proto-src", &invalid_type,
+ [NFT_CT_PROTO_SRC] = CT_TEMPLATE("proto-src", &inet_service_type,
BYTEORDER_BIG_ENDIAN,
2 * BITS_PER_BYTE),
- [NFT_CT_PROTO_DST] = CT_TEMPLATE("proto-dst", &invalid_type,
+ [NFT_CT_PROTO_DST] = CT_TEMPLATE("proto-dst", &inet_service_type,
BYTEORDER_BIG_ENDIAN,
2 * BITS_PER_BYTE),
[NFT_CT_LABELS] = CT_TEMPLATE("label", &ct_label_type,
@@ -449,7 +456,11 @@ struct expr *ct_expr_alloc(const struct location *loc, enum nft_ct_keys key,
switch (key) {
case NFT_CT_SRC:
+ case NFT_CT_SRC_IP:
+ case NFT_CT_SRC_IP6:
case NFT_CT_DST:
+ case NFT_CT_DST_IP:
+ case NFT_CT_DST_IP6:
expr->ct.base = PROTO_BASE_NETWORK_HDR;
break;
case NFT_CT_PROTO_SRC:
@@ -523,7 +534,7 @@ static void ct_stmt_destroy(struct stmt *stmt)
expr_free(stmt->ct.expr);
}
-static const struct stmt_ops ct_stmt_ops = {
+const struct stmt_ops ct_stmt_ops = {
.type = STMT_CT,
.name = "ct",
.print = ct_stmt_print,
@@ -550,7 +561,7 @@ static void notrack_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, "notrack");
}
-static const struct stmt_ops notrack_stmt_ops = {
+const struct stmt_ops notrack_stmt_ops = {
.type = STMT_NOTRACK,
.name = "notrack",
.print = notrack_stmt_print,
@@ -570,10 +581,10 @@ static void flow_offload_stmt_print(const struct stmt *stmt,
static void flow_offload_stmt_destroy(struct stmt *stmt)
{
- xfree(stmt->flow.table_name);
+ free_const(stmt->flow.table_name);
}
-static const struct stmt_ops flow_offload_stmt_ops = {
+const struct stmt_ops flow_offload_stmt_ops = {
.type = STMT_FLOW_OFFLOAD,
.name = "flow_offload",
.print = flow_offload_stmt_print,
diff --git a/src/datatype.c b/src/datatype.c
index 2e31c858..f347010f 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -8,8 +8,8 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <stdlib.h>
-#include <string.h>
+#include <nft.h>
+
#include <inttypes.h>
#include <ctype.h> /* isdigit */
#include <errno.h>
@@ -28,6 +28,8 @@
#include <erec.h>
#include <netlink.h>
#include <json.h>
+#include <misspell.h>
+#include "nftutils.h"
#include <netinet/ip_icmp.h>
@@ -62,6 +64,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_CT_DIR] = &ct_dir_type,
[TYPE_CT_STATUS] = &ct_status_type,
[TYPE_ICMP6_TYPE] = &icmp6_type_type,
+ [TYPE_CT_LABEL] = &ct_label_type,
[TYPE_PKTTYPE] = &pkttype_type,
[TYPE_ICMP_CODE] = &icmp_code_type,
[TYPE_ICMPV6_CODE] = &icmpv6_code_type,
@@ -71,6 +74,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_ECN] = &ecn_type,
[TYPE_FIB_ADDR] = &fib_addr_type,
[TYPE_BOOLEAN] = &boolean_type,
+ [TYPE_CT_EVENTBIT] = &ct_event_type,
[TYPE_IFNAME] = &ifname_type,
[TYPE_IGMP_TYPE] = &igmp_type_type,
[TYPE_TIME_DATE] = &date_type,
@@ -119,10 +123,16 @@ void datatype_print(const struct expr *expr, struct output_ctx *octx)
expr->dtype->name);
}
+bool datatype_prefix_notation(const struct datatype *dtype)
+{
+ return dtype->type == TYPE_IPADDR || dtype->type == TYPE_IP6ADDR;
+}
+
struct error_record *symbol_parse(struct parse_ctx *ctx, const struct expr *sym,
struct expr **res)
{
const struct datatype *dtype = sym->dtype;
+ struct error_record *erec;
assert(sym->etype == EXPR_SYMBOL);
@@ -136,11 +146,54 @@ struct error_record *symbol_parse(struct parse_ctx *ctx, const struct expr *sym,
res);
} while ((dtype = dtype->basetype));
- return error(&sym->location,
- "Can't parse symbolic %s expressions",
+ dtype = sym->dtype;
+ if (dtype->err) {
+ erec = dtype->err(sym);
+ if (erec)
+ return erec;
+ }
+
+ return error(&sym->location, "Could not parse symbolic %s expression",
sym->dtype->desc);
}
+static struct error_record *__symbol_parse_fuzzy(const struct expr *sym,
+ const struct symbol_table *tbl)
+{
+ const struct symbolic_constant *s;
+ struct string_misspell_state st;
+
+ string_misspell_init(&st);
+
+ for (s = tbl->symbols; s->identifier != NULL; s++) {
+ string_misspell_update(sym->identifier, s->identifier,
+ (void *)s->identifier, &st);
+ }
+
+ if (st.obj) {
+ return error(&sym->location,
+ "Could not parse %s expression; did you you mean `%s`?",
+ sym->dtype->desc, st.obj);
+ }
+
+ return NULL;
+}
+
+static struct error_record *symbol_parse_fuzzy(const struct expr *sym,
+ const struct symbol_table *tbl)
+{
+ struct error_record *erec;
+
+ if (!tbl)
+ return NULL;
+
+ erec = __symbol_parse_fuzzy(sym, tbl);
+ if (erec)
+ return erec;
+
+ return NULL;
+}
+
struct error_record *symbolic_constant_parse(struct parse_ctx *ctx,
const struct expr *sym,
const struct symbol_table *tbl,
@@ -163,8 +216,16 @@ struct error_record *symbolic_constant_parse(struct parse_ctx *ctx,
do {
if (dtype->basetype->parse) {
erec = dtype->basetype->parse(ctx, sym, res);
- if (erec != NULL)
- return erec;
+ if (erec != NULL) {
+ struct error_record *fuzzy_erec;
+
+ fuzzy_erec = symbol_parse_fuzzy(sym, tbl);
+ if (!fuzzy_erec)
+ return erec;
+
+ erec_destroy(erec);
+ return fuzzy_erec;
+ }
if (*res)
return NULL;
goto out;
@@ -321,15 +382,33 @@ static void verdict_type_print(const struct expr *expr, struct output_ctx *octx)
}
}
-static struct error_record *verdict_type_parse(struct parse_ctx *ctx,
- const struct expr *sym,
- struct expr **res)
+static struct error_record *verdict_type_error(const struct expr *sym)
{
- *res = constant_expr_alloc(&sym->location, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(sym->identifier) + 1) * BITS_PER_BYTE,
- sym->identifier);
- return NULL;
+ /* Skip jump and goto from fuzzy match to provide better error
+ * reporting, fall back to `jump chain' if no clue.
+ */
+ static const char *verdict_array[] = {
+ "continue", "break", "return", "accept", "drop", "queue",
+ "stolen", NULL,
+ };
+ struct string_misspell_state st;
+ int i;
+
+ string_misspell_init(&st);
+
+ for (i = 0; verdict_array[i] != NULL; i++) {
+ string_misspell_update(sym->identifier, verdict_array[i],
+ (void *)verdict_array[i], &st);
+ }
+
+ if (st.obj) {
+ return error(&sym->location, "Could not parse %s; did you mean `%s'?",
+ sym->dtype->desc, st.obj);
+ }
+
+ /* assume user would like to jump to chain as a hint. */
+ return error(&sym->location, "Could not parse %s; did you mean `jump %s'?",
+ sym->dtype->desc, sym->identifier);
}
const struct datatype verdict_type = {
@@ -337,7 +416,7 @@ const struct datatype verdict_type = {
.name = "verdict",
.desc = "netfilter verdict",
.print = verdict_type_print,
- .parse = verdict_type_parse,
+ .err = verdict_type_error,
};
static const struct symbol_table nfproto_tbl = {
@@ -426,6 +505,24 @@ const struct datatype xinteger_type = {
.parse = integer_type_parse,
};
+static void queue_type_print(const struct expr *expr, struct output_ctx *octx)
+{
+ nft_gmp_print(octx, "queue");
+}
+
+/* Dummy queue_type for set declaration with typeof, see
+ * constant_expr_build_udata and constant_expr_parse_udata,
+ * basetype is used for elements.
+*/
+const struct datatype queue_type = {
+ .type = TYPE_INTEGER,
+ .is_typeof = 1,
+ .name = "queue",
+ .desc = "queue",
+ .basetype = &integer_type,
+ .print = queue_type_print,
+};
+
static void string_type_print(const struct expr *expr, struct output_ctx *octx)
{
unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
@@ -528,27 +625,34 @@ static struct error_record *ipaddr_type_parse(struct parse_ctx *ctx,
const struct expr *sym,
struct expr **res)
{
- struct addrinfo *ai, hints = { .ai_family = AF_INET,
- .ai_socktype = SOCK_DGRAM};
- struct in_addr *addr;
- int err;
+ struct in_addr addr;
+
+ if (nft_input_no_dns(ctx->input)) {
+ if (inet_pton(AF_INET, sym->identifier, &addr) != 1)
+ return error(&sym->location, "Invalid IPv4 address");
+ } else {
+ struct addrinfo *ai, hints = { .ai_family = AF_INET,
+ .ai_socktype = SOCK_DGRAM};
+ int err;
- err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
- if (err != 0)
- return error(&sym->location, "Could not resolve hostname: %s",
- gai_strerror(err));
+ err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
+ if (err != 0)
+ return error(&sym->location, "Could not resolve hostname: %s",
+ gai_strerror(err));
- if (ai->ai_next != NULL) {
+ if (ai->ai_next != NULL) {
+ freeaddrinfo(ai);
+ return error(&sym->location,
+ "Hostname resolves to multiple addresses");
+ }
+ assert(ai->ai_addr->sa_family == AF_INET);
+ addr = ((struct sockaddr_in *) (void *) ai->ai_addr)->sin_addr;
freeaddrinfo(ai);
- return error(&sym->location,
- "Hostname resolves to multiple addresses");
}
- addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
*res = constant_expr_alloc(&sym->location, &ipaddr_type,
BYTEORDER_BIG_ENDIAN,
- sizeof(*addr) * BITS_PER_BYTE, addr);
- freeaddrinfo(ai);
+ sizeof(addr) * BITS_PER_BYTE, &addr);
return NULL;
}
@@ -561,7 +665,6 @@ const struct datatype ipaddr_type = {
.basetype = &integer_type,
.print = ipaddr_type_print,
.parse = ipaddr_type_parse,
- .flags = DTYPE_F_PREFIX,
};
static void ip6addr_type_print(const struct expr *expr, struct output_ctx *octx)
@@ -587,27 +690,35 @@ static struct error_record *ip6addr_type_parse(struct parse_ctx *ctx,
const struct expr *sym,
struct expr **res)
{
- struct addrinfo *ai, hints = { .ai_family = AF_INET6,
- .ai_socktype = SOCK_DGRAM};
- struct in6_addr *addr;
- int err;
+ struct in6_addr addr;
- err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
- if (err != 0)
- return error(&sym->location, "Could not resolve hostname: %s",
- gai_strerror(err));
+ if (nft_input_no_dns(ctx->input)) {
+ if (inet_pton(AF_INET6, sym->identifier, &addr) != 1)
+ return error(&sym->location, "Invalid IPv6 address");
+ } else {
+ struct addrinfo *ai, hints = { .ai_family = AF_INET6,
+ .ai_socktype = SOCK_DGRAM};
+ int err;
+
+ err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
+ if (err != 0)
+ return error(&sym->location, "Could not resolve hostname: %s",
+ gai_strerror(err));
+
+ if (ai->ai_next != NULL) {
+ freeaddrinfo(ai);
+ return error(&sym->location,
+ "Hostname resolves to multiple addresses");
+ }
- if (ai->ai_next != NULL) {
+ assert(ai->ai_addr->sa_family == AF_INET6);
+ addr = ((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_addr;
freeaddrinfo(ai);
- return error(&sym->location,
- "Hostname resolves to multiple addresses");
}
- addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
*res = constant_expr_alloc(&sym->location, &ip6addr_type,
BYTEORDER_BIG_ENDIAN,
- sizeof(*addr) * BITS_PER_BYTE, addr);
- freeaddrinfo(ai);
+ sizeof(addr) * BITS_PER_BYTE, &addr);
return NULL;
}
@@ -620,18 +731,17 @@ const struct datatype ip6addr_type = {
.basetype = &integer_type,
.print = ip6addr_type_print,
.parse = ip6addr_type_parse,
- .flags = DTYPE_F_PREFIX,
};
static void inet_protocol_type_print(const struct expr *expr,
struct output_ctx *octx)
{
- struct protoent *p;
+ if (!nft_output_numeric_proto(octx) &&
+ mpz_cmp_ui(expr->value, UINT8_MAX) <= 0) {
+ char name[NFT_PROTONAME_MAXSIZE];
- if (!nft_output_numeric_proto(octx)) {
- p = getprotobynumber(mpz_get_uint8(expr->value));
- if (p != NULL) {
- nft_print(octx, "%s", p->p_name);
+ if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name))) {
+ nft_print(octx, "%s", name);
return;
}
}
@@ -640,15 +750,15 @@ static void inet_protocol_type_print(const struct expr *expr,
static void inet_protocol_type_describe(struct output_ctx *octx)
{
- struct protoent *p;
uint8_t protonum;
for (protonum = 0; protonum < UINT8_MAX; protonum++) {
- p = getprotobynumber(protonum);
- if (!p)
+ char name[NFT_PROTONAME_MAXSIZE];
+
+ if (!nft_getprotobynumber(protonum, name, sizeof(name)))
continue;
- nft_print(octx, "\t%-30s\t%u\n", p->p_name, protonum);
+ nft_print(octx, "\t%-30s\t%u\n", name, protonum);
}
}
@@ -656,7 +766,6 @@ static struct error_record *inet_protocol_type_parse(struct parse_ctx *ctx,
const struct expr *sym,
struct expr **res)
{
- struct protoent *p;
uint8_t proto;
uintmax_t i;
char *end;
@@ -669,11 +778,13 @@ static struct error_record *inet_protocol_type_parse(struct parse_ctx *ctx,
proto = i;
} else {
- p = getprotobyname(sym->identifier);
- if (p == NULL)
+ int r;
+
+ r = nft_getprotobyname(sym->identifier);
+ if (r < 0)
return error(&sym->location, "Could not resolve protocol name");
- proto = p->p_proto;
+ proto = r;
}
*res = constant_expr_alloc(&sym->location, &inet_protocol_type,
@@ -697,17 +808,18 @@ const struct datatype inet_protocol_type = {
static void inet_service_print(const struct expr *expr, struct output_ctx *octx)
{
uint16_t port = mpz_get_be16(expr->value);
- const struct servent *s = getservbyport(port, NULL);
+ char name[NFT_SERVNAME_MAXSIZE];
- if (s == NULL)
+ if (!nft_getservbyport(port, NULL, name, sizeof(name)))
nft_print(octx, "%hu", ntohs(port));
else
- nft_print(octx, "\"%s\"", s->s_name);
+ nft_print(octx, "\"%s\"", name);
}
void inet_service_type_print(const struct expr *expr, struct output_ctx *octx)
{
- if (nft_output_service(octx)) {
+ if (nft_output_service(octx) &&
+ mpz_cmp_ui(expr->value, UINT16_MAX) <= 0) {
inet_service_print(expr, octx);
return;
}
@@ -737,7 +849,12 @@ static struct error_record *inet_service_type_parse(struct parse_ctx *ctx,
return error(&sym->location, "Could not resolve service: %s",
gai_strerror(err));
- port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
+ if (ai->ai_addr->sa_family == AF_INET) {
+ port = ((struct sockaddr_in *)(void *)ai->ai_addr)->sin_port;
+ } else {
+ assert(ai->ai_addr->sa_family == AF_INET6);
+ port = ((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_port;
+ }
freeaddrinfo(ai);
}
@@ -761,19 +878,48 @@ const struct datatype inet_service_type = {
#define RT_SYM_TAB_INITIAL_SIZE 16
+static FILE *open_iproute2_db(const char *filename, char **path)
+{
+ FILE *ret;
+
+ if (filename[0] == '/')
+ return fopen(filename, "r");
+
+ if (asprintf(path, "/etc/iproute2/%s", filename) == -1)
+ goto fail;
+
+ ret = fopen(*path, "r");
+ if (ret)
+ return ret;
+
+ free(*path);
+ if (asprintf(path, "/usr/share/iproute2/%s", filename) == -1)
+ goto fail;
+
+ ret = fopen(*path, "r");
+ if (ret)
+ return ret;
+
+ free(*path);
+fail:
+ *path = NULL;
+ return NULL;
+}
+
struct symbol_table *rt_symbol_table_init(const char *filename)
{
+ char buf[512], namebuf[512], *p, *path = NULL;
struct symbolic_constant s;
struct symbol_table *tbl;
unsigned int size, nelems, val;
- char buf[512], namebuf[512], *p;
FILE *f;
size = RT_SYM_TAB_INITIAL_SIZE;
tbl = xmalloc(sizeof(*tbl) + size * sizeof(s));
+ tbl->base = BASE_DECIMAL;
nelems = 0;
- f = fopen(filename, "r");
+ f = open_iproute2_db(filename, &path);
if (f == NULL)
goto out;
@@ -783,12 +929,15 @@ struct symbol_table *rt_symbol_table_init(const char *filename)
p++;
if (*p == '#' || *p == '\n' || *p == '\0')
continue;
- if (sscanf(p, "0x%x %511s\n", &val, namebuf) != 2 &&
- sscanf(p, "0x%x %511s #", &val, namebuf) != 2 &&
- sscanf(p, "%u %511s\n", &val, namebuf) != 2 &&
- sscanf(p, "%u %511s #", &val, namebuf) != 2) {
+ if (sscanf(p, "0x%x %511s\n", &val, namebuf) == 2 ||
+ sscanf(p, "0x%x %511s #", &val, namebuf) == 2) {
+ tbl->base = BASE_HEXADECIMAL;
+ } else if (sscanf(p, "%u %511s\n", &val, namebuf) == 2 ||
+ sscanf(p, "%u %511s #", &val, namebuf) == 2) {
+ tbl->base = BASE_DECIMAL;
+ } else {
fprintf(stderr, "iproute database '%s' corrupted\n",
- filename);
+ path ?: filename);
break;
}
@@ -805,6 +954,8 @@ struct symbol_table *rt_symbol_table_init(const char *filename)
fclose(f);
out:
+ if (path)
+ free(path);
tbl->symbols[nelems] = SYMBOL_LIST_END;
return tbl;
}
@@ -814,13 +965,40 @@ void rt_symbol_table_free(const struct symbol_table *tbl)
const struct symbolic_constant *s;
for (s = tbl->symbols; s->identifier != NULL; s++)
- xfree(s->identifier);
- xfree(tbl);
+ free_const(s->identifier);
+ free_const(tbl);
+}
+
+void rt_symbol_table_describe(struct output_ctx *octx, const char *name,
+ const struct symbol_table *tbl,
+ const struct datatype *type)
+{
+ char *path = NULL;
+ FILE *f;
+
+ if (!tbl || !tbl->symbols[0].identifier)
+ return;
+
+ f = open_iproute2_db(name, &path);
+ if (f)
+ fclose(f);
+ if (!path && asprintf(&path, "%s%s",
+ name[0] == '/' ? "" : "unknown location of ",
+ name) < 0)
+ return;
+
+ nft_print(octx, "\npre-defined symbolic constants from %s ", path);
+ if (tbl->base == BASE_DECIMAL)
+ nft_print(octx, "(in decimal):\n");
+ else
+ nft_print(octx, "(in hexadecimal):\n");
+ symbol_table_print(tbl, type, type->byteorder, octx);
+ free(path);
}
void mark_table_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.mark = rt_symbol_table_init("/etc/iproute2/rt_marks");
+ ctx->output.tbl.mark = rt_symbol_table_init("rt_marks");
}
void mark_table_exit(struct nft_ctx *ctx)
@@ -840,10 +1018,17 @@ static struct error_record *mark_type_parse(struct parse_ctx *ctx,
return symbolic_constant_parse(ctx, sym, ctx->tbl->mark, res);
}
+static void mark_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, "rt_marks",
+ octx->tbl.mark, &mark_type);
+}
+
const struct datatype mark_type = {
.type = TYPE_MARK,
.name = "mark",
.desc = "packet mark",
+ .describe = mark_type_describe,
.size = 4 * BITS_PER_BYTE,
.byteorder = BYTEORDER_HOST_ENDIAN,
.basetype = &integer_type,
@@ -851,9 +1036,9 @@ const struct datatype mark_type = {
.print = mark_type_print,
.json = mark_type_json,
.parse = mark_type_parse,
- .flags = DTYPE_F_PREFIX,
};
+/* symbol table for private datatypes for reject statement. */
static const struct symbol_table icmp_code_tbl = {
.base = BASE_DECIMAL,
.symbols = {
@@ -869,16 +1054,17 @@ static const struct symbol_table icmp_code_tbl = {
},
};
-const struct datatype icmp_code_type = {
- .type = TYPE_ICMP_CODE,
+/* private datatype for reject statement. */
+const struct datatype reject_icmp_code_type = {
.name = "icmp_code",
- .desc = "icmp code",
+ .desc = "reject icmp code",
.size = BITS_PER_BYTE,
.byteorder = BYTEORDER_BIG_ENDIAN,
.basetype = &integer_type,
.sym_tbl = &icmp_code_tbl,
};
+/* symbol table for private datatypes for reject statement. */
static const struct symbol_table icmpv6_code_tbl = {
.base = BASE_DECIMAL,
.symbols = {
@@ -892,16 +1078,17 @@ static const struct symbol_table icmpv6_code_tbl = {
},
};
-const struct datatype icmpv6_code_type = {
- .type = TYPE_ICMPV6_CODE,
+/* private datatype for reject statement. */
+const struct datatype reject_icmpv6_code_type = {
.name = "icmpv6_code",
- .desc = "icmpv6 code",
+ .desc = "reject icmpv6 code",
.size = BITS_PER_BYTE,
.byteorder = BYTEORDER_BIG_ENDIAN,
.basetype = &integer_type,
.sym_tbl = &icmpv6_code_tbl,
};
+/* symbol table for private datatypes for reject statement. */
static const struct symbol_table icmpx_code_tbl = {
.base = BASE_DECIMAL,
.symbols = {
@@ -913,6 +1100,60 @@ static const struct symbol_table icmpx_code_tbl = {
},
};
+/* private datatype for reject statement. */
+const struct datatype reject_icmpx_code_type = {
+ .name = "icmpx_code",
+ .desc = "reject icmpx code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .sym_tbl = &icmpx_code_tbl,
+};
+
+/* Backward compatible parser for the reject statement. */
+static struct error_record *icmp_code_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(ctx, sym, &icmp_code_tbl, res);
+}
+
+const struct datatype icmp_code_type = {
+ .type = TYPE_ICMP_CODE,
+ .name = "icmp_code",
+ .desc = "icmp code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .parse = icmp_code_parse,
+};
+
+/* Backward compatible parser for the reject statement. */
+static struct error_record *icmpv6_code_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(ctx, sym, &icmpv6_code_tbl, res);
+}
+
+const struct datatype icmpv6_code_type = {
+ .type = TYPE_ICMPV6_CODE,
+ .name = "icmpv6_code",
+ .desc = "icmpv6 code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .parse = icmpv6_code_parse,
+};
+
+/* Backward compatible parser for the reject statement. */
+static struct error_record *icmpx_code_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(ctx, sym, &icmpx_code_tbl, res);
+}
+
const struct datatype icmpx_code_type = {
.type = TYPE_ICMPX_CODE,
.name = "icmpx_code",
@@ -920,12 +1161,13 @@ const struct datatype icmpx_code_type = {
.size = BITS_PER_BYTE,
.byteorder = BYTEORDER_BIG_ENDIAN,
.basetype = &integer_type,
- .sym_tbl = &icmpx_code_tbl,
+ .parse = icmpx_code_parse,
};
void time_print(uint64_t ms, struct output_ctx *octx)
{
uint64_t days, hours, minutes, seconds;
+ bool printed = false;
if (nft_output_seconds(octx)) {
nft_print(octx, "%" PRIu64 "s", ms / 1000);
@@ -944,16 +1186,29 @@ void time_print(uint64_t ms, struct output_ctx *octx)
seconds = ms / 1000;
ms %= 1000;
- if (days > 0)
+ if (days > 0) {
nft_print(octx, "%" PRIu64 "d", days);
- if (hours > 0)
+ printed = true;
+ }
+ if (hours > 0) {
nft_print(octx, "%" PRIu64 "h", hours);
- if (minutes > 0)
+ printed = true;
+ }
+ if (minutes > 0) {
nft_print(octx, "%" PRIu64 "m", minutes);
- if (seconds > 0)
+ printed = true;
+ }
+ if (seconds > 0) {
nft_print(octx, "%" PRIu64 "s", seconds);
- if (ms > 0)
+ printed = true;
+ }
+ if (ms > 0) {
nft_print(octx, "%" PRIu64 "ms", ms);
+ printed = true;
+ }
+
+ if (!printed)
+ nft_print(octx, "0s");
}
enum {
@@ -1105,47 +1360,55 @@ static struct error_record *concat_type_parse(struct parse_ctx *ctx,
sym->dtype->desc);
}
-static struct datatype *dtype_alloc(void)
+static struct datatype *datatype_alloc(void)
{
struct datatype *dtype;
dtype = xzalloc(sizeof(*dtype));
- dtype->flags = DTYPE_F_ALLOC;
+ dtype->alloc = 1;
+ dtype->refcnt = 1;
return dtype;
}
-struct datatype *datatype_get(const struct datatype *ptr)
+const struct datatype *datatype_get(const struct datatype *ptr)
{
struct datatype *dtype = (struct datatype *)ptr;
if (!dtype)
return NULL;
- if (!(dtype->flags & DTYPE_F_ALLOC))
+ if (!dtype->alloc)
return dtype;
dtype->refcnt++;
return dtype;
}
+void __datatype_set(struct expr *expr, const struct datatype *dtype)
+{
+ const struct datatype *dtype_free;
+
+ dtype_free = expr->dtype;
+ expr->dtype = dtype;
+ datatype_free(dtype_free);
+}
+
void datatype_set(struct expr *expr, const struct datatype *dtype)
{
- if (dtype == expr->dtype)
- return;
- datatype_free(expr->dtype);
- expr->dtype = datatype_get(dtype);
+ if (dtype != expr->dtype)
+ __datatype_set(expr, datatype_get(dtype));
}
-struct datatype *dtype_clone(const struct datatype *orig_dtype)
+struct datatype *datatype_clone(const struct datatype *orig_dtype)
{
struct datatype *dtype;
- dtype = xzalloc(sizeof(*dtype));
+ dtype = xmalloc(sizeof(*dtype));
*dtype = *orig_dtype;
dtype->name = xstrdup(orig_dtype->name);
dtype->desc = xstrdup(orig_dtype->desc);
- dtype->flags = DTYPE_F_ALLOC | orig_dtype->flags;
- dtype->refcnt = 0;
+ dtype->alloc = 1;
+ dtype->refcnt = 1;
return dtype;
}
@@ -1156,14 +1419,17 @@ void datatype_free(const struct datatype *ptr)
if (!dtype)
return;
- if (!(dtype->flags & DTYPE_F_ALLOC))
+ if (!dtype->alloc)
return;
+
+ assert(dtype->refcnt != 0);
+
if (--dtype->refcnt > 0)
return;
- xfree(dtype->name);
- xfree(dtype->desc);
- xfree(dtype);
+ free_const(dtype->name);
+ free_const(dtype->desc);
+ free(dtype);
}
const struct datatype *concat_type_alloc(uint32_t type)
@@ -1192,7 +1458,7 @@ const struct datatype *concat_type_alloc(uint32_t type)
}
strncat(desc, ")", sizeof(desc) - strlen(desc) - 1);
- dtype = dtype_alloc();
+ dtype = datatype_alloc();
dtype->type = type;
dtype->size = size;
dtype->subtypes = subtypes;
@@ -1204,15 +1470,15 @@ const struct datatype *concat_type_alloc(uint32_t type)
}
const struct datatype *set_datatype_alloc(const struct datatype *orig_dtype,
- unsigned int byteorder)
+ enum byteorder byteorder)
{
struct datatype *dtype;
/* Restrict dynamic datatype allocation to generic integer datatype. */
if (orig_dtype != &integer_type)
- return orig_dtype;
+ return datatype_get(orig_dtype);
- dtype = dtype_clone(orig_dtype);
+ dtype = datatype_clone(orig_dtype);
dtype->byteorder = byteorder;
return dtype;
@@ -1232,7 +1498,7 @@ static struct error_record *time_unit_parse(const struct location *loc,
else if (strcmp(str, "week") == 0)
*unit = 1ULL * 60 * 60 * 24 * 7;
else
- return error(loc, "Wrong rate format");
+ return error(loc, "Wrong time format, expecting second, minute, hour, day or week");
return NULL;
}
@@ -1240,14 +1506,14 @@ static struct error_record *time_unit_parse(const struct location *loc,
struct error_record *data_unit_parse(const struct location *loc,
const char *str, uint64_t *rate)
{
- if (strncmp(str, "bytes", strlen("bytes")) == 0)
+ if (strcmp(str, "bytes") == 0)
*rate = 1ULL;
- else if (strncmp(str, "kbytes", strlen("kbytes")) == 0)
+ else if (strcmp(str, "kbytes") == 0)
*rate = 1024;
- else if (strncmp(str, "mbytes", strlen("mbytes")) == 0)
+ else if (strcmp(str, "mbytes") == 0)
*rate = 1024 * 1024;
else
- return error(loc, "Wrong rate format");
+ return error(loc, "Wrong unit format, expecting bytes, kbytes or mbytes");
return NULL;
}
@@ -1255,14 +1521,20 @@ struct error_record *data_unit_parse(const struct location *loc,
struct error_record *rate_parse(const struct location *loc, const char *str,
uint64_t *rate, uint64_t *unit)
{
+ const char *slash, *rate_str;
struct error_record *erec;
- const char *slash;
slash = strchr(str, '/');
if (!slash)
- return error(loc, "wrong rate format");
+ return error(loc, "wrong rate format, expecting {bytes,kbytes,mbytes}/{second,minute,hour,day,week}");
+
+ rate_str = strndup(str, slash - str);
+ if (!rate_str)
+ memory_allocation_error();
+
+ erec = data_unit_parse(loc, rate_str, rate);
+ free_const(rate_str);
- erec = data_unit_parse(loc, str, rate);
if (erec != NULL)
return erec;
@@ -1282,11 +1554,35 @@ static const struct symbol_table boolean_tbl = {
},
};
+static struct error_record *boolean_type_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ struct error_record *erec;
+ int num;
+
+ erec = integer_type_parse(ctx, sym, res);
+ if (erec)
+ return erec;
+
+ if (mpz_cmp_ui((*res)->value, 0))
+ num = 1;
+ else
+ num = 0;
+
+ expr_free(*res);
+
+ *res = constant_expr_alloc(&sym->location, &boolean_type,
+ BYTEORDER_HOST_ENDIAN, 1, &num);
+ return NULL;
+}
+
const struct datatype boolean_type = {
.type = TYPE_BOOLEAN,
.name = "boolean",
.desc = "boolean type",
.size = 1,
+ .parse = boolean_type_parse,
.basetype = &integer_type,
.sym_tbl = &boolean_tbl,
.json = boolean_type_json,
@@ -1360,10 +1656,10 @@ const struct datatype policy_type = {
#define SYSFS_CGROUPSV2_PATH "/sys/fs/cgroup"
-static const char *cgroupv2_get_path(const char *path, uint64_t id)
+static char *cgroupv2_get_path(const char *path, uint64_t id)
{
- const char *cgroup_path = NULL;
char dent_name[PATH_MAX + 1];
+ char *cgroup_path = NULL;
struct dirent *dent;
struct stat st;
DIR *d;
@@ -1401,7 +1697,7 @@ static void cgroupv2_type_print(const struct expr *expr,
struct output_ctx *octx)
{
uint64_t id = mpz_get_uint64(expr->value);
- const char *cgroup_path;
+ char *cgroup_path;
cgroup_path = cgroupv2_get_path(SYSFS_CGROUPSV2_PATH, id);
if (cgroup_path)
@@ -1410,7 +1706,7 @@ static void cgroupv2_type_print(const struct expr *expr,
else
nft_print(octx, "%" PRIu64, id);
- xfree(cgroup_path);
+ free(cgroup_path);
}
static struct error_record *cgroupv2_type_parse(struct parse_ctx *ctx,
diff --git a/src/dccpopt.c b/src/dccpopt.c
new file mode 100644
index 00000000..ebb645a9
--- /dev/null
+++ b/src/dccpopt.c
@@ -0,0 +1,277 @@
+#include <nft.h>
+
+#include <stddef.h>
+
+#include <datatype.h>
+#include <dccpopt.h>
+#include <expression.h>
+#include <nftables.h>
+#include <utils.h>
+
+#define PHT(__token, __offset, __len) \
+ PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \
+ __offset, __len)
+
+static const struct proto_hdr_template dccpopt_unknown_template =
+ PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0);
+
+/*
+ * Option DCCP- Section
+ * Type Length Meaning Data? Reference
+ * ---- ------ ------- ----- ---------
+ * 0 1 Padding Y 5.8.1
+ * 1 1 Mandatory N 5.8.2
+ * 2 1 Slow Receiver Y 11.6
+ * 3-31 1 Reserved
+ * 32 variable Change L N 6.1
+ * 33 variable Confirm L N 6.2
+ * 34 variable Change R N 6.1
+ * 35 variable Confirm R N 6.2
+ * 36 variable Init Cookie N 8.1.4
+ * 37 3-8 NDP Count Y 7.7
+ * 38 variable Ack Vector [Nonce 0] N 11.4
+ * 39 variable Ack Vector [Nonce 1] N 11.4
+ * 40 variable Data Dropped N 11.7
+ * 41 6 Timestamp Y 13.1
+ * 42 6/8/10 Timestamp Echo Y 13.3
+ * 43 4/6 Elapsed Time N 13.2
+ * 44 6 Data Checksum Y 9.3
+ * 45-127 variable Reserved
+ * 128-255 variable CCID-specific options - 10.3
+ */
+
+static const struct exthdr_desc dccpopt_padding = {
+ .name = "padding",
+ .type = DCCPOPT_PADDING,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_mandatory = {
+ .name = "mandatory",
+ .type = DCCPOPT_MANDATORY,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_slow_receiver = {
+ .name = "slow_receiver",
+ .type = DCCPOPT_SLOW_RECEIVER,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_reserved_short = {
+ .name = "reserved_short",
+ .type = DCCPOPT_RESERVED_SHORT,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_change_l = {
+ .name = "change_l",
+ .type = DCCPOPT_CHANGE_L,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8)
+ },
+};
+
+static const struct exthdr_desc dccpopt_confirm_l = {
+ .name = "confirm_l",
+ .type = DCCPOPT_CONFIRM_L,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_change_r = {
+ .name = "change_r",
+ .type = DCCPOPT_CHANGE_R,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_confirm_r = {
+ .name = "confirm_r",
+ .type = DCCPOPT_CONFIRM_R,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_init_cookie = {
+ .name = "init_cookie",
+ .type = DCCPOPT_INIT_COOKIE,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ndp_count = {
+ .name = "ndp_count",
+ .type = DCCPOPT_NDP_COUNT,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ack_vector_nonce_0 = {
+ .name = "ack_vector_nonce_0",
+ .type = DCCPOPT_ACK_VECTOR_NONCE_0,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ack_vector_nonce_1 = {
+ .name = "ack_vector_nonce_1",
+ .type = DCCPOPT_ACK_VECTOR_NONCE_1,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_data_dropped = {
+ .name = "data_dropped",
+ .type = DCCPOPT_DATA_DROPPED,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_timestamp = {
+ .name = "timestamp",
+ .type = DCCPOPT_TIMESTAMP,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_timestamp_echo = {
+ .name = "timestamp_echo",
+ .type = DCCPOPT_TIMESTAMP_ECHO,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_elapsed_time = {
+ .name = "elapsed_time",
+ .type = DCCPOPT_ELAPSED_TIME,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_data_checksum = {
+ .name = "data_checksum",
+ .type = DCCPOPT_DATA_CHECKSUM,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_reserved_long = {
+ .name = "reserved_long",
+ .type = DCCPOPT_RESERVED_LONG,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ccid_specific = {
+ .name = "ccid_specific",
+ .type = DCCPOPT_CCID_SPECIFIC,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+const struct exthdr_desc *dccpopt_protocols[1 + UINT8_MAX] = {
+ [DCCPOPT_PADDING] = &dccpopt_padding,
+ [DCCPOPT_MANDATORY] = &dccpopt_mandatory,
+ [DCCPOPT_SLOW_RECEIVER] = &dccpopt_slow_receiver,
+ [DCCPOPT_RESERVED_SHORT] = &dccpopt_reserved_short,
+ [DCCPOPT_CHANGE_L] = &dccpopt_change_l,
+ [DCCPOPT_CONFIRM_L] = &dccpopt_confirm_l,
+ [DCCPOPT_CHANGE_R] = &dccpopt_change_r,
+ [DCCPOPT_CONFIRM_R] = &dccpopt_confirm_r,
+ [DCCPOPT_INIT_COOKIE] = &dccpopt_init_cookie,
+ [DCCPOPT_NDP_COUNT] = &dccpopt_ndp_count,
+ [DCCPOPT_ACK_VECTOR_NONCE_0] = &dccpopt_ack_vector_nonce_0,
+ [DCCPOPT_ACK_VECTOR_NONCE_1] = &dccpopt_ack_vector_nonce_1,
+ [DCCPOPT_DATA_DROPPED] = &dccpopt_data_dropped,
+ [DCCPOPT_TIMESTAMP] = &dccpopt_timestamp,
+ [DCCPOPT_TIMESTAMP_ECHO] = &dccpopt_timestamp_echo,
+ [DCCPOPT_ELAPSED_TIME] = &dccpopt_elapsed_time,
+ [DCCPOPT_DATA_CHECKSUM] = &dccpopt_data_checksum,
+ [DCCPOPT_RESERVED_LONG] = &dccpopt_reserved_long,
+ [DCCPOPT_CCID_SPECIFIC] = &dccpopt_ccid_specific,
+};
+
+const struct exthdr_desc *
+dccpopt_find_desc(uint8_t type)
+{
+ enum dccpopt_types proto_idx =
+ 3 <= type && type <= 31 ? DCCPOPT_RESERVED_SHORT :
+ 45 <= type && type <= 127 ? DCCPOPT_RESERVED_LONG :
+ 128 <= type ? DCCPOPT_CCID_SPECIFIC : type;
+
+ return dccpopt_protocols[proto_idx];
+}
+
+struct expr *
+dccpopt_expr_alloc(const struct location *loc, uint8_t type)
+{
+ const struct proto_hdr_template *tmpl;
+ const struct exthdr_desc *desc;
+ struct expr *expr;
+
+ desc = dccpopt_find_desc(type);
+ tmpl = &desc->templates[DCCPOPT_FIELD_TYPE];
+
+ expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE);
+ expr->exthdr.desc = desc;
+ expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
+ expr->exthdr.raw_type = type;
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ expr->exthdr.op = NFT_EXTHDR_OP_DCCP;
+
+ return expr;
+}
+
+void
+dccpopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset,
+ unsigned int len)
+{
+ const struct proto_hdr_template *tmpl;
+ const struct exthdr_desc *desc;
+
+ assert(expr->etype == EXPR_EXTHDR);
+
+ desc = dccpopt_find_desc(type);
+ tmpl = &desc->templates[DCCPOPT_FIELD_TYPE];
+
+ expr->len = len;
+ datatype_set(expr, &boolean_type);
+
+ expr->exthdr.offset = offset;
+ expr->exthdr.desc = desc;
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ expr->exthdr.op = NFT_EXTHDR_OP_DCCP;
+
+ /* Make sure that it's the right template based on offset and
+ * len
+ */
+ if (tmpl->offset != offset || tmpl->len != len)
+ expr->exthdr.tmpl = &dccpopt_unknown_template;
+ else
+ expr->exthdr.tmpl = tmpl;
+}
diff --git a/src/erec.c b/src/erec.c
index a4b93fb0..fe66abbe 100644
--- a/src/erec.c
+++ b/src/erec.c
@@ -8,12 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#define _GNU_SOURCE
-#include <config.h>
+#include <nft.h>
+
#include <stdio.h>
-#include <string.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <netlink.h>
#include <gmputil.h>
@@ -45,8 +43,8 @@ void erec_add_location(struct error_record *erec, const struct location *loc)
void erec_destroy(struct error_record *erec)
{
- xfree(erec->msg);
- xfree(erec);
+ free(erec->msg);
+ free(erec);
}
__attribute__((format(printf, 3, 0)))
@@ -170,6 +168,8 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
fprintf(f, "%s\n", erec->msg);
for (l = 0; l < (int)erec->num_locations; l++) {
loc = &erec->locations[l];
+ if (!loc->nle)
+ continue;
netlink_dump_expr(loc->nle, f, debug_mask);
}
return;
@@ -203,7 +203,7 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
}
pbuf[end] = '\0';
fprintf(f, "%s", pbuf);
- xfree(pbuf);
+ free(pbuf);
}
fprintf(f, "\n");
}
diff --git a/src/evaluate.c b/src/evaluate.c
index 919c38c5..a2d5d7c2 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -8,11 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
#include <linux/netfilter_arp.h>
@@ -39,6 +38,13 @@
#include <utils.h>
#include <xt.h>
+struct proto_ctx *eval_proto_ctx(struct eval_ctx *ctx)
+{
+ uint8_t idx = ctx->inner_desc ? 1 : 0;
+
+ return &ctx->_pctx[idx];
+}
+
static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr);
static const char * const byteorder_names[] = {
@@ -68,6 +74,33 @@ static int __fmtstring(3, 4) set_error(struct eval_ctx *ctx,
return -1;
}
+const char *stmt_name(const struct stmt *stmt)
+{
+ switch (stmt->type) {
+ case STMT_NAT:
+ switch (stmt->nat.type) {
+ case NFT_NAT_SNAT:
+ return "snat";
+ case NFT_NAT_DNAT:
+ return "dnat";
+ case NFT_NAT_REDIR:
+ return "redirect";
+ case NFT_NAT_MASQ:
+ return "masquerade";
+ }
+ break;
+ default:
+ break;
+ }
+
+ return stmt_ops(stmt)->name;
+}
+
+static int stmt_error_range(struct eval_ctx *ctx, const struct stmt *stmt, const struct expr *e)
+{
+ return expr_error(ctx->msgs, e, "%s: range argument not supported", stmt_name(stmt));
+}
+
static void key_fix_dtype_byteorder(struct expr *key)
{
const struct datatype *dtype = key->dtype;
@@ -75,7 +108,7 @@ static void key_fix_dtype_byteorder(struct expr *key)
if (dtype->byteorder == key->byteorder)
return;
- datatype_set(key, set_datatype_alloc(dtype, key->byteorder));
+ __datatype_set(key, set_datatype_alloc(dtype, key->byteorder));
}
static int set_evaluate(struct eval_ctx *ctx, struct set *set);
@@ -83,27 +116,36 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
const char *name,
struct expr *key,
struct expr *data,
- struct expr *expr)
+ struct expr *expr,
+ uint32_t flags)
{
struct cmd *cmd;
struct set *set;
struct handle h;
- if (set_is_datamap(expr->set_flags))
+ if (set_is_datamap(flags))
key_fix_dtype_byteorder(key);
set = set_alloc(&expr->location);
- set->flags = NFT_SET_ANONYMOUS | expr->set_flags;
+ set->flags = flags;
set->handle.set.name = xstrdup(name);
set->key = key;
set->data = data;
set->init = expr;
- set->automerge = set->flags & NFT_SET_INTERVAL;
+ set->automerge = flags & NFT_SET_INTERVAL;
+
+ handle_merge(&set->handle, &ctx->cmd->handle);
+
+ if (set_evaluate(ctx, set) < 0) {
+ if (set->flags & (NFT_SET_MAP|NFT_SET_OBJECT))
+ set->init = NULL;
+ set_free(set);
+ return NULL;
+ }
if (ctx->table != NULL)
list_add_tail(&set->list, &ctx->table->sets);
else {
- handle_merge(&set->handle, &ctx->cmd->handle);
memset(&h, 0, sizeof(h));
handle_merge(&h, &set->handle);
h.set.location = expr->location;
@@ -112,8 +154,6 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
list_add_tail(&cmd->list, &ctx->cmd->list);
}
- set_evaluate(ctx, set);
-
return set_ref_expr_alloc(&expr->location, set);
}
@@ -148,8 +188,38 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
return 0;
/* Conversion for EXPR_CONCAT is handled for single composing ranges */
- if ((*expr)->etype == EXPR_CONCAT)
+ if ((*expr)->etype == EXPR_CONCAT) {
+ struct expr *i, *next, *unary;
+
+ list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
+ if (i->byteorder == BYTEORDER_BIG_ENDIAN)
+ continue;
+
+ basetype = expr_basetype(i)->type;
+ if (basetype == TYPE_STRING)
+ continue;
+
+ assert(basetype == TYPE_INTEGER);
+
+ switch (i->etype) {
+ case EXPR_VALUE:
+ if (i->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(i->value, div_round_up(i->len, BITS_PER_BYTE));
+ break;
+ default:
+ if (div_round_up(i->len, BITS_PER_BYTE) >= 2) {
+ op = byteorder_conversion_op(i, byteorder);
+ unary = unary_expr_alloc(&i->location, op, i);
+ if (expr_evaluate(ctx, &unary) < 0)
+ return -1;
+
+ list_replace(&i->list, &unary->list);
+ }
+ }
+ }
+
return 0;
+ }
basetype = expr_basetype(*expr)->type;
switch (basetype) {
@@ -165,7 +235,7 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
byteorder_names[(*expr)->byteorder]);
}
- if (expr_is_constant(*expr) || (*expr)->len / BITS_PER_BYTE < 2)
+ if (expr_is_constant(*expr) || div_round_up((*expr)->len, BITS_PER_BYTE) < 2)
(*expr)->byteorder = byteorder;
else {
op = byteorder_conversion_op(*expr, byteorder);
@@ -186,7 +256,7 @@ static int table_not_found(struct eval_ctx *ctx)
"%s", strerror(ENOENT));
return cmd_error(ctx, &ctx->cmd->handle.table.location,
- "%s; did you mean table ‘%s’ in family %s?",
+ "%s; did you mean table '%s' in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
}
@@ -202,7 +272,7 @@ static int chain_not_found(struct eval_ctx *ctx)
"%s", strerror(ENOENT));
return cmd_error(ctx, &ctx->cmd->handle.chain.location,
- "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean chain '%s' in table %s '%s'?",
strerror(ENOENT), chain->handle.chain.name,
family2str(chain->handle.family),
table->handle.table.name);
@@ -219,7 +289,7 @@ static int set_not_found(struct eval_ctx *ctx, const struct location *loc,
return cmd_error(ctx, loc, "%s", strerror(ENOENT));
return cmd_error(ctx, loc,
- "%s; did you mean %s ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean %s '%s' in table %s '%s'?",
strerror(ENOENT),
set_is_map(set->flags) ? "map" : "set",
set->handle.set.name,
@@ -238,7 +308,7 @@ static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc,
return cmd_error(ctx, loc, "%s", strerror(ENOENT));
return cmd_error(ctx, loc,
- "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean flowtable '%s' in table %s '%s'?",
strerror(ENOENT), ft->handle.flowtable.name,
family2str(ft->handle.family),
table->handle.table.name);
@@ -249,7 +319,10 @@ static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc,
*/
static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
{
- struct parse_ctx parse_ctx = { .tbl = &ctx->nft->output.tbl, };
+ struct parse_ctx parse_ctx = {
+ .tbl = &ctx->nft->output.tbl,
+ .input = &ctx->nft->input,
+ };
struct error_record *erec;
struct table *table;
struct set *set;
@@ -361,6 +434,7 @@ static int expr_evaluate_integer(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp;
char *valstr, *rangestr;
+ uint32_t masklen;
mpz_t mask;
if (ctx->ectx.maxval > 0 &&
@@ -369,24 +443,29 @@ static int expr_evaluate_integer(struct eval_ctx *ctx, struct expr **exprp)
expr_error(ctx->msgs, expr,
"Value %s exceeds valid range 0-%u",
valstr, ctx->ectx.maxval);
- free(valstr);
+ nft_gmp_free(valstr);
return -1;
}
- mpz_init_bitmask(mask, ctx->ectx.len);
+ if (ctx->stmt_len > ctx->ectx.len)
+ masklen = ctx->stmt_len;
+ else
+ masklen = ctx->ectx.len;
+
+ mpz_init_bitmask(mask, masklen);
if (mpz_cmp(expr->value, mask) > 0) {
valstr = mpz_get_str(NULL, 10, expr->value);
rangestr = mpz_get_str(NULL, 10, mask);
expr_error(ctx->msgs, expr,
"Value %s exceeds valid range 0-%s",
valstr, rangestr);
- free(valstr);
- free(rangestr);
+ nft_gmp_free(valstr);
+ nft_gmp_free(rangestr);
mpz_clear(mask);
return -1;
}
expr->byteorder = ctx->ectx.byteorder;
- expr->len = ctx->ectx.len;
+ expr->len = masklen;
mpz_clear(mask);
return 0;
}
@@ -403,7 +482,8 @@ static int expr_evaluate_value(struct eval_ctx *ctx, struct expr **expr)
return -1;
break;
default:
- BUG("invalid basetype %s\n", expr_basetype(*expr)->name);
+ return expr_error(ctx->msgs, *expr, "Unexpected datatype %s",
+ (*expr)->dtype->name);
}
return 0;
}
@@ -418,20 +498,34 @@ static int expr_evaluate_primary(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+int stmt_dependency_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ uint32_t stmt_len = ctx->stmt_len;
+
+ if (stmt_evaluate(ctx, stmt) < 0)
+ return stmt_error(ctx, stmt, "dependency statement is invalid");
+
+ ctx->stmt_len = stmt_len;
+
+ return 0;
+}
+
static int
-conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
- const struct expr *expr,
- struct stmt **res)
+ll_conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
+ const struct expr *expr,
+ struct stmt **res)
{
enum proto_bases base = expr->payload.base;
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc = NULL;
struct expr *dep, *left, *right;
+ struct proto_ctx *pctx;
struct stmt *stmt;
assert(expr->payload.base == PROTO_BASE_LL_HDR);
- desc = ctx->pctx.protocol[base].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[base].desc;
tmpl = &desc->templates[desc->protocol_key];
left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
@@ -441,10 +535,13 @@ conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
stmt = expr_stmt_alloc(&dep->location, dep);
- if (stmt_evaluate(ctx, stmt) < 0)
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
return expr_error(ctx->msgs, expr,
"dependency statement is invalid");
+ if (ctx->inner_desc)
+ left->payload.inner_desc = ctx->inner_desc;
+
*res = stmt;
return 0;
}
@@ -465,13 +562,21 @@ static uint8_t expr_offset_shift(const struct expr *expr, unsigned int offset,
return shift;
}
-static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
+static int expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp, *and, *mask, *rshift, *off;
unsigned masklen, len = expr->len, extra_len = 0;
+ enum byteorder byteorder;
uint8_t shift;
mpz_t bitmask;
+ /* payload statement with relational expression as a value does not
+ * require the transformations that are needed for payload matching,
+ * skip this.
+ */
+ if (ctx->stmt && ctx->stmt->type == STMT_PAYLOAD)
+ return 0;
+
switch (expr->etype) {
case EXPR_PAYLOAD:
shift = expr_offset_shift(expr, expr->payload.offset,
@@ -486,7 +591,10 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
}
masklen = len + shift;
- assert(masklen <= NFT_REG_SIZE * BITS_PER_BYTE);
+
+ if (masklen > NFT_REG_SIZE * BITS_PER_BYTE)
+ return expr_error(ctx->msgs, expr, "mask length %u exceeds allowed maximum of %u\n",
+ masklen, NFT_REG_SIZE * BITS_PER_BYTE);
mpz_init2(bitmask, masklen);
mpz_bitmask(bitmask, len);
@@ -503,6 +611,16 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
and->len = masklen;
if (shift) {
+ if ((ctx->ectx.key || ctx->stmt_len > 0) &&
+ div_round_up(masklen, BITS_PER_BYTE) > 1) {
+ int op = byteorder_conversion_op(expr, BYTEORDER_HOST_ENDIAN);
+ and = unary_expr_alloc(&expr->location, op, and);
+ and->len = masklen;
+ byteorder = BYTEORDER_HOST_ENDIAN;
+ } else {
+ byteorder = expr->byteorder;
+ }
+
off = constant_expr_alloc(&expr->location,
expr_basetype(expr),
BYTEORDER_HOST_ENDIAN,
@@ -510,7 +628,7 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
rshift = binop_expr_alloc(&expr->location, OP_RSHIFT, and, off);
rshift->dtype = expr->dtype;
- rshift->byteorder = expr->byteorder;
+ rshift->byteorder = byteorder;
rshift->len = masklen;
*exprp = rshift;
@@ -519,10 +637,13 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
if (extra_len)
expr->len += extra_len;
+
+ return 0;
}
static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
{
+ const struct expr *key = ctx->ectx.key;
struct expr *expr = *exprp;
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
@@ -531,9 +652,15 @@ static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
if (expr_evaluate_primary(ctx, exprp) < 0)
return -1;
+ ctx->ectx.key = key;
+
if (expr->exthdr.offset % BITS_PER_BYTE != 0 ||
- expr->len % BITS_PER_BYTE != 0)
- expr_evaluate_bits(ctx, exprp);
+ expr->len % BITS_PER_BYTE != 0) {
+ int err = expr_evaluate_bits(ctx, exprp);
+
+ if (err)
+ return err;
+ }
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT: {
@@ -577,11 +704,13 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
const struct proto_desc *base, *dependency = NULL;
enum proto_bases pb = PROTO_BASE_NETWORK_HDR;
struct expr *expr = *exprp;
+ struct proto_ctx *pctx;
struct stmt *nstmt;
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT:
case NFT_EXTHDR_OP_SCTP:
+ case NFT_EXTHDR_OP_DCCP:
return __expr_evaluate_exthdr(ctx, exprp);
case NFT_EXTHDR_OP_IPV4:
dependency = &proto_ip;
@@ -594,7 +723,8 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
assert(dependency);
- base = ctx->pctx.protocol[pb].desc;
+ pctx = eval_proto_ctx(ctx);
+ base = pctx->protocol[pb].desc;
if (base == dependency)
return __expr_evaluate_exthdr(ctx, exprp);
@@ -638,9 +768,11 @@ static int meta_iiftype_gen_dependency(struct eval_ctx *ctx,
"for this family");
nstmt = meta_stmt_meta_iiftype(&payload->location, type);
- if (stmt_evaluate(ctx, nstmt) < 0)
- return expr_error(ctx->msgs, payload,
- "dependency statement is invalid");
+ if (stmt_dependency_evaluate(ctx, nstmt) < 0)
+ return -1;
+
+ if (ctx->inner_desc)
+ nstmt->expr->left->meta.inner_desc = ctx->inner_desc;
*res = nstmt;
return 0;
@@ -651,50 +783,90 @@ static bool proto_is_dummy(const struct proto_desc *desc)
return desc == &proto_inet || desc == &proto_netdev;
}
-static int resolve_protocol_conflict(struct eval_ctx *ctx,
- const struct proto_desc *desc,
- struct expr *payload)
+static int stmt_dep_conflict(struct eval_ctx *ctx, const struct stmt *nstmt)
+{
+ struct stmt *stmt;
+
+ list_for_each_entry(stmt, &ctx->rule->stmts, list) {
+ if (stmt == nstmt)
+ break;
+
+ if (stmt->type != STMT_EXPRESSION ||
+ stmt->expr->etype != EXPR_RELATIONAL ||
+ stmt->expr->right->etype != EXPR_VALUE ||
+ stmt->expr->left->etype != EXPR_PAYLOAD ||
+ stmt->expr->left->etype != nstmt->expr->left->etype ||
+ stmt->expr->left->len != nstmt->expr->left->len)
+ continue;
+
+ if (stmt->expr->left->payload.desc != nstmt->expr->left->payload.desc ||
+ stmt->expr->left->payload.inner_desc != nstmt->expr->left->payload.inner_desc ||
+ stmt->expr->left->payload.base != nstmt->expr->left->payload.base ||
+ stmt->expr->left->payload.offset != nstmt->expr->left->payload.offset)
+ continue;
+
+ return stmt_binary_error(ctx, stmt, nstmt,
+ "conflicting statements");
+ }
+
+ return 0;
+}
+
+static int rule_stmt_dep_add(struct eval_ctx *ctx,
+ struct stmt *nstmt, struct stmt *stmt)
+{
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ if (stmt_dep_conflict(ctx, nstmt) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
+ const struct proto_desc *desc,
+ struct expr *payload)
{
enum proto_bases base = payload->payload.base;
struct stmt *nstmt = NULL;
+ struct proto_ctx *pctx;
+ unsigned int i;
int link, err;
- if (payload->payload.base == PROTO_BASE_LL_HDR) {
- if (proto_is_dummy(desc)) {
+ assert(base == PROTO_BASE_LL_HDR);
+
+ pctx = eval_proto_ctx(ctx);
+
+ if (proto_is_dummy(desc)) {
+ if (ctx->inner_desc) {
+ proto_ctx_update(pctx, PROTO_BASE_LL_HDR, &payload->location, &proto_eth);
+ } else {
err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
if (err < 0)
return err;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
- } else {
- unsigned int i;
-
- /* payload desc stored in the L2 header stack? No conflict. */
- for (i = 0; i < ctx->pctx.stacked_ll_count; i++) {
- if (ctx->pctx.stacked_ll[i] == payload->payload.desc)
- return 0;
- }
+ desc = payload->payload.desc;
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
+ }
+ } else {
+ /* payload desc stored in the L2 header stack? No conflict. */
+ for (i = 0; i < pctx->stacked_ll_count; i++) {
+ if (pctx->stacked_ll[i] == payload->payload.desc)
+ return 0;
}
}
- assert(base <= PROTO_BASE_MAX);
- /* This payload and the existing context don't match, conflict. */
- if (ctx->pctx.protocol[base + 1].desc != NULL)
- return 1;
-
link = proto_find_num(desc, payload->payload.desc);
if (link < 0 ||
- conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
+ ll_conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
return 1;
- if (base == PROTO_BASE_LL_HDR) {
- unsigned int i;
-
- for (i = 0; i < ctx->pctx.stacked_ll_count; i++)
- payload->payload.offset += ctx->pctx.stacked_ll[i]->length;
- }
+ for (i = 0; i < pctx->stacked_ll_count; i++)
+ payload->payload.offset += pctx->stacked_ll[i]->length;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}
@@ -709,25 +881,52 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
struct expr *payload = expr;
enum proto_bases base = payload->payload.base;
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
struct stmt *nstmt;
int err;
if (expr->etype == EXPR_PAYLOAD && expr->payload.is_raw)
return 0;
- desc = ctx->pctx.protocol[base].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[base].desc;
if (desc == NULL) {
if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
return -1;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
- desc = ctx->pctx.protocol[base].desc;
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
+
+ desc = pctx->protocol[base].desc;
+
+ if (desc == expr->payload.desc)
+ goto check_icmp;
+
+ if (base == PROTO_BASE_LL_HDR) {
+ int link;
+
+ link = proto_find_num(desc, payload->payload.desc);
+ if (link < 0 ||
+ ll_conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
+ return expr_error(ctx->msgs, payload,
+ "conflicting protocols specified: %s vs. %s",
+ desc->name,
+ payload->payload.desc->name);
+
+ assert(pctx->stacked_ll_count);
+ payload->payload.offset += pctx->stacked_ll[0]->length;
+
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
+
+ return 1;
+ }
goto check_icmp;
}
if (payload->payload.base == desc->base &&
- proto_ctx_is_ambiguous(&ctx->pctx, base)) {
- desc = proto_ctx_find_conflict(&ctx->pctx, base, payload->payload.desc);
+ proto_ctx_is_ambiguous(pctx, base)) {
+ desc = proto_ctx_find_conflict(pctx, base, payload->payload.desc);
assert(desc);
return expr_error(ctx->msgs, payload,
@@ -745,8 +944,8 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
if (desc->base == PROTO_BASE_LL_HDR) {
unsigned int i;
- for (i = 0; i < ctx->pctx.stacked_ll_count; i++)
- payload->payload.offset += ctx->pctx.stacked_ll[i]->length;
+ for (i = 0; i < pctx->stacked_ll_count; i++)
+ payload->payload.offset += pctx->stacked_ll[i]->length;
}
check_icmp:
if (desc != &proto_icmp && desc != &proto_icmp6)
@@ -760,26 +959,27 @@ check_icmp:
if (payload_gen_icmp_dependency(ctx, expr, &nstmt) < 0)
return -1;
- if (nstmt)
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (nstmt && rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}
/* If we already have context and this payload is on the same
* base, try to resolve the protocol conflict.
*/
- if (payload->payload.base == desc->base) {
- err = resolve_protocol_conflict(ctx, desc, payload);
+ if (base == PROTO_BASE_LL_HDR) {
+ err = resolve_ll_protocol_conflict(ctx, desc, payload);
if (err <= 0)
return err;
- desc = ctx->pctx.protocol[base].desc;
+ desc = pctx->protocol[base].desc;
if (desc == payload->payload.desc)
return 0;
}
return expr_error(ctx->msgs, payload,
- "conflicting protocols specified: %s vs. %s",
- ctx->pctx.protocol[base].desc->name,
+ "conflicting %s protocols specified: %s vs. %s",
+ proto_base_names[base],
+ pctx->protocol[base].desc->name,
payload->payload.desc->name);
}
@@ -791,6 +991,7 @@ static bool payload_needs_adjustment(const struct expr *expr)
static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
{
+ const struct expr *key = ctx->ectx.key;
struct expr *expr = *exprp;
if (expr->payload.evaluated)
@@ -802,14 +1003,82 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
if (expr_evaluate_primary(ctx, exprp) < 0)
return -1;
- if (payload_needs_adjustment(expr))
- expr_evaluate_bits(ctx, exprp);
+ ctx->ectx.key = key;
+
+ if (payload_needs_adjustment(expr)) {
+ int err = expr_evaluate_bits(ctx, exprp);
+
+ if (err)
+ return err;
+ }
expr->payload.evaluated = true;
return 0;
}
+static int expr_evaluate_inner(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ const struct proto_desc *desc = NULL;
+ struct expr *expr = *exprp;
+ int ret;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+
+ if (desc == NULL &&
+ expr->payload.inner_desc->base < PROTO_BASE_INNER_HDR) {
+ struct stmt *nstmt;
+
+ if (payload_gen_inner_dependency(ctx, expr, &nstmt) < 0)
+ return -1;
+
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
+
+ proto_ctx_update(pctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, expr->payload.inner_desc);
+ }
+
+ if (expr->payload.inner_desc->base == PROTO_BASE_INNER_HDR) {
+ desc = pctx->protocol[expr->payload.inner_desc->base - 1].desc;
+ if (!desc) {
+ return expr_error(ctx->msgs, expr,
+ "no transport protocol specified");
+ }
+
+ if (proto_find_num(desc, expr->payload.inner_desc) < 0) {
+ return expr_error(ctx->msgs, expr,
+ "unexpected transport protocol %s",
+ desc->name);
+ }
+
+ proto_ctx_update(pctx, expr->payload.inner_desc->base, &expr->location,
+ expr->payload.inner_desc);
+ }
+
+ if (expr->payload.base != PROTO_BASE_INNER_HDR)
+ ctx->inner_desc = expr->payload.inner_desc;
+
+ ret = expr_evaluate_payload(ctx, exprp);
+
+ return ret;
+}
+
+static int expr_evaluate_payload_inner(struct eval_ctx *ctx, struct expr **exprp)
+{
+ int ret;
+
+ if ((*exprp)->payload.inner_desc)
+ ret = expr_evaluate_inner(ctx, exprp);
+ else
+ ret = expr_evaluate_payload(ctx, exprp);
+
+ return ret;
+}
+
/*
* RT expression: validate protocol dependencies.
*/
@@ -817,20 +1086,22 @@ static int expr_evaluate_rt(struct eval_ctx *ctx, struct expr **expr)
{
static const char emsg[] = "cannot determine ip protocol version, use \"ip nexthop\" or \"ip6 nexthop\" instead";
struct expr *rt = *expr;
+ struct proto_ctx *pctx;
- rt_expr_update_type(&ctx->pctx, rt);
+ pctx = eval_proto_ctx(ctx);
+ rt_expr_update_type(pctx, rt);
switch (rt->rt.key) {
case NFT_RT_NEXTHOP4:
if (rt->dtype != &ipaddr_type)
return expr_error(ctx->msgs, rt, "%s", emsg);
- if (ctx->pctx.family == NFPROTO_IPV6)
+ if (pctx->family == NFPROTO_IPV6)
return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip");
break;
case NFT_RT_NEXTHOP6:
if (rt->dtype != &ip6addr_type)
return expr_error(ctx->msgs, rt, "%s", emsg);
- if (ctx->pctx.family == NFPROTO_IPV4)
+ if (pctx->family == NFPROTO_IPV4)
return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip6");
break;
default:
@@ -845,8 +1116,10 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
const struct proto_desc *base, *base_now;
struct expr *left, *right, *dep;
struct stmt *nstmt = NULL;
+ struct proto_ctx *pctx;
- base_now = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ pctx = eval_proto_ctx(ctx);
+ base_now = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
switch (ct->ct.nfproto) {
case NFPROTO_IPV4:
@@ -856,10 +1129,10 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
base = &proto_ip6;
break;
default:
- base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (base == &proto_ip)
ct->ct.nfproto = NFPROTO_IPV4;
- else if (base == &proto_ip)
+ else if (base == &proto_ip6)
ct->ct.nfproto = NFPROTO_IPV6;
if (base)
@@ -878,8 +1151,8 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
return expr_error(ctx->msgs, ct,
"conflicting dependencies: %s vs. %s\n",
base->name,
- ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name);
- switch (ctx->pctx.family) {
+ pctx->protocol[PROTO_BASE_NETWORK_HDR].desc->name);
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
return 0;
@@ -892,10 +1165,12 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
constant_data_ptr(ct->ct.nfproto, left->len));
dep = relational_expr_alloc(&ct->location, OP_EQ, left, right);
- relational_expr_pctx_update(&ctx->pctx, dep);
+ relational_expr_pctx_update(pctx, dep);
nstmt = expr_stmt_alloc(&dep->location, dep);
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}
@@ -908,13 +1183,16 @@ static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr)
{
const struct proto_desc *base, *error;
struct expr *ct = *expr;
+ struct proto_ctx *pctx;
- base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ pctx = eval_proto_ctx(ctx);
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
switch (ct->ct.key) {
case NFT_CT_SRC:
case NFT_CT_DST:
- ct_gen_nh_dependency(ctx, ct);
+ if (ct_gen_nh_dependency(ctx, ct) < 0)
+ return -1;
break;
case NFT_CT_SRC_IP:
case NFT_CT_DST_IP:
@@ -934,13 +1212,13 @@ static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr)
break;
}
- ct_expr_update_type(&ctx->pctx, ct);
+ ct_expr_update_type(pctx, ct);
return expr_evaluate_primary(ctx, expr);
err_conflict:
return stmt_binary_error(ctx, ct,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: %s vs. %s",
base->name, error->name);
}
@@ -987,7 +1265,6 @@ static int expr_evaluate_prefix(struct eval_ctx *ctx, struct expr **expr)
mpz_prefixmask(mask->value, base->len, prefix->prefix_len);
break;
case TYPE_STRING:
- mpz_init2(mask->value, base->len);
mpz_bitmask(mask->value, prefix->prefix_len);
break;
}
@@ -998,7 +1275,7 @@ static int expr_evaluate_prefix(struct eval_ctx *ctx, struct expr **expr)
base = prefix->prefix;
assert(expr_is_constant(base));
- prefix->dtype = base->dtype;
+ prefix->dtype = datatype_get(base->dtype);
prefix->byteorder = base->byteorder;
prefix->len = base->len;
prefix->flags |= EXPR_F_CONSTANT;
@@ -1037,12 +1314,12 @@ static int __expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
-static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
+static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **exprp)
{
- struct expr *range = *expr, *left, *right;
+ struct expr *range = *exprp, *left, *right;
int rc;
- rc = __expr_evaluate_range(ctx, expr);
+ rc = __expr_evaluate_range(ctx, exprp);
if (rc)
return rc;
@@ -1050,8 +1327,14 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
right = range->right;
if (mpz_cmp(left->value, right->value) > 0)
- return expr_error(ctx->msgs, range,
- "Range has zero or negative size");
+ return expr_error(ctx->msgs, range, "Range negative size");
+
+ if (mpz_cmp(left->value, right->value) == 0) {
+ *exprp = expr_get(left);
+ expr_free(range);
+ return 0;
+ }
+
datatype_set(range, left->dtype);
range->flags |= EXPR_F_CONSTANT;
return 0;
@@ -1063,12 +1346,10 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
*/
static int expr_evaluate_unary(struct eval_ctx *ctx, struct expr **expr)
{
- struct expr *unary = *expr, *arg;
+ struct expr *unary = *expr, *arg = unary->arg;
enum byteorder byteorder;
- if (expr_evaluate(ctx, &unary->arg) < 0)
- return -1;
- arg = unary->arg;
+ /* unary expression arguments has already been evaluated. */
assert(!expr_is_constant(arg));
assert(expr_basetype(arg)->type == TYPE_INTEGER);
@@ -1087,7 +1368,7 @@ static int expr_evaluate_unary(struct eval_ctx *ctx, struct expr **expr)
BUG("invalid unary operation %u\n", unary->op);
}
- unary->dtype = arg->dtype;
+ __datatype_set(unary, datatype_clone(arg->dtype));
unary->byteorder = byteorder;
unary->len = arg->len;
return 0;
@@ -1154,14 +1435,24 @@ static int constant_binop_simplify(struct eval_ctx *ctx, struct expr **expr)
static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *op = *expr, *left = op->left, *right = op->right;
+ unsigned int shift, max_shift_len;
+
+ /* mpz_get_uint32 has assert() for huge values */
+ if (mpz_cmp_ui(right->value, UINT_MAX) > 0)
+ return expr_binary_error(ctx->msgs, right, left,
+ "shifts exceeding %u bits are not supported", UINT_MAX);
- if (mpz_get_uint32(right->value) >= left->len)
+ shift = mpz_get_uint32(right->value);
+ if (ctx->stmt_len > left->len)
+ max_shift_len = ctx->stmt_len;
+ else
+ max_shift_len = left->len;
+
+ if (shift >= max_shift_len)
return expr_binary_error(ctx->msgs, right, left,
- "%s shift of %u bits is undefined "
- "for type of %u bits width",
+ "%s shift of %u bits is undefined for type of %u bits width",
op->op == OP_LSHIFT ? "Left" : "Right",
- mpz_get_uint32(right->value),
- left->len);
+ shift, max_shift_len);
/* Both sides need to be in host byte order */
if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
@@ -1170,9 +1461,9 @@ static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
if (byteorder_conversion(ctx, &op->right, BYTEORDER_HOST_ENDIAN) < 0)
return -1;
- op->dtype = &integer_type;
+ datatype_set(op, &integer_type);
op->byteorder = BYTEORDER_HOST_ENDIAN;
- op->len = left->len;
+ op->len = max_shift_len;
if (expr_is_constant(left))
return constant_binop_simplify(ctx, expr);
@@ -1182,41 +1473,82 @@ static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
static int expr_evaluate_bitwise(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *op = *expr, *left = op->left;
+ const struct datatype *dtype;
+ enum byteorder byteorder;
+ unsigned int max_len;
+
+ if (ctx->stmt_len > left->len) {
+ max_len = ctx->stmt_len;
+ byteorder = BYTEORDER_HOST_ENDIAN;
+ dtype = &integer_type;
- if (byteorder_conversion(ctx, &op->right, left->byteorder) < 0)
+ /* Both sides need to be in host byte order */
+ if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
+ return -1;
+
+ left = op->left;
+ } else {
+ max_len = left->len;
+ byteorder = left->byteorder;
+ dtype = left->dtype;
+ }
+
+ if (byteorder_conversion(ctx, &op->right, byteorder) < 0)
return -1;
- op->dtype = left->dtype;
- op->byteorder = left->byteorder;
- op->len = left->len;
+ datatype_set(op, dtype);
+ op->byteorder = byteorder;
+ op->len = max_len;
- if (expr_is_constant(left))
+ if (expr_is_constant(left) && expr_is_constant(op->right))
return constant_binop_simplify(ctx, expr);
return 0;
}
/*
- * Binop expression: both sides must be of integer base type. The left
- * hand side may be either constant or non-constant; in case its constant
- * it must be a singleton. The ride hand side must always be a constant
- * singleton.
+ * Binop expression: both sides must be of integer base type. The left-hand side
+ * may be either constant or non-constant; if it is constant, it must be a
+ * singleton. For bitwise operations, the right-hand side must be constant if
+ * the left-hand side is constant; the right-hand side may be constant or
+ * non-constant, if the left-hand side is non-constant; for shifts, the
+ * right-hand side must be constant; if it is constant, it must be a singleton.
*/
static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *op = *expr, *left, *right;
const char *sym = expr_op_symbols[op->op];
+ unsigned int max_shift_len = ctx->ectx.len;
+ int ret = -1;
+
+ if (ctx->recursion.binop >= USHRT_MAX)
+ return expr_binary_error(ctx->msgs, op, NULL,
+ "Binary operation limit %u reached ",
+ ctx->recursion.binop);
+ ctx->recursion.binop++;
if (expr_evaluate(ctx, &op->left) < 0)
return -1;
left = op->left;
- if (op->op == OP_LSHIFT || op->op == OP_RSHIFT)
+ if (op->op == OP_LSHIFT || op->op == OP_RSHIFT) {
+ if (left->len > max_shift_len)
+ max_shift_len = left->len;
+
__expr_set_context(&ctx->ectx, &integer_type,
- left->byteorder, ctx->ectx.len, 0);
+ left->byteorder, max_shift_len, 0);
+ }
+
if (expr_evaluate(ctx, &op->right) < 0)
return -1;
right = op->right;
+ /* evaluation expects constant to the right hand side. */
+ if (expr_is_constant(left) && !expr_is_constant(right)) {
+ range_expr_swap_values(op);
+ left = op->left;
+ right = op->right;
+ }
+
switch (expr_basetype(left)->type) {
case TYPE_INTEGER:
case TYPE_STRING:
@@ -1234,31 +1566,73 @@ static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr)
"for %s expressions",
sym, expr_name(left));
- if (!expr_is_constant(right))
- return expr_binary_error(ctx->msgs, right, op,
- "Right hand side of binary operation "
- "(%s) must be constant", sym);
-
- if (!expr_is_singleton(right))
+ if (!datatype_equal(expr_basetype(left), expr_basetype(right)))
return expr_binary_error(ctx->msgs, left, op,
- "Binary operation (%s) is undefined "
- "for %s expressions",
- sym, expr_name(right));
-
- /* The grammar guarantees this */
- assert(expr_basetype(left) == expr_basetype(right));
+ "Binary operation (%s) with different base types "
+ "(%s vs %s) is not supported",
+ sym, expr_basetype(left)->name, expr_basetype(right)->name);
switch (op->op) {
case OP_LSHIFT:
case OP_RSHIFT:
- return expr_evaluate_shift(ctx, expr);
+ if (!expr_is_constant(right))
+ return expr_binary_error(ctx->msgs, right, op,
+ "Right hand side of binary operation "
+ "(%s) must be constant", sym);
+
+ if (!expr_is_singleton(right))
+ return expr_binary_error(ctx->msgs, left, op,
+ "Binary operation (%s) is undefined "
+ "for %s expressions",
+ sym, expr_name(right));
+
+ ret = expr_evaluate_shift(ctx, expr);
+ break;
case OP_AND:
case OP_XOR:
case OP_OR:
- return expr_evaluate_bitwise(ctx, expr);
+ if (expr_is_constant(left) && !expr_is_constant(right))
+ return expr_binary_error(ctx->msgs, right, op,
+ "Right hand side of binary operation "
+ "(%s) must be constant", sym);
+
+ if (expr_is_constant(right) && !expr_is_singleton(right))
+ return expr_binary_error(ctx->msgs, left, op,
+ "Binary operation (%s) is undefined "
+ "for %s expressions",
+ sym, expr_name(right));
+
+ ret = expr_evaluate_bitwise(ctx, expr);
+ break;
default:
BUG("invalid binary operation %u\n", op->op);
}
+
+
+ if (ctx->recursion.binop == 0)
+ BUG("recursion counter underflow");
+
+ /* can't check earlier: evaluate functions might do constant-merging + expr_free.
+ *
+ * So once we've evaluate everything check for remaining length of the
+ * binop chain.
+ */
+ if (--ctx->recursion.binop == 0) {
+ unsigned int to_linearize = 0;
+
+ op = *expr;
+ while (op && op->etype == EXPR_BINOP && op->left != NULL) {
+ to_linearize++;
+ op = op->left;
+
+ if (to_linearize >= NFT_MAX_EXPR_RECURSION)
+ return expr_binary_error(ctx->msgs, op, NULL,
+ "Binary operation limit %u reached ",
+ NFT_MAX_EXPR_RECURSION);
+ }
+ }
+
+ return ret;
}
static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr)
@@ -1266,10 +1640,18 @@ static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr)
struct expr *next = list_entry((*expr)->list.next, struct expr, list);
int err;
+ /* should never be hit in practice */
+ if (ctx->recursion.list >= USHRT_MAX)
+ return expr_binary_error(ctx->msgs, next, NULL,
+ "List limit %u reached ",
+ ctx->recursion.list);
+
+ ctx->recursion.list++;
assert(*expr != next);
list_del(&(*expr)->list);
err = expr_evaluate(ctx, expr);
list_add_tail(&(*expr)->list, &next->list);
+ ctx->recursion.list--;
return err;
}
@@ -1279,20 +1661,28 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
uint32_t type = dtype ? dtype->type : 0, ntype = 0;
int off = dtype ? dtype->subtypes : 0;
unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+ const struct list_head *expressions = NULL;
struct expr *i, *next, *key = NULL;
const struct expr *key_ctx = NULL;
+ bool runaway = false;
uint32_t size = 0;
if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
key_ctx = ctx->ectx.key;
assert(!list_empty(&ctx->ectx.key->expressions));
key = list_first_entry(&ctx->ectx.key->expressions, struct expr, list);
+ expressions = &ctx->ectx.key->expressions;
}
list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
enum byteorder bo = BYTEORDER_INVALID;
unsigned dsize_bytes, dsize = 0;
+ if (runaway) {
+ return expr_binary_error(ctx->msgs, *expr, key_ctx,
+ "too many concatenation components");
+ }
+
if (i->etype == EXPR_CT &&
(i->ct.key == NFT_CT_SRC ||
i->ct.key == NFT_CT_DST))
@@ -1310,7 +1700,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
dsize = key->len;
bo = key->byteorder;
off--;
- } else if (dtype == NULL) {
+ } else if (dtype == NULL || off == 0) {
tmp = datatype_lookup(TYPE_INVALID);
} else {
tmp = concat_subtype_lookup(type, --off);
@@ -1319,19 +1709,81 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
}
__expr_set_context(&ctx->ectx, tmp, bo, dsize, 0);
+ ctx->ectx.key = i;
if (list_member_evaluate(ctx, &i) < 0)
return -1;
+
+ switch (i->etype) {
+ case EXPR_INVALID:
+ case __EXPR_MAX:
+ BUG("Unexpected etype %d", i->etype);
+ break;
+ case EXPR_VALUE:
+ case EXPR_UNARY:
+ case EXPR_BINOP:
+ case EXPR_RELATIONAL:
+ case EXPR_CONCAT:
+ case EXPR_MAP:
+ case EXPR_PAYLOAD:
+ case EXPR_EXTHDR:
+ case EXPR_META:
+ case EXPR_RT:
+ case EXPR_CT:
+ case EXPR_SET_ELEM:
+ case EXPR_NUMGEN:
+ case EXPR_HASH:
+ case EXPR_FIB:
+ case EXPR_SOCKET:
+ case EXPR_OSF:
+ case EXPR_XFRM:
+ break;
+ case EXPR_RANGE:
+ case EXPR_PREFIX:
+ case EXPR_RANGE_VALUE:
+ /* allowed on RHS (e.g. th dport . mark { 1-65535 . 42 }
+ * ~~~~~~~~ allowed
+ * but not on LHS (e.g 1-4 . mark { ...}
+ * ~~~ illegal
+ *
+ * recursion.list > 0 means that the concatenation is
+ * part of another expression, such as EXPR_MAPPING or
+ * EXPR_SET_ELEM (is used as RHS).
+ */
+ if (ctx->recursion.list > 0)
+ break;
+
+ return expr_error(ctx->msgs, i,
+ "cannot use %s in concatenation",
+ expr_name(i));
+ case EXPR_VERDICT:
+ case EXPR_SYMBOL:
+ case EXPR_VARIABLE:
+ case EXPR_LIST:
+ case EXPR_SET:
+ case EXPR_SET_REF:
+ case EXPR_MAPPING:
+ case EXPR_SET_ELEM_CATCHALL:
+ case EXPR_RANGE_SYMBOL:
+ return expr_error(ctx->msgs, i,
+ "cannot use %s in concatenation",
+ expr_name(i));
+ }
+
+ if (!i->dtype)
+ return expr_error(ctx->msgs, i,
+ "cannot use %s in concatenation, lacks datatype",
+ expr_name(i));
+
flags &= i->flags;
if (!key && i->dtype->type == TYPE_INTEGER) {
struct datatype *clone;
- clone = dtype_clone(i->dtype);
+ clone = datatype_clone(i->dtype);
clone->size = i->len;
clone->byteorder = i->byteorder;
- clone->refcnt = 1;
- i->dtype = clone;
+ __datatype_set(i, clone);
}
if (dtype == NULL && i->dtype->size == 0)
@@ -1348,12 +1800,22 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
dsize_bytes = div_round_up(dsize, BITS_PER_BYTE);
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
size += netlink_padded_len(dsize);
- if (key)
- key = list_next_entry(key, list);
+ if (key && expressions) {
+ if (list_is_last(&key->list, expressions))
+ runaway = true;
+ else
+ key = list_next_entry(key, list);
+ }
+
+ ctx->inner_desc = NULL;
+
+ if (size > NFT_MAX_EXPR_LEN_BITS)
+ return expr_error(ctx->msgs, i, "Concatenation of size %u exceeds maximum size of %u",
+ size, NFT_MAX_EXPR_LEN_BITS);
}
(*expr)->flags |= flags;
- datatype_set(*expr, concat_type_alloc(ntype));
+ __datatype_set(*expr, concat_type_alloc(ntype));
(*expr)->len = size;
if (off > 0)
@@ -1378,16 +1840,22 @@ static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr)
mpz_init_set_ui(val, 0);
list_for_each_entry_safe(i, next, &list->expressions, list) {
- if (list_member_evaluate(ctx, &i) < 0)
+ if (list_member_evaluate(ctx, &i) < 0) {
+ mpz_clear(val);
return -1;
- if (i->etype != EXPR_VALUE)
+ }
+ if (i->etype != EXPR_VALUE) {
+ mpz_clear(val);
return expr_error(ctx->msgs, i,
"List member must be a constant "
"value");
- if (i->dtype->basetype->type != TYPE_BITMASK)
+ }
+ if (datatype_basetype(i->dtype)->type != TYPE_BITMASK) {
+ mpz_clear(val);
return expr_error(ctx->msgs, i,
"Basetype of type %s is not bitmask",
i->dtype->desc);
+ }
mpz_ior(val, val, i->value);
}
@@ -1421,7 +1889,8 @@ static int __expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr *elem)
"but element has %d", num_set_exprs,
num_elem_exprs);
} else if (num_set_exprs == 0) {
- if (!(set->flags & NFT_SET_EVAL)) {
+ if (!(set->flags & NFT_SET_ANONYMOUS) &&
+ !(set->flags & NFT_SET_EVAL)) {
elem_stmt = list_first_entry(&elem->stmt_list, struct stmt, list);
return stmt_error(ctx, elem_stmt,
"missing statement in %s declaration",
@@ -1433,13 +1902,13 @@ static int __expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr *elem)
set_stmt = list_first_entry(&set->stmt_list, struct stmt, list);
list_for_each_entry(elem_stmt, &elem->stmt_list, list) {
- if (set_stmt->ops != elem_stmt->ops) {
+ if (set_stmt->type != elem_stmt->type) {
return stmt_error(ctx, elem_stmt,
"statement mismatch, element expects %s, "
"but %s has type %s",
- elem_stmt->ops->name,
+ stmt_name(elem_stmt),
set_is_map(set->flags) ? "map" : "set",
- set_stmt->ops->name);
+ stmt_name(set_stmt));
}
set_stmt = list_next_entry(set_stmt, list);
}
@@ -1470,6 +1939,7 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
switch (elem->key->etype) {
case EXPR_PREFIX:
case EXPR_RANGE:
+ case EXPR_RANGE_VALUE:
key = elem->key;
goto err_missing_flag;
case EXPR_CONCAT:
@@ -1477,6 +1947,7 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
switch (key->etype) {
case EXPR_PREFIX:
case EXPR_RANGE:
+ case EXPR_RANGE_VALUE:
goto err_missing_flag;
default:
break;
@@ -1500,6 +1971,16 @@ err_missing_flag:
set_is_map(ctx->set->flags) ? "map" : "set", expr_name(key));
}
+static int expr_evaluate_set_elem_catchall(struct eval_ctx *ctx, struct expr **expr)
+{
+ struct expr *elem = *expr;
+
+ if (ctx->set)
+ elem->len = ctx->set->key->len;
+
+ return 0;
+}
+
static const struct expr *expr_set_elem(const struct expr *expr)
{
if (expr->etype == EXPR_MAPPING)
@@ -1520,6 +2001,8 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
switch (ctx->cmd->op) {
case CMD_CREATE:
case CMD_ADD:
+ case CMD_REPLACE:
+ case CMD_INSERT:
if (set->automerge) {
ret = set_automerge(ctx->msgs, ctx->cmd, set, init,
ctx->nft->debug_mask);
@@ -1528,10 +2011,12 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
}
break;
case CMD_DELETE:
+ case CMD_DESTROY:
ret = set_delete(ctx->msgs, ctx->cmd, set, init,
ctx->nft->debug_mask);
break;
case CMD_GET:
+ case CMD_RESET:
break;
default:
BUG("unhandled op %d\n", ctx->cmd->op);
@@ -1541,6 +2026,14 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
return ret;
}
+static void expr_evaluate_set_ref(struct eval_ctx *ctx, struct expr *expr)
+{
+ struct set *set = expr->set;
+
+ expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
+ ctx->ectx.key = set->key;
+}
+
static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *set = *expr, *i, *next;
@@ -1606,21 +2099,6 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
if (ctx->set) {
if (ctx->set->flags & NFT_SET_CONCAT)
set->set_flags |= NFT_SET_CONCAT;
- } else if (set->size == 1) {
- i = list_first_entry(&set->expressions, struct expr, list);
- if (i->etype == EXPR_SET_ELEM) {
- switch (i->key->etype) {
- case EXPR_PREFIX:
- case EXPR_RANGE:
- case EXPR_VALUE:
- *expr = i->key;
- i->key = NULL;
- expr_free(set);
- return 0;
- default:
- break;
- }
- }
}
set->set_flags |= NFT_SET_CONSTANT;
@@ -1647,11 +2125,62 @@ static void map_set_concat_info(struct expr *map)
}
}
+static void __mapping_expr_expand(struct expr *i)
+{
+ struct expr *j, *range, *next;
+
+ assert(i->etype == EXPR_MAPPING);
+ switch (i->right->etype) {
+ case EXPR_VALUE:
+ range = range_expr_alloc(&i->location, expr_get(i->right), expr_get(i->right));
+ expr_free(i->right);
+ i->right = range;
+ break;
+ case EXPR_CONCAT:
+ list_for_each_entry_safe(j, next, &i->right->expressions, list) {
+ if (j->etype != EXPR_VALUE)
+ continue;
+
+ range = range_expr_alloc(&j->location, expr_get(j), expr_get(j));
+ list_replace(&j->list, &range->list);
+ expr_free(j);
+ }
+ i->right->flags &= ~EXPR_F_SINGLETON;
+ break;
+ default:
+ break;
+ }
+}
+
+static int mapping_expr_expand(struct eval_ctx *ctx)
+{
+ struct expr *i;
+
+ if (!set_is_anonymous(ctx->set->flags))
+ return 0;
+
+ list_for_each_entry(i, &ctx->set->init->expressions, list) {
+ if (i->etype != EXPR_MAPPING)
+ return expr_error(ctx->msgs, i,
+ "expected mapping, not %s", expr_name(i));
+ __mapping_expr_expand(i);
+ }
+
+ return 0;
+}
+
+static bool datatype_compatible(const struct datatype *a, const struct datatype *b)
+{
+ return (a->type == TYPE_MARK &&
+ datatype_equal(datatype_basetype(a), datatype_basetype(b))) ||
+ datatype_equal(a, b);
+}
+
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_ctx ectx = ctx->ectx;
+ uint32_t set_flags = NFT_SET_MAP;
struct expr *key, *data;
if (map->map->etype == EXPR_CT &&
@@ -1678,11 +2207,16 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
return expr_error(ctx->msgs, map->map,
"Map expression can not be constant");
+ ctx->stmt_len = 0;
mappings = map->mappings;
- mappings->set_flags |= NFT_SET_MAP;
switch (map->mappings->etype) {
+ case EXPR_CONCAT:
+ case EXPR_LIST:
case EXPR_SET:
+ set_flags |= mappings->set_flags;
+ /* fallthrough */
+ case EXPR_VARIABLE:
if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
key = expr_clone(ctx->ectx.key);
} else {
@@ -1692,16 +2226,29 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->ectx.len, NULL);
}
- dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
- if (dtype->type == TYPE_VERDICT)
+ if (!ectx.dtype) {
+ expr_free(key);
+ return expr_error(ctx->msgs, map,
+ "Implicit map expression without known datatype");
+ }
+
+ if (ectx.dtype->type == TYPE_VERDICT) {
data = verdict_expr_alloc(&netlink_location, 0, NULL);
- else
+ } else {
+ const struct datatype *dtype;
+
+ dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
data = constant_expr_alloc(&netlink_location, dtype,
dtype->byteorder, ectx.len, NULL);
+ datatype_free(dtype);
+ }
mappings = implicit_set_declaration(ctx, "__map%d",
key, data,
- mappings);
+ mappings,
+ NFT_SET_ANONYMOUS | set_flags);
+ if (!mappings)
+ return -1;
if (ectx.len && mappings->set->data->len != ectx.len)
BUG("%d vs %d\n", mappings->set->data->len, ectx.len);
@@ -1712,6 +2259,11 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+ if (map->mappings->set->init->etype != EXPR_SET) {
+ return expr_error(ctx->msgs, map->mappings->set->init,
+ "Expression is not a map");
+ }
+
if (set_is_interval(map->mappings->set->init->set_flags) &&
!(map->mappings->set->init->set_flags & NFT_SET_CONCAT) &&
interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0)
@@ -1721,9 +2273,13 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
if (binop_transfer(ctx, expr) < 0)
return -1;
- if (ctx->set->data->flags & EXPR_F_INTERVAL)
+ if (ctx->set->data->flags & EXPR_F_INTERVAL) {
ctx->set->data->len *= 2;
+ if (mapping_expr_expand(ctx))
+ return -1;
+ }
+
ctx->set->key->len = ctx->ectx.len;
ctx->set = NULL;
map = *expr;
@@ -1740,13 +2296,16 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
break;
case EXPR_SET_REF:
/* symbol has been already evaluated to set reference */
+ if (!set_is_map(mappings->set->flags))
+ return expr_error(ctx->msgs, map->mappings,
+ "Expression is not a map");
break;
default:
- BUG("invalid mapping expression %s\n",
- expr_name(map->mappings));
+ return expr_binary_error(ctx->msgs, map->mappings, map->map,
+ "invalid mapping expression %s", expr_name(map->mappings));
}
- if (!datatype_equal(map->map->dtype, map->mappings->set->key->dtype))
+ if (!datatype_compatible(map->mappings->set->key->dtype, map->map->dtype))
return expr_binary_error(ctx->msgs, map->mappings, map->map,
"datatype mismatch, map expects %s, "
"mapping expression has type %s",
@@ -1769,6 +2328,7 @@ static bool data_mapping_has_interval(struct expr *data)
struct expr *i;
if (data->etype == EXPR_RANGE ||
+ data->etype == EXPR_RANGE_VALUE ||
data->etype == EXPR_PREFIX)
return true;
@@ -1777,6 +2337,7 @@ static bool data_mapping_has_interval(struct expr *data)
list_for_each_entry(i, &data->expressions, list) {
if (i->etype == EXPR_RANGE ||
+ i->etype == EXPR_RANGE_VALUE ||
i->etype == EXPR_PREFIX)
return true;
}
@@ -1804,17 +2365,17 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
"Key must be a constant");
mapping->flags |= mapping->left->flags & EXPR_F_SINGLETON;
- if (set->data) {
- if (!set_is_anonymous(set->flags) &&
- set->data->flags & EXPR_F_INTERVAL)
- datalen = set->data->len / 2;
- else
- datalen = set->data->len;
+ /* This can happen for malformed map definitions */
+ if (!set->data)
+ return set_error(ctx, set, "map has no mapping data");
- __expr_set_context(&ctx->ectx, set->data->dtype, set->data->byteorder, datalen, 0);
- } else {
- assert((set->flags & NFT_SET_MAP) == 0);
- }
+ if (!set_is_anonymous(set->flags) &&
+ set->data->flags & EXPR_F_INTERVAL)
+ datalen = set->data->len / 2;
+ else
+ datalen = set->data->len;
+ __expr_set_context(&ctx->ectx, set->data->dtype,
+ set->data->byteorder, datalen, 0);
if (expr_evaluate(ctx, &mapping->right) < 0)
return -1;
@@ -1826,15 +2387,71 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
data_mapping_has_interval(mapping->right))
set->data->flags |= EXPR_F_INTERVAL;
+ if (!set_is_anonymous(set->flags) &&
+ set->data->flags & EXPR_F_INTERVAL)
+ __mapping_expr_expand(mapping);
+
if (!(set->data->flags & EXPR_F_INTERVAL) &&
!expr_is_singleton(mapping->right))
return expr_error(ctx->msgs, mapping->right,
"Value must be a singleton");
+ if (set_is_objmap(set->flags) && mapping->right->etype != EXPR_VALUE)
+ return expr_error(ctx->msgs, mapping->right,
+ "Object mapping data should be a value, not %s",
+ expr_name(mapping->right));
+
mapping->flags |= EXPR_F_CONSTANT;
return 0;
}
+static int expr_evaluate_symbol_range(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct expr *left, *right, *range, *constant_range;
+ struct expr *expr = *exprp;
+
+ /* expand to symbol and range expressions to consolidate evaluation. */
+ left = symbol_expr_alloc(&expr->location, expr->symtype,
+ (struct scope *)expr->scope,
+ expr->identifier_range[0]);
+ right = symbol_expr_alloc(&expr->location, expr->symtype,
+ (struct scope *)expr->scope,
+ expr->identifier_range[1]);
+ range = range_expr_alloc(&expr->location, left, right);
+
+ if (expr_evaluate(ctx, &range) < 0) {
+ expr_free(range);
+ return -1;
+ }
+
+ if (range->etype != EXPR_RANGE)
+ goto out_done;
+
+ left = range->left;
+ right = range->right;
+
+ if (ctx->set &&
+ left->etype == EXPR_VALUE &&
+ right->etype == EXPR_VALUE) {
+ constant_range = constant_range_expr_alloc(&expr->location,
+ left->dtype,
+ left->byteorder,
+ left->len,
+ left->value,
+ right->value);
+ expr_free(range);
+ expr_free(expr);
+ *exprp = constant_range;
+ return 0;
+ }
+
+out_done:
+ expr_free(expr);
+ *exprp = range;
+
+ return 0;
+}
+
/* We got datatype context via statement. If the basetype is compatible, set
* this expression datatype to the one of the statement to make it datatype
* compatible. This is a more conservative approach than enabling datatype
@@ -2065,7 +2682,7 @@ static int binop_transfer(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
-static bool lhs_is_meta_hour(const struct expr *meta)
+bool lhs_is_meta_hour(const struct expr *meta)
{
if (meta->etype != EXPR_META)
return false;
@@ -2074,7 +2691,7 @@ static bool lhs_is_meta_hour(const struct expr *meta)
meta->meta.key == NFT_META_TIME_DAY;
}
-static void swap_values(struct expr *range)
+void range_expr_swap_values(struct expr *range)
{
struct expr *left_tmp;
@@ -2091,16 +2708,54 @@ static bool range_needs_swap(const struct expr *range)
return mpz_cmp(left->value, right->value) > 0;
}
+static void optimize_singleton_set(struct expr *rel, struct expr **expr)
+{
+ struct expr *set = rel->right, *i;
+
+ i = list_first_entry(&set->expressions, struct expr, list);
+ if (i->etype == EXPR_SET_ELEM &&
+ list_empty(&i->stmt_list)) {
+
+ switch (i->key->etype) {
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ case EXPR_VALUE:
+ rel->right = *expr = i->key;
+ i->key = NULL;
+ expr_free(set);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (rel->op == OP_IMPLICIT &&
+ rel->right->dtype->basetype &&
+ rel->right->dtype->basetype->type == TYPE_BITMASK &&
+ rel->right->dtype->type != TYPE_CT_STATE) {
+ rel->op = OP_EQ;
+ }
+}
+
static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *rel = *expr, *left, *right;
+ struct proto_ctx *pctx;
struct expr *range;
int ret;
+ right = rel->right;
+ if (right->etype == EXPR_SYMBOL &&
+ right->symtype == SYMBOL_SET &&
+ expr_evaluate(ctx, &rel->right) < 0)
+ return -1;
+
if (expr_evaluate(ctx, &rel->left) < 0)
return -1;
left = rel->left;
+ pctx = eval_proto_ctx(ctx);
+
if (rel->right->etype == EXPR_RANGE && lhs_is_meta_hour(rel->left)) {
ret = __expr_evaluate_range(ctx, &rel->right);
if (ret)
@@ -2118,10 +2773,10 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
"Inverting range values for cross-day hour matching\n\n");
if (rel->op == OP_EQ || rel->op == OP_IMPLICIT) {
- swap_values(range);
+ range_expr_swap_values(range);
rel->op = OP_NEQ;
} else if (rel->op == OP_NEQ) {
- swap_values(range);
+ range_expr_swap_values(range);
rel->op = OP_EQ;
}
}
@@ -2161,6 +2816,19 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
return expr_binary_error(ctx->msgs, right, left,
"Cannot be used with right hand side constant value");
+ if (left->etype != EXPR_CONCAT) {
+ switch (rel->op) {
+ case OP_EQ:
+ case OP_IMPLICIT:
+ case OP_NEQ:
+ if (right->etype == EXPR_SET && right->size == 1)
+ optimize_singleton_set(rel, &right);
+ break;
+ default:
+ break;
+ }
+ }
+
switch (rel->op) {
case OP_EQ:
case OP_IMPLICIT:
@@ -2168,7 +2836,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
* Update protocol context for payload and meta iiftype
* equality expressions.
*/
- relational_expr_pctx_update(&ctx->pctx, rel);
+ relational_expr_pctx_update(pctx, rel);
/* fall through */
case OP_NEQ:
@@ -2181,7 +2849,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
right->dtype->basetype == NULL ||
right->dtype->basetype->type != TYPE_BITMASK)
return expr_binary_error(ctx->msgs, left, right,
- "negation can only be used with singleton bitmask values");
+ "negation can only be used with singleton bitmask values. Did you mean \"!=\"?");
}
switch (right->etype) {
@@ -2208,7 +2876,11 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
right = rel->right =
implicit_set_declaration(ctx, "__set%d",
expr_get(left), NULL,
- right);
+ right,
+ right->set_flags | NFT_SET_ANONYMOUS);
+ if (!right)
+ return -1;
+
/* fall through */
case EXPR_SET_REF:
if (rel->left->etype == EXPR_CT &&
@@ -2280,11 +2952,12 @@ static int expr_evaluate_fib(struct eval_ctx *ctx, struct expr **exprp)
static int expr_evaluate_meta(struct eval_ctx *ctx, struct expr **exprp)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
struct expr *meta = *exprp;
switch (meta->meta.key) {
case NFT_META_NFPROTO:
- if (ctx->pctx.family != NFPROTO_INET &&
+ if (pctx->family != NFPROTO_INET &&
meta->flags & EXPR_F_PROTOCOL)
return expr_error(ctx->msgs, meta,
"meta nfproto is only useful in the inet family");
@@ -2351,9 +3024,10 @@ static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
struct expr *expr = *exprp;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_INET:
@@ -2366,23 +3040,49 @@ static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp)
return expr_evaluate_primary(ctx, exprp);
}
-static int expr_evaluate_flagcmp(struct eval_ctx *ctx, struct expr **exprp)
+static int verdict_validate_chain(struct eval_ctx *ctx,
+ struct expr *chain)
{
- struct expr *expr = *exprp, *binop, *rel;
+ char buf[NFT_CHAIN_MAXNAMELEN];
+ unsigned int len;
- if (expr->op != OP_EQ &&
- expr->op != OP_NEQ)
- return expr_error(ctx->msgs, expr, "either == or != is allowed");
+ len = chain->len / BITS_PER_BYTE;
+ if (len > NFT_CHAIN_MAXNAMELEN)
+ return expr_error(ctx->msgs, chain,
+ "chain name too long (%u, max %u)",
+ chain->len / BITS_PER_BYTE,
+ NFT_CHAIN_MAXNAMELEN);
- binop = binop_expr_alloc(&expr->location, OP_AND,
- expr_get(expr->flagcmp.expr),
- expr_get(expr->flagcmp.mask));
- rel = relational_expr_alloc(&expr->location, expr->op, binop,
- expr_get(expr->flagcmp.value));
- expr_free(expr);
- *exprp = rel;
+ if (!len)
+ return expr_error(ctx->msgs, chain,
+ "chain name length 0 not allowed");
+
+ memset(buf, 0, sizeof(buf));
+ mpz_export_data(buf, chain->value, BYTEORDER_HOST_ENDIAN, len);
+
+ if (strnlen(buf, sizeof(buf)) < sizeof(buf))
+ return 0;
+
+ return expr_error(ctx->msgs, chain,
+ "chain name must be smaller than %u",
+ NFT_CHAIN_MAXNAMELEN);
+}
+
+static int expr_evaluate_verdict(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct expr *expr = *exprp;
- return expr_evaluate(ctx, exprp);
+ switch (expr->verdict) {
+ case NFT_GOTO:
+ case NFT_JUMP:
+ if (expr->chain->etype == EXPR_VALUE &&
+ verdict_validate_chain(ctx, expr->chain))
+ return -1;
+
+ break;
+ }
+
+ return expr_evaluate_primary(ctx, exprp);
}
static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
@@ -2403,13 +3103,14 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_VARIABLE:
return expr_evaluate_variable(ctx, expr);
case EXPR_SET_REF:
+ expr_evaluate_set_ref(ctx, *expr);
return 0;
case EXPR_VALUE:
return expr_evaluate_value(ctx, expr);
case EXPR_EXTHDR:
return expr_evaluate_exthdr(ctx, expr);
case EXPR_VERDICT:
- return expr_evaluate_primary(ctx, expr);
+ return expr_evaluate_verdict(ctx, expr);
case EXPR_META:
return expr_evaluate_meta(ctx, expr);
case EXPR_SOCKET:
@@ -2419,7 +3120,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_FIB:
return expr_evaluate_fib(ctx, expr);
case EXPR_PAYLOAD:
- return expr_evaluate_payload(ctx, expr);
+ return expr_evaluate_payload_inner(ctx, expr);
case EXPR_RT:
return expr_evaluate_rt(ctx, expr);
case EXPR_CT:
@@ -2453,9 +3154,9 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_XFRM:
return expr_evaluate_xfrm(ctx, expr);
case EXPR_SET_ELEM_CATCHALL:
- return 0;
- case EXPR_FLAGCMP:
- return expr_evaluate_flagcmp(ctx, expr);
+ return expr_evaluate_set_elem_catchall(ctx, expr);
+ case EXPR_RANGE_SYMBOL:
+ return expr_evaluate_symbol_range(ctx, expr);
default:
BUG("unknown expression type %s\n", expr_name(*expr));
}
@@ -2526,13 +3227,18 @@ static int __stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
"expression has type %s with length %d",
dtype->desc, (*expr)->dtype->desc,
(*expr)->len);
- else if ((*expr)->dtype->type != TYPE_INTEGER &&
- !datatype_equal((*expr)->dtype, dtype))
+
+ if (!datatype_compatible(dtype, (*expr)->dtype))
return stmt_binary_error(ctx, *expr, stmt, /* verdict vs invalid? */
"datatype mismatch: expected %s, "
"expression has type %s",
dtype->desc, (*expr)->dtype->desc);
+ if (dtype->type == TYPE_MARK &&
+ datatype_equal(datatype_basetype(dtype), datatype_basetype((*expr)->dtype)) &&
+ !expr_is_constant(*expr))
+ return byteorder_conversion(ctx, expr, byteorder);
+
/* we are setting a value, we can't use a set */
switch ((*expr)->etype) {
case EXPR_SET:
@@ -2547,6 +3253,10 @@ static int __stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
return byteorder_conversion(ctx, expr, byteorder);
case EXPR_PREFIX:
return stmt_prefix_conversion(ctx, expr, byteorder);
+ case EXPR_NUMGEN:
+ if (dtype->type == TYPE_IPADDR)
+ return byteorder_conversion(ctx, expr, byteorder);
+ break;
default:
break;
}
@@ -2565,6 +3275,25 @@ static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
}
+/* like stmt_evaluate_arg, but keep existing context created
+ * by previous expr_evaluate().
+ *
+ * This is needed for add/update statements:
+ * ctx->ectx.key has the set key, which may be needed for 'typeof'
+ * sets: the 'add/update' expression might contain integer data types.
+ *
+ * Without the key we cannot derive the element size.
+ */
+static int stmt_evaluate_key(struct eval_ctx *ctx, struct stmt *stmt,
+ const struct datatype *dtype, unsigned int len,
+ enum byteorder byteorder, struct expr **expr)
+{
+ if (expr_evaluate(ctx, expr) < 0)
+ return -1;
+
+ return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
+}
+
static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
{
if (stmt_evaluate_arg(ctx, stmt, &verdict_type, 0, 0, &stmt->expr) < 0)
@@ -2575,12 +3304,16 @@ static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->expr->verdict != NFT_CONTINUE)
stmt->flags |= STMT_F_TERMINAL;
if (stmt->expr->chain != NULL) {
- if (expr_evaluate(ctx, &stmt->expr->chain) < 0)
+ if (stmt_evaluate_arg(ctx, stmt, &string_type, 0, 0,
+ &stmt->expr->chain) < 0)
return -1;
if (stmt->expr->chain->etype != EXPR_VALUE) {
return expr_error(ctx->msgs, stmt->expr->chain,
"not a value expression");
}
+
+ if (verdict_validate_chain(ctx, stmt->expr->chain))
+ return -1;
}
break;
case EXPR_MAP:
@@ -2603,28 +3336,75 @@ static bool stmt_evaluate_payload_need_csum(const struct expr *payload)
return desc && desc->checksum_key;
}
+static bool stmt_evaluate_is_vlan(const struct expr *payload)
+{
+ return payload->payload.base == PROTO_BASE_LL_HDR &&
+ payload->payload.desc == &proto_vlan;
+}
+
+/** stmt_evaluate_payload_need_aligned_fetch
+ *
+ * @payload: payload expression to check
+ *
+ * Some types of stores need to round up to an even sized byte length,
+ * typically 1 -> 2 or 3 -> 4 bytes.
+ *
+ * This includes anything that needs inet checksum fixups and also writes
+ * to the vlan header. This is because of VLAN header removal in the
+ * kernel: nftables kernel side provides illusion of a linear packet, i.e.
+ * ethernet_header|vlan_header|network_header.
+ *
+ * When a write to the vlan header is performed, kernel side updates the
+ * pseudoheader, but only accepts 2 or 4 byte writes to vlan proto/TCI.
+ *
+ * Return true if load needs to be expanded to cover even amount of bytes
+ */
+static bool stmt_evaluate_payload_need_aligned_fetch(const struct expr *payload)
+{
+ if (stmt_evaluate_payload_need_csum(payload))
+ return true;
+
+ if (stmt_evaluate_is_vlan(payload))
+ return true;
+
+ return false;
+}
+
static int stmt_evaluate_exthdr(struct eval_ctx *ctx, struct stmt *stmt)
{
struct expr *exthdr;
+ int ret;
if (__expr_evaluate_exthdr(ctx, &stmt->exthdr.expr) < 0)
return -1;
exthdr = stmt->exthdr.expr;
- return stmt_evaluate_arg(ctx, stmt, exthdr->dtype, exthdr->len,
- BYTEORDER_BIG_ENDIAN,
- &stmt->exthdr.val);
+ ret = stmt_evaluate_arg(ctx, stmt, exthdr->dtype, exthdr->len,
+ BYTEORDER_BIG_ENDIAN,
+ &stmt->exthdr.val);
+ if (ret < 0)
+ return ret;
+
+ if (stmt->exthdr.val->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->exthdr.val);
+
+ return 0;
}
static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct expr *mask, *and, *xor, *payload_bytes;
- unsigned int masklen, extra_len = 0;
+ struct expr *mask, *and, *xor, *expr, *payload_bytes;
unsigned int payload_byte_size, payload_byte_offset;
uint8_t shift_imm, data[NFT_REG_SIZE];
+ unsigned int masklen, extra_len = 0;
struct expr *payload;
mpz_t bitmask, ff;
- bool need_csum;
+ bool aligned_fetch;
+
+ if (stmt->payload.expr->payload.inner_desc) {
+ return expr_error(ctx->msgs, stmt->payload.expr,
+ "payload statement for this expression is not supported");
+ }
if (__expr_evaluate_payload(ctx, stmt->payload.expr) < 0)
return -1;
@@ -2639,7 +3419,10 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
payload->byteorder) < 0)
return -1;
- need_csum = stmt_evaluate_payload_need_csum(payload);
+ if (stmt->payload.val->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->payload.val);
+
+ aligned_fetch = stmt_evaluate_payload_need_aligned_fetch(payload);
if (!payload_needs_adjustment(payload)) {
@@ -2647,7 +3430,7 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
* update checksum and the length is not even because
* kernel checksum functions cannot deal with odd lengths.
*/
- if (!need_csum || ((payload->len / BITS_PER_BYTE) & 1) == 0)
+ if (!aligned_fetch || ((payload->len / BITS_PER_BYTE) & 1) == 0)
return 0;
}
@@ -2658,7 +3441,12 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
payload_byte_size = div_round_up(payload->len + extra_len,
BITS_PER_BYTE);
- if (need_csum && payload_byte_size & 1) {
+ if (payload_byte_size > sizeof(data))
+ return expr_error(ctx->msgs, stmt->payload.expr,
+ "uneven load cannot span more than %u bytes, got %u",
+ sizeof(data), payload_byte_size);
+
+ if (aligned_fetch && payload_byte_size & 1) {
payload_byte_size++;
if (payload_byte_offset & 1) { /* prefer 16bit aligned fetch */
@@ -2669,20 +3457,68 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
}
}
- if (shift_imm) {
- struct expr *off, *lshift;
+ switch (stmt->payload.val->etype) {
+ case EXPR_VALUE:
+ if (shift_imm)
+ mpz_lshift_ui(stmt->payload.val->value, shift_imm);
+ break;
+ case EXPR_BINOP:
+ expr = stmt->payload.val;
+ while (expr->left->etype == EXPR_BINOP)
+ expr = expr->left;
- off = constant_expr_alloc(&payload->location,
- expr_basetype(payload),
- BYTEORDER_HOST_ENDIAN,
- sizeof(shift_imm), &shift_imm);
+ if (expr->left->etype != EXPR_PAYLOAD)
+ break;
+
+ if (!payload_expr_cmp(payload, expr->left))
+ break;
+
+ /* Adjust payload to fetch 16-bits. */
+ expr->left->payload.offset = payload_byte_offset * BITS_PER_BYTE;
+ expr->left->len = payload_byte_size * BITS_PER_BYTE;
+ expr->left->payload.is_raw = 1;
+
+ switch (expr->right->etype) {
+ case EXPR_VALUE:
+ if (shift_imm)
+ mpz_lshift_ui(expr->right->value, shift_imm);
+
+ /* build bitmask to keep unmodified bits intact */
+ if (expr->op == OP_AND) {
+ masklen = payload_byte_size * BITS_PER_BYTE;
+ mpz_init_bitmask(ff, masklen);
+
+ mpz_init2(bitmask, masklen);
+ mpz_bitmask(bitmask, payload->len);
+ mpz_lshift_ui(bitmask, shift_imm);
+
+ mpz_xor(bitmask, ff, bitmask);
+ mpz_clear(ff);
+
+ mpz_ior(bitmask, expr->right->value, bitmask);
+ mpz_set(expr->right->value, bitmask);
+
+ mpz_clear(bitmask);
+ }
+ break;
+ default:
+ return expr_error(ctx->msgs, expr->right,
+ "payload statement for this expression is not supported");
+ }
+
+ expr_free(stmt->payload.expr);
+ /* statement payload is the same in expr and value, update it. */
+ stmt->payload.expr = expr_clone(expr->left);
+ payload = stmt->payload.expr;
+ ctx->stmt_len = stmt->payload.expr->len;
- lshift = binop_expr_alloc(&payload->location, OP_LSHIFT,
- stmt->payload.val, off);
- lshift->dtype = payload->dtype;
- lshift->byteorder = payload->byteorder;
+ if (expr_evaluate(ctx, &stmt->payload.val) < 0)
+ return -1;
- stmt->payload.val = lshift;
+ return 0;
+ default:
+ return expr_error(ctx->msgs, stmt->payload.val,
+ "payload statement for this expression is not supported");
}
masklen = payload_byte_size * BITS_PER_BYTE;
@@ -2730,9 +3566,40 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
return expr_evaluate(ctx, &stmt->payload.val);
}
+static int stmt_evaluate_stateful(struct eval_ctx *ctx, struct stmt *stmt, const char *name)
+{
+ if (stmt_evaluate(ctx, stmt) < 0)
+ return -1;
+
+ if (!(stmt->flags & STMT_F_STATEFUL))
+ return stmt_error(ctx, stmt, "%s statement must be stateful", name);
+
+ return 0;
+}
+
static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct expr *key, *set, *setref;
+ struct expr *key, *setref;
+ struct set *existing_set;
+ struct table *table;
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ ctx->cmd->handle.table.name,
+ ctx->cmd->handle.family);
+ if (table == NULL)
+ return table_not_found(ctx);
+
+ existing_set = set_cache_find(table, stmt->meter.name);
+ if (existing_set &&
+ (!set_is_meter_compat(existing_set->flags) ||
+ set_is_map(existing_set->flags)))
+ return cmd_error(ctx, &stmt->location,
+ "%s; meter '%s' overlaps an existing %s '%s' in family %s",
+ strerror(EEXIST),
+ stmt->meter.name,
+ set_is_map(existing_set->flags) ? "map" : "set",
+ existing_set->handle.set.name,
+ family2str(existing_set->handle.family));
expr_set_context(&ctx->ectx, NULL, 0);
if (expr_evaluate(ctx, &stmt->meter.key) < 0)
@@ -2746,48 +3613,90 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
/* Declare an empty set */
key = stmt->meter.key;
- set = set_expr_alloc(&key->location, NULL);
- set->set_flags |= NFT_SET_EVAL;
- if (key->timeout)
- set->set_flags |= NFT_SET_TIMEOUT;
+ if (existing_set) {
+ if ((existing_set->flags & NFT_SET_TIMEOUT) && !key->timeout)
+ return expr_error(ctx->msgs, stmt->meter.key,
+ "existing set '%s' has timeout flag",
+ stmt->meter.name);
+
+ if ((existing_set->flags & NFT_SET_TIMEOUT) == 0 && key->timeout)
+ return expr_error(ctx->msgs, stmt->meter.key,
+ "existing set '%s' lacks timeout flag",
+ stmt->meter.name);
+
+ if (stmt->meter.size > 0 && existing_set->desc.size != stmt->meter.size)
+ return expr_error(ctx->msgs, stmt->meter.key,
+ "existing set '%s' has size %u, meter has %u",
+ stmt->meter.name, existing_set->desc.size,
+ stmt->meter.size);
+ setref = set_ref_expr_alloc(&key->location, existing_set);
+ } else {
+ struct expr *set;
+
+ set = set_expr_alloc(&key->location, existing_set);
+ if (key->timeout)
+ set->set_flags |= NFT_SET_TIMEOUT;
- setref = implicit_set_declaration(ctx, stmt->meter.name,
- expr_get(key), NULL, set);
+ set->set_flags |= NFT_SET_EVAL;
+ setref = implicit_set_declaration(ctx, stmt->meter.name,
+ expr_get(key), NULL, set,
+ NFT_SET_EVAL | set->set_flags);
+ if (setref)
+ setref->set->desc.size = stmt->meter.size;
+ }
+
+ if (!setref)
+ return -1;
- setref->set->desc.size = stmt->meter.size;
stmt->meter.set = setref;
- if (stmt_evaluate(ctx, stmt->meter.stmt) < 0)
+ if (stmt_evaluate_stateful(ctx, stmt->meter.stmt, "meter") < 0)
return -1;
- if (!(stmt->meter.stmt->flags & STMT_F_STATEFUL))
- return stmt_binary_error(ctx, stmt->meter.stmt, stmt,
- "meter statement must be stateful");
return 0;
}
static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
{
- return stmt_evaluate_arg(ctx, stmt,
- stmt->meta.tmpl->dtype,
- stmt->meta.tmpl->len,
- stmt->meta.tmpl->byteorder,
- &stmt->meta.expr);
+ int ret;
+
+ ctx->stmt_len = stmt->meta.tmpl->len;
+
+ ret = stmt_evaluate_arg(ctx, stmt,
+ stmt->meta.tmpl->dtype,
+ stmt->meta.tmpl->len,
+ stmt->meta.tmpl->byteorder,
+ &stmt->meta.expr);
+ if (ret < 0)
+ return ret;
+
+ if (stmt->meta.expr->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->meta.expr);
+
+ return ret;
}
static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt)
{
- if (stmt_evaluate_arg(ctx, stmt,
- stmt->ct.tmpl->dtype,
- stmt->ct.tmpl->len,
- stmt->ct.tmpl->byteorder,
- &stmt->ct.expr) < 0)
+ int ret;
+
+ ctx->stmt_len = stmt->ct.tmpl->len;
+
+ ret = stmt_evaluate_arg(ctx, stmt,
+ stmt->ct.tmpl->dtype,
+ stmt->ct.tmpl->len,
+ stmt->ct.tmpl->byteorder,
+ &stmt->ct.expr);
+ if (ret < 0)
return -1;
if (stmt->ct.key == NFT_CT_SECMARK && expr_is_constant(stmt->ct.expr))
return stmt_error(ctx, stmt,
"ct secmark must not be set to constant value");
+ if (stmt->ct.expr->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->ct.expr);
+
return 0;
}
@@ -2795,9 +3704,10 @@ static int reject_payload_gen_dependency_tcp(struct eval_ctx *ctx,
struct stmt *stmt,
struct expr **payload)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc;
- desc = ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
if (desc != NULL)
return 0;
*payload = payload_expr_alloc(&stmt->location, &proto_tcp,
@@ -2809,9 +3719,10 @@ static int reject_payload_gen_dependency_family(struct eval_ctx *ctx,
struct stmt *stmt,
struct expr **payload)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *base;
- base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (base != NULL)
return 0;
@@ -2834,8 +3745,7 @@ static int reject_payload_gen_dependency_family(struct eval_ctx *ctx,
return 1;
}
-static int stmt_reject_gen_dependency(struct eval_ctx *ctx, struct stmt *stmt,
- struct expr *expr)
+static int stmt_reject_gen_dependency(struct eval_ctx *ctx, struct stmt *stmt)
{
struct expr *payload = NULL;
struct stmt *nstmt;
@@ -2870,7 +3780,7 @@ static int stmt_reject_gen_dependency(struct eval_ctx *ctx, struct stmt *stmt,
*/
list_add(&nstmt->list, &ctx->rule->stmts);
out:
- xfree(payload);
+ free(payload);
return ret;
}
@@ -2878,6 +3788,7 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
struct stmt *stmt,
const struct proto_desc *desc)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *base;
int protocol;
@@ -2887,7 +3798,7 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
case NFT_REJECT_ICMPX_UNREACH:
break;
case NFT_REJECT_ICMP_UNREACH:
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
@@ -2895,14 +3806,14 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
if (stmt->reject.family == NFPROTO_IPV4)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
case NFPROTO_IPV6:
case __constant_htons(ETH_P_IPV6):
if (stmt->reject.family == NFPROTO_IPV6)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
default:
return stmt_error(ctx, stmt,
@@ -2914,18 +3825,18 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
return 0;
}
-static int stmt_evaluate_reject_inet(struct eval_ctx *ctx, struct stmt *stmt,
- struct expr *expr)
+static int stmt_evaluate_reject_inet(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc;
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc != NULL &&
stmt_evaluate_reject_inet_family(ctx, stmt, desc) < 0)
return -1;
if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
return 0;
- if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0)
+ if (stmt_reject_gen_dependency(ctx, stmt) < 0)
return -1;
return 0;
}
@@ -2934,13 +3845,14 @@ static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx,
struct stmt *stmt,
const struct proto_desc *desc)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *base;
int protocol;
switch (stmt->reject.type) {
case NFT_REJECT_ICMPX_UNREACH:
case NFT_REJECT_TCP_RST:
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
@@ -2948,29 +3860,29 @@ static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx,
break;
default:
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"cannot reject this network family");
}
break;
case NFT_REJECT_ICMP_UNREACH:
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
if (NFPROTO_IPV4 == stmt->reject.family)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
case __constant_htons(ETH_P_IPV6):
if (NFPROTO_IPV6 == stmt->reject.family)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
default:
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"cannot reject this network family");
}
break;
@@ -2979,57 +3891,70 @@ static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx,
return 0;
}
-static int stmt_evaluate_reject_bridge(struct eval_ctx *ctx, struct stmt *stmt,
- struct expr *expr)
+static int stmt_evaluate_reject_bridge(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc;
- desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_LL_HDR].desc;
if (desc != &proto_eth && desc != &proto_vlan && desc != &proto_netdev)
return __stmt_binary_error(ctx, &stmt->location, NULL,
"cannot reject from this link layer protocol");
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc != NULL &&
stmt_evaluate_reject_bridge_family(ctx, stmt, desc) < 0)
return -1;
if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
return 0;
- if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0)
+ if (stmt_reject_gen_dependency(ctx, stmt) < 0)
return -1;
return 0;
}
-static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
- struct expr *expr)
+static int stmt_reject_error(struct eval_ctx *ctx,
+ const struct stmt *stmt,
+ const char *msg)
+{
+ struct expr *e = stmt->reject.expr;
+
+ if (e)
+ return stmt_binary_error(ctx, e, stmt, "%s", msg);
+
+ return stmt_error(ctx, stmt, "%s", msg);
+}
+
+static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt)
{
- switch (ctx->pctx.family) {
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+
+ switch (pctx->family) {
case NFPROTO_ARP:
return stmt_error(ctx, stmt, "cannot use reject with arp");
case NFPROTO_IPV4:
case NFPROTO_IPV6:
switch (stmt->reject.type) {
case NFT_REJECT_TCP_RST:
- if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0)
+ if (stmt_reject_gen_dependency(ctx, stmt) < 0)
return -1;
break;
case NFT_REJECT_ICMPX_UNREACH:
- return stmt_binary_error(ctx, stmt->reject.expr, stmt,
+ return stmt_reject_error(ctx, stmt,
"abstracted ICMP unreachable not supported");
case NFT_REJECT_ICMP_UNREACH:
- if (stmt->reject.family == ctx->pctx.family)
+ if (stmt->reject.family == pctx->family)
break;
- return stmt_binary_error(ctx, stmt->reject.expr, stmt,
+ return stmt_reject_error(ctx, stmt,
"conflicting protocols specified: ip vs ip6");
}
break;
case NFPROTO_BRIDGE:
case NFPROTO_NETDEV:
- if (stmt_evaluate_reject_bridge(ctx, stmt, expr) < 0)
+ if (stmt_evaluate_reject_bridge(ctx, stmt) < 0)
return -1;
break;
case NFPROTO_INET:
- if (stmt_evaluate_reject_inet(ctx, stmt, expr) < 0)
+ if (stmt_evaluate_reject_inet(ctx, stmt) < 0)
return -1;
break;
}
@@ -3041,28 +3966,29 @@ static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
struct stmt *stmt)
{
- int protocol;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc, *base;
+ int protocol;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- stmt->reject.family = ctx->pctx.family;
- if (ctx->pctx.family == NFPROTO_IPV4)
+ stmt->reject.family = pctx->family;
+ if (pctx->family == NFPROTO_IPV4)
stmt->reject.icmp_code = ICMP_PORT_UNREACH;
else
stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
break;
case NFPROTO_INET:
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc == NULL) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
break;
}
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
@@ -3079,14 +4005,14 @@ static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
break;
case NFPROTO_BRIDGE:
case NFPROTO_NETDEV:
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc == NULL) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
break;
}
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
@@ -3105,7 +4031,10 @@ static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct parse_ctx parse_ctx = { .tbl = &ctx->nft->output.tbl, };
+ struct parse_ctx parse_ctx = {
+ .tbl = &ctx->nft->output.tbl,
+ .input = &ctx->nft->input,
+ };
struct error_record *erec;
struct expr *code;
@@ -3114,6 +4043,13 @@ static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
erec_queue(erec, ctx->msgs);
return -1;
}
+
+ if (mpz_cmp_ui(code->value, UINT8_MAX) > 0) {
+ expr_free(code);
+ return expr_error(ctx->msgs, stmt->reject.expr,
+ "reject code must be integer in range 0-255");
+ }
+
stmt->reject.icmp_code = mpz_get_uint8(code->value);
expr_free(code);
@@ -3122,9 +4058,9 @@ static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_reset(struct eval_ctx *ctx, struct stmt *stmt)
{
- int protonum;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc, *base;
- struct proto_ctx *pctx = &ctx->pctx;
+ int protonum;
desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
if (desc == NULL)
@@ -3141,7 +4077,7 @@ static int stmt_evaluate_reset(struct eval_ctx *ctx, struct stmt *stmt)
default:
if (stmt->reject.type == NFT_REJECT_TCP_RST) {
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR],
+ &pctx->protocol[PROTO_BASE_TRANSPORT_HDR],
"you cannot use tcp reset with this protocol");
}
break;
@@ -3151,8 +4087,6 @@ static int stmt_evaluate_reset(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct expr *expr = ctx->cmd->expr;
-
if (stmt->reject.icmp_code < 0) {
if (stmt_evaluate_reject_default(ctx, stmt) < 0)
return -1;
@@ -3164,18 +4098,19 @@ static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
return -1;
}
- return stmt_evaluate_reject_family(ctx, stmt, expr);
+ return stmt_evaluate_reject_family(ctx, stmt);
}
static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *nproto;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
if (stmt->nat.family == NFPROTO_UNSPEC)
- stmt->nat.family = ctx->pctx.family;
+ stmt->nat.family = pctx->family;
return 0;
case NFPROTO_INET:
if (!stmt->nat.addr) {
@@ -3185,7 +4120,7 @@ static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->nat.family != NFPROTO_UNSPEC)
return 0;
- nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ nproto = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (nproto == &proto_ip)
stmt->nat.family = NFPROTO_IPV4;
@@ -3214,7 +4149,7 @@ static const struct datatype *get_addr_dtype(uint8_t family)
static int evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
struct expr **expr)
{
- struct proto_ctx *pctx = &ctx->pctx;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct datatype *dtype;
dtype = get_addr_dtype(pctx->family);
@@ -3267,7 +4202,14 @@ static bool nat_evaluate_addr_has_th_expr(const struct expr *map)
static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
struct expr **expr)
{
- struct proto_ctx *pctx = &ctx->pctx;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ int err;
+
+ err = stmt_evaluate_arg(ctx, stmt,
+ &inet_service_type, 2 * BITS_PER_BYTE,
+ BYTEORDER_BIG_ENDIAN, expr);
+ if (err < 0)
+ return err;
if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
!nat_evaluate_addr_has_th_expr(stmt->nat.addr))
@@ -3275,41 +4217,75 @@ static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
"transport protocol mapping is only "
"valid after transport protocol match");
- return stmt_evaluate_arg(ctx, stmt,
- &inet_service_type, 2 * BITS_PER_BYTE,
- BYTEORDER_BIG_ENDIAN, expr);
+ return 0;
}
static int stmt_evaluate_l3proto(struct eval_ctx *ctx,
struct stmt *stmt, uint8_t family)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *nproto;
- nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ nproto = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if ((nproto == &proto_ip && family != NFPROTO_IPV4) ||
(nproto == &proto_ip6 && family != NFPROTO_IPV6))
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: %s vs. %s. You must specify ip or ip6 family in %s statement",
- ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name,
+ pctx->protocol[PROTO_BASE_NETWORK_HDR].desc->name,
family2str(family),
- stmt->ops->name);
+ stmt_name(stmt));
return 0;
}
+static void expr_family_infer(struct proto_ctx *pctx, const struct expr *expr,
+ uint8_t *family)
+{
+ struct expr *i;
+
+ if (expr->etype == EXPR_MAP) {
+ switch (expr->map->etype) {
+ case EXPR_CONCAT:
+ list_for_each_entry(i, &expr->map->expressions, list) {
+ if (i->etype == EXPR_PAYLOAD) {
+ if (i->payload.desc == &proto_ip)
+ *family = NFPROTO_IPV4;
+ else if (i->payload.desc == &proto_ip6)
+ *family = NFPROTO_IPV6;
+ }
+ }
+ break;
+ case EXPR_PAYLOAD:
+ if (expr->map->payload.desc == &proto_ip)
+ *family = NFPROTO_IPV4;
+ else if (expr->map->payload.desc == &proto_ip6)
+ *family = NFPROTO_IPV6;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
- uint8_t family,
- struct expr **addr)
+ uint8_t *family, struct expr **addr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct datatype *dtype;
int err;
- if (ctx->pctx.family == NFPROTO_INET) {
- dtype = get_addr_dtype(family);
- if (dtype->size == 0)
+ if (pctx->family == NFPROTO_INET) {
+ if (*family == NFPROTO_INET ||
+ *family == NFPROTO_UNSPEC)
+ expr_family_infer(pctx, *addr, family);
+
+ dtype = get_addr_dtype(*family);
+ if (dtype->size == 0) {
return stmt_error(ctx, stmt,
- "ip or ip6 must be specified with address for inet tables.");
+ "specify `%s ip' or '%s ip6' in %s table to disambiguate",
+ stmt_name(stmt), stmt_name(stmt), family2str(pctx->family));
+ }
err = stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN, addr);
@@ -3322,10 +4298,19 @@ static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct proto_ctx *pctx = &ctx->pctx;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
struct expr *one, *two, *data, *tmp;
- const struct datatype *dtype;
- int addr_type, err;
+ const struct datatype *dtype = NULL;
+ const struct datatype *dtype2;
+ int addr_type;
+ int err;
+
+ if (stmt->nat.proto)
+ return stmt_binary_error(ctx, stmt, stmt->nat.proto,
+ "nat map and protocol are mutually exclusive");
+
+ if (stmt->nat.family == NFPROTO_INET)
+ expr_family_infer(pctx, stmt->nat.addr, &stmt->nat.family);
switch (stmt->nat.family) {
case NFPROTO_IPV4:
@@ -3335,23 +4320,30 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
addr_type = TYPE_IP6ADDR;
break;
default:
- return -1;
+ return stmt_error(ctx, stmt,
+ "specify `%s ip' or '%s ip6' in %s table to disambiguate",
+ stmt_name(stmt), stmt_name(stmt), family2str(pctx->family));
}
dtype = concat_type_alloc((addr_type << TYPE_BITS) | TYPE_INET_SERVICE);
expr_set_context(&ctx->ectx, dtype, dtype->size);
- if (expr_evaluate(ctx, &stmt->nat.addr))
- return -1;
+ if (expr_evaluate(ctx, &stmt->nat.addr)) {
+ err = -1;
+ goto out;
+ }
if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
!nat_evaluate_addr_has_th_expr(stmt->nat.addr)) {
- return stmt_binary_error(ctx, stmt->nat.addr, stmt,
+ err = stmt_binary_error(ctx, stmt->nat.addr, stmt,
"transport protocol mapping is only "
"valid after transport protocol match");
+ goto out;
}
- if (stmt->nat.addr->etype != EXPR_MAP)
- return 0;
+ if (stmt->nat.addr->etype != EXPR_MAP) {
+ err = 0;
+ goto out;
+ }
data = stmt->nat.addr->mappings->set->data;
if (data->flags & EXPR_F_INTERVAL)
@@ -3359,36 +4351,42 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
datatype_set(data, dtype);
- if (expr_ops(data)->type != EXPR_CONCAT)
- return __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ if (expr_ops(data)->type != EXPR_CONCAT) {
+ err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN,
&stmt->nat.addr);
+ goto out;
+ }
one = list_first_entry(&data->expressions, struct expr, list);
two = list_entry(one->list.next, struct expr, list);
- if (one == two || !list_is_last(&two->list, &data->expressions))
- return __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ if (one == two || !list_is_last(&two->list, &data->expressions)) {
+ err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN,
&stmt->nat.addr);
+ goto out;
+ }
- dtype = get_addr_dtype(stmt->nat.family);
+ dtype2 = get_addr_dtype(stmt->nat.family);
tmp = one;
- err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ err = __stmt_evaluate_arg(ctx, stmt, dtype2, dtype2->size,
BYTEORDER_BIG_ENDIAN,
&tmp);
if (err < 0)
- return err;
+ goto out;
if (tmp != one)
BUG("Internal error: Unexpected alteration of l3 expression");
tmp = two;
err = nat_evaluate_transport(ctx, stmt, &tmp);
if (err < 0)
- return err;
+ goto out;
if (tmp != two)
BUG("Internal error: Unexpected alteration of l4 expression");
+out:
+ datatype_free(dtype);
return err;
}
@@ -3414,6 +4412,12 @@ static bool nat_concat_map(struct eval_ctx *ctx, struct stmt *stmt)
if (expr_evaluate(ctx, &stmt->nat.addr->mappings))
return false;
+ if (!set_is_datamap(stmt->nat.addr->mappings->set->flags)) {
+ expr_error(ctx->msgs, stmt->nat.addr->mappings,
+ "Expression is not a map");
+ return false;
+ }
+
if (stmt->nat.addr->mappings->set->data->etype == EXPR_CONCAT ||
stmt->nat.addr->mappings->set->data->dtype->subtypes) {
stmt->nat.type_flags |= STMT_NAT_F_CONCAT;
@@ -3451,7 +4455,7 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
- err = stmt_evaluate_addr(ctx, stmt, stmt->nat.family,
+ err = stmt_evaluate_addr(ctx, stmt, &stmt->nat.family,
&stmt->nat.addr);
if (err < 0)
return err;
@@ -3471,13 +4475,14 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
int err;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6: /* fallthrough */
if (stmt->tproxy.family == NFPROTO_UNSPEC)
- stmt->tproxy.family = ctx->pctx.family;
+ stmt->tproxy.family = pctx->family;
break;
case NFPROTO_INET:
break;
@@ -3486,7 +4491,7 @@ static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
"tproxy is only supported for IPv4/IPv6/INET");
}
- if (ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL)
+ if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL)
return stmt_error(ctx, stmt, "Transparent proxy support requires"
" transport protocol match");
@@ -3498,22 +4503,22 @@ static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
return err;
if (stmt->tproxy.addr != NULL) {
- if (stmt->tproxy.addr->etype == EXPR_RANGE)
- return stmt_error(ctx, stmt, "Address ranges are not supported for tproxy.");
-
- err = stmt_evaluate_addr(ctx, stmt, stmt->tproxy.family,
+ err = stmt_evaluate_addr(ctx, stmt, &stmt->tproxy.family,
&stmt->tproxy.addr);
-
if (err < 0)
return err;
+
+ if (stmt->tproxy.addr->etype == EXPR_RANGE)
+ return stmt_error(ctx, stmt, "Address ranges are not supported for tproxy.");
}
if (stmt->tproxy.port != NULL) {
- if (stmt->tproxy.port->etype == EXPR_RANGE)
- return stmt_error(ctx, stmt, "Port ranges are not supported for tproxy.");
err = nat_evaluate_transport(ctx, stmt, &stmt->tproxy.port);
if (err < 0)
return err;
+
+ if (stmt->tproxy.port->etype == EXPR_RANGE)
+ return stmt_error(ctx, stmt, "Port ranges are not supported for tproxy.");
}
return 0;
@@ -3550,7 +4555,7 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
memset(&h, 0, sizeof(h));
handle_merge(&h, &chain->handle);
h.family = ctx->rule->handle.family;
- xfree(h.table.name);
+ free_const(h.table.name);
h.table.name = xstrdup(ctx->rule->handle.table.name);
h.chain.location = stmt->location;
h.chain_id = chain->handle.chain_id;
@@ -3570,9 +4575,9 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
struct handle h2 = {};
handle_merge(&rule->handle, &ctx->rule->handle);
- xfree(rule->handle.table.name);
+ free_const(rule->handle.table.name);
rule->handle.table.name = xstrdup(ctx->rule->handle.table.name);
- xfree(rule->handle.chain.name);
+ free_const(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)
@@ -3596,9 +4601,10 @@ static int stmt_evaluate_optstrip(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
int err;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
if (stmt->dup.to == NULL)
@@ -3615,6 +4621,9 @@ static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
&stmt->dup.dev);
if (err < 0)
return err;
+
+ if (stmt->dup.dev->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->dup.dev);
}
break;
case NFPROTO_NETDEV:
@@ -3633,15 +4642,20 @@ static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
default:
return stmt_error(ctx, stmt, "unsupported family");
}
+
+ if (stmt->dup.to->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->dup.to);
+
return 0;
}
static int stmt_evaluate_fwd(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct datatype *dtype;
int err, len;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_NETDEV:
if (stmt->fwd.dev == NULL)
return stmt_error(ctx, stmt,
@@ -3653,6 +4667,9 @@ static int stmt_evaluate_fwd(struct eval_ctx *ctx, struct stmt *stmt)
if (err < 0)
return err;
+ if (stmt->fwd.dev->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->fwd.dev);
+
if (stmt->fwd.addr != NULL) {
switch (stmt->fwd.family) {
case NFPROTO_IPV4:
@@ -3671,6 +4688,9 @@ static int stmt_evaluate_fwd(struct eval_ctx *ctx, struct stmt *stmt)
&stmt->fwd.addr);
if (err < 0)
return err;
+
+ if (stmt->fwd.addr->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->fwd.addr);
}
break;
default:
@@ -3704,39 +4724,12 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
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;
+ unsigned int len = strlen(stmt->log.prefix);
- 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)
+ 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;
+ else if (len == 0)
+ return stmt_error(ctx, stmt, "log prefix must have a minimum length of 1 character");
return 0;
}
@@ -3777,7 +4770,7 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
return expr_error(ctx->msgs, stmt->set.set,
"Expression does not refer to a set");
- if (stmt_evaluate_arg(ctx, stmt,
+ if (stmt_evaluate_key(ctx, stmt,
stmt->set.set->set->key->dtype,
stmt->set.set->set->key->len,
stmt->set.set->set->key->byteorder,
@@ -3790,11 +4783,8 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
return expr_error(ctx->msgs, stmt->set.key,
"Key expression comments are not supported");
list_for_each_entry(this, &stmt->set.stmt_list, list) {
- if (stmt_evaluate(ctx, this) < 0)
+ if (stmt_evaluate_stateful(ctx, this, "set") < 0)
return -1;
- if (!(this->flags & STMT_F_STATEFUL))
- return stmt_error(ctx, this,
- "statement must be stateful");
}
this_set = stmt->set.set->set;
@@ -3820,7 +4810,11 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
return expr_error(ctx->msgs, stmt->map.set,
"Expression does not refer to a set");
- if (stmt_evaluate_arg(ctx, stmt,
+ if (!set_is_map(stmt->map.set->set->flags))
+ return expr_error(ctx->msgs, stmt->map.set,
+ "%s is not a map", stmt->map.set->set->handle.set.name);
+
+ if (stmt_evaluate_key(ctx, stmt,
stmt->map.set->set->key->dtype,
stmt->map.set->set->key->len,
stmt->map.set->set->key->byteorder,
@@ -3845,13 +4839,13 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->map.data->comment != NULL)
return expr_error(ctx->msgs, stmt->map.data,
"Data expression comments are not supported");
+ if (stmt->map.data->timeout > 0)
+ return expr_error(ctx->msgs, stmt->map.data,
+ "Data expression timeouts are not supported");
list_for_each_entry(this, &stmt->map.stmt_list, list) {
- if (stmt_evaluate(ctx, this) < 0)
+ if (stmt_evaluate_stateful(ctx, this, "map") < 0)
return -1;
- if (!(this->flags & STMT_F_STATEFUL))
- return stmt_error(ctx, this,
- "statement must be stateful");
}
return 0;
@@ -3860,6 +4854,7 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
{
struct expr *map = stmt->objref.expr;
+ uint32_t set_flags = NFT_SET_OBJECT;
struct expr *mappings;
struct expr *key;
@@ -3871,17 +4866,22 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
"Map expression can not be constant");
mappings = map->mappings;
- mappings->set_flags |= NFT_SET_OBJECT;
switch (map->mappings->etype) {
case EXPR_SET:
+ set_flags |= mappings->set_flags;
+ /* fallthrough */
+ case EXPR_VARIABLE:
key = constant_expr_alloc(&stmt->location,
ctx->ectx.dtype,
ctx->ectx.byteorder,
ctx->ectx.len, NULL);
mappings = implicit_set_declaration(ctx, "__objmap%d",
- key, NULL, mappings);
+ key, NULL, mappings,
+ set_flags | NFT_SET_ANONYMOUS);
+ if (!mappings)
+ return -1;
mappings->set->objtype = stmt->objref.type;
map->mappings = mappings;
@@ -3890,6 +4890,11 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+ if (map->mappings->set->init->etype != EXPR_SET) {
+ return expr_error(ctx->msgs, map->mappings->set->init,
+ "Expression is not a map");
+ }
+
if (set_is_interval(map->mappings->set->init->set_flags) &&
!(map->mappings->set->init->set_flags & NFT_SET_CONCAT) &&
interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0)
@@ -3910,11 +4915,12 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
"Expression is not a map with objects");
break;
default:
- BUG("invalid mapping expression %s\n",
- expr_name(map->mappings));
+ return expr_binary_error(ctx->msgs, map->mappings, map->map,
+ "invalid mapping expression %s",
+ expr_name(map->mappings));
}
- if (!datatype_equal(map->map->dtype, map->mappings->set->key->dtype))
+ if (!datatype_compatible(map->mappings->set->key->dtype, map->map->dtype))
return expr_binary_error(ctx->msgs, map->mappings, map->map,
"datatype mismatch, map expects %s, "
"mapping expression has type %s",
@@ -3955,16 +4961,19 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) {
struct error_record *erec;
erec = erec_create(EREC_INFORMATIONAL, &stmt->location,
- "Evaluate %s", stmt->ops->name);
+ "Evaluate %s", stmt_name(stmt));
erec_print(&ctx->nft->output, erec, ctx->nft->debug_mask);
stmt_print(stmt, &ctx->nft->output);
nft_print(&ctx->nft->output, "\n\n");
erec_destroy(erec);
}
- switch (stmt->ops->type) {
+ ctx->stmt_len = 0;
+
+ switch (stmt->type) {
case STMT_CONNLIMIT:
case STMT_COUNTER:
+ case STMT_LAST:
case STMT_LIMIT:
case STMT_QUOTA:
case STMT_NOTRACK:
@@ -4011,7 +5020,7 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
case STMT_OPTSTRIP:
return stmt_evaluate_optstrip(ctx, stmt);
default:
- BUG("unknown statement type %s\n", stmt->ops->name);
+ BUG("unknown statement type %d\n", stmt->type);
}
}
@@ -4041,11 +5050,14 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
return -1;
cmd->elem.set = set_get(set);
+ if (set_is_interval(ctx->set->flags)) {
+ if (!(set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, cmd->expr) < 0)
+ return -1;
- if (set_is_interval(ctx->set->flags) &&
- !(set->flags & NFT_SET_CONCAT) &&
- interval_set_eval(ctx, ctx->set, cmd->expr) < 0)
- return -1;
+ assert(cmd->expr->etype == EXPR_SET);
+ cmd->expr->set_flags |= NFT_SET_INTERVAL;
+ }
ctx->set = NULL;
@@ -4085,11 +5097,10 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
i->dtype->type == TYPE_INTEGER) {
struct datatype *dtype;
- dtype = dtype_clone(i->dtype);
+ dtype = datatype_clone(i->dtype);
dtype->size = i->len;
dtype->byteorder = i->byteorder;
- dtype->refcnt = 1;
- i->dtype = dtype;
+ __datatype_set(i, dtype);
}
if (i->dtype->size == 0 && i->len == 0)
@@ -4099,20 +5110,25 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
"expressions",
i->dtype->name);
- if (i->dtype->size)
- assert(i->len == i->dtype->size);
-
flags &= i->flags;
ntype = concat_subtype_add(ntype, i->dtype->type);
dsize_bytes = div_round_up(i->len, BITS_PER_BYTE);
+
+ if (i->dtype->size)
+ assert(dsize_bytes == div_round_up(i->dtype->size, BITS_PER_BYTE));
+
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
size += netlink_padded_len(i->len);
+
+ if (size > NFT_MAX_EXPR_LEN_BITS)
+ return expr_error(ctx->msgs, i, "Concatenation of size %u exceeds maximum size of %u",
+ size, NFT_MAX_EXPR_LEN_BITS);
}
(*expr)->flags |= flags;
- datatype_set(*expr, concat_type_alloc(ntype));
+ __datatype_set(*expr, concat_type_alloc(ntype));
(*expr)->len = size;
expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
@@ -4121,6 +5137,46 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+static int elems_evaluate(struct eval_ctx *ctx, struct set *set)
+{
+ ctx->set = set;
+ if (set->init != NULL) {
+ if (set->key == NULL)
+ return set_error(ctx, set, "set definition does not specify key");
+
+ __expr_set_context(&ctx->ectx, set->key->dtype,
+ set->key->byteorder, set->key->len, 0);
+ if (expr_evaluate(ctx, &set->init) < 0) {
+ set->errors = true;
+ return -1;
+ }
+ if (set->init->etype != EXPR_SET)
+ return expr_error(ctx->msgs, set->init, "Set %s: Unexpected initial type %s, missing { }?",
+ set->handle.set.name, expr_name(set->init));
+ }
+
+ if (set_is_interval(ctx->set->flags) &&
+ !(ctx->set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, set->init) < 0)
+ return -1;
+
+ ctx->set = NULL;
+
+ return 0;
+}
+
+static bool set_type_compatible(const struct set *set, const struct set *existing_set)
+{
+ if (set_is_datamap(set->flags))
+ return set_is_datamap(existing_set->flags);
+
+ if (set_is_objmap(set->flags))
+ return set_is_objmap(existing_set->flags);
+
+ assert(!set_is_map(set->flags));
+ return !set_is_map(existing_set->flags);
+}
+
static int set_evaluate(struct eval_ctx *ctx, struct set *set)
{
struct set *existing_set = NULL;
@@ -4129,6 +5185,12 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
struct stmt *stmt;
const char *type;
+ type = set_is_map(set->flags) ? "map" : "set";
+
+ if (set->key == NULL)
+ return set_error(ctx, set, "%s definition does not specify key",
+ type);
+
if (!set_is_anonymous(set->flags)) {
table = table_cache_find(&ctx->nft->cache.table_cache,
set->handle.table.name,
@@ -4137,15 +5199,22 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
return table_not_found(ctx);
existing_set = set_cache_find(table, set->handle.set.name);
- if (!existing_set)
+ if (existing_set) {
+ if (existing_set->flags & NFT_SET_EVAL) {
+ uint32_t existing_flags = existing_set->flags & ~NFT_SET_EVAL;
+ uint32_t new_flags = set->flags & ~NFT_SET_EVAL;
+
+ if (existing_flags == new_flags)
+ set->flags |= NFT_SET_EVAL;
+ }
+ } else {
set_cache_add(set_get(set), table);
+ }
}
if (!(set->flags & NFT_SET_INTERVAL) && set->automerge)
return set_error(ctx, set, "auto-merge only works with interval sets");
- type = set_is_map(set->flags) ? "map" : "set";
-
if (set->key == NULL)
return set_error(ctx, set, "%s definition does not specify key",
type);
@@ -4165,6 +5234,24 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
sizeof(set->desc.field_len));
set->desc.field_count = set->key->field_count;
set->flags |= NFT_SET_CONCAT;
+
+ if (set->automerge)
+ set->automerge = false;
+ }
+
+ if (set_is_anonymous(set->flags) && set->key->etype == EXPR_CONCAT) {
+ struct expr *i;
+
+ list_for_each_entry(i, &set->init->expressions, list) {
+ if ((i->etype == EXPR_SET_ELEM &&
+ i->key->etype != EXPR_CONCAT &&
+ i->key->etype != EXPR_SET_ELEM_CATCHALL) ||
+ (i->etype == EXPR_MAPPING &&
+ i->left->etype == EXPR_SET_ELEM &&
+ i->left->key->etype != EXPR_CONCAT &&
+ i->left->key->etype != EXPR_SET_ELEM_CATCHALL))
+ return expr_error(ctx->msgs, i, "expression is not a concatenation");
+ }
}
if (set_is_datamap(set->flags)) {
@@ -4195,8 +5282,11 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (set->timeout)
set->flags |= NFT_SET_TIMEOUT;
- list_for_each_entry(stmt, &set->stmt_list, list)
+ list_for_each_entry(stmt, &set->stmt_list, list) {
+ if (stmt_evaluate_stateful(ctx, stmt,type) < 0)
+ return -1;
num_stmts++;
+ }
if (num_stmts > 1)
set->flags |= NFT_SET_EXPR;
@@ -4210,24 +5300,29 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
return 0;
}
- set->existing_set = existing_set;
- ctx->set = set;
- if (set->init != NULL) {
- __expr_set_context(&ctx->ectx, set->key->dtype,
- set->key->byteorder, set->key->len, 0);
- if (expr_evaluate(ctx, &set->init) < 0)
- return -1;
- if (set->init->etype != EXPR_SET)
- return expr_error(ctx->msgs, set->init, "Set %s: Unexpected initial type %s, missing { }?",
- set->handle.set.name, expr_name(set->init));
+ if (existing_set) {
+ if (set_is_interval(set->flags) && !set_is_interval(existing_set->flags))
+ return set_error(ctx, set,
+ "existing %s lacks interval flag", type);
+ if (set->data && existing_set->data &&
+ !datatype_equal(existing_set->data->dtype, set->data->dtype))
+ return set_error(ctx, set,
+ "%s already exists with different datatype (%s vs %s)",
+ type, existing_set->data->dtype->desc,
+ set->data->dtype->desc);
+ if (!datatype_equal(existing_set->key->dtype, set->key->dtype))
+ return set_error(ctx, set,
+ "%s already exists with different datatype (%s vs %s)",
+ type, existing_set->key->dtype->desc,
+ set->key->dtype->desc);
+ /* Catch attempt to merge set and map */
+ if (!set_type_compatible(set, existing_set))
+ return set_error(ctx, set, "Cannot merge %s with incompatible existing %s of same name",
+ type,
+ set_is_map(existing_set->flags) ? "map" : "set");
}
- if (set_is_interval(ctx->set->flags) &&
- !(ctx->set->flags & NFT_SET_CONCAT) &&
- interval_set_eval(ctx, ctx->set, set->init) < 0)
- return -1;
-
- ctx->set = NULL;
+ set->existing_set = existing_set;
return 0;
}
@@ -4258,7 +5353,7 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
NFT_NAME_MAXLEN);
loc = prio->expr->location;
- if (sscanf(prio_str, "%s %c %d", prio_fst, &op, &prio_snd) < 3) {
+ if (sscanf(prio_str, "%255s %c %d", prio_fst, &op, &prio_snd) < 3) {
priority = std_prio_lookup(prio_str, family, hook);
if (priority == NF_IP_PRI_LAST)
return false;
@@ -4333,7 +5428,7 @@ static bool evaluate_device_expr(struct eval_ctx *ctx, struct expr **dev_expr)
case EXPR_VALUE:
break;
default:
- BUG("invalid expresion type %s\n", expr_name(expr));
+ BUG("invalid expression type %s\n", expr_name(expr));
break;
}
@@ -4356,8 +5451,12 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
if (table == NULL)
return table_not_found(ctx);
- if (!ft_cache_find(table, ft->handle.flowtable.name))
+ if (!ft_cache_find(table, ft->handle.flowtable.name)) {
+ if (!ft->hook.name && !ft->dev_expr)
+ return chain_error(ctx, ft, "missing hook and priority in flowtable declaration");
+
ft_cache_add(flowtable_get(ft), table);
+ }
if (ft->hook.name) {
ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name);
@@ -4406,6 +5505,10 @@ static int rule_cache_update(struct eval_ctx *ctx, enum cmd_ops op)
if (!table)
return table_not_found(ctx);
+ /* chain is anonymous, adding new rules via index is not supported. */
+ if (!rule->handle.chain.name)
+ return 0;
+
chain = chain_cache_find(table, rule->handle.chain.name);
if (!chain)
return chain_not_found(ctx);
@@ -4467,7 +5570,9 @@ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule,
struct stmt *stmt, *tstmt = NULL;
struct error_record *erec;
- proto_ctx_init(&ctx->pctx, rule->handle.family, ctx->nft->debug_mask);
+ proto_ctx_init(&ctx->_pctx[0], rule->handle.family, ctx->nft->debug_mask, false);
+ /* use NFPROTO_BRIDGE to set up proto_eth as base protocol. */
+ proto_ctx_init(&ctx->_pctx[1], NFPROTO_BRIDGE, ctx->nft->debug_mask, true);
memset(&ctx->ectx, 0, sizeof(ctx->ectx));
ctx->rule = rule;
@@ -4482,6 +5587,8 @@ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule,
return -1;
if (stmt->flags & STMT_F_TERMINAL)
tstmt = stmt;
+
+ ctx->inner_desc = NULL;
}
erec = rule_postprocess(rule);
@@ -4545,7 +5652,6 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
{
struct table *table;
- struct rule *rule;
table = table_cache_find(&ctx->nft->cache.table_cache,
ctx->cmd->handle.table.name,
@@ -4555,7 +5661,7 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
if (chain == NULL) {
if (!chain_cache_find(table, ctx->cmd->handle.chain.name)) {
- chain = chain_alloc(NULL);
+ chain = chain_alloc();
handle_merge(&chain->handle, &ctx->cmd->handle);
chain_cache_add(chain, table);
}
@@ -4585,15 +5691,17 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
return chain_error(ctx, chain, "invalid policy expression %s",
expr_name(chain->policy));
}
+ }
+
+ if (chain->dev_expr) {
+ if (!(chain->flags & CHAIN_F_BASECHAIN))
+ chain->flags |= CHAIN_F_BASECHAIN;
if (chain->handle.family == NFPROTO_NETDEV ||
(chain->handle.family == NFPROTO_INET &&
chain->hook.num == NF_INET_INGRESS)) {
- 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))
+ if (chain->dev_expr &&
+ !evaluate_device_expr(ctx, &chain->dev_expr))
return -1;
} else if (chain->dev_expr) {
return __stmt_binary_error(ctx, &chain->dev_expr->location, NULL,
@@ -4601,11 +5709,6 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
}
}
- list_for_each_entry(rule, &chain->rules, list) {
- handle_merge(&rule->handle, &chain->handle);
- if (rule_evaluate(ctx, rule, CMD_INVALID) < 0)
- return -1;
- }
return 0;
}
@@ -4639,8 +5742,8 @@ static int ct_timeout_evaluate(struct eval_ctx *ctx, struct obj *obj)
ct->timeout[ts->timeout_index] = ts->timeout_value;
list_del(&ts->head);
- xfree(ts->timeout_str);
- xfree(ts);
+ free_const(ts->timeout_str);
+ free(ts);
}
return 0;
@@ -4673,11 +5776,6 @@ static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj)
static int table_evaluate(struct eval_ctx *ctx, struct table *table)
{
- struct flowtable *ft;
- struct chain *chain;
- struct set *set;
- struct obj *obj;
-
if (!table_cache_find(&ctx->nft->cache.table_cache,
ctx->cmd->handle.table.name,
ctx->cmd->handle.family)) {
@@ -4690,34 +5788,6 @@ static int table_evaluate(struct eval_ctx *ctx, struct table *table)
}
}
- if (ctx->cmd->table == NULL)
- return 0;
-
- ctx->table = table;
- list_for_each_entry(set, &table->sets, list) {
- expr_set_context(&ctx->ectx, NULL, 0);
- handle_merge(&set->handle, &table->handle);
- if (set_evaluate(ctx, set) < 0)
- return -1;
- }
- list_for_each_entry(chain, &table->chains, list) {
- handle_merge(&chain->handle, &table->handle);
- ctx->cmd->handle.chain.location = chain->location;
- if (chain_evaluate(ctx, chain) < 0)
- return -1;
- }
- list_for_each_entry(ft, &table->flowtables, list) {
- handle_merge(&ft->handle, &table->handle);
- if (flowtable_evaluate(ctx, ft) < 0)
- return -1;
- }
- list_for_each_entry(obj, &table->objs, list) {
- handle_merge(&obj->handle, &table->handle);
- if (obj_evaluate(ctx, obj) < 0)
- return -1;
- }
-
- ctx->table = NULL;
return 0;
}
@@ -4729,6 +5799,8 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SET:
handle_merge(&cmd->set->handle, &cmd->handle);
return set_evaluate(ctx, cmd->set);
+ case CMD_OBJ_SETELEMS:
+ return elems_evaluate(ctx, cmd->set);
case CMD_OBJ_RULE:
handle_merge(&cmd->rule->handle, &cmd->handle);
return rule_evaluate(ctx, cmd->rule, cmd->op);
@@ -4928,7 +6000,7 @@ static int obj_not_found(struct eval_ctx *ctx, const struct location *loc,
return cmd_error(ctx, loc, "%s", strerror(ENOENT));
return cmd_error(ctx, loc,
- "%s; did you mean obj ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean obj '%s' in table %s '%s'?",
strerror(ENOENT), obj->handle.obj.name,
family2str(obj->handle.family),
table->handle.table.name);
@@ -4974,38 +6046,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
return 0;
case CMD_OBJ_SET:
- table = table_cache_find(&ctx->nft->cache.table_cache,
- cmd->handle.table.name,
- cmd->handle.family);
- if (!table)
- return table_not_found(ctx);
-
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return set_not_found(ctx, &ctx->cmd->handle.set.location,
- ctx->cmd->handle.set.name);
- else if (!set_is_literal(set->flags))
- return cmd_error(ctx, &ctx->cmd->handle.set.location,
- "%s", strerror(ENOENT));
-
- return 0;
- case CMD_OBJ_METER:
- table = table_cache_find(&ctx->nft->cache.table_cache,
- cmd->handle.table.name,
- cmd->handle.family);
- if (!table)
- return table_not_found(ctx);
-
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return set_not_found(ctx, &ctx->cmd->handle.set.location,
- ctx->cmd->handle.set.name);
- else if (!set_is_meter(set->flags))
- return cmd_error(ctx, &ctx->cmd->handle.set.location,
- "%s", strerror(ENOENT));
-
- return 0;
case CMD_OBJ_MAP:
+ case CMD_OBJ_METER:
table = table_cache_find(&ctx->nft->cache.table_cache,
cmd->handle.table.name,
cmd->handle.family);
@@ -5016,10 +6058,13 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
if (set == NULL)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
- else if (!map_is_literal(set->flags))
+ if ((cmd->obj == CMD_OBJ_SET && !set_is_literal(set->flags)) ||
+ (cmd->obj == CMD_OBJ_MAP && !map_is_literal(set->flags)) ||
+ (cmd->obj == CMD_OBJ_METER && !set_is_meter_compat(set->flags)))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+ cmd->set = set_get(set);
return 0;
case CMD_OBJ_CHAIN:
table = table_cache_find(&ctx->nft->cache.table_cache,
@@ -5069,6 +6114,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_FLOWTABLES:
case CMD_OBJ_SECMARKS:
case CMD_OBJ_SYNPROXYS:
+ case CMD_OBJ_CT_TIMEOUTS:
+ case CMD_OBJ_CT_EXPECTATIONS:
if (cmd->handle.table.name == NULL)
return 0;
if (!table_cache_find(&ctx->nft->cache.table_cache,
@@ -5104,6 +6151,8 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_QUOTA:
case CMD_OBJ_COUNTERS:
case CMD_OBJ_QUOTAS:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
if (cmd->handle.table.name == NULL)
return 0;
if (!table_cache_find(&ctx->nft->cache.table_cache,
@@ -5112,6 +6161,13 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
return table_not_found(ctx);
return 0;
+ case CMD_OBJ_ELEMENTS:
+ return setelem_evaluate(ctx, cmd);
+ case CMD_OBJ_TABLE:
+ case CMD_OBJ_CHAIN:
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ return cmd_evaluate_list(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -5185,7 +6241,7 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
if (set == NULL)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
- else if (!set_is_meter(set->flags))
+ else if (!set_is_meter_compat(set->flags))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
@@ -5348,11 +6404,12 @@ static const char * const cmd_op_name[] = {
[CMD_EXPORT] = "export",
[CMD_MONITOR] = "monitor",
[CMD_DESCRIBE] = "describe",
+ [CMD_DESTROY] = "destroy",
};
static const char *cmd_op_to_name(enum cmd_ops op)
{
- if (op > CMD_DESCRIBE)
+ if (op >= array_size(cmd_op_name))
return "unknown";
return cmd_op_name[op];
@@ -5380,6 +6437,7 @@ int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_INSERT:
return cmd_evaluate_add(ctx, cmd);
case CMD_DELETE:
+ case CMD_DESTROY:
return cmd_evaluate_delete(ctx, cmd);
case CMD_GET:
return cmd_evaluate_get(ctx, cmd);
@@ -5399,7 +6457,9 @@ int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
return cmd_evaluate_monitor(ctx, cmd);
case CMD_IMPORT:
return cmd_evaluate_import(ctx, cmd);
- default:
- BUG("invalid command operation %u\n", cmd->op);
+ case CMD_INVALID:
+ break;
};
+
+ BUG("invalid command operation %u\n", cmd->op);
}
diff --git a/src/expression.c b/src/expression.c
index 7390089c..aa97413d 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -8,11 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <limits.h>
#include <expression.h>
@@ -29,6 +28,7 @@
extern const struct expr_ops ct_expr_ops;
extern const struct expr_ops fib_expr_ops;
extern const struct expr_ops hash_expr_ops;
+extern const struct expr_ops inner_expr_ops;
extern const struct expr_ops meta_expr_ops;
extern const struct expr_ops numgen_expr_ops;
extern const struct expr_ops osf_expr_ops;
@@ -94,7 +94,7 @@ void expr_free(struct expr *expr)
*/
if (expr->etype != EXPR_INVALID)
expr_destroy(expr);
- xfree(expr);
+ free(expr);
}
void expr_print(const struct expr *expr, struct output_ctx *octx)
@@ -140,8 +140,10 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, "%s expression, datatype %s (%s)",
expr_name(expr), dtype->name, dtype->desc);
- if (dtype == &invalid_type)
+ if (dtype == &invalid_type) {
+ nft_print(octx, "\n");
return;
+ }
}
if (dtype->basetype != NULL) {
@@ -181,15 +183,6 @@ 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)
{
@@ -314,7 +307,7 @@ static void symbol_expr_clone(struct expr *new, const struct expr *expr)
static void symbol_expr_destroy(struct expr *expr)
{
- xfree(expr->identifier);
+ free_const(expr->identifier);
}
static const struct expr_ops symbol_expr_ops = {
@@ -378,6 +371,84 @@ struct expr *variable_expr_alloc(const struct location *loc,
return expr;
}
+#define NFTNL_UDATA_CONSTANT_TYPE 0
+#define NFTNL_UDATA_CONSTANT_MAX NFTNL_UDATA_CONSTANT_TYPE
+
+#define CONSTANT_EXPR_NFQUEUE_ID 0
+
+static int constant_expr_build_udata(struct nftnl_udata_buf *udbuf,
+ const struct expr *expr)
+{
+ uint32_t type;
+
+ if (expr->dtype == &queue_type)
+ type = CONSTANT_EXPR_NFQUEUE_ID;
+ else
+ return -1;
+
+ if (!nftnl_udata_put_u32(udbuf, NFTNL_UDATA_CONSTANT_TYPE, type))
+ return -1;
+
+ return 0;
+}
+
+static int constant_parse_udata(const struct nftnl_udata *attr, void *data)
+{
+ const struct nftnl_udata **ud = data;
+ uint8_t type = nftnl_udata_type(attr);
+ uint8_t len = nftnl_udata_len(attr);
+ uint32_t value;
+
+ switch (type) {
+ case NFTNL_UDATA_CONSTANT_TYPE:
+ if (len != sizeof(uint32_t))
+ return -1;
+
+ value = nftnl_udata_get_u32(attr);
+ switch (value) {
+ case CONSTANT_EXPR_NFQUEUE_ID:
+ break;
+ default:
+ return -1;
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ ud[type] = attr;
+
+ return 0;
+}
+
+static struct expr *constant_expr_parse_udata(const struct nftnl_udata *attr)
+{
+ const struct nftnl_udata *ud[NFTNL_UDATA_CONSTANT_MAX + 1] = {};
+ const struct datatype *dtype = NULL;
+ uint32_t type;
+ int err;
+
+ err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr),
+ constant_parse_udata, ud);
+ if (err < 0)
+ return NULL;
+
+ if (!ud[NFTNL_UDATA_CONSTANT_TYPE])
+ return NULL;
+
+ type = nftnl_udata_get_u32(ud[NFTNL_UDATA_CONSTANT_TYPE]);
+ switch (type) {
+ case CONSTANT_EXPR_NFQUEUE_ID:
+ dtype = &queue_type;
+ break;
+ default:
+ break;
+ }
+
+ return constant_expr_alloc(&internal_location, dtype, BYTEORDER_HOST_ENDIAN,
+ 16, NULL);
+}
+
static void constant_expr_print(const struct expr *expr,
struct output_ctx *octx)
{
@@ -408,6 +479,8 @@ static const struct expr_ops constant_expr_ops = {
.cmp = constant_expr_cmp,
.clone = constant_expr_clone,
.destroy = constant_expr_destroy,
+ .build_udata = constant_expr_build_udata,
+ .parse_udata = constant_expr_parse_udata,
};
struct expr *constant_expr_alloc(const struct location *loc,
@@ -421,7 +494,7 @@ struct expr *constant_expr_alloc(const struct location *loc,
expr->flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
mpz_init2(expr->value, len);
- if (data != NULL)
+ if (data != NULL && len)
mpz_import_data(expr->value, data, byteorder,
div_round_up(len, BITS_PER_BYTE));
@@ -469,6 +542,128 @@ struct expr *constant_expr_splice(struct expr *expr, unsigned int len)
return slice;
}
+static void constant_range_expr_print_one(const struct expr *expr,
+ const mpz_t value,
+ struct output_ctx *octx)
+{
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ unsigned char data[len];
+ struct expr *dummy;
+
+ /* create dummy temporary constant expression to print range. */
+ mpz_export_data(data, value, expr->byteorder, len);
+ dummy = constant_expr_alloc(&expr->location, expr->dtype,
+ expr->byteorder, expr->len, data);
+ expr_print(dummy, octx);
+ expr_free(dummy);
+}
+
+static void constant_range_expr_print(const struct expr *expr,
+ struct output_ctx *octx)
+{
+ unsigned int flags = octx->flags;
+
+ /* similar to range_expr_print(). */
+ octx->flags &= ~(NFT_CTX_OUTPUT_SERVICE |
+ NFT_CTX_OUTPUT_REVERSEDNS |
+ NFT_CTX_OUTPUT_GUID);
+ octx->flags |= NFT_CTX_OUTPUT_NUMERIC_ALL;
+
+ constant_range_expr_print_one(expr, expr->range.low, octx);
+ nft_print(octx, "-");
+ constant_range_expr_print_one(expr, expr->range.high, octx);
+
+ octx->flags = flags;
+}
+
+static bool constant_range_expr_cmp(const struct expr *e1, const struct expr *e2)
+{
+ return expr_basetype(e1) == expr_basetype(e2) &&
+ !mpz_cmp(e1->range.low, e2->range.low) &&
+ !mpz_cmp(e1->range.high, e2->range.high);
+}
+
+static void constant_range_expr_clone(struct expr *new, const struct expr *expr)
+{
+ mpz_init_set(new->range.low, expr->range.low);
+ mpz_init_set(new->range.high, expr->range.high);
+}
+
+static void constant_range_expr_destroy(struct expr *expr)
+{
+ mpz_clear(expr->range.low);
+ mpz_clear(expr->range.high);
+}
+
+static const struct expr_ops constant_range_expr_ops = {
+ .type = EXPR_RANGE_VALUE,
+ .name = "range_value",
+ .print = constant_range_expr_print,
+ .cmp = constant_range_expr_cmp,
+ .clone = constant_range_expr_clone,
+ .destroy = constant_range_expr_destroy,
+};
+
+struct expr *constant_range_expr_alloc(const struct location *loc,
+ const struct datatype *dtype,
+ enum byteorder byteorder,
+ unsigned int len, mpz_t low, mpz_t high)
+{
+ struct expr *expr;
+
+ expr = expr_alloc(loc, EXPR_RANGE_VALUE, dtype, byteorder, len);
+ expr->flags = EXPR_F_CONSTANT;
+
+ mpz_init_set(expr->range.low, low);
+ mpz_init_set(expr->range.high, high);
+
+ return expr;
+}
+
+static void symbol_range_expr_print(const struct expr *expr, struct output_ctx *octx)
+{
+ nft_print(octx, "%s", expr->identifier_range[0]);
+ nft_print(octx, "-");
+ nft_print(octx, "%s", expr->identifier_range[1]);
+}
+
+static void symbol_range_expr_clone(struct expr *new, const struct expr *expr)
+{
+ new->symtype = expr->symtype;
+ new->scope = expr->scope;
+ new->identifier_range[0] = xstrdup(expr->identifier_range[0]);
+ new->identifier_range[1] = xstrdup(expr->identifier_range[1]);
+}
+
+static void symbol_range_expr_destroy(struct expr *expr)
+{
+ free_const(expr->identifier_range[0]);
+ free_const(expr->identifier_range[1]);
+}
+
+static const struct expr_ops symbol_range_expr_ops = {
+ .type = EXPR_RANGE_SYMBOL,
+ .name = "range_symbol",
+ .print = symbol_range_expr_print,
+ .clone = symbol_range_expr_clone,
+ .destroy = symbol_range_expr_destroy,
+};
+
+struct expr *symbol_range_expr_alloc(const struct location *loc,
+ enum symbol_types type, const struct scope *scope,
+ const char *identifier_low, const char *identifier_high)
+{
+ struct expr *expr;
+
+ expr = expr_alloc(loc, EXPR_RANGE_SYMBOL, &invalid_type,
+ BYTEORDER_INVALID, 0);
+ expr->symtype = type;
+ expr->scope = scope;
+ expr->identifier_range[0] = xstrdup(identifier_low);
+ expr->identifier_range[1] = xstrdup(identifier_high);
+ return expr;
+}
+
/*
* Allocate a constant expression with a single bit set at position n.
*/
@@ -750,6 +945,17 @@ void relational_expr_pctx_update(struct proto_ctx *ctx,
i->key->etype == EXPR_VALUE)
ops->pctx_update(ctx, &expr->location, left, i->key);
}
+ } else if (ops == &meta_expr_ops &&
+ right->etype == EXPR_SET_REF) {
+ const struct expr *key = right->set->key;
+ struct expr *tmp;
+
+ tmp = constant_expr_alloc(&expr->location, key->dtype,
+ key->byteorder, key->len,
+ NULL);
+
+ ops->pctx_update(ctx, &expr->location, left, tmp);
+ expr_free(tmp);
}
}
}
@@ -883,7 +1089,7 @@ static struct expr *expr_build_udata_recurse(struct expr *e)
{
switch (e->etype) {
case EXPR_BINOP:
- return e->left;
+ return expr_build_udata_recurse(e->left);
default:
break;
}
@@ -994,7 +1200,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
goto err_free;
etype = nftnl_udata_get_u32(nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE]);
- ops = expr_ops_by_type(etype);
+ ops = expr_ops_by_type_u32(etype);
if (!ops || !ops->parse_udata)
goto err_free;
@@ -1012,7 +1218,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
if (!dtype)
goto err_free;
- concat_expr->dtype = datatype_get(dtype);
+ __datatype_set(concat_expr, dtype);
concat_expr->len = len;
return concat_expr;
@@ -1057,11 +1263,43 @@ struct expr *list_expr_alloc(const struct location *loc)
return compound_expr_alloc(loc, EXPR_LIST);
}
-static const char *calculate_delim(const struct expr *expr, int *count)
+/* list is assumed to have two items at least, otherwise extend this! */
+struct expr *list_expr_to_binop(struct expr *expr)
+{
+ struct expr *first, *last = NULL, *i;
+
+ assert(!list_empty(&expr->expressions));
+
+ first = list_first_entry(&expr->expressions, struct expr, list);
+ i = first;
+
+ list_for_each_entry_continue(i, &expr->expressions, list) {
+ if (first) {
+ last = binop_expr_alloc(&expr->location, OP_OR, first, i);
+ first = NULL;
+ } else {
+ last = binop_expr_alloc(&expr->location, OP_OR, i, last);
+ }
+ }
+ /* list with one single item only, this should not happen. */
+ assert(!first);
+
+ /* zap list expressions, they have been moved to binop expression. */
+ init_list_head(&expr->expressions);
+ expr_free(expr);
+
+ return last;
+}
+
+static const char *calculate_delim(const struct expr *expr, int *count,
+ struct output_ctx *octx)
{
const char *newline = ",\n\t\t\t ";
const char *singleline = ", ";
+ if (octx->force_newline)
+ return newline;
+
if (set_is_anonymous(expr->set_flags))
return singleline;
@@ -1114,7 +1352,7 @@ static void set_expr_print(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, "%s", d);
expr_print(i, octx);
count++;
- d = calculate_delim(expr, &count);
+ d = calculate_delim(expr, &count, octx);
}
nft_print(octx, " }");
@@ -1290,6 +1528,32 @@ static void set_ref_expr_destroy(struct expr *expr)
set_free(expr->set);
}
+static void set_ref_expr_set_type(const struct expr *expr,
+ const struct datatype *dtype,
+ enum byteorder byteorder)
+{
+ const struct set *s = expr->set;
+
+ /* normal sets already have a precise datatype that is given in
+ * the set definition via type foo.
+ *
+ * Anon sets do not have this, and need to rely on type info
+ * generated at rule creation time.
+ *
+ * For most cases, the type info is correct.
+ * In some cases however, the kernel only stores TYPE_INTEGER.
+ *
+ * This happens with expressions that only use an integer alias
+ * type, e.g. the mptcpopt_subtype datatype.
+ *
+ * In this case nft will print the elements as numerical values
+ * because the base type lacks the ->sym_tbl information of the
+ * subtypes.
+ */
+ if (s->init && set_is_anonymous(s->flags))
+ expr_set_type(s->init, dtype, byteorder);
+}
+
static const struct expr_ops set_ref_expr_ops = {
.type = EXPR_SET_REF,
.name = "set reference",
@@ -1297,6 +1561,7 @@ static const struct expr_ops set_ref_expr_ops = {
.json = set_ref_expr_json,
.clone = set_ref_expr_clone,
.destroy = set_ref_expr_destroy,
+ .set_type = set_ref_expr_set_type,
};
struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set)
@@ -1321,9 +1586,14 @@ static void set_elem_expr_print(const struct expr *expr,
}
if (expr->timeout) {
nft_print(octx, " timeout ");
- time_print(expr->timeout, octx);
+ if (expr->timeout == NFT_NEVER_TIMEOUT)
+ nft_print(octx, "never");
+ else
+ time_print(expr->timeout, octx);
}
- if (!nft_output_stateless(octx) && expr->expiration) {
+ if (!nft_output_stateless(octx) &&
+ expr->timeout != NFT_NEVER_TIMEOUT &&
+ expr->expiration) {
nft_print(octx, " expires ");
time_print(expr->expiration, octx);
}
@@ -1335,15 +1605,14 @@ static void set_elem_expr_destroy(struct expr *expr)
{
struct stmt *stmt, *next;
- xfree(expr->comment);
+ free_const(expr->comment);
expr_free(expr->key);
list_for_each_entry_safe(stmt, next, &expr->stmt_list, list)
stmt_free(stmt);
}
-static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
+static void __set_elem_expr_clone(struct expr *new, const struct expr *expr)
{
- new->key = expr_clone(expr->key);
new->expiration = expr->expiration;
new->timeout = expr->timeout;
if (expr->comment)
@@ -1351,6 +1620,19 @@ static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
init_list_head(&new->stmt_list);
}
+static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
+{
+ new->key = expr_clone(expr->key);
+ __set_elem_expr_clone(new, expr);
+}
+
+static void set_elem_expr_set_type(const struct expr *expr,
+ const struct datatype *dtype,
+ enum byteorder byteorder)
+{
+ expr_set_type(expr->key, dtype, byteorder);
+}
+
static const struct expr_ops set_elem_expr_ops = {
.type = EXPR_SET_ELEM,
.name = "set element",
@@ -1358,6 +1640,7 @@ static const struct expr_ops set_elem_expr_ops = {
.print = set_elem_expr_print,
.json = set_elem_expr_json,
.destroy = set_elem_expr_destroy,
+ .set_type = set_elem_expr_set_type,
};
struct expr *set_elem_expr_alloc(const struct location *loc, struct expr *key)
@@ -1378,11 +1661,17 @@ static void set_elem_catchall_expr_print(const struct expr *expr,
nft_print(octx, "*");
}
+static void set_elem_catchall_expr_clone(struct expr *new, const struct expr *expr)
+{
+ __set_elem_expr_clone(new, expr);
+}
+
static const struct expr_ops set_elem_catchall_expr_ops = {
.type = EXPR_SET_ELEM_CATCHALL,
.name = "catch-all set element",
.print = set_elem_catchall_expr_print,
.json = set_elem_catchall_expr_json,
+ .clone = set_elem_catchall_expr_clone,
};
struct expr *set_elem_catchall_expr_alloc(const struct location *loc)
@@ -1396,66 +1685,13 @@ struct expr *set_elem_catchall_expr_alloc(const struct location *loc)
return expr;
}
-static void flagcmp_expr_print(const struct expr *expr, struct output_ctx *octx)
-{
- expr_print(expr->flagcmp.expr, octx);
-
- if (expr->op == OP_NEQ)
- nft_print(octx, " != ");
- else
- nft_print(octx, " ");
-
- expr_print(expr->flagcmp.value, octx);
- nft_print(octx, " / ");
- expr_print(expr->flagcmp.mask, octx);
-}
-
-static void flagcmp_expr_clone(struct expr *new, const struct expr *expr)
-{
- new->flagcmp.expr = expr_clone(expr->flagcmp.expr);
- new->flagcmp.mask = expr_clone(expr->flagcmp.mask);
- new->flagcmp.value = expr_clone(expr->flagcmp.value);
-}
-
-static void flagcmp_expr_destroy(struct expr *expr)
-{
- expr_free(expr->flagcmp.expr);
- expr_free(expr->flagcmp.mask);
- expr_free(expr->flagcmp.value);
-}
-
-static const struct expr_ops flagcmp_expr_ops = {
- .type = EXPR_FLAGCMP,
- .name = "flags comparison",
- .print = flagcmp_expr_print,
- .json = flagcmp_expr_json,
- .clone = flagcmp_expr_clone,
- .destroy = flagcmp_expr_destroy,
-};
-
-struct expr *flagcmp_expr_alloc(const struct location *loc, enum ops op,
- struct expr *match, struct expr *mask,
- struct expr *value)
-{
- struct expr *expr;
-
- expr = expr_alloc(loc, EXPR_FLAGCMP, match->dtype, match->byteorder,
- match->len);
- expr->op = op;
- expr->flagcmp.expr = match;
- expr->flagcmp.mask = mask;
- /* json output needs this operation for compatibility */
- expr->flagcmp.mask->op = OP_OR;
- expr->flagcmp.value = value;
-
- return expr;
-}
-
void range_expr_value_low(mpz_t rop, const struct expr *expr)
{
switch (expr->etype) {
case EXPR_VALUE:
return mpz_set(rop, expr->value);
+ case EXPR_RANGE_VALUE:
+ return mpz_set(rop, expr->range.low);
case EXPR_PREFIX:
return range_expr_value_low(rop, expr->prefix);
case EXPR_RANGE:
@@ -1476,6 +1712,8 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
switch (expr->etype) {
case EXPR_VALUE:
return mpz_set(rop, expr->value);
+ case EXPR_RANGE_VALUE:
+ return mpz_set(rop, expr->range.high);
case EXPR_PREFIX:
range_expr_value_low(rop, expr->prefix);
assert(expr->len >= expr->prefix_len);
@@ -1497,9 +1735,7 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
{
switch (etype) {
- case EXPR_INVALID:
- BUG("Invalid expression ops requested");
- break;
+ case EXPR_INVALID: break;
case EXPR_VERDICT: return &verdict_expr_ops;
case EXPR_SYMBOL: return &symbol_expr_ops;
case EXPR_VARIABLE: return &variable_expr_ops;
@@ -1528,23 +1764,28 @@ static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
case EXPR_FIB: return &fib_expr_ops;
case EXPR_XFRM: return &xfrm_expr_ops;
case EXPR_SET_ELEM_CATCHALL: return &set_elem_catchall_expr_ops;
- case EXPR_FLAGCMP: return &flagcmp_expr_ops;
+ case EXPR_RANGE_VALUE: return &constant_range_expr_ops;
+ case EXPR_RANGE_SYMBOL: return &symbol_range_expr_ops;
+ case __EXPR_MAX: break;
}
- BUG("Unknown expression type %d\n", etype);
+ return NULL;
}
const struct expr_ops *expr_ops(const struct expr *e)
{
- return __expr_ops_by_type(e->etype);
+ const struct expr_ops *ops;
+
+ ops = __expr_ops_by_type(e->etype);
+ if (!ops)
+ BUG("Unknown expression type %d\n", e->etype);
+
+ return ops;
}
-const struct expr_ops *expr_ops_by_type(uint32_t value)
+const struct expr_ops *expr_ops_by_type_u32(uint32_t value)
{
- /* value might come from unreliable source, such as "udata"
- * annotation of set keys. Avoid BUG() assertion.
- */
- if (value == EXPR_INVALID || value > EXPR_MAX)
+ if (value > EXPR_MAX)
return NULL;
return __expr_ops_by_type(value);
diff --git a/src/exthdr.c b/src/exthdr.c
index 3e5f5cd8..c7d876a4 100644
--- a/src/exthdr.c
+++ b/src/exthdr.c
@@ -10,11 +10,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
@@ -84,6 +83,9 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
nft_print(octx, " %s", expr->exthdr.tmpl->token);
+ } else if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) {
+ nft_print(octx, "dccp option %d", expr->exthdr.raw_type);
+ return;
} else {
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
nft_print(octx, "exthdr %s", name);
@@ -177,6 +179,8 @@ static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
case NFT_EXTHDR_OP_SCTP:
return sctp_chunk_expr_alloc(&internal_location,
desc_id, type);
+ case NFT_EXTHDR_OP_DCCP:
+ return dccpopt_expr_alloc(&internal_location, type);
case __NFT_EXTHDR_OP_MAX:
return NULL;
}
@@ -206,6 +210,7 @@ static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
case NFT_EXTHDR_OP_TCPOPT:
case NFT_EXTHDR_OP_IPV4:
case NFT_EXTHDR_OP_SCTP:
+ case NFT_EXTHDR_OP_DCCP:
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_OP, op);
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, expr->exthdr.raw_type);
break;
@@ -264,7 +269,7 @@ static void exthdr_stmt_destroy(struct stmt *stmt)
expr_free(stmt->exthdr.val);
}
-static const struct stmt_ops exthdr_stmt_ops = {
+const struct stmt_ops exthdr_stmt_ops = {
.type = STMT_EXTHDR,
.name = "exthdr",
.print = exthdr_stmt_print,
@@ -283,7 +288,7 @@ struct stmt *exthdr_stmt_alloc(const struct location *loc,
return stmt;
}
-static const struct exthdr_desc *exthdr_protocols[IPPROTO_MAX] = {
+static const struct exthdr_desc *exthdr_protocols[UINT8_MAX + 1] = {
[IPPROTO_HOPOPTS] = &exthdr_hbh,
[IPPROTO_ROUTING] = &exthdr_rt,
[IPPROTO_FRAGMENT] = &exthdr_frag,
@@ -332,14 +337,15 @@ void exthdr_init_raw(struct expr *expr, uint8_t type,
return ipopt_init_raw(expr, type, offset, len, flags, true);
if (op == NFT_EXTHDR_OP_SCTP)
return sctp_chunk_init_raw(expr, type, offset, len, flags);
+ if (op == NFT_EXTHDR_OP_DCCP)
+ return dccpopt_init_raw(expr, type, offset, len);
expr->len = len;
expr->exthdr.flags = flags;
expr->exthdr.offset = offset;
expr->exthdr.desc = NULL;
- if (type < array_size(exthdr_protocols))
- expr->exthdr.desc = exthdr_protocols[type];
+ expr->exthdr.desc = exthdr_protocols[type];
if (expr->exthdr.desc == NULL)
goto out;
@@ -398,7 +404,7 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i
found = tcpopt_find_template(expr, off, mask_len - mask_offset);
break;
case NFT_EXTHDR_OP_IPV6:
- exthdr_init_raw(expr, expr->exthdr.desc->type,
+ exthdr_init_raw(expr, expr->exthdr.raw_type,
off, mask_len - mask_offset, expr->exthdr.op, 0);
/* still failed to find a template... Bug. */
@@ -444,13 +450,23 @@ const struct exthdr_desc exthdr_hbh = {
* Routing header
*/
+/* similar to uapi/linux/ipv6.h */
+struct ip6_rt2_hdr {
+ struct ip6_rthdr rt_hdr;
+ uint32_t reserved;
+ struct in6_addr addr;
+};
+
+#define RT2_FIELD(__name, __member, __dtype) \
+ HDR_TEMPLATE(__name, __dtype, struct ip6_rt2_hdr, __member)
+
const struct exthdr_desc exthdr_rt2 = {
.name = "rt2",
.id = EXTHDR_DESC_RT2,
.type = IPPROTO_ROUTING,
.templates = {
- [RT2HDR_RESERVED] = {},
- [RT2HDR_ADDR] = {},
+ [RT2HDR_RESERVED] = RT2_FIELD("reserved", reserved, &integer_type),
+ [RT2HDR_ADDR] = RT2_FIELD("addr", addr, &ip6addr_type),
},
};
diff --git a/src/fib.c b/src/fib.c
index c6ad0f9c..e28c5224 100644
--- a/src/fib.c
+++ b/src/fib.c
@@ -4,17 +4,18 @@
* Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <erec.h>
#include <expression.h>
#include <datatype.h>
#include <gmputil.h>
#include <utils.h>
-#include <string.h>
#include <fib.h>
#include <linux/rtnetlink.h>
@@ -52,8 +53,16 @@ const struct datatype fib_addr_type = {
.sym_tbl = &addrtype_tbl,
};
-const char *fib_result_str(enum nft_fib_result result)
+const char *fib_result_str(const struct expr *expr)
{
+ enum nft_fib_result result = expr->fib.result;
+ uint32_t flags = expr->fib.flags;
+
+ /* Exception: check if route exists. */
+ if (result == NFT_FIB_RESULT_OIF &&
+ flags & NFTA_FIB_F_PRESENT)
+ return "check";
+
if (result <= NFT_FIB_RESULT_MAX)
return fib_result[result];
@@ -86,7 +95,7 @@ static void fib_expr_print(const struct expr *expr, struct output_ctx *octx)
if (flags)
nft_print(octx, "0x%x", flags);
- nft_print(octx, " %s", fib_result_str(expr->fib.result));
+ nft_print(octx, " %s", fib_result_str(expr));
}
static bool fib_expr_cmp(const struct expr *e1, const struct expr *e2)
@@ -178,7 +187,7 @@ struct expr *fib_expr_alloc(const struct location *loc,
type = &ifindex_type;
break;
case NFT_FIB_RESULT_OIFNAME:
- type = &string_type;
+ type = &ifname_type;
len = IFNAMSIZ * BITS_PER_BYTE;
break;
case NFT_FIB_RESULT_ADDRTYPE:
diff --git a/src/gmputil.c b/src/gmputil.c
index b356460f..b4529259 100644
--- a/src/gmputil.c
+++ b/src/gmputil.c
@@ -8,12 +8,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
-#include <string.h>
#include <nftables.h>
#include <datatype.h>
@@ -184,7 +184,7 @@ int mpz_vfprintf(FILE *fp, const char *f, va_list args)
str = mpz_get_str(NULL, base, *value);
ok = str && fwrite(str, 1, len, fp) == len;
- free(str);
+ nft_gmp_free(str);
if (!ok)
return -1;
@@ -197,12 +197,21 @@ int mpz_vfprintf(FILE *fp, const char *f, va_list args)
}
#endif
-static void *gmp_xrealloc(void *ptr, size_t old_size, size_t new_size)
+void nft_gmp_free(void *ptr)
{
- return xrealloc(ptr, new_size);
-}
+ void (*free_fcn)(void *, size_t);
-void gmp_init(void)
-{
- mp_set_memory_functions(xmalloc, gmp_xrealloc, NULL);
+ /* When we get allocated memory from gmp, it was allocated via the
+ * allocator() from mp_set_memory_functions(). We should pair the free
+ * with the corresponding free function, which we get via
+ * mp_get_memory_functions().
+ *
+ * It's not clear what the correct blk_size is. The default allocator
+ * function of gmp just wraps free() and ignores the extra argument.
+ * Assume 0 is fine.
+ */
+
+ mp_get_memory_functions(NULL, NULL, &free_fcn);
+
+ (*free_fcn)(ptr, 0);
}
diff --git a/src/hash.c b/src/hash.c
index 42c50407..1c8c00aa 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -4,10 +4,12 @@
* Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <datatype.h>
diff --git a/src/iface.c b/src/iface.c
index c0642e0c..a85341a1 100644
--- a/src/iface.c
+++ b/src/iface.c
@@ -2,15 +2,15 @@
* Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <stdio.h>
-#include <stdlib.h>
#include <net/if.h>
#include <time.h>
-#include <string.h>
#include <errno.h>
#include <libmnl/libmnl.h>
@@ -171,3 +171,20 @@ char *nft_if_indextoname(unsigned int ifindex, char *name)
}
return NULL;
}
+
+const struct iface *iface_cache_get_next_entry(const struct iface *prev)
+{
+ if (!iface_cache_init)
+ iface_cache_update();
+
+ if (list_empty(&iface_list))
+ return NULL;
+
+ if (!prev)
+ return list_first_entry(&iface_list, struct iface, list);
+
+ if (list_is_last(&prev->list, &iface_list))
+ return NULL;
+
+ return list_next_entry(prev, list);
+}
diff --git a/src/intervals.c b/src/intervals.c
index 13009ca1..e5bbb038 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -6,40 +6,53 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <intervals.h>
#include <rule.h>
+static void set_to_range(struct expr *init);
+
static void setelem_expr_to_range(struct expr *expr)
{
- unsigned char data[sizeof(struct in6_addr) * BITS_PER_BYTE];
- struct expr *key, *value;
+ struct expr *key;
mpz_t rop;
assert(expr->etype == EXPR_SET_ELEM);
switch (expr->key->etype) {
case EXPR_SET_ELEM_CATCHALL:
+ case EXPR_RANGE_VALUE:
+ break;
case EXPR_RANGE:
+ key = constant_range_expr_alloc(&expr->location,
+ expr->key->dtype,
+ expr->key->byteorder,
+ expr->key->len,
+ expr->key->left->value,
+ expr->key->right->value);
+ expr_free(expr->key);
+ expr->key = key;
break;
case EXPR_PREFIX:
+ if (expr->key->prefix->etype != EXPR_VALUE)
+ BUG("Prefix for unexpected type %d", expr->key->prefix->etype);
+
mpz_init(rop);
mpz_bitmask(rop, expr->key->len - expr->key->prefix_len);
if (expr_basetype(expr)->type == TYPE_STRING)
mpz_switch_byteorder(expr->key->prefix->value, expr->len / BITS_PER_BYTE);
mpz_ior(rop, rop, expr->key->prefix->value);
- mpz_export_data(data, rop, expr->key->prefix->byteorder,
- expr->key->prefix->len / BITS_PER_BYTE);
+ key = constant_range_expr_alloc(&expr->location,
+ expr->key->dtype,
+ expr->key->byteorder,
+ expr->key->len,
+ expr->key->prefix->value,
+ rop);
mpz_clear(rop);
- value = constant_expr_alloc(&expr->location,
- expr->key->prefix->dtype,
- expr->key->prefix->byteorder,
- expr->key->prefix->len, data);
- key = range_expr_alloc(&expr->location,
- expr_get(expr->key->prefix),
- value);
expr_free(expr->key);
expr->key = key;
break;
@@ -47,14 +60,17 @@ static void setelem_expr_to_range(struct expr *expr)
if (expr_basetype(expr)->type == TYPE_STRING)
mpz_switch_byteorder(expr->key->value, expr->len / BITS_PER_BYTE);
- key = range_expr_alloc(&expr->location,
- expr_clone(expr->key),
- expr_get(expr->key));
+ key = constant_range_expr_alloc(&expr->location,
+ expr->key->dtype,
+ expr->key->byteorder,
+ expr->key->len,
+ expr->key->value,
+ expr->key->value);
expr_free(expr->key);
expr->key = key;
break;
default:
- BUG("unhandled key type %d\n", expr->key->etype);
+ BUG("unhandled key type %s\n", expr_name(expr->key));
}
}
@@ -69,8 +85,8 @@ static void purge_elem(struct set_automerge_ctx *ctx, struct expr *i)
{
if (ctx->debug_mask & NFT_DEBUG_SEGTREE) {
pr_gmp_debug("remove: [%Zx-%Zx]\n",
- i->key->left->value,
- i->key->right->value);
+ i->key->range.low,
+ i->key->range.high);
}
list_move_tail(&i->list, &ctx->purge->expressions);
}
@@ -79,6 +95,7 @@ static void remove_overlapping_range(struct set_automerge_ctx *ctx,
struct expr *prev, struct expr *i)
{
if (i->flags & EXPR_F_KERNEL) {
+ i->location = prev->location;
purge_elem(ctx, i);
return;
}
@@ -97,19 +114,18 @@ static bool merge_ranges(struct set_automerge_ctx *ctx,
struct range *prev_range, struct range *range)
{
if (prev->flags & EXPR_F_KERNEL) {
+ prev->location = i->location;
purge_elem(ctx, prev);
- expr_free(i->key->left);
- i->key->left = expr_get(prev->key->left);
+ mpz_set(i->key->range.low, prev->key->range.low);
mpz_set(prev_range->high, range->high);
return true;
} else if (i->flags & EXPR_F_KERNEL) {
+ i->location = prev->location;
purge_elem(ctx, i);
- expr_free(prev->key->right);
- prev->key->right = expr_get(i->key->right);
+ mpz_set(prev->key->range.high, i->key->range.high);
mpz_set(prev_range->high, range->high);
} else {
- expr_free(prev->key->right);
- prev->key->right = expr_get(i->key->right);
+ mpz_set(prev->key->range.high, i->key->range.high);
mpz_set(prev_range->high, range->high);
list_del(&i->list);
expr_free(i);
@@ -125,7 +141,7 @@ static void set_sort_splice(struct expr *init, struct set *set)
set_to_range(init);
list_expr_sort(&init->expressions);
- if (!existing_set)
+ if (!existing_set || existing_set->errors)
return;
if (existing_set->init) {
@@ -138,6 +154,14 @@ static void set_sort_splice(struct expr *init, struct set *set)
}
}
+static void set_prev_elem(struct expr **prev, struct expr *i,
+ struct range *prev_range, struct range *range)
+{
+ *prev = i;
+ mpz_set(prev_range->low, range->low);
+ mpz_set(prev_range->high, range->high);
+}
+
static void setelem_automerge(struct set_automerge_ctx *ctx)
{
struct expr *i, *next, *prev = NULL;
@@ -158,9 +182,7 @@ static void setelem_automerge(struct set_automerge_ctx *ctx)
range_expr_value_high(range.high, i);
if (!prev) {
- prev = i;
- mpz_set(prev_range.low, range.low);
- mpz_set(prev_range.high, range.high);
+ set_prev_elem(&prev, i, &prev_range, &range);
continue;
}
@@ -182,9 +204,7 @@ static void setelem_automerge(struct set_automerge_ctx *ctx)
}
}
- prev = i;
- mpz_set(prev_range.low, range.low);
- mpz_set(prev_range.high, range.high);
+ set_prev_elem(&prev, i, &prev_range, &range);
}
mpz_clear(prev_range.low);
@@ -213,7 +233,7 @@ static struct expr *interval_expr_key(struct expr *i)
return elem;
}
-void set_to_range(struct expr *init)
+static void set_to_range(struct expr *init)
{
struct expr *i, *elem;
@@ -254,7 +274,7 @@ int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set,
} else if (existing_set) {
if (debug_mask & NFT_DEBUG_SEGTREE) {
pr_gmp_debug("add: [%Zx-%Zx]\n",
- i->key->left->value, i->key->right->value);
+ i->key->range.low, i->key->range.high);
}
clone = expr_clone(i);
clone->flags |= EXPR_F_KERNEL;
@@ -288,15 +308,15 @@ static void remove_elem(struct expr *prev, struct set *set, struct expr *purge)
static void __adjust_elem_left(struct set *set, struct expr *prev, struct expr *i)
{
prev->flags &= ~EXPR_F_KERNEL;
- expr_free(prev->key->left);
- prev->key->left = expr_get(i->key->right);
- mpz_add_ui(prev->key->left->value, prev->key->left->value, 1);
+ mpz_set(prev->key->range.low, i->key->range.high);
+ mpz_add_ui(prev->key->range.low, prev->key->range.low, 1);
list_move(&prev->list, &set->existing_set->init->expressions);
}
static void adjust_elem_left(struct set *set, struct expr *prev, struct expr *i,
struct expr *purge)
{
+ prev->location = i->location;
remove_elem(prev, set, purge);
__adjust_elem_left(set, prev, i);
@@ -307,15 +327,15 @@ static void adjust_elem_left(struct set *set, struct expr *prev, struct expr *i,
static void __adjust_elem_right(struct set *set, struct expr *prev, struct expr *i)
{
prev->flags &= ~EXPR_F_KERNEL;
- expr_free(prev->key->right);
- prev->key->right = expr_get(i->key->left);
- mpz_sub_ui(prev->key->right->value, prev->key->right->value, 1);
+ mpz_set(prev->key->range.high, i->key->range.low);
+ mpz_sub_ui(prev->key->range.high, prev->key->range.high, 1);
list_move(&prev->list, &set->existing_set->init->expressions);
}
static void adjust_elem_right(struct set *set, struct expr *prev, struct expr *i,
struct expr *purge)
{
+ prev->location = i->location;
remove_elem(prev, set, purge);
__adjust_elem_right(set, prev, i);
@@ -328,6 +348,8 @@ static void split_range(struct set *set, struct expr *prev, struct expr *i,
{
struct expr *clone;
+ prev->location = i->location;
+
if (prev->flags & EXPR_F_KERNEL) {
clone = expr_clone(prev);
list_move_tail(&clone->list, &purge->expressions);
@@ -335,14 +357,12 @@ static void split_range(struct set *set, struct expr *prev, struct expr *i,
prev->flags &= ~EXPR_F_KERNEL;
clone = expr_clone(prev);
- expr_free(clone->key->left);
- clone->key->left = expr_get(i->key->right);
- mpz_add_ui(clone->key->left->value, i->key->right->value, 1);
+ mpz_set(clone->key->range.low, i->key->range.high);
+ mpz_add_ui(clone->key->range.low, i->key->range.high, 1);
list_add_tail(&clone->list, &set->existing_set->init->expressions);
- expr_free(prev->key->right);
- prev->key->right = expr_get(i->key->left);
- mpz_sub_ui(prev->key->right->value, i->key->left->value, 1);
+ mpz_set(prev->key->range.high, i->key->range.low);
+ mpz_sub_ui(prev->key->range.high, i->key->range.low, 1);
list_move(&prev->list, &set->existing_set->init->expressions);
list_del(&i->list);
@@ -376,7 +396,7 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
struct expr *purge, struct expr *elems,
unsigned int debug_mask)
{
- struct expr *i, *next, *prev = NULL;
+ struct expr *i, *next, *elem, *prev = NULL;
struct range range, prev_range;
int err = 0;
mpz_t rop;
@@ -387,21 +407,26 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
mpz_init(range.high);
mpz_init(rop);
- list_for_each_entry_safe(i, next, &elems->expressions, list) {
- if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
- continue;
+ list_for_each_entry_safe(elem, next, &elems->expressions, list) {
+ i = interval_expr_key(elem);
- range_expr_value_low(range.low, i);
- range_expr_value_high(range.high, i);
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL) {
+ /* Assume max value to simplify handling. */
+ mpz_bitmask(range.low, i->len);
+ mpz_bitmask(range.high, i->len);
+ } else {
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+ }
- if (!prev && i->flags & EXPR_F_REMOVE) {
+ if (!prev && elem->flags & EXPR_F_REMOVE) {
expr_error(msgs, i, "element does not exist");
err = -1;
goto err;
}
- if (!(i->flags & EXPR_F_REMOVE)) {
- prev = i;
+ if (!(elem->flags & EXPR_F_REMOVE)) {
+ prev = elem;
mpz_set(prev_range.low, range.low);
mpz_set(prev_range.high, range.high);
continue;
@@ -409,19 +434,22 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
if (mpz_cmp(prev_range.low, range.low) == 0 &&
mpz_cmp(prev_range.high, range.high) == 0) {
- if (i->flags & EXPR_F_REMOVE) {
- if (prev->flags & EXPR_F_KERNEL)
+ if (elem->flags & EXPR_F_REMOVE) {
+ if (prev->flags & EXPR_F_KERNEL) {
+ prev->location = elem->location;
list_move_tail(&prev->list, &purge->expressions);
+ }
- list_del(&i->list);
- expr_free(i);
+ list_del(&elem->list);
+ expr_free(elem);
}
- } else if (set->automerge &&
- setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) {
- expr_error(msgs, i, "element does not exist");
- err = -1;
- goto err;
- } else if (i->flags & EXPR_F_REMOVE) {
+ } else if (set->automerge) {
+ if (setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+ } else if (elem->flags & EXPR_F_REMOVE) {
expr_error(msgs, i, "element does not exist");
err = -1;
goto err;
@@ -458,7 +486,7 @@ static int __set_delete(struct list_head *msgs, struct expr *i, struct set *set,
unsigned int debug_mask)
{
i->flags |= EXPR_F_REMOVE;
- list_move(&i->list, &existing_set->init->expressions);
+ list_move_tail(&i->list, &existing_set->init->expressions);
list_expr_sort(&existing_set->init->expressions);
return setelem_delete(msgs, set, init, existing_set->init, debug_mask);
@@ -507,13 +535,13 @@ int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
if (debug_mask & NFT_DEBUG_SEGTREE) {
list_for_each_entry(i, &init->expressions, list)
pr_gmp_debug("remove: [%Zx-%Zx]\n",
- i->key->left->value, i->key->right->value);
+ i->key->range.low, i->key->range.high);
list_for_each_entry(i, &add->expressions, list)
pr_gmp_debug("add: [%Zx-%Zx]\n",
- i->key->left->value, i->key->right->value);
+ i->key->range.low, i->key->range.high);
list_for_each_entry(i, &existing_set->init->expressions, list)
pr_gmp_debug("existing: [%Zx-%Zx]\n",
- i->key->left->value, i->key->right->value);
+ i->key->range.low, i->key->range.high);
}
if (list_empty(&add->expressions)) {
@@ -651,30 +679,29 @@ static bool segtree_needs_first_segment(const struct set *set,
int set_to_intervals(const struct set *set, struct expr *init, bool add)
{
- struct expr *i, *n, *prev = NULL, *elem, *newelem = NULL, *root, *expr;
+ struct expr *i, *n, *prev = NULL, *elem, *root, *expr;
LIST_HEAD(intervals);
- uint32_t flags;
- mpz_t p, q;
-
- mpz_init2(p, set->key->len);
- mpz_init2(q, set->key->len);
+ mpz_t p;
list_for_each_entry_safe(i, n, &init->expressions, list) {
- flags = 0;
-
elem = interval_expr_key(i);
if (elem->key->etype == EXPR_SET_ELEM_CATCHALL)
continue;
- if (!prev && segtree_needs_first_segment(set, init, add) &&
- mpz_cmp_ui(elem->key->left->value, 0)) {
+ if (prev)
+ break;
+
+ if (segtree_needs_first_segment(set, init, add) &&
+ mpz_cmp_ui(elem->key->range.low, 0)) {
+ mpz_init2(p, set->key->len);
mpz_set_ui(p, 0);
- expr = constant_expr_alloc(&internal_location,
- set->key->dtype,
- set->key->byteorder,
- set->key->len, NULL);
- mpz_set(expr->value, p);
+ expr = constant_range_expr_alloc(&internal_location,
+ set->key->dtype,
+ set->key->byteorder,
+ set->key->len, p, p);
+ mpz_clear(p);
+
root = set_elem_expr_alloc(&internal_location, expr);
if (i->etype == EXPR_MAPPING) {
root = mapping_expr_alloc(&internal_location,
@@ -683,65 +710,131 @@ int set_to_intervals(const struct set *set, struct expr *init, bool add)
}
root->flags |= EXPR_F_INTERVAL_END;
list_add(&root->list, &intervals);
- init->size++;
+ break;
}
+ prev = i;
+ }
- if (newelem) {
- mpz_set(p, interval_expr_key(newelem)->key->value);
- if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(p, set->key->len / BITS_PER_BYTE);
+ list_splice_init(&intervals, &init->expressions);
- if (!(set->flags & NFT_SET_ANONYMOUS) ||
- mpz_cmp(p, elem->key->left->value) != 0)
- list_add_tail(&newelem->list, &intervals);
- else
- expr_free(newelem);
- }
- newelem = NULL;
-
- if (mpz_scan0(elem->key->right->value, 0) != set->key->len) {
- mpz_add_ui(p, elem->key->right->value, 1);
- expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
- set->key->byteorder, set->key->len,
- NULL);
- mpz_set(expr->value, p);
- if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
-
- newelem = set_elem_expr_alloc(&internal_location, expr);
- if (i->etype == EXPR_MAPPING) {
- newelem = mapping_expr_alloc(&internal_location,
- newelem,
- expr_get(i->right));
- }
- newelem->flags |= EXPR_F_INTERVAL_END;
- } else {
- flags = NFTNL_SET_ELEM_F_INTERVAL_OPEN;
- }
+ return 0;
+}
- expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
- set->key->byteorder, set->key->len, NULL);
+/* This only works for the supported stateful statements. */
+static void set_elem_stmt_clone(struct expr *dst, const struct expr *src)
+{
+ struct stmt *stmt, *nstmt;
- mpz_set(expr->value, elem->key->left->value);
- if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
+ list_for_each_entry(stmt, &src->stmt_list, list) {
+ nstmt = xzalloc(sizeof(*stmt));
+ *nstmt = *stmt;
+ list_add_tail(&nstmt->list, &dst->stmt_list);
+ }
+}
- expr_free(elem->key);
- elem->key = expr;
- i->elem_flags |= flags;
- init->size++;
- list_move_tail(&i->list, &intervals);
+static void set_elem_expr_copy(struct expr *dst, const struct expr *src)
+{
+ if (src->comment)
+ dst->comment = xstrdup(src->comment);
+ if (src->timeout)
+ dst->timeout = src->timeout;
+ if (src->expiration)
+ dst->expiration = src->expiration;
+
+ set_elem_stmt_clone(dst, src);
+}
- prev = i;
+static struct expr *setelem_key(struct expr *expr)
+{
+ struct expr *key;
+
+ switch (expr->etype) {
+ case EXPR_MAPPING:
+ key = expr->left->key;
+ break;
+ case EXPR_SET_ELEM:
+ key = expr->key;
+ break;
+ default:
+ BUG("unhandled expression type %d\n", expr->etype);
+ return NULL;
}
- if (newelem)
- list_add_tail(&newelem->list, &intervals);
+ return key;
+}
- list_splice_init(&intervals, &init->expressions);
+int setelem_to_interval(const struct set *set, struct expr *elem,
+ struct expr *next_elem, struct list_head *intervals)
+{
+ struct expr *key, *next_key = NULL, *low, *high;
+ bool adjacent = false;
+
+ key = setelem_key(elem);
+ if (key->etype == EXPR_SET_ELEM_CATCHALL)
+ return 0;
+
+ if (next_elem) {
+ next_key = setelem_key(next_elem);
+ if (next_key->etype == EXPR_SET_ELEM_CATCHALL)
+ next_key = NULL;
+ }
+
+ if (key->etype != EXPR_RANGE_VALUE)
+ BUG("key must be RANGE_VALUE, not %s\n", expr_name(key));
+
+ assert(!next_key || next_key->etype == EXPR_RANGE_VALUE);
+
+ /* skip end element for adjacents intervals in anonymous sets. */
+ if (!(elem->flags & EXPR_F_INTERVAL_END) && next_key) {
+ mpz_t p;
+
+ mpz_init2(p, set->key->len);
+ mpz_add_ui(p, key->range.high, 1);
+
+ if (!mpz_cmp(p, next_key->range.low))
+ adjacent = true;
+
+ mpz_clear(p);
+ }
+
+ low = constant_expr_alloc(&key->location, set->key->dtype,
+ set->key->byteorder, set->key->len, NULL);
+
+ mpz_set(low->value, key->range.low);
+ if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(low->value, set->key->len / BITS_PER_BYTE);
+
+ low = set_elem_expr_alloc(&key->location, low);
+ set_elem_expr_copy(low, interval_expr_key(elem));
+
+ if (elem->etype == EXPR_MAPPING)
+ low = mapping_expr_alloc(&elem->location,
+ low, expr_get(elem->right));
+
+ list_add_tail(&low->list, intervals);
+
+ if (adjacent)
+ return 0;
+ else if (!mpz_cmp_ui(key->value, 0) && elem->flags & EXPR_F_INTERVAL_END) {
+ low->flags |= EXPR_F_INTERVAL_END;
+ return 0;
+ } else if (mpz_scan0(key->range.high, 0) == set->key->len) {
+ low->flags |= EXPR_F_INTERVAL_OPEN;
+ return 0;
+ }
+
+ high = constant_expr_alloc(&key->location, set->key->dtype,
+ set->key->byteorder, set->key->len,
+ NULL);
+ mpz_set(high->value, key->range.high);
+ mpz_add_ui(high->value, high->value, 1);
+ if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(high->value, set->key->len / BITS_PER_BYTE);
+
+ high = set_elem_expr_alloc(&key->location, high);
- mpz_clear(p);
- mpz_clear(q);
+ high->flags |= EXPR_F_INTERVAL_END;
+ list_add_tail(&high->list, intervals);
return 0;
}
diff --git a/src/ipopt.c b/src/ipopt.c
index 67e904ff..c03a8041 100644
--- a/src/ipopt.c
+++ b/src/ipopt.c
@@ -1,4 +1,5 @@
-#include <stdint.h>
+#include <nft.h>
+
#include <netinet/in.h>
#include <netinet/ip.h>
@@ -23,7 +24,7 @@ static const struct exthdr_desc ipopt_lsrr = {
[IPOPT_FIELD_TYPE] = PHT("type", 0, 8),
[IPOPT_FIELD_LENGTH] = PHT("length", 8, 8),
[IPOPT_FIELD_PTR] = PHT("ptr", 16, 8),
- [IPOPT_FIELD_ADDR_0] = PHT("addr", 24, 32),
+ [IPOPT_FIELD_ADDR_0] = PROTO_HDR_TEMPLATE("addr", &ipaddr_type, BYTEORDER_BIG_ENDIAN, 24, 32),
},
};
@@ -34,7 +35,7 @@ static const struct exthdr_desc ipopt_rr = {
[IPOPT_FIELD_TYPE] = PHT("type", 0, 8),
[IPOPT_FIELD_LENGTH] = PHT("length", 8, 8),
[IPOPT_FIELD_PTR] = PHT("ptr", 16, 8),
- [IPOPT_FIELD_ADDR_0] = PHT("addr", 24, 32),
+ [IPOPT_FIELD_ADDR_0] = PROTO_HDR_TEMPLATE("addr", &ipaddr_type, BYTEORDER_BIG_ENDIAN, 24, 32),
},
};
@@ -45,7 +46,7 @@ static const struct exthdr_desc ipopt_ssrr = {
[IPOPT_FIELD_TYPE] = PHT("type", 0, 8),
[IPOPT_FIELD_LENGTH] = PHT("length", 8, 8),
[IPOPT_FIELD_PTR] = PHT("ptr", 16, 8),
- [IPOPT_FIELD_ADDR_0] = PHT("addr", 24, 32),
+ [IPOPT_FIELD_ADDR_0] = PROTO_HDR_TEMPLATE("addr", &ipaddr_type, BYTEORDER_BIG_ENDIAN, 24, 32),
},
};
diff --git a/src/json.c b/src/json.c
index a525fd1b..f0430776 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1,12 +1,21 @@
-#define _GNU_SOURCE
+/*
+ * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <stdio.h>
-#include <string.h>
#include <expression.h>
#include <list.h>
#include <netlink.h>
#include <rule.h>
#include <rt.h>
+#include "nftutils.h"
#include <netdb.h>
#include <netinet/icmp6.h>
@@ -24,14 +33,44 @@
#include <jansson.h>
#include <syslog.h>
-#ifdef DEBUG
-#define __json_pack json_pack
-#define json_pack(...) ({ \
- json_t *__out = __json_pack(__VA_ARGS__); \
- assert(__out); \
- __out; \
-})
-#endif
+static json_t *__nft_json_pack(unsigned int line, const char *fmt, ...)
+{
+ json_error_t error;
+ json_t *value;
+ va_list ap;
+
+ va_start(ap, fmt);
+ value = json_vpack_ex(&error, 0, fmt, ap);
+ va_end(ap);
+
+ if (value)
+ return value;
+
+ fprintf(stderr, "%s:%d: json_pack failure (%s)\n", __FILE__, line, error.text);
+ return NULL;
+}
+#define nft_json_pack(...) __nft_json_pack(__LINE__, __VA_ARGS__)
+
+static int json_array_extend_new(json_t *array, json_t *other_array)
+{
+ int ret;
+
+ ret = json_array_extend(array, other_array);
+ json_decref(other_array);
+ return ret;
+}
+
+static void json_add_array_new(json_t *obj, const char *name, json_t *array)
+{
+ if (json_array_size(array) > 1) {
+ json_object_set_new(obj, name, array);
+ } else {
+ if (json_array_size(array))
+ json_object_set(obj, name,
+ json_array_get(array, 0));
+ json_decref(array);
+ }
+}
static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx)
{
@@ -54,93 +93,144 @@ static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx)
fclose(octx->output_fp);
octx->output_fp = fp;
- return json_pack("s", buf);
+ return nft_json_pack("s", buf);
}
static json_t *set_dtype_json(const struct expr *key)
{
char *namedup = xstrdup(key->dtype->name), *tok;
json_t *root = NULL;
+ char *tok_safe;
- tok = strtok(namedup, " .");
+ tok = strtok_r(namedup, " .", &tok_safe);
while (tok) {
json_t *jtok = json_string(tok);
if (!root)
root = jtok;
else if (json_is_string(root))
- root = json_pack("[o, o]", root, jtok);
+ root = nft_json_pack("[o, o]", root, jtok);
else
json_array_append_new(root, jtok);
- tok = strtok(NULL, " .");
+ tok = strtok_r(NULL, " .", &tok_safe);
}
- xfree(namedup);
+ free(namedup);
return root;
}
-static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
+static json_t *set_key_dtype_json(const struct set *set,
+ struct output_ctx *octx)
+{
+ bool use_typeof = set->key_typeof_valid;
+
+ if (!use_typeof)
+ return set_dtype_json(set->key);
+
+ return nft_json_pack("{s:o}", "typeof", expr_print_json(set->key, octx));
+}
+
+static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ const struct stmt_ops *ops = stmt_ops(stmt);
+ char buf[1024];
+ FILE *fp;
+
+ if (ops->json)
+ return ops->json(stmt, octx);
+
+ fprintf(stderr, "warning: stmt ops %s have no json callback\n",
+ ops->name);
+
+ fp = octx->output_fp;
+ octx->output_fp = fmemopen(buf, 1024, "w");
+
+ ops->print(stmt, octx);
+
+ fclose(octx->output_fp);
+ octx->output_fp = fp;
+
+ return nft_json_pack("s", buf);
+}
+
+static json_t *set_stmt_list_json(const struct list_head *stmt_list,
+ struct output_ctx *octx)
{
+ unsigned int flags = octx->flags;
json_t *root, *tmp;
- const char *type, *datatype_ext = NULL;
+ struct stmt *i;
+
+ root = json_array();
+ octx->flags |= NFT_CTX_OUTPUT_STATELESS;
+
+ list_for_each_entry(i, stmt_list, list) {
+ tmp = stmt_print_json(i, octx);
+ json_array_append_new(root, tmp);
+ }
+ octx->flags = flags;
+
+ return root;
+}
+
+static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
+{
+ json_t *root, *tmp, *datatype_ext = NULL;
+ const char *type;
if (set_is_datamap(set->flags)) {
type = "map";
- datatype_ext = set->data->dtype->name;
+ datatype_ext = set_dtype_json(set->data);
} else if (set_is_objmap(set->flags)) {
type = "map";
- datatype_ext = obj_type_name(set->objtype);
+ datatype_ext = json_string(obj_type_name(set->objtype));
} else if (set_is_meter(set->flags)) {
type = "meter";
} else {
type = "set";
}
- root = json_pack("{s:s, s:s, s:s, s:o, s:I}",
+ root = nft_json_pack("{s:s, s:s, s:s, s:o, s:I}",
"family", family2str(set->handle.family),
"name", set->handle.set.name,
"table", set->handle.table.name,
- "type", set_dtype_json(set->key),
+ "type", set_key_dtype_json(set, octx),
"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));
+ json_object_set_new(root, "map", datatype_ext);
if (!(set->flags & (NFT_SET_CONSTANT))) {
if (set->policy != NFT_SET_POL_PERFORMANCE) {
- tmp = json_pack("s", set_policy2str(set->policy));
+ tmp = nft_json_pack("s", set_policy2str(set->policy));
json_object_set_new(root, "policy", tmp);
}
if (set->desc.size) {
- tmp = json_pack("i", set->desc.size);
+ tmp = nft_json_pack("i", set->desc.size);
json_object_set_new(root, "size", tmp);
}
}
tmp = json_array();
if (set->flags & NFT_SET_CONSTANT)
- json_array_append_new(tmp, json_pack("s", "constant"));
+ json_array_append_new(tmp, nft_json_pack("s", "constant"));
if (set->flags & NFT_SET_INTERVAL)
- json_array_append_new(tmp, json_pack("s", "interval"));
+ json_array_append_new(tmp, nft_json_pack("s", "interval"));
if (set->flags & NFT_SET_TIMEOUT)
- json_array_append_new(tmp, json_pack("s", "timeout"));
-
- if (json_array_size(tmp) > 0) {
- json_object_set_new(root, "flags", tmp);
- } else {
- if (json_array_size(tmp))
- json_object_set(root, "flags", json_array_get(tmp, 0));
- json_decref(tmp);
- }
+ json_array_append_new(tmp, nft_json_pack("s", "timeout"));
+ if (set->flags & NFT_SET_EVAL)
+ json_array_append_new(tmp, nft_json_pack("s", "dynamic"));
+ json_add_array_new(root, "flags", tmp);
if (set->timeout) {
tmp = json_integer(set->timeout / 1000);
json_object_set_new(root, "timeout", tmp);
}
if (set->gc_int) {
- tmp = json_pack("i", set->gc_int / 1000);
+ tmp = nft_json_pack("i", set->gc_int / 1000);
json_object_set_new(root, "gc-interval", tmp);
}
+ if (set->automerge)
+ json_object_set_new(root, "auto-merge", json_true());
if (!nft_output_terse(octx) && set->init && set->init->size > 0) {
json_t *array = json_array();
@@ -152,7 +242,12 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_object_set_new(root, "elem", array);
}
- return json_pack("{s:o}", type, root);
+ if (!list_empty(&set->stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&set->stmt_list, octx));
+ }
+
+ return nft_json_pack("{s:o}", type, root);
}
/* XXX: Merge with set_print_json()? */
@@ -161,48 +256,20 @@ static json_t *element_print_json(struct output_ctx *octx,
{
json_t *root = expr_print_json(set->init, octx);
- return json_pack("{s: {s:s, s:s, s:s, s:o}}", "element",
+ return nft_json_pack("{s: {s:s, s:s, s:s, s:o}}", "element",
"family", family2str(set->handle.family),
"table", set->handle.table.name,
"name", set->handle.set.name,
"elem", root);
}
-static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx)
-{
- char buf[1024];
- FILE *fp;
-
- /* XXX: Can't be supported at this point:
- * xt_stmt_xlate() ignores output_fp.
- */
- if (stmt->ops->type == STMT_XT)
- return json_pack("{s:n}", "xt");
-
- if (stmt->ops->json)
- return stmt->ops->json(stmt, octx);
-
- fprintf(stderr, "warning: stmt ops %s have no json callback\n",
- stmt->ops->name);
-
- fp = octx->output_fp;
- octx->output_fp = fmemopen(buf, 1024, "w");
-
- stmt->ops->print(stmt, octx);
-
- fclose(octx->output_fp);
- octx->output_fp = fp;
-
- return json_pack("s", buf);
-}
-
static json_t *rule_print_json(struct output_ctx *octx,
const struct rule *rule)
{
const struct stmt *stmt;
json_t *root, *tmp;
- root = json_pack("{s:s, s:s, s:s, s:I}",
+ root = nft_json_pack("{s:s, s:s, s:s, s:I}",
"family", family2str(rule->handle.family),
"table", rule->handle.table.name,
"chain", rule->handle.chain.name,
@@ -222,56 +289,66 @@ static json_t *rule_print_json(struct output_ctx *octx,
json_decref(tmp);
}
- return json_pack("{s:o}", "rule", root);
+ return nft_json_pack("{s:o}", "rule", root);
}
static json_t *chain_print_json(const struct chain *chain)
{
- int priority, policy, n = 0;
- struct expr *dev, *expr;
- json_t *root, *tmp;
+ json_t *root, *tmp, *devs = NULL;
+ int priority, policy, i;
- root = json_pack("{s:s, s:s, s:s, s:I}",
+ root = nft_json_pack("{s:s, s:s, s:s, s:I}",
"family", family2str(chain->handle.family),
"table", chain->handle.table.name,
"name", chain->handle.chain.name,
"handle", chain->handle.handle.id);
+ if (chain->comment)
+ json_object_set_new(root, "comment", json_string(chain->comment));
+
if (chain->flags & CHAIN_F_BASECHAIN) {
mpz_export_data(&priority, chain->priority.expr->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
- mpz_export_data(&policy, chain->policy->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- tmp = json_pack("{s:s, s:s, s:i, s:s}",
+
+ if (chain->policy) {
+ mpz_export_data(&policy, chain->policy->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ } else {
+ policy = NF_ACCEPT;
+ }
+
+ tmp = nft_json_pack("{s:s, s:s, s:i, s:s}",
"type", chain->type.str,
"hook", hooknum2str(chain->handle.family,
chain->hook.num),
"prio", priority,
"policy", chain_policy2str(policy));
- if (chain->dev_expr) {
- list_for_each_entry(expr, &chain->dev_expr->expressions, list) {
- dev = expr;
- n++;
- }
- }
- if (n == 1) {
- json_object_set_new(tmp, "dev",
- json_string(dev->identifier));
+ for (i = 0; i < chain->dev_array_len; i++) {
+ const char *dev = chain->dev_array[i];
+ if (!devs)
+ devs = json_string(dev);
+ else if (json_is_string(devs))
+ devs = nft_json_pack("[o, s]", devs, dev);
+ else
+ json_array_append_new(devs, json_string(dev));
}
+ if (devs)
+ json_object_set_new(root, "dev", devs);
+
json_object_update(root, tmp);
json_decref(tmp);
}
- return json_pack("{s:o}", "chain", root);
+ return nft_json_pack("{s:o}", "chain", root);
}
static json_t *proto_name_json(uint8_t proto)
{
- const struct protoent *p = getprotobynumber(proto);
+ char name[NFT_PROTONAME_MAXSIZE];
- if (p)
- return json_string(p->p_name);
+ if (nft_getprotobynumber(proto, name, sizeof(name)))
+ return json_string(name);
return json_integer(proto);
}
@@ -299,22 +376,28 @@ static json_t *obj_print_json(const struct obj *obj)
json_t *root, *tmp, *flags;
uint64_t rate, burst;
- root = json_pack("{s:s, s:s, s:s, s:I}",
+ root = nft_json_pack("{s:s, s:s, s:s, s:I}",
"family", family2str(obj->handle.family),
"name", obj->handle.obj.name,
"table", obj->handle.table.name,
"handle", obj->handle.handle.id);
+ if (obj->comment) {
+ tmp = nft_json_pack("{s:s}", "comment", obj->comment);
+ json_object_update(root, tmp);
+ json_decref(tmp);
+ }
+
switch (obj->type) {
case NFT_OBJECT_COUNTER:
- tmp = json_pack("{s:I, s:I}",
+ tmp = nft_json_pack("{s:I, s:I}",
"packets", obj->counter.packets,
"bytes", obj->counter.bytes);
json_object_update(root, tmp);
json_decref(tmp);
break;
case NFT_OBJECT_QUOTA:
- tmp = json_pack("{s:I, s:I, s:b}",
+ tmp = nft_json_pack("{s:I, s:I, s:b}",
"bytes", obj->quota.bytes,
"used", obj->quota.used,
"inv", obj->quota.flags & NFT_QUOTA_F_INV);
@@ -322,13 +405,13 @@ static json_t *obj_print_json(const struct obj *obj)
json_decref(tmp);
break;
case NFT_OBJECT_SECMARK:
- tmp = json_pack("{s:s}",
+ tmp = nft_json_pack("{s:s}",
"context", obj->secmark.ctx);
json_object_update(root, tmp);
json_decref(tmp);
break;
case NFT_OBJECT_CT_HELPER:
- tmp = json_pack("{s:s, s:o, s:s}",
+ tmp = nft_json_pack("{s:s, s:o, s:s}",
"type", obj->ct_helper.name, "protocol",
proto_name_json(obj->ct_helper.l4proto),
"l3proto", family2str(obj->ct_helper.l3proto));
@@ -338,7 +421,7 @@ static json_t *obj_print_json(const struct obj *obj)
case NFT_OBJECT_CT_TIMEOUT:
tmp = timeout_policy_json(obj->ct_timeout.l4proto,
obj->ct_timeout.timeout);
- tmp = json_pack("{s:o, s:s, s:o}",
+ tmp = nft_json_pack("{s:o, s:s, s:o}",
"protocol",
proto_name_json(obj->ct_timeout.l4proto),
"l3proto", family2str(obj->ct_timeout.l3proto),
@@ -347,7 +430,7 @@ static json_t *obj_print_json(const struct obj *obj)
json_decref(tmp);
break;
case NFT_OBJECT_CT_EXPECT:
- tmp = json_pack("{s:o, s:I, s:I, s:I, s:s}",
+ tmp = nft_json_pack("{s:o, s:I, s:I, s:I, s:s}",
"protocol",
proto_name_json(obj->ct_expect.l4proto),
"dport", obj->ct_expect.dport,
@@ -366,7 +449,7 @@ static json_t *obj_print_json(const struct obj *obj)
burst_unit = get_rate(obj->limit.burst, &burst);
}
- tmp = json_pack("{s:I, s:s}",
+ tmp = nft_json_pack("{s:I, s:s}",
"rate", rate,
"per", get_unit(obj->limit.unit));
@@ -386,56 +469,62 @@ static json_t *obj_print_json(const struct obj *obj)
json_decref(tmp);
break;
case NFT_OBJECT_SYNPROXY:
+ tmp = nft_json_pack("{s:i, s:i}",
+ "mss", obj->synproxy.mss,
+ "wscale", obj->synproxy.wscale);
+
flags = json_array();
- tmp = json_pack("{s:i, s:i}",
- "mss", obj->synproxy.mss,
- "wscale", obj->synproxy.wscale);
if (obj->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP)
json_array_append_new(flags, json_string("timestamp"));
if (obj->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM)
json_array_append_new(flags, json_string("sack-perm"));
-
- if (json_array_size(flags) > 0)
- json_object_set_new(tmp, "flags", flags);
- else
- json_decref(flags);
+ json_add_array_new(tmp, "flags", flags);
json_object_update(root, tmp);
json_decref(tmp);
break;
}
- return json_pack("{s:o}", type, root);
+ return nft_json_pack("{s:o}", type, root);
}
static json_t *flowtable_print_json(const struct flowtable *ftable)
{
json_t *root, *devs = NULL;
- int i, priority;
+ int i, priority = 0;
- mpz_export_data(&priority, ftable->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- root = json_pack("{s:s, s:s, s:s, s:I, s:s, s:i}",
+ root = nft_json_pack("{s:s, s:s, s:s, s:I}",
"family", family2str(ftable->handle.family),
"name", ftable->handle.flowtable.name,
"table", ftable->handle.table.name,
- "handle", ftable->handle.handle.id,
- "hook", hooknum2str(NFPROTO_NETDEV, ftable->hook.num),
- "prio", priority);
+ "handle", ftable->handle.handle.id);
+
+ if (ftable->priority.expr) {
+ json_t *tmp;
+
+ mpz_export_data(&priority, ftable->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+
+ tmp = nft_json_pack("{s:s, s:i}",
+ "hook", hooknum2str(NFPROTO_NETDEV,
+ ftable->hook.num),
+ "prio", priority);
+ json_object_update_new(root, tmp);
+ }
for (i = 0; i < ftable->dev_array_len; i++) {
const char *dev = ftable->dev_array[i];
if (!devs)
devs = json_string(dev);
else if (json_is_string(devs))
- devs = json_pack("[o, s]", devs, dev);
+ devs = nft_json_pack("[o, s]", devs, dev);
else
json_array_append_new(devs, json_string(dev));
}
if (devs)
json_object_set_new(root, "dev", devs);
- return json_pack("{s:o}", "flowtable", root);
+ return nft_json_pack("{s:o}", "flowtable", root);
}
static json_t *table_flags_json(const struct table *table)
@@ -452,59 +541,50 @@ static json_t *table_flags_json(const struct table *table)
flags >>= 1;
i++;
}
- switch (json_array_size(root)) {
- case 0:
- json_decref(root);
- return NULL;
- case 1:
- json_unpack(root, "[o]", &tmp);
- json_decref(root);
- root = tmp;
- break;
- }
return root;
}
static json_t *table_print_json(const struct table *table)
{
- json_t *root, *tmp;
+ json_t *root;
- root = json_pack("{s:s, s:s, s:I}",
+ root = nft_json_pack("{s:s, s:s, s:I}",
"family", family2str(table->handle.family),
"name", table->handle.table.name,
"handle", table->handle.handle.id);
+ json_add_array_new(root, "flags", table_flags_json(table));
- tmp = table_flags_json(table);
- if (tmp)
- json_object_set_new(root, "flags", tmp);
+ if (table->comment)
+ json_object_set_new(root, "comment", json_string(table->comment));
- return json_pack("{s:o}", "table", root);
+ return nft_json_pack("{s:o}", "table", root);
}
-json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx)
+static json_t *
+__binop_expr_json(int op, const struct expr *expr, struct output_ctx *octx)
{
- json_t *left;
+ json_t *a = json_array();
- left = json_pack("{s:[o, o]}", expr_op_symbols[OP_AND],
- expr_print_json(expr->flagcmp.expr, octx),
- expr_print_json(expr->flagcmp.mask, octx));
-
- return json_pack("{s:{s:s, s:o, s:o}}", "match",
- "op", expr_op_symbols[expr->op] ? : "in",
- "left", left,
- "right", expr_print_json(expr->flagcmp.value, octx));
+ if (expr->etype == EXPR_BINOP && expr->op == op) {
+ json_array_extend_new(a,
+ __binop_expr_json(op, expr->left, octx));
+ json_array_extend_new(a,
+ __binop_expr_json(op, expr->right, octx));
+ } else {
+ json_array_append_new(a, expr_print_json(expr, octx));
+ }
+ return a;
}
json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx)
{
- return json_pack("{s:[o, o]}", expr_op_symbols[expr->op],
- expr_print_json(expr->left, octx),
- expr_print_json(expr->right, octx));
+ return nft_json_pack("{s:o}", expr_op_symbols[expr->op],
+ __binop_expr_json(expr->op, expr, octx));
}
json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx)
{
- return json_pack("{s:{s:s, s:o, s:o}}", "match",
+ return nft_json_pack("{s:{s:s, s:o, s:o}}", "match",
"op", expr_op_symbols[expr->op] ? : "in",
"left", expr_print_json(expr->left, octx),
"right", expr_print_json(expr->right, octx));
@@ -517,7 +597,7 @@ json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx)
octx->flags &= ~NFT_CTX_OUTPUT_SERVICE;
octx->flags |= NFT_CTX_OUTPUT_NUMERIC_PROTO;
- root = json_pack("{s:[o, o]}", "range",
+ root = nft_json_pack("{s:[o, o]}", "range",
expr_print_json(expr->left, octx),
expr_print_json(expr->right, octx));
octx->flags = flags;
@@ -527,7 +607,7 @@ json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx)
json_t *meta_expr_json(const struct expr *expr, struct output_ctx *octx)
{
- return json_pack("{s:{s:s}}", "meta",
+ return nft_json_pack("{s:{s:s}}", "meta",
"key", meta_templates[expr->meta.key].token);
}
@@ -535,17 +615,25 @@ json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx)
{
json_t *root;
- if (payload_is_known(expr))
- root = json_pack("{s:s, s:s}",
- "protocol", expr->payload.desc->name,
- "field", expr->payload.tmpl->token);
- else
- root = json_pack("{s:s, s:i, s:i}",
+ if (payload_is_known(expr)) {
+ if (expr->payload.inner_desc) {
+ root = nft_json_pack("{s:s, s:s, s:s}",
+ "tunnel", expr->payload.inner_desc->name,
+ "protocol", expr->payload.desc->name,
+ "field", expr->payload.tmpl->token);
+ } else {
+ root = nft_json_pack("{s:s, s:s}",
+ "protocol", expr->payload.desc->name,
+ "field", expr->payload.tmpl->token);
+ }
+ } else {
+ root = nft_json_pack("{s:s, s:i, s:i}",
"base", proto_base_tokens[expr->payload.base],
"offset", expr->payload.offset,
"len", expr->len);
+ }
- return json_pack("{s:o}", "payload", root);
+ return nft_json_pack("{s:o}", "payload", root);
}
json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -554,7 +642,7 @@ json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx)
enum nft_ct_keys key = expr->ct.key;
json_t *root;
- root = json_pack("{s:s}", "key", ct_templates[key].token);
+ root = nft_json_pack("{s:s}", "key", ct_templates[key].token);
if (expr->ct.direction < 0)
goto out;
@@ -562,7 +650,7 @@ json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx)
if (dirstr)
json_object_set_new(root, "dir", json_string(dirstr));
out:
- return json_pack("{s:o}", "ct", root);
+ return nft_json_pack("{s:o}", "ct", root);
}
json_t *concat_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -573,7 +661,7 @@ json_t *concat_expr_json(const struct expr *expr, struct output_ctx *octx)
list_for_each_entry(i, &expr->expressions, list)
json_array_append_new(array, expr_print_json(i, octx));
- return json_pack("{s:o}", "concat", array);
+ return nft_json_pack("{s:o}", "concat", array);
}
json_t *set_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -584,7 +672,7 @@ json_t *set_expr_json(const struct expr *expr, struct output_ctx *octx)
list_for_each_entry(i, &expr->expressions, list)
json_array_append_new(array, expr_print_json(i, octx));
- return json_pack("{s:o}", "set", array);
+ return nft_json_pack("{s:o}", "set", array);
}
json_t *set_ref_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -592,7 +680,7 @@ json_t *set_ref_expr_json(const struct expr *expr, struct output_ctx *octx)
if (set_is_anonymous(expr->set->flags)) {
return expr_print_json(expr->set->init, octx);
} else {
- return json_pack("s+", "@", expr->set->handle.set.name);
+ return nft_json_pack("s+", "@", expr->set->handle.set.name);
}
}
@@ -608,7 +696,7 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
/* these element attributes require formal set elem syntax */
if (expr->timeout || expr->expiration || expr->comment ||
!list_empty(&expr->stmt_list)) {
- root = json_pack("{s:o}", "val", root);
+ root = nft_json_pack("{s:o}", "val", root);
if (expr->timeout) {
tmp = json_integer(expr->timeout / 1000);
@@ -630,7 +718,7 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
/* TODO: only one statement per element. */
break;
}
- return json_pack("{s:o}", "elem", root);
+ return nft_json_pack("{s:o}", "elem", root);
}
return root;
@@ -640,7 +728,7 @@ json_t *prefix_expr_json(const struct expr *expr, struct output_ctx *octx)
{
json_t *root = expr_print_json(expr->prefix, octx);
- return json_pack("{s:{s:o, s:i}}", "prefix",
+ return nft_json_pack("{s:{s:o, s:i}}", "prefix",
"addr", root,
"len", expr->prefix_len);
}
@@ -653,7 +741,7 @@ json_t *list_expr_json(const struct expr *expr, struct output_ctx *octx)
list_for_each_entry(i, &expr->expressions, list)
json_array_append_new(array, expr_print_json(i, octx));
- //return json_pack("{s:s, s:o}", "type", "list", "val", array);
+ //return nft_json_pack("{s:s, s:o}", "type", "list", "val", array);
return array;
}
@@ -664,7 +752,7 @@ json_t *unary_expr_json(const struct expr *expr, struct output_ctx *octx)
json_t *mapping_expr_json(const struct expr *expr, struct output_ctx *octx)
{
- return json_pack("[o, o]",
+ return nft_json_pack("[o, o]",
expr_print_json(expr->left, octx),
expr_print_json(expr->right, octx));
}
@@ -677,7 +765,7 @@ json_t *map_expr_json(const struct expr *expr, struct output_ctx *octx)
expr->mappings->set->data->dtype->type == TYPE_VERDICT)
type = "vmap";
- return json_pack("{s:{s:o, s:o}}", type,
+ return nft_json_pack("{s:{s:o, s:o}}", type,
"key", expr_print_json(expr->map, octx),
"data", expr_print_json(expr->mappings, octx));
}
@@ -699,31 +787,36 @@ json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx)
if (offset < 4)
offstr = offstrs[offset];
- root = json_pack("{s:s+}", "name", desc, offstr);
+ root = nft_json_pack("{s:s+}", "name", desc, offstr);
if (!is_exists)
json_object_set_new(root, "field", json_string(field));
} else {
- root = json_pack("{s:i, s:i, s:i}",
+ root = nft_json_pack("{s:i, s:i, s:i}",
"base", expr->exthdr.raw_type,
"offset", expr->exthdr.offset,
"len", expr->len);
}
- return json_pack("{s:o}", "tcp option", root);
+ return nft_json_pack("{s:o}", "tcp option", root);
+ }
+
+ if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) {
+ root = nft_json_pack("{s:i}", "type", expr->exthdr.raw_type);
+ return nft_json_pack("{s:o}", "dccp option", root);
}
- root = json_pack("{s:s}", "name", desc);
+ root = nft_json_pack("{s:s}", "name", desc);
if (!is_exists)
json_object_set_new(root, "field", json_string(field));
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_IPV4:
- return json_pack("{s:o}", "ip option", root);
+ return nft_json_pack("{s:o}", "ip option", root);
case NFT_EXTHDR_OP_SCTP:
- return json_pack("{s:o}", "sctp chunk", root);
+ return nft_json_pack("{s:o}", "sctp chunk", root);
default:
- return json_pack("{s:o}", "exthdr", root);
+ return nft_json_pack("{s:o}", "exthdr", root);
}
}
@@ -760,15 +853,15 @@ json_t *verdict_expr_json(const struct expr *expr, struct output_ctx *octx)
return NULL;
}
if (chain)
- return json_pack("{s:{s:o}}", name, "target", chain);
+ return nft_json_pack("{s:{s:o}}", name, "target", chain);
else
- return json_pack("{s:n}", name);
+ return nft_json_pack("{s:n}", name);
}
json_t *rt_expr_json(const struct expr *expr, struct output_ctx *octx)
{
const char *key = rt_templates[expr->rt.key].token;
- json_t *root = json_pack("{s:s}", "key", key);
+ json_t *root = nft_json_pack("{s:s}", "key", key);
const char *family = NULL;
switch (expr->rt.key) {
@@ -785,7 +878,7 @@ json_t *rt_expr_json(const struct expr *expr, struct output_ctx *octx)
if (family)
json_object_set_new(root, "family", json_string(family));
- return json_pack("{s:o}", "rt", root);
+ return nft_json_pack("{s:o}", "rt", root);
}
json_t *numgen_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -804,7 +897,7 @@ json_t *numgen_expr_json(const struct expr *expr, struct output_ctx *octx)
break;
}
- return json_pack("{s:{s:s, s:i, s:i}}", "numgen",
+ return nft_json_pack("{s:{s:s, s:i, s:i}}", "numgen",
"mode", mode,
"mod", expr->numgen.mod,
"offset", expr->numgen.offset);
@@ -826,7 +919,7 @@ json_t *hash_expr_json(const struct expr *expr, struct output_ctx *octx)
break;
}
- root = json_pack("{s:i}", "mod", expr->hash.mod);
+ root = nft_json_pack("{s:i}", "mod", expr->hash.mod);
if (expr->hash.seed_set)
json_object_set_new(root, "seed",
json_integer(expr->hash.seed));
@@ -836,7 +929,7 @@ json_t *hash_expr_json(const struct expr *expr, struct output_ctx *octx)
if (jexpr)
json_object_set_new(root, "expr", jexpr);
- return json_pack("{s:o}", type, root);
+ return nft_json_pack("{s:o}", type, root);
}
json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -845,7 +938,7 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx)
unsigned int flags = expr->fib.flags & ~NFTA_FIB_F_PRESENT;
json_t *root;
- root = json_pack("{s:s}", "result", fib_result_str(expr->fib.result));
+ root = nft_json_pack("{s:s}", "result", fib_result_str(expr));
if (flags) {
json_t *tmp = json_array();
@@ -859,9 +952,10 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx)
}
if (flags)
json_array_append_new(tmp, json_integer(flags));
- json_object_set_new(root, "flags", tmp);
+
+ json_add_array_new(root, "flags", tmp);
}
- return json_pack("{s:o}", "fib", root);
+ return nft_json_pack("{s:o}", "fib", root);
}
static json_t *symbolic_constant_json(const struct symbol_table *tbl,
@@ -934,7 +1028,7 @@ json_t *constant_expr_json(const struct expr *expr, struct output_ctx *octx)
json_t *socket_expr_json(const struct expr *expr, struct output_ctx *octx)
{
- return json_pack("{s:{s:s}}", "socket", "key",
+ return nft_json_pack("{s:{s:s}}", "socket", "key",
socket_templates[expr->socket.key].token);
}
@@ -943,9 +1037,9 @@ json_t *osf_expr_json(const struct expr *expr, struct output_ctx *octx)
json_t *root;
if (expr->osf.flags & NFT_OSF_F_VERSION)
- root = json_pack("{s:s}", "key", "version");
+ root = nft_json_pack("{s:s}", "key", "version");
else
- root = json_pack("{s:s}", "key", "name");
+ root = nft_json_pack("{s:s}", "key", "name");
switch (expr->osf.ttl) {
case 1:
@@ -956,7 +1050,7 @@ json_t *osf_expr_json(const struct expr *expr, struct output_ctx *octx)
break;
}
- return json_pack("{s:o}", "osf", root);
+ return nft_json_pack("{s:o}", "osf", root);
}
json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -993,7 +1087,7 @@ json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx)
break;
}
- root = json_pack("{s:s}", "key", name);
+ root = nft_json_pack("{s:s}", "key", name);
if (family)
json_object_set_new(root, "family", json_string(family));
@@ -1001,7 +1095,7 @@ json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx)
json_object_set_new(root, "dir", json_string(dirstr));
json_object_set_new(root, "spnum", json_integer(expr->xfrm.spnum));
- return json_pack("{s:o}", "ipsec", root);
+ return nft_json_pack("{s:o}", "ipsec", root);
}
json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx)
@@ -1042,12 +1136,11 @@ json_t *boolean_type_json(const struct expr *expr, struct output_ctx *octx)
json_t *inet_protocol_type_json(const struct expr *expr,
struct output_ctx *octx)
{
- struct protoent *p;
-
if (!nft_output_numeric_proto(octx)) {
- p = getprotobynumber(mpz_get_uint8(expr->value));
- if (p != NULL)
- return json_string(p->p_name);
+ char name[NFT_PROTONAME_MAXSIZE];
+
+ if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name)))
+ return json_string(name);
}
return integer_type_json(expr, octx);
}
@@ -1055,13 +1148,13 @@ json_t *inet_protocol_type_json(const struct expr *expr,
json_t *inet_service_type_json(const struct expr *expr, struct output_ctx *octx)
{
uint16_t port = mpz_get_be16(expr->value);
- const struct servent *s = NULL;
+ char name[NFT_SERVNAME_MAXSIZE];
if (!nft_output_service(octx) ||
- (s = getservbyport(port, NULL)) == NULL)
+ !nft_getservbyport(port, NULL, name, sizeof(name)))
return json_integer(ntohs(port));
- return json_string(s->s_name);
+ return json_string(name);
}
json_t *mark_type_json(const struct expr *expr, struct output_ctx *octx)
@@ -1124,21 +1217,21 @@ json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- return json_pack("{s:{s:s, s:s+}}", "flow",
+ return nft_json_pack("{s:{s:s, s:s+}}", "flow",
"op", "add", "flowtable",
"@", stmt->flow.table_name);
}
json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- return json_pack("{s: {s:o, s:o}}", "mangle",
+ return nft_json_pack("{s: {s:o, s:o}}", "mangle",
"key", expr_print_json(stmt->payload.expr, octx),
"value", expr_print_json(stmt->payload.val, octx));
}
json_t *exthdr_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- return json_pack("{s: {s:o, s:o}}", "mangle",
+ return nft_json_pack("{s: {s:o, s:o}}", "mangle",
"key", expr_print_json(stmt->exthdr.expr, octx),
"value", expr_print_json(stmt->exthdr.val, octx));
}
@@ -1150,7 +1243,7 @@ json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_t *root;
data_unit = get_rate(stmt->quota.bytes, &bytes);
- root = json_pack("{s:I, s:s}",
+ root = nft_json_pack("{s:I, s:s}",
"val", bytes,
"val_unit", data_unit);
@@ -1162,7 +1255,7 @@ json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_object_set_new(root, "used_unit", json_string(data_unit));
}
- return json_pack("{s:o}", "quota", root);
+ return nft_json_pack("{s:o}", "quota", root);
}
json_t *ct_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1175,7 +1268,7 @@ json_t *ct_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
},
};
- return json_pack("{s:{s:o, s:o}}", "mangle",
+ return nft_json_pack("{s:{s:o, s:o}}", "mangle",
"key", ct_expr_json(&expr, octx),
"value", expr_print_json(stmt->ct.expr, octx));
}
@@ -1193,7 +1286,7 @@ json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
burst_unit = get_rate(stmt->limit.burst, &burst);
}
- root = json_pack("{s:I, s:I, s:s}",
+ root = nft_json_pack("{s:I, s:I, s:s}",
"rate", rate,
"burst", burst,
"per", get_unit(stmt->limit.unit));
@@ -1205,14 +1298,14 @@ json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_object_set_new(root, "burst_unit",
json_string(burst_unit));
- return json_pack("{s:o}", "limit", root);
+ return nft_json_pack("{s:o}", "limit", root);
}
json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root, *tmp;
- root = json_pack("{s:o}", "dev", expr_print_json(stmt->fwd.dev, octx));
+ root = nft_json_pack("{s:o}", "dev", expr_print_json(stmt->fwd.dev, octx));
if (stmt->fwd.addr) {
tmp = json_string(family2str(stmt->fwd.family));
@@ -1222,12 +1315,12 @@ json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_object_set_new(root, "addr", tmp);
}
- return json_pack("{s:o}", "fwd", root);
+ return nft_json_pack("{s:o}", "fwd", root);
}
json_t *notrack_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- return json_pack("{s:n}", "notrack");
+ return nft_json_pack("{s:n}", "notrack");
}
json_t *dup_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1235,39 +1328,36 @@ json_t *dup_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_t *root;
if (stmt->dup.to) {
- root = json_pack("{s:o}", "addr", expr_print_json(stmt->dup.to, octx));
+ root = nft_json_pack("{s:o}", "addr", expr_print_json(stmt->dup.to, octx));
if (stmt->dup.dev)
json_object_set_new(root, "dev",
expr_print_json(stmt->dup.dev, octx));
} else {
root = json_null();
}
- return json_pack("{s:o}", "dup", root);
+ return nft_json_pack("{s:o}", "dup", root);
}
json_t *meta_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root;
- root = json_pack("{s:{s:s}}", "meta",
+ root = nft_json_pack("{s:{s:s}}", "meta",
"key", meta_templates[stmt->meta.key].token);
- root = json_pack("{s:o, s:o}",
+ root = nft_json_pack("{s:o, s:o}",
"key", root,
"value", expr_print_json(stmt->meta.expr, octx));
- return json_pack("{s:o}", "mangle", root);
+ return nft_json_pack("{s:o}", "mangle", root);
}
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) {
- char prefix[NF_LOG_PREFIXLEN] = {};
+ if (stmt->log.flags & STMT_LOG_PREFIX)
+ json_object_set_new(root, "prefix", json_string(stmt->log.prefix));
- 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));
@@ -1300,21 +1390,14 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
if (stmt->log.logflags & NF_LOG_MACDECODE)
json_array_append_new(flags, json_string("ether"));
}
- if (json_array_size(flags) > 1) {
- json_object_set_new(root, "flags", flags);
- } else {
- if (json_array_size(flags))
- json_object_set(root, "flags",
- json_array_get(flags, 0));
- json_decref(flags);
- }
+ json_add_array_new(root, "flags", flags);
if (!json_object_size(root)) {
json_decref(root);
root = json_null();
}
- return json_pack("{s:o}", "log", root);
+ return nft_json_pack("{s:o}", "log", root);
}
static json_t *nat_flags_json(uint32_t flags)
@@ -1342,18 +1425,6 @@ static json_t *nat_type_flags_json(uint32_t type_flags)
return array;
}
-static void nat_stmt_add_array(json_t *root, const char *name, json_t *array)
-{
- if (json_array_size(array) > 1) {
- json_object_set_new(root, name, array);
- } else {
- if (json_array_size(array))
- json_object_set(root, name,
- json_array_get(array, 0));
- json_decref(array);
- }
-}
-
json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root = json_object();
@@ -1375,12 +1446,12 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_object_set_new(root, "port",
expr_print_json(stmt->nat.proto, octx));
- nat_stmt_add_array(root, "flags", array);
+ json_add_array_new(root, "flags", array);
if (stmt->nat.type_flags) {
array = nat_type_flags_json(stmt->nat.type_flags);
- nat_stmt_add_array(root, "type_flags", array);
+ json_add_array_new(root, "type_flags", array);
}
if (!json_object_size(root)) {
@@ -1388,7 +1459,7 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
root = json_null();
}
- return json_pack("{s:o}", nat_etype2str(stmt->nat.type), root);
+ return nft_json_pack("{s:o}", nat_etype2str(stmt->nat.type), root);
}
json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1418,7 +1489,7 @@ json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
}
if (!type && !jexpr)
- return json_pack("{s:n}", "reject");
+ return nft_json_pack("{s:n}", "reject");
root = json_object();
if (type)
@@ -1426,25 +1497,62 @@ json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
if (jexpr)
json_object_set_new(root, "expr", jexpr);
- return json_pack("{s:o}", "reject", root);
+ return nft_json_pack("{s:o}", "reject", root);
}
json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
if (nft_output_stateless(octx))
- return json_pack("{s:n}", "counter");
+ return nft_json_pack("{s:n}", "counter");
- return json_pack("{s:{s:I, s:I}}", "counter",
+ return nft_json_pack("{s:{s:I, s:I}}", "counter",
"packets", stmt->counter.packets,
"bytes", stmt->counter.bytes);
}
+json_t *last_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ if (nft_output_stateless(octx) || stmt->last.set == 0)
+ return nft_json_pack("{s:n}", "last");
+
+ return nft_json_pack("{s:{s:I}}", "last", "used", stmt->last.used);
+}
+
json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- return json_pack("{s:{s:s, s:o, s:s+}}", "set",
+ json_t *root;
+
+ root = nft_json_pack("{s:s, s:o, s:s+}",
"op", set_stmt_op_names[stmt->set.op],
"elem", expr_print_json(stmt->set.key, octx),
"set", "@", stmt->set.set->set->handle.set.name);
+
+ if (!list_empty(&stmt->set.stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&stmt->set.stmt_list,
+ octx));
+ }
+
+ return nft_json_pack("{s:o}", "set", root);
+}
+
+json_t *map_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ json_t *root;
+
+ root = nft_json_pack("{s:s, s:o, s:o, s:s+}",
+ "op", set_stmt_op_names[stmt->map.op],
+ "elem", expr_print_json(stmt->map.key, octx),
+ "data", expr_print_json(stmt->map.data, octx),
+ "map", "@", stmt->map.set->set->handle.set.name);
+
+ if (!list_empty(&stmt->map.stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&stmt->map.stmt_list,
+ octx));
+ }
+
+ return nft_json_pack("{s:o}", "map", root);
}
json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1456,7 +1564,7 @@ json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
else
name = objref_type_name(stmt->objref.type);
- return json_pack("{s:o}", name, expr_print_json(stmt->objref.expr, octx));
+ return nft_json_pack("{s:o}", name, expr_print_json(stmt->objref.expr, octx));
}
json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1468,7 +1576,7 @@ json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
tmp = stmt_print_json(stmt->meter.stmt, octx);
octx->flags = flags;
- root = json_pack("{s:o, s:o, s:i}",
+ root = nft_json_pack("{s:o, s:o, s:i}",
"key", expr_print_json(stmt->meter.key, octx),
"stmt", tmp,
"size", stmt->meter.size);
@@ -1477,7 +1585,7 @@ json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_object_set_new(root, "name", tmp);
}
- return json_pack("{s:o}", "meter", root);
+ return nft_json_pack("{s:o}", "meter", root);
}
json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1495,21 +1603,14 @@ json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_array_append_new(flags, json_string("bypass"));
if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT)
json_array_append_new(flags, json_string("fanout"));
- if (json_array_size(flags) > 1) {
- json_object_set_new(root, "flags", flags);
- } else {
- if (json_array_size(flags))
- json_object_set(root, "flags",
- json_array_get(flags, 0));
- json_decref(flags);
- }
+ json_add_array_new(root, "flags", flags);
if (!json_object_size(root)) {
json_decref(root);
root = json_null();
}
- return json_pack("{s:o}", "queue", root);
+ return nft_json_pack("{s:o}", "queue", root);
}
json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1519,12 +1620,12 @@ json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_t *connlimit_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- json_t *root = json_pack("{s:i}", "val", stmt->connlimit.count);
+ json_t *root = nft_json_pack("{s:i}", "val", stmt->connlimit.count);
if (stmt->connlimit.flags & NFT_CONNLIMIT_F_INV)
json_object_set_new(root, "inv", json_true());
- return json_pack("{s:o}", "ct count", root);
+ return nft_json_pack("{s:o}", "ct count", root);
}
json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1547,7 +1648,7 @@ json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_object_set_new(root, "port", tmp);
}
- return json_pack("{s:o}", "tproxy", root);
+ return nft_json_pack("{s:o}", "tproxy", root);
}
json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1565,25 +1666,35 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
if (stmt->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM)
json_array_append_new(flags, json_string("sack-perm"));
- if (json_array_size(flags) > 0)
- json_object_set_new(root, "flags", flags);
- else
- json_decref(flags);
+ json_add_array_new(root, "flags", flags);
if (!json_object_size(root)) {
json_decref(root);
root = json_null();
}
- return json_pack("{s:o}", "synproxy", root);
+ return nft_json_pack("{s:o}", "synproxy", root);
}
json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- return json_pack("{s:o}", "reset",
+ return nft_json_pack("{s:o}", "reset",
expr_print_json(stmt->optstrip.expr, octx));
}
+json_t *xt_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ static const char *xt_typename[NFT_XT_MAX] = {
+ [NFT_XT_MATCH] = "match",
+ [NFT_XT_TARGET] = "target",
+ [NFT_XT_WATCHER] = "watcher",
+ };
+
+ return nft_json_pack("{s:{s:s, s:s}}", "xt",
+ "type", xt_typename[stmt->xt.type],
+ "name", stmt->xt.name);
+}
+
static json_t *table_print_json_full(struct netlink_ctx *ctx,
struct table *table)
{
@@ -1597,6 +1708,11 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
tmp = table_print_json(table);
json_array_append_new(root, tmp);
+ /* both maps and rules may refer to chains, list them first */
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
+ tmp = chain_print_json(chain);
+ json_array_append_new(root, tmp);
+ }
list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
tmp = obj_print_json(obj);
json_array_append_new(root, tmp);
@@ -1612,17 +1728,13 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
json_array_append_new(root, tmp);
}
list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
- tmp = chain_print_json(chain);
- json_array_append_new(root, tmp);
-
list_for_each_entry(rule, &chain->rules, list) {
tmp = rule_print_json(&ctx->nft->output, rule);
json_array_append_new(rules, tmp);
}
}
- json_array_extend(root, rules);
- json_decref(rules);
+ json_array_extend_new(root, rules);
return root;
}
@@ -1630,7 +1742,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(), *tmp;
+ json_t *root = json_array();
struct table *table;
list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
@@ -1638,9 +1750,7 @@ static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
table->handle.family != family)
continue;
- tmp = table_print_json_full(ctx, table);
- json_array_extend(root, tmp);
- json_decref(tmp);
+ json_array_extend_new(root, table_print_json_full(ctx, table));
}
return root;
@@ -1717,12 +1827,15 @@ static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd)
static json_t *do_list_set_json(struct netlink_ctx *ctx,
struct cmd *cmd, struct table *table)
{
- struct set *set = set_cache_find(table, cmd->handle.set.name);
+ struct set *set = cmd->set;
- if (set == NULL)
- return json_null();
+ if (!set) {
+ set = set_cache_find(table, cmd->handle.set.name);
+ if (set == NULL)
+ return json_null();
+ }
- return json_pack("[o]", set_print_json(&ctx->nft->output, set));
+ return nft_json_pack("[o]", set_print_json(&ctx->nft->output, set));
}
static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd)
@@ -1820,7 +1933,7 @@ static json_t *do_list_flowtables_json(struct netlink_ctx *ctx, struct cmd *cmd)
static json_t *generate_json_metainfo(void)
{
- return json_pack("{s: {s:s, s:s, s:i}}", "metainfo",
+ return nft_json_pack("{s: {s:s, s:s, s:i}}", "metainfo",
"version", PACKAGE_VERSION,
"release_name", RELEASE_NAME,
"json_schema_version", JSON_SCHEMA_VERSION);
@@ -1829,12 +1942,17 @@ static json_t *generate_json_metainfo(void)
int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct table *table = NULL;
- json_t *root;
+ json_t *root = NULL;
- if (cmd->handle.table.name)
+ if (cmd->handle.table.name) {
table = table_cache_find(&ctx->nft->cache.table_cache,
cmd->handle.table.name,
cmd->handle.family);
+ if (!table) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
switch (cmd->obj) {
case CMD_OBJ_TABLE:
@@ -1856,6 +1974,7 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SET:
root = do_list_set_json(ctx, cmd, table);
break;
+ case CMD_OBJ_RULES:
case CMD_OBJ_RULESET:
root = do_list_ruleset_json(ctx, cmd);
break;
@@ -1883,6 +2002,13 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_CT_HELPERS:
root = do_list_obj_json(ctx, cmd, NFT_OBJECT_CT_HELPER);
break;
+ case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
+ root = do_list_obj_json(ctx, cmd, NFT_OBJECT_CT_TIMEOUT);
+ case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_CT_EXPECTATIONS:
+ root = do_list_obj_json(ctx, cmd, NFT_OBJECT_CT_EXPECT);
+ break;
case CMD_OBJ_LIMIT:
case CMD_OBJ_LIMITS:
root = do_list_obj_json(ctx, cmd, NFT_OBJECT_LIMIT);
@@ -1891,14 +2017,29 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SECMARKS:
root = do_list_obj_json(ctx, cmd, NFT_OBJECT_SECMARK);
break;
+ case CMD_OBJ_SYNPROXY:
+ case CMD_OBJ_SYNPROXYS:
+ root = do_list_obj_json(ctx, cmd, NFT_OBJECT_SYNPROXY);
+ break;
case CMD_OBJ_FLOWTABLE:
root = do_list_flowtable_json(ctx, cmd, table);
break;
case CMD_OBJ_FLOWTABLES:
root = do_list_flowtables_json(ctx, cmd);
break;
- default:
+ case CMD_OBJ_HOOKS:
+ return 0;
+ case CMD_OBJ_MONITOR:
+ case CMD_OBJ_MARKUP:
+ case CMD_OBJ_SETELEMS:
+ case CMD_OBJ_RULE:
+ case CMD_OBJ_EXPR:
+ case CMD_OBJ_ELEMENTS:
+ errno = EOPNOTSUPP;
+ return -1;
+ case CMD_OBJ_INVALID:
BUG("invalid command object type %u\n", cmd->obj);
+ break;
}
if (!json_is_array(root)) {
@@ -1910,7 +2051,7 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
json_array_insert_new(root, 0, generate_json_metainfo());
- root = json_pack("{s:o}", "nftables", root);
+ root = nft_json_pack("{s:o}", "nftables", root);
json_dumpf(root, ctx->nft->output.output_fp, 0);
json_decref(root);
fprintf(ctx->nft->output.output_fp, "\n");
@@ -1923,7 +2064,7 @@ static void monitor_print_json(struct netlink_mon_handler *monh,
{
struct nft_ctx *nft = monh->ctx->nft;
- obj = json_pack("{s:o}", cmd, obj);
+ obj = nft_json_pack("{s:o}", cmd, obj);
if (nft_output_echo(&nft->output) && !nft->json_root) {
json_array_append_new(nft->json_echo, obj);
} else {
@@ -1966,6 +2107,12 @@ void monitor_print_obj_json(struct netlink_mon_handler *monh,
monitor_print_json(monh, cmd, obj_print_json(o));
}
+void monitor_print_flowtable_json(struct netlink_mon_handler *monh,
+ const char *cmd, struct flowtable *ft)
+{
+ monitor_print_json(monh, cmd, flowtable_print_json(ft));
+}
+
void monitor_print_rule_json(struct netlink_mon_handler *monh,
const char *cmd, struct rule *r)
{
diff --git a/src/libnftables.c b/src/libnftables.c
index a376825d..c8293f77 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -2,10 +2,12 @@
* Copyright (c) 2017 Eric Leblond <eric@regit.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+
+#include <nft.h>
+
#include <nftables/libnftables.h>
#include <erec.h>
#include <mnl.h>
@@ -14,12 +16,11 @@
#include <iface.h>
#include <cmd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
+#include <sys/stat.h>
+#include <libgen.h>
static int nft_netlink(struct nft_ctx *nft,
- struct list_head *cmds, struct list_head *msgs,
- struct mnl_socket *nf_sock)
+ struct list_head *cmds, struct list_head *msgs)
{
uint32_t batch_seqnum, seqnum = 0, last_seqnum = UINT32_MAX, num_cmds = 0;
struct netlink_ctx ctx = {
@@ -36,9 +37,9 @@ static int nft_netlink(struct nft_ctx *nft,
if (list_empty(cmds))
goto out;
- batch_seqnum = mnl_batch_begin(ctx.batch, mnl_seqnum_alloc(&seqnum));
+ batch_seqnum = mnl_batch_begin(ctx.batch, mnl_seqnum_inc(&seqnum));
list_for_each_entry(cmd, cmds, list) {
- ctx.seqnum = cmd->seqnum = mnl_seqnum_alloc(&seqnum);
+ ctx.seqnum = cmd->seqnum_from = mnl_seqnum_inc(&seqnum);
ret = do_command(&ctx, cmd);
if (ret < 0) {
netlink_io_error(&ctx, &cmd->location,
@@ -46,16 +47,25 @@ static int nft_netlink(struct nft_ctx *nft,
strerror(errno));
goto out;
}
+ seqnum = cmd->seqnum_to = ctx.seqnum;
+ mnl_seqnum_inc(&seqnum);
num_cmds++;
}
if (!nft->check)
- mnl_batch_end(ctx.batch, mnl_seqnum_alloc(&seqnum));
+ mnl_batch_end(ctx.batch, mnl_seqnum_inc(&seqnum));
if (!mnl_batch_ready(ctx.batch))
goto out;
ret = mnl_batch_talk(&ctx, &err_list, num_cmds);
if (ret < 0) {
+ if (ctx.maybe_emsgsize && errno == EMSGSIZE) {
+ netlink_io_error(&ctx, NULL,
+ "Could not process rule: %s\n"
+ "Please, rise /proc/sys/net/core/wmem_max on the host namespace. Hint: %d bytes",
+ strerror(errno), round_pow_2(ctx.maybe_emsgsize));
+ goto out;
+ }
netlink_io_error(&ctx, NULL,
"Could not process rule: %s", strerror(errno));
goto out;
@@ -72,12 +82,14 @@ static int nft_netlink(struct nft_ctx *nft,
cmd = list_first_entry(cmds, struct cmd, list);
list_for_each_entry_from(cmd, cmds, list) {
- last_seqnum = cmd->seqnum;
- if (err->seqnum == cmd->seqnum ||
+ last_seqnum = cmd->seqnum_to;
+ if ((err->seqnum >= cmd->seqnum_from &&
+ err->seqnum <= cmd->seqnum_to) ||
err->seqnum == batch_seqnum) {
nft_cmd_error(&ctx, cmd, err);
errno = err->err;
- if (err->seqnum == cmd->seqnum) {
+ if (err->seqnum >= cmd->seqnum_from ||
+ err->seqnum <= cmd->seqnum_to) {
mnl_err_list_free(err);
break;
}
@@ -147,15 +159,27 @@ void nft_ctx_clear_vars(struct nft_ctx *ctx)
unsigned int i;
for (i = 0; i < ctx->num_vars; i++) {
- xfree(ctx->vars[i].key);
- xfree(ctx->vars[i].value);
+ free_const(ctx->vars[i].key);
+ free_const(ctx->vars[i].value);
}
ctx->num_vars = 0;
- xfree(ctx->vars);
+ free(ctx->vars);
+ ctx->vars = NULL;
}
-EXPORT_SYMBOL(nft_ctx_add_include_path);
-int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
+static bool nft_ctx_find_include_path(struct nft_ctx *ctx, const char *path)
+{
+ unsigned int i;
+
+ for (i = 0; i < ctx->num_include_paths; i++) {
+ if (!strcmp(ctx->include_paths[i], path))
+ return true;
+ }
+
+ return false;
+}
+
+static int __nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
{
char **tmp;
int pcount = ctx->num_include_paths;
@@ -171,40 +195,43 @@ int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
return 0;
}
+EXPORT_SYMBOL(nft_ctx_add_include_path);
+int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
+{
+ char canonical_path[PATH_MAX];
+
+ if (!realpath(path, canonical_path))
+ return -1;
+
+ if (nft_ctx_find_include_path(ctx, canonical_path))
+ return 0;
+
+ return __nft_ctx_add_include_path(ctx, canonical_path);
+}
+
EXPORT_SYMBOL(nft_ctx_clear_include_paths);
void nft_ctx_clear_include_paths(struct nft_ctx *ctx)
{
while (ctx->num_include_paths)
- xfree(ctx->include_paths[--ctx->num_include_paths]);
+ free(ctx->include_paths[--ctx->num_include_paths]);
- xfree(ctx->include_paths);
+ free(ctx->include_paths);
ctx->include_paths = NULL;
}
-static void nft_ctx_netlink_init(struct nft_ctx *ctx)
-{
- ctx->nf_sock = nft_mnl_socket_open();
-}
-
EXPORT_SYMBOL(nft_ctx_new);
struct nft_ctx *nft_ctx_new(uint32_t flags)
{
- static bool init_once;
struct nft_ctx *ctx;
- if (!init_once) {
- init_once = true;
- gmp_init();
#ifdef HAVE_LIBXTABLES
- xt_init();
+ xt_init();
#endif
- }
ctx = xzalloc(sizeof(struct nft_ctx));
nft_init(ctx);
ctx->state = xzalloc(sizeof(struct parser_state));
- nft_ctx_add_include_path(ctx, DEFAULT_INCLUDE_PATH);
ctx->parser_max_errors = 10;
cache_init(&ctx->cache.table_cache);
ctx->top_scope = scope_alloc();
@@ -213,8 +240,7 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
ctx->output.error_fp = stderr;
init_list_head(&ctx->vars_ctx.indesc_list);
- if (flags == NFT_CTX_DEFAULT)
- nft_ctx_netlink_init(ctx);
+ ctx->nf_sock = nft_mnl_socket_open();
return ctx;
}
@@ -338,8 +364,7 @@ const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx)
EXPORT_SYMBOL(nft_ctx_free);
void nft_ctx_free(struct nft_ctx *ctx)
{
- if (ctx->nf_sock)
- mnl_socket_close(ctx->nf_sock);
+ mnl_socket_close(ctx->nf_sock);
exit_cookie(&ctx->output.output_cookie);
exit_cookie(&ctx->output.error_cookie);
@@ -348,9 +373,9 @@ void nft_ctx_free(struct nft_ctx *ctx)
nft_ctx_clear_vars(ctx);
nft_ctx_clear_include_paths(ctx);
scope_free(ctx->top_scope);
- xfree(ctx->state);
+ free(ctx->state);
nft_exit(ctx);
- xfree(ctx);
+ free(ctx);
}
EXPORT_SYMBOL(nft_ctx_set_output);
@@ -403,6 +428,22 @@ void nft_ctx_set_optimize(struct nft_ctx *ctx, uint32_t flags)
ctx->optimize_flags = flags;
}
+EXPORT_SYMBOL(nft_ctx_input_get_flags);
+unsigned int nft_ctx_input_get_flags(struct nft_ctx *ctx)
+{
+ return ctx->input.flags;
+}
+
+EXPORT_SYMBOL(nft_ctx_input_set_flags);
+unsigned int nft_ctx_input_set_flags(struct nft_ctx *ctx, unsigned int flags)
+{
+ unsigned int old_flags;
+
+ old_flags = ctx->input.flags;
+ ctx->input.flags = flags;
+ return old_flags;
+}
+
EXPORT_SYMBOL(nft_ctx_output_get_flags);
unsigned int nft_ctx_output_get_flags(struct nft_ctx *ctx)
{
@@ -501,7 +542,6 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
{
struct nft_cache_filter *filter;
struct cmd *cmd, *next;
- bool collapsed = false;
unsigned int flags;
int err = 0;
@@ -517,8 +557,13 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
nft_cache_filter_fini(filter);
- if (nft_cmd_collapse(cmds))
- collapsed = true;
+ list_for_each_entry(cmd, cmds, list) {
+ if (cmd->op != CMD_ADD &&
+ cmd->op != CMD_CREATE)
+ continue;
+
+ nft_cmd_expand(cmd);
+ }
list_for_each_entry_safe(cmd, next, cmds, list) {
struct eval_ctx ectx = {
@@ -533,19 +578,9 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
}
}
- if (collapsed)
- nft_cmd_uncollapse(cmds);
-
if (err < 0 || nft->state->nerrs)
return -1;
- list_for_each_entry(cmd, cmds, list) {
- if (cmd->op != CMD_ADD)
- continue;
-
- nft_cmd_expand(cmd);
- }
-
return 0;
}
@@ -561,7 +596,7 @@ int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
nlbuf = xzalloc(strlen(buf) + 2);
sprintf(nlbuf, "%s\n", buf);
- if (nft_output_json(&nft->output))
+ if (nft_output_json(&nft->output) || nft_input_json(&nft->input))
rc = nft_parse_json_buffer(nft, nlbuf, &msgs, &cmds);
if (rc == -EINVAL)
rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds,
@@ -583,7 +618,7 @@ int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
goto err;
}
- if (nft_netlink(nft, &cmds, &msgs, nft->nf_sock) != 0)
+ if (nft_netlink(nft, &cmds, &msgs) != 0)
rc = -1;
err:
erec_print_list(&nft->output, &msgs, nft->debug_mask);
@@ -602,8 +637,10 @@ err:
nft_output_json(&nft->output) &&
nft_output_echo(&nft->output))
json_print_echo(nft);
- if (rc)
+
+ if (rc || nft->check)
nft_cache_release(&nft->cache);
+
return rc;
}
@@ -648,19 +685,53 @@ retry:
return rc;
}
+/* need to use stat() to, fopen() will block for named fifos and
+ * libjansson makes no checks before or after open either.
+ * /dev/stdin is *never* used, read() from STDIN_FILENO is used instead.
+ */
+static struct error_record *filename_is_useable(struct nft_ctx *nft, const char *name)
+{
+ unsigned int type;
+ struct stat sb;
+ int err;
+
+ if (!strcmp(name, "/dev/stdin"))
+ return NULL;
+
+ err = stat(name, &sb);
+ if (err)
+ return error(&internal_location, "Could not open file \"%s\": %s\n",
+ name, strerror(errno));
+
+ type = sb.st_mode & S_IFMT;
+
+ if (type == S_IFREG || type == S_IFIFO)
+ return NULL;
+
+ return error(&internal_location, "Not a regular file: \"%s\"\n", name);
+}
+
static int __nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
{
+ struct error_record *erec;
struct cmd *cmd, *next;
int rc, parser_rc;
LIST_HEAD(msgs);
LIST_HEAD(cmds);
+ erec = filename_is_useable(nft, filename);
+ if (erec) {
+ erec_print(&nft->output, erec, nft->debug_mask);
+ erec_destroy(erec);
+ return -1;
+ }
+
rc = load_cmdline_vars(nft, &msgs);
if (rc < 0)
goto err;
rc = -EINVAL;
- if (nft_output_json(&nft->output))
+ if (nft_output_json(&nft->output) || nft_input_json(&nft->input))
rc = nft_parse_json_filename(nft, filename, &msgs, &cmds);
if (rc == -EINVAL)
rc = nft_parse_bison_filename(nft, filename, &msgs, &cmds);
@@ -679,7 +750,7 @@ static int __nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename
goto err;
}
- if (nft_netlink(nft, &cmds, &msgs, nft->nf_sock) != 0)
+ if (nft_netlink(nft, &cmds, &msgs) != 0)
rc = -1;
err:
erec_print_list(&nft->output, &msgs, nft->debug_mask);
@@ -697,18 +768,19 @@ err:
list_for_each_entry_safe(indesc, next, &nft->vars_ctx.indesc_list, list) {
if (indesc->name)
- xfree(indesc->name);
+ free_const(indesc->name);
- xfree(indesc);
+ free(indesc);
}
}
- xfree(nft->vars_ctx.buf);
+ free_const(nft->vars_ctx.buf);
if (!rc &&
nft_output_json(&nft->output) &&
nft_output_echo(&nft->output))
json_print_echo(nft);
- if (rc)
+
+ if (rc || nft->check)
nft_cache_release(&nft->cache);
scope_release(nft->state->scopes[0]);
@@ -738,6 +810,19 @@ static int nft_run_optimized_file(struct nft_ctx *nft, const char *filename)
return __nft_run_cmd_from_filename(nft, filename);
}
+static int nft_ctx_add_basedir_include_path(struct nft_ctx *nft,
+ const char *filename)
+{
+ char *basedir = xstrdup(filename);
+ int ret;
+
+ ret = nft_ctx_add_include_path(nft, dirname(basedir));
+
+ free(basedir);
+
+ return ret;
+}
+
EXPORT_SYMBOL(nft_run_cmd_from_filename);
int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
{
@@ -746,18 +831,21 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
if (!strcmp(filename, "-"))
filename = "/dev/stdin";
- if (!strcmp(filename, "/dev/stdin") &&
- !nft_output_json(&nft->output))
+ if (!strcmp(filename, "/dev/stdin"))
nft->stdin_buf = stdin_to_buffer();
+ if (!nft->stdin_buf &&
+ nft_ctx_add_basedir_include_path(nft, filename) < 0)
+ return -1;
+
if (nft->optimize_flags) {
ret = nft_run_optimized_file(nft, filename);
- xfree(nft->stdin_buf);
+ free_const(nft->stdin_buf);
return ret;
}
ret = __nft_run_cmd_from_filename(nft, filename);
- xfree(nft->stdin_buf);
+ free_const(nft->stdin_buf);
return ret;
}
diff --git a/src/libnftables.map b/src/libnftables.map
index a46a3ad5..9369f44f 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -33,3 +33,8 @@ LIBNFTABLES_3 {
nft_ctx_set_optimize;
nft_ctx_get_optimize;
} LIBNFTABLES_2;
+
+LIBNFTABLES_4 {
+ nft_ctx_input_get_flags;
+ nft_ctx_input_set_flags;
+} LIBNFTABLES_3;
diff --git a/src/main.c b/src/main.c
index 9bd25db8..72151e62 100644
--- a/src/main.c
+++ b/src/main.c
@@ -8,12 +8,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <stdlib.h>
+#include <nft.h>
+
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
-#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/types.h>
@@ -325,6 +325,7 @@ static bool nft_options_check(int argc, char * const argv[])
{
bool skip = false, nonoption = false;
int pos = 0, i;
+ size_t j;
for (i = 1; i < argc; i++) {
pos += strlen(argv[i - 1]) + 1;
@@ -333,23 +334,22 @@ static bool nft_options_check(int argc, char * const argv[])
} else if (skip) {
skip = false;
continue;
- } else if (argv[i][0] == '-') {
- if (nonoption) {
- nft_options_error(argc, argv, pos);
- return false;
- } else if (argv[i][1] == 'd' ||
- argv[i][1] == 'I' ||
- argv[i][1] == 'f' ||
- argv[i][1] == 'D' ||
- !strcmp(argv[i], "--debug") ||
- !strcmp(argv[i], "--includepath") ||
- !strcmp(argv[i], "--define") ||
- !strcmp(argv[i], "--file")) {
- skip = true;
- continue;
- }
} else if (argv[i][0] != '-') {
nonoption = true;
+ continue;
+ }
+ if (nonoption) {
+ nft_options_error(argc, argv, pos);
+ return false;
+ }
+ for (j = 0; j < NR_NFT_OPTIONS; j++) {
+ if (nft_options[j].arg &&
+ (argv[i][1] == (char)nft_options[j].val ||
+ (argv[i][1] == '-' &&
+ !strcmp(argv[i] + 2, nft_options[j].name)))) {
+ skip = true;
+ break;
+ }
}
}
@@ -361,11 +361,11 @@ int main(int argc, char * const *argv)
const struct option *options = get_options();
bool interactive = false, define = false;
const char *optstring = get_optstring();
- char *buf = NULL, *filename = NULL;
unsigned int output_flags = 0;
+ int i, val, rc = EXIT_SUCCESS;
unsigned int debug_mask;
+ char *filename = NULL;
unsigned int len;
- int i, val, rc;
/* nftables cannot be used with setuid in a safe way. */
if (getuid() != geteuid())
@@ -384,20 +384,20 @@ int main(int argc, char * const *argv)
switch (val) {
case OPT_HELP:
show_help(argv[0]);
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_VERSION:
printf("%s v%s (%s)\n",
PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME);
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_VERSION_LONG:
show_version();
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_DEFINE:
if (nft_ctx_add_var(nft, optarg)) {
fprintf(stderr,
- "Failed to define variable '%s'\n",
+ "Error: Failed to define variable '%s'\n",
optarg);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
define = true;
break;
@@ -405,17 +405,26 @@ int main(int argc, char * const *argv)
nft_ctx_set_dry_run(nft, true);
break;
case OPT_FILE:
+ if (interactive) {
+ fprintf(stderr,
+ "Error: -i/--interactive and -f/--file options cannot be combined\n");
+ goto out_fail;
+ }
filename = optarg;
break;
case OPT_INTERACTIVE:
+ if (filename) {
+ fprintf(stderr,
+ "Error: -i/--interactive and -f/--file options cannot be combined\n");
+ goto out_fail;
+ }
interactive = true;
break;
case OPT_INCLUDEPATH:
if (nft_ctx_add_include_path(nft, optarg)) {
fprintf(stderr,
- "Failed to add include path '%s'\n",
+ "Warning: Cannot include path '%s'\n",
optarg);
- exit(EXIT_FAILURE);
}
break;
case OPT_NUMERIC:
@@ -448,9 +457,9 @@ int main(int argc, char * const *argv)
}
if (i == array_size(debug_param)) {
- fprintf(stderr, "invalid debug parameter `%s'\n",
+ fprintf(stderr, "Error: invalid debug parameter `%s'\n",
optarg);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
if (end == NULL)
@@ -469,8 +478,8 @@ int main(int argc, char * const *argv)
#ifdef HAVE_LIBJANSSON
output_flags |= NFT_CTX_OUTPUT_JSON;
#else
- fprintf(stderr, "JSON support not compiled-in\n");
- exit(EXIT_FAILURE);
+ fprintf(stderr, "Error: JSON support not compiled-in\n");
+ goto out_fail;
#endif
break;
case OPT_GUID:
@@ -492,18 +501,20 @@ int main(int argc, char * const *argv)
nft_ctx_set_optimize(nft, 0x1);
break;
case OPT_INVALID:
- exit(EXIT_FAILURE);
+ goto out_fail;
}
}
if (!filename && define) {
fprintf(stderr, "Error: -D/--define can only be used with -f/--filename\n");
- exit(EXIT_FAILURE);
+ goto out_fail;
}
nft_ctx_output_set_flags(nft, output_flags);
if (optind != argc) {
+ char *buf;
+
for (len = 0, i = optind; i < argc; i++)
len += strlen(argv[i]) + strlen(" ");
@@ -511,7 +522,7 @@ int main(int argc, char * const *argv)
if (buf == NULL) {
fprintf(stderr, "%s:%u: Memory allocation failure\n",
__FILE__, __LINE__);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
for (i = optind; i < argc; i++) {
strcat(buf, argv[i]);
@@ -519,22 +530,24 @@ int main(int argc, char * const *argv)
strcat(buf, " ");
}
rc = !!nft_run_cmd_from_buffer(nft, buf);
+ free(buf);
} else if (filename != NULL) {
rc = !!nft_run_cmd_from_filename(nft, filename);
} else if (interactive) {
if (cli_init(nft) < 0) {
fprintf(stderr, "%s: interactive CLI not supported in this build\n",
argv[0]);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
- return EXIT_SUCCESS;
} else {
fprintf(stderr, "%s: no command specified\n", argv[0]);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
- free(buf);
+out:
nft_ctx_free(nft);
-
return rc;
+out_fail:
+ nft_ctx_free(nft);
+ return EXIT_FAILURE;
}
diff --git a/src/mergesort.c b/src/mergesort.c
index dca71422..0452d60a 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -2,17 +2,16 @@
* Copyright (c) 2017 Elise Lennion <elise.lennion@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
-#include <stdint.h>
+#include <nft.h>
+
#include <expression.h>
#include <gmputil.h>
#include <list.h>
-static void expr_msort_value(const struct expr *expr, mpz_t value);
-
static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
{
unsigned int len = 0, ilen;
@@ -28,20 +27,19 @@ static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
mpz_import_data(value, data, BYTEORDER_HOST_ENDIAN, len);
}
-static void expr_msort_value(const struct expr *expr, mpz_t value)
+static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value)
{
switch (expr->etype) {
case EXPR_SET_ELEM:
- expr_msort_value(expr->key, value);
- break;
+ return expr_msort_value(expr->key, value);
case EXPR_BINOP:
case EXPR_MAPPING:
case EXPR_RANGE:
- expr_msort_value(expr->left, value);
- break;
+ return expr_msort_value(expr->left, value);
case EXPR_VALUE:
- mpz_set(value, expr->value);
- break;
+ return expr->value;
+ case EXPR_RANGE_VALUE:
+ return expr->range.low;
case EXPR_CONCAT:
concat_expr_msort_value(expr, value);
break;
@@ -52,20 +50,24 @@ static void expr_msort_value(const struct expr *expr, mpz_t value)
default:
BUG("Unknown expression %s\n", expr_name(expr));
}
+ return value;
}
static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
{
- mpz_t value1, value2;
+ mpz_srcptr value1;
+ mpz_srcptr value2;
+ mpz_t value1_tmp;
+ mpz_t value2_tmp;
int ret;
- mpz_init(value1);
- mpz_init(value2);
- expr_msort_value(e1, value1);
- expr_msort_value(e2, value2);
+ mpz_init(value1_tmp);
+ mpz_init(value2_tmp);
+ value1 = expr_msort_value(e1, value1_tmp);
+ value2 = expr_msort_value(e2, value2_tmp);
ret = mpz_cmp(value1, value2);
- mpz_clear(value1);
- mpz_clear(value2);
+ mpz_clear(value1_tmp);
+ mpz_clear(value2_tmp);
return ret;
}
@@ -78,7 +80,7 @@ void list_splice_sorted(struct list_head *list, struct list_head *head)
while (l != list) {
if (h == head ||
expr_msort_cmp(list_entry(l, typeof(struct expr), list),
- list_entry(h, typeof(struct expr), list)) < 0) {
+ list_entry(h, typeof(struct expr), list)) <= 0) {
l = l->next;
list_add_tail(l->prev, h);
continue;
diff --git a/src/meta.c b/src/meta.c
index 257bbc9f..1010209d 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -10,13 +10,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <errno.h>
#include <limits.h>
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <pwd.h>
@@ -25,6 +24,7 @@
#include <linux/netfilter.h>
#include <linux/pkt_sched.h>
#include <linux/if_packet.h>
+#include <time.h>
#include <nftables.h>
#include <expression.h>
@@ -37,10 +37,6 @@
#include <iface.h>
#include <json.h>
-#define _XOPEN_SOURCE
-#define __USE_XOPEN
-#include <time.h>
-
static void tchandle_type_print(const struct expr *expr,
struct output_ctx *octx)
{
@@ -66,50 +62,39 @@ static struct error_record *tchandle_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
uint32_t handle;
- char *str = NULL;
if (strcmp(sym->identifier, "root") == 0)
handle = TC_H_ROOT;
else if (strcmp(sym->identifier, "none") == 0)
handle = TC_H_UNSPEC;
else if (strchr(sym->identifier, ':')) {
+ char *colon, *end;
uint32_t tmp;
- char *colon;
-
- str = xstrdup(sym->identifier);
-
- colon = strchr(str, ':');
- if (!colon)
- goto err;
-
- *colon = '\0';
errno = 0;
- tmp = strtoull(str, NULL, 16);
- if (errno != 0)
+ tmp = strtoul(sym->identifier, &colon, 16);
+ if (errno != 0 || sym->identifier == colon)
goto err;
- handle = (tmp << 16);
- if (str[strlen(str) - 1] == ':')
- goto out;
+ if (*colon != ':')
+ goto err;
+ handle = tmp << 16;
errno = 0;
- tmp = strtoull(colon + 1, NULL, 16);
- if (errno != 0)
+ tmp = strtoul(colon + 1, &end, 16);
+ if (errno != 0 || *end)
goto err;
handle |= tmp;
} else {
handle = strtoull(sym->identifier, NULL, 0);
}
-out:
- xfree(str);
+
*res = constant_expr_alloc(&sym->location, sym->dtype,
BYTEORDER_HOST_ENDIAN,
sizeof(handle) * BITS_PER_BYTE, &handle);
return NULL;
err:
- xfree(str);
return error(&sym->location, "Could not parse %s", sym->dtype->desc);
}
@@ -340,7 +325,7 @@ const struct datatype pkttype_type = {
void devgroup_table_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.devgroup = rt_symbol_table_init("/etc/iproute2/group");
+ ctx->output.tbl.devgroup = rt_symbol_table_init("group");
}
void devgroup_table_exit(struct nft_ctx *ctx)
@@ -361,17 +346,23 @@ static struct error_record *devgroup_type_parse(struct parse_ctx *ctx,
return symbolic_constant_parse(ctx, sym, ctx->tbl->devgroup, res);
}
+static void devgroup_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, "group",
+ octx->tbl.devgroup, &devgroup_type);
+}
+
const struct datatype devgroup_type = {
.type = TYPE_DEVGROUP,
.name = "devgroup",
.desc = "devgroup name",
+ .describe = devgroup_type_describe,
.byteorder = BYTEORDER_HOST_ENDIAN,
.size = 4 * BITS_PER_BYTE,
.basetype = &integer_type,
.print = devgroup_type_print,
.json = devgroup_type_json,
.parse = devgroup_type_parse,
- .flags = DTYPE_F_PREFIX,
};
const struct datatype ifname_type = {
@@ -385,21 +376,22 @@ const struct datatype ifname_type = {
static void date_type_print(const struct expr *expr, struct output_ctx *octx)
{
- uint64_t tstamp = mpz_get_uint64(expr->value);
- struct tm *tm, *cur_tm;
+ uint64_t tstamp64 = mpz_get_uint64(expr->value);
char timestr[21];
+ time_t tstamp;
+ struct tm tm;
/* Convert from nanoseconds to seconds */
- tstamp /= 1000000000L;
+ tstamp64 /= 1000000000L;
/* Obtain current tm, to add tm_gmtoff to the timestamp */
- cur_tm = localtime((time_t *) &tstamp);
+ tstamp = tstamp64;
+ if (localtime_r(&tstamp, &tm))
+ tstamp64 += tm.tm_gmtoff;
- if (cur_tm)
- tstamp += cur_tm->tm_gmtoff;
-
- if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
- strftime(timestr, sizeof(timestr) - 1, "%Y-%m-%d %T", tm))
+ tstamp = tstamp64;
+ if (gmtime_r(&tstamp, &tm) &&
+ strftime(timestr, sizeof(timestr) - 1, "%Y-%m-%d %T", &tm))
nft_print(octx, "\"%s\"", timestr);
else
nft_print(octx, "Error converting timestamp to printed time");
@@ -407,7 +399,8 @@ static void date_type_print(const struct expr *expr, struct output_ctx *octx)
static bool parse_iso_date(uint64_t *tstamp, const char *sym)
{
- struct tm tm, *cur_tm;
+ struct tm cur_tm;
+ struct tm tm;
time_t ts;
memset(&tm, 0, sizeof(struct tm));
@@ -429,14 +422,15 @@ success:
*/
ts = timegm(&tm);
- /* Obtain current tm as well (at the specified time), so that we can substract tm_gmtoff */
- cur_tm = localtime(&ts);
+ if (ts == (time_t) -1)
+ return false;
- if (ts == (time_t) -1 || cur_tm == NULL)
- return ts;
+ /* Obtain current tm as well (at the specified time), so that we can substract tm_gmtoff */
+ if (!localtime_r(&ts, &cur_tm))
+ return false;
/* Substract tm_gmtoff to get the current time */
- *tstamp = ts - cur_tm->tm_gmtoff;
+ *tstamp = ts - cur_tm.tm_gmtoff;
return true;
}
@@ -491,16 +485,21 @@ static void day_type_print(const struct expr *expr, struct output_ctx *octx)
static void hour_type_print(const struct expr *expr, struct output_ctx *octx)
{
uint32_t seconds = mpz_get_uint32(expr->value), minutes, hours;
- struct tm *cur_tm;
+ struct tm cur_tm;
time_t ts;
/* Obtain current tm, so that we can add tm_gmtoff */
ts = time(NULL);
- cur_tm = localtime(&ts);
+ if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm)) {
+ int32_t adj = seconds + cur_tm.tm_gmtoff;
- if (cur_tm)
- seconds = (seconds + cur_tm->tm_gmtoff) % SECONDS_PER_DAY;
+ if (adj < 0)
+ adj += SECONDS_PER_DAY;
+ else if (adj >= SECONDS_PER_DAY)
+ adj -= SECONDS_PER_DAY;
+ seconds = adj;
+ }
minutes = seconds / 60;
seconds %= 60;
hours = minutes / 60;
@@ -517,10 +516,12 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct error_record *er;
- struct tm tm, *cur_tm;
+ struct tm cur_tm_data;
+ struct tm *cur_tm;
uint32_t result;
uint64_t tmp;
char *endptr;
+ struct tm tm;
time_t ts;
memset(&tm, 0, sizeof(struct tm));
@@ -534,7 +535,10 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
/* Obtain current tm, so that we can substract tm_gmtoff */
ts = time(NULL);
- cur_tm = localtime(&ts);
+ if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm_data))
+ cur_tm = &cur_tm_data;
+ else
+ cur_tm = NULL;
endptr = strptime(sym->identifier, "%T", &tm);
if (endptr && *endptr == '\0')
@@ -698,6 +702,8 @@ const struct meta_template meta_templates[] = {
[NFT_META_SDIFNAME] = META_TEMPLATE("sdifname", &ifname_type,
IFNAMSIZ * BITS_PER_BYTE,
BYTEORDER_HOST_ENDIAN),
+ [NFT_META_BRI_BROUTE] = META_TEMPLATE("broute", &integer_type,
+ 1 , BYTEORDER_HOST_ENDIAN),
};
static bool meta_key_is_unqualified(enum nft_meta_keys key)
@@ -717,12 +723,16 @@ static bool meta_key_is_unqualified(enum nft_meta_keys key)
static void meta_expr_print(const struct expr *expr, struct output_ctx *octx)
{
- if (meta_key_is_unqualified(expr->meta.key))
- nft_print(octx, "%s",
- meta_templates[expr->meta.key].token);
+ const char *token = "unknown";
+ uint32_t key = expr->meta.key;
+
+ if (key < array_size(meta_templates))
+ token = meta_templates[key].token;
+
+ if (meta_key_is_unqualified(key))
+ nft_print(octx, "%s", token);
else
- nft_print(octx, "meta %s",
- meta_templates[expr->meta.key].token);
+ nft_print(octx, "meta %s", token);
}
static bool meta_expr_cmp(const struct expr *e1, const struct expr *e2)
@@ -734,6 +744,7 @@ static void meta_expr_clone(struct expr *new, const struct expr *expr)
{
new->meta.key = expr->meta.key;
new->meta.base = expr->meta.base;
+ new->meta.inner_desc = expr->meta.inner_desc;
}
/**
@@ -768,6 +779,11 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
break;
case NFT_META_NFPROTO:
protonum = mpz_get_uint8(right->value);
+ if (protonum == NFPROTO_IPV4 && h->desc == &proto_ip)
+ break;
+ else if (protonum == NFPROTO_IPV6 && h->desc == &proto_ip6)
+ break;
+
desc = proto_find_upper(h->desc, protonum);
if (desc == NULL) {
desc = &proto_unknown;
@@ -807,13 +823,19 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
}
#define NFTNL_UDATA_META_KEY 0
-#define NFTNL_UDATA_META_MAX 1
+#define NFTNL_UDATA_META_INNER_DESC 1
+#define NFTNL_UDATA_META_MAX 2
static int meta_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct expr *expr)
{
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_KEY, expr->meta.key);
+ if (expr->meta.inner_desc) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_INNER_DESC,
+ expr->meta.inner_desc->id);
+ }
+
return 0;
}
@@ -825,6 +847,7 @@ static int meta_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_META_KEY:
+ case NFTNL_UDATA_META_INNER_DESC:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -839,6 +862,8 @@ static int meta_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *meta_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_META_MAX + 1] = {};
+ const struct proto_desc *desc;
+ struct expr *expr;
uint32_t key;
int err;
@@ -852,7 +877,14 @@ static struct expr *meta_expr_parse_udata(const struct nftnl_udata *attr)
key = nftnl_udata_get_u32(ud[NFTNL_UDATA_META_KEY]);
- return meta_expr_alloc(&internal_location, key);
+ expr = meta_expr_alloc(&internal_location, key);
+
+ if (ud[NFTNL_UDATA_META_INNER_DESC]) {
+ desc = find_proto_desc(ud[NFTNL_UDATA_META_INNER_DESC]);
+ expr->meta.inner_desc = desc;
+ }
+
+ return expr;
}
const struct expr_ops meta_expr_ops = {
@@ -901,12 +933,16 @@ struct expr *meta_expr_alloc(const struct location *loc, enum nft_meta_keys key)
static void meta_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
+ const char *token = "unknown";
+ uint32_t key = stmt->meta.key;
+
+ if (key < array_size(meta_templates))
+ token = meta_templates[key].token;
+
if (meta_key_is_unqualified(stmt->meta.key))
- nft_print(octx, "%s set ",
- meta_templates[stmt->meta.key].token);
+ nft_print(octx, "%s set ", token);
else
- nft_print(octx, "meta %s set ",
- meta_templates[stmt->meta.key].token);
+ nft_print(octx, "meta %s set ", token);
expr_print(stmt->meta.expr, octx);
}
@@ -916,7 +952,7 @@ static void meta_stmt_destroy(struct stmt *stmt)
expr_free(stmt->meta.expr);
}
-static const struct stmt_ops meta_stmt_ops = {
+const struct stmt_ops meta_stmt_ops = {
.type = STMT_META,
.name = "meta",
.print = meta_stmt_print,
@@ -931,8 +967,11 @@ struct stmt *meta_stmt_alloc(const struct location *loc, enum nft_meta_keys key,
stmt = stmt_alloc(loc, &meta_stmt_ops);
stmt->meta.key = key;
- stmt->meta.tmpl = &meta_templates[key];
stmt->meta.expr = expr;
+
+ if (key < array_size(meta_templates))
+ stmt->meta.tmpl = &meta_templates[key];
+
return stmt;
}
@@ -960,11 +999,11 @@ struct error_record *meta_key_parse(const struct location *loc,
const char *str,
unsigned int *value)
{
- int ret, len, offset = 0;
const char *sep = "";
+ size_t offset = 0;
unsigned int i;
char buf[1024];
- size_t size;
+ size_t len;
for (i = 0; i < array_size(meta_templates); i++) {
if (!meta_templates[i].token || strcmp(meta_templates[i].token, str))
@@ -987,9 +1026,10 @@ struct error_record *meta_key_parse(const struct location *loc,
}
len = (int)sizeof(buf);
- size = sizeof(buf);
for (i = 0; i < array_size(meta_templates); i++) {
+ int ret;
+
if (!meta_templates[i].token)
continue;
@@ -997,8 +1037,8 @@ struct error_record *meta_key_parse(const struct location *loc,
sep = ", ";
ret = snprintf(buf+offset, len, "%s%s", sep, meta_templates[i].token);
- SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- assert(offset < (int)sizeof(buf));
+ SNPRINTF_BUFFER_SIZE(ret, &len, &offset);
+ assert(len > 0);
}
return error(loc, "syntax error, unexpected %s, known keys are %s", str, buf);
diff --git a/src/mini-gmp.c b/src/mini-gmp.c
index 04bed3f5..186dc3a4 100644
--- a/src/mini-gmp.c
+++ b/src/mini-gmp.c
@@ -41,12 +41,12 @@ see https://www.gnu.org/licenses/. */
mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c,
mpn/generic/submul_1.c. */
+#include <nft.h>
+
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include "mini-gmp.h"
diff --git a/src/misspell.c b/src/misspell.c
index 6536d755..f5354fa8 100644
--- a/src/misspell.c
+++ b/src/misspell.c
@@ -1,5 +1,13 @@
-#include <stdlib.h>
-#include <string.h>
+/*
+ * Copyright (c) 2018 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <limits.h>
#include <utils.h>
#include <misspell.h>
@@ -64,7 +72,7 @@ static unsigned int string_distance(const char *a, const char *b)
ret = DISTANCE(len_a, len_b);
- xfree(distance);
+ free(distance);
return ret;
}
diff --git a/src/mnl.c b/src/mnl.c
index e87b0338..8a8dc4d6 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -2,12 +2,15 @@
* Copyright (c) 2013-2017 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+#include <iface.h>
+
#include <libmnl/libmnl.h>
#include <libnftnl/common.h>
#include <libnftnl/ruleset.h>
@@ -26,13 +29,13 @@
#include <linux/netfilter/nf_tables.h>
#include <mnl.h>
-#include <string.h>
+#include <cmd.h>
+#include <intervals.h>
#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
-#include <stdlib.h>
#include <utils.h>
#include <nftables.h>
#include <linux/netfilter.h>
@@ -44,6 +47,7 @@ struct basehook {
const char *hookfn;
const char *table;
const char *chain;
+ const char *devname;
int family;
int chain_family;
uint32_t num;
@@ -67,7 +71,7 @@ struct mnl_socket *nft_mnl_socket_open(void)
return nf_sock;
}
-uint32_t mnl_seqnum_alloc(unsigned int *seqnum)
+uint32_t mnl_seqnum_inc(unsigned int *seqnum)
{
return (*seqnum)++;
}
@@ -241,12 +245,13 @@ static void mnl_err_list_node_add(struct list_head *err_list, int error,
void mnl_err_list_free(struct mnl_err *err)
{
list_del(&err->head);
- xfree(err);
+ free(err);
}
-static void mnl_set_sndbuffer(const struct mnl_socket *nl,
- struct nftnl_batch *batch)
+static void mnl_set_sndbuffer(struct netlink_ctx *ctx)
{
+ struct mnl_socket *nl = ctx->nft->nf_sock;
+ struct nftnl_batch *batch = ctx->batch;
socklen_t len = sizeof(int);
int sndnlbuffsiz = 0;
int newbuffsiz;
@@ -259,9 +264,15 @@ static void mnl_set_sndbuffer(const struct mnl_socket *nl,
return;
/* Rise sender buffer length to avoid hitting -EMSGSIZE */
+ setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUF,
+ &newbuffsiz, sizeof(socklen_t));
+
+ /* unpriviledged containers check for CAP_NET_ADMIN on the init_user_ns. */
if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE,
- &newbuffsiz, sizeof(socklen_t)) < 0)
- return;
+ &newbuffsiz, sizeof(socklen_t)) < 0) {
+ if (errno == EPERM)
+ ctx->maybe_emsgsize = newbuffsiz;
+ }
}
static unsigned int nlsndbufsiz;
@@ -408,7 +419,7 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
.nl_ctx = ctx,
};
- mnl_set_sndbuffer(ctx->nft->nf_sock, ctx->batch);
+ mnl_set_sndbuffer(ctx);
mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
@@ -464,7 +475,7 @@ static int mnl_nft_expr_build_cb(struct nftnl_expr *nle, void *data)
eloc = nft_expr_loc_find(nle, ctx->lctx);
if (eloc)
- cmd_add_loc(cmd, nlh->nlmsg_len, eloc->loc);
+ cmd_add_loc(cmd, nlh, eloc->loc);
nest = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
nftnl_expr_build_payload(nlh, nle);
@@ -517,9 +528,9 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ cmd_add_loc(cmd, nlh, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ cmd_add_loc(cmd, nlh, &h->chain.location);
if (h->chain_id)
mnl_attr_put_u32(nlh, NFTA_RULE_CHAIN_ID, htonl(h->chain_id));
@@ -568,11 +579,11 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
cmd->handle.family,
NLM_F_REPLACE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ cmd_add_loc(cmd, nlh, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ cmd_add_loc(cmd, nlh, &h->chain.location);
mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location);
+ cmd_add_loc(cmd, nlh, &h->handle.location);
mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id));
mnl_nft_rule_build_ctx_init(&rule_ctx, nlh, cmd, &lctx);
@@ -592,6 +603,7 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELRULE;
struct handle *h = &cmd->handle;
struct nftnl_rule *nlr;
struct nlmsghdr *nlh;
@@ -602,19 +614,22 @@ int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYRULE;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELRULE,
+ msg_type,
nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY),
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ cmd_add_loc(cmd, nlh, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
if (h->chain.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ cmd_add_loc(cmd, nlh, &h->chain.location);
mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
}
if (h->handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location);
+ cmd_add_loc(cmd, nlh, &h->handle.location);
mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id));
}
@@ -654,13 +669,21 @@ err_free:
}
struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
- const char *table, const char *chain)
+ const char *table, const char *chain,
+ uint64_t rule_handle,
+ bool dump, bool reset)
{
+ uint16_t nl_flags = dump ? NLM_F_DUMP : NLM_F_ACK;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_rule_list *nlr_list;
struct nftnl_rule *nlr = NULL;
struct nlmsghdr *nlh;
- int ret;
+ int msg_type, ret;
+
+ if (reset)
+ msg_type = NFT_MSG_GETRULE_RESET;
+ else
+ msg_type = NFT_MSG_GETRULE;
if (table) {
nlr = nftnl_rule_alloc();
@@ -670,14 +693,16 @@ struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
nftnl_rule_set_str(nlr, NFTNL_RULE_TABLE, table);
if (chain)
nftnl_rule_set_str(nlr, NFTNL_RULE_CHAIN, chain);
+ if (rule_handle)
+ nftnl_rule_set_u64(nlr, NFTNL_RULE_HANDLE, rule_handle);
}
nlr_list = nftnl_rule_list_alloc();
if (nlr_list == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family,
- NLM_F_DUMP, ctx->seqnum);
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type, family,
+ nl_flags, ctx->seqnum);
if (nlr) {
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
@@ -696,18 +721,109 @@ err:
/*
* Chain
*/
+
+struct nft_dev {
+ const char *ifname;
+ const struct location *location;
+};
+
+static void nft_dev_add(struct nft_dev *dev_array, const struct expr *expr, int i)
+{
+ unsigned int ifname_len;
+ char ifname[IFNAMSIZ];
+
+ if (expr->etype != EXPR_VALUE)
+ BUG("Must be a value, not %s\n", expr_name(expr));
+
+ ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
+ memset(ifname, 0, sizeof(ifname));
+
+ if (ifname_len > sizeof(ifname))
+ BUG("Interface length %u exceeds limit\n", ifname_len);
+
+ mpz_export_data(ifname, expr->value, BYTEORDER_HOST_ENDIAN, ifname_len);
+
+ if (strnlen(ifname, IFNAMSIZ) >= IFNAMSIZ)
+ BUG("Interface length %zu exceeds limit, no NUL byte\n", strnlen(ifname, IFNAMSIZ));
+
+ dev_array[i].ifname = xstrdup(ifname);
+ dev_array[i].location = &expr->location;
+}
+
+static struct nft_dev *nft_dev_array(const struct expr *dev_expr, int *num_devs)
+{
+ struct nft_dev *dev_array;
+ int i = 0, len = 1;
+ struct expr *expr;
+
+ switch (dev_expr->etype) {
+ case EXPR_SET:
+ case EXPR_LIST:
+ list_for_each_entry(expr, &dev_expr->expressions, list)
+ len++;
+
+ dev_array = xmalloc(sizeof(struct nft_dev) * len);
+
+ list_for_each_entry(expr, &dev_expr->expressions, list) {
+ nft_dev_add(dev_array, expr, i);
+ i++;
+ }
+ break;
+ case EXPR_VALUE:
+ len++;
+ dev_array = xmalloc(sizeof(struct nft_dev) * len);
+ nft_dev_add(dev_array, dev_expr, i);
+ i++;
+ break;
+ default:
+ assert(0);
+ }
+
+ dev_array[i].ifname = NULL;
+ *num_devs = i;
+
+ return dev_array;
+}
+
+static void nft_dev_array_free(const struct nft_dev *dev_array)
+{
+ int i = 0;
+
+ while (dev_array[i].ifname != NULL)
+ free_const(dev_array[i++].ifname);
+
+ free_const(dev_array);
+}
+
+static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
+{
+ const struct expr *dev_expr = cmd->chain->dev_expr;
+ const struct nft_dev *dev_array;
+ struct nlattr *nest_dev;
+ int i, num_devs = 0;
+
+ dev_array = nft_dev_array(dev_expr, &num_devs);
+ if (num_devs == 1) {
+ cmd_add_loc(cmd, nlh, dev_array[0].location);
+ mnl_attr_put_strz(nlh, NFTA_HOOK_DEV, dev_array[0].ifname);
+ } else {
+ nest_dev = mnl_attr_nest_start(nlh, NFTA_HOOK_DEVS);
+ for (i = 0; i < num_devs; i++) {
+ cmd_add_loc(cmd, nlh, dev_array[i].location);
+ mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
+ mnl_attr_nest_end(nlh, nest_dev);
+ }
+ }
+ nft_dev_array_free(dev_array);
+}
+
int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct nftnl_udata_buf *udbuf;
- int priority, policy, i = 0;
struct nftnl_chain *nlc;
- unsigned int ifname_len;
- const char **dev_array;
- char ifname[IFNAMSIZ];
struct nlmsghdr *nlh;
- struct expr *expr;
- int dev_array_len;
+ int priority, policy;
nlc = nftnl_chain_alloc();
if (nlc == NULL)
@@ -720,46 +836,6 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS,
CHAIN_F_HW_OFFLOAD);
}
- if (cmd->chain->flags & CHAIN_F_BASECHAIN) {
- nftnl_chain_set_u32(nlc, NFTNL_CHAIN_HOOKNUM,
- cmd->chain->hook.num);
- mpz_export_data(&priority,
- cmd->chain->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- nftnl_chain_set_s32(nlc, NFTNL_CHAIN_PRIO, priority);
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_TYPE,
- cmd->chain->type.str);
- }
- if (cmd->chain->dev_expr) {
- dev_array = xmalloc(sizeof(char *) * 8);
- dev_array_len = 8;
- list_for_each_entry(expr, &cmd->chain->dev_expr->expressions, list) {
- ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
- memset(ifname, 0, sizeof(ifname));
- mpz_export_data(ifname, expr->value,
- BYTEORDER_HOST_ENDIAN,
- ifname_len);
- dev_array[i++] = xstrdup(ifname);
- if (i == dev_array_len) {
- dev_array_len *= 2;
- dev_array = xrealloc(dev_array,
- dev_array_len * sizeof(char *));
- }
- }
-
- dev_array[i] = NULL;
- if (i == 1)
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_DEV, dev_array[0]);
- else if (i > 1)
- nftnl_chain_set_data(nlc, NFTNL_CHAIN_DEVICES, dev_array,
- sizeof(char *) * dev_array_len);
-
- i = 0;
- while (dev_array[i] != NULL)
- xfree(dev_array[i++]);
-
- xfree(dev_array);
- }
if (cmd->chain->comment) {
udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udbuf)
@@ -771,16 +847,24 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_udata_buf_free(udbuf);
}
}
+
+ nftnl_chain_set_str(nlc, NFTNL_CHAIN_TABLE, cmd->handle.table.name);
+ if (cmd->handle.chain.name)
+ nftnl_chain_set_str(nlc, NFTNL_CHAIN_NAME, cmd->handle.chain.name);
+
netlink_dump_chain(nlc, ctx);
+ nftnl_chain_unset(nlc, NFTNL_CHAIN_TABLE);
+ nftnl_chain_unset(nlc, NFTNL_CHAIN_NAME);
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWCHAIN,
cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &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);
+ cmd_add_loc(cmd, nlh, &cmd->handle.chain.location);
if (!cmd->chain || !(cmd->chain->flags & CHAIN_F_BINDING)) {
mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
@@ -794,20 +878,40 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS, cmd->chain->flags);
}
- if (cmd->chain && cmd->chain->flags & CHAIN_F_BASECHAIN) {
- nftnl_chain_unset(nlc, NFTNL_CHAIN_TYPE);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->type.loc);
- mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str);
- }
-
if (cmd->chain && cmd->chain->policy) {
mpz_export_data(&policy, cmd->chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->policy->location);
+ cmd_add_loc(cmd, nlh, &cmd->chain->policy->location);
mnl_attr_put_u32(nlh, NFTA_CHAIN_POLICY, htonl(policy));
}
+ nftnl_chain_unset(nlc, NFTNL_CHAIN_TYPE);
+
nftnl_chain_nlmsg_build_payload(nlh, nlc);
+
+ if (cmd->chain && cmd->chain->flags & CHAIN_F_BASECHAIN) {
+ struct nlattr *nest;
+
+ if (cmd->chain->type.str) {
+ cmd_add_loc(cmd, nlh, &cmd->chain->type.loc);
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str);
+ }
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK);
+
+ if (cmd->chain->type.str) {
+ mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(cmd->chain->hook.num));
+ mpz_export_data(&priority, cmd->chain->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ mnl_attr_put_u32(nlh, NFTA_HOOK_PRIORITY, htonl(priority));
+ }
+
+ if (cmd->chain && cmd->chain->dev_expr)
+ mnl_nft_chain_devs_build(nlh, cmd);
+
+ mnl_attr_nest_end(nlh, nest);
+ }
+
nftnl_chain_free(nlc);
mnl_nft_batch_continue(ctx->batch);
@@ -847,6 +951,7 @@ int mnl_nft_chain_rename(struct netlink_ctx *ctx, const struct cmd *cmd,
int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELCHAIN;
struct nftnl_chain *nlc;
struct nlmsghdr *nlh;
@@ -856,22 +961,34 @@ int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FAMILY, cmd->handle.family);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYCHAIN;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELCHAIN,
+ msg_type,
cmd->handle.family,
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name);
if (cmd->handle.chain.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.chain.location);
mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
} else if (cmd->handle.handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_CHAIN_HANDLE,
htobe64(cmd->handle.handle.id));
}
+ if (cmd->op == CMD_DELETE &&
+ cmd->chain && cmd->chain->dev_expr) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK);
+ mnl_nft_chain_devs_build(nlh, cmd);
+ mnl_attr_nest_end(nlh, nest);
+ }
+
nftnl_chain_nlmsg_build_payload(nlh, nlc);
nftnl_chain_free(nlc);
@@ -980,7 +1097,7 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd->handle.family,
flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, cmd->handle.table.name);
nftnl_table_nlmsg_build_payload(nlh, nlt);
nftnl_table_free(nlt);
@@ -992,6 +1109,7 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELTABLE;
struct nftnl_table *nlt;
struct nlmsghdr *nlh;
@@ -1001,16 +1119,17 @@ int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family);
- nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELTABLE,
- cmd->handle.family,
- 0, ctx->seqnum);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYTABLE;
+
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), msg_type,
+ cmd->handle.family, 0, ctx->seqnum);
if (cmd->handle.table.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, cmd->handle.table.name);
} else if (cmd->handle.handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_TABLE_HANDLE,
htobe64(cmd->handle.handle.id));
}
@@ -1064,8 +1183,11 @@ struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
if (!nlt)
memory_allocation_error();
- nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, family);
- nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, table);
+ if (family != NFPROTO_UNSPEC)
+ nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, family);
+ if (table)
+ nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, table);
+
flags = NLM_F_ACK;
}
@@ -1155,8 +1277,6 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
if (set->desc.size != 0)
nftnl_set_set_u32(nls, NFTNL_SET_DESC_SIZE,
set->desc.size);
- } else if (set->init) {
- nftnl_set_set_u32(nls, NFTNL_SET_DESC_SIZE, set->init->size);
}
udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
@@ -1223,9 +1343,9 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
h->family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ cmd_add_loc(cmd, nlh, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_SET_TABLE, h->table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->set.location);
+ cmd_add_loc(cmd, nlh, &h->set.location);
mnl_attr_put_strz(nlh, NFTA_SET_NAME, h->set.name);
nftnl_set_nlmsg_build_payload(nlh, nls);
@@ -1238,6 +1358,7 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSET;
const struct handle *h = &cmd->handle;
struct nftnl_set *nls;
struct nlmsghdr *nlh;
@@ -1248,18 +1369,21 @@ int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYSET;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELSET,
+ msg_type,
h->family,
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_SET_TABLE, cmd->handle.table.name);
if (h->set.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.set.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.set.location);
mnl_attr_put_strz(nlh, NFTA_SET_NAME, cmd->handle.set.name);
} else if (h->handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_SET_HANDLE,
htobe64(cmd->handle.handle.id));
}
@@ -1272,9 +1396,15 @@ int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
return 0;
}
+struct set_cb_args {
+ struct netlink_ctx *ctx;
+ struct nftnl_set_list *list;
+};
+
static int set_cb(const struct nlmsghdr *nlh, void *data)
{
- struct nftnl_set_list *nls_list = data;
+ struct set_cb_args *args = data;
+ struct nftnl_set_list *nls_list = args->list;
struct nftnl_set *s;
if (check_genid(nlh) < 0)
@@ -1287,6 +1417,8 @@ static int set_cb(const struct nlmsghdr *nlh, void *data)
if (nftnl_set_nlmsg_parse(nlh, s) < 0)
goto err_free;
+ netlink_dump_set(s, args->ctx);
+
nftnl_set_list_add_tail(s, nls_list);
return MNL_CB_OK;
@@ -1305,6 +1437,7 @@ mnl_nft_set_dump(struct netlink_ctx *ctx, int family,
struct nlmsghdr *nlh;
struct nftnl_set *s;
int ret;
+ struct set_cb_args args;
s = nftnl_set_alloc();
if (s == NULL)
@@ -1326,7 +1459,9 @@ mnl_nft_set_dump(struct netlink_ctx *ctx, int family,
if (nls_list == NULL)
memory_allocation_error();
- ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_cb, nls_list);
+ args.list = nls_list;
+ args.ctx = ctx;
+ ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_cb, &args);
if (ret < 0 && errno != ENOENT)
goto err;
@@ -1438,9 +1573,9 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
NFT_MSG_NEWOBJ, cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, cmd->handle.table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.obj.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.obj.location);
mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, cmd->handle.obj.name);
nftnl_obj_nlmsg_build_payload(nlh, nlo);
@@ -1453,6 +1588,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELOBJ;
struct nftnl_obj *nlo;
struct nlmsghdr *nlh;
@@ -1463,18 +1599,21 @@ int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type)
nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family);
nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, type);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYOBJ;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELOBJ, cmd->handle.family,
+ msg_type, cmd->handle.family,
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, cmd->handle.table.name);
if (cmd->handle.obj.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.obj.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.obj.location);
mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, cmd->handle.obj.name);
} else if (cmd->handle.handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_OBJ_HANDLE,
htobe64(cmd->handle.handle.id));
}
@@ -1609,29 +1748,58 @@ static void netlink_dump_setelem_done(struct netlink_ctx *ctx)
fprintf(fp, "\n");
}
+static struct nftnl_set_elem *
+alloc_nftnl_setelem_interval(const struct set *set, const struct expr *init,
+ struct expr *elem, struct expr *next_elem,
+ struct nftnl_set_elem **nlse_high)
+{
+ struct nftnl_set_elem *nlse[2] = {};
+ LIST_HEAD(interval_list);
+ struct expr *expr, *next;
+ int i = 0;
+
+ if (setelem_to_interval(set, elem, next_elem, &interval_list) < 0)
+ memory_allocation_error();
+
+ if (list_empty(&interval_list)) {
+ *nlse_high = NULL;
+ nlse[i++] = alloc_nftnl_setelem(init, elem);
+ return nlse[0];
+ }
+
+ list_for_each_entry_safe(expr, next, &interval_list, list) {
+ nlse[i++] = alloc_nftnl_setelem(init, expr);
+ list_del(&expr->list);
+ expr_free(expr);
+ }
+ *nlse_high = nlse[1];
+
+ return nlse[0];
+}
+
static int mnl_nft_setelem_batch(const struct nftnl_set *nls, struct cmd *cmd,
struct nftnl_batch *batch,
enum nf_tables_msg_types msg_type,
- unsigned int flags, uint32_t seqnum,
- const struct expr *set,
+ unsigned int flags, uint32_t *seqnum,
+ const struct set *set, const struct expr *init,
struct netlink_ctx *ctx)
{
+ struct nftnl_set_elem *nlse, *nlse_high = NULL;
+ struct expr *expr = NULL, *next;
struct nlattr *nest1, *nest2;
- struct nftnl_set_elem *nlse;
struct nlmsghdr *nlh;
- struct expr *expr = NULL;
int i = 0;
if (msg_type == NFT_MSG_NEWSETELEM)
flags |= NLM_F_CREATE;
- if (set)
- expr = list_first_entry(&set->expressions, struct expr, list);
+ if (init)
+ expr = list_first_entry(&init->expressions, struct expr, list);
next:
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), msg_type,
nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
- flags, seqnum);
+ flags, *seqnum);
if (nftnl_set_is_set(nls, NFTNL_SET_TABLE)) {
mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_TABLE,
@@ -1646,15 +1814,36 @@ next:
htonl(nftnl_set_get_u32(nls, NFTNL_SET_ID)));
}
- if (!set || list_empty(&set->expressions))
+ if (!init || list_empty(&init->expressions))
return 0;
assert(expr);
nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_LIST_ELEMENTS);
- list_for_each_entry_from(expr, &set->expressions, list) {
- nlse = alloc_nftnl_setelem(set, expr);
+ list_for_each_entry_from(expr, &init->expressions, list) {
+
+ if (set_is_non_concat_range(set)) {
+ if (set_is_anonymous(set->flags) &&
+ !list_is_last(&expr->list, &init->expressions))
+ next = list_next_entry(expr, list);
+ else
+ next = NULL;
+
+ if (!nlse_high) {
+ nlse = alloc_nftnl_setelem_interval(set, init, expr, next, &nlse_high);
+ } else {
+ nlse = nlse_high;
+ nlse_high = NULL;
+ }
+ } else {
+ nlse = alloc_nftnl_setelem(init, expr);
+ }
+
+ cmd_add_loc(cmd, nlh, &expr->location);
+
+ /* remain with this element, range high still needs to be added. */
+ if (nlse_high)
+ expr = list_prev_entry(expr, list);
- cmd_add_loc(cmd, nlh->nlmsg_len, &expr->location);
nest2 = mnl_attr_nest_start(nlh, ++i);
nftnl_set_elem_nlmsg_build_payload(nlh, nlse);
mnl_attr_nest_end(nlh, nest2);
@@ -1662,8 +1851,13 @@ next:
netlink_dump_setelem(nlse, ctx);
nftnl_set_elem_free(nlse);
if (mnl_nft_attr_nest_overflow(nlh, nest1, nest2)) {
+ if (nlse_high) {
+ nftnl_set_elem_free(nlse_high);
+ nlse_high = NULL;
+ }
mnl_attr_nest_end(nlh, nest1);
mnl_nft_batch_continue(batch);
+ mnl_seqnum_inc(seqnum);
goto next;
}
}
@@ -1698,7 +1892,7 @@ int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
netlink_dump_set(nls, ctx);
err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_NEWSETELEM,
- flags, ctx->seqnum, expr, ctx);
+ flags, &ctx->seqnum, set, expr, ctx);
nftnl_set_free(nls);
return err;
@@ -1735,8 +1929,10 @@ int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd)
}
int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
- const struct handle *h, const struct expr *init)
+ const struct handle *h, const struct set *set,
+ const struct expr *init)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSETELEM;
struct nftnl_set *nls;
int err;
@@ -1753,22 +1949,32 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_DELSETELEM, 0,
- ctx->seqnum, init, ctx);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYSETELEM;
+
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, msg_type, 0,
+ &ctx->seqnum, set, init, ctx);
nftnl_set_free(nls);
return err;
}
struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx,
- struct nftnl_set *nls_in)
+ struct nftnl_set *nls_in,
+ bool reset)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_set *nls_out;
struct nlmsghdr *nlh;
+ int msg_type;
int err;
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM,
+ if (reset)
+ msg_type = NFT_MSG_GETSETELEM_RESET;
+ else
+ msg_type = NFT_MSG_GETSETELEM;
+
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type,
nftnl_set_get_u32(nls_in, NFTNL_SET_FAMILY),
NLM_F_ACK, ctx->seqnum);
nftnl_set_elems_nlmsg_build_payload(nlh, nls_in);
@@ -1791,12 +1997,19 @@ struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx,
return nls_out;
}
-int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls)
+int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls,
+ bool reset)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
+ int msg_type;
+
+ if (reset)
+ msg_type = NFT_MSG_GETSETELEM_RESET;
+ else
+ msg_type = NFT_MSG_GETSETELEM;
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM,
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type,
nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
NLM_F_DUMP, ctx->seqnum);
nftnl_set_elems_nlmsg_build_payload(nlh, nls);
@@ -1867,48 +2080,30 @@ err:
return NULL;
}
-static const char **nft_flowtable_dev_array(struct cmd *cmd)
+static void mnl_nft_ft_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
{
- unsigned int ifname_len;
- const char **dev_array;
- char ifname[IFNAMSIZ];
- int i = 0, len = 1;
- struct expr *expr;
-
- list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list)
- len++;
-
- dev_array = xmalloc(sizeof(char *) * len);
+ const struct expr *dev_expr = cmd->flowtable->dev_expr;
+ const struct nft_dev *dev_array;
+ struct nlattr *nest_dev;
+ int i, num_devs= 0;
- list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list) {
- ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
- memset(ifname, 0, sizeof(ifname));
- mpz_export_data(ifname, expr->value, BYTEORDER_HOST_ENDIAN,
- ifname_len);
- dev_array[i++] = xstrdup(ifname);
+ dev_array = nft_dev_array(dev_expr, &num_devs);
+ nest_dev = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK_DEVS);
+ for (i = 0; i < num_devs; i++) {
+ cmd_add_loc(cmd, nlh, dev_array[i].location);
+ mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
}
- dev_array[i] = NULL;
-
- 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);
+ mnl_attr_nest_end(nlh, nest_dev);
+ nft_dev_array_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;
+ struct nlattr *nest;
int priority;
flo = nftnl_flowtable_alloc();
@@ -1918,24 +2113,6 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
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);
@@ -1945,12 +2122,27 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
NFT_MSG_NEWFLOWTABLE, cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, cmd->handle.table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.flowtable.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.flowtable.location);
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, cmd->handle.flowtable.name);
nftnl_flowtable_nlmsg_build_payload(nlh, flo);
+
+ nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK);
+
+ if (cmd->flowtable && cmd->flowtable->priority.expr) {
+ mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_NUM, htonl(cmd->flowtable->hook.num));
+ mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(priority));
+ }
+
+ if (cmd->flowtable->dev_expr)
+ mnl_nft_ft_devs_build(nlh, cmd);
+
+ mnl_attr_nest_end(nlh, nest);
+
nftnl_flowtable_free(flo);
mnl_nft_batch_continue(ctx->batch);
@@ -1960,9 +2152,10 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELFLOWTABLE;
struct nftnl_flowtable *flo;
- const char **dev_array;
struct nlmsghdr *nlh;
+ struct nlattr *nest;
flo = nftnl_flowtable_alloc();
if (!flo)
@@ -1971,35 +2164,35 @@ 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);
- }
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYFLOWTABLE;
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELFLOWTABLE, cmd->handle.family,
+ msg_type, cmd->handle.family,
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, cmd->handle.table.name);
if (cmd->handle.flowtable.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len,
- &cmd->handle.flowtable.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.flowtable.location);
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME,
cmd->handle.flowtable.name);
} else if (cmd->handle.handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_FLOWTABLE_HANDLE,
htobe64(cmd->handle.handle.id));
}
nftnl_flowtable_nlmsg_build_payload(nlh, flo);
+
+ if (cmd->op == CMD_DELETE &&
+ cmd->flowtable && cmd->flowtable->dev_expr) {
+ nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK);
+ mnl_nft_ft_devs_build(nlh, cmd);
+ mnl_attr_nest_end(nlh, nest);
+ }
+
nftnl_flowtable_free(flo);
mnl_nft_batch_continue(ctx->batch);
@@ -2071,11 +2264,26 @@ static struct basehook *basehook_alloc(void)
static void basehook_free(struct basehook *b)
{
list_del(&b->list);
- xfree(b->module_name);
- xfree(b->hookfn);
- xfree(b->chain);
- xfree(b->table);
- xfree(b);
+ free_const(b->module_name);
+ free_const(b->hookfn);
+ free_const(b->chain);
+ free_const(b->table);
+ free_const(b->devname);
+ free(b);
+}
+
+static bool basehook_eq(const struct basehook *prev, const struct basehook *hook)
+{
+ if (prev->num != hook->num)
+ return false;
+
+ if (prev->devname != NULL && hook->devname != NULL)
+ return strcmp(prev->devname, hook->devname) == 0;
+
+ if (prev->devname == NULL && hook->devname == NULL)
+ return true;
+
+ return false;
}
static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
@@ -2085,7 +2293,7 @@ static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
list_for_each_entry(hook, head, list) {
if (hook->family != b->family)
continue;
- if (hook->num != b->num)
+ if (!basehook_eq(hook, b))
continue;
if (hook->prio < b->prio)
continue;
@@ -2183,8 +2391,30 @@ static int dump_nf_attr_chain_cb(const struct nlattr *attr, void *data)
return MNL_CB_OK;
}
+static int dump_nf_attr_bpf_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFNLA_HOOK_BPF_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFNLA_HOOK_BPF_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ default:
+ return MNL_CB_OK;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
struct dump_nf_hook_data {
struct list_head *hook_list;
+ const char *devname;
int family;
};
@@ -2206,6 +2436,7 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
hook = basehook_alloc();
hook->prio = ntohl(mnl_attr_get_u32(tb[NFNLA_HOOK_PRIORITY]));
+ hook->devname = data->devname ? xstrdup(data->devname) : NULL;
if (tb[NFNLA_HOOK_FUNCTION_NAME])
hook->hookfn = xstrdup(mnl_attr_get_str(tb[NFNLA_HOOK_FUNCTION_NAME]));
@@ -2217,16 +2448,23 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
struct nlattr *nested[NFNLA_HOOK_INFO_MAX + 1] = {};
uint32_t type;
- if (mnl_attr_parse_nested(tb[NFNLA_HOOK_CHAIN_INFO], dump_nf_chain_info_cb, nested) < 0)
+ if (mnl_attr_parse_nested(tb[NFNLA_HOOK_CHAIN_INFO],
+ dump_nf_chain_info_cb, nested) < 0) {
+ basehook_free(hook);
return -1;
+ }
type = ntohl(mnl_attr_get_u32(nested[NFNLA_HOOK_INFO_TYPE]));
if (type == NFNL_HOOK_TYPE_NFTABLES) {
struct nlattr *info[NFNLA_CHAIN_MAX + 1] = {};
const char *tablename, *chainname;
- if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC], dump_nf_attr_chain_cb, info) < 0)
+ if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC],
+ dump_nf_attr_chain_cb,
+ info) < 0) {
+ basehook_free(hook);
return -1;
+ }
tablename = mnl_attr_get_str(info[NFNLA_CHAIN_TABLE]);
chainname = mnl_attr_get_str(info[NFNLA_CHAIN_NAME]);
@@ -2235,6 +2473,23 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
hook->chain = xstrdup(chainname);
}
hook->chain_family = mnl_attr_get_u8(info[NFNLA_CHAIN_FAMILY]);
+ } else if (type == NFNL_HOOK_TYPE_BPF) {
+ struct nlattr *info[NFNLA_HOOK_BPF_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC],
+ dump_nf_attr_bpf_cb, info) < 0) {
+ basehook_free(hook);
+ return -1;
+ }
+
+ if (info[NFNLA_HOOK_BPF_ID]) {
+ char tmpbuf[16];
+
+ snprintf(tmpbuf, sizeof(tmpbuf), "id %u",
+ ntohl(mnl_attr_get_u32(info[NFNLA_HOOK_BPF_ID])));
+
+ hook->chain = xstrdup(tmpbuf);
+ }
}
}
if (tb[NFNLA_HOOK_HOOKNUM])
@@ -2242,25 +2497,6 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
hook->family = nfg->nfgen_family;
- /* Netdev hooks potentially interfer with this family datapath. */
- if (hook->family == NFPROTO_NETDEV) {
- switch (data->family) {
- case NFPROTO_IPV4:
- case NFPROTO_IPV6:
- case NFPROTO_INET:
- case NFPROTO_BRIDGE:
- hook->family = data->family;
- hook->num = NF_INET_INGRESS;
- break;
- case NFPROTO_ARP:
- if (hook->chain_family == NFPROTO_NETDEV) {
- hook->family = data->family;
- hook->num = __NF_ARP_INGRESS;
- }
- break;
- }
- }
-
basehook_list_add_tail(hook, data->hook_list);
return MNL_CB_OK;
@@ -2290,6 +2526,7 @@ static int __mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, uint8_t query_family
char buf[MNL_SOCKET_BUFFER_SIZE];
struct dump_nf_hook_data data = {
.hook_list = hook_list,
+ .devname = devname,
.family = query_family,
};
struct nlmsghdr *nlh;
@@ -2329,7 +2566,7 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
continue;
if (prev) {
- if (prev->num == hook->num) {
+ if (basehook_eq(prev, hook)) {
fprintf(fp, "\n");
same = true;
} else {
@@ -2342,8 +2579,12 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
prev = hook;
if (!same) {
- fprintf(fp, "\thook %s {\n",
- hooknum2str(family, hook->num));
+ if (hook->devname)
+ fprintf(fp, "\thook %s device %s {\n",
+ hooknum2str(family, hook->num), hook->devname);
+ else
+ fprintf(fp, "\thook %s {\n",
+ hooknum2str(family, hook->num));
}
prio = hook->prio;
@@ -2356,6 +2597,8 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
if (hook->table && hook->chain)
fprintf(fp, " chain %s %s %s", family2str(hook->chain_family), hook->table, hook->chain);
+ else if (hook->hookfn && hook->chain)
+ fprintf(fp, " %s %s", hook->hookfn, hook->chain);
else if (hook->hookfn) {
fprintf(fp, " %s", hook->hookfn);
}
@@ -2367,90 +2610,42 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
fprintf(fp, "}\n");
}
-#define HOOK_FAMILY_MAX 5
-
-static uint8_t hook_family[HOOK_FAMILY_MAX] = {
- NFPROTO_IPV4,
- NFPROTO_IPV6,
- NFPROTO_BRIDGE,
- NFPROTO_ARP,
-};
-
-static int mnl_nft_dump_nf(struct netlink_ctx *ctx, int family, int hook,
- const char *devname, struct list_head *hook_list,
- int *ret)
+static int mnl_nft_dump_nf(struct netlink_ctx *ctx, int family,
+ const char *devname, struct list_head *hook_list)
{
int i, err;
- /* show ingress in first place in hook listing. */
- err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
- if (err < 0)
- *ret = err;
-
for (i = 0; i <= NF_INET_POST_ROUTING; i++) {
- err = __mnl_nft_dump_nf_hooks(ctx, family, family, i, devname, hook_list);
- if (err < 0)
- *ret = err;
- }
-
- return err;
-}
-
-static int mnl_nft_dump_nf_arp(struct netlink_ctx *ctx, int family, int hook,
- const char *devname, struct list_head *hook_list,
- int *ret)
-{
- int err;
+ int tmp;
- /* show ingress in first place in hook listing. */
- err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
- if (err < 0)
- *ret = err;
-
- err = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_IN, devname, hook_list);
- if (err < 0)
- *ret = err;
- err = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_OUT, devname, hook_list);
- if (err < 0)
- *ret = err;
+ tmp = __mnl_nft_dump_nf_hooks(ctx, family, family, i, devname, hook_list);
+ if (tmp == 0)
+ err = 0;
+ }
return err;
}
-static int mnl_nft_dump_nf_netdev(struct netlink_ctx *ctx, int family, int hook,
- const char *devname, struct list_head *hook_list,
- int *ret)
+static int mnl_nft_dump_nf_arp(struct netlink_ctx *ctx, int family,
+ const char *devname, struct list_head *hook_list)
{
- int err;
+ int err1, err2;
- err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
- if (err < 0)
- *ret = err;
+ err1 = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_IN, devname, hook_list);
+ err2 = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_OUT, devname, hook_list);
- return err;
+ return err1 ? err2 : err1;
}
-static int mnl_nft_dump_nf_decnet(struct netlink_ctx *ctx, int family, int hook,
- const char *devname, struct list_head *hook_list,
- int *ret)
+static int mnl_nft_dump_nf_netdev(struct netlink_ctx *ctx, int family,
+ const char *devname, struct list_head *hook_list)
{
- int i, err;
+ int err1, err2;
- /* show ingress in first place in hook listing. */
- err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
- if (err < 0)
- *ret = err;
-
-#define NF_DN_NUMHOOKS 7
- for (i = 0; i < NF_DN_NUMHOOKS; i++) {
- err = __mnl_nft_dump_nf_hooks(ctx, family, family, i, devname, hook_list);
- if (err < 0) {
- *ret = err;
- return err;
- }
- }
+ err1 = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
+ err2 = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_EGRESS, devname, hook_list);
- return err;
+ return err1 ? err2 : err1;
}
static void release_hook_list(struct list_head *hook_list)
@@ -2461,58 +2656,80 @@ static void release_hook_list(struct list_head *hook_list)
basehook_free(hook);
}
-int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, int hook, const char *devname)
+static void warn_if_device(struct nft_ctx *nft, const char *devname)
+{
+ if (devname)
+ nft_print(&nft->output, "# device keyword (%s) unexpected for this family\n", devname);
+}
+
+int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, const char *devname)
{
LIST_HEAD(hook_list);
- unsigned int i;
- int ret;
+ int ret = -1, tmp;
errno = 0;
- ret = 0;
switch (family) {
case NFPROTO_UNSPEC:
- mnl_nft_dump_nf(ctx, NFPROTO_IPV4, hook, devname, &hook_list, &ret);
- mnl_nft_dump_nf(ctx, NFPROTO_IPV6, hook, devname, &hook_list, &ret);
- mnl_nft_dump_nf(ctx, NFPROTO_BRIDGE, hook, devname, &hook_list, &ret);
- mnl_nft_dump_nf_decnet(ctx, NFPROTO_DECNET, hook, devname, &hook_list, &ret);
- break;
+ ret = mnl_nft_dump_nf_hooks(ctx, NFPROTO_ARP, NULL);
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_INET, NULL);
+ if (tmp == 0)
+ ret = 0;
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_BRIDGE, NULL);
+ if (tmp == 0)
+ ret = 0;
+
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_NETDEV, devname);
+ if (tmp == 0)
+ ret = 0;
+
+ return ret;
case NFPROTO_INET:
- mnl_nft_dump_nf(ctx, NFPROTO_IPV4, hook, devname, &hook_list, &ret);
- mnl_nft_dump_nf(ctx, NFPROTO_IPV6, hook, devname, &hook_list, &ret);
+ ret = 0;
+ if (devname)
+ ret = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV,
+ NF_NETDEV_INGRESS, devname, &hook_list);
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_IPV4, NULL);
+ if (tmp == 0)
+ ret = 0;
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_IPV6, NULL);
+ if (tmp == 0)
+ ret = 0;
+
break;
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_BRIDGE:
- mnl_nft_dump_nf(ctx, family, hook, devname, &hook_list, &ret);
+ warn_if_device(ctx->nft, devname);
+ ret = mnl_nft_dump_nf(ctx, family, devname, &hook_list);
break;
case NFPROTO_ARP:
- mnl_nft_dump_nf_arp(ctx, family, hook, devname, &hook_list, &ret);
+ warn_if_device(ctx->nft, devname);
+ ret = mnl_nft_dump_nf_arp(ctx, family, devname, &hook_list);
break;
case NFPROTO_NETDEV:
- mnl_nft_dump_nf_netdev(ctx, family, hook, devname, &hook_list, &ret);
- break;
- case NFPROTO_DECNET:
- mnl_nft_dump_nf_decnet(ctx, family, hook, devname, &hook_list, &ret);
- break;
- }
+ if (devname) {
+ ret = mnl_nft_dump_nf_netdev(ctx, family, devname, &hook_list);
+ } else {
+ const struct iface *iface;
+
+ iface = iface_cache_get_next_entry(NULL);
+ ret = 0;
+
+ while (iface) {
+ tmp = mnl_nft_dump_nf_netdev(ctx, family, iface->name, &hook_list);
+ if (tmp == 0)
+ ret = 0;
+
+ iface = iface_cache_get_next_entry(iface);
+ }
+ }
- switch (family) {
- case NFPROTO_UNSPEC:
- for (i = 0; i < HOOK_FAMILY_MAX; i++)
- print_hooks(ctx, hook_family[i], &hook_list);
- break;
- case NFPROTO_INET:
- print_hooks(ctx, NFPROTO_IPV4, &hook_list);
- print_hooks(ctx, NFPROTO_IPV6, &hook_list);
- break;
- default:
- print_hooks(ctx, family, &hook_list);
break;
}
+ print_hooks(ctx, family, &hook_list);
release_hook_list(&hook_list);
- ret = 0;
return ret;
}
diff --git a/src/monitor.c b/src/monitor.c
index 7fa92ebf..e3e38c2a 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -2,17 +2,17 @@
* Copyright (c) 2015 Arturo Borrero Gonzalez <arturo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
-#include <string.h>
+#include <nft.h>
+
#include <fcntl.h>
#include <errno.h>
#include <libmnl/libmnl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <stdlib.h>
#include <inttypes.h>
#include <libnftnl/table.h>
@@ -127,6 +127,19 @@ struct nftnl_obj *netlink_obj_alloc(const struct nlmsghdr *nlh)
return nlo;
}
+struct nftnl_flowtable *netlink_flowtable_alloc(const struct nlmsghdr *nlh)
+{
+ struct nftnl_flowtable *nlf;
+
+ nlf = nftnl_flowtable_alloc();
+ if (nlf == NULL)
+ memory_allocation_error();
+ if (nftnl_flowtable_nlmsg_parse(nlh, nlf) < 0)
+ netlink_abi_error();
+
+ return nlf;
+}
+
static uint32_t netlink_msg2nftnl_of(uint32_t type, uint16_t flags)
{
switch (type) {
@@ -272,10 +285,13 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type,
chain_print_plain(c, &monh->ctx->nft->output);
break;
case NFT_MSG_DELCHAIN:
- nft_mon_print(monh, "chain %s %s %s",
- family2str(c->handle.family),
- c->handle.table.name,
- c->handle.chain.name);
+ if (c->dev_array_len > 0)
+ chain_print_plain(c, &monh->ctx->nft->output);
+ else
+ nft_mon_print(monh, "chain %s %s %s",
+ family2str(c->handle.family),
+ c->handle.table.name,
+ c->handle.chain.name);
break;
}
nft_mon_print(monh, "\n");
@@ -360,7 +376,7 @@ static bool set_elem_is_open_interval(struct expr *elem)
{
switch (elem->etype) {
case EXPR_SET_ELEM:
- return elem->elem_flags & NFTNL_SET_ELEM_F_INTERVAL_OPEN;
+ return elem->flags & EXPR_F_INTERVAL_OPEN;
case EXPR_MAPPING:
return set_elem_is_open_interval(elem->left);
default:
@@ -387,13 +403,19 @@ static bool netlink_event_range_cache(struct set *cached_set,
/* don't cache half-open range elements */
elem = list_entry(dummyset->init->expressions.prev, struct expr, list);
- if (!set_elem_is_open_interval(elem)) {
+ if (!set_elem_is_open_interval(elem) &&
+ dummyset->desc.field_count <= 1) {
cached_set->rg_cache = expr_clone(elem);
return true;
}
out_decompose:
- interval_map_decompose(dummyset->init);
+ if (dummyset->flags & NFT_SET_INTERVAL &&
+ dummyset->desc.field_count > 1)
+ concat_range_aggregate(dummyset->init);
+ else
+ interval_map_decompose(dummyset->init);
+
return false;
}
@@ -428,11 +450,13 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
* used by named sets, so use a dummy set.
*/
dummyset = set_alloc(monh->loc);
+ handle_merge(&dummyset->handle, &set->handle);
dummyset->key = expr_clone(set->key);
if (set->data)
dummyset->data = expr_clone(set->data);
dummyset->flags = set->flags;
dummyset->init = set_expr_alloc(monh->loc, set);
+ dummyset->desc.field_count = set->desc.field_count;
nlsei = nftnl_set_elems_iter_create(nls);
if (nlsei == NULL)
@@ -445,8 +469,8 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
nftnl_set_elems_iter_destroy(nlsei);
goto out;
}
- if (netlink_delinearize_setelem(nlse, dummyset,
- &monh->ctx->nft->cache) < 0) {
+ if (netlink_delinearize_setelem(monh->ctx,
+ nlse, dummyset) < 0) {
set_free(dummyset);
nftnl_set_elems_iter_destroy(nlsei);
goto out;
@@ -531,6 +555,54 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
return MNL_CB_OK;
}
+static int netlink_events_flowtable_cb(const struct nlmsghdr *nlh, int type,
+ struct netlink_mon_handler *monh)
+{
+ const char *family, *cmd;
+ struct nftnl_flowtable *nlf;
+ struct flowtable *ft;
+
+ nlf = netlink_flowtable_alloc(nlh);
+
+ ft = netlink_delinearize_flowtable(monh->ctx, nlf);
+ if (!ft) {
+ nftnl_flowtable_free(nlf);
+ return MNL_CB_ERROR;
+ }
+ family = family2str(ft->handle.family);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
+
+ switch (monh->format) {
+ case NFTNL_OUTPUT_DEFAULT:
+ nft_mon_print(monh, "%s ", cmd);
+
+ switch (type) {
+ case NFT_MSG_DELFLOWTABLE:
+ if (!ft->dev_array_len) {
+ nft_mon_print(monh, "flowtable %s %s %s",
+ family,
+ ft->handle.table.name,
+ ft->handle.flowtable.name);
+ break;
+ }
+ /* fall through */
+ case NFT_MSG_NEWFLOWTABLE:
+ flowtable_print_plain(ft, &monh->ctx->nft->output);
+ break;
+ }
+ nft_mon_print(monh, "\n");
+ break;
+ case NFTNL_OUTPUT_JSON:
+ monitor_print_flowtable_json(monh, cmd, ft);
+ if (!nft_output_echo(&monh->ctx->nft->output))
+ nft_mon_print(monh, "\n");
+ break;
+ }
+ flowtable_free(ft);
+ nftnl_flowtable_free(nlf);
+ return MNL_CB_OK;
+}
+
static void rule_map_decompose_cb(struct set *s, void *data)
{
if (!set_is_anonymous(s->flags))
@@ -551,6 +623,10 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
nlr = netlink_rule_alloc(nlh);
r = netlink_delinearize_rule(monh->ctx, nlr);
+ if (!r) {
+ fprintf(stderr, "W: Received event for an unknown table.\n");
+ goto out_free_nlr;
+ }
nlr_for_each_set(nlr, rule_map_decompose_cb, NULL,
&monh->ctx->nft->cache);
cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
@@ -587,6 +663,7 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
break;
}
rule_free(r);
+out_free_nlr:
nftnl_rule_free(nlr);
return MNL_CB_OK;
}
@@ -638,6 +715,7 @@ static void netlink_events_cache_addset(struct netlink_mon_handler *monh,
memset(&set_tmpctx, 0, sizeof(set_tmpctx));
init_list_head(&set_tmpctx.list);
init_list_head(&msgs);
+ set_tmpctx.nft = monh->ctx->nft;
set_tmpctx.msgs = &msgs;
nls = netlink_set_alloc(nlh);
@@ -697,8 +775,7 @@ static void netlink_events_cache_addsetelem(struct netlink_mon_handler *monh,
nlse = nftnl_set_elems_iter_next(nlsei);
while (nlse != NULL) {
- if (netlink_delinearize_setelem(nlse, set,
- &monh->ctx->nft->cache) < 0) {
+ if (netlink_delinearize_setelem(monh->ctx, nlse, set) < 0) {
fprintf(stderr,
"W: Unable to cache set_elem. "
"Delinearize failed.\n");
@@ -945,6 +1022,10 @@ static int netlink_events_cb(const struct nlmsghdr *nlh, void *data)
case NFT_MSG_DELOBJ:
ret = netlink_events_obj_cb(nlh, type, monh);
break;
+ case NFT_MSG_NEWFLOWTABLE:
+ case NFT_MSG_DELFLOWTABLE:
+ ret = netlink_events_flowtable_cb(nlh, type, monh);
+ break;
case NFT_MSG_NEWGEN:
ret = netlink_events_newgen_cb(nlh, type, monh);
break;
diff --git a/src/netlink.c b/src/netlink.c
index 799cf9b8..f3157d9d 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -9,12 +9,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <string.h>
+#include <nft.h>
+
#include <errno.h>
#include <libmnl/libmnl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <stdlib.h>
#include <inttypes.h>
#include <libnftnl/table.h>
@@ -96,9 +96,10 @@ struct nftnl_expr *alloc_nft_expr(const char *name)
return nle;
}
-
-void __netlink_gen_data(const struct expr *expr,
- struct nft_data_linearize *data, bool expand);
+static void netlink_gen_key(const struct expr *expr,
+ struct nft_data_linearize *data);
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand);
struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
const struct expr *expr)
@@ -133,23 +134,35 @@ struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
case EXPR_SET_ELEM_CATCHALL:
break;
default:
- __netlink_gen_data(key, &nld, false);
- nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
if (set->set_flags & NFT_SET_INTERVAL &&
key->etype == EXPR_CONCAT && key->field_count > 1) {
+ key->flags |= EXPR_F_INTERVAL;
+ netlink_gen_key(key, &nld);
+ key->flags &= ~EXPR_F_INTERVAL;
+
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
+
key->flags |= EXPR_F_INTERVAL_END;
- __netlink_gen_data(key, &nld, false);
+ netlink_gen_key(key, &nld);
key->flags &= ~EXPR_F_INTERVAL_END;
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY_END,
&nld.value, nld.len);
+ } else {
+ netlink_gen_key(key, &nld);
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
}
break;
}
- if (elem->timeout)
- nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_TIMEOUT,
- elem->timeout);
+ if (elem->timeout) {
+ uint64_t timeout = elem->timeout;
+
+ if (elem->timeout == NFT_NEVER_TIMEOUT)
+ timeout = 0;
+
+ nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_TIMEOUT, timeout);
+ }
if (elem->expiration)
nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_EXPIRATION,
elem->expiration);
@@ -167,7 +180,7 @@ struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
netlink_gen_stmt_stateful(stmt));
}
}
- if (elem->comment || expr->elem_flags) {
+ if (elem->comment || expr->flags & EXPR_F_INTERVAL_OPEN) {
udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udbuf)
memory_allocation_error();
@@ -177,9 +190,9 @@ struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
elem->comment))
memory_allocation_error();
}
- if (expr->elem_flags) {
+ if (expr->flags & EXPR_F_INTERVAL_OPEN) {
if (!nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_ELEM_FLAGS,
- expr->elem_flags))
+ NFTNL_SET_ELEM_F_INTERVAL_OPEN))
memory_allocation_error();
}
if (udbuf) {
@@ -203,6 +216,7 @@ struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
/* fallthrough */
case EXPR_VALUE:
case EXPR_RANGE:
+ case EXPR_RANGE_VALUE:
case EXPR_PREFIX:
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_DATA,
nld.value, nld.len);
@@ -246,22 +260,53 @@ static int netlink_export_pad(unsigned char *data, const mpz_t v,
return netlink_padded_len(i->len) / BITS_PER_BYTE;
}
-static int netlink_gen_concat_data_expr(int end, const struct expr *i,
- unsigned char *data)
+static void byteorder_switch_expr_value(mpz_t v, const struct expr *e)
{
+ mpz_switch_byteorder(v, div_round_up(e->len, BITS_PER_BYTE));
+}
+
+static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i,
+ unsigned char *data)
+{
+ struct expr *expr;
+ mpz_t value;
+ int ret;
+
switch (i->etype) {
case EXPR_RANGE:
- i = end ? i->right : i->left;
+ if (flags & EXPR_F_INTERVAL_END)
+ expr = i->right;
+ else
+ expr = i->left;
+
+ mpz_init_set(value, expr->value);
+
+ if (expr_basetype(expr)->type == TYPE_INTEGER &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ byteorder_switch_expr_value(value, expr);
+
+ i = expr;
+ break;
+ case EXPR_RANGE_VALUE:
+ if (flags & EXPR_F_INTERVAL_END)
+ mpz_init_set(value, i->range.high);
+ else
+ mpz_init_set(value, i->range.low);
+
+ if (expr_basetype(i)->type == TYPE_INTEGER &&
+ i->byteorder == BYTEORDER_HOST_ENDIAN)
+ byteorder_switch_expr_value(value, i);
+
break;
case EXPR_PREFIX:
- if (end) {
+ if (flags & EXPR_F_INTERVAL_END) {
int count;
mpz_t v;
mpz_init_bitmask(v, i->len - i->prefix_len);
if (i->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(v, i->len / BITS_PER_BYTE);
+ byteorder_switch_expr_value(v, i);
mpz_add(v, i->prefix->value, v);
count = netlink_export_pad(data, v, i);
@@ -270,53 +315,146 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i,
}
return netlink_export_pad(data, i->prefix->value, i);
case EXPR_VALUE:
+ mpz_init_set(value, i->value);
+
+ /* Switch byteorder to big endian representation when the set
+ * contains concatenation of intervals.
+ */
+ if (!(flags & (EXPR_F_INTERVAL| EXPR_F_INTERVAL_END)))
+ break;
+
+ expr = (struct expr *)i;
+ if (expr_basetype(expr)->type == TYPE_INTEGER &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ byteorder_switch_expr_value(value, expr);
break;
default:
BUG("invalid expression type '%s' in set", expr_ops(i)->name);
}
- return netlink_export_pad(data, i->value, i);
+ ret = netlink_export_pad(data, value, i);
+ mpz_clear(value);
+
+ return ret;
}
-static void __netlink_gen_concat(const struct expr *expr,
- struct nft_data_linearize *nld)
+static void nft_data_memcpy(struct nft_data_linearize *nld,
+ const void *src, unsigned int len)
{
- unsigned int len = expr->len / BITS_PER_BYTE, offset = 0;
- int end = expr->flags & EXPR_F_INTERVAL_END;
- unsigned char data[len];
+ if (len > sizeof(nld->value))
+ BUG("nld buffer overflow: want to copy %u, max %u\n", len, (unsigned int)sizeof(nld->value));
+
+ memcpy(nld->value, src, len);
+ nld->len = len;
+}
+
+static void netlink_gen_concat_key(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = netlink_padded_len(expr->len) / BITS_PER_BYTE;
+ unsigned char data[NFT_MAX_EXPR_LEN_BYTES];
+ unsigned int offset = 0;
const struct expr *i;
- memset(data, 0, len);
+ if (len > sizeof(data))
+ BUG("Value export of %u bytes would overflow", len);
+
+ memset(data, 0, sizeof(data));
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(end, i, data + offset);
+ offset += __netlink_gen_concat_key(expr->flags, i, data + offset);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static int __netlink_gen_concat_data(int end, const struct expr *i,
+ unsigned char *data)
+{
+ mpz_t value;
+ int ret;
+
+ switch (i->etype) {
+ case EXPR_RANGE:
+ if (end)
+ i = i->right;
+ else
+ i = i->left;
+
+ mpz_init_set(value, i->value);
+ break;
+ case EXPR_RANGE_VALUE:
+ if (end)
+ mpz_init_set(value, i->range.high);
+ else
+ mpz_init_set(value, i->range.low);
+ break;
+ case EXPR_PREFIX:
+ if (end) {
+ int count;
+
+ mpz_init_bitmask(value, i->len - i->prefix_len);
+ mpz_add(value, i->prefix->value, value);
+ count = netlink_export_pad(data, value, i);
+ mpz_clear(value);
+ return count;
+ }
+ return netlink_export_pad(data, i->prefix->value, i);
+ case EXPR_VALUE:
+ mpz_init_set(value, i->value);
+ break;
+ default:
+ BUG("invalid expression type '%s' in set", expr_ops(i)->name);
+ }
+
+ ret = netlink_export_pad(data, value, i);
+ mpz_clear(value);
+
+ return ret;
}
static void __netlink_gen_concat_expand(const struct expr *expr,
struct nft_data_linearize *nld)
{
- unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2, offset = 0;
- unsigned char data[len];
+ unsigned int len = (netlink_padded_len(expr->len) / BITS_PER_BYTE) * 2;
+ unsigned char data[NFT_MAX_EXPR_LEN_BYTES];
+ unsigned int offset = 0;
const struct expr *i;
- memset(data, 0, len);
+ if (len > sizeof(data))
+ BUG("Value export of %u bytes would overflow", len);
+
+ memset(data, 0, sizeof(data));
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(false, i, data + offset);
+ offset += __netlink_gen_concat_data(false, i, data + offset);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(true, i, data + offset);
+ offset += __netlink_gen_concat_data(true, i, data + offset);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static void __netlink_gen_concat(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = netlink_padded_len(expr->len) / BITS_PER_BYTE;
+ unsigned char data[NFT_MAX_EXPR_LEN_BYTES];
+ unsigned int offset = 0;
+ const struct expr *i;
+
+ if (len > sizeof(data))
+ BUG("Value export of %u bytes would overflow", len);
+
+ memset(data, 0, sizeof(data));
+
+ list_for_each_entry(i, &expr->expressions, list)
+ offset += __netlink_gen_concat_data(expr->flags, i, data + offset);
+
+ nft_data_memcpy(nld, data, len);
}
static void netlink_gen_concat_data(const struct expr *expr,
- struct nft_data_linearize *nld,
- bool expand)
+ struct nft_data_linearize *nld, bool expand)
{
if (expand)
__netlink_gen_concat_expand(expr, nld);
@@ -374,37 +512,74 @@ static void netlink_gen_verdict(const struct expr *expr,
static void netlink_gen_range(const struct expr *expr,
struct nft_data_linearize *nld)
{
- unsigned int len = div_round_up(expr->left->len, BITS_PER_BYTE) * 2;
- unsigned char data[len];
- unsigned int offset = 0;
+ unsigned int len = (netlink_padded_len(expr->left->len) / BITS_PER_BYTE) * 2;
+ unsigned char data[NFT_MAX_EXPR_LEN_BYTES];
+ unsigned int offset;
+
+ if (len > sizeof(data))
+ BUG("Value export of %u bytes would overflow", len);
- memset(data, 0, len);
+ memset(data, 0, sizeof(data));
offset = netlink_export_pad(data, expr->left->value, expr->left);
netlink_export_pad(data + offset, expr->right->value, expr->right);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static void netlink_gen_range_value(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = (netlink_padded_len(expr->len) / BITS_PER_BYTE) * 2;
+ unsigned char data[NFT_MAX_EXPR_LEN_BYTES];
+ unsigned int offset;
+
+ if (len > sizeof(data))
+ BUG("Value export of %u bytes would overflow", len);
+
+ memset(data, 0, sizeof(data));
+ offset = netlink_export_pad(data, expr->range.low, expr);
+ netlink_export_pad(data + offset, expr->range.high, expr);
+ nft_data_memcpy(nld, data, len);
}
static void netlink_gen_prefix(const struct expr *expr,
struct nft_data_linearize *nld)
{
- unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2;
- unsigned char data[len];
+ unsigned int len = (netlink_padded_len(expr->len) / BITS_PER_BYTE) * 2;
+ unsigned char data[NFT_MAX_EXPR_LEN_BYTES];
int offset;
mpz_t v;
+ if (len > sizeof(data))
+ BUG("Value export of %u bytes would overflow", len);
+
offset = netlink_export_pad(data, expr->prefix->value, expr);
mpz_init_bitmask(v, expr->len - expr->prefix_len);
mpz_add(v, expr->prefix->value, v);
netlink_export_pad(data + offset, v, expr->prefix);
mpz_clear(v);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static void netlink_gen_key(const struct expr *expr,
+ struct nft_data_linearize *data)
+{
+ switch (expr->etype) {
+ case EXPR_VALUE:
+ return netlink_gen_constant_data(expr, data);
+ case EXPR_CONCAT:
+ return netlink_gen_concat_key(expr, data);
+ case EXPR_RANGE:
+ return netlink_gen_range(expr, data);
+ case EXPR_PREFIX:
+ return netlink_gen_prefix(expr, data);
+ default:
+ BUG("invalid data expression type %s\n", expr_name(expr));
+ }
}
-void __netlink_gen_data(const struct expr *expr,
- struct nft_data_linearize *data, bool expand)
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand)
{
switch (expr->etype) {
case EXPR_VALUE:
@@ -415,6 +590,8 @@ void __netlink_gen_data(const struct expr *expr,
return netlink_gen_verdict(expr, data);
case EXPR_RANGE:
return netlink_gen_range(expr, data);
+ case EXPR_RANGE_VALUE:
+ return netlink_gen_range_value(expr, data);
case EXPR_PREFIX:
return netlink_gen_prefix(expr, data);
default:
@@ -529,18 +706,20 @@ static int qsort_device_cmp(const void *a, const void *b)
struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
const struct nftnl_chain *nlc)
{
- const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {};
+ const struct nftnl_udata *ud[NFTNL_UDATA_CHAIN_MAX + 1] = {};
int priority, policy, len = 0, i;
const char * const *dev_array;
struct chain *chain;
const char *udata;
uint32_t ulen;
- chain = chain_alloc(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME));
+ chain = chain_alloc();
chain->handle.family =
nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
chain->handle.table.name =
xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE));
+ chain->handle.chain.name =
+ xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME));
chain->handle.handle.id =
nftnl_chain_get_u64(nlc, NFTNL_CHAIN_HANDLE);
if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_FLAGS))
@@ -707,23 +886,30 @@ enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype)
static const struct datatype *dtype_map_from_kernel(enum nft_data_types type)
{
+ /* The function always returns ownership of a reference. But for
+ * &verdict_Type and datatype_lookup(), those are static instances,
+ * we can omit the datatype_get() call.
+ */
switch (type) {
case NFT_DATA_VERDICT:
return &verdict_type;
default:
if (type & ~TYPE_MASK)
return concat_type_alloc(type);
- return datatype_lookup(type);
+ return datatype_lookup((enum datatypes) type);
}
}
void netlink_dump_set(const struct nftnl_set *nls, struct netlink_ctx *ctx)
{
FILE *fp = ctx->nft->output.output_fp;
+ uint32_t family;
if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
return;
+ family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY);
+ fprintf(fp, "family %d ", family);
nftnl_set_fprintf(fp, nls, 0, 0);
fprintf(fp, "\n");
}
@@ -783,8 +969,8 @@ static struct expr *set_make_key(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_SET_TYPEOF_MAX + 1] = {};
const struct expr_ops *ops;
- enum expr_types etype;
struct expr *expr;
+ uint32_t etype;
int err;
if (!attr)
@@ -800,7 +986,9 @@ static struct expr *set_make_key(const struct nftnl_udata *attr)
return NULL;
etype = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_TYPEOF_EXPR]);
- ops = expr_ops_by_type(etype);
+ ops = expr_ops_by_type_u32(etype);
+ if (!ops || !ops->parse_udata)
+ return NULL;
expr = ops->parse_udata(ud[NFTNL_UDATA_SET_TYPEOF_DATA]);
if (!expr)
@@ -831,7 +1019,8 @@ static int set_elem_parse_expressions(struct nftnl_expr *e, void *data)
struct stmt *stmt;
stmt = netlink_parse_set_expr(set, cache, e);
- list_add_tail(&stmt->list, &setelem_parse_ctx->stmt_list);
+ if (stmt)
+ list_add_tail(&stmt->list, &setelem_parse_ctx->stmt_list);
return 0;
}
@@ -842,21 +1031,21 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
const struct nftnl_udata *ud[NFTNL_UDATA_SET_MAX + 1] = {};
enum byteorder keybyteorder = BYTEORDER_INVALID;
enum byteorder databyteorder = BYTEORDER_INVALID;
- const struct datatype *keytype, *datatype = NULL;
- struct expr *typeof_expr_key, *typeof_expr_data;
struct setelem_parse_ctx set_parse_ctx;
+ const struct datatype *datatype = NULL;
+ const struct datatype *keytype = NULL;
+ const struct datatype *dtype2 = NULL;
+ const struct datatype *dtype = NULL;
+ struct expr *typeof_expr_data = NULL;
+ struct expr *typeof_expr_key = NULL;
const char *udata, *comment = NULL;
uint32_t flags, key, objtype = 0;
- const struct datatype *dtype;
uint32_t data_interval = 0;
bool automerge = false;
struct set *set;
uint32_t ulen;
uint32_t klen;
- typeof_expr_key = NULL;
- typeof_expr_data = NULL;
-
if (nftnl_set_is_set(nls, NFTNL_SET_USERDATA)) {
udata = nftnl_set_get_data(nls, NFTNL_SET_USERDATA, &ulen);
if (nftnl_udata_parse(udata, ulen, set_parse_udata_cb, ud) < 0) {
@@ -899,8 +1088,8 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
netlink_io_error(ctx, NULL,
"Unknown data type in set key %u",
data);
- datatype_free(keytype);
- return NULL;
+ set = NULL;
+ goto out;
}
}
@@ -935,17 +1124,28 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
}
list_splice_tail(&set_parse_ctx.stmt_list, &set->stmt_list);
+ set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);
+
if (datatype) {
- dtype = set_datatype_alloc(datatype, databyteorder);
+ uint32_t dlen;
+
+ dtype2 = set_datatype_alloc(datatype, databyteorder);
klen = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE;
- if (set_udata_key_valid(typeof_expr_data, klen)) {
- datatype_free(datatype_get(dtype));
+ dlen = data_interval ? klen / 2 : klen;
+
+ if (set_udata_key_valid(typeof_expr_data, dlen)) {
+ typeof_expr_data->len = klen;
set->data = typeof_expr_data;
+ typeof_expr_data = NULL;
+ } else if (set->flags & NFT_SET_OBJECT) {
+ set->data = constant_expr_alloc(&netlink_location,
+ dtype2,
+ databyteorder, klen,
+ NULL);
} else {
- expr_free(typeof_expr_data);
set->data = constant_expr_alloc(&netlink_location,
- dtype,
+ dtype2,
databyteorder, klen,
NULL);
@@ -956,29 +1156,21 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
if (data_interval)
set->data->flags |= EXPR_F_INTERVAL;
-
- if (dtype != datatype)
- datatype_free(datatype);
}
dtype = set_datatype_alloc(keytype, keybyteorder);
klen = nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE;
if (set_udata_key_valid(typeof_expr_key, klen)) {
- datatype_free(datatype_get(dtype));
set->key = typeof_expr_key;
+ typeof_expr_key = NULL;
set->key_typeof_valid = true;
} else {
- expr_free(typeof_expr_key);
set->key = constant_expr_alloc(&netlink_location, dtype,
keybyteorder, klen,
NULL);
}
- if (dtype != keytype)
- datatype_free(keytype);
-
- set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);
set->handle.handle.id = nftnl_set_get_u64(nls, NFTNL_SET_HANDLE);
set->objtype = objtype;
@@ -994,6 +1186,9 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
if (nftnl_set_is_set(nls, NFTNL_SET_DESC_SIZE))
set->desc.size = nftnl_set_get_u32(nls, NFTNL_SET_DESC_SIZE);
+ if (nftnl_set_is_set(nls, NFTNL_SET_COUNT))
+ set->count = nftnl_set_get_u32(nls, NFTNL_SET_COUNT);
+
if (nftnl_set_is_set(nls, NFTNL_SET_DESC_CONCAT)) {
uint32_t len = NFT_REG32_COUNT;
const uint8_t *data;
@@ -1005,6 +1200,13 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
}
}
+out:
+ expr_free(typeof_expr_data);
+ expr_free(typeof_expr_key);
+ datatype_free(datatype);
+ datatype_free(keytype);
+ datatype_free(dtype2);
+ datatype_free(dtype);
return set;
}
@@ -1091,10 +1293,15 @@ static struct expr *range_expr_reduce(struct expr *range)
static struct expr *netlink_parse_interval_elem(const struct set *set,
struct expr *expr)
{
- unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ unsigned int len = netlink_padded_len(expr->len) / BITS_PER_BYTE;
const struct datatype *dtype = set->data->dtype;
struct expr *range, *left, *right;
- char data[len];
+ char data[NFT_MAX_EXPR_LEN_BYTES];
+
+ if (len > sizeof(data))
+ BUG("Value export of %u bytes would overflow", len);
+
+ memset(data, 0, sizeof(data));
mpz_export_data(data, expr->value, dtype->byteorder, len);
left = constant_expr_alloc(&internal_location, dtype,
@@ -1109,7 +1316,7 @@ static struct expr *netlink_parse_interval_elem(const struct set *set,
return range_expr_to_prefix(range);
}
-static struct expr *concat_elem_expr(struct expr *key,
+static struct expr *concat_elem_expr(const struct set *set, struct expr *key,
const struct datatype *dtype,
struct expr *data, int *off)
{
@@ -1133,7 +1340,9 @@ static struct expr *concat_elem_expr(struct expr *key,
expr->byteorder = subtype->byteorder;
}
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ if (expr_basetype(expr)->type == TYPE_STRING ||
+ (!(set->flags & NFT_SET_INTERVAL) &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN))
mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
if (expr->dtype->basetype != NULL &&
@@ -1157,7 +1366,7 @@ static struct expr *netlink_parse_concat_elem_key(const struct set *set,
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- expr = concat_elem_expr(n, dtype, data, &off);
+ expr = concat_elem_expr(set, n, dtype, data, &off);
compound_expr_add(concat, expr);
if (set->key->etype == EXPR_CONCAT)
n = list_next_entry(n, list);
@@ -1180,7 +1389,7 @@ static struct expr *netlink_parse_concat_elem(const struct set *set,
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- expr = concat_elem_expr(NULL, dtype, data, &off);
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
list_add_tail(&expr->list, &expressions);
}
@@ -1192,7 +1401,7 @@ static struct expr *netlink_parse_concat_elem(const struct set *set,
while (off > 0) {
left = list_first_entry(&expressions, struct expr, list);
- expr = concat_elem_expr(NULL, dtype, data, &off);
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
list_del(&left->list);
range = range_expr_alloc(&data->location, left, expr);
@@ -1246,17 +1455,23 @@ static void set_elem_parse_udata(struct nftnl_set_elem *nlse,
if (ud[NFTNL_UDATA_SET_ELEM_COMMENT])
expr->comment =
xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_SET_ELEM_COMMENT]));
- if (ud[NFTNL_UDATA_SET_ELEM_FLAGS])
- expr->elem_flags =
+ if (ud[NFTNL_UDATA_SET_ELEM_FLAGS]) {
+ uint32_t elem_flags;
+
+ elem_flags =
nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_ELEM_FLAGS]);
+ if (elem_flags & NFTNL_SET_ELEM_F_INTERVAL_OPEN)
+ expr->flags |= EXPR_F_INTERVAL_OPEN;
+ }
}
-int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
- struct set *set, struct nft_cache *cache)
+int netlink_delinearize_setelem(struct netlink_ctx *ctx,
+ struct nftnl_set_elem *nlse,
+ struct set *set)
{
struct setelem_parse_ctx setelem_parse_ctx = {
.set = set,
- .cache = cache,
+ .cache = &ctx->nft->cache,
};
struct nft_data_delinearize nld;
struct expr *expr, *key, *data;
@@ -1290,24 +1505,33 @@ key_end:
key->byteorder = set->key->byteorder;
key->len = set->key->len;
} else {
- BUG("Unexpected set element with no key\n");
+ netlink_io_error(ctx, NULL,
+ "Unexpected set element with no key");
+ return 0;
}
expr = set_elem_expr_alloc(&netlink_location, key);
expr->flags |= EXPR_F_KERNEL;
- if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT))
+ if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT)) {
expr->timeout = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_TIMEOUT);
+ if (expr->timeout == 0)
+ expr->timeout = NFT_NEVER_TIMEOUT;
+ }
+
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_EXPIRATION))
expr->expiration = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_EXPIRATION);
- if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_USERDATA))
+ if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_USERDATA)) {
set_elem_parse_udata(nlse, expr);
+ if (expr->comment)
+ set->elem_has_comment = true;
+ }
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_EXPR)) {
const struct nftnl_expr *nle;
struct stmt *stmt;
nle = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_EXPR, NULL);
- stmt = netlink_parse_set_expr(set, cache, nle);
+ stmt = netlink_parse_set_expr(set, &ctx->nft->cache, nle);
list_add_tail(&stmt->list, &setelem_parse_ctx.stmt_list);
} else if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_EXPRESSIONS)) {
nftnl_set_elem_expr_foreach(nlse, set_elem_parse_expressions,
@@ -1336,7 +1560,11 @@ key_end:
data = netlink_alloc_data(&netlink_location, &nld,
set->data->dtype->type == TYPE_VERDICT ?
NFT_REG_VERDICT : NFT_REG_1);
- datatype_set(data, set->data->dtype);
+
+ if (set->data->dtype->is_typeof)
+ datatype_set(data, set->data->dtype->basetype);
+ else
+ datatype_set(data, set->data->dtype);
data->byteorder = set->data->byteorder;
if (set->data->dtype->subtypes) {
@@ -1380,7 +1608,7 @@ out:
static int list_setelem_cb(struct nftnl_set_elem *nlse, void *arg)
{
struct netlink_ctx *ctx = arg;
- return netlink_delinearize_setelem(nlse, ctx->set, &ctx->nft->cache);
+ return netlink_delinearize_setelem(ctx, nlse, ctx->set);
}
static int list_setelem_debug_cb(struct nftnl_set_elem *nlse, void *arg)
@@ -1420,7 +1648,7 @@ static int list_setelements(struct nftnl_set *s, struct netlink_ctx *ctx)
}
int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
- struct set *set)
+ struct set *set, bool reset)
{
struct nftnl_set *nls;
int err;
@@ -1435,7 +1663,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
if (h->handle.id)
nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id);
- err = mnl_nft_setelem_get(ctx, nls);
+ err = mnl_nft_setelem_get(ctx, nls, reset);
if (err < 0) {
nftnl_set_free(nls);
if (errno == EINTR)
@@ -1463,7 +1691,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 set *cache_set,
- struct set *set, struct expr *init)
+ struct set *set, struct expr *init, bool reset)
{
struct nftnl_set *nls, *nls_out = NULL;
int err = 0;
@@ -1482,7 +1710,7 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
netlink_dump_set(nls, ctx);
- nls_out = mnl_nft_setelem_get_one(ctx, nls);
+ nls_out = mnl_nft_setelem_get_one(ctx, nls, reset);
if (!nls_out) {
nftnl_set_free(nls);
return -1;
@@ -1594,9 +1822,10 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
init_list_head(&obj->ct_timeout.timeout_list);
obj->ct_timeout.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO);
obj->ct_timeout.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_TIMEOUT_L4PROTO);
- memcpy(obj->ct_timeout.timeout,
- nftnl_obj_get(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY),
- NFTNL_CTTIMEOUT_ARRAY_MAX * sizeof(uint32_t));
+ if (nftnl_obj_is_set(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY))
+ memcpy(obj->ct_timeout.timeout,
+ nftnl_obj_get(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY),
+ NFTNL_CTTIMEOUT_ARRAY_MAX * sizeof(uint32_t));
break;
case NFT_OBJECT_LIMIT:
obj->limit.rate =
@@ -1630,6 +1859,10 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
obj->synproxy.flags =
nftnl_obj_get_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS);
break;
+ default:
+ netlink_io_error(ctx, NULL, "Unknown object type %u", type);
+ obj_free(obj);
+ return NULL;
}
obj->type = type;
@@ -1648,35 +1881,6 @@ void netlink_dump_flowtable(struct nftnl_flowtable *flo,
fprintf(fp, "\n");
}
-static int list_obj_cb(struct nftnl_obj *nls, void *arg)
-{
- struct netlink_ctx *ctx = arg;
- struct obj *obj;
-
- obj = netlink_delinearize_obj(ctx, nls);
- if (obj == NULL)
- return -1;
- list_add_tail(&obj->list, &ctx->list);
- return 0;
-}
-
-int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd,
- uint32_t type, bool dump)
-{
- const struct handle *h = &cmd->handle;
- struct nftnl_obj_list *obj_cache;
- int err;
-
- obj_cache = mnl_nft_obj_dump(ctx, h->family,
- h->table.name, h->obj.name, type, dump, true);
- if (obj_cache == NULL)
- return -1;
-
- err = nftnl_obj_list_foreach(obj_cache, list_obj_cb, ctx);
- nftnl_obj_list_free(obj_cache);
- return err;
-}
-
struct flowtable *
netlink_delinearize_flowtable(struct netlink_ctx *ctx,
struct nftnl_flowtable *nlo)
@@ -1697,10 +1901,11 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
if (nftnl_flowtable_is_set(nlo, NFTNL_FLOWTABLE_FLAGS))
flowtable->flags = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FLAGS);
dev_array = nftnl_flowtable_get(nlo, NFTNL_FLOWTABLE_DEVICES);
- while (dev_array[len])
+ while (dev_array && dev_array[len])
len++;
- flowtable->dev_array = calloc(1, len * sizeof(char *));
+ if (len)
+ flowtable->dev_array = xmalloc(len * sizeof(char *));
for (i = 0; i < len; i++)
flowtable->dev_array[i] = xstrdup(dev_array[i]);
@@ -1711,14 +1916,16 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
sizeof(char *), qsort_device_cmp);
}
- priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO);
- flowtable->priority.expr =
+ if (nftnl_flowtable_is_set(nlo, NFTNL_FLOWTABLE_PRIO)) {
+ priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO);
+ flowtable->priority.expr =
constant_expr_alloc(&netlink_location,
&integer_type,
BYTEORDER_HOST_ENDIAN,
sizeof(int) *
BITS_PER_BYTE,
&priority);
+ }
flowtable->hook.num =
nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_HOOKNUM);
flowtable->flags =
@@ -1925,6 +2132,7 @@ restart:
/* Skip unknown and filtered expressions */
desc = lhs->payload.desc;
if (lhs->dtype == &invalid_type ||
+ lhs->payload.tmpl == &proto_unknown_template ||
desc->checksum_key == payload_hdr_field(lhs) ||
desc->format.filter & (1 << payload_hdr_field(lhs))) {
expr_free(lhs);
@@ -1995,7 +2203,7 @@ static void trace_print_packet(const struct nftnl_trace *nlt,
meta_expr_alloc(&netlink_location,
NFT_META_OIF), octx);
- proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0);
+ proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false);
ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc;
if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) &&
nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) {
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 0da6cc78..48a3e33a 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -9,9 +9,8 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
+#include <nft.h>
+
#include <limits.h>
#include <linux/netfilter/nf_tables.h>
#include <arpa/inet.h>
@@ -30,6 +29,16 @@
#include <cache.h>
#include <xt.h>
+struct dl_proto_ctx *dl_proto_ctx(struct rule_pp_ctx *ctx)
+{
+ return ctx->dl;
+}
+
+static struct dl_proto_ctx *dl_proto_ctx_outer(struct rule_pp_ctx *ctx)
+{
+ return &ctx->_dl[0];
+}
+
static int netlink_parse_expr(const struct nftnl_expr *nle,
struct netlink_parse_ctx *ctx);
@@ -72,8 +81,7 @@ static void netlink_set_register(struct netlink_parse_ctx *ctx,
return;
}
- if (ctx->registers[reg] != NULL)
- expr_free(ctx->registers[reg]);
+ expr_free(ctx->registers[reg]);
ctx->registers[reg] = expr;
}
@@ -447,12 +455,12 @@ static void netlink_parse_lookup(struct netlink_parse_ctx *ctx,
ctx->stmt = expr_stmt_alloc(loc, expr);
}
-static struct expr *netlink_parse_bitwise_bool(struct netlink_parse_ctx *ctx,
- const struct location *loc,
- const struct nftnl_expr *nle,
- enum nft_registers sreg,
- struct expr *left)
-
+static struct expr *
+netlink_parse_bitwise_mask_xor(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle,
+ enum nft_registers sreg,
+ struct expr *left)
{
struct nft_data_delinearize nld;
struct expr *expr, *mask, *xor, *or;
@@ -479,7 +487,7 @@ static struct expr *netlink_parse_bitwise_bool(struct netlink_parse_ctx *ctx,
mpz_ior(m, m, o);
}
- if (left->len > 0 && mpz_scan0(m, 0) == left->len) {
+ if (left->len > 0 && mpz_scan0(m, 0) >= left->len) {
/* mask encompasses the entire value */
expr_free(mask);
} else {
@@ -512,10 +520,39 @@ static struct expr *netlink_parse_bitwise_bool(struct netlink_parse_ctx *ctx,
return expr;
}
+static struct expr *netlink_parse_bitwise_bool(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle,
+ enum nft_bitwise_ops op,
+ enum nft_registers sreg,
+ struct expr *left)
+{
+ enum nft_registers sreg2;
+ struct expr *right, *expr;
+
+ sreg2 = netlink_parse_register(nle, NFTNL_EXPR_BITWISE_SREG2);
+ right = netlink_get_register(ctx, loc, sreg2);
+ if (right == NULL) {
+ netlink_error(ctx, loc,
+ "Bitwise expression has no right-hand expression");
+ return NULL;
+ }
+
+ expr = binop_expr_alloc(loc,
+ op == NFT_BITWISE_XOR ? OP_XOR :
+ op == NFT_BITWISE_AND ? OP_AND : OP_OR,
+ left, right);
+
+ if (left->len > 0)
+ expr->len = left->len;
+
+ return expr;
+}
+
static struct expr *netlink_parse_bitwise_shift(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle,
- enum ops op,
+ enum nft_bitwise_ops op,
enum nft_registers sreg,
struct expr *left)
{
@@ -526,8 +563,10 @@ static struct expr *netlink_parse_bitwise_shift(struct netlink_parse_ctx *ctx,
right = netlink_alloc_value(loc, &nld);
right->byteorder = BYTEORDER_HOST_ENDIAN;
- expr = binop_expr_alloc(loc, op, left, right);
- expr->len = left->len;
+ expr = binop_expr_alloc(loc,
+ op == NFT_BITWISE_LSHIFT ? OP_LSHIFT : OP_RSHIFT,
+ left, right);
+ expr->len = nftnl_expr_get_u32(nle, NFTNL_EXPR_BITWISE_LEN) * BITS_PER_BYTE;
return expr;
}
@@ -550,20 +589,24 @@ static void netlink_parse_bitwise(struct netlink_parse_ctx *ctx,
op = nftnl_expr_get_u32(nle, NFTNL_EXPR_BITWISE_OP);
switch (op) {
- case NFT_BITWISE_BOOL:
- expr = netlink_parse_bitwise_bool(ctx, loc, nle, sreg,
- left);
+ case NFT_BITWISE_MASK_XOR:
+ expr = netlink_parse_bitwise_mask_xor(ctx, loc, nle, sreg,
+ left);
break;
- case NFT_BITWISE_LSHIFT:
- expr = netlink_parse_bitwise_shift(ctx, loc, nle, OP_LSHIFT,
- sreg, left);
+ case NFT_BITWISE_XOR:
+ case NFT_BITWISE_AND:
+ case NFT_BITWISE_OR:
+ expr = netlink_parse_bitwise_bool(ctx, loc, nle, op,
+ sreg, left);
break;
+ case NFT_BITWISE_LSHIFT:
case NFT_BITWISE_RSHIFT:
- expr = netlink_parse_bitwise_shift(ctx, loc, nle, OP_RSHIFT,
+ expr = netlink_parse_bitwise_shift(ctx, loc, nle, op,
sreg, left);
break;
default:
- BUG("invalid bitwise operation %u\n", op);
+ return netlink_error(ctx, loc,
+ "Invalid bitwise operation %u", op);
}
dreg = netlink_parse_register(nle, NFTNL_EXPR_BITWISE_DREG);
@@ -574,6 +617,7 @@ static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
+ uint32_t opval = nftnl_expr_get_u32(nle, NFTNL_EXPR_BYTEORDER_OP);
enum nft_registers sreg, dreg;
struct expr *expr, *arg;
enum ops op;
@@ -585,7 +629,7 @@ static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx,
"Byteorder expression has no left "
"hand side");
- switch (nftnl_expr_get_u32(nle, NFTNL_EXPR_BYTEORDER_OP)) {
+ switch (opval) {
case NFT_BYTEORDER_NTOH:
op = OP_NTOH;
break;
@@ -593,8 +637,9 @@ static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx,
op = OP_HTON;
break;
default:
- BUG("invalid byteorder operation %u\n",
- nftnl_expr_get_u32(nle, NFTNL_EXPR_BYTEORDER_OP));
+ expr_free(arg);
+ return netlink_error(ctx, loc,
+ "Invalid byteorder operation %u", opval);
}
expr = unary_expr_alloc(loc, op, arg);
@@ -613,6 +658,10 @@ static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx,
struct expr *expr;
base = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_BASE) + 1;
+
+ if (base == NFT_PAYLOAD_TUN_HEADER + 1)
+ base = NFT_PAYLOAD_INNER_HEADER + 1;
+
offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_OFFSET) * BITS_PER_BYTE;
len = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_LEN) * BITS_PER_BYTE;
@@ -620,9 +669,82 @@ static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx,
payload_init_raw(expr, base, offset, len);
dreg = netlink_parse_register(nle, NFTNL_EXPR_PAYLOAD_DREG);
+
+ if (ctx->inner)
+ ctx->inner_reg = dreg;
+
netlink_set_register(ctx, dreg, expr);
}
+static void netlink_parse_inner(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle)
+{
+ const struct proto_desc *inner_desc;
+ const struct nftnl_expr *inner_nle;
+ uint32_t hdrsize, flags, type;
+ struct expr *expr;
+
+ hdrsize = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_HDRSIZE);
+ type = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_TYPE);
+ flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_FLAGS);
+
+ inner_nle = nftnl_expr_get(nle, NFTNL_EXPR_INNER_EXPR, NULL);
+ if (!inner_nle) {
+ netlink_error(ctx, loc, "Could not parse inner expression");
+ return;
+ }
+
+ inner_desc = proto_find_inner(type, hdrsize, flags);
+
+ ctx->inner = true;
+ if (netlink_parse_expr(inner_nle, ctx) < 0) {
+ ctx->inner = false;
+ return;
+ }
+ ctx->inner = false;
+
+ expr = netlink_get_register(ctx, loc, ctx->inner_reg);
+ assert(expr);
+
+ if (expr->etype == EXPR_PAYLOAD &&
+ expr->payload.base == PROTO_BASE_INNER_HDR) {
+ const struct proto_hdr_template *tmpl;
+ unsigned int i;
+
+ for (i = 1; i < array_size(inner_desc->templates); i++) {
+ tmpl = &inner_desc->templates[i];
+
+ if (tmpl->len == 0)
+ return;
+
+ if (tmpl->offset != expr->payload.offset ||
+ tmpl->len != expr->len)
+ continue;
+
+ expr->payload.desc = inner_desc;
+ expr->payload.tmpl = tmpl;
+ break;
+ }
+ }
+
+ switch (expr->etype) {
+ case EXPR_PAYLOAD:
+ expr->payload.inner_desc = inner_desc;
+ break;
+ case EXPR_META:
+ expr->meta.inner_desc = inner_desc;
+ break;
+ default:
+ netlink_error(ctx, loc, "Unsupported inner expression type %s",
+ expr_ops(expr)->name);
+ expr_free(expr);
+ return;
+ }
+
+ netlink_set_register(ctx, ctx->inner_reg, expr);
+}
+
static void netlink_parse_payload_stmt(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -779,6 +901,9 @@ static void netlink_parse_meta_expr(struct netlink_parse_ctx *ctx,
expr = meta_expr_alloc(loc, key);
dreg = netlink_parse_register(nle, NFTNL_EXPR_META_DREG);
+ if (ctx->inner)
+ ctx->inner_reg = dreg;
+
netlink_set_register(ctx, dreg, expr);
}
@@ -832,7 +957,9 @@ static void netlink_parse_meta_stmt(struct netlink_parse_ctx *ctx,
key = nftnl_expr_get_u32(nle, NFTNL_EXPR_META_KEY);
stmt = meta_stmt_alloc(loc, key, expr);
- expr_set_type(expr, stmt->meta.tmpl->dtype, stmt->meta.tmpl->byteorder);
+
+ if (stmt->meta.tmpl)
+ expr_set_type(expr, stmt->meta.tmpl->dtype, stmt->meta.tmpl->byteorder);
ctx->stmt = stmt;
}
@@ -979,6 +1106,19 @@ static void netlink_parse_counter(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
+static void netlink_parse_last(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle)
+{
+ struct stmt *stmt;
+
+ stmt = last_stmt_alloc(loc);
+ stmt->last.used = nftnl_expr_get_u64(nle, NFTNL_EXPR_LAST_MSECS);
+ stmt->last.set = nftnl_expr_get_u32(nle, NFTNL_EXPR_LAST_SET);
+
+ ctx->stmt = stmt;
+}
+
static void netlink_parse_log(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -989,11 +1129,7 @@ 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 = constant_expr_alloc(&internal_location,
- &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(prefix) + 1) * BITS_PER_BYTE,
- prefix);
+ stmt->log.prefix = xstrdup(prefix);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_GROUP)) {
@@ -1631,6 +1767,8 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
expr = netlink_parse_concat_key(ctx, loc, sreg, set->key);
if (expr == NULL)
return;
+ } else if (expr->dtype == &invalid_type) {
+ expr_set_type(expr, datatype_get(set->key->dtype), set->key->byteorder);
}
expr = set_elem_expr_alloc(&expr->location, expr);
@@ -1660,6 +1798,14 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_SREG_DATA)) {
sreg_data = netlink_parse_register(nle, NFTNL_EXPR_DYNSET_SREG_DATA);
expr_data = netlink_get_register(ctx, loc, sreg_data);
+
+ if (expr_data && expr_data->len < set->data->len) {
+ expr_free(expr_data);
+ expr_data = netlink_parse_concat_expr(ctx, loc, sreg_data, set->data->len);
+ if (expr_data == NULL)
+ netlink_error(ctx, loc,
+ "Could not parse dynset map data expressions");
+ }
}
if (expr_data != NULL) {
@@ -1772,6 +1918,7 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "bitwise", .parse = netlink_parse_bitwise },
{ .name = "byteorder", .parse = netlink_parse_byteorder },
{ .name = "payload", .parse = netlink_parse_payload },
+ { .name = "inner", .parse = netlink_parse_inner },
{ .name = "exthdr", .parse = netlink_parse_exthdr },
{ .name = "meta", .parse = netlink_parse_meta },
{ .name = "socket", .parse = netlink_parse_socket },
@@ -1780,6 +1927,7 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "ct", .parse = netlink_parse_ct },
{ .name = "connlimit", .parse = netlink_parse_connlimit },
{ .name = "counter", .parse = netlink_parse_counter },
+ { .name = "last", .parse = netlink_parse_last },
{ .name = "log", .parse = netlink_parse_log },
{ .name = "limit", .parse = netlink_parse_limit },
{ .name = "range", .parse = netlink_parse_range },
@@ -1868,6 +2016,37 @@ struct stmt *netlink_parse_set_expr(const struct set *set,
return pctx->stmt;
}
+static bool meta_outer_may_dependency_kill(struct rule_pp_ctx *ctx,
+ const struct expr *expr)
+{
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+ struct stmt *stmt = dl_outer->pdctx.pdeps[expr->payload.inner_desc->base];
+ struct expr *dep;
+ uint8_t l4proto;
+
+ if (!stmt)
+ return false;
+
+ dep = stmt->expr;
+
+ if (dep->left->meta.key != NFT_META_L4PROTO)
+ return false;
+
+ l4proto = mpz_get_uint8(dep->right->value);
+
+ switch (l4proto) {
+ case IPPROTO_GRE:
+ if (expr->payload.inner_desc == &proto_gre ||
+ expr->payload.inner_desc == &proto_gretap)
+ return true;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp);
static void payload_match_expand(struct rule_pp_ctx *ctx,
@@ -1876,11 +2055,12 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
{
struct expr *left = payload, *right = expr->right, *tmp;
struct list_head list = LIST_HEAD_INIT(list);
- struct stmt *nstmt;
- struct expr *nexpr = NULL;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
enum proto_bases base = left->payload.base;
+ struct expr *nexpr = NULL;
+ struct stmt *nstmt;
- payload_expr_expand(&list, left, &ctx->pctx);
+ payload_expr_expand(&list, left, &dl->pctx);
list_for_each_entry(left, &list, list) {
tmp = constant_expr_splice(right, left->len);
@@ -1895,7 +2075,7 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
nexpr = relational_expr_alloc(&expr->location, expr->op,
left, tmp);
if (expr->op == OP_EQ)
- relational_expr_pctx_update(&ctx->pctx, nexpr);
+ relational_expr_pctx_update(&dl->pctx, nexpr);
nstmt = expr_stmt_alloc(&ctx->stmt->location, nexpr);
list_add_tail(&nstmt->list, &ctx->stmt->list);
@@ -1904,17 +2084,32 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
assert(left->payload.base);
assert(base == left->payload.base);
- if (payload_is_stacked(ctx->pctx.protocol[base].desc, nexpr))
+ if (expr->left->payload.inner_desc) {
+ if (expr->left->payload.inner_desc == expr->left->payload.desc) {
+ nexpr->left->payload.desc = expr->left->payload.desc;
+ nexpr->left->payload.tmpl = expr->left->payload.tmpl;
+ }
+ nexpr->left->payload.inner_desc = expr->left->payload.inner_desc;
+
+ if (meta_outer_may_dependency_kill(ctx, expr->left)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->left->payload.inner_desc->base);
+ }
+ }
+
+ if (payload_is_stacked(dl->pctx.protocol[base].desc, nexpr))
base--;
/* Remember the first payload protocol expression to
* kill it later on if made redundant by a higher layer
* payload expression.
*/
- payload_dependency_kill(&ctx->pdctx, nexpr->left,
- ctx->pctx.family);
+ payload_dependency_kill(&dl->pdctx, nexpr->left,
+ dl->pctx.family);
+ expr_set_type(tmp, nexpr->left->dtype, nexpr->byteorder);
if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, nstmt, base);
+ payload_dependency_store(&dl->pdctx, nstmt, base);
}
list_del(&ctx->stmt->list);
stmt_free(ctx->stmt);
@@ -1923,6 +2118,7 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, const struct expr *value)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
uint8_t icmp_type;
@@ -1937,10 +2133,10 @@ static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, cons
/* icmp(v6) type is 8 bit, if value is smaller or larger, this is not
* a protocol dependency.
*/
- if (expr->len != 8 || value->len != 8 || rctx->pctx.th_dep.icmp.type)
+ if (expr->len != 8 || value->len != 8 || dl->pctx.th_dep.icmp.type)
return;
- desc = rctx->pctx.protocol[expr->payload.base].desc;
+ desc = dl->pctx.protocol[expr->payload.base].desc;
if (desc == NULL)
return;
@@ -1968,7 +2164,7 @@ static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, cons
expr->payload.desc = desc;
expr->payload.tmpl = tmpl;
- rctx->pctx.th_dep.icmp.type = icmp_type;
+ dl->pctx.th_dep.icmp.type = icmp_type;
return;
}
}
@@ -1977,6 +2173,8 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
struct expr *expr,
struct expr *payload)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
+
switch (expr->op) {
case OP_EQ:
case OP_NEQ:
@@ -2000,10 +2198,10 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
}
/* Fall through */
default:
- payload_expr_complete(payload, &ctx->pctx);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(expr->right, payload->dtype,
payload->byteorder);
- payload_dependency_kill(&ctx->pdctx, payload, ctx->pctx.family);
+ payload_dependency_kill(&dl->pdctx, payload, dl->pctx.family);
break;
}
}
@@ -2057,17 +2255,71 @@ static bool __meta_dependency_may_kill(const struct expr *dep, uint8_t *nfproto)
return false;
}
+static bool ct_may_dependency_kill(unsigned int meta_nfproto,
+ const struct expr *ct)
+{
+ assert(ct->etype == EXPR_CT);
+
+ switch (ct->ct.key) {
+ case NFT_CT_DST:
+ case NFT_CT_SRC:
+ switch (ct->len) {
+ case 32:
+ return meta_nfproto == NFPROTO_IPV4;
+ case 128:
+ return meta_nfproto == NFPROTO_IPV6;
+ default:
+ break;
+ }
+ return false;
+ case NFT_CT_DST_IP:
+ case NFT_CT_SRC_IP:
+ return meta_nfproto == NFPROTO_IPV4;
+ case NFT_CT_DST_IP6:
+ case NFT_CT_SRC_IP6:
+ return meta_nfproto == NFPROTO_IPV6;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool meta_may_dependency_kill(uint8_t nfproto, const struct expr *meta, const struct expr *v)
+{
+ uint8_t l4proto;
+
+ if (meta->meta.key != NFT_META_L4PROTO)
+ return true;
+
+ if (v->etype != EXPR_VALUE || v->len != 8)
+ return false;
+
+ l4proto = mpz_get_uint8(v->value);
+
+ switch (l4proto) {
+ case IPPROTO_ICMP:
+ return nfproto == NFPROTO_IPV4;
+ case IPPROTO_ICMPV6:
+ return nfproto == NFPROTO_IPV6;
+ default:
+ break;
+ }
+
+ return false;
+}
+
/* We have seen a protocol key expression that restricts matching at the network
* base, leave it in place since this is meaningful in bridge, inet and netdev
* families. Exceptions are ICMP and ICMPv6 where this code assumes that can
* only happen with IPv4 and IPv6.
*/
-static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
+static bool ct_meta_may_dependency_kill(struct payload_dep_ctx *ctx,
unsigned int family,
const struct expr *expr)
{
- uint8_t l4proto, nfproto = NFPROTO_UNSPEC;
struct expr *dep = payload_dependency_get(ctx, PROTO_BASE_NETWORK_HDR);
+ uint8_t nfproto = NFPROTO_UNSPEC;
if (!dep)
return true;
@@ -2087,23 +2339,15 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
return true;
}
- if (expr->left->meta.key != NFT_META_L4PROTO)
- return true;
-
- l4proto = mpz_get_uint8(expr->right->value);
-
- switch (l4proto) {
- case IPPROTO_ICMP:
- case IPPROTO_ICMPV6:
- break;
+ switch (expr->left->etype) {
+ case EXPR_META:
+ return meta_may_dependency_kill(nfproto, expr->left, expr->right);
+ case EXPR_CT:
+ return ct_may_dependency_kill(nfproto, expr->left);
default:
- return false;
+ break;
}
- if ((nfproto == NFPROTO_IPV4 && l4proto == IPPROTO_ICMPV6) ||
- (nfproto == NFPROTO_IPV6 && l4proto == IPPROTO_ICMP))
- return false;
-
return true;
}
@@ -2111,6 +2355,7 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx,
const struct expr *expr,
enum proto_bases base)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
const struct expr *left = expr->left;
struct expr *right = expr->right;
@@ -2124,16 +2369,16 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx,
expr->right->etype == EXPR_SET_REF)
break;
- relational_expr_pctx_update(&ctx->pctx, expr);
+ relational_expr_pctx_update(&dl->pctx, expr);
if (base < PROTO_BASE_TRANSPORT_HDR) {
- if (payload_dependency_exists(&ctx->pdctx, base) &&
- meta_may_dependency_kill(&ctx->pdctx,
- ctx->pctx.family, expr))
- payload_dependency_release(&ctx->pdctx, base);
+ if (payload_dependency_exists(&dl->pdctx, base) &&
+ ct_meta_may_dependency_kill(&dl->pdctx,
+ dl->pctx.family, expr))
+ payload_dependency_release(&dl->pdctx, base);
if (left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
+ payload_dependency_store(&dl->pdctx, ctx->stmt, base);
}
break;
default:
@@ -2228,6 +2473,9 @@ static void binop_adjust(const struct expr *binop, struct expr *right,
binop_adjust_one(binop, right, shift);
break;
case EXPR_SET_REF:
+ if (!set_is_anonymous(right->set->flags))
+ break;
+
list_for_each_entry(i, &right->set->init->expressions, list) {
switch (i->key->etype) {
case EXPR_VALUE:
@@ -2255,19 +2503,20 @@ static void binop_adjust(const struct expr *binop, struct expr *right,
}
}
-static void __binop_postprocess(struct rule_pp_ctx *ctx,
+static bool __binop_postprocess(struct rule_pp_ctx *ctx,
struct expr *expr,
struct expr *left,
struct expr *mask,
struct expr **expr_binop)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct expr *binop = *expr_binop;
unsigned int shift;
assert(binop->etype == EXPR_BINOP);
if ((left->etype == EXPR_PAYLOAD &&
- payload_expr_trim(left, mask, &ctx->pctx, &shift)) ||
+ payload_expr_trim(left, mask, &dl->pctx, &shift)) ||
(left->etype == EXPR_EXTHDR &&
exthdr_find_template(left, mask, &shift))) {
struct expr *right = NULL;
@@ -2304,17 +2553,27 @@ static void __binop_postprocess(struct rule_pp_ctx *ctx,
expr_set_type(right, left->dtype, left->byteorder);
expr_free(binop);
+ return true;
+ } else if (left->etype == EXPR_PAYLOAD &&
+ expr->right->etype == EXPR_VALUE &&
+ payload_expr_trim_force(left, mask, &shift)) {
+ mpz_rshift_ui(expr->right->value, shift);
+ *expr_binop = expr_get(left);
+ expr_free(binop);
+ return true;
}
+
+ return false;
}
-static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr,
+static bool binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr,
struct expr **expr_binop)
{
struct expr *binop = *expr_binop;
struct expr *left = binop->left;
struct expr *mask = binop->right;
- __binop_postprocess(ctx, expr, left, mask, expr_binop);
+ return __binop_postprocess(ctx, expr, left, mask, expr_binop);
}
static void map_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
@@ -2350,57 +2609,30 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) &&
right->dtype->basetype &&
- right->dtype->basetype->type == TYPE_BITMASK) {
- switch (right->etype) {
- case EXPR_VALUE:
- if (!mpz_cmp_ui(right->value, 0)) {
- /* Flag comparison: data & flags != 0
- *
- * Split the flags into a list of flag values and convert the
- * op to OP_EQ.
- */
- expr_free(right);
+ right->dtype->basetype->type == TYPE_BITMASK &&
+ right->etype == EXPR_VALUE &&
+ !mpz_cmp_ui(right->value, 0)) {
+ /* Flag comparison: data & flags != 0
+ *
+ * Split the flags into a list of flag values and convert the
+ * op to OP_EQ.
+ */
+ expr_free(right);
- expr->left = expr_get(binop->left);
- expr->right = binop_tree_to_list(NULL, binop->right);
- switch (expr->op) {
- case OP_NEQ:
- expr->op = OP_IMPLICIT;
- break;
- case OP_EQ:
- expr->op = OP_NEG;
- break;
- default:
- BUG("unknown operation type %d\n", expr->op);
- }
- expr_free(binop);
- } else if (binop->right->etype == EXPR_VALUE &&
- right->etype == EXPR_VALUE &&
- !mpz_cmp(right->value, binop->right->value)) {
- /* Skip flag / flag representation for:
- * data & flag == flag
- * data & flag != flag
- */
- ;
- } else {
- *exprp = flagcmp_expr_alloc(&expr->location, expr->op,
- expr_get(binop->left),
- binop_tree_to_list(NULL, binop->right),
- expr_get(right));
- expr_free(expr);
- }
+ expr->left = expr_get(binop->left);
+ expr->right = binop_tree_to_list(NULL, binop->right);
+ switch (expr->op) {
+ case OP_NEQ:
+ expr->op = OP_IMPLICIT;
break;
- case EXPR_BINOP:
- *exprp = flagcmp_expr_alloc(&expr->location, expr->op,
- expr_get(binop->left),
- binop_tree_to_list(NULL, binop->right),
- binop_tree_to_list(NULL, right));
- expr_free(expr);
+ case OP_EQ:
+ expr->op = OP_NEG;
break;
default:
- break;
+ BUG("unknown operation type %d\n", expr->op);
}
- } else if (binop->left->dtype->flags & DTYPE_F_PREFIX &&
+ expr_free(binop);
+ } else if (datatype_prefix_notation(binop->left->dtype) &&
binop->op == OP_AND && expr->right->etype == EXPR_VALUE &&
expr_mask_is_prefix(binop->right)) {
expr->left = expr_get(binop->left);
@@ -2452,6 +2684,50 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
}
}
+static bool payload_binop_postprocess(struct rule_pp_ctx *ctx,
+ struct expr **exprp)
+{
+ struct expr *expr = *exprp;
+
+ if (expr->op != OP_RSHIFT)
+ return false;
+
+ if (expr->left->etype == EXPR_UNARY) {
+ /*
+ * If the payload value was originally in a different byte-order
+ * from the payload expression, there will be a byte-order
+ * conversion to remove.
+ */
+ struct expr *left = expr_get(expr->left->arg);
+ expr_free(expr->left);
+ expr->left = left;
+ }
+
+ if (expr->left->etype != EXPR_BINOP || expr->left->op != OP_AND)
+ return false;
+
+ switch (expr->left->left->etype) {
+ case EXPR_EXTHDR:
+ break;
+ case EXPR_PAYLOAD:
+ break;
+ default:
+ return false;
+ }
+
+ expr_postprocess(ctx, &expr->left->left);
+
+ expr_set_type(expr->right, &integer_type,
+ BYTEORDER_HOST_ENDIAN);
+ expr_postprocess(ctx, &expr->right);
+
+ binop_postprocess(ctx, expr, &expr->left);
+ *exprp = expr_get(expr->left);
+ expr_free(expr);
+
+ return true;
+}
+
static struct expr *string_wildcard_expr_alloc(struct location *loc,
const struct expr *mask,
const struct expr *expr)
@@ -2517,8 +2793,54 @@ static struct expr *expr_postprocess_string(struct expr *expr)
return out;
}
+static void expr_postprocess_value(struct rule_pp_ctx *ctx, struct expr **exprp)
+{
+ bool interval = (ctx->set && ctx->set->flags & NFT_SET_INTERVAL);
+ struct expr *expr = *exprp;
+
+ // FIXME
+ if (expr->byteorder == BYTEORDER_HOST_ENDIAN && !interval)
+ mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+
+ if (expr_basetype(expr)->type == TYPE_STRING)
+ *exprp = expr_postprocess_string(expr);
+
+ expr = *exprp;
+ if (expr->dtype->basetype != NULL &&
+ expr->dtype->basetype->type == TYPE_BITMASK)
+ *exprp = bitmask_expr_to_binops(expr);
+}
+
+static void expr_postprocess_concat(struct rule_pp_ctx *ctx, struct expr **exprp)
+{
+ struct expr *i, *n, *expr = *exprp;
+ unsigned int type = expr->dtype->type, ntype = 0;
+ int off = expr->dtype->subtypes;
+ const struct datatype *dtype;
+ LIST_HEAD(tmp);
+
+ assert(expr->etype == EXPR_CONCAT);
+
+ ctx->flags |= RULE_PP_IN_CONCATENATION;
+ list_for_each_entry_safe(i, n, &expr->expressions, list) {
+ if (type) {
+ dtype = concat_subtype_lookup(type, --off);
+ expr_set_type(i, dtype, dtype->byteorder);
+ }
+ list_del(&i->list);
+ expr_postprocess(ctx, &i);
+ list_add_tail(&i->list, &tmp);
+
+ ntype = concat_subtype_add(ntype, i->dtype->type);
+ }
+ ctx->flags &= ~RULE_PP_IN_CONCATENATION;
+ list_splice(&tmp, &expr->expressions);
+ __datatype_set(expr, concat_type_alloc(ntype));
+}
+
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct expr *expr = *exprp, *i;
switch (expr->etype) {
@@ -2542,35 +2864,17 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
list_for_each_entry(i, &expr->expressions, list)
expr_postprocess(ctx, &i);
break;
- case EXPR_CONCAT: {
- unsigned int type = expr->dtype->type, ntype = 0;
- int off = expr->dtype->subtypes;
- const struct datatype *dtype;
- LIST_HEAD(tmp);
- struct expr *n;
-
- ctx->flags |= RULE_PP_IN_CONCATENATION;
- list_for_each_entry_safe(i, n, &expr->expressions, list) {
- if (type) {
- dtype = concat_subtype_lookup(type, --off);
- expr_set_type(i, dtype, dtype->byteorder);
- }
- list_del(&i->list);
- expr_postprocess(ctx, &i);
- list_add_tail(&i->list, &tmp);
-
- ntype = concat_subtype_add(ntype, i->dtype->type);
- }
- ctx->flags &= ~RULE_PP_IN_CONCATENATION;
- list_splice(&tmp, &expr->expressions);
- datatype_set(expr, concat_type_alloc(ntype));
+ case EXPR_CONCAT:
+ expr_postprocess_concat(ctx, exprp);
break;
- }
case EXPR_UNARY:
expr_postprocess(ctx, &expr->arg);
expr_set_type(expr, expr->arg->dtype, !expr->arg->byteorder);
break;
case EXPR_BINOP:
+ if (payload_binop_postprocess(ctx, exprp))
+ break;
+
expr_postprocess(ctx, &expr->left);
switch (expr->op) {
case OP_LSHIFT:
@@ -2579,8 +2883,13 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
BYTEORDER_HOST_ENDIAN);
break;
case OP_AND:
- expr_set_type(expr->right, expr->left->dtype,
- expr->left->byteorder);
+ if (expr->right->len > expr->left->len) {
+ expr_set_type(expr->right, expr->left->dtype,
+ BYTEORDER_HOST_ENDIAN);
+ } else {
+ expr_set_type(expr->right, expr->left->dtype,
+ expr->left->byteorder);
+ }
/* Do not process OP_AND in ordinary rule context.
*
@@ -2600,19 +2909,62 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
}
break;
default:
- expr_set_type(expr->right, expr->left->dtype,
- expr->left->byteorder);
+ if (expr->right->len > expr->left->len) {
+ expr_set_type(expr->right, expr->left->dtype,
+ BYTEORDER_HOST_ENDIAN);
+ } else {
+ expr_set_type(expr->right, expr->left->dtype,
+ expr->left->byteorder);
+ }
}
expr_postprocess(ctx, &expr->right);
- expr_set_type(expr, expr->left->dtype,
- expr->left->byteorder);
+ switch (expr->op) {
+ case OP_LSHIFT:
+ case OP_RSHIFT:
+ expr_set_type(expr, &xinteger_type,
+ BYTEORDER_HOST_ENDIAN);
+ break;
+ default:
+ expr_set_type(expr, expr->left->dtype,
+ expr->left->byteorder);
+ }
+
break;
case EXPR_RELATIONAL:
switch (expr->left->etype) {
case EXPR_PAYLOAD:
payload_match_postprocess(ctx, expr, expr->left);
return;
+ case EXPR_CONCAT:
+ if (expr->right->etype == EXPR_SET_REF) {
+ assert(expr->left->dtype == &invalid_type);
+ assert(expr->right->dtype != &invalid_type);
+
+ datatype_set(expr->left, expr->right->dtype);
+ }
+ ctx->set = expr->right->set;
+ expr_postprocess(ctx, &expr->left);
+ ctx->set = NULL;
+ break;
+ case EXPR_UNARY:
+ if (lhs_is_meta_hour(expr->left->arg) &&
+ expr->right->etype == EXPR_RANGE) {
+ struct expr *range = expr->right;
+
+ /* Cross-day range needs to be reversed.
+ * Kernel handles time in UTC. Therefore,
+ * 03:00-14:00 AEDT (Sidney, Australia) time
+ * is a cross-day range.
+ */
+ if (mpz_cmp(range->left->value,
+ range->right->value) <= 0 &&
+ expr->op == OP_NEQ) {
+ range_expr_swap_values(range);
+ expr->op = OP_IMPLICIT;
+ }
+ }
+ /* fallthrough */
default:
expr_postprocess(ctx, &expr->left);
break;
@@ -2636,22 +2988,18 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
}
break;
case EXPR_PAYLOAD:
- payload_expr_complete(expr, &ctx->pctx);
- payload_dependency_kill(&ctx->pdctx, expr, ctx->pctx.family);
+ payload_expr_complete(expr, &dl->pctx);
+ if (expr->payload.inner_desc) {
+ if (meta_outer_may_dependency_kill(ctx, expr)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->payload.inner_desc->base);
+ }
+ }
+ payload_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_VALUE:
- // FIXME
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
-
- if (expr_basetype(expr)->type == TYPE_STRING)
- *exprp = expr_postprocess_string(expr);
-
- expr = *exprp;
- if (expr->dtype->basetype != NULL &&
- expr->dtype->basetype->type == TYPE_BITMASK)
- *exprp = bitmask_expr_to_binops(expr);
-
+ expr_postprocess_value(ctx, exprp);
break;
case EXPR_RANGE:
expr_postprocess(ctx, &expr->left);
@@ -2666,7 +3014,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
ctx->flags &= ~RULE_PP_IN_SET_ELEM;
break;
case EXPR_EXTHDR:
- exthdr_dependency_kill(&ctx->pdctx, expr, ctx->pctx.family);
+ exthdr_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_SET_REF:
case EXPR_META:
@@ -2683,7 +3031,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
expr_postprocess(ctx, &expr->hash.expr);
break;
case EXPR_CT:
- ct_expr_update_type(&ctx->pctx, expr);
+ ct_expr_update_type(&dl->pctx, expr);
break;
default:
BUG("unknown expression type %s\n", expr_name(expr));
@@ -2692,34 +3040,35 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
const struct proto_desc *desc, *base;
struct stmt *stmt = rctx->stmt;
int protocol;
- switch (rctx->pctx.family) {
+ switch (dl->pctx.family) {
case NFPROTO_IPV4:
- stmt->reject.family = rctx->pctx.family;
- datatype_set(stmt->reject.expr, &icmp_code_type);
+ stmt->reject.family = dl->pctx.family;
+ datatype_set(stmt->reject.expr, &reject_icmp_code_type);
if (stmt->reject.type == NFT_REJECT_TCP_RST &&
- payload_dependency_exists(&rctx->pdctx,
+ payload_dependency_exists(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(&rctx->pdctx,
+ payload_dependency_release(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR);
break;
case NFPROTO_IPV6:
- stmt->reject.family = rctx->pctx.family;
- datatype_set(stmt->reject.expr, &icmpv6_code_type);
+ stmt->reject.family = dl->pctx.family;
+ datatype_set(stmt->reject.expr, &reject_icmpv6_code_type);
if (stmt->reject.type == NFT_REJECT_TCP_RST &&
- payload_dependency_exists(&rctx->pdctx,
+ payload_dependency_exists(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(&rctx->pdctx,
+ payload_dependency_release(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR);
break;
case NFPROTO_INET:
case NFPROTO_BRIDGE:
case NFPROTO_NETDEV:
if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH) {
- datatype_set(stmt->reject.expr, &icmpx_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmpx_code_type);
break;
}
@@ -2728,26 +3077,26 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
*/
stmt->reject.verbose_print = 1;
- base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = dl->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = dl->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4: /* INET */
case __constant_htons(ETH_P_IP): /* BRIDGE, NETDEV */
stmt->reject.family = NFPROTO_IPV4;
- datatype_set(stmt->reject.expr, &icmp_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmp_code_type);
break;
case NFPROTO_IPV6: /* INET */
case __constant_htons(ETH_P_IPV6): /* BRIDGE, NETDEV */
stmt->reject.family = NFPROTO_IPV6;
- datatype_set(stmt->reject.expr, &icmpv6_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmpv6_code_type);
break;
default:
break;
}
- if (payload_dependency_exists(&rctx->pdctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(&rctx->pdctx,
+ if (payload_dependency_exists(&dl->pdctx, PROTO_BASE_NETWORK_HDR))
+ payload_dependency_release(&dl->pdctx,
PROTO_BASE_NETWORK_HDR);
break;
default:
@@ -2791,23 +3140,24 @@ static bool expr_may_merge_range(struct expr *expr, struct expr *prev,
static void expr_postprocess_range(struct rule_pp_ctx *ctx, enum ops op)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct stmt *nstmt, *stmt = ctx->stmt;
struct expr *nexpr, *rel;
- nexpr = range_expr_alloc(&ctx->pdctx.prev->location,
- expr_clone(ctx->pdctx.prev->expr->right),
+ nexpr = range_expr_alloc(&dl->pdctx.prev->location,
+ expr_clone(dl->pdctx.prev->expr->right),
expr_clone(stmt->expr->right));
expr_set_type(nexpr, stmt->expr->right->dtype,
stmt->expr->right->byteorder);
- rel = relational_expr_alloc(&ctx->pdctx.prev->location, op,
+ rel = relational_expr_alloc(&dl->pdctx.prev->location, op,
expr_clone(stmt->expr->left), nexpr);
nstmt = expr_stmt_alloc(&stmt->location, rel);
list_add_tail(&nstmt->list, &stmt->list);
- list_del(&ctx->pdctx.prev->list);
- stmt_free(ctx->pdctx.prev);
+ list_del(&dl->pdctx.prev->list);
+ stmt_free(dl->pdctx.prev);
list_del(&stmt->list);
stmt_free(stmt);
@@ -2816,26 +3166,28 @@ static void expr_postprocess_range(struct rule_pp_ctx *ctx, enum ops op)
static void stmt_expr_postprocess(struct rule_pp_ctx *ctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
enum ops op;
expr_postprocess(ctx, &ctx->stmt->expr);
- if (ctx->pdctx.prev && ctx->stmt &&
- ctx->stmt->ops->type == ctx->pdctx.prev->ops->type &&
- expr_may_merge_range(ctx->stmt->expr, ctx->pdctx.prev->expr, &op))
+ if (dl->pdctx.prev && ctx->stmt &&
+ ctx->stmt->type == dl->pdctx.prev->type &&
+ expr_may_merge_range(ctx->stmt->expr, dl->pdctx.prev->expr, &op))
expr_postprocess_range(ctx, op);
}
static void stmt_payload_binop_pp(struct rule_pp_ctx *ctx, struct expr *binop)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct expr *payload = binop->left;
struct expr *mask = binop->right;
unsigned int shift;
assert(payload->etype == EXPR_PAYLOAD);
- if (payload_expr_trim(payload, mask, &ctx->pctx, &shift)) {
+ if (payload_expr_trim(payload, mask, &dl->pctx, &shift)) {
binop_adjust(binop, mask, shift);
- payload_expr_complete(payload, &ctx->pctx);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(mask, payload->dtype,
payload->byteorder);
}
@@ -2883,7 +3235,8 @@ static void stmt_payload_binop_pp(struct rule_pp_ctx *ctx, struct expr *binop)
* decoding changed '(payload & mask) ^ bits_to_set' into
* 'payload | bits_to_set', discarding the redundant "& 0xfff...".
*/
-static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
+static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx,
+ const struct proto_ctx *pctx)
{
struct expr *expr, *binop, *payload, *value, *mask;
struct stmt *stmt = ctx->stmt;
@@ -2896,6 +3249,7 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
switch (expr->left->etype) {
case EXPR_BINOP: {/* I? */
+ unsigned int shift = 0;
mpz_t tmp;
if (expr->op != OP_OR)
@@ -2929,13 +3283,18 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
mpz_set(mask->value, bitmask);
mpz_clear(bitmask);
- binop_postprocess(ctx, expr, &expr->left);
- if (!payload_is_known(payload)) {
+ if (!binop_postprocess(ctx, expr, &expr->left) &&
+ !payload_is_known(payload) &&
+ !payload_expr_trim_force(payload,
+ mask, &shift)) {
mpz_set(mask->value, tmp);
mpz_clear(tmp);
return;
}
+ if (shift)
+ mpz_rshift_ui(value->value, shift);
+
mpz_clear(tmp);
expr_free(stmt->payload.expr);
stmt->payload.expr = expr_get(payload);
@@ -2944,41 +3303,67 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
break;
}
case EXPR_PAYLOAD: /* II? */
- value = expr->right;
- if (value->etype != EXPR_VALUE)
+ payload = expr->left;
+ mask = expr->right;
+
+ if (mask->etype != EXPR_VALUE)
+ return;
+
+ if (!payload_expr_cmp(stmt->payload.expr, payload))
return;
switch (expr->op) {
- case OP_AND: /* IIa */
- payload = expr->left;
+ case OP_AND: { /* IIa */
+ unsigned int shift_unused;
+ mpz_t tmp;
+
+ if (stmt_payload_expr_trim(stmt, pctx))
+ return;
+
+ mpz_init(tmp);
+ mpz_set(tmp, mask->value);
+
mpz_init_bitmask(bitmask, payload->len);
- mpz_xor(bitmask, bitmask, value->value);
- mpz_set(value->value, bitmask);
+ mpz_xor(bitmask, bitmask, mask->value);
+ mpz_set(mask->value, bitmask);
mpz_clear(bitmask);
- break;
- case OP_OR: /* IIb */
- break;
- default: /* No idea */
- return;
- }
- stmt_payload_binop_pp(ctx, expr);
- if (!payload_is_known(expr->left))
- return;
+ stmt_payload_binop_pp(ctx, expr);
+ if (!payload_is_known(expr->left) &&
+ !payload_expr_trim_force(expr->left, mask, &shift_unused)) {
+ mpz_set(mask->value, tmp);
+ mpz_clear(tmp);
+ return;
+ }
- expr_free(stmt->payload.expr);
+ mpz_clear(tmp);
- switch (expr->op) {
- case OP_AND:
- /* Mask was used to match payload, i.e.
- * user asked to set zero value.
+ /* Mask was used to match payload, i.e. user asked to
+ * clear the payload expression.
+ * The "mask" value becomes new stmt->payload.value
+ * so set this to 0.
+ * Also the reason why &shift_unused is ignored.
*/
- mpz_set_ui(value->value, 0);
+ mpz_set_ui(mask->value, 0);
break;
- default:
+ }
+ case OP_OR: /* IIb */
+ stmt_payload_binop_pp(ctx, expr);
+ if (stmt_payload_expr_trim(stmt, pctx))
+ return;
+ if (!payload_is_known(expr->left))
+ return;
break;
+ case OP_XOR:
+ if (stmt_payload_expr_trim(stmt, pctx))
+ return;
+
+ return;
+ default: /* No idea what to do */
+ return;
}
+ expr_free(stmt->payload.expr);
stmt->payload.expr = expr_get(expr->left);
stmt->payload.val = expr_get(expr->right);
expr_free(expr);
@@ -2990,17 +3375,19 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
static void stmt_payload_postprocess(struct rule_pp_ctx *ctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct stmt *stmt = ctx->stmt;
+ payload_expr_complete(stmt->payload.expr, &dl->pctx);
+ if (!payload_is_known(stmt->payload.expr))
+ stmt_payload_binop_postprocess(ctx, &dl->pctx);
+
expr_postprocess(ctx, &stmt->payload.expr);
expr_set_type(stmt->payload.val,
stmt->payload.expr->dtype,
stmt->payload.expr->byteorder);
- if (!payload_is_known(stmt->payload.expr))
- stmt_payload_binop_postprocess(ctx);
-
expr_postprocess(ctx, &stmt->payload.val);
}
@@ -3037,19 +3424,75 @@ rule_maybe_reset_payload_deps(struct payload_dep_ctx *pdctx, enum stmt_types t)
payload_dependency_reset(pdctx);
}
+static bool has_inner_desc(const struct expr *expr)
+{
+ struct expr *i;
+
+ switch (expr->etype) {
+ case EXPR_BINOP:
+ return has_inner_desc(expr->left);
+ case EXPR_CONCAT:
+ list_for_each_entry(i, &expr->expressions, list) {
+ if (has_inner_desc(i))
+ return true;
+ }
+ break;
+ case EXPR_META:
+ return expr->meta.inner_desc;
+ case EXPR_PAYLOAD:
+ return expr->payload.inner_desc;
+ case EXPR_SET_ELEM:
+ return has_inner_desc(expr->key);
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static struct dl_proto_ctx *rule_update_dl_proto_ctx(struct rule_pp_ctx *rctx)
+{
+ const struct stmt *stmt = rctx->stmt;
+ bool inner = false;
+
+ switch (stmt->type) {
+ case STMT_EXPRESSION:
+ if (has_inner_desc(stmt->expr->left))
+ inner = true;
+ break;
+ case STMT_SET:
+ if (has_inner_desc(stmt->set.key))
+ inner = true;
+ break;
+ default:
+ break;
+ }
+
+ if (inner)
+ rctx->dl = &rctx->_dl[1];
+ else
+ rctx->dl = &rctx->_dl[0];
+
+ return rctx->dl;
+}
+
static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
{
struct stmt *stmt, *next;
+ struct dl_proto_ctx *dl;
struct rule_pp_ctx rctx;
struct expr *expr;
memset(&rctx, 0, sizeof(rctx));
- proto_ctx_init(&rctx.pctx, rule->handle.family, ctx->debug_mask);
+ proto_ctx_init(&rctx._dl[0].pctx, rule->handle.family, ctx->debug_mask, false);
+ /* use NFPROTO_BRIDGE to set up proto_eth as base protocol. */
+ proto_ctx_init(&rctx._dl[1].pctx, NFPROTO_BRIDGE, ctx->debug_mask, true);
list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
- enum stmt_types type = stmt->ops->type;
+ enum stmt_types type = stmt->type;
rctx.stmt = stmt;
+ dl = rule_update_dl_proto_ctx(&rctx);
switch (type) {
case STMT_EXPRESSION:
@@ -3080,16 +3523,14 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
case STMT_NAT:
if (stmt->nat.addr != NULL)
expr_postprocess(&rctx, &stmt->nat.addr);
- if (stmt->nat.proto != NULL) {
- payload_dependency_reset(&rctx.pdctx);
+ if (stmt->nat.proto != NULL)
expr_postprocess(&rctx, &stmt->nat.proto);
- }
break;
case STMT_TPROXY:
if (stmt->tproxy.addr)
expr_postprocess(&rctx, &stmt->tproxy.addr);
if (stmt->tproxy.port) {
- payload_dependency_reset(&rctx.pdctx);
+ payload_dependency_reset(&dl->pdctx);
expr_postprocess(&rctx, &stmt->tproxy.port);
}
break;
@@ -3127,9 +3568,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
break;
}
- rctx.pdctx.prev = rctx.stmt;
+ dl->pdctx.prev = rctx.stmt;
- rule_maybe_reset_payload_deps(&rctx.pdctx, type);
+ rule_maybe_reset_payload_deps(&dl->pdctx, type);
}
}
@@ -3195,7 +3636,10 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
pctx->rule = rule_alloc(&netlink_location, &h);
pctx->table = table_cache_find(&ctx->nft->cache.table_cache,
h.table.name, h.family);
- assert(pctx->table != NULL);
+ if (!pctx->table) {
+ errno = ENOENT;
+ return NULL;
+ }
pctx->rule->comment = nftnl_rule_get_comment(nlr);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index c8bbcb74..5f73183b 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -9,10 +9,11 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_log.h>
-#include <string.h>
#include <rule.h>
#include <statement.h>
#include <expression.h>
@@ -178,9 +179,8 @@ static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx, nle, &expr->location);
}
-static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
- const struct expr *expr,
- enum nft_registers dreg)
+static struct nftnl_expr *
+__netlink_gen_payload(const struct expr *expr, enum nft_registers dreg)
{
struct nftnl_expr *nle;
@@ -193,6 +193,72 @@ static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
+ return nle;
+}
+
+static struct nftnl_expr *
+__netlink_gen_meta(const struct expr *expr, enum nft_registers dreg)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("meta");
+ netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
+
+ return nle;
+}
+
+static struct nftnl_expr *netlink_gen_inner_expr(const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct expr *_expr = (struct expr *)expr;
+ struct nftnl_expr *nle;
+
+ switch (expr->etype) {
+ case EXPR_PAYLOAD:
+ if (expr->payload.base == NFT_PAYLOAD_INNER_HEADER + 1)
+ _expr->payload.base = NFT_PAYLOAD_TUN_HEADER + 1;
+
+ nle = __netlink_gen_payload(expr, dreg);
+ break;
+ case EXPR_META:
+ nle = __netlink_gen_meta(expr, dreg);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return nle;
+}
+
+static void netlink_gen_inner(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg,
+ const struct proto_desc *desc)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("inner");
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_HDRSIZE, desc->inner.hdrsize);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_FLAGS, desc->inner.flags);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_TYPE, desc->inner.type);
+ nftnl_expr_set(nle, NFTNL_EXPR_INNER_EXPR, netlink_gen_inner_expr(expr, dreg), 0);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+}
+
+static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct nftnl_expr *nle;
+
+ if (expr->payload.inner_desc) {
+ netlink_gen_inner(ctx, expr, dreg, expr->payload.inner_desc);
+ return;
+ }
+
+ nle = __netlink_gen_payload(expr, dreg);
nft_rule_add_expr(ctx, nle, &expr->location);
}
@@ -221,9 +287,12 @@ static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
{
struct nftnl_expr *nle;
- nle = alloc_nft_expr("meta");
- netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
+ if (expr->meta.inner_desc) {
+ netlink_gen_inner(ctx, expr, dreg, expr->meta.inner_desc);
+ return;
+ }
+
+ nle = __netlink_gen_meta(expr, dreg);
nft_rule_add_expr(ctx, nle, &expr->location);
}
@@ -391,7 +460,8 @@ static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx,
mpz_init(mask);
mpz_prefixmask(mask, expr->right->len, expr->right->prefix_len);
netlink_gen_raw_data(mask, expr->right->byteorder,
- expr->right->len / BITS_PER_BYTE, &nld);
+ div_round_up(expr->right->len, BITS_PER_BYTE),
+ &nld);
mpz_clear(mask);
zero.len = nld.len;
@@ -423,9 +493,11 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
switch (expr->op) {
case OP_NEQ:
+ case OP_EQ:
+ case OP_IMPLICIT:
nle = alloc_nft_expr("range");
netlink_put_register(nle, NFTNL_EXPR_RANGE_SREG, sreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_RANGE_OP, NFT_RANGE_NEQ);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_RANGE_OP, netlink_gen_cmp_op(expr->op));
netlink_gen_data(range->left, &nld);
nftnl_expr_set(nle, NFTNL_EXPR_RANGE_FROM_DATA,
nld.value, nld.len);
@@ -434,24 +506,6 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
nld.value, nld.len);
nft_rule_add_expr(ctx, nle, &expr->location);
break;
- case OP_EQ:
- case OP_IMPLICIT:
- nle = alloc_nft_expr("cmp");
- netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP,
- netlink_gen_cmp_op(OP_GTE));
- netlink_gen_data(range->left, &nld);
- nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nft_rule_add_expr(ctx, nle, &expr->location);
-
- nle = alloc_nft_expr("cmp");
- netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP,
- netlink_gen_cmp_op(OP_LTE));
- netlink_gen_data(range->right, &nld);
- nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nft_rule_add_expr(ctx, nle, &expr->location);
- break;
default:
BUG("invalid range operation %u\n", expr->op);
@@ -594,9 +648,9 @@ static void combine_binop(mpz_t mask, mpz_t xor, const mpz_t m, const mpz_t x)
mpz_and(mask, mask, m);
}
-static void netlink_gen_shift(struct netlink_linearize_ctx *ctx,
- const struct expr *expr,
- enum nft_registers dreg)
+static void netlink_gen_bitwise_shift(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg)
{
enum nft_bitwise_ops op = expr->op == OP_LSHIFT ?
NFT_BITWISE_LSHIFT : NFT_BITWISE_RSHIFT;
@@ -621,14 +675,14 @@ static void netlink_gen_shift(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx, nle, &expr->location);
}
-static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
- const struct expr *expr,
- enum nft_registers dreg)
+static void netlink_gen_bitwise_mask_xor(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg)
{
+ struct expr *binops[NFT_MAX_EXPR_RECURSION];
struct nftnl_expr *nle;
struct nft_data_linearize nld;
struct expr *left, *i;
- struct expr *binops[16];
mpz_t mask, xor, val, tmp;
unsigned int len;
int n = 0;
@@ -639,17 +693,19 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
mpz_init(tmp);
binops[n++] = left = (struct expr *) expr;
- while (left->etype == EXPR_BINOP && left->left != NULL &&
- (left->op == OP_AND || left->op == OP_OR || left->op == OP_XOR))
+ while (left->etype == EXPR_BINOP && left->left != NULL && expr_is_constant(left->right) &&
+ (left->op == OP_AND || left->op == OP_OR || left->op == OP_XOR)) {
+ if (n == array_size(binops))
+ BUG("NFT_MAX_EXPR_RECURSION limit reached");
binops[n++] = left = left->left;
- n--;
+ }
- netlink_gen_expr(ctx, binops[n--], dreg);
+ netlink_gen_expr(ctx, binops[--n], dreg);
mpz_bitmask(mask, expr->len);
mpz_set_ui(xor, 0);
- for (; n >= 0; n--) {
- i = binops[n];
+ while (n > 0) {
+ i = binops[--n];
mpz_set(val, i->right->value);
switch (i->op) {
@@ -675,7 +731,7 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("bitwise");
netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, dreg);
netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, dreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_OP, NFT_BITWISE_BOOL);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_OP, NFT_BITWISE_MASK_XOR);
nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
netlink_gen_raw_data(mask, expr->byteorder, len, &nld);
@@ -691,6 +747,45 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx, nle, &expr->location);
}
+static void netlink_gen_bitwise_bool(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg)
+{
+ enum nft_registers sreg2;
+ struct nftnl_expr *nle;
+ unsigned int len;
+
+ nle = alloc_nft_expr("bitwise");
+
+ switch (expr->op) {
+ case OP_XOR:
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_OP, NFT_BITWISE_XOR);
+ break;
+ case OP_AND:
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_OP, NFT_BITWISE_AND);
+ break;
+ case OP_OR:
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_OP, NFT_BITWISE_OR);
+ break;
+ default:
+ BUG("invalid binary operation %u\n", expr->op);
+ }
+
+ netlink_gen_expr(ctx, expr->left, dreg);
+ netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, dreg);
+ netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, dreg);
+
+ sreg2 = get_register(ctx, expr->right);
+ netlink_gen_expr(ctx, expr->right, sreg2);
+ netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG2, sreg2);
+ release_register(ctx, expr->right);
+
+ len = div_round_up(expr->len, BITS_PER_BYTE);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
+
+ nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
@@ -698,10 +793,13 @@ static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
switch(expr->op) {
case OP_LSHIFT:
case OP_RSHIFT:
- netlink_gen_shift(ctx, expr, dreg);
+ netlink_gen_bitwise_shift(ctx, expr, dreg);
break;
default:
- netlink_gen_bitwise(ctx, expr, dreg);
+ if (expr_is_constant(expr->right))
+ netlink_gen_bitwise_mask_xor(ctx, expr, dreg);
+ else
+ netlink_gen_bitwise_bool(ctx, expr, dreg);
break;
}
}
@@ -725,6 +823,8 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx,
struct nftnl_expr *nle;
int byte_size;
+ assert(div_round_up(expr->arg->len, BITS_PER_BYTE) != 1);
+
if ((expr->arg->len % 64) == 0)
byte_size = 8;
else if ((expr->arg->len % 32) == 0)
@@ -738,7 +838,7 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx,
netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_SREG, dreg);
netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_DREG, dreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_LEN,
- expr->len / BITS_PER_BYTE);
+ div_round_up(expr->len, BITS_PER_BYTE));
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_SIZE,
byte_size);
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_OP,
@@ -933,9 +1033,20 @@ static struct nftnl_expr *netlink_gen_quota_stmt(const struct stmt *stmt)
return nle;
}
+static struct nftnl_expr *netlink_gen_last_stmt(const struct stmt *stmt)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("last");
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_LAST_SET, stmt->last.set);
+ nftnl_expr_set_u64(nle, NFTNL_EXPR_LAST_MSECS, stmt->last.used);
+
+ return nle;
+}
+
struct nftnl_expr *netlink_gen_stmt_stateful(const struct stmt *stmt)
{
- switch (stmt->ops->type) {
+ switch (stmt->type) {
case STMT_CONNLIMIT:
return netlink_gen_connlimit_stmt(stmt);
case STMT_COUNTER:
@@ -944,8 +1055,10 @@ struct nftnl_expr *netlink_gen_stmt_stateful(const struct stmt *stmt)
return netlink_gen_limit_stmt(stmt);
case STMT_QUOTA:
return netlink_gen_quota_stmt(stmt);
+ case STMT_LAST:
+ return netlink_gen_last_stmt(stmt);
default:
- BUG("unknown stateful statement type %s\n", stmt->ops->name);
+ BUG("unknown stateful statement type %d\n", stmt->type);
}
}
@@ -1030,6 +1143,8 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
}
if ((expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
payload_needs_l4csum_update_pseudohdr(expr, desc)) ||
+ (expr->payload.base == PROTO_BASE_TRANSPORT_HDR && desc &&
+ desc == &proto_udp) ||
expr->payload.base == PROTO_BASE_INNER_HDR)
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS,
NFT_PAYLOAD_L4CSUM_PSEUDOHDR);
@@ -1059,12 +1174,9 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
struct nftnl_expr *nle;
nle = alloc_nft_expr("log");
- if (stmt->log.prefix != NULL) {
- char prefix[NF_LOG_PREFIXLEN] = {};
+ if (stmt->log.prefix != NULL)
+ nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, stmt->log.prefix);
- 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);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -1502,14 +1614,14 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
int num_stmts = 0;
struct stmt *this;
- sreg_key = get_register(ctx, stmt->map.key);
- netlink_gen_expr(ctx, stmt->map.key, sreg_key);
+ sreg_key = get_register(ctx, stmt->map.key->key);
+ netlink_gen_expr(ctx, stmt->map.key->key, sreg_key);
- sreg_data = get_register(ctx, stmt->map.data);
- netlink_gen_expr(ctx, stmt->map.data, sreg_data);
+ sreg_data = get_register(ctx, stmt->map.data->key);
+ netlink_gen_expr(ctx, stmt->map.data->key, sreg_data);
- release_register(ctx, stmt->map.key);
- release_register(ctx, stmt->map.data);
+ release_register(ctx, stmt->map.key->key);
+ release_register(ctx, stmt->map.data->key);
nle = alloc_nft_expr("dynset");
netlink_put_register(nle, NFTNL_EXPR_DYNSET_SREG_KEY, sreg_key);
@@ -1520,6 +1632,10 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
nft_rule_add_expr(ctx, nle, &stmt->location);
+ if (stmt->map.key->timeout > 0)
+ nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT,
+ stmt->map.key->timeout);
+
list_for_each_entry(this, &stmt->map.stmt_list, list)
num_stmts++;
@@ -1578,7 +1694,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
{
struct nftnl_expr *nle;
- switch (stmt->ops->type) {
+ switch (stmt->type) {
case STMT_EXPRESSION:
return netlink_gen_expr(ctx, stmt->expr, NFT_REG_VERDICT);
case STMT_VERDICT:
@@ -1615,6 +1731,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
case STMT_COUNTER:
case STMT_LIMIT:
case STMT_QUOTA:
+ case STMT_LAST:
nle = netlink_gen_stmt_stateful(stmt);
nft_rule_add_expr(ctx, nle, &stmt->location);
break;
@@ -1631,7 +1748,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
case STMT_OPTSTRIP:
return netlink_gen_optstrip_stmt(ctx, stmt);
default:
- BUG("unknown statement type %s\n", stmt->ops->name);
+ BUG("unknown statement type %d\n", stmt->type);
}
}
@@ -1656,9 +1773,9 @@ void netlink_linearize_fini(struct netlink_linearize_ctx *lctx)
for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) {
list_for_each_entry_safe(eloc, next, &lctx->expr_loc_htable[i], hlist)
- xfree(eloc);
+ free(eloc);
}
- xfree(lctx->expr_loc_htable);
+ free(lctx->expr_loc_htable);
}
void netlink_linearize_rule(struct netlink_ctx *ctx,
diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c
index 08e978de..20a1bfe7 100644
--- a/src/nfnl_osf.c
+++ b/src/nfnl_osf.c
@@ -19,12 +19,12 @@
* Based on iptables/utils/nfnl_osf.c.
*/
+#include <nft.h>
+
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
#include <time.h>
#include <netinet/ip.h>
diff --git a/src/nftutils.c b/src/nftutils.c
new file mode 100644
index 00000000..ca178aa0
--- /dev/null
+++ b/src/nftutils.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <nft.h>
+
+#include "nftutils.h"
+
+#include <netdb.h>
+
+/* Buffer size used for getprotobynumber_r() and similar. The manual comments
+ * that a buffer of 1024 should be sufficient "for most applications"(??), so
+ * let's double it. It still fits reasonably on the stack, so no need to
+ * choose a smaller one. */
+#define NETDB_BUFSIZE 2048
+
+bool nft_getprotobynumber(int proto, char *out_name, size_t name_len)
+{
+ const struct protoent *result;
+
+#if HAVE_DECL_GETPROTOBYNUMBER_R
+ struct protoent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getprotobynumber_r(proto,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct protoent **) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getprotobynumber(proto);
+#endif
+
+ if (!result)
+ return false;
+
+ if (strlen(result->p_name) >= name_len)
+ return false;
+ strcpy(out_name, result->p_name);
+ return true;
+}
+
+int nft_getprotobyname(const char *name)
+{
+ const struct protoent *result;
+
+#if HAVE_DECL_GETPROTOBYNAME_R
+ struct protoent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getprotobyname_r(name,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct protoent **) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getprotobyname(name);
+#endif
+
+ if (!result)
+ return -1;
+
+ if (result->p_proto < 0 || result->p_proto > UINT8_MAX)
+ return -1;
+ return (uint8_t) result->p_proto;
+}
+
+bool nft_getservbyport(int port, const char *proto, char *out_name, size_t name_len)
+{
+ const struct servent *result;
+
+#if HAVE_DECL_GETSERVBYPORT_R
+ struct servent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getservbyport_r(port,
+ proto,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct servent**) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getservbyport(port, proto);
+#endif
+
+ if (!result)
+ return false;
+
+ if (strlen(result->s_name) >= name_len)
+ return false;
+ strcpy(out_name, result->s_name);
+ return true;
+}
diff --git a/src/nftutils.h b/src/nftutils.h
new file mode 100644
index 00000000..7db56f42
--- /dev/null
+++ b/src/nftutils.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef NFTUTILS_H
+#define NFTUTILS_H
+
+#include <stddef.h>
+
+/* The maximum buffer size for (struct protoent).p_name. It is excessively large,
+ * while still reasonably fitting on the stack. Arbitrarily chosen. */
+#define NFT_PROTONAME_MAXSIZE 1024
+
+bool nft_getprotobynumber(int number, char *out_name, size_t name_len);
+int nft_getprotobyname(const char *name);
+
+/* The maximum buffer size for (struct servent).s_name. It is excessively large,
+ * while still reasonably fitting on the stack. Arbitrarily chosen. */
+#define NFT_SERVNAME_MAXSIZE 1024
+
+bool nft_getservbyport(int port, const char *proto, char *out_name, size_t name_len);
+
+#endif /* NFTUTILS_H */
diff --git a/src/numgen.c b/src/numgen.c
index ea2b2626..3029fa58 100644
--- a/src/numgen.c
+++ b/src/numgen.c
@@ -4,10 +4,12 @@
* Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <datatype.h>
diff --git a/src/optimize.c b/src/optimize.c
index 419a37f2..89ba0d9d 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -11,8 +11,8 @@
* programme.
*/
-#define _GNU_SOURCE
-#include <string.h>
+#include <nft.h>
+
#include <errno.h>
#include <inttypes.h>
#include <nftables.h>
@@ -21,6 +21,7 @@
#include <statement.h>
#include <utils.h>
#include <erec.h>
+#include <linux/netfilter.h>
#define MAX_STMTS 32
@@ -37,6 +38,8 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
{
if (expr_a->etype != expr_b->etype)
return false;
+ if (expr_a->len != expr_b->len)
+ return false;
switch (expr_a->etype) {
case EXPR_PAYLOAD:
@@ -46,6 +49,8 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
return false;
if (expr_a->payload.desc != expr_b->payload.desc)
return false;
+ if (expr_a->payload.inner_desc != expr_b->payload.inner_desc)
+ return false;
if (expr_a->payload.tmpl != expr_b->payload.tmpl)
return false;
break;
@@ -60,6 +65,8 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
return false;
if (expr_a->meta.base != expr_b->meta.base)
return false;
+ if (expr_a->meta.inner_desc != expr_b->meta.inner_desc)
+ return false;
break;
case EXPR_CT:
if (expr_a->ct.key != expr_b->ct.key)
@@ -120,7 +127,19 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
return false;
break;
case EXPR_BINOP:
- return __expr_cmp(expr_a->left, expr_b->left);
+ if (!__expr_cmp(expr_a->left, expr_b->left))
+ return false;
+
+ return __expr_cmp(expr_a->right, expr_b->right);
+ case EXPR_SYMBOL:
+ if (expr_a->symtype != expr_b->symtype)
+ return false;
+ if (expr_a->symtype != SYMBOL_VALUE)
+ return false;
+
+ return !strcmp(expr_a->identifier, expr_b->identifier);
+ case EXPR_VALUE:
+ return !mpz_cmp(expr_a->value, expr_b->value);
default:
return false;
}
@@ -128,16 +147,41 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
return true;
}
+static bool is_bitmask(const struct expr *expr)
+{
+ switch (expr->etype) {
+ case EXPR_BINOP:
+ if (expr->op == OP_OR &&
+ !is_bitmask(expr->left))
+ return false;
+
+ return is_bitmask(expr->right);
+ case EXPR_VALUE:
+ case EXPR_SYMBOL:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static bool stmt_expr_supported(const struct expr *expr)
{
switch (expr->right->etype) {
case EXPR_SYMBOL:
+ case EXPR_RANGE_SYMBOL:
case EXPR_RANGE:
+ case EXPR_RANGE_VALUE:
case EXPR_PREFIX:
case EXPR_SET:
case EXPR_LIST:
case EXPR_VALUE:
return true;
+ case EXPR_BINOP:
+ if (is_bitmask(expr->right))
+ return true;
+ break;
default:
break;
}
@@ -156,10 +200,10 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
{
struct expr *expr_a, *expr_b;
- if (stmt_a->ops->type != stmt_b->ops->type)
+ if (stmt_a->type != stmt_b->type)
return false;
- switch (stmt_a->ops->type) {
+ switch (stmt_a->type) {
case STMT_EXPRESSION:
expr_a = stmt_a->expr;
expr_b = stmt_b->expr;
@@ -212,9 +256,7 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
if (!stmt_a->log.prefix)
return true;
- if (stmt_a->log.prefix->etype != EXPR_VALUE ||
- stmt_b->log.prefix->etype != EXPR_VALUE ||
- mpz_cmp(stmt_a->log.prefix->value, stmt_b->log.prefix->value))
+ if (strcmp(stmt_a->log.prefix, stmt_b->log.prefix))
return false;
break;
case STMT_REJECT:
@@ -229,28 +271,65 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
if (!stmt_a->reject.expr)
return true;
- if (__expr_cmp(stmt_a->reject.expr, stmt_b->reject.expr))
+ if (!__expr_cmp(stmt_a->reject.expr, stmt_b->reject.expr))
return false;
break;
case STMT_NAT:
if (stmt_a->nat.type != stmt_b->nat.type ||
stmt_a->nat.flags != stmt_b->nat.flags ||
stmt_a->nat.family != stmt_b->nat.family ||
- stmt_a->nat.type_flags != stmt_b->nat.type_flags ||
- (stmt_a->nat.addr &&
- stmt_a->nat.addr->etype != EXPR_SYMBOL &&
- stmt_a->nat.addr->etype != EXPR_RANGE) ||
- (stmt_b->nat.addr &&
- stmt_b->nat.addr->etype != EXPR_SYMBOL &&
- stmt_b->nat.addr->etype != EXPR_RANGE) ||
- (stmt_a->nat.proto &&
- stmt_a->nat.proto->etype != EXPR_SYMBOL &&
- stmt_a->nat.proto->etype != EXPR_RANGE) ||
- (stmt_b->nat.proto &&
- stmt_b->nat.proto->etype != EXPR_SYMBOL &&
- stmt_b->nat.proto->etype != EXPR_RANGE))
+ stmt_a->nat.type_flags != stmt_b->nat.type_flags)
return false;
+ switch (stmt_a->nat.type) {
+ case NFT_NAT_SNAT:
+ case NFT_NAT_DNAT:
+ if ((stmt_a->nat.addr &&
+ stmt_a->nat.addr->etype != EXPR_SYMBOL &&
+ stmt_a->nat.addr->etype != EXPR_RANGE) ||
+ (stmt_b->nat.addr &&
+ stmt_b->nat.addr->etype != EXPR_SYMBOL &&
+ stmt_b->nat.addr->etype != EXPR_RANGE) ||
+ (stmt_a->nat.proto &&
+ stmt_a->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_a->nat.proto->etype != EXPR_RANGE) ||
+ (stmt_b->nat.proto &&
+ stmt_b->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_b->nat.proto->etype != EXPR_RANGE))
+ return false;
+ break;
+ case NFT_NAT_MASQ:
+ break;
+ case NFT_NAT_REDIR:
+ if ((stmt_a->nat.proto &&
+ stmt_a->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_a->nat.proto->etype != EXPR_RANGE) ||
+ (stmt_b->nat.proto &&
+ stmt_b->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_b->nat.proto->etype != EXPR_RANGE))
+ return false;
+
+ /* it should be possible to infer implicit redirections
+ * such as:
+ *
+ * tcp dport 1234 redirect
+ * tcp dport 3456 redirect to :7890
+ * merge:
+ * redirect to tcp dport map { 1234 : 1234, 3456 : 7890 }
+ *
+ * currently not implemented.
+ */
+ if (fully_compare &&
+ stmt_a->nat.type == NFT_NAT_REDIR &&
+ stmt_b->nat.type == NFT_NAT_REDIR &&
+ (!!stmt_a->nat.proto ^ !!stmt_b->nat.proto))
+ return false;
+
+ break;
+ default:
+ assert(0);
+ }
+
return true;
default:
/* ... Merging anything else is yet unsupported. */
@@ -281,7 +360,7 @@ static bool stmt_verdict_eq(const struct stmt *stmt_a, const struct stmt *stmt_b
{
struct expr *expr_a, *expr_b;
- assert (stmt_a->ops->type == STMT_VERDICT);
+ assert (stmt_a->type == STMT_VERDICT);
expr_a = stmt_a->expr;
expr_b = stmt_b->expr;
@@ -302,14 +381,14 @@ static bool stmt_type_find(struct optimize_ctx *ctx, const struct stmt *stmt)
uint32_t i;
for (i = 0; i < ctx->num_stmts; i++) {
- if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ if (ctx->stmt[i]->type == STMT_INVALID)
unsupported_exists = true;
if (__stmt_type_eq(stmt, ctx->stmt[i], false))
return true;
}
- switch (stmt->ops->type) {
+ switch (stmt->type) {
case STMT_EXPRESSION:
case STMT_VERDICT:
case STMT_COUNTER:
@@ -328,13 +407,9 @@ static bool stmt_type_find(struct optimize_ctx *ctx, const struct stmt *stmt)
return false;
}
-static struct stmt_ops unsupported_stmt_ops = {
- .type = STMT_INVALID,
- .name = "unsupported",
-};
-
static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
{
+ const struct stmt_ops *ops;
struct stmt *stmt, *clone;
list_for_each_entry(stmt, &rule->stmts, list) {
@@ -344,18 +419,20 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
/* No refcounter available in statement objects, clone it to
* to store in the array of selectors.
*/
- clone = stmt_alloc(&internal_location, stmt->ops);
- switch (stmt->ops->type) {
+ ops = stmt_ops(stmt);
+ clone = stmt_alloc(&internal_location, ops);
+ switch (stmt->type) {
case STMT_EXPRESSION:
if (stmt->expr->op != OP_IMPLICIT &&
stmt->expr->op != OP_EQ) {
- clone->ops = &unsupported_stmt_ops;
+ clone->type = STMT_INVALID;
break;
}
if (stmt->expr->left->etype == EXPR_CONCAT) {
- clone->ops = &unsupported_stmt_ops;
+ clone->type = STMT_INVALID;
break;
}
+ /* fall-through */
case STMT_VERDICT:
clone->expr = expr_get(stmt->expr);
break;
@@ -365,9 +442,18 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
case STMT_LOG:
memcpy(&clone->log, &stmt->log, sizeof(clone->log));
if (stmt->log.prefix)
- clone->log.prefix = expr_get(stmt->log.prefix);
+ clone->log.prefix = xstrdup(stmt->log.prefix);
break;
case STMT_NAT:
+ if ((stmt->nat.addr &&
+ (stmt->nat.addr->etype == EXPR_MAP ||
+ stmt->nat.addr->etype == EXPR_VARIABLE)) ||
+ (stmt->nat.proto &&
+ (stmt->nat.proto->etype == EXPR_MAP ||
+ stmt->nat.proto->etype == EXPR_VARIABLE))) {
+ clone->type = STMT_INVALID;
+ break;
+ }
clone->nat.type = stmt->nat.type;
clone->nat.family = stmt->nat.family;
if (stmt->nat.addr)
@@ -385,7 +471,7 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
clone->reject.family = stmt->reject.family;
break;
default:
- clone->ops = &unsupported_stmt_ops;
+ clone->type = STMT_INVALID;
break;
}
@@ -402,7 +488,7 @@ static int unsupported_in_stmt_matrix(const struct optimize_ctx *ctx)
uint32_t i;
for (i = 0; i < ctx->num_stmts; i++) {
- if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ if (ctx->stmt[i]->type == STMT_INVALID)
return i;
}
/* this should not happen. */
@@ -422,7 +508,7 @@ static int cmd_stmt_find_in_stmt_matrix(struct optimize_ctx *ctx, struct stmt *s
}
static struct stmt unsupported_stmt = {
- .ops = &unsupported_stmt_ops,
+ .type = STMT_INVALID,
};
static void rule_build_stmt_matrix_stmts(struct optimize_ctx *ctx,
@@ -449,7 +535,7 @@ static int stmt_verdict_find(const struct optimize_ctx *ctx)
uint32_t i;
for (i = 0; i < ctx->num_stmts; i++) {
- if (ctx->stmt[i]->ops->type != STMT_VERDICT)
+ if (ctx->stmt[i]->type != STMT_VERDICT)
continue;
return i;
@@ -465,6 +551,8 @@ struct merge {
/* statements to be merged (index relative to statement matrix) */
uint32_t stmt[MAX_STMTS];
uint32_t num_stmts;
+ /* merge has been invalidated */
+ bool skip;
};
static void merge_expr_stmts(const struct optimize_ctx *ctx,
@@ -516,7 +604,7 @@ static void merge_verdict_stmts(const struct optimize_ctx *ctx,
for (i = from + 1; i <= to; i++) {
stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
- switch (stmt_b->ops->type) {
+ switch (stmt_b->type) {
case STMT_VERDICT:
switch (stmt_b->expr->etype) {
case EXPR_MAP:
@@ -538,7 +626,7 @@ static void merge_stmts(const struct optimize_ctx *ctx,
{
struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
- switch (stmt_a->ops->type) {
+ switch (stmt_a->type) {
case STMT_EXPRESSION:
merge_expr_stmts(ctx, from, to, merge, stmt_a);
break;
@@ -550,12 +638,80 @@ static void merge_stmts(const struct optimize_ctx *ctx,
}
}
+static void __merge_concat(const struct optimize_ctx *ctx, uint32_t i,
+ const struct merge *merge, struct list_head *concat_list)
+{
+ struct expr *concat, *next, *expr, *concat_clone, *clone;
+ LIST_HEAD(pending_list);
+ struct stmt *stmt_a;
+ uint32_t k;
+
+ concat = concat_expr_alloc(&internal_location);
+ list_add(&concat->list, concat_list);
+
+ for (k = 0; k < merge->num_stmts; k++) {
+ list_for_each_entry_safe(concat, next, concat_list, list) {
+ stmt_a = ctx->stmt_matrix[i][merge->stmt[k]];
+ switch (stmt_a->expr->right->etype) {
+ case EXPR_SET:
+ list_for_each_entry(expr, &stmt_a->expr->right->expressions, list) {
+ concat_clone = expr_clone(concat);
+ clone = expr_clone(expr->key);
+ compound_expr_add(concat_clone, clone);
+ list_add_tail(&concat_clone->list, &pending_list);
+ }
+ list_del(&concat->list);
+ expr_free(concat);
+ break;
+ case EXPR_SYMBOL:
+ case EXPR_VALUE:
+ case EXPR_PREFIX:
+ case EXPR_RANGE_SYMBOL:
+ case EXPR_RANGE:
+ case EXPR_RANGE_VALUE:
+ clone = expr_clone(stmt_a->expr->right);
+ compound_expr_add(concat, clone);
+ break;
+ case EXPR_LIST:
+ list_for_each_entry(expr, &stmt_a->expr->right->expressions, list) {
+ concat_clone = expr_clone(concat);
+ clone = expr_clone(expr);
+ compound_expr_add(concat_clone, clone);
+ list_add_tail(&concat_clone->list, &pending_list);
+ }
+ list_del(&concat->list);
+ expr_free(concat);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ list_splice_init(&pending_list, concat_list);
+ }
+}
+
+static void __merge_concat_stmts(const struct optimize_ctx *ctx, uint32_t i,
+ const struct merge *merge, struct expr *set)
+{
+ struct expr *concat, *next, *elem;
+ LIST_HEAD(concat_list);
+
+ __merge_concat(ctx, i, merge, &concat_list);
+
+ list_for_each_entry_safe(concat, next, &concat_list, list) {
+ list_del(&concat->list);
+ elem = set_elem_expr_alloc(&internal_location, concat);
+ compound_expr_add(set, elem);
+ }
+}
+
static void merge_concat_stmts(const struct optimize_ctx *ctx,
uint32_t from, uint32_t to,
const struct merge *merge)
{
- struct expr *concat, *elem, *set;
struct stmt *stmt, *stmt_a;
+ struct expr *concat, *set;
uint32_t i, k;
stmt = ctx->stmt_matrix[from][merge->stmt[0]];
@@ -573,15 +729,9 @@ static void merge_concat_stmts(const struct optimize_ctx *ctx,
set = set_expr_alloc(&internal_location, NULL);
set->set_flags |= NFT_SET_ANONYMOUS;
- for (i = from; i <= to; i++) {
- concat = concat_expr_alloc(&internal_location);
- for (k = 0; k < merge->num_stmts; k++) {
- stmt_a = ctx->stmt_matrix[i][merge->stmt[k]];
- compound_expr_add(concat, expr_get(stmt_a->expr->right));
- }
- elem = set_elem_expr_alloc(&internal_location, concat);
- compound_expr_add(set, elem);
- }
+ for (i = from; i <= to; i++)
+ __merge_concat_stmts(ctx, i, merge, set);
+
expr_free(stmt->expr->right);
stmt->expr->right = set;
@@ -592,33 +742,52 @@ static void merge_concat_stmts(const struct optimize_ctx *ctx,
}
}
-static void build_verdict_map(struct expr *expr, struct stmt *verdict, struct expr *set)
+static void build_verdict_map(struct expr *expr, struct stmt *verdict,
+ struct expr *set, struct stmt *counter)
{
struct expr *item, *elem, *mapping;
+ struct stmt *counter_elem;
switch (expr->etype) {
case EXPR_LIST:
list_for_each_entry(item, &expr->expressions, list) {
elem = set_elem_expr_alloc(&internal_location, expr_get(item));
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
+
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
}
+ stmt_free(counter);
break;
case EXPR_SET:
list_for_each_entry(item, &expr->expressions, list) {
elem = set_elem_expr_alloc(&internal_location, expr_get(item->key));
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
+
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
}
+ stmt_free(counter);
break;
case EXPR_PREFIX:
+ case EXPR_RANGE_SYMBOL:
case EXPR_RANGE:
+ case EXPR_RANGE_VALUE:
case EXPR_VALUE:
case EXPR_SYMBOL:
case EXPR_CONCAT:
elem = set_elem_expr_alloc(&internal_location, expr_get(expr));
+ if (counter)
+ list_add_tail(&counter->list, &elem->stmt_list);
+
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
@@ -640,13 +809,33 @@ static void remove_counter(const struct optimize_ctx *ctx, uint32_t from)
if (!stmt)
continue;
- if (stmt->ops->type == STMT_COUNTER) {
+ if (stmt->type == STMT_COUNTER) {
list_del(&stmt->list);
stmt_free(stmt);
}
}
}
+static struct stmt *zap_counter(const struct optimize_ctx *ctx, uint32_t from)
+{
+ struct stmt *stmt;
+ uint32_t i;
+
+ /* remove counter statement */
+ for (i = 0; i < ctx->num_stmts; i++) {
+ stmt = ctx->stmt_matrix[from][i];
+ if (!stmt)
+ continue;
+
+ if (stmt->type == STMT_COUNTER) {
+ list_del(&stmt->list);
+ return stmt;
+ }
+ }
+
+ return NULL;
+}
+
static void merge_stmts_vmap(const struct optimize_ctx *ctx,
uint32_t from, uint32_t to,
const struct merge *merge)
@@ -654,31 +843,33 @@ static void merge_stmts_vmap(const struct optimize_ctx *ctx,
struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
struct stmt *stmt_b, *verdict_a, *verdict_b, *stmt;
struct expr *expr_a, *expr_b, *expr, *left, *set;
+ struct stmt *counter;
uint32_t i;
int k;
k = stmt_verdict_find(ctx);
assert(k >= 0);
- verdict_a = ctx->stmt_matrix[from][k];
set = set_expr_alloc(&internal_location, NULL);
set->set_flags |= NFT_SET_ANONYMOUS;
expr_a = stmt_a->expr->right;
- build_verdict_map(expr_a, verdict_a, set);
+ verdict_a = ctx->stmt_matrix[from][k];
+ counter = zap_counter(ctx, from);
+ build_verdict_map(expr_a, verdict_a, set, counter);
+
for (i = from + 1; i <= to; i++) {
stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
expr_b = stmt_b->expr->right;
verdict_b = ctx->stmt_matrix[i][k];
-
- build_verdict_map(expr_b, verdict_b, set);
+ counter = zap_counter(ctx, i);
+ build_verdict_map(expr_b, verdict_b, set, counter);
}
left = expr_get(stmt_a->expr->left);
expr = map_expr_alloc(&internal_location, left, set);
stmt = verdict_stmt_alloc(&internal_location, expr);
- remove_counter(ctx, from);
list_add(&stmt->list, &stmt_a->list);
list_del(&stmt_a->list);
stmt_free(stmt_a);
@@ -686,14 +877,40 @@ static void merge_stmts_vmap(const struct optimize_ctx *ctx,
stmt_free(verdict_a);
}
+static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
+ uint32_t i, const struct merge *merge,
+ struct expr *set, struct stmt *verdict)
+{
+ struct expr *concat, *next, *elem, *mapping;
+ struct stmt *counter, *counter_elem;
+ LIST_HEAD(concat_list);
+
+ counter = zap_counter(ctx, i);
+ __merge_concat(ctx, i, merge, &concat_list);
+
+ list_for_each_entry_safe(concat, next, &concat_list, list) {
+ list_del(&concat->list);
+ elem = set_elem_expr_alloc(&internal_location, concat);
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
+
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+ stmt_free(counter);
+}
+
static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
uint32_t from, uint32_t to,
const struct merge *merge)
{
struct stmt *orig_stmt = ctx->stmt_matrix[from][merge->stmt[0]];
- struct expr *concat_a, *concat_b, *expr, *set;
- struct stmt *stmt, *stmt_a, *stmt_b, *verdict;
- uint32_t i, j;
+ struct stmt *stmt, *stmt_a, *verdict;
+ struct expr *concat_a, *expr, *set;
+ uint32_t i;
int k;
k = stmt_verdict_find(ctx);
@@ -711,21 +928,13 @@ static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
set->set_flags |= NFT_SET_ANONYMOUS;
for (i = from; i <= to; i++) {
- concat_b = concat_expr_alloc(&internal_location);
- for (j = 0; j < merge->num_stmts; j++) {
- stmt_b = ctx->stmt_matrix[i][merge->stmt[j]];
- expr = stmt_b->expr->right;
- compound_expr_add(concat_b, expr_get(expr));
- }
verdict = ctx->stmt_matrix[i][k];
- build_verdict_map(concat_b, verdict, set);
- expr_free(concat_b);
+ __merge_concat_stmts_vmap(ctx, i, merge, set, verdict);
}
expr = map_expr_alloc(&internal_location, concat_a, set);
stmt = verdict_stmt_alloc(&internal_location, expr);
- remove_counter(ctx, from);
list_add(&stmt->list, &orig_stmt->list);
list_del(&orig_stmt->list);
stmt_free(orig_stmt);
@@ -766,12 +975,35 @@ static bool stmt_verdict_cmp(const struct optimize_ctx *ctx,
return true;
}
-static int stmt_nat_find(const struct optimize_ctx *ctx)
+static int stmt_nat_type(const struct optimize_ctx *ctx, int from,
+ enum nft_nat_etypes *nat_type)
+{
+ uint32_t j;
+
+ for (j = 0; j < ctx->num_stmts; j++) {
+ if (!ctx->stmt_matrix[from][j])
+ continue;
+
+ if (ctx->stmt_matrix[from][j]->type == STMT_NAT) {
+ *nat_type = ctx->stmt_matrix[from][j]->nat.type;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int stmt_nat_find(const struct optimize_ctx *ctx, int from)
{
+ enum nft_nat_etypes nat_type;
uint32_t i;
+ if (stmt_nat_type(ctx, from, &nat_type) < 0)
+ return -1;
+
for (i = 0; i < ctx->num_stmts; i++) {
- if (ctx->stmt[i]->ops->type != STMT_NAT)
+ if (ctx->stmt[i]->type != STMT_NAT ||
+ ctx->stmt[i]->nat.type != nat_type)
continue;
return i;
@@ -784,16 +1016,24 @@ static struct expr *stmt_nat_expr(struct stmt *nat_stmt)
{
struct expr *nat_expr;
+ assert(nat_stmt->type == STMT_NAT);
+
if (nat_stmt->nat.proto) {
- nat_expr = concat_expr_alloc(&internal_location);
- compound_expr_add(nat_expr, expr_get(nat_stmt->nat.addr));
- compound_expr_add(nat_expr, expr_get(nat_stmt->nat.proto));
+ if (nat_stmt->nat.addr) {
+ nat_expr = concat_expr_alloc(&internal_location);
+ compound_expr_add(nat_expr, expr_get(nat_stmt->nat.addr));
+ compound_expr_add(nat_expr, expr_get(nat_stmt->nat.proto));
+ } else {
+ nat_expr = expr_get(nat_stmt->nat.proto);
+ }
expr_free(nat_stmt->nat.proto);
nat_stmt->nat.proto = NULL;
} else {
nat_expr = expr_get(nat_stmt->nat.addr);
}
+ assert(nat_expr);
+
return nat_expr;
}
@@ -802,11 +1042,11 @@ static void merge_nat(const struct optimize_ctx *ctx,
const struct merge *merge)
{
struct expr *expr, *set, *elem, *nat_expr, *mapping, *left;
+ int k, family = NFPROTO_UNSPEC;
struct stmt *stmt, *nat_stmt;
uint32_t i;
- int k;
- k = stmt_nat_find(ctx);
+ k = stmt_nat_find(ctx, from);
assert(k >= 0);
set = set_expr_alloc(&internal_location, NULL);
@@ -826,11 +1066,23 @@ static void merge_nat(const struct optimize_ctx *ctx,
stmt = ctx->stmt_matrix[from][merge->stmt[0]];
left = expr_get(stmt->expr->left);
+ if (left->etype == EXPR_PAYLOAD) {
+ if (left->payload.desc == &proto_ip)
+ family = NFPROTO_IPV4;
+ else if (left->payload.desc == &proto_ip6)
+ family = NFPROTO_IPV6;
+ }
expr = map_expr_alloc(&internal_location, left, set);
nat_stmt = ctx->stmt_matrix[from][k];
+ if (nat_stmt->nat.family == NFPROTO_UNSPEC)
+ nat_stmt->nat.family = family;
+
expr_free(nat_stmt->nat.addr);
- nat_stmt->nat.addr = expr;
+ if (nat_stmt->nat.type == NFT_NAT_REDIR)
+ nat_stmt->nat.proto = expr;
+ else
+ nat_stmt->nat.addr = expr;
remove_counter(ctx, from);
list_del(&stmt->list);
@@ -842,11 +1094,11 @@ static void merge_concat_nat(const struct optimize_ctx *ctx,
const struct merge *merge)
{
struct expr *expr, *set, *elem, *nat_expr, *mapping, *left, *concat;
+ int k, family = NFPROTO_UNSPEC;
struct stmt *stmt, *nat_stmt;
uint32_t i, j;
- int k;
- k = stmt_nat_find(ctx);
+ k = stmt_nat_find(ctx, from);
assert(k >= 0);
set = set_expr_alloc(&internal_location, NULL);
@@ -873,11 +1125,20 @@ static void merge_concat_nat(const struct optimize_ctx *ctx,
for (j = 0; j < merge->num_stmts; j++) {
stmt = ctx->stmt_matrix[from][merge->stmt[j]];
left = stmt->expr->left;
+ if (left->etype == EXPR_PAYLOAD) {
+ if (left->payload.desc == &proto_ip)
+ family = NFPROTO_IPV4;
+ else if (left->payload.desc == &proto_ip6)
+ family = NFPROTO_IPV6;
+ }
compound_expr_add(concat, expr_get(left));
}
expr = map_expr_alloc(&internal_location, concat, set);
nat_stmt = ctx->stmt_matrix[from][k];
+ if (nat_stmt->nat.family == NFPROTO_UNSPEC)
+ nat_stmt->nat.family = family;
+
expr_free(nat_stmt->nat.addr);
nat_stmt->nat.addr = expr;
@@ -922,22 +1183,36 @@ static void rule_optimize_print(struct output_ctx *octx,
fprintf(octx->error_fp, "%s\n", line);
}
-static enum stmt_types merge_stmt_type(const struct optimize_ctx *ctx)
+enum {
+ MERGE_BY_VERDICT,
+ MERGE_BY_NAT_MAP,
+ MERGE_BY_NAT,
+};
+
+static uint32_t merge_stmt_type(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to)
{
- uint32_t i;
+ const struct stmt *stmt;
+ uint32_t i, j;
- for (i = 0; i < ctx->num_stmts; i++) {
- switch (ctx->stmt[i]->ops->type) {
- case STMT_VERDICT:
- case STMT_NAT:
- return ctx->stmt[i]->ops->type;
- default:
- continue;
+ for (i = from; i <= to; i++) {
+ for (j = 0; j < ctx->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[i][j];
+ if (!stmt)
+ continue;
+ if (stmt->type == STMT_NAT) {
+ if ((stmt->nat.type == NFT_NAT_REDIR &&
+ !stmt->nat.proto) ||
+ stmt->nat.type == NFT_NAT_MASQ)
+ return MERGE_BY_NAT;
+
+ return MERGE_BY_NAT_MAP;
+ }
}
}
- /* actually no verdict, this assumes rules have the same verdict. */
- return STMT_VERDICT;
+ /* merge by verdict, even if no verdict is specified. */
+ return MERGE_BY_VERDICT;
}
static void merge_rules(const struct optimize_ctx *ctx,
@@ -945,14 +1220,14 @@ static void merge_rules(const struct optimize_ctx *ctx,
const struct merge *merge,
struct output_ctx *octx)
{
- enum stmt_types stmt_type;
+ uint32_t merge_type;
bool same_verdict;
uint32_t i;
- stmt_type = merge_stmt_type(ctx);
+ merge_type = merge_stmt_type(ctx, from, to);
- switch (stmt_type) {
- case STMT_VERDICT:
+ switch (merge_type) {
+ case MERGE_BY_VERDICT:
same_verdict = stmt_verdict_cmp(ctx, from, to);
if (merge->num_stmts > 1) {
if (same_verdict)
@@ -966,18 +1241,24 @@ static void merge_rules(const struct optimize_ctx *ctx,
merge_stmts_vmap(ctx, from, to, merge);
}
break;
- case STMT_NAT:
+ case MERGE_BY_NAT_MAP:
if (merge->num_stmts > 1)
merge_concat_nat(ctx, from, to, merge);
else
merge_nat(ctx, from, to, merge);
break;
+ case MERGE_BY_NAT:
+ if (merge->num_stmts > 1)
+ merge_concat_stmts(ctx, from, to, merge);
+ else
+ merge_stmts(ctx, from, to, merge);
+ break;
default:
assert(0);
}
if (ctx->rule[from]->comment) {
- xfree(ctx->rule[from]->comment);
+ free_const(ctx->rule[from]->comment);
ctx->rule[from]->comment = NULL;
}
@@ -1011,15 +1292,41 @@ static bool stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
return __stmt_type_eq(stmt_a, stmt_b, true);
}
+static bool stmt_is_mergeable(const struct stmt *stmt)
+{
+ if (!stmt)
+ return false;
+
+ switch (stmt->type) {
+ case STMT_VERDICT:
+ if (stmt->expr->etype == EXPR_MAP)
+ return true;
+ break;
+ case STMT_EXPRESSION:
+ case STMT_NAT:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static bool rules_eq(const struct optimize_ctx *ctx, int i, int j)
{
- uint32_t k;
+ uint32_t k, mergeable = 0;
for (k = 0; k < ctx->num_stmts; k++) {
+ if (stmt_is_mergeable(ctx->stmt_matrix[i][k]))
+ mergeable++;
+
if (!stmt_type_eq(ctx->stmt_matrix[i][k], ctx->stmt_matrix[j][k]))
return false;
}
+ if (mergeable == 0)
+ return false;
+
return true;
}
@@ -1043,10 +1350,11 @@ static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
ctx->num_rules++;
}
- ctx->rule = xzalloc(sizeof(ctx->rule) * ctx->num_rules);
- ctx->stmt_matrix = xzalloc(sizeof(struct stmt *) * ctx->num_rules);
+ ctx->rule = xzalloc(sizeof(*ctx->rule) * ctx->num_rules);
+ ctx->stmt_matrix = xzalloc(sizeof(*ctx->stmt_matrix) * ctx->num_rules);
for (i = 0; i < ctx->num_rules; i++)
- ctx->stmt_matrix[i] = xzalloc(sizeof(struct stmt *) * MAX_STMTS);
+ ctx->stmt_matrix[i] = xzalloc_array(MAX_STMTS,
+ sizeof(**ctx->stmt_matrix));
merge = xzalloc(sizeof(*merge) * ctx->num_rules);
@@ -1078,14 +1386,48 @@ static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
}
}
- /* Step 4: Infer how to merge the candidate rules */
+ /* Step 4: Invalidate merge in case of duplicated keys in set/map. */
for (k = 0; k < num_merges; k++) {
+ uint32_t r1, r2;
+
+ i = merge[k].rule_from;
+
+ for (r1 = i; r1 < i + merge[k].num_rules; r1++) {
+ for (r2 = r1 + 1; r2 < i + merge[k].num_rules; r2++) {
+ bool match_same_value = true, match_seen = false;
+
+ for (m = 0; m < ctx->num_stmts; m++) {
+ if (!ctx->stmt_matrix[r1][m])
+ continue;
+
+ switch (ctx->stmt_matrix[r1][m]->type) {
+ case STMT_EXPRESSION:
+ match_seen = true;
+ if (!__expr_cmp(ctx->stmt_matrix[r1][m]->expr->right,
+ ctx->stmt_matrix[r2][m]->expr->right))
+ match_same_value = false;
+ break;
+ default:
+ break;
+ }
+ }
+ if (match_seen && match_same_value)
+ merge[k].skip = true;
+ }
+ }
+ }
+
+ /* Step 5: Infer how to merge the candidate rules */
+ for (k = 0; k < num_merges; k++) {
+ if (merge[k].skip)
+ continue;
+
i = merge[k].rule_from;
for (m = 0; m < ctx->num_stmts; m++) {
if (!ctx->stmt_matrix[i][m])
continue;
- switch (ctx->stmt_matrix[i][m]->ops->type) {
+ switch (ctx->stmt_matrix[i][m]->type) {
case STMT_EXPRESSION:
merge[k].stmt[merge[k].num_stmts++] = m;
break;
@@ -1103,16 +1445,16 @@ static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
}
ret = 0;
for (i = 0; i < ctx->num_rules; i++)
- xfree(ctx->stmt_matrix[i]);
+ free(ctx->stmt_matrix[i]);
- xfree(ctx->stmt_matrix);
- xfree(merge);
+ free(ctx->stmt_matrix);
+ free(merge);
err:
for (i = 0; i < ctx->num_stmts; i++)
stmt_free(ctx->stmt[i]);
- xfree(ctx->rule);
- xfree(ctx);
+ free(ctx->rule);
+ free(ctx);
return ret;
}
@@ -1146,7 +1488,7 @@ static int cmd_optimize(struct nft_ctx *nft, struct cmd *cmd)
int nft_optimize(struct nft_ctx *nft, struct list_head *cmds)
{
struct cmd *cmd;
- int ret;
+ int ret = 0;
list_for_each_entry(cmd, cmds, list) {
switch (cmd->op) {
diff --git a/src/osf.c b/src/osf.c
index cb58315d..a8f80b2b 100644
--- a/src/osf.c
+++ b/src/osf.c
@@ -1,7 +1,16 @@
+/*
+ * Copyright (c) 2018 Fernando Fernandez Mancera <ffmancera@riseup.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <utils.h>
-#include <string.h>
#include <osf.h>
#include <json.h>
diff --git a/src/owner.c b/src/owner.c
index 2d98a2e9..65eaad3e 100644
--- a/src/owner.c
+++ b/src/owner.c
@@ -1,6 +1,15 @@
+/*
+ * Copyright (c) 2021 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <stdio.h>
#include <unistd.h>
-#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <inttypes.h>
@@ -66,7 +75,7 @@ static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode)
continue;
rl = readlink(procname, tmp, sizeof(tmp));
- if (rl <= 0 || rl > (ssize_t)sizeof(tmp))
+ if (rl <= 0 || rl >= (ssize_t)sizeof(tmp))
continue;
tmp[rl] = 0;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index ae14eb1a..f9cc9098 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -9,12 +9,14 @@
*/
%{
+#include <nft.h>
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <inttypes.h>
#include <syslog.h>
+#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/if_ether.h>
@@ -33,6 +35,7 @@
#include <libnftnl/udata.h>
#include <rule.h>
+#include <cmd.h>
#include <statement.h>
#include <expression.h>
#include <headers.h>
@@ -65,16 +68,26 @@ static struct scope *current_scope(const struct parser_state *state)
return state->scopes[state->scope];
}
-static void open_scope(struct parser_state *state, struct scope *scope)
+static int open_scope(struct parser_state *state, struct scope *scope)
{
- assert(state->scope < array_size(state->scopes) - 1);
+ if (state->scope >= array_size(state->scopes) - 1) {
+ state->scope_err = true;
+ return -1;
+ }
+
scope_init(scope, current_scope(state));
state->scopes[++state->scope] = scope;
+
+ return 0;
}
static void close_scope(struct parser_state *state)
{
- assert(state->scope > 0);
+ if (state->scope_err || state->scope == 0) {
+ state->scope_err = false;
+ return;
+ }
+
state->scope--;
}
@@ -89,17 +102,14 @@ static void location_update(struct location *loc, struct location *rhs, int n)
{
if (n) {
loc->indesc = rhs[n].indesc;
- loc->token_offset = rhs[1].token_offset;
loc->line_offset = rhs[1].line_offset;
loc->first_line = rhs[1].first_line;
loc->first_column = rhs[1].first_column;
- loc->last_line = rhs[n].last_line;
loc->last_column = rhs[n].last_column;
} else {
loc->indesc = rhs[0].indesc;
- loc->token_offset = rhs[0].token_offset;
loc->line_offset = rhs[0].line_offset;
- loc->first_line = loc->last_line = rhs[0].last_line;
+ loc->first_line = rhs[0].first_line;
loc->first_column = loc->last_column = rhs[0].last_column;
}
}
@@ -134,6 +144,51 @@ static bool already_set(const void *attr, const struct location *loc,
return true;
}
+static struct expr *ifname_expr_alloc(const struct location *location,
+ struct list_head *queue,
+ const char *name)
+{
+ size_t length = strlen(name);
+ struct expr *expr;
+
+ if (length == 0) {
+ free_const(name);
+ erec_queue(error(location, "empty interface name"), queue);
+ return NULL;
+ }
+
+ if (length >= IFNAMSIZ) {
+ free_const(name);
+ erec_queue(error(location, "interface name too long"), queue);
+ return NULL;
+ }
+
+ expr = constant_expr_alloc(location, &ifname_type, BYTEORDER_HOST_ENDIAN,
+ length * BITS_PER_BYTE, name);
+
+ free_const(name);
+
+ return expr;
+}
+
+static void timeout_state_free(struct timeout_state *s)
+{
+ free_const(s->timeout_str);
+ free(s);
+}
+
+static void timeout_states_free(struct list_head *list)
+{
+ struct timeout_state *ts, *next;
+
+ list_for_each_entry_safe(ts, next, list, head) {
+ list_del(&ts->head);
+ timeout_state_free(ts);
+ }
+
+ free(list);
+}
+
#define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N)
#define symbol_value(loc, str) \
@@ -191,10 +246,12 @@ int nft_lex(void *, void *, void *);
uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */
uint8_t field;
} tcp_kind_field;
+ struct timeout_state *timeout_state;
}
%token TOKEN_EOF 0 "end of file"
%token JUNK "junk"
+%token CRLF "CRLF line terminators"
%token NEWLINE "newline"
%token COLON "colon"
@@ -227,6 +284,7 @@ int nft_lex(void *, void *, void *);
%token UNDEFINE "undefine"
%token FIB "fib"
+%token CHECK "check"
%token SOCKET "socket"
%token TRANSPARENT "transparent"
@@ -280,6 +338,8 @@ int nft_lex(void *, void *, void *);
%token DESCRIBE "describe"
%token IMPORT "import"
%token EXPORT "export"
+%token DESTROY "destroy"
+
%token MONITOR "monitor"
%token ALL "all"
@@ -317,7 +377,7 @@ int nft_lex(void *, void *, void *);
%token <string> STRING "string"
%token <string> QUOTED_STRING "quoted string"
%token <string> ASTERISK_STRING "string with a trailing asterisk"
-%destructor { xfree($$); } STRING QUOTED_STRING ASTERISK_STRING
+%destructor { free_const($$); } STRING QUOTED_STRING ASTERISK_STRING
%token LL_HDR "ll"
%token NETWORK_HDR "nh"
@@ -382,6 +442,7 @@ int nft_lex(void *, void *, void *);
%token ICMP6 "icmpv6"
%token PPTR "param-problem"
%token MAXDELAY "max-delay"
+%token TADDR "taddr"
%token AH "ah"
%token RESERVED "reserved"
@@ -428,6 +489,14 @@ int nft_lex(void *, void *, void *);
%token DCCP "dccp"
+%token VXLAN "vxlan"
+%token VNI "vni"
+
+%token GRE "gre"
+%token GRETAP "gretap"
+
+%token GENEVE "geneve"
+
%token SCTP "sctp"
%token CHUNK "chunk"
%token DATA "data"
@@ -533,6 +602,9 @@ int nft_lex(void *, void *, void *);
%token BYTES "bytes"
%token AVGPKT "avgpkt"
+%token LAST "last"
+%token NEVER "never"
+
%token COUNTERS "counters"
%token QUOTAS "quotas"
%token LIMITS "limits"
@@ -615,13 +687,15 @@ int nft_lex(void *, void *, void *);
%token IN "in"
%token OUT "out"
+%token XT "xt"
+
%type <limit_rate> limit_rate_pkts
%type <limit_rate> limit_rate_bytes
%type <string> identifier type_identifier string comment_spec
-%destructor { xfree($$); } identifier type_identifier string comment_spec
+%destructor { free_const($$); } identifier type_identifier string comment_spec
-%type <val> time_spec quota_used
+%type <val> time_spec time_spec_or_num_s set_elem_time_spec quota_used
%type <expr> data_type_expr data_type_atom_expr
%destructor { expr_free($$); } data_type_expr data_type_atom_expr
@@ -629,8 +703,8 @@ int nft_lex(void *, void *, void *);
%type <cmd> line
%destructor { cmd_free($$); } line
-%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
-%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
+%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
+%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
%type <handle> table_spec tableid_spec table_or_id_spec
%destructor { handle_free(&$$); } table_spec tableid_spec table_or_id_spec
@@ -650,14 +724,19 @@ int nft_lex(void *, void *, void *);
%type <handle> basehook_spec
%destructor { handle_free(&$$); } basehook_spec
+%type <handle> list_cmd_spec_any list_cmd_spec_table
+%destructor { handle_free(&$$); } list_cmd_spec_any list_cmd_spec_table
+
%type <val> family_spec family_spec_explicit
%type <val32> int_num chain_policy
%type <prio_spec> extended_prio_spec prio_spec
+%destructor { expr_free($$.expr); } extended_prio_spec prio_spec
+
%type <string> extended_prio_name quota_unit basehook_device_name
-%destructor { xfree($$); } extended_prio_name quota_unit basehook_device_name
+%destructor { free_const($$); } extended_prio_name quota_unit basehook_device_name
%type <expr> dev_spec
-%destructor { xfree($$); } dev_spec
+%destructor { free($$); } dev_spec
%type <table> table_block_alloc table_block
%destructor { close_scope(state); table_free($$); } table_block_alloc
@@ -666,6 +745,8 @@ int nft_lex(void *, void *, void *);
%type <rule> rule rule_alloc
%destructor { rule_free($$); } rule
+%type <val> table_flags table_flag
+
%type <val> set_flag_list set_flag
%type <val> set_policy_spec
@@ -675,7 +756,7 @@ int nft_lex(void *, void *, void *);
%type <set> map_block_alloc map_block
%destructor { set_free($$); } map_block_alloc
-%type <val> map_block_obj_type
+%type <val> map_block_obj_type map_block_obj_typeof map_block_data_interval
%type <flowtable> flowtable_block_alloc flowtable_block
%destructor { flowtable_free($$); } flowtable_block_alloc
@@ -684,11 +765,16 @@ int nft_lex(void *, void *, void *);
%destructor { obj_free($$); } obj_block_alloc
%type <list> stmt_list stateful_stmt_list set_elem_stmt_list
-%destructor { stmt_list_free($$); xfree($$); } stmt_list stateful_stmt_list set_elem_stmt_list
+%destructor { stmt_list_free($$); free($$); } stmt_list stateful_stmt_list set_elem_stmt_list
%type <stmt> stmt match_stmt verdict_stmt set_elem_stmt
%destructor { stmt_free($$); } stmt match_stmt verdict_stmt set_elem_stmt
-%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt
-%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt
+%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt last_stmt
+%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt last_stmt
+%type <stmt> limit_stmt_alloc quota_stmt_alloc last_stmt_alloc ct_limit_stmt_alloc
+%destructor { stmt_free($$); } limit_stmt_alloc quota_stmt_alloc last_stmt_alloc ct_limit_stmt_alloc
+%type <stmt> objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy
+%destructor { stmt_free($$); } objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy
+
%type <stmt> payload_stmt
%destructor { stmt_free($$); } payload_stmt
%type <stmt> ct_stmt
@@ -728,13 +814,13 @@ int nft_lex(void *, void *, void *);
%type <val> set_stmt_op
%type <stmt> map_stmt
%destructor { stmt_free($$); } map_stmt
-%type <stmt> meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc
-%destructor { stmt_free($$); } meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc
+%type <stmt> meter_stmt
+%destructor { stmt_free($$); } meter_stmt
%type <expr> symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
-%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr
-%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr
+%type <expr> primary_expr shift_expr and_expr primary_typeof_expr typeof_expr typeof_data_expr typeof_key_expr typeof_verdict_expr selector_expr
+%destructor { expr_free($$); } primary_expr shift_expr and_expr primary_typeof_expr typeof_expr typeof_data_expr typeof_key_expr typeof_verdict_expr selector_expr
%type <expr> exclusive_or_expr inclusive_or_expr
%destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr
%type <expr> basic_expr
@@ -804,6 +890,8 @@ int nft_lex(void *, void *, void *);
%type <expr> payload_expr payload_raw_expr
%destructor { expr_free($$); } payload_expr payload_raw_expr
%type <val> payload_base_spec
+%type <val> payload_raw_len
+
%type <expr> eth_hdr_expr vlan_hdr_expr
%destructor { expr_free($$); } eth_hdr_expr vlan_hdr_expr
%type <val> eth_hdr_field vlan_hdr_field
@@ -873,7 +961,7 @@ int nft_lex(void *, void *, void *);
%type <val> markup_format
%type <string> monitor_event
-%destructor { xfree($$); } monitor_event
+%destructor { free_const($$); } monitor_event
%type <val> monitor_object monitor_format
%type <val> synproxy_ts synproxy_sack
@@ -886,9 +974,19 @@ int nft_lex(void *, void *, void *);
%type <val> tcpopt_field_maxseg tcpopt_field_mptcp tcpopt_field_sack tcpopt_field_tsopt tcpopt_field_window
%type <tcp_kind_field> tcp_hdr_option_kind_and_field
+%type <expr> inner_eth_expr inner_inet_expr inner_expr
+%destructor { expr_free($$); } inner_eth_expr inner_inet_expr inner_expr
+
+%type <expr> vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%destructor { expr_free($$); } vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%type <val> vxlan_hdr_field geneve_hdr_field gre_hdr_field
+
%type <stmt> optstrip_stmt
%destructor { stmt_free($$); } optstrip_stmt
+%type <stmt> xt_stmt
+%destructor { stmt_free($$); } xt_stmt
+
%type <expr> boolean_expr
%destructor { expr_free($$); } boolean_expr
%type <val8> boolean_keys
@@ -897,10 +995,13 @@ int nft_lex(void *, void *, void *);
%destructor { expr_free($$); } exthdr_exists_expr
%type <val> exthdr_key
-%type <val> ct_l4protoname ct_obj_type ct_cmd_type
+%type <val> ct_l4protoname ct_obj_type ct_cmd_type ct_obj_type_map
-%type <list> timeout_states timeout_state
-%destructor { xfree($$); } timeout_states timeout_state
+%type <timeout_state> timeout_state
+%destructor { timeout_state_free($$); } timeout_state
+
+%type <list> timeout_states
+%destructor { timeout_states_free($$); } timeout_states
%type <val> xfrm_state_key xfrm_state_proto_key xfrm_dir xfrm_spnum
%type <expr> xfrm_expr
@@ -935,7 +1036,9 @@ close_scope_at : { scanner_pop_start_cond(nft->scanner, PARSER_SC_AT); };
close_scope_comp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_COMP); };
close_scope_ct : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CT); };
close_scope_counter : { scanner_pop_start_cond(nft->scanner, PARSER_SC_COUNTER); };
+close_scope_last : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LAST); };
close_scope_dccp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DCCP); };
+close_scope_destroy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_DESTROY); };
close_scope_dst : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DST); };
close_scope_dup : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_DUP); };
close_scope_esp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_ESP); };
@@ -944,6 +1047,7 @@ close_scope_export : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT
close_scope_fib : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); };
close_scope_frag : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); };
close_scope_fwd : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); };
+close_scope_gre : { scanner_pop_start_cond(nft->scanner, PARSER_SC_GRE); };
close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); };
close_scope_hbh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HBH); };
close_scope_ip : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP); };
@@ -980,14 +1084,15 @@ close_scope_udplite : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPL
close_scope_log : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_LOG); }
close_scope_synproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_SYNPROXY); }
+close_scope_xt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); }
common_block : INCLUDE QUOTED_STRING stmt_separator
{
if (scanner_include_file(nft, scanner, $2, &@$) < 0) {
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
| DEFINE identifier '=' initializer_expr stmt_separator
{
@@ -997,19 +1102,19 @@ common_block : INCLUDE QUOTED_STRING stmt_separator
erec_queue(error(&@2, "redefinition of symbol '%s'", $2),
state->msgs);
expr_free($4);
- xfree($2);
+ free_const($2);
YYERROR;
}
symbol_bind(scope, $2, $4);
- xfree($2);
+ free_const($2);
}
| REDEFINE identifier '=' initializer_expr stmt_separator
{
struct scope *scope = current_scope(state);
symbol_bind(scope, $2, $4);
- xfree($2);
+ free_const($2);
}
| UNDEFINE identifier stmt_separator
{
@@ -1018,10 +1123,10 @@ common_block : INCLUDE QUOTED_STRING stmt_separator
if (symbol_unbind(scope, $2) < 0) {
erec_queue(error(&@2, "undefined symbol '%s'", $2),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
| error stmt_separator
{
@@ -1069,6 +1174,7 @@ base_cmd : /* empty */ add_cmd { $$ = $1; }
| EXPORT export_cmd close_scope_export { $$ = $2; }
| MONITOR monitor_cmd close_scope_monitor { $$ = $2; }
| DESCRIBE describe_cmd { $$ = $2; }
+ | DESTROY destroy_cmd close_scope_destroy { $$ = $2; }
;
add_cmd : TABLE table_spec
@@ -1118,6 +1224,12 @@ add_cmd : TABLE table_spec
}
| ELEMENT set_spec set_block_expr
{
+ if (nft_cmd_collapse_elems(CMD_ADD, state->cmds, &$2, $3)) {
+ handle_free(&$2);
+ expr_free($3);
+ $$ = NULL;
+ break;
+ }
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
| FLOWTABLE flowtable_spec flowtable_block_alloc
@@ -1235,6 +1347,12 @@ create_cmd : TABLE table_spec
}
| ELEMENT set_spec set_block_expr
{
+ if (nft_cmd_collapse_elems(CMD_CREATE, state->cmds, &$2, $3)) {
+ handle_free(&$2);
+ expr_free($3);
+ $$ = NULL;
+ break;
+ }
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
| FLOWTABLE flowtable_spec flowtable_block_alloc
@@ -1317,6 +1435,14 @@ delete_cmd : TABLE table_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, NULL);
}
+ | CHAIN chain_spec chain_block_alloc
+ '{' chain_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ close_scope(state);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, $5);
+ }
| RULE ruleid_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &$2, &@$, NULL);
@@ -1325,7 +1451,7 @@ delete_cmd : TABLE table_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
}
- | MAP set_spec
+ | MAP set_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
}
@@ -1376,12 +1502,87 @@ delete_cmd : TABLE table_or_id_spec
}
;
+destroy_cmd : TABLE table_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | CHAIN chain_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | SET set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | FLOWTABLE flowtable_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtableid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
+ | COUNTER obj_or_id_spec close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
+ | QUOTA obj_or_id_spec close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
+ | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_DESTROY, $2, &$3, &@$, $4);
+ if ($2 == NFT_OBJECT_CT_TIMEOUT)
+ init_list_head(&$4->ct_timeout.timeout_list);
+ }
+ | LIMIT obj_or_id_spec close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ }
+ | SECMARK obj_or_id_spec close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ }
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ }
+ ;
+
+
get_cmd : ELEMENT set_spec set_block_expr
{
$$ = cmd_alloc(CMD_GET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
;
+list_cmd_spec_table : TABLE table_spec { $$ = $2; }
+ | table_spec
+ ;
+list_cmd_spec_any : list_cmd_spec_table
+ | ruleset_spec
+ ;
+
list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_TABLE, &$2, &@$, NULL);
@@ -1398,74 +1599,50 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_CHAINS, &$2, &@$, NULL);
}
- | SETS ruleset_spec
+ | SETS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &$2, &@$, NULL);
}
- | SETS TABLE table_spec
- {
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &$3, &@$, NULL);
- }
| SET set_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, &@$, NULL);
}
- | COUNTERS ruleset_spec
+ | COUNTERS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
}
- | COUNTERS TABLE table_spec
- {
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$3, &@$, NULL);
- }
| COUNTER obj_spec close_scope_counter
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &$2, &@$, NULL);
}
- | QUOTAS ruleset_spec
+ | QUOTAS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$2, &@$, NULL);
}
- | QUOTAS TABLE table_spec
- {
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$3, &@$, NULL);
- }
| QUOTA obj_spec close_scope_quota
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &$2, &@$, NULL);
}
- | LIMITS ruleset_spec
+ | LIMITS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$2, &@$, NULL);
}
- | LIMITS TABLE table_spec
- {
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$3, &@$, NULL);
- }
| LIMIT obj_spec close_scope_limit
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMIT, &$2, &@$, NULL);
}
- | SECMARKS ruleset_spec
+ | SECMARKS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$2, &@$, NULL);
}
- | SECMARKS TABLE table_spec
- {
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$3, &@$, NULL);
- }
| SECMARK obj_spec close_scope_secmark
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARK, &$2, &@$, NULL);
}
- | SYNPROXYS ruleset_spec
+ | SYNPROXYS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$2, &@$, NULL);
}
- | SYNPROXYS TABLE table_spec
- {
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$3, &@$, NULL);
- }
| SYNPROXY obj_spec close_scope_synproxy
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
@@ -1490,7 +1667,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &$2, &@$, NULL);
}
- | FLOWTABLES ruleset_spec
+ | FLOWTABLES list_cmd_spec_any
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLES, &$2, &@$, NULL);
}
@@ -1498,7 +1675,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
}
- | MAPS ruleset_spec
+ | MAPS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_MAPS, &$2, &@$, NULL);
}
@@ -1540,30 +1717,55 @@ basehook_spec : ruleset_spec
}
;
-reset_cmd : COUNTERS ruleset_spec
+reset_cmd : COUNTERS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
}
- | COUNTERS TABLE table_spec
- {
- $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$3, &@$, NULL);
- }
| COUNTER obj_spec close_scope_counter
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTER, &$2,&@$, NULL);
}
- | QUOTAS ruleset_spec
+ | QUOTAS list_cmd_spec_any
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$2, &@$, NULL);
}
- | QUOTAS TABLE table_spec
- {
- $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$3, &@$, NULL);
- }
| QUOTA obj_spec close_scope_quota
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTA, &$2, &@$, NULL);
}
+ | RULES ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES list_cmd_spec_table
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | RULES chain_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | RULES CHAIN chain_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_CHAIN, &$3, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | SET set_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &$2, &@$, NULL);
+ }
;
flush_cmd : TABLE table_spec
@@ -1674,35 +1876,48 @@ describe_cmd : primary_expr
table_block_alloc : /* empty */
{
$$ = table_alloc();
- open_scope(state, &$$->scope);
+ if (open_scope(state, &$$->scope) < 0) {
+ erec_queue(error(&@$, "too many levels of nesting"),
+ state->msgs);
+ state->nerrs++;
+ }
}
;
-table_options : FLAGS STRING
+table_options : FLAGS table_flags
{
- if (strcmp($2, "dormant") == 0) {
- $<table>0->flags |= TABLE_F_DORMANT;
- xfree($2);
- } else if (strcmp($2, "owner") == 0) {
- $<table>0->flags |= TABLE_F_OWNER;
- xfree($2);
- } else {
- erec_queue(error(&@2, "unknown table option %s", $2),
- state->msgs);
- xfree($2);
- YYERROR;
- }
+ $<table>0->flags |= $2;
}
| comment_spec
{
if (already_set($<table>0->comment, &@$, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<table>0->comment = $1;
}
;
+table_flags : table_flag
+ | table_flags COMMA table_flag
+ {
+ $$ = $1 | $3;
+ }
+ ;
+table_flag : STRING
+ {
+ $$ = parse_table_flag($1);
+ if ($$ == 0) {
+ erec_queue(error(&@1, "unknown table option %s", $1),
+ state->msgs);
+ free_const($1);
+ YYERROR;
+ }
+
+ free_const($1);
+ }
+ ;
+
table_block : /* empty */ { $$ = $<table>-1; }
| table_block common_block
| table_block stmt_separator
@@ -1771,7 +1986,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
list_add_tail(&$4->list, &$1->objs);
$$ = $1;
}
- | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' close_scope_ct stmt_separator
+ | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_HELPER;
@@ -1780,7 +1995,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
list_add_tail(&$5->list, &$1->objs);
$$ = $1;
}
- | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' close_scope_ct stmt_separator
+ | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_TIMEOUT;
@@ -1789,7 +2004,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
list_add_tail(&$5->list, &$1->objs);
$$ = $1;
}
- | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' close_scope_ct stmt_separator
+ | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_EXPECT;
@@ -1835,8 +2050,12 @@ table_block : /* empty */ { $$ = $<table>-1; }
chain_block_alloc : /* empty */
{
- $$ = chain_alloc(NULL);
- open_scope(state, &$$->scope);
+ $$ = chain_alloc();
+ if (open_scope(state, &$$->scope) < 0) {
+ erec_queue(error(&@$, "too many levels of nesting"),
+ state->msgs);
+ state->nerrs++;
+ }
}
;
@@ -1851,10 +2070,19 @@ chain_block : /* empty */ { $$ = $<chain>-1; }
list_add_tail(&$2->list, &$1->rules);
$$ = $1;
}
+ | chain_block DEVICES '=' flowtable_expr stmt_separator
+ {
+ if ($$->dev_expr) {
+ list_splice_init(&$4->expressions, &$$->dev_expr->expressions);
+ expr_free($4);
+ break;
+ }
+ $$->dev_expr = $4;
+ }
| chain_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -1870,19 +2098,10 @@ subchain_block : /* empty */ { $$ = $<chain>-1; }
}
;
-typeof_data_expr : primary_expr
+typeof_verdict_expr : selector_expr
{
struct expr *e = $1;
- if (e->etype == EXPR_SYMBOL &&
- strcmp("verdict", e->identifier) == 0) {
- struct expr *v = verdict_expr_alloc(&@1, NF_ACCEPT, NULL);
-
- expr_free(e);
- v->flags &= ~EXPR_F_CONSTANT;
- e = v;
- }
-
if (expr_ops(e)->build_udata == NULL) {
erec_queue(error(&@1, "map data type '%s' lacks typeof serialization", expr_ops(e)->name),
state->msgs);
@@ -1891,7 +2110,7 @@ typeof_data_expr : primary_expr
}
$$ = e;
}
- | typeof_expr DOT primary_expr
+ | typeof_expr DOT selector_expr
{
struct location rhs[] = {
[1] = @2,
@@ -1902,7 +2121,37 @@ typeof_data_expr : primary_expr
}
;
-typeof_expr : primary_expr
+typeof_data_expr : INTERVAL typeof_expr
+ {
+ $2->flags |= EXPR_F_INTERVAL;
+ $$ = $2;
+ }
+ | typeof_verdict_expr
+ {
+ $$ = $1;
+ }
+ | QUEUE
+ {
+ $$ = constant_expr_alloc(&@$, &queue_type, BYTEORDER_HOST_ENDIAN, 16, NULL);
+ }
+ | STRING
+ {
+ struct expr *verdict;
+
+ if (strcmp("verdict", $1) != 0) {
+ erec_queue(error(&@1, "map data type '%s' lacks typeof serialization", $1),
+ state->msgs);
+ free_const($1);
+ YYERROR;
+ }
+ verdict = verdict_expr_alloc(&@1, NF_ACCEPT, NULL);
+ verdict->flags &= ~EXPR_F_CONSTANT;
+ $$ = verdict;
+ free_const($1);
+ }
+ ;
+
+primary_typeof_expr : selector_expr
{
if (expr_ops($1)->build_udata == NULL) {
erec_queue(error(&@1, "primary expression type '%s' lacks typeof serialization", expr_ops($1)->name),
@@ -1913,7 +2162,13 @@ typeof_expr : primary_expr
$$ = $1;
}
- | typeof_expr DOT primary_expr
+ ;
+
+typeof_expr : primary_typeof_expr
+ {
+ $$ = $1;
+ }
+ | typeof_expr DOT primary_typeof_expr
{
struct location rhs[] = {
[1] = @2,
@@ -1927,22 +2182,25 @@ typeof_expr : primary_expr
set_block_alloc : /* empty */
{
- $$ = set_alloc(NULL);
+ $$ = set_alloc(&internal_location);
}
;
+typeof_key_expr : TYPEOF typeof_expr { $$ = $2; }
+ | TYPE data_type_expr close_scope_type { $$ = $2; }
+ ;
+
set_block : /* empty */ { $$ = $<set>-1; }
| set_block common_block
| set_block stmt_separator
- | set_block TYPE data_type_expr stmt_separator close_scope_type
- {
- $1->key = $3;
- $$ = $1;
- }
- | set_block TYPEOF typeof_expr stmt_separator
+ | set_block typeof_key_expr stmt_separator
{
- $1->key = $3;
- datatype_set($1->key, $3->dtype);
+ if (already_set($1->key, &@2, state)) {
+ expr_free($2);
+ YYERROR;
+ }
+
+ $1->key = $2;
$$ = $1;
}
| set_block FLAGS set_flag_list stmt_separator
@@ -1968,6 +2226,10 @@ set_block : /* empty */ { $$ = $<set>-1; }
}
| set_block ELEMENTS '=' set_block_expr
{
+ if (already_set($1->init, &@2, state)) {
+ expr_free($4);
+ YYERROR;
+ }
$1->init = $4;
$$ = $1;
}
@@ -1980,7 +2242,7 @@ set_block : /* empty */ { $$ = $<set>-1; }
| set_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -2007,10 +2269,14 @@ set_flag : CONSTANT { $$ = NFT_SET_CONSTANT; }
map_block_alloc : /* empty */
{
- $$ = set_alloc(NULL);
+ $$ = set_alloc(&internal_location);
}
;
+ct_obj_type_map : TIMEOUT { $$ = NFT_OBJECT_CT_TIMEOUT; }
+ | EXPECTATION { $$ = NFT_OBJECT_CT_EXPECT; }
+ ;
+
map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; }
| QUOTA close_scope_quota { $$ = NFT_OBJECT_QUOTA; }
| LIMIT close_scope_limit { $$ = NFT_OBJECT_LIMIT; }
@@ -2018,6 +2284,14 @@ map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; }
| SYNPROXY close_scope_synproxy { $$ = NFT_OBJECT_SYNPROXY; }
;
+map_block_obj_typeof : map_block_obj_type
+ | CT ct_obj_type_map close_scope_ct { $$ = $2; }
+ ;
+
+map_block_data_interval : INTERVAL { $$ = EXPR_F_INTERVAL; }
+ | { $$ = 0; }
+ ;
+
map_block : /* empty */ { $$ = $<set>-1; }
| map_block common_block
| map_block stmt_separator
@@ -2026,23 +2300,24 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->timeout = $3;
$$ = $1;
}
- | map_block TYPE
- data_type_expr COLON data_type_expr
- stmt_separator close_scope_type
+ | map_block GC_INTERVAL time_spec stmt_separator
{
- $1->key = $3;
- $1->data = $5;
-
- $1->flags |= NFT_SET_MAP;
+ $1->gc_int = $3;
$$ = $1;
}
| map_block TYPE
- data_type_expr COLON INTERVAL data_type_expr
+ data_type_expr COLON map_block_data_interval data_type_expr
stmt_separator close_scope_type
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ expr_free($6);
+ YYERROR;
+ }
+
$1->key = $3;
$1->data = $6;
- $1->data->flags |= EXPR_F_INTERVAL;
+ $1->data->flags |= $5;
$1->flags |= NFT_SET_MAP;
$$ = $1;
@@ -2051,29 +2326,43 @@ map_block : /* empty */ { $$ = $<set>-1; }
typeof_expr COLON typeof_data_expr
stmt_separator
{
- $1->key = $3;
- datatype_set($1->key, $3->dtype);
- $1->data = $5;
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ expr_free($5);
+ YYERROR;
+ }
- $1->flags |= NFT_SET_MAP;
- $$ = $1;
- }
- | map_block TYPEOF
- typeof_expr COLON INTERVAL typeof_expr
- stmt_separator
- {
$1->key = $3;
- datatype_set($1->key, $3->dtype);
- $1->data = $6;
- $1->data->flags |= EXPR_F_INTERVAL;
- $1->flags |= NFT_SET_MAP;
+ if ($5->etype == EXPR_CT && $5->ct.key == NFT_CT_HELPER) {
+ $1->objtype = NFT_OBJECT_CT_HELPER;
+ $1->flags |= NFT_SET_OBJECT;
+ expr_free($5);
+ } else {
+ $1->data = $5;
+ $1->flags |= NFT_SET_MAP;
+ }
+
$$ = $1;
}
| map_block TYPE
data_type_expr COLON map_block_obj_type
stmt_separator close_scope_type
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ YYERROR;
+ }
+
+ $1->key = $3;
+ $1->objtype = $5;
+ $1->flags |= NFT_SET_OBJECT;
+ $$ = $1;
+ }
+ | map_block TYPEOF
+ typeof_expr COLON map_block_obj_typeof
+ stmt_separator
+ {
$1->key = $3;
$1->objtype = $5;
$1->flags |= NFT_SET_OBJECT;
@@ -2098,7 +2387,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
| map_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -2123,7 +2412,7 @@ set_policy_spec : PERFORMANCE { $$ = NFT_SET_POL_PERFORMANCE; }
flowtable_block_alloc : /* empty */
{
- $$ = flowtable_alloc(NULL);
+ $$ = flowtable_alloc(&internal_location);
}
;
@@ -2137,10 +2426,10 @@ flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
if ($$->hook.name == NULL) {
erec_queue(error(&@3, "unknown chain hook"),
state->msgs);
- xfree($3);
+ free_const($3);
YYERROR;
}
- xfree($3);
+ free_const($3);
$$->priority = $4;
}
@@ -2185,17 +2474,21 @@ flowtable_list_expr : flowtable_expr_member
flowtable_expr_member : QUOTED_STRING
{
- $$ = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen($1) * BITS_PER_BYTE, $1);
- xfree($1);
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
}
| STRING
{
- $$ = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen($1) * BITS_PER_BYTE, $1);
- xfree($1);
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
}
| variable_expr
{
@@ -2210,12 +2503,12 @@ data_type_atom_expr : type_identifier
if (dtype == NULL) {
erec_queue(error(&@1, "unknown datatype %s", $1),
state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
$$ = constant_expr_alloc(&@1, dtype, dtype->byteorder,
dtype->size, NULL);
- xfree($1);
+ free_const($1);
}
| TIME
{
@@ -2238,7 +2531,7 @@ data_type_expr : data_type_atom_expr
obj_block_alloc : /* empty */
{
- $$ = obj_alloc(NULL);
+ $$ = obj_alloc(&internal_location);
}
;
@@ -2252,7 +2545,7 @@ counter_block : /* empty */ { $$ = $<obj>-1; }
| counter_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2269,7 +2562,7 @@ quota_block : /* empty */ { $$ = $<obj>-1; }
| quota_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2286,7 +2579,7 @@ ct_helper_block : /* empty */ { $$ = $<obj>-1; }
| ct_helper_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2297,6 +2590,7 @@ ct_timeout_block : /*empty */
{
$$ = $<obj>-1;
init_list_head(&$$->ct_timeout.timeout_list);
+ $$->type = NFT_OBJECT_CT_TIMEOUT;
}
| ct_timeout_block common_block
| ct_timeout_block stmt_separator
@@ -2307,7 +2601,7 @@ ct_timeout_block : /*empty */
| ct_timeout_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2324,7 +2618,7 @@ ct_expect_block : /*empty */ { $$ = $<obj>-1; }
| ct_expect_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2341,7 +2635,7 @@ limit_block : /* empty */ { $$ = $<obj>-1; }
| limit_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2358,7 +2652,7 @@ secmark_block : /* empty */ { $$ = $<obj>-1; }
| secmark_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2375,7 +2669,7 @@ synproxy_block : /* empty */ { $$ = $<obj>-1; }
| synproxy_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2396,12 +2690,15 @@ hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
if (chain_type == NULL) {
erec_queue(error(&@3, "unknown chain type"),
state->msgs);
- xfree($3);
+ free_const($3);
+ free_const($5);
+ expr_free($6);
+ expr_free($7.expr);
YYERROR;
}
$<chain>0->type.loc = @3;
$<chain>0->type.str = xstrdup(chain_type);
- xfree($3);
+ free_const($3);
$<chain>0->loc = @$;
$<chain>0->hook.loc = @5;
@@ -2409,10 +2706,12 @@ hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
if ($<chain>0->hook.name == NULL) {
erec_queue(error(&@5, "unknown chain hook"),
state->msgs);
- xfree($5);
+ free_const($5);
+ expr_free($6);
+ expr_free($7.expr);
YYERROR;
}
- xfree($5);
+ free_const($5);
$<chain>0->dev_expr = $6;
$<chain>0->priority = $7;
@@ -2459,7 +2758,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen($1) * BITS_PER_BYTE,
$1);
- xfree($1);
+ free_const($1);
$$ = spec;
}
| extended_prio_name PLUS NUM
@@ -2472,7 +2771,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen(str) * BITS_PER_BYTE,
str);
- xfree($1);
+ free_const($1);
$$ = spec;
}
| extended_prio_name DASH NUM
@@ -2485,7 +2784,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen(str) * BITS_PER_BYTE,
str);
- xfree($1);
+ free_const($1);
$$ = spec;
}
;
@@ -2496,12 +2795,11 @@ int_num : NUM { $$ = $1; }
dev_spec : DEVICE string
{
- struct expr *expr;
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $2);
+
+ if (!expr)
+ YYERROR;
- expr = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen($2) * BITS_PER_BYTE, $2);
- xfree($2);
$$ = compound_expr_alloc(&@$, EXPR_LIST);
compound_expr_add($$, expr);
@@ -2557,6 +2855,7 @@ chain_policy : ACCEPT { $$ = NF_ACCEPT; }
;
identifier : STRING
+ | LAST { $$ = xstrdup("last"); }
;
string : STRING
@@ -2570,7 +2869,7 @@ time_spec : STRING
uint64_t res;
erec = time_parse(&@1, $1, &res);
- xfree($1);
+ free_const($1);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -2579,6 +2878,11 @@ time_spec : STRING
}
;
+/* compatibility kludge to allow either 60, 60s, 1m, ... */
+time_spec_or_num_s : NUM
+ | time_spec { $$ = $1 / 1000u; }
+ ;
+
family_spec : /* empty */ { $$ = NFPROTO_IPV4; }
| family_spec_explicit
;
@@ -2766,7 +3070,7 @@ comment_spec : COMMENT string
erec_queue(error(&@2, "comment too long, %d characters maximum allowed",
NFTNL_UDATA_COMMENT_MAXLEN),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
$$ = $2;
@@ -2803,7 +3107,7 @@ rule_alloc : stmt_list
list_for_each_entry(i, $1, list)
$$->num_stmts++;
list_splice_tail($1, &$$->stmts);
- xfree($1);
+ free($1);
}
;
@@ -2833,10 +3137,65 @@ stateful_stmt_list : stateful_stmt
}
;
+objref_stmt_counter : COUNTER NAME stmt_expr close_scope_counter
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_COUNTER;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_limit : LIMIT NAME stmt_expr close_scope_limit
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_LIMIT;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_quota : QUOTA NAME stmt_expr close_scope_quota
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_QUOTA;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_synproxy : SYNPROXY NAME stmt_expr close_scope_synproxy
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_SYNPROXY;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_ct : CT TIMEOUT SET stmt_expr close_scope_ct
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
+ $$->objref.expr = $4;
+
+ }
+ | CT EXPECTATION SET stmt_expr close_scope_ct
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_EXPECT;
+ $$->objref.expr = $4;
+ }
+ ;
+
+objref_stmt : objref_stmt_counter
+ | objref_stmt_limit
+ | objref_stmt_quota
+ | objref_stmt_synproxy
+ | objref_stmt_ct
+ ;
+
stateful_stmt : counter_stmt close_scope_counter
- | limit_stmt
- | quota_stmt
- | connlimit_stmt
+ | limit_stmt close_scope_limit
+ | quota_stmt close_scope_quota
+ | connlimit_stmt close_scope_ct
+ | last_stmt close_scope_last
;
stmt : verdict_stmt
@@ -2860,6 +3219,19 @@ stmt : verdict_stmt
| synproxy_stmt close_scope_synproxy
| chain_stmt
| optstrip_stmt
+ | xt_stmt close_scope_xt
+ | objref_stmt
+ ;
+
+xt_stmt : XT STRING string
+ {
+ $$ = NULL;
+ free_const($2);
+ free_const($3);
+ erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"),
+ state->msgs);
+ YYERROR;
+ }
;
chain_stmt_type : JUMP { $$ = NFT_JUMP; }
@@ -2918,16 +3290,27 @@ verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_n
}
;
-connlimit_stmt : CT COUNT NUM close_scope_ct
+ct_limit_stmt_alloc : CT COUNT
{
$$ = connlimit_stmt_alloc(&@$);
- $$->connlimit.count = $3;
}
- | CT COUNT OVER NUM close_scope_ct
+ ;
+
+connlimit_stmt : ct_limit_stmt_alloc ct_limit_args
+ ;
+
+ct_limit_args : NUM
{
- $$ = connlimit_stmt_alloc(&@$);
- $$->connlimit.count = $4;
- $$->connlimit.flags = NFT_CONNLIMIT_F_INV;
+ assert($<stmt>0->type == STMT_CONNLIMIT);
+
+ $<stmt>0->connlimit.count = $1;
+ }
+ | OVER NUM
+ {
+ assert($<stmt>0->type == STMT_CONNLIMIT);
+
+ $<stmt>0->connlimit.count = $2;
+ $<stmt>0->connlimit.flags = NFT_CONNLIMIT_F_INV;
}
;
@@ -2938,12 +3321,6 @@ counter_stmt_alloc : COUNTER
{
$$ = counter_stmt_alloc(&@$);
}
- | COUNTER NAME stmt_expr
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_COUNTER;
- $$->objref.expr = $3;
- }
;
counter_args : counter_arg
@@ -2955,14 +3332,38 @@ counter_args : counter_arg
counter_arg : PACKETS NUM
{
+ assert($<stmt>0->type == STMT_COUNTER);
$<stmt>0->counter.packets = $2;
}
| BYTES NUM
{
+ assert($<stmt>0->type == STMT_COUNTER);
$<stmt>0->counter.bytes = $2;
}
;
+last_stmt_alloc : LAST
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ ;
+
+last_stmt : last_stmt_alloc
+ | last_stmt_alloc last_args
+ ;
+
+last_args : USED NEVER
+ | USED time_spec
+ {
+ struct last_stmt *last;
+
+ assert($<stmt>0->type == STMT_LAST);
+ last = &$<stmt>0->last;
+ last->used = $2;
+ last->set = true;
+ }
+ ;
+
log_stmt : log_stmt_alloc
| log_stmt_alloc log_args
;
@@ -2983,127 +3384,19 @@ log_args : log_arg
log_arg : PREFIX string
{
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;
+ struct error_record *erec;
+ const char *prefix;
- 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;
- }
+ prefix = str_preprocess(state, &@2, scope, $2, &erec);
+ if (!prefix) {
+ erec_queue(erec, state->msgs);
+ free_const($2);
+ YYERROR;
}
- xfree($2);
- $<stmt>0->log.prefix = expr;
- $<stmt>0->log.flags |= STMT_LOG_PREFIX;
+ free_const($2);
+ $<stmt>0->log.prefix = prefix;
+ $<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
| GROUP NUM
{
@@ -3154,10 +3447,10 @@ level_type : string
else {
erec_queue(error(&@1, "invalid log level"),
state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
@@ -3200,39 +3493,45 @@ log_flag_tcp : SEQUENCE
}
;
-limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
- {
- if ($5 == 0) {
- erec_queue(error(&@5, "limit burst must be > 0"),
- state->msgs);
- YYERROR;
- }
+limit_stmt_alloc : LIMIT RATE
+ {
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4.rate;
- $$->limit.unit = $4.unit;
- $$->limit.burst = $5;
- $$->limit.type = NFT_LIMIT_PKTS;
- $$->limit.flags = $3;
}
- | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
- {
- if ($5 == 0) {
- erec_queue(error(&@5, "limit burst must be > 0"),
+ ;
+
+limit_stmt : limit_stmt_alloc limit_args
+ ;
+
+limit_args : limit_mode limit_rate_pkts limit_burst_pkts
+ {
+ struct limit_stmt *limit;
+
+ assert($<stmt>0->type == STMT_LIMIT);
+
+ if ($3 == 0) {
+ erec_queue(error(&@3, "packet limit burst must be > 0"),
state->msgs);
YYERROR;
}
- $$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4.rate;
- $$->limit.unit = $4.unit;
- $$->limit.burst = $5;
- $$->limit.type = NFT_LIMIT_PKT_BYTES;
- $$->limit.flags = $3;
+ limit = &$<stmt>0->limit;
+ limit->rate = $2.rate;
+ limit->unit = $2.unit;
+ limit->burst = $3;
+ limit->type = NFT_LIMIT_PKTS;
+ limit->flags = $1;
}
- | LIMIT NAME stmt_expr close_scope_limit
+ | limit_mode limit_rate_bytes limit_burst_bytes
{
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_LIMIT;
- $$->objref.expr = $3;
+ struct limit_stmt *limit;
+
+ assert($<stmt>0->type == STMT_LIMIT);
+
+ limit = &$<stmt>0->limit;
+ limit->rate = $2.rate;
+ limit->unit = $2.unit;
+ limit->burst = $3;
+ limit->type = NFT_LIMIT_PKT_BYTES;
+ limit->flags = $1;
}
;
@@ -3252,7 +3551,7 @@ quota_used : /* empty */ { $$ = 0; }
uint64_t rate;
erec = data_unit_parse(&@$, $3, &rate);
- xfree($3);
+ free_const($3);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3261,27 +3560,33 @@ quota_used : /* empty */ { $$ = 0; }
}
;
-quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
+quota_stmt_alloc : QUOTA
+ {
+ $$ = quota_stmt_alloc(&@$);
+ }
+ ;
+
+quota_stmt : quota_stmt_alloc quota_args
+ ;
+
+quota_args : quota_mode NUM quota_unit quota_used
{
struct error_record *erec;
+ struct quota_stmt *quota;
uint64_t rate;
- erec = data_unit_parse(&@$, $4, &rate);
- xfree($4);
+ assert($<stmt>0->type == STMT_QUOTA);
+
+ erec = data_unit_parse(&@$, $3, &rate);
+ free_const($3);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
}
- $$ = quota_stmt_alloc(&@$);
- $$->quota.bytes = $3 * rate;
- $$->quota.used = $5;
- $$->quota.flags = $2;
- }
- | QUOTA NAME stmt_expr close_scope_quota
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_QUOTA;
- $$->objref.expr = $3;
+ quota = &$<stmt>0->quota;
+ quota->bytes = $2 * rate;
+ quota->used = $4;
+ quota->flags = $1;
}
;
@@ -3301,7 +3606,7 @@ limit_rate_pkts : NUM SLASH time_unit
}
;
-limit_burst_bytes : /* empty */ { $$ = 5; }
+limit_burst_bytes : /* empty */ { $$ = 0; }
| BURST limit_bytes { $$ = $2; }
;
@@ -3311,7 +3616,7 @@ limit_rate_bytes : NUM STRING
uint64_t rate, unit;
erec = rate_parse(&@$, $2, &rate, &unit);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3333,7 +3638,7 @@ limit_bytes : NUM BYTES { $$ = $1; }
uint64_t rate;
erec = data_unit_parse(&@$, $2, &rate);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3362,7 +3667,7 @@ reject_with_expr : STRING
{
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state), $1);
- xfree($1);
+ free_const($1);
}
| integer_expr { $$ = $1; }
;
@@ -3377,40 +3682,40 @@ reject_opts : /* empty */
$<stmt>0->reject.family = NFPROTO_IPV4;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmp_code_type);
}
| WITH ICMP reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV4;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmp_code_type);
}
| WITH ICMP6 TYPE reject_with_expr close_scope_type close_scope_icmp
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type);
}
| WITH ICMP6 reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type);
}
| WITH ICMPX TYPE reject_with_expr close_scope_type
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type);
}
| WITH ICMPX reject_with_expr
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type);
}
| WITH TCP close_scope_tcp RESET close_scope_reset
{
@@ -3421,8 +3726,8 @@ reject_opts : /* empty */
nat_stmt : nat_stmt_alloc nat_stmt_args
;
-nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, NFT_NAT_SNAT); }
- | DNAT { $$ = nat_stmt_alloc(&@$, NFT_NAT_DNAT); }
+nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_SNAT); }
+ | DNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_DNAT); }
;
tproxy_stmt : TPROXY TO stmt_expr
@@ -3473,12 +3778,6 @@ synproxy_stmt_alloc : SYNPROXY
{
$$ = synproxy_stmt_alloc(&@$);
}
- | SYNPROXY NAME stmt_expr
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_SYNPROXY;
- $$->objref.expr = $3;
- }
;
synproxy_args : synproxy_arg
@@ -3574,6 +3873,7 @@ primary_stmt_expr : symbol_expr { $$ = $1; }
| payload_expr { $$ = $1; }
| keyword_expr { $$ = $1; }
| socket_expr { $$ = $1; }
+ | fib_expr { $$ = $1; }
| osf_expr { $$ = $1; }
| '(' basic_stmt_expr ')' { $$ = $2; }
;
@@ -3954,36 +4254,7 @@ map_stmt : set_stmt_op set_ref_expr '{' set_elem_expr_stmt COLON set_elem_expr_
}
;
-meter_stmt : flow_stmt_legacy_alloc flow_stmt_opts '{' meter_key_expr stmt '}'
- {
- $1->meter.key = $4;
- $1->meter.stmt = $5;
- $$->location = @$;
- $$ = $1;
- }
- | meter_stmt_alloc { $$ = $1; }
- ;
-
-flow_stmt_legacy_alloc : FLOW
- {
- $$ = meter_stmt_alloc(&@$);
- }
- ;
-
-flow_stmt_opts : flow_stmt_opt
- {
- $<stmt>$ = $<stmt>0;
- }
- | flow_stmt_opts flow_stmt_opt
- ;
-
-flow_stmt_opt : TABLE identifier
- {
- $<stmt>0->meter.name = $2;
- }
- ;
-
-meter_stmt_alloc : METER identifier '{' meter_key_expr stmt '}'
+meter_stmt : METER identifier '{' meter_key_expr stmt '}'
{
$$ = meter_stmt_alloc(&@$);
$$->meter.name = $2;
@@ -4019,19 +4290,19 @@ variable_expr : '$' identifier
sym = symbol_lookup_fuzzy(scope, $2);
if (sym) {
erec_queue(error(&@2, "unknown identifier '%s'; "
- "did you mean identifier ‘%s’?",
+ "did you mean identifier '%s’?",
$2, sym->identifier),
state->msgs);
} else {
erec_queue(error(&@2, "unknown identifier '%s'", $2),
state->msgs);
}
- xfree($2);
+ free_const($2);
YYERROR;
}
$$ = variable_expr_alloc(&@$, scope, sym);
- xfree($2);
+ free_const($2);
}
;
@@ -4041,7 +4312,7 @@ symbol_expr : variable_expr
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state),
$1);
- xfree($1);
+ free_const($1);
}
;
@@ -4054,7 +4325,7 @@ set_ref_symbol_expr : AT identifier close_scope_at
$$ = symbol_expr_alloc(&@$, SYMBOL_SET,
current_scope(state),
$2);
- xfree($2);
+ free_const($2);
}
;
@@ -4069,9 +4340,7 @@ integer_expr : NUM
}
;
-primary_expr : symbol_expr { $$ = $1; }
- | integer_expr { $$ = $1; }
- | payload_expr { $$ = $1; }
+selector_expr : payload_expr { $$ = $1; }
| exthdr_expr { $$ = $1; }
| exthdr_exists_expr { $$ = $1; }
| meta_expr { $$ = $1; }
@@ -4083,35 +4352,48 @@ primary_expr : symbol_expr { $$ = $1; }
| fib_expr { $$ = $1; }
| osf_expr { $$ = $1; }
| xfrm_expr { $$ = $1; }
+ ;
+
+primary_expr : symbol_expr { $$ = $1; }
+ | integer_expr { $$ = $1; }
+ | selector_expr { $$ = $1; }
| '(' basic_expr ')' { $$ = $2; }
;
fib_expr : FIB fib_tuple fib_result close_scope_fib
{
- if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) {
+ uint32_t flags = $2, result = $3;
+
+ if (result == __NFT_FIB_RESULT_MAX) {
+ result = NFT_FIB_RESULT_OIF;
+ flags |= NFTA_FIB_F_PRESENT;
+ }
+
+ if ((flags & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) {
erec_queue(error(&@2, "fib: need either saddr or daddr"), state->msgs);
YYERROR;
}
- if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) ==
- (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) {
+ if ((flags & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) ==
+ (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) {
erec_queue(error(&@2, "fib: saddr and daddr are mutually exclusive"), state->msgs);
YYERROR;
}
- if (($2 & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) ==
- (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) {
+ if ((flags & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) ==
+ (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) {
erec_queue(error(&@2, "fib: iif and oif are mutually exclusive"), state->msgs);
YYERROR;
}
- $$ = fib_expr_alloc(&@$, $2, $3);
+ $$ = fib_expr_alloc(&@$, flags, result);
}
;
fib_result : OIF { $$ =NFT_FIB_RESULT_OIF; }
| OIFNAME { $$ =NFT_FIB_RESULT_OIFNAME; }
| TYPE close_scope_type { $$ =NFT_FIB_RESULT_ADDRTYPE; }
+ | CHECK { $$ = __NFT_FIB_RESULT_MAX; } /* actually, NFT_FIB_F_PRESENT. */
;
fib_flag : SADDR { $$ = NFTA_FIB_F_SADDR; }
@@ -4151,10 +4433,10 @@ osf_ttl : /* empty */
else {
erec_queue(error(&@2, "invalid ttl option"),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
;
@@ -4213,7 +4495,16 @@ prefix_rhs_expr : basic_rhs_expr SLASH NUM
range_rhs_expr : basic_rhs_expr DASH basic_rhs_expr
{
- $$ = range_expr_alloc(&@$, $1, $3);
+ if ($1->etype == EXPR_SYMBOL &&
+ $1->symtype == SYMBOL_VALUE &&
+ $3->etype == EXPR_SYMBOL &&
+ $3->symtype == SYMBOL_VALUE) {
+ $$ = symbol_range_expr_alloc(&@$, $1->symtype, $1->scope, $1->identifier, $3->identifier);
+ expr_free($1);
+ expr_free($3);
+ } else {
+ $$ = range_expr_alloc(&@$, $1, $3);
+ }
}
;
@@ -4282,6 +4573,12 @@ meter_key_expr_alloc : concat_expr
set_elem_expr : set_elem_expr_alloc
| set_elem_expr_alloc set_elem_expr_options
+ | set_elem_expr_alloc set_elem_expr_options set_elem_stmt_list
+ {
+ $$ = $1;
+ list_splice_tail($3, &$$->stmt_list);
+ free($3);
+ }
;
set_elem_key_expr : set_lhs_expr { $$ = $1; }
@@ -4292,7 +4589,7 @@ set_elem_expr_alloc : set_elem_key_expr set_elem_stmt_list
{
$$ = set_elem_expr_alloc(&@1, $1);
list_splice_tail($2, &$$->stmt_list);
- xfree($2);
+ free($2);
}
| set_elem_key_expr
{
@@ -4307,7 +4604,28 @@ set_elem_options : set_elem_option
| set_elem_options set_elem_option
;
-set_elem_option : TIMEOUT time_spec
+set_elem_time_spec : STRING
+ {
+ struct error_record *erec;
+ uint64_t res;
+
+ if (!strcmp("never", $1)) {
+ free_const($1);
+ $$ = NFT_NEVER_TIMEOUT;
+ break;
+ }
+
+ erec = time_parse(&@1, $1, &res);
+ free_const($1);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = res;
+ }
+ ;
+
+set_elem_option : TIMEOUT time_spec
{
$<expr>0->timeout = $2;
}
@@ -4318,7 +4636,7 @@ set_elem_option : TIMEOUT time_spec
| comment_spec
{
if (already_set($<expr>0->comment, &@1, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<expr>0->comment = $1;
@@ -4345,58 +4663,14 @@ set_elem_stmt_list : set_elem_stmt
}
;
-set_elem_stmt : COUNTER close_scope_counter
- {
- $$ = counter_stmt_alloc(&@$);
- }
- | COUNTER PACKETS NUM BYTES NUM close_scope_counter
- {
- $$ = counter_stmt_alloc(&@$);
- $$->counter.packets = $3;
- $$->counter.bytes = $5;
- }
- | LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
- {
- if ($5 == 0) {
- erec_queue(error(&@5, "limit burst must be > 0"),
- state->msgs);
- YYERROR;
- }
- $$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4.rate;
- $$->limit.unit = $4.unit;
- $$->limit.burst = $5;
- $$->limit.type = NFT_LIMIT_PKTS;
- $$->limit.flags = $3;
- }
- | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
- {
- if ($5 == 0) {
- erec_queue(error(&@6, "limit burst must be > 0"),
- state->msgs);
- YYERROR;
- }
- $$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4.rate;
- $$->limit.unit = $4.unit;
- $$->limit.burst = $5;
- $$->limit.type = NFT_LIMIT_PKT_BYTES;
- $$->limit.flags = $3;
- }
- | CT COUNT NUM close_scope_ct
- {
- $$ = connlimit_stmt_alloc(&@$);
- $$->connlimit.count = $3;
- }
- | CT COUNT OVER NUM close_scope_ct
- {
- $$ = connlimit_stmt_alloc(&@$);
- $$->connlimit.count = $4;
- $$->connlimit.flags = NFT_CONNLIMIT_F_INV;
- }
+set_elem_stmt : counter_stmt close_scope_counter
+ | limit_stmt close_scope_limit
+ | connlimit_stmt close_scope_ct
+ | quota_stmt close_scope_quota
+ | last_stmt close_scope_last
;
-set_elem_expr_option : TIMEOUT time_spec
+set_elem_expr_option : TIMEOUT set_elem_time_spec
{
$<expr>0->timeout = $2;
}
@@ -4407,7 +4681,7 @@ set_elem_expr_option : TIMEOUT time_spec
| comment_spec
{
if (already_set($<expr>0->comment, &@1, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<expr>0->comment = $1;
@@ -4459,7 +4733,7 @@ quota_config : quota_mode NUM quota_unit quota_used
uint64_t rate;
erec = data_unit_parse(&@$, $3, &rate);
- xfree($3);
+ free_const($3);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -4488,10 +4762,10 @@ secmark_config : string
ret = snprintf(secmark->ctx, sizeof(secmark->ctx), "%s", $1);
if (ret <= 0 || ret >= (int)sizeof(secmark->ctx)) {
erec_queue(error(&@1, "invalid context '%s', max length is %u\n", $1, (int)sizeof(secmark->ctx)), state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
@@ -4508,27 +4782,34 @@ ct_obj_type : HELPER { $$ = NFT_OBJECT_CT_HELPER; }
;
ct_cmd_type : HELPERS { $$ = CMD_OBJ_CT_HELPERS; }
- | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUT; }
- | EXPECTATION { $$ = CMD_OBJ_CT_EXPECT; }
+ | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUTS; }
+ | EXPECTATION { $$ = CMD_OBJ_CT_EXPECTATIONS; }
;
ct_l4protoname : TCP close_scope_tcp { $$ = IPPROTO_TCP; }
| UDP close_scope_udp { $$ = IPPROTO_UDP; }
;
-ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type
+ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type
{
struct ct_helper *ct;
int ret;
ct = &$<obj>0->ct_helper;
+ if (ct->l4proto) {
+ erec_queue(error(&@2, "You can only specify this once. This statement is already set for %s.", ct->name), state->msgs);
+ free_const($2);
+ YYERROR;
+ }
+
ret = snprintf(ct->name, sizeof(ct->name), "%s", $2);
if (ret <= 0 || ret >= (int)sizeof(ct->name)) {
erec_queue(error(&@2, "invalid name '%s', max length is %u\n", $2, (int)sizeof(ct->name)), state->msgs);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
ct->l4proto = $4;
}
@@ -4542,17 +4823,16 @@ timeout_states : timeout_state
{
$$ = xmalloc(sizeof(*$$));
init_list_head($$);
- list_add_tail($1, $$);
+ list_add_tail(&$1->head, $$);
}
| timeout_states COMMA timeout_state
{
- list_add_tail($3, $1);
+ list_add_tail(&$3->head, $1);
$$ = $1;
}
;
-timeout_state : STRING COLON NUM
-
+timeout_state : STRING COLON time_spec_or_num_s
{
struct timeout_state *ts;
@@ -4561,7 +4841,7 @@ timeout_state : STRING COLON NUM
ts->timeout_value = $3;
ts->location = @1;
init_list_head(&ts->head);
- $$ = &ts->head;
+ $$ = ts;
}
;
@@ -4579,7 +4859,7 @@ ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator
ct = &$<obj>0->ct_timeout;
list_splice_tail($4, &ct->timeout_list);
- xfree($4);
+ free($4);
}
| L3PROTOCOL family_spec_explicit stmt_separator
{
@@ -4656,19 +4936,33 @@ relational_expr : expr /* implicit */ rhs_expr
}
| expr /* implicit */ basic_rhs_expr SLASH list_rhs_expr
{
- $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2);
+ struct expr *mask = list_expr_to_binop($4);
+ struct expr *binop = binop_expr_alloc(&@$, OP_AND, $1, mask);
+
+ $$ = relational_expr_alloc(&@$, OP_IMPLICIT, binop, $2);
}
| expr /* implicit */ list_rhs_expr SLASH list_rhs_expr
{
- $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2);
+ struct expr *value = list_expr_to_binop($2);
+ struct expr *mask = list_expr_to_binop($4);
+ struct expr *binop = binop_expr_alloc(&@$, OP_AND, $1, mask);
+
+ $$ = relational_expr_alloc(&@$, OP_IMPLICIT, binop, value);
}
| expr relational_op basic_rhs_expr SLASH list_rhs_expr
{
- $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3);
+ struct expr *mask = list_expr_to_binop($5);
+ struct expr *binop = binop_expr_alloc(&@$, OP_AND, $1, mask);
+
+ $$ = relational_expr_alloc(&@$, $2, binop, $3);
}
| expr relational_op list_rhs_expr SLASH list_rhs_expr
{
- $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3);
+ struct expr *value = list_expr_to_binop($3);
+ struct expr *mask = list_expr_to_binop($5);
+ struct expr *binop = binop_expr_alloc(&@$, OP_AND, $1, mask);
+
+ $$ = relational_expr_alloc(&@$, $2, binop, value);
}
| expr relational_op rhs_expr
{
@@ -4777,9 +5071,11 @@ keyword_expr : ETHER close_scope_eth { $$ = symbol_value(&@$, "ether"); }
| SNAT close_scope_nat { $$ = symbol_value(&@$, "snat"); }
| ECN { $$ = symbol_value(&@$, "ecn"); }
| RESET close_scope_reset { $$ = symbol_value(&@$, "reset"); }
+ | DESTROY close_scope_destroy { $$ = symbol_value(&@$, "destroy"); }
| ORIGINAL { $$ = symbol_value(&@$, "original"); }
| REPLY { $$ = symbol_value(&@$, "reply"); }
| LABEL { $$ = symbol_value(&@$, "label"); }
+ | LAST close_scope_last { $$ = symbol_value(&@$, "last"); }
;
primary_rhs_expr : symbol_expr { $$ = $1; }
@@ -4842,6 +5138,13 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
+ | GRE close_scope_gre
+ {
+ uint8_t data = IPPROTO_GRE;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
| COMP close_scope_comp
{
uint8_t data = IPPROTO_COMP;
@@ -4915,7 +5218,7 @@ chain_expr : variable_expr
BYTEORDER_HOST_ENDIAN,
strlen($1) * BITS_PER_BYTE,
$1);
- xfree($1);
+ free_const($1);
}
;
@@ -4933,7 +5236,7 @@ meta_expr : META meta_key close_scope_meta
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -5010,9 +5313,10 @@ meta_stmt : META meta_key SET stmt_expr close_scope_meta
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
+ expr_free($4);
YYERROR;
}
@@ -5251,19 +5555,6 @@ ct_stmt : CT ct_key SET stmt_expr close_scope_ct
break;
}
}
- | CT TIMEOUT SET stmt_expr close_scope_ct
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
- $$->objref.expr = $4;
-
- }
- | CT EXPECTATION SET stmt_expr close_scope_ct
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_CT_EXPECT;
- $$->objref.expr = $4;
- }
| CT ct_dir ct_key_dir_optional SET stmt_expr close_scope_ct
{
$$ = ct_stmt_alloc(&@$, $3, $2, $5);
@@ -5297,9 +5588,31 @@ payload_expr : payload_raw_expr
| dccp_hdr_expr
| sctp_hdr_expr
| th_hdr_expr
+ | vxlan_hdr_expr
+ | geneve_hdr_expr
+ | gre_hdr_expr
+ | gretap_hdr_expr
;
-payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM close_scope_at
+payload_raw_len : NUM
+ {
+ if ($1 > NFT_MAX_EXPR_LEN_BITS) {
+ erec_queue(error(&@1, "raw payload length %u exceeds upper limit of %u",
+ $1, NFT_MAX_EXPR_LEN_BITS),
+ state->msgs);
+ YYERROR;
+ }
+
+ if ($1 == 0) {
+ erec_queue(error(&@1, "raw payload length cannot be 0"), state->msgs);
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+payload_raw_expr : AT payload_base_spec COMMA NUM COMMA payload_raw_len close_scope_at
{
$$ = payload_expr_alloc(&@$, NULL, 0);
payload_init_raw($$, $2, $4, $6);
@@ -5317,10 +5630,10 @@ payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; }
$$ = PROTO_BASE_INNER_HDR;
} else {
erec_queue(error(&@1, "unknown raw payload base"), state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
@@ -5376,6 +5689,9 @@ ip_hdr_expr : IP ip_hdr_field close_scope_ip
erec_queue(error(&@1, "unknown ip option type/field"), state->msgs);
YYERROR;
}
+
+ if ($4 == IPOPT_FIELD_TYPE)
+ $$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
| IP OPTION ip_option_type close_scope_ip
{
@@ -5468,6 +5784,8 @@ icmp6_hdr_field : TYPE close_scope_type { $$ = ICMP6HDR_TYPE; }
| ID { $$ = ICMP6HDR_ID; }
| SEQUENCE { $$ = ICMP6HDR_SEQ; }
| MAXDELAY { $$ = ICMP6HDR_MAXDELAY; }
+ | TADDR { $$ = ICMP6HDR_TADDR; }
+ | DADDR { $$ = ICMP6HDR_DADDR; }
;
auth_hdr_expr : AH auth_hdr_field close_scope_ah
@@ -5540,14 +5858,106 @@ tcp_hdr_expr : TCP tcp_hdr_field
| TCP OPTION tcp_hdr_option_kind_and_field
{
$$ = tcpopt_expr_alloc(&@$, $3.kind, $3.field);
+ if ($$ == NULL) {
+ erec_queue(error(&@1, "Could not find a tcp option template"), state->msgs);
+ YYERROR;
+ }
}
- | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA NUM
+ | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA payload_raw_len
{
$$ = tcpopt_expr_alloc(&@$, $5, 0);
tcpopt_init_raw($$, $5, $7, $9, 0);
}
;
+inner_inet_expr : ip_hdr_expr
+ | icmp_hdr_expr
+ | igmp_hdr_expr
+ | ip6_hdr_expr
+ | icmp6_hdr_expr
+ | auth_hdr_expr
+ | esp_hdr_expr
+ | comp_hdr_expr
+ | udp_hdr_expr
+ | udplite_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
+ | dccp_hdr_expr
+ | sctp_hdr_expr
+ | th_hdr_expr
+ ;
+
+inner_eth_expr : eth_hdr_expr
+ | vlan_hdr_expr
+ | arp_hdr_expr
+ ;
+
+inner_expr : inner_eth_expr
+ | inner_inet_expr
+ ;
+
+vxlan_hdr_expr : VXLAN vxlan_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_vxlan, $2);
+ expr->payload.inner_desc = &proto_vxlan;
+ $$ = expr;
+ }
+ | VXLAN inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_vxlan;
+ }
+ ;
+
+vxlan_hdr_field : VNI { $$ = VXLANHDR_VNI; }
+ | FLAGS { $$ = VXLANHDR_FLAGS; }
+ ;
+
+geneve_hdr_expr : GENEVE geneve_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_geneve, $2);
+ expr->payload.inner_desc = &proto_geneve;
+ $$ = expr;
+ }
+ | GENEVE inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_geneve;
+ }
+ ;
+
+geneve_hdr_field : VNI { $$ = GNVHDR_VNI; }
+ | TYPE { $$ = GNVHDR_TYPE; }
+ ;
+
+gre_hdr_expr : GRE gre_hdr_field close_scope_gre
+ {
+ $$ = payload_expr_alloc(&@$, &proto_gre, $2);
+ }
+ | GRE close_scope_gre inner_inet_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gre;
+ }
+ ;
+
+gre_hdr_field : HDRVERSION { $$ = GREHDR_VERSION; }
+ | FLAGS { $$ = GREHDR_FLAGS; }
+ | PROTOCOL { $$ = GREHDR_PROTOCOL; }
+ ;
+
+gretap_hdr_expr : GRETAP close_scope_gre inner_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gretap;
+ }
+ ;
+
optstrip_stmt : RESET TCP OPTION tcp_hdr_option_type close_scope_tcp
{
$$ = optstrip_stmt_alloc(&@$, tcpopt_expr_alloc(&@$,
@@ -5647,6 +6057,15 @@ dccp_hdr_expr : DCCP dccp_hdr_field close_scope_dccp
{
$$ = payload_expr_alloc(&@$, &proto_dccp, $2);
}
+ | DCCP OPTION NUM close_scope_dccp
+ {
+ if ($3 > DCCPOPT_TYPE_MAX) {
+ erec_queue(error(&@1, "value too large"),
+ state->msgs);
+ YYERROR;
+ }
+ $$ = dccpopt_expr_alloc(&@$, $3);
+ }
;
dccp_hdr_field : SPORT { $$ = DCCPHDR_SPORT; }
@@ -5895,7 +6314,7 @@ exthdr_exists_expr : EXTHDR exthdr_key
desc = exthdr_find_proto($2);
/* Assume that NEXTHDR template is always
- * the fist one in list of templates.
+ * the first one in list of templates.
*/
$$ = exthdr_expr_alloc(&@$, desc, 1);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
diff --git a/src/parser_json.c b/src/parser_json.c
index 9e93927a..08657f28 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1,7 +1,14 @@
-#define _GNU_SOURCE
+/*
+ * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <errno.h>
-#include <stdint.h> /* needed by gmputil.h */
-#include <string.h>
#include <syslog.h>
#include <erec.h>
@@ -11,6 +18,7 @@
#include <netlink.h>
#include <parser.h>
#include <rule.h>
+#include <cmd.h>
#include <sctp_chunk.h>
#include <socket.h>
@@ -42,6 +50,7 @@
#define CTX_F_SES (1 << 6) /* set_elem_expr_stmt */
#define CTX_F_MAP (1 << 7) /* LHS of map_expr */
#define CTX_F_CONCAT (1 << 8) /* inside concat_expr */
+#define CTX_F_COLLAPSED (1 << 9)
struct json_ctx {
struct nft_ctx *nft;
@@ -114,7 +123,6 @@ static void json_lib_error(struct json_ctx *ctx, json_error_t *err)
.indesc = &json_indesc,
.line_offset = err->position - err->column,
.first_line = err->line,
- .last_line = err->line,
.first_column = err->column,
/* no information where problematic part ends :( */
.last_column = err->column,
@@ -174,8 +182,11 @@ static int json_unpack_stmt(struct json_ctx *ctx, json_t *root,
assert(value);
if (json_object_size(root) != 1) {
+ const char *dump = json_dumps(root, 0);
+
json_error(ctx, "Malformed object (too many properties): '%s'.",
- json_dumps(root, 0));
+ dump);
+ free_const(dump);
return 1;
}
@@ -186,6 +197,60 @@ static int json_unpack_stmt(struct json_ctx *ctx, json_t *root,
return 1;
}
+/**
+ * parse_flags_array - parse JSON property as an array of flags
+ *
+ * @ctx: JSON parser context
+ * @obj: JSON object to extract property from
+ * @key: name of property containing the flags array
+ * @flag_parser: Callback parsing a single flag, returns 0 on error
+ *
+ * The property value may be a string representing a single flag or an array of
+ * strings representing a number of flags whose values are ORed together.
+ *
+ * @return: Combined flag value, 0 if no such property found or -1 if data is
+ * malformed or flag parsing failed.
+ */
+static int parse_flags_array(struct json_ctx *ctx, json_t *obj, const char *key,
+ unsigned int (*flag_parser)(const char *flag))
+{
+ json_t *value = json_object_get(obj, key), *tmp;
+ size_t index;
+ int ret = 0;
+
+ if (!value)
+ return 0;
+
+ if (json_is_string(value)) {
+ ret = flag_parser(json_string_value(value));
+ return ret ?: -1;
+ }
+
+ if (!json_is_array(value)) {
+ json_error(ctx,
+ "Expecting string or array in '%s' property.", key);
+ return -1;
+ }
+
+ json_array_foreach(value, index, tmp) {
+ int flag = 0;
+
+ if (json_is_string(tmp))
+ flag = flag_parser(json_string_value(tmp));
+
+ if (!flag) {
+ json_error(ctx,
+ "Invalid flag in '%s' property array at index %zu.",
+ key, index);
+ return -1;
+ }
+
+ ret |= flag;
+ }
+
+ return ret;
+}
+
static int parse_family(const char *name, uint32_t *family)
{
unsigned int i;
@@ -533,6 +598,27 @@ static const struct proto_desc *proto_lookup_byname(const char *name)
&proto_dccp,
&proto_sctp,
&proto_th,
+ &proto_vxlan,
+ &proto_gre,
+ &proto_gretap,
+ &proto_geneve,
+ };
+ unsigned int i;
+
+ for (i = 0; i < array_size(proto_tbl); i++) {
+ if (!strcmp(proto_tbl[i]->name, name))
+ return proto_tbl[i];
+ }
+ return NULL;
+}
+
+static const struct proto_desc *inner_proto_lookup_byname(const char *name)
+{
+ const struct proto_desc *proto_tbl[] = {
+ &proto_geneve,
+ &proto_gre,
+ &proto_gretap,
+ &proto_vxlan,
};
unsigned int i;
@@ -546,7 +632,7 @@ static const struct proto_desc *proto_lookup_byname(const char *name)
static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
const char *type, json_t *root)
{
- const char *protocol, *field, *base;
+ const char *tunnel, *protocol, *field, *base;
int offset, len, val;
struct expr *expr;
@@ -564,11 +650,45 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
json_error(ctx, "Invalid payload base '%s'.", base);
return NULL;
}
+
+ if (len <= 0 || len > (int)NFT_MAX_EXPR_LEN_BITS) {
+ json_error(ctx, "Payload length must be between 0 and %lu, got %d",
+ NFT_MAX_EXPR_LEN_BITS, len);
+ return NULL;
+ }
+
expr = payload_expr_alloc(int_loc, NULL, 0);
payload_init_raw(expr, val, offset, len);
expr->byteorder = BYTEORDER_BIG_ENDIAN;
expr->payload.is_raw = true;
return expr;
+ } else if (!json_unpack(root, "{s:s, s:s, s:s}",
+ "tunnel", &tunnel, "protocol", &protocol, "field", &field)) {
+ const struct proto_desc *proto = proto_lookup_byname(protocol);
+ const struct proto_desc *inner_proto = inner_proto_lookup_byname(tunnel);
+
+ if (!inner_proto) {
+ json_error(ctx, "Unknown payload tunnel protocol '%s'.",
+ tunnel);
+ return NULL;
+ }
+ if (!proto) {
+ json_error(ctx, "Unknown payload protocol '%s'.",
+ protocol);
+ return NULL;
+ }
+ if (json_parse_payload_field(proto, field, &val)) {
+ json_error(ctx, "Unknown %s field '%s'.",
+ protocol, field);
+ return NULL;
+ }
+ expr = payload_expr_alloc(int_loc, proto, val);
+ expr->payload.inner_desc = inner_proto;
+
+ if (proto == &proto_th)
+ expr->payload.is_raw = true;
+
+ return expr;
} else if (!json_unpack(root, "{s:s, s:s}",
"protocol", &protocol, "field", &field)) {
const struct proto_desc *proto = proto_lookup_byname(protocol);
@@ -602,12 +722,18 @@ static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx,
struct expr *expr;
if (!json_unpack(root, "{s:i, s:i, s:i}",
- "base", &kind, "offset", &offset, "len", &len)) {
+ "base", &kind, "offset", &offset, "len", &len)) {
uint32_t flag = 0;
if (kind < 0 || kind > 255)
return NULL;
+ if (len < 0 || len > (int)NFT_MAX_EXPR_LEN_BITS) {
+ json_error(ctx, "option length must be between 0 and %lu, got %d",
+ NFT_MAX_EXPR_LEN_BITS, len);
+ return NULL;
+ }
+
expr = tcpopt_expr_alloc(int_loc, kind,
TCPOPT_COMMON_KIND);
@@ -673,7 +799,7 @@ static int json_parse_ip_option_field(int type, const char *name, int *val)
}
static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
- const char *type, json_t *root)
+ const char *type, json_t *root)
{
const char *desc, *field;
int descval, fieldval;
@@ -689,7 +815,7 @@ static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
if (json_unpack(root, "{s:s}", "field", &field)) {
expr = ipopt_expr_alloc(int_loc, descval,
- IPOPT_FIELD_TYPE);
+ IPOPT_FIELD_TYPE);
expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
return expr;
@@ -748,6 +874,22 @@ static struct expr *json_parse_sctp_chunk_expr(struct json_ctx *ctx,
return sctp_chunk_expr_alloc(int_loc, desc->type, fieldval);
}
+static struct expr *json_parse_dccp_option_expr(struct json_ctx *ctx,
+ const char *type, json_t *root)
+{
+ int opt_type;
+
+ if (json_unpack_err(ctx, root, "{s:i}", "type", &opt_type))
+ return NULL;
+
+ if (opt_type < DCCPOPT_TYPE_MIN || opt_type > DCCPOPT_TYPE_MAX) {
+ json_error(ctx, "Unknown dccp option type '%d'.", opt_type);
+ return NULL;
+ }
+
+ return dccpopt_expr_alloc(int_loc, opt_type);
+}
+
static const struct exthdr_desc *exthdr_lookup_byname(const char *name)
{
const struct exthdr_desc *exthdr_tbl[] = {
@@ -989,7 +1131,7 @@ static struct expr *json_parse_hash_expr(struct json_ctx *ctx,
return hash_expr;
}
-static int fib_flag_parse(const char *name, int *flags)
+static unsigned int fib_flag_parse(const char *name)
{
const char *fib_flags[] = {
"saddr",
@@ -1001,12 +1143,10 @@ static int fib_flag_parse(const char *name, int *flags)
unsigned int i;
for (i = 0; i < array_size(fib_flags); i++) {
- if (!strcmp(name, fib_flags[i])) {
- *flags |= (1 << i);
- return 0;
- }
+ if (!strcmp(name, fib_flags[i]))
+ return 1 << i;
}
- return 1;
+ return 0;
}
static struct expr *json_parse_fib_expr(struct json_ctx *ctx,
@@ -1017,13 +1157,12 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx,
[NFT_FIB_RESULT_OIF] = "oif",
[NFT_FIB_RESULT_OIFNAME] = "oifname",
[NFT_FIB_RESULT_ADDRTYPE] = "type",
+ [__NFT_FIB_RESULT_MAX] = "check", /* Actually, NFT_FIB_F_PRESENT. */
};
enum nft_fib_result resultval = NFT_FIB_RESULT_UNSPEC;
- json_t *flags, *value;
const char *result;
- unsigned int i;
- size_t index;
int flagval = 0;
+ unsigned int i;
if (json_unpack_err(ctx, root, "{s:s}", "result", &result))
return NULL;
@@ -1034,39 +1173,21 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx,
break;
}
}
- if (resultval == NFT_FIB_RESULT_UNSPEC) {
+ switch (resultval) {
+ case NFT_FIB_RESULT_UNSPEC:
json_error(ctx, "Invalid fib result '%s'.", result);
return NULL;
+ case __NFT_FIB_RESULT_MAX:
+ resultval = NFT_FIB_RESULT_OIF;
+ flagval = NFTA_FIB_F_PRESENT;
+ break;
+ default:
+ break;
}
- if (!json_unpack(root, "{s:o}", "flags", &flags)) {
- const char *flag;
-
- if (json_is_string(flags)) {
- flag = json_string_value(flags);
-
- if (fib_flag_parse(flag, &flagval)) {
- json_error(ctx, "Invalid fib flag '%s'.", flag);
- return NULL;
- }
- } else if (!json_is_array(flags)) {
- json_error(ctx, "Unexpected object type in fib tuple.");
- return NULL;
- }
-
- json_array_foreach(flags, index, value) {
- if (!json_is_string(value)) {
- json_error(ctx, "Unexpected object type in fib flags array at index %zd.", index);
- return NULL;
- }
- flag = json_string_value(value);
-
- if (fib_flag_parse(flag, &flagval)) {
- json_error(ctx, "Invalid fib flag '%s'.", flag);
- return NULL;
- }
- }
- }
+ flagval |= parse_flags_array(ctx, root, "flags", fib_flag_parse);
+ if (flagval < 0)
+ return NULL;
/* sanity checks from fib_expr in parser_bison.y */
@@ -1076,13 +1197,13 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx,
}
if ((flagval & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) ==
- (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) {
+ (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) {
json_error(ctx, "fib: saddr and daddr are mutually exclusive");
return NULL;
}
if ((flagval & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) ==
- (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) {
+ (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) {
json_error(ctx, "fib: iif and oif are mutually exclusive");
return NULL;
}
@@ -1120,6 +1241,32 @@ static struct expr *json_parse_binop_expr(struct json_ctx *ctx,
return NULL;
}
+ if (json_array_size(root) > 2) {
+ left = json_parse_primary_expr(ctx, json_array_get(root, 0));
+ if (!left) {
+ json_error(ctx, "Failed to parse LHS of binop expression.");
+ return NULL;
+ }
+ right = json_parse_primary_expr(ctx, json_array_get(root, 1));
+ if (!right) {
+ json_error(ctx, "Failed to parse RHS of binop expression.");
+ expr_free(left);
+ return NULL;
+ }
+ left = binop_expr_alloc(int_loc, thisop, left, right);
+ for (i = 2; i < json_array_size(root); i++) {
+ jright = json_array_get(root, i);
+ right = json_parse_primary_expr(ctx, jright);
+ if (!right) {
+ json_error(ctx, "Failed to parse RHS of binop expression.");
+ expr_free(left);
+ return NULL;
+ }
+ left = binop_expr_alloc(int_loc, thisop, left, right);
+ }
+ return left;
+ }
+
if (json_unpack_err(ctx, root, "[o, o!]", &jleft, &jright))
return NULL;
@@ -1137,6 +1284,16 @@ static struct expr *json_parse_binop_expr(struct json_ctx *ctx,
return binop_expr_alloc(int_loc, thisop, left, right);
}
+static struct expr *json_check_concat_expr(struct json_ctx *ctx, struct expr *e)
+{
+ if (e->size >= 2)
+ return e;
+
+ json_error(ctx, "Concatenation with %d elements is illegal", e->size);
+ expr_free(e);
+ return NULL;
+}
+
static struct expr *json_parse_concat_expr(struct json_ctx *ctx,
const char *type, json_t *root)
{
@@ -1170,7 +1327,7 @@ static struct expr *json_parse_concat_expr(struct json_ctx *ctx,
}
compound_expr_add(expr, tmp);
}
- return expr;
+ return expr ? json_check_concat_expr(ctx, expr) : NULL;
}
static struct expr *json_parse_prefix_expr(struct json_ctx *ctx,
@@ -1209,6 +1366,7 @@ static struct expr *json_parse_range_expr(struct json_ctx *ctx,
expr_high = json_parse_primary_expr(ctx, high);
if (!expr_high) {
json_error(ctx, "Invalid high value in range expression.");
+ expr_free(expr_low);
return NULL;
}
return range_expr_alloc(int_loc, expr_low, expr_high);
@@ -1245,9 +1403,13 @@ static struct expr *json_parse_verdict_expr(struct json_ctx *ctx,
if (strcmp(type, verdict_tbl[i].name))
continue;
- if (verdict_tbl[i].need_chain &&
- json_unpack_err(ctx, root, "{s:s}", "target", &chain))
- return NULL;
+ if (verdict_tbl[i].need_chain) {
+ if (json_unpack_err(ctx, root, "{s:s}", "target", &chain))
+ return NULL;
+
+ if (!chain || chain[0] == '\0')
+ return NULL;
+ }
return verdict_expr_alloc(int_loc, verdict_tbl[i].verdict,
json_alloc_chain_expr(chain));
@@ -1454,17 +1616,18 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
{ "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
{ "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
{ "sctp chunk", json_parse_sctp_chunk_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
- { "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "dccp option", json_parse_dccp_option_expr, CTX_F_PRIMARY },
+ { "meta", json_parse_meta_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT },
{ "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT },
{ "socket", json_parse_socket_expr, CTX_F_PRIMARY | CTX_F_CONCAT },
{ "rt", json_parse_rt_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
- { "ct", json_parse_ct_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "ct", json_parse_ct_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "numgen", json_parse_numgen_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
/* below two are hash expr */
{ "jhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "symhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
- { "fib", json_parse_fib_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "fib", json_parse_fib_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "|", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "^", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "&", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
@@ -1627,8 +1790,18 @@ static struct expr *json_parse_dtype_expr(struct json_ctx *ctx, json_t *root)
}
compound_expr_add(expr, i);
}
- return expr;
+
+ return json_check_concat_expr(ctx, expr);
+ } else if (json_is_object(root)) {
+ const char *key;
+ json_t *val;
+
+ if (!json_unpack_stmt(ctx, root, &key, &val) &&
+ !strcmp(key, "typeof")) {
+ return json_parse_expr(ctx, val);
+ }
}
+
json_error(ctx, "Invalid set datatype.");
return NULL;
}
@@ -1652,13 +1825,18 @@ static struct stmt *json_parse_match_stmt(struct json_ctx *ctx,
!strcmp(opstr, expr_op_symbols[op]))
break;
}
- if (op == __OP_MAX) {
+ switch (op) {
+ case OP_EQ ... OP_NEG:
+ break;
+ case __OP_MAX:
if (!strcmp(opstr, "in")) {
op = OP_IMPLICIT;
- } else {
- json_error(ctx, "Unknown relational op '%s'.", opstr);
- return NULL;
+ break;
}
+ /* fall through */
+ default:
+ json_error(ctx, "Invalid relational op '%s'.", opstr);
+ return NULL;
}
left = json_parse_expr(ctx, jleft);
@@ -1678,7 +1856,7 @@ static struct stmt *json_parse_match_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
uint64_t packets, bytes;
struct stmt *stmt;
@@ -1687,8 +1865,8 @@ static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx,
return counter_stmt_alloc(int_loc);
if (!json_unpack(value, "{s:I, s:I}",
- "packets", &packets,
- "bytes", &bytes)) {
+ "packets", &packets,
+ "bytes", &bytes)) {
stmt = counter_stmt_alloc(int_loc);
stmt->counter.packets = packets;
stmt->counter.bytes = bytes;
@@ -1706,6 +1884,27 @@ static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx,
return stmt;
}
+static struct stmt *json_parse_last_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct stmt *stmt;
+ int64_t used;
+
+ if (json_is_null(value))
+ return last_stmt_alloc(int_loc);
+
+ if (!json_unpack(value, "{s:I}", "used", &used)) {
+ stmt = last_stmt_alloc(int_loc);
+ if (used != -1) {
+ stmt->last.used = used;
+ stmt->last.set = 1;
+ }
+ return stmt;
+ }
+
+ return NULL;
+}
+
static struct stmt *json_parse_verdict_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
@@ -1719,14 +1918,14 @@ static struct stmt *json_parse_verdict_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_mangle_stmt(struct json_ctx *ctx,
- const char *type, json_t *root)
+ const char *type, json_t *root)
{
json_t *jkey, *jvalue;
struct expr *key, *value;
struct stmt *stmt;
if (json_unpack_err(ctx, root, "{s:o, s:o}",
- "key", &jkey, "value", &jvalue))
+ "key", &jkey, "value", &jvalue))
return NULL;
key = json_parse_mangle_lhs_expr(ctx, jkey);
@@ -1763,6 +1962,8 @@ static struct stmt *json_parse_mangle_stmt(struct json_ctx *ctx,
return stmt;
default:
json_error(ctx, "Invalid mangle statement key expression type.");
+ expr_free(key);
+ expr_free(value);
return NULL;
}
}
@@ -1779,7 +1980,7 @@ static uint64_t rate_to_bytes(uint64_t val, const char *unit)
}
static struct stmt *json_parse_quota_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
struct stmt *stmt;
int inv = 0;
@@ -1826,7 +2027,7 @@ static struct stmt *json_parse_limit_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
struct stmt *stmt;
- uint64_t rate, burst = 5;
+ uint64_t rate, burst = 0;
const char *rate_unit = "packets", *time, *burst_unit = "bytes";
int inv = 0;
@@ -1840,6 +2041,9 @@ static struct stmt *json_parse_limit_stmt(struct json_ctx *ctx,
stmt = limit_stmt_alloc(int_loc);
if (!strcmp(rate_unit, "packets")) {
+ if (burst == 0)
+ burst = 5;
+
stmt->limit.type = NFT_LIMIT_PKTS;
stmt->limit.rate = rate;
stmt->limit.burst = burst;
@@ -1926,7 +2130,7 @@ static struct stmt *json_parse_flow_offload_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_notrack_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
return notrack_stmt_alloc(int_loc);
}
@@ -1963,8 +2167,24 @@ static struct stmt *json_parse_dup_stmt(struct json_ctx *ctx,
return stmt;
}
-static int json_parse_nat_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct stmt *stmt;
+
+ stmt = objref_stmt_alloc(int_loc);
+ stmt->objref.type = NFT_OBJECT_SECMARK;
+ stmt->objref.expr = json_parse_stmt_expr(ctx, value);
+ if (!stmt->objref.expr) {
+ json_error(ctx, "Invalid secmark reference.");
+ stmt_free(stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
+static unsigned int json_parse_nat_flag(const char *flag)
{
const struct {
const char *flag;
@@ -1975,51 +2195,16 @@ static int json_parse_nat_flag(struct json_ctx *ctx,
{ "persistent", NF_NAT_RANGE_PERSISTENT },
{ "netmap", NF_NAT_RANGE_NETMAP },
};
- const char *flag;
unsigned int i;
- assert(flags);
-
- if (!json_is_string(root)) {
- json_error(ctx, "Invalid nat flag type %s, expected string.",
- json_typename(root));
- return 1;
- }
- flag = json_string_value(root);
for (i = 0; i < array_size(flag_tbl); i++) {
- if (!strcmp(flag, flag_tbl[i].flag)) {
- *flags |= flag_tbl[i].val;
- return 0;
- }
- }
- json_error(ctx, "Unknown nat flag '%s'.", flag);
- return 1;
-}
-
-static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root)
-{
- int flags = 0;
- json_t *value;
- size_t index;
-
- if (json_is_string(root)) {
- json_parse_nat_flag(ctx, root, &flags);
- return flags;
- } else if (!json_is_array(root)) {
- json_error(ctx, "Invalid nat flags type %s.",
- json_typename(root));
- return -1;
- }
- json_array_foreach(root, index, value) {
- if (json_parse_nat_flag(ctx, value, &flags))
- json_error(ctx, "Parsing nat flag at index %zu failed.",
- index);
+ if (!strcmp(flag, flag_tbl[i].flag))
+ return flag_tbl[i].val;
}
- return flags;
+ return 0;
}
-static int json_parse_nat_type_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+static unsigned int json_parse_nat_type_flag(const char *flag)
{
const struct {
const char *flag;
@@ -2029,47 +2214,13 @@ static int json_parse_nat_type_flag(struct json_ctx *ctx,
{ "prefix", STMT_NAT_F_PREFIX },
{ "concat", STMT_NAT_F_CONCAT },
};
- const char *flag;
unsigned int i;
- assert(flags);
-
- if (!json_is_string(root)) {
- json_error(ctx, "Invalid nat type flag type %s, expected string.",
- json_typename(root));
- return 1;
- }
- flag = json_string_value(root);
for (i = 0; i < array_size(flag_tbl); i++) {
- if (!strcmp(flag, flag_tbl[i].flag)) {
- *flags |= flag_tbl[i].val;
- return 0;
- }
- }
- json_error(ctx, "Unknown nat type flag '%s'.", flag);
- return 1;
-}
-
-static int json_parse_nat_type_flags(struct json_ctx *ctx, json_t *root)
-{
- int flags = 0;
- json_t *value;
- size_t index;
-
- if (json_is_string(root)) {
- json_parse_nat_type_flag(ctx, root, &flags);
- return flags;
- } else if (!json_is_array(root)) {
- json_error(ctx, "Invalid nat flags type %s.",
- json_typename(root));
- return -1;
- }
- json_array_foreach(root, index, value) {
- if (json_parse_nat_type_flag(ctx, value, &flags))
- json_error(ctx, "Parsing nat type flag at index %zu failed.",
- index);
+ if (!strcmp(flag, flag_tbl[i].flag))
+ return flag_tbl[i].val;
}
- return flags;
+ return 0;
}
static int nat_type_parse(const char *type)
@@ -2092,7 +2243,7 @@ static int nat_type_parse(const char *type)
static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
- int type, familyval;
+ int type, familyval, flags;
struct stmt *stmt;
json_t *tmp;
@@ -2125,31 +2276,26 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx,
return NULL;
}
}
- if (!json_unpack(value, "{s:o}", "flags", &tmp)) {
- int flags = json_parse_nat_flags(ctx, tmp);
-
- if (flags < 0) {
- stmt_free(stmt);
- return NULL;
- }
- stmt->nat.flags = flags;
+ flags = parse_flags_array(ctx, value, "flags", json_parse_nat_flag);
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
}
+ stmt->nat.flags = flags;
- if (!json_unpack(value, "{s:o}", "type_flags", &tmp)) {
- int flags = json_parse_nat_type_flags(ctx, tmp);
-
- if (flags < 0) {
- stmt_free(stmt);
- return NULL;
- }
- stmt->nat.type_flags = flags;
+ flags = parse_flags_array(ctx, value, "type_flags",
+ json_parse_nat_type_flag);
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
}
+ stmt->nat.type_flags = flags;
return stmt;
}
static struct stmt *json_parse_tproxy_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
json_t *jaddr, *tmp;
struct stmt *stmt;
@@ -2185,7 +2331,7 @@ out_free:
}
static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
struct stmt *stmt = reject_stmt_alloc(int_loc);
const struct datatype *dtype = NULL;
@@ -2201,17 +2347,17 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
stmt->reject.icmp_code = 0;
} else if (!strcmp(type, "icmpx")) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
- dtype = &icmpx_code_type;
+ dtype = &reject_icmpx_code_type;
stmt->reject.icmp_code = 0;
} else if (!strcmp(type, "icmp")) {
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
stmt->reject.family = NFPROTO_IPV4;
- dtype = &icmp_code_type;
+ dtype = &reject_icmp_code_type;
stmt->reject.icmp_code = 0;
} else if (!strcmp(type, "icmpv6")) {
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
stmt->reject.family = NFPROTO_IPV6;
- dtype = &icmpv6_code_type;
+ dtype = &reject_icmpv6_code_type;
stmt->reject.icmp_code = 0;
}
}
@@ -2227,13 +2373,51 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
return stmt;
}
+static int json_parse_set_stmt_list(struct json_ctx *ctx,
+ struct list_head *stmt_list,
+ json_t *stmt_json)
+{
+ struct list_head *head;
+ struct stmt *stmt;
+ json_t *value;
+ size_t index;
+
+ if (!stmt_json)
+ return 0;
+
+ if (!json_is_array(stmt_json)) {
+ json_error(ctx, "Unexpected object type in stmt");
+ return -1;
+ }
+
+ head = stmt_list;
+ json_array_foreach(stmt_json, index, value) {
+ stmt = json_parse_stmt(ctx, value);
+ if (!stmt) {
+ json_error(ctx, "Parsing set statements array at index %zd failed.", index);
+ stmt_list_free(stmt_list);
+ return -1;
+ }
+ if (!(stmt->flags & STMT_F_STATEFUL)) {
+ stmt_free(stmt);
+ json_error(ctx, "Unsupported set statements array at index %zd failed.", index);
+ stmt_list_free(stmt_list);
+ return -1;
+ }
+ list_add(&stmt->list, head);
+ head = &stmt->list;
+ }
+
+ return 0;
+}
+
static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
const char *opstr, *set;
struct expr *expr, *expr2;
+ json_t *elem, *stmt_json;
struct stmt *stmt;
- json_t *elem;
int op;
if (json_unpack_err(ctx, value, "{s:s, s:o, s:s}",
@@ -2268,11 +2452,77 @@ static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
stmt->set.op = op;
stmt->set.key = expr;
stmt->set.set = expr2;
+
+ if (!json_unpack(value, "{s:o}", "stmt", &stmt_json) &&
+ json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json) < 0) {
+ stmt_free(stmt);
+ return NULL;
+ }
+
return stmt;
}
-static int json_parse_log_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+static struct stmt *json_parse_map_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct expr *expr, *expr2, *expr_data;
+ json_t *elem, *data, *stmt_json;
+ const char *opstr, *set;
+ struct stmt *stmt;
+ int op;
+
+ if (json_unpack_err(ctx, value, "{s:s, s:o, s:o, s:s}",
+ "op", &opstr, "elem", &elem, "data", &data, "map", &set))
+ return NULL;
+
+ if (!strcmp(opstr, "add")) {
+ op = NFT_DYNSET_OP_ADD;
+ } else if (!strcmp(opstr, "update")) {
+ op = NFT_DYNSET_OP_UPDATE;
+ } else if (!strcmp(opstr, "delete")) {
+ op = NFT_DYNSET_OP_DELETE;
+ } else {
+ json_error(ctx, "Unknown map statement op '%s'.", opstr);
+ return NULL;
+ }
+
+ expr = json_parse_set_elem_expr_stmt(ctx, elem);
+ if (!expr) {
+ json_error(ctx, "Illegal map statement element.");
+ return NULL;
+ }
+
+ expr_data = json_parse_set_elem_expr_stmt(ctx, data);
+ if (!expr_data) {
+ json_error(ctx, "Illegal map expression data.");
+ expr_free(expr);
+ return NULL;
+ }
+
+ if (set[0] != '@') {
+ json_error(ctx, "Illegal map reference in map statement.");
+ expr_free(expr);
+ expr_free(expr_data);
+ return NULL;
+ }
+ expr2 = symbol_expr_alloc(int_loc, SYMBOL_SET, NULL, set + 1);
+
+ stmt = map_stmt_alloc(int_loc);
+ stmt->map.op = op;
+ stmt->map.key = expr;
+ stmt->map.data = expr_data;
+ stmt->map.set = expr2;
+
+ if (!json_unpack(value, "{s:o}", "stmt", &stmt_json) &&
+ json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json) < 0) {
+ stmt_free(stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
+static unsigned int json_parse_log_flag(const char *flag)
{
const struct {
const char *flag;
@@ -2285,47 +2535,13 @@ static int json_parse_log_flag(struct json_ctx *ctx,
{ "ether", NF_LOG_MACDECODE },
{ "all", NF_LOG_MASK },
};
- const char *flag;
unsigned int i;
- assert(flags);
-
- if (!json_is_string(root)) {
- json_error(ctx, "Invalid log flag type %s, expected string.",
- json_typename(root));
- return 1;
- }
- flag = json_string_value(root);
for (i = 0; i < array_size(flag_tbl); i++) {
- if (!strcmp(flag, flag_tbl[i].flag)) {
- *flags |= flag_tbl[i].val;
- return 0;
- }
- }
- json_error(ctx, "Unknown log flag '%s'.", flag);
- return 1;
-}
-
-static int json_parse_log_flags(struct json_ctx *ctx, json_t *root)
-{
- int flags = 0;
- json_t *value;
- size_t index;
-
- if (json_is_string(root)) {
- json_parse_log_flag(ctx, root, &flags);
- return flags;
- } else if (!json_is_array(root)) {
- json_error(ctx, "Invalid log flags type %s.",
- json_typename(root));
- return -1;
- }
- json_array_foreach(root, index, value) {
- if (json_parse_log_flag(ctx, value, &flags))
- json_error(ctx, "Parsing log flag at index %zu failed.",
- index);
+ if (!strcmp(flag, flag_tbl[i].flag))
+ return flag_tbl[i].val;
}
- return flags;
+ return 0;
}
static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
@@ -2333,15 +2549,12 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
{
const char *tmpstr;
struct stmt *stmt;
- json_t *jflags;
- int tmp;
+ int tmp, flags;
stmt = log_stmt_alloc(int_loc);
if (!json_unpack(value, "{s:s}", "prefix", &tmpstr)) {
- stmt->log.prefix = constant_expr_alloc(int_loc, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(tmpstr) + 1) * BITS_PER_BYTE, tmpstr);
+ stmt->log.prefix = xstrdup(tmpstr);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (!json_unpack(value, "{s:i}", "group", &tmp)) {
@@ -2367,20 +2580,17 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
stmt->log.level = level;
stmt->log.flags |= STMT_LOG_LEVEL;
}
- if (!json_unpack(value, "{s:o}", "flags", &jflags)) {
- int flags = json_parse_log_flags(ctx, jflags);
-
- if (flags < 0) {
- stmt_free(stmt);
- return NULL;
- }
- stmt->log.logflags = flags;
+ flags = parse_flags_array(ctx, value, "flags", json_parse_log_flag);
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
}
+ stmt->log.logflags = flags;
+
return stmt;
}
-static int json_parse_synproxy_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+static unsigned int json_parse_synproxy_flag(const char *flag)
{
const struct {
const char *flag;
@@ -2389,54 +2599,19 @@ static int json_parse_synproxy_flag(struct json_ctx *ctx,
{ "timestamp", NF_SYNPROXY_OPT_TIMESTAMP },
{ "sack-perm", NF_SYNPROXY_OPT_SACK_PERM },
};
- const char *flag;
unsigned int i;
- assert(flags);
-
- if (!json_is_string(root)) {
- json_error(ctx, "Invalid synproxy flag type %s, expected string.",
- json_typename(root));
- return 1;
- }
- flag = json_string_value(root);
for (i = 0; i < array_size(flag_tbl); i++) {
- if (!strcmp(flag, flag_tbl[i].flag)) {
- *flags |= flag_tbl[i].val;
- return 0;
- }
- }
- json_error(ctx, "Unknown synproxy flag '%s'.", flag);
- return 1;
-}
-
-static int json_parse_synproxy_flags(struct json_ctx *ctx, json_t *root)
-{
- int flags = 0;
- json_t *value;
- size_t index;
-
- if (json_is_string(root)) {
- json_parse_synproxy_flag(ctx, root, &flags);
- return flags;
- } else if (!json_is_array(root)) {
- json_error(ctx, "Invalid synproxy flags type %s.",
- json_typename(root));
- return -1;
- }
- json_array_foreach(root, index, value) {
- if (json_parse_synproxy_flag(ctx, value, &flags))
- json_error(ctx, "Parsing synproxy flag at index %zu failed.",
- index);
+ if (!strcmp(flag, flag_tbl[i].flag))
+ return flag_tbl[i].val;
}
- return flags;
+ return 0;
}
static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
struct stmt *stmt = NULL;
- json_t *jflags;
int tmp, flags;
if (json_typeof(value) == JSON_NULL) {
@@ -2466,15 +2641,16 @@ static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx,
stmt->synproxy.wscale = tmp;
stmt->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
}
- if (!json_unpack(value, "{s:o}", "flags", &jflags)) {
+
+ flags = parse_flags_array(ctx, value, "flags",
+ json_parse_synproxy_flag);
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
+ }
+ if (flags) {
if (!stmt)
stmt = synproxy_stmt_alloc(int_loc);
- flags = json_parse_synproxy_flags(ctx, jflags);
-
- if (flags < 0) {
- stmt_free(stmt);
- return NULL;
- }
stmt->synproxy.flags |= flags;
}
@@ -2507,7 +2683,7 @@ static struct stmt *json_parse_cthelper_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_cttimeout_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
struct stmt *stmt = objref_stmt_alloc(int_loc);
@@ -2542,7 +2718,7 @@ static struct stmt *json_parse_meter_stmt(struct json_ctx *ctx,
json_t *jkey, *jstmt;
struct stmt *stmt;
const char *name;
- uint32_t size = 0xffff;
+ uint32_t size = 0;
if (json_unpack_err(ctx, value, "{s:s, s:o, s:o}",
"name", &name, "key", &jkey, "stmt", &jstmt))
@@ -2569,14 +2745,12 @@ static struct stmt *json_parse_meter_stmt(struct json_ctx *ctx,
return stmt;
}
-static int queue_flag_parse(const char *name, uint16_t *flags)
+static unsigned int queue_flag_parse(const char *name)
{
if (!strcmp(name, "bypass"))
- *flags |= NFT_QUEUE_FLAG_BYPASS;
+ return NFT_QUEUE_FLAG_BYPASS;
else if (!strcmp(name, "fanout"))
- *flags |= NFT_QUEUE_FLAG_CPU_FANOUT;
- else
- return 1;
+ return NFT_QUEUE_FLAG_CPU_FANOUT;
return 0;
}
@@ -2584,8 +2758,8 @@ static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
struct expr *qexpr = NULL;
- uint16_t flags = 0;
json_t *tmp;
+ int flags;
if (!json_unpack(value, "{s:o}", "num", &tmp)) {
qexpr = json_parse_stmt_expr(ctx, tmp);
@@ -2594,43 +2768,13 @@ static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx,
return NULL;
}
}
- if (!json_unpack(value, "{s:o}", "flags", &tmp)) {
- const char *flag;
- size_t index;
- json_t *val;
-
- if (json_is_string(tmp)) {
- flag = json_string_value(tmp);
-
- if (queue_flag_parse(flag, &flags)) {
- json_error(ctx, "Invalid queue flag '%s'.",
- flag);
- expr_free(qexpr);
- return NULL;
- }
- } else if (!json_is_array(tmp)) {
- json_error(ctx, "Unexpected object type in queue flags.");
- expr_free(qexpr);
- return NULL;
- }
- json_array_foreach(tmp, index, val) {
- if (!json_is_string(val)) {
- json_error(ctx, "Invalid object in queue flag array at index %zu.",
- index);
- expr_free(qexpr);
- return NULL;
- }
- flag = json_string_value(val);
-
- if (queue_flag_parse(flag, &flags)) {
- json_error(ctx, "Invalid queue flag '%s'.",
- flag);
- expr_free(qexpr);
- return NULL;
- }
- }
+ flags = parse_flags_array(ctx, value, "flags", queue_flag_parse);
+ if (flags < 0) {
+ expr_free(qexpr);
+ return NULL;
}
+
return queue_stmt_alloc(int_loc, qexpr, flags);
}
@@ -2657,7 +2801,15 @@ static struct stmt *json_parse_optstrip_stmt(struct json_ctx *ctx,
{
struct expr *expr = json_parse_expr(ctx, value);
- return expr ? optstrip_stmt_alloc(int_loc, expr) : NULL;
+ if (!expr ||
+ expr->etype != EXPR_EXTHDR ||
+ expr->exthdr.op != NFT_EXTHDR_OP_TCPOPT) {
+ json_error(ctx, "Illegal TCP optstrip argument");
+ expr_free(expr);
+ return NULL;
+ }
+
+ return optstrip_stmt_alloc(int_loc, expr);
}
static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
@@ -2676,6 +2828,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "counter", json_parse_counter_stmt },
{ "mangle", json_parse_mangle_stmt },
{ "quota", json_parse_quota_stmt },
+ { "last", json_parse_last_stmt },
{ "limit", json_parse_limit_stmt },
{ "flow", json_parse_flow_offload_stmt },
{ "fwd", json_parse_fwd_stmt },
@@ -2687,6 +2840,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "redirect", json_parse_nat_stmt },
{ "reject", json_parse_reject_stmt },
{ "set", json_parse_set_stmt },
+ { "map", json_parse_map_stmt },
{ "log", json_parse_log_stmt },
{ "ct helper", json_parse_cthelper_stmt },
{ "ct timeout", json_parse_cttimeout_stmt },
@@ -2697,6 +2851,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "tproxy", json_parse_tproxy_stmt },
{ "synproxy", json_parse_synproxy_stmt },
{ "reset", json_parse_optstrip_stmt },
+ { "secmark", json_parse_secmark_stmt },
};
const char *type;
unsigned int i;
@@ -2716,6 +2871,11 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
return verdict_stmt_alloc(int_loc, expr);
}
+ if (!strcmp(type, "xt")) {
+ json_error(ctx, "unsupported xtables compat expression, use iptables-nft with this ruleset");
+ return NULL;
+ }
+
for (i = 0; i < array_size(stmt_parser_tbl); i++) {
if (!strcmp(type, stmt_parser_tbl[i].key))
return stmt_parser_tbl[i].cb(ctx, stmt_parser_tbl[i].key, tmp);
@@ -2728,17 +2888,26 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
+ const char *family = "", *comment = NULL;
struct handle h = {
.table.location = *int_loc,
};
- const char *family = "";
+ struct table *table = NULL;
+ int flags = 0;
if (json_unpack_err(ctx, root, "{s:s}",
"family", &family))
return NULL;
- if (op != CMD_DELETE &&
- json_unpack_err(ctx, root, "{s:s}", "name", &h.table.name)) {
- return NULL;
+
+ if (op != CMD_DELETE) {
+ if (json_unpack_err(ctx, root, "{s:s}", "name", &h.table.name))
+ return NULL;
+
+ json_unpack(root, "{s:s}", "comment", &comment);
+
+ flags = parse_flags_array(ctx, root, "flags", parse_table_flag);
+ if (flags < 0)
+ return NULL;
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.table.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -2752,10 +2921,18 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
if (h.table.name)
h.table.name = xstrdup(h.table.name);
+ if (comment || flags) {
+ table = table_alloc();
+ handle_merge(&table->handle, &h);
+ if (comment)
+ table->comment = xstrdup(comment);
+ table->flags = flags;
+ }
+
if (op == CMD_ADD)
json_object_del(root, "handle");
- return cmd_alloc(op, obj, &h, int_loc, NULL);
+ return cmd_alloc(op, obj, &h, int_loc, table);
}
static struct expr *parse_policy(const char *policy)
@@ -2774,23 +2951,73 @@ static struct expr *parse_policy(const char *policy)
sizeof(int) * BITS_PER_BYTE, &policy_num);
}
+static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root)
+{
+ struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST);
+ const char *dev;
+ json_t *value;
+ size_t index;
+
+ if (!json_unpack(root, "s", &dev)) {
+ if (strlen(dev) >= IFNAMSIZ) {
+ json_error(ctx, "Device name %s too long", dev);
+ expr_free(expr);
+ return NULL;
+ }
+
+ tmp = constant_expr_alloc(int_loc, &ifname_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(dev) * BITS_PER_BYTE, dev);
+ compound_expr_add(expr, tmp);
+ return expr;
+ }
+ if (!json_is_array(root)) {
+ expr_free(expr);
+ return NULL;
+ }
+
+ json_array_foreach(root, index, value) {
+ if (json_unpack(value, "s", &dev)) {
+ json_error(ctx, "Invalid device at index %zu.",
+ index);
+ expr_free(expr);
+ return NULL;
+ }
+
+ if (strlen(dev) >= IFNAMSIZ) {
+ json_error(ctx, "Device name %s too long at index %zu", dev, index);
+ expr_free(expr);
+ return NULL;
+ }
+
+ tmp = constant_expr_alloc(int_loc, &ifname_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(dev) * BITS_PER_BYTE, dev);
+ compound_expr_add(expr, tmp);
+ }
+ return expr;
+}
+
static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
struct handle h = {
.table.location = *int_loc,
};
- const char *family = "", *policy = "", *type, *hookstr, *name;
- struct chain *chain;
+ const char *family = "", *policy = "", *type, *hookstr, *comment = NULL;
+ struct chain *chain = NULL;
+ json_t *devs = NULL;
int prio;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
"table", &h.table.name))
return NULL;
- if (op != CMD_DELETE &&
- json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name)) {
- return NULL;
+ if (op != CMD_DELETE) {
+ if (json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name))
+ return NULL;
+
+ json_unpack(root, "{s:s}", "comment", &comment);
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.chain.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -2805,14 +3032,22 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
if (h.chain.name)
h.chain.name = xstrdup(h.chain.name);
+ if (comment) {
+ chain = chain_alloc();
+ handle_merge(&chain->handle, &h);
+ chain->comment = xstrdup(comment);
+ }
+
if (op == CMD_DELETE ||
op == CMD_LIST ||
op == CMD_FLUSH ||
json_unpack(root, "{s:s, s:s, s:i}",
"type", &type, "hook", &hookstr, "prio", &prio))
- return cmd_alloc(op, obj, &h, int_loc, NULL);
+ return cmd_alloc(op, obj, &h, int_loc, chain);
+
+ if (!chain)
+ chain = chain_alloc();
- chain = chain_alloc(NULL);
chain->flags |= CHAIN_F_BASECHAIN;
chain->type.str = xstrdup(type);
chain->priority.expr = constant_expr_alloc(int_loc, &integer_type,
@@ -2822,28 +3057,24 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
chain->hook.name = chain_hookname_lookup(hookstr);
if (!chain->hook.name) {
json_error(ctx, "Invalid chain hook '%s'.", hookstr);
- chain_free(chain);
- return NULL;
+ goto err_free_chain;
}
- if (!json_unpack(root, "{s:s}", "dev", &name)) {
- struct expr *dev_expr, *expr;
+ json_unpack(root, "{s:o}", "dev", &devs);
- dev_expr = compound_expr_alloc(int_loc, EXPR_LIST);
- expr = constant_expr_alloc(int_loc, &integer_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(name) * BITS_PER_BYTE,
- name);
- compound_expr_add(dev_expr, expr);
- chain->dev_expr = dev_expr;
+ if (devs) {
+ chain->dev_expr = json_parse_devs(ctx, devs);
+ if (!chain->dev_expr) {
+ json_error(ctx, "Invalid chain dev.");
+ goto err_free_chain;
+ }
}
if (!json_unpack(root, "{s:s}", "policy", &policy)) {
chain->policy = parse_policy(policy);
if (!chain->policy) {
json_error(ctx, "Unknown policy '%s'.", policy);
- chain_free(chain);
- return NULL;
+ goto err_free_chain;
}
}
@@ -2852,6 +3083,11 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
handle_merge(&chain->handle, &h);
return cmd_alloc(op, obj, &h, int_loc, chain);
+
+err_free_chain:
+ chain_free(chain);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
@@ -2875,7 +3111,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:o}", "expr", &tmp))
return NULL;
- else if (op == CMD_DELETE &&
+ else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack_err(ctx, root, "{s:I}", "handle", &h.handle.id))
return NULL;
@@ -2886,11 +3122,12 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
h.table.name = xstrdup(h.table.name);
h.chain.name = xstrdup(h.chain.name);
- if (op == CMD_DELETE)
+ if (op == CMD_DELETE || op == CMD_DESTROY)
return cmd_alloc(op, obj, &h, int_loc, NULL);
if (!json_is_array(tmp)) {
json_error(ctx, "Value of property \"expr\" must be an array.");
+ handle_free(&h);
return NULL;
}
@@ -2910,16 +3147,14 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
if (!json_is_object(value)) {
json_error(ctx, "Unexpected expr array element of type %s, expected object.",
json_typename(value));
- rule_free(rule);
- return NULL;
+ goto err_free_rule;
}
stmt = json_parse_stmt(ctx, value);
if (!stmt) {
json_error(ctx, "Parsing expr array at index %zd failed.", index);
- rule_free(rule);
- return NULL;
+ goto err_free_rule;
}
rule_stmt_append(rule, stmt);
@@ -2929,26 +3164,35 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
json_object_del(root, "handle");
return cmd_alloc(op, obj, &h, int_loc, rule);
+
+err_free_rule:
+ rule_free(rule);
+ handle_free(&h);
+ return NULL;
}
static int string_to_nft_object(const char *str)
{
const char *obj_tbl[__NFT_OBJECT_MAX] = {
- [NFT_OBJECT_COUNTER] = "counter",
- [NFT_OBJECT_QUOTA] = "quota",
- [NFT_OBJECT_LIMIT] = "limit",
- [NFT_OBJECT_SECMARK] = "secmark",
+ [NFT_OBJECT_COUNTER] = "counter",
+ [NFT_OBJECT_QUOTA] = "quota",
+ [NFT_OBJECT_CT_HELPER] = "ct helper",
+ [NFT_OBJECT_LIMIT] = "limit",
+ [NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
+ [NFT_OBJECT_SECMARK] = "secmark",
+ [NFT_OBJECT_CT_EXPECT] = "ct expectation",
+ [NFT_OBJECT_SYNPROXY] = "synproxy",
};
unsigned int i;
- for (i = 0; i < NFT_OBJECT_MAX; i++) {
+ for (i = 0; i <= NFT_OBJECT_MAX; i++) {
if (obj_tbl[i] && !strcmp(str, obj_tbl[i]))
return i;
}
return 0;
}
-static int string_to_set_flag(const char *str)
+static unsigned int string_to_set_flag(const char *str)
{
const struct {
enum nft_set_flags val;
@@ -2957,6 +3201,7 @@ static int string_to_set_flag(const char *str)
{ NFT_SET_CONSTANT, "constant" },
{ NFT_SET_INTERVAL, "interval" },
{ NFT_SET_TIMEOUT, "timeout" },
+ { NFT_SET_EVAL, "dynamic" },
};
unsigned int i;
@@ -2971,9 +3216,10 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
struct handle h = { 0 };
- const char *family = "", *policy, *dtype_ext = NULL;
+ const char *family = "", *policy;
+ json_t *tmp, *stmt_json;
struct set *set;
- json_t *tmp;
+ int flags;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
@@ -2982,7 +3228,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.set.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack(root, "{s:s}", "name", &h.set.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
json_error(ctx, "Either name or handle required to delete a set.");
@@ -2999,14 +3245,16 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
switch (op) {
case CMD_DELETE:
+ case CMD_DESTROY:
case CMD_LIST:
case CMD_FLUSH:
+ case CMD_RESET:
return cmd_alloc(op, obj, &h, int_loc, NULL);
default:
break;
}
- set = set_alloc(NULL);
+ set = set_alloc(&internal_location);
if (json_unpack(root, "{s:o}", "type", &tmp)) {
json_error(ctx, "Invalid set type.");
@@ -3022,19 +3270,21 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
return NULL;
}
- if (!json_unpack(root, "{s:s}", "map", &dtype_ext)) {
- const struct datatype *dtype;
+ if (!json_unpack(root, "{s:o}", "map", &tmp)) {
+ if (json_is_string(tmp)) {
+ const char *s = json_string_value(tmp);
- set->objtype = string_to_nft_object(dtype_ext);
+ set->objtype = string_to_nft_object(s);
+ }
if (set->objtype) {
set->flags |= NFT_SET_OBJECT;
- } else if ((dtype = datatype_lookup_byname(dtype_ext))) {
- set->data = constant_expr_alloc(&netlink_location,
- dtype, dtype->byteorder,
- dtype->size, NULL);
+ } else if ((set->data = json_parse_dtype_expr(ctx, tmp))) {
set->flags |= NFT_SET_MAP;
} else {
- json_error(ctx, "Invalid map type '%s'.", dtype_ext);
+ const char *dump = json_dumps(tmp, 0);
+
+ json_error(ctx, "Invalid map type '%s'.", dump);
+ free_const(dump);
set_free(set);
handle_free(&h);
return NULL;
@@ -3052,23 +3302,16 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
return NULL;
}
}
- if (!json_unpack(root, "{s:o}", "flags", &tmp)) {
- json_t *value;
- size_t index;
-
- json_array_foreach(tmp, index, value) {
- int flag;
- if (!json_is_string(value) ||
- !(flag = string_to_set_flag(json_string_value(value)))) {
- json_error(ctx, "Invalid set flag at index %zu.", index);
- set_free(set);
- handle_free(&h);
- return NULL;
- }
- set->flags |= flag;
- }
+ flags = parse_flags_array(ctx, root, "flags", string_to_set_flag);
+ if (flags < 0) {
+ json_error(ctx, "Invalid set flags in set '%s'.", h.set.name);
+ set_free(set);
+ handle_free(&h);
+ return NULL;
}
+ set->flags |= flags;
+
if (!json_unpack(root, "{s:o}", "elem", &tmp)) {
set->init = json_parse_set_expr(ctx, "elem", tmp);
if (!set->init) {
@@ -3083,6 +3326,14 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
if (!json_unpack(root, "{s:i}", "gc-interval", &set->gc_int))
set->gc_int *= 1000;
json_unpack(root, "{s:i}", "size", &set->desc.size);
+ json_unpack(root, "{s:b}", "auto-merge", &set->automerge);
+
+ if (!json_unpack(root, "{s:o}", "stmt", &stmt_json) &&
+ json_parse_set_stmt_list(ctx, &set->stmt_list, stmt_json) < 0) {
+ set_free(set);
+ handle_free(&h);
+ return NULL;
+ }
handle_merge(&set->handle, &h);
@@ -3121,42 +3372,16 @@ static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx,
handle_free(&h);
return NULL;
}
- return cmd_alloc(op, cmd_obj, &h, int_loc, expr);
-}
-
-static struct expr *json_parse_flowtable_devs(struct json_ctx *ctx,
- json_t *root)
-{
- struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST);
- const char *dev;
- json_t *value;
- size_t index;
- if (!json_unpack(root, "s", &dev)) {
- tmp = constant_expr_alloc(int_loc, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(dev) * BITS_PER_BYTE, dev);
- compound_expr_add(expr, tmp);
- return expr;
- }
- if (!json_is_array(root)) {
+ if ((op == CMD_CREATE || op == CMD_ADD) &&
+ nft_cmd_collapse_elems(op, ctx->cmds, &h, expr)) {
+ handle_free(&h);
expr_free(expr);
+ ctx->flags |= CTX_F_COLLAPSED;
return NULL;
}
- json_array_foreach(root, index, value) {
- if (json_unpack(value, "s", &dev)) {
- json_error(ctx, "Invalid flowtable dev at index %zu.",
- index);
- expr_free(expr);
- return NULL;
- }
- tmp = constant_expr_alloc(int_loc, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(dev) * BITS_PER_BYTE, dev);
- compound_expr_add(expr, tmp);
- }
- return expr;
+ return cmd_alloc(op, cmd_obj, &h, int_loc, expr);
}
static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
@@ -3177,7 +3402,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.flowtable.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack(root, "{s:s}", "name", &h.flowtable.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
json_error(ctx, "Either name or handle required to delete a flowtable.");
@@ -3192,17 +3417,17 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
if (h.flowtable.name)
h.flowtable.name = xstrdup(h.flowtable.name);
- if (op == CMD_DELETE || op == CMD_LIST)
+ if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY)
return cmd_alloc(op, cmd_obj, &h, int_loc, NULL);
- if (json_unpack_err(ctx, root, "{s:s, s:I}",
+ if (json_unpack_err(ctx, root, "{s:s, s:i}",
"hook", &hook,
"prio", &prio)) {
handle_free(&h);
return NULL;
}
- json_unpack(root, "{s:o}", &devs);
+ json_unpack(root, "{s:o}", "dev", &devs);
hookstr = chain_hookname_lookup(hook);
if (!hookstr) {
@@ -3219,7 +3444,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
sizeof(int) * BITS_PER_BYTE, &prio);
if (devs) {
- flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs);
+ flowtable->dev_expr = json_parse_devs(ctx, devs);
if (!flowtable->dev_expr) {
json_error(ctx, "Invalid flowtable dev.");
flowtable_free(flowtable);
@@ -3236,7 +3461,7 @@ static int json_parse_ct_timeout_policy(struct json_ctx *ctx,
json_t *tmp, *val;
const char *key;
- if (!json_unpack(root, "{s:o}", "policy", &tmp))
+ if (json_unpack(root, "{s:o}", "policy", &tmp))
return 0;
if (!json_is_object(tmp)) {
@@ -3268,10 +3493,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
{
const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes";
uint32_t l3proto = NFPROTO_UNSPEC;
+ int inv = 0, flags = 0, i, j;
struct handle h = { 0 };
- int inv = 0, flags = 0;
struct obj *obj;
- json_t *jflags;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
@@ -3281,7 +3505,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
cmd_obj == NFT_OBJECT_CT_HELPER) &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.obj.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
cmd_obj != NFT_OBJECT_CT_HELPER &&
json_unpack(root, "{s:s}", "name", &h.obj.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -3297,7 +3521,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (h.obj.name)
h.obj.name = xstrdup(h.obj.name);
- if (op == CMD_DELETE || op == CMD_LIST) {
+ if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY) {
if (cmd_obj == NFT_OBJECT_CT_HELPER)
return cmd_alloc_obj_ct(op, NFT_OBJECT_CT_HELPER,
&h, int_loc, obj_alloc(int_loc));
@@ -3306,6 +3530,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj = obj_alloc(int_loc);
+ if (!json_unpack(root, "{s:s}", "comment", &obj->comment))
+ obj->comment = xstrdup(obj->comment);
+
switch (cmd_obj) {
case CMD_OBJ_COUNTER:
obj->type = NFT_OBJECT_COUNTER;
@@ -3328,8 +3555,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (ret < 0 || ret >= (int)sizeof(obj->secmark.ctx)) {
json_error(ctx, "Invalid secmark context '%s', max length is %zu.",
tmp, sizeof(obj->secmark.ctx));
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
break;
@@ -3345,8 +3571,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
ret >= (int)sizeof(obj->ct_helper.name)) {
json_error(ctx, "Invalid CT helper type '%s', max length is %zu.",
tmp, sizeof(obj->ct_helper.name));
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "protocol", &tmp)) {
@@ -3356,20 +3581,19 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_helper.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct helper protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct helper l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
obj->ct_helper.l3proto = l3proto;
break;
case NFT_OBJECT_CT_TIMEOUT:
cmd_obj = CMD_OBJ_CT_TIMEOUT;
+ init_list_head(&obj->ct_timeout.timeout_list);
obj->type = NFT_OBJECT_CT_TIMEOUT;
if (!json_unpack(root, "{s:s}", "protocol", &tmp)) {
if (!strcmp(tmp, "tcp")) {
@@ -3378,23 +3602,18 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_timeout.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct timeout protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct timeout l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
- obj->ct_helper.l3proto = l3proto;
+ obj->ct_timeout.l3proto = l3proto;
- init_list_head(&obj->ct_timeout.timeout_list);
- if (json_parse_ct_timeout_policy(ctx, root, obj)) {
- obj_free(obj);
- return NULL;
- }
+ if (json_parse_ct_timeout_policy(ctx, root, obj))
+ goto err_free_obj;
break;
case NFT_OBJECT_CT_EXPECT:
cmd_obj = CMD_OBJ_CT_EXPECT;
@@ -3402,8 +3621,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct expectation l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
obj->ct_expect.l3proto = l3proto;
if (!json_unpack(root, "{s:s}", "protocol", &tmp)) {
@@ -3413,27 +3631,26 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_expect.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct expectation protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
- if (!json_unpack(root, "{s:o}", "dport", &tmp))
- obj->ct_expect.dport = atoi(tmp);
- json_unpack(root, "{s:I}", "timeout", &obj->ct_expect.timeout);
- if (!json_unpack(root, "{s:o}", "size", &tmp))
- obj->ct_expect.size = atoi(tmp);
+ if (!json_unpack(root, "{s:i}", "dport", &i))
+ obj->ct_expect.dport = i;
+ if (!json_unpack(root, "{s:i}", "timeout", &i))
+ obj->ct_expect.timeout = i;
+ if (!json_unpack(root, "{s:i}", "size", &i))
+ obj->ct_expect.size = i;
break;
case CMD_OBJ_LIMIT:
obj->type = NFT_OBJECT_LIMIT;
if (json_unpack_err(ctx, root, "{s:I, s:s}",
"rate", &obj->limit.rate,
- "per", &tmp)) {
- obj_free(obj);
- return NULL;
- }
+ "per", &tmp))
+ goto err_free_obj;
+
json_unpack(root, "{s:s}", "rate_unit", &rate_unit);
json_unpack(root, "{s:b}", "inv", &inv);
- json_unpack(root, "{s:I}", "burst", &obj->limit.burst);
+ json_unpack(root, "{s:i}", "burst", &obj->limit.burst);
json_unpack(root, "{s:s}", "burst_unit", &burst_unit);
if (!strcmp(rate_unit, "packets")) {
@@ -3451,21 +3668,19 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
case CMD_OBJ_SYNPROXY:
obj->type = NFT_OBJECT_SYNPROXY;
if (json_unpack_err(ctx, root, "{s:i, s:i}",
- "mss", &obj->synproxy.mss,
- "wscale", &obj->synproxy.wscale)) {
- obj_free(obj);
- return NULL;
- }
+ "mss", &i, "wscale", &j))
+ goto err_free_obj;
+
+ obj->synproxy.mss = i;
+ obj->synproxy.wscale = j;
obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
- if (!json_unpack(root, "{s:o}", "flags", &jflags)) {
- flags = json_parse_synproxy_flags(ctx, jflags);
- if (flags < 0) {
- obj_free(obj);
- return NULL;
- }
- obj->synproxy.flags |= flags;
- }
+ flags = parse_flags_array(ctx, root, "flags",
+ json_parse_synproxy_flag);
+ if (flags < 0)
+ goto err_free_obj;
+
+ obj->synproxy.flags |= flags;
break;
default:
BUG("Invalid CMD '%d'", cmd_obj);
@@ -3475,6 +3690,11 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
json_object_del(root, "handle");
return cmd_alloc(op, cmd_obj, &h, int_loc, obj);
+
+err_free_obj:
+ obj_free(obj);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_add(struct json_ctx *ctx,
@@ -3499,7 +3719,8 @@ static struct cmd *json_parse_cmd_add(struct json_ctx *ctx,
{ "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object },
{ "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object },
{ "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object },
- { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object }
+ { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object },
+ { "synproxy", CMD_OBJ_SYNPROXY, json_parse_cmd_add_object }
};
unsigned int i;
json_t *tmp;
@@ -3588,8 +3809,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
if (!json_is_object(value)) {
json_error(ctx, "Unexpected expr array element of type %s, expected object.",
json_typename(value));
- rule_free(rule);
- return NULL;
+ goto err_free_replace;
}
stmt = json_parse_stmt(ctx, value);
@@ -3597,8 +3817,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
if (!stmt) {
json_error(ctx, "Parsing expr array at index %zd failed.",
index);
- rule_free(rule);
- return NULL;
+ goto err_free_replace;
}
rule_stmt_append(rule, stmt);
@@ -3608,6 +3827,11 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
json_object_del(root, "handle");
return cmd_alloc(op, CMD_OBJ_RULE, &h, int_loc, rule);
+
+err_free_replace:
+ rule_free(rule);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_list_multiple(struct json_ctx *ctx,
@@ -3696,6 +3920,39 @@ static struct cmd *json_parse_cmd_list(struct json_ctx *ctx,
return NULL;
}
+static struct cmd *json_parse_cmd_reset_rule(struct json_ctx *ctx,
+ json_t *root, enum cmd_ops op,
+ enum cmd_obj obj)
+{
+ struct handle h = {
+ .family = NFPROTO_UNSPEC,
+ };
+ const char *family = NULL, *table = NULL, *chain = NULL;
+
+
+ if (obj == CMD_OBJ_RULE &&
+ json_unpack_err(ctx, root, "{s:s, s:s, s:s, s:I}",
+ "family", &family, "table", &table,
+ "chain", &chain, "handle", &h.handle.id))
+ return NULL;
+ else if (obj == CMD_OBJ_RULES) {
+ json_unpack(root, "{s:s}", "family", &family);
+ json_unpack(root, "{s:s}", "table", &table);
+ json_unpack(root, "{s:s}", "chain", &chain);
+ }
+
+ if (family && parse_family(family, &h.family)) {
+ json_error(ctx, "Unknown family '%s'.", family);
+ return NULL;
+ }
+ if (table) {
+ h.table.name = xstrdup(table);
+ if (chain)
+ h.chain.name = xstrdup(chain);
+ }
+ return cmd_alloc(op, obj, &h, int_loc, NULL);
+}
+
static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx,
json_t *root, enum cmd_ops op)
{
@@ -3709,6 +3966,11 @@ static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx,
{ "counters", CMD_OBJ_COUNTERS, json_parse_cmd_list_multiple },
{ "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object },
{ "quotas", CMD_OBJ_QUOTAS, json_parse_cmd_list_multiple },
+ { "rule", CMD_OBJ_RULE, json_parse_cmd_reset_rule },
+ { "rules", CMD_OBJ_RULES, json_parse_cmd_reset_rule },
+ { "element", CMD_OBJ_ELEMENTS, json_parse_cmd_add_element },
+ { "set", CMD_OBJ_SET, json_parse_cmd_add_set },
+ { "map", CMD_OBJ_MAP, json_parse_cmd_add_set },
};
unsigned int i;
json_t *tmp;
@@ -3807,6 +4069,7 @@ static struct cmd *json_parse_cmd(struct json_ctx *ctx, json_t *root)
{ "reset", CMD_RESET, json_parse_cmd_reset },
{ "flush", CMD_FLUSH, json_parse_cmd_flush },
{ "rename", CMD_RENAME, json_parse_cmd_rename },
+ { "destroy", CMD_DESTROY, json_parse_cmd_add },
//{ "export", CMD_EXPORT, json_parse_cmd_export },
//{ "monitor", CMD_MONITOR, json_parse_cmd_monitor },
//{ "describe", CMD_DESCRIBE, json_parse_cmd_describe }
@@ -3829,13 +4092,14 @@ static int json_verify_metainfo(struct json_ctx *ctx, json_t *root)
{
int schema_version;
- if (!json_unpack(root, "{s:i}", "json_schema_version", &schema_version))
- return 0;
-
- if (schema_version > JSON_SCHEMA_VERSION) {
- json_error(ctx, "Schema version %d not supported, maximum supported version is %d\n",
- schema_version, JSON_SCHEMA_VERSION);
- return 1;
+ if (!json_unpack(root, "{s:i}", "json_schema_version", &schema_version)) {
+ if (schema_version > JSON_SCHEMA_VERSION) {
+ json_error(ctx,
+ "Schema version %d not supported, maximum"
+ " supported version is %d\n",
+ schema_version, JSON_SCHEMA_VERSION);
+ return 1;
+ }
}
return 0;
@@ -3894,13 +4158,13 @@ static json_t *seqnum_to_json(const uint32_t seqnum)
cur = json_cmd_assoc_list;
json_cmd_assoc_list = cur->next;
- key = cur->cmd->seqnum % CMD_ASSOC_HSIZE;
+ key = cur->cmd->seqnum_from % CMD_ASSOC_HSIZE;
hlist_add_head(&cur->hnode, &json_cmd_assoc_hash[key]);
}
key = seqnum % CMD_ASSOC_HSIZE;
hlist_for_each_entry(cur, n, &json_cmd_assoc_hash[key], hnode) {
- if (cur->cmd->seqnum == seqnum)
+ if (cur->cmd->seqnum_from == seqnum)
return cur->json;
}
@@ -3944,6 +4208,11 @@ static int __json_parse(struct json_ctx *ctx)
cmd = json_parse_cmd(ctx, value);
if (!cmd) {
+ if (ctx->flags & CTX_F_COLLAPSED) {
+ ctx->flags &= ~CTX_F_COLLAPSED;
+ continue;
+ }
+
json_error(ctx, "Parsing command array at index %zd failed.", index);
return -1;
}
@@ -3997,6 +4266,13 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename,
json_error_t err;
int ret;
+ if (nft->stdin_buf) {
+ json_indesc.type = INDESC_STDIN;
+ json_indesc.name = "/dev/stdin";
+
+ return nft_parse_json_buffer(nft, nft->stdin_buf, msgs, cmds);
+ }
+
json_indesc.type = INDESC_FILE;
json_indesc.name = filename;
@@ -4030,6 +4306,7 @@ static int json_echo_error(struct netlink_mon_handler *monh,
static uint64_t handle_from_nlmsg(const struct nlmsghdr *nlh)
{
+ struct nftnl_flowtable *nlf;
struct nftnl_table *nlt;
struct nftnl_chain *nlc;
struct nftnl_rule *nlr;
@@ -4066,6 +4343,11 @@ static uint64_t handle_from_nlmsg(const struct nlmsghdr *nlh)
handle = nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE);
nftnl_obj_free(nlo);
break;
+ case NFT_MSG_NEWFLOWTABLE:
+ nlf = netlink_flowtable_alloc(nlh);
+ handle = nftnl_flowtable_get_u64(nlf, NFTNL_FLOWTABLE_HANDLE);
+ nftnl_flowtable_free(nlf);
+ break;
}
return handle;
}
diff --git a/src/payload.c b/src/payload.c
index 2c0d0ac9..a38f5bf7 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -10,11 +10,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
@@ -47,6 +46,10 @@ static void payload_expr_print(const struct expr *expr, struct output_ctx *octx)
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
+ if (expr->payload.inner_desc &&
+ expr->payload.inner_desc != expr->payload.desc)
+ nft_print(octx, "%s ", expr->payload.inner_desc->name);
+
desc = expr->payload.desc;
tmpl = expr->payload.tmpl;
if (payload_is_known(expr))
@@ -59,7 +62,8 @@ static void payload_expr_print(const struct expr *expr, struct output_ctx *octx)
bool payload_expr_cmp(const struct expr *e1, const struct expr *e2)
{
- return e1->payload.desc == e2->payload.desc &&
+ return e1->payload.inner_desc == e2->payload.inner_desc &&
+ e1->payload.desc == e2->payload.desc &&
e1->payload.tmpl == e2->payload.tmpl &&
e1->payload.base == e2->payload.base &&
e1->payload.offset == e2->payload.offset;
@@ -67,6 +71,7 @@ bool payload_expr_cmp(const struct expr *e1, const struct expr *e2)
static void payload_expr_clone(struct expr *new, const struct expr *expr)
{
+ new->payload.inner_desc = expr->payload.inner_desc;
new->payload.desc = expr->payload.desc;
new->payload.tmpl = expr->payload.tmpl;
new->payload.base = expr->payload.base;
@@ -114,11 +119,10 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
assert(desc->base <= PROTO_BASE_MAX);
if (desc->base == base->base) {
- assert(base->length > 0);
-
if (!left->payload.is_raw) {
if (desc->base == PROTO_BASE_LL_HDR &&
ctx->stacked_ll_count < PROTO_CTX_NUM_PROTOS) {
+ assert(base->length > 0);
ctx->stacked_ll[ctx->stacked_ll_count] = base;
ctx->stacked_ll_count++;
}
@@ -132,7 +136,8 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
#define NFTNL_UDATA_SET_KEY_PAYLOAD_BASE 2
#define NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET 3
#define NFTNL_UDATA_SET_KEY_PAYLOAD_LEN 4
-#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 5
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC 5
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 6
static unsigned int expr_payload_type(const struct proto_desc *desc,
const struct proto_hdr_template *tmpl)
@@ -162,10 +167,15 @@ static int payload_expr_build_udata(struct nftnl_udata_buf *udbuf,
if (expr->dtype->type == TYPE_INTEGER)
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_LEN, expr->len);
+ if (expr->payload.inner_desc) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC,
+ expr->payload.inner_desc->id);
+ }
+
return 0;
}
-static const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud)
+const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud)
{
return proto_find_desc(nftnl_udata_get_u32(ud));
}
@@ -182,6 +192,7 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data)
case NFTNL_UDATA_SET_KEY_PAYLOAD_BASE:
case NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET:
case NFTNL_UDATA_SET_KEY_PAYLOAD_LEN:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -238,11 +249,15 @@ static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr)
expr->payload.offset = offset;
expr->payload.is_raw = true;
expr->len = len;
- dtype = dtype_clone(&xinteger_type);
+ dtype = datatype_clone(&xinteger_type);
dtype->size = len;
dtype->byteorder = BYTEORDER_BIG_ENDIAN;
- dtype->refcnt = 1;
- expr->dtype = dtype;
+ __datatype_set(expr, dtype);
+ }
+
+ if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]) {
+ desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]);
+ expr->payload.inner_desc = desc;
}
return expr;
@@ -339,7 +354,6 @@ void payload_init_raw(struct expr *expr, enum proto_bases base,
expr->payload.tmpl = &proto_th.templates[thf];
expr->payload.desc = &proto_th;
expr->dtype = &inet_service_type;
- expr->payload.desc = &proto_th;
break;
default:
break;
@@ -364,7 +378,7 @@ static void payload_stmt_destroy(struct stmt *stmt)
expr_free(stmt->payload.val);
}
-static const struct stmt_ops payload_stmt_ops = {
+const struct stmt_ops payload_stmt_ops = {
.type = STMT_PAYLOAD,
.name = "payload",
.print = payload_stmt_print,
@@ -391,9 +405,11 @@ static int payload_add_dependency(struct eval_ctx *ctx,
{
const struct proto_hdr_template *tmpl;
struct expr *dep, *left, *right;
+ struct proto_ctx *pctx;
struct stmt *stmt;
- int protocol = proto_find_num(desc, upper);
+ int protocol;
+ protocol = proto_find_num(desc, upper);
if (protocol < 0)
return expr_error(ctx->msgs, expr,
"conflicting protocols specified: %s vs. %s",
@@ -410,20 +426,28 @@ static int payload_add_dependency(struct eval_ctx *ctx,
constant_data_ptr(protocol, tmpl->len));
dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+
stmt = expr_stmt_alloc(&dep->location, dep);
- if (stmt_evaluate(ctx, stmt) < 0) {
- return expr_error(ctx->msgs, expr,
- "dependency statement is invalid");
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
+
+ if (ctx->inner_desc) {
+ if (tmpl->meta_key)
+ left->meta.inner_desc = ctx->inner_desc;
+ else
+ left->payload.inner_desc = ctx->inner_desc;
}
- relational_expr_pctx_update(&ctx->pctx, dep);
+
+ pctx = eval_proto_ctx(ctx);
+ relational_expr_pctx_update(pctx, dep);
*res = stmt;
return 0;
}
static const struct proto_desc *
-payload_get_get_ll_hdr(const struct eval_ctx *ctx)
+payload_get_get_ll_hdr(const struct proto_ctx *pctx)
{
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_INET:
return &proto_inet;
case NFPROTO_BRIDGE:
@@ -440,9 +464,11 @@ payload_get_get_ll_hdr(const struct eval_ctx *ctx)
static const struct proto_desc *
payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+
switch (expr->payload.base) {
case PROTO_BASE_LL_HDR:
- return payload_get_get_ll_hdr(ctx);
+ return payload_get_get_ll_hdr(pctx);
case PROTO_BASE_TRANSPORT_HDR:
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_icmp6 ||
@@ -450,13 +476,21 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
const struct proto_desc *desc, *desc_upper;
struct stmt *nstmt;
- desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_LL_HDR].desc;
if (!desc) {
- desc = payload_get_get_ll_hdr(ctx);
+ desc = payload_get_get_ll_hdr(pctx);
if (!desc)
break;
}
+ /* this tunnel protocol does not encapsulate an inner
+ * link layer, use proto_netdev which relies on
+ * NFT_META_PROTOCOL for dependencies.
+ */
+ if (expr->payload.inner_desc &&
+ !(expr->payload.inner_desc->inner.flags & NFT_INNER_LL))
+ desc = &proto_netdev;
+
desc_upper = &proto_ip6;
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_igmp)
@@ -502,11 +536,14 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res)
{
- const struct hook_proto_desc *h = &hook_proto_desc[ctx->pctx.family];
+ const struct hook_proto_desc *h;
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
struct stmt *stmt;
uint16_t type;
+ pctx = eval_proto_ctx(ctx);
+ h = &hook_proto_desc[pctx->family];
if (expr->payload.base < h->base) {
if (expr->payload.base < h->base - 1)
return expr_error(ctx->msgs, expr,
@@ -519,15 +556,15 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
"for this family");
stmt = meta_stmt_meta_iiftype(&expr->location, type);
- if (stmt_evaluate(ctx, stmt) < 0) {
- return expr_error(ctx->msgs, expr,
- "dependency statement is invalid");
- }
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
+
*res = stmt;
+
return 0;
}
- desc = ctx->pctx.protocol[expr->payload.base - 1].desc;
+ desc = pctx->protocol[expr->payload.base - 1].desc;
/* Special case for mixed IPv4/IPv6 and bridge tables */
if (desc == NULL)
desc = payload_gen_special_dependency(ctx, expr);
@@ -538,7 +575,7 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
"no %s protocol specified",
proto_base_names[expr->payload.base - 1]);
- if (ctx->pctx.family == NFPROTO_BRIDGE && desc == &proto_eth) {
+ if (pctx->family == NFPROTO_BRIDGE && desc == &proto_eth) {
/* prefer netdev proto, which adds dependencies based
* on skb->protocol.
*
@@ -563,11 +600,13 @@ int exthdr_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
enum proto_bases pb, struct stmt **res)
{
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
- desc = ctx->pctx.protocol[pb].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[pb].desc;
if (desc == NULL) {
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_NETDEV:
case NFPROTO_BRIDGE:
case NFPROTO_INET:
@@ -739,29 +778,61 @@ static uint8_t icmp_dep_to_type(enum icmp_hdr_field_type t)
case PROTO_ICMP6_MTU: return ICMP6_PACKET_TOO_BIG;
case PROTO_ICMP6_MGMQ: return MLD_LISTENER_QUERY;
case PROTO_ICMP6_PPTR: return ICMP6_PARAM_PROB;
+ case PROTO_ICMP6_REDIRECT: return ND_REDIRECT;
+ case PROTO_ICMP6_ADDRESS: return ND_NEIGHBOR_SOLICIT;
}
BUG("Missing icmp type mapping");
}
-static bool payload_may_dependency_kill_icmp(struct payload_dep_ctx *ctx, struct expr *expr)
+static bool icmp_dep_type_match(enum icmp_hdr_field_type t, uint8_t type)
+{
+ switch (t) {
+ case PROTO_ICMP_ECHO:
+ return type == ICMP_ECHO || type == ICMP_ECHOREPLY;
+ case PROTO_ICMP6_ECHO:
+ return type == ICMP6_ECHO_REQUEST || type == ICMP6_ECHO_REPLY;
+ case PROTO_ICMP6_ADDRESS:
+ return type == ND_NEIGHBOR_SOLICIT ||
+ type == ND_NEIGHBOR_ADVERT ||
+ type == ND_REDIRECT ||
+ type == MLD_LISTENER_QUERY ||
+ type == MLD_LISTENER_REPORT ||
+ type == MLD_LISTENER_REDUCTION;
+ case PROTO_ICMP_ADDRESS:
+ case PROTO_ICMP_MTU:
+ case PROTO_ICMP6_MTU:
+ case PROTO_ICMP6_MGMQ:
+ case PROTO_ICMP6_PPTR:
+ case PROTO_ICMP6_REDIRECT:
+ return icmp_dep_to_type(t) == type;
+ case PROTO_ICMP_ANY:
+ return true;
+ }
+ BUG("Missing icmp type mapping");
+}
+
+static bool payload_may_dependency_kill_icmp(struct payload_dep_ctx *ctx, const struct expr *expr)
{
const struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
- uint8_t icmp_type;
+ enum icmp_hdr_field_type icmp_dep;
- icmp_type = expr->payload.tmpl->icmp_dep;
- if (icmp_type == PROTO_ICMP_ANY)
+ icmp_dep = expr->payload.tmpl->icmp_dep;
+ if (icmp_dep == PROTO_ICMP_ANY)
return false;
if (dep->left->payload.desc != expr->payload.desc)
return false;
- icmp_type = icmp_dep_to_type(expr->payload.tmpl->icmp_dep);
+ if (expr->payload.tmpl->icmp_dep == PROTO_ICMP_ECHO ||
+ expr->payload.tmpl->icmp_dep == PROTO_ICMP6_ECHO ||
+ expr->payload.tmpl->icmp_dep == PROTO_ICMP6_ADDRESS)
+ return false;
- return ctx->icmp_type == icmp_type;
+ return ctx->icmp_type == icmp_dep_to_type(icmp_dep);
}
-static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct expr *expr)
+static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, const struct expr *expr)
{
const struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
@@ -823,7 +894,20 @@ static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
if (expr->payload.base != PROTO_BASE_TRANSPORT_HDR)
return true;
- if (dep->left->payload.base != PROTO_BASE_TRANSPORT_HDR)
+ if (expr->payload.desc == &proto_th) {
+ /* &proto_th could mean any of udp, tcp, dccp, ... so we
+ * cannot remove the dependency.
+ *
+ * Also prefer raw payload @th syntax, there is no
+ * 'source/destination port' protocol here.
+ */
+ expr->payload.desc = &proto_unknown;
+ expr->dtype = &xinteger_type;
+ return false;
+ }
+
+ if (dep->left->etype != EXPR_PAYLOAD ||
+ dep->left->payload.base != PROTO_BASE_TRANSPORT_HDR)
return true;
if (dep->left->payload.desc == &proto_icmp)
@@ -848,7 +932,8 @@ static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
void payload_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
unsigned int family)
{
- if (payload_dependency_exists(ctx, expr->payload.base) &&
+ if (expr->payload.desc != &proto_unknown &&
+ payload_dependency_exists(ctx, expr->payload.base) &&
payload_may_dependency_kill(ctx, family, expr))
payload_dependency_release(ctx, expr->payload.base);
}
@@ -942,11 +1027,13 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
continue;
if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
- ctx->th_dep.icmp.type != icmp_dep_to_type(tmpl->icmp_dep))
+ !icmp_dep_type_match(tmpl->icmp_dep,
+ ctx->th_dep.icmp.type))
continue;
expr->dtype = tmpl->dtype;
expr->payload.desc = desc;
+ expr->byteorder = tmpl->byteorder;
expr->payload.tmpl = tmpl;
return;
}
@@ -972,6 +1059,7 @@ static unsigned int mask_length(const struct expr *mask)
* @expr: the payload expression
* @mask: mask to use when searching templates
* @ctx: protocol context
+ * @shift: shift adjustment to fix up RHS value
*
* Walk the template list and determine if a match can be found without
* using the provided mask.
@@ -1033,6 +1121,230 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
}
/**
+ * payload_expr_trim_force - adjust payload len/offset according to mask
+ *
+ * @expr: the payload expression
+ * @mask: mask to use when searching templates
+ * @shift: shift adjustment to fix up RHS value
+ *
+ * Force-trim an unknown payload expression according to mask.
+ *
+ * This is only useful for unkown payload expressions that need
+ * to be printed in raw syntax (@base,offset,length). The kernel
+ * can only deal with byte-divisible offsets/length, e.g. @th,16,8.
+ * In such case we might be able to get rid of the mask.
+ * @base,offset,length & MASK OPERATOR VALUE then becomes
+ * @base,offset,length VALUE, where at least one of offset increases
+ * and length decreases.
+ *
+ * This function also returns the shift for the right hand
+ * constant side of the expression.
+ *
+ * @return: true if @expr was adjusted and mask can be discarded.
+ */
+bool payload_expr_trim_force(struct expr *expr, struct expr *mask, unsigned int *shift)
+{
+ unsigned int payload_offset = expr->payload.offset;
+ unsigned int mask_len = mask_length(mask);
+ unsigned int off, real_len;
+
+ if (payload_is_known(expr) || expr->len <= mask_len)
+ return false;
+
+ /* This provides the payload offset to use.
+ * mask->len is the total length of the mask, e.g. 16.
+ * mask_len holds the last bit number that will be zeroed,
+ */
+ off = round_up(mask->len, BITS_PER_BYTE) - mask_len;
+ payload_offset += off;
+
+ /* kernel only allows offsets <= 255 */
+ if (round_up(payload_offset, BITS_PER_BYTE) > 255)
+ return false;
+
+ real_len = mpz_popcount(mask->value);
+ if (real_len > expr->len)
+ return false;
+
+ expr->payload.offset = payload_offset;
+ expr->len = real_len;
+
+ *shift = mask_to_offset(mask);
+ return true;
+}
+
+/**
+ * stmt_payload_expr_trim - adjust payload len/offset according to mask
+ *
+ * @stmt: the payload statement
+ * @pctx: protocol context
+ *
+ * Infer offset to header field from mask, walk the template list to determine
+ * if offset falls within a matching header field.
+ *
+ * Trim the payload expression length accordingly, adjust the payload offset
+ * and return true if payload statement expressions has been updated.
+ *
+ * @return: true if @stmt was adjusted.
+ */
+bool stmt_payload_expr_trim(struct stmt *stmt, const struct proto_ctx *pctx)
+{
+ struct expr *expr = stmt->payload.val;
+ const struct proto_hdr_template *tmpl;
+ const struct proto_desc *desc;
+ struct expr *payload, *mask;
+ uint32_t offset, i, shift;
+ unsigned int mask_offset;
+ mpz_t bitmask, tmp, tmp2;
+ unsigned long n;
+
+ assert(stmt->type == STMT_PAYLOAD);
+ assert(expr->etype == EXPR_BINOP);
+
+ payload = expr->left;
+ mask = expr->right;
+
+ if (payload->etype != EXPR_PAYLOAD ||
+ mask->etype != EXPR_VALUE)
+ return false;
+
+ if (payload_is_known(payload) ||
+ !pctx->protocol[payload->payload.base].desc ||
+ payload->len % (2 * BITS_PER_BYTE) != 0)
+ return false;
+
+ switch (expr->op) {
+ case OP_AND:
+ /* infer offset from first 0 in mask */
+ n = mpz_scan0(mask->value, 0);
+ if (n == ULONG_MAX)
+ return false;
+
+ mask_offset = payload->len - n;
+ break;
+ case OP_OR:
+ case OP_XOR:
+ /* infer offset from first 1 in mask */
+ n = mpz_scan1(mask->value, 0);
+ if (n == ULONG_MAX)
+ return false;
+
+ mask_offset = payload->len - n;
+ break;
+ default:
+ return false;
+ }
+
+ offset = payload->payload.offset + mask_offset;
+
+ desc = pctx->protocol[payload->payload.base].desc;
+ for (i = 1; i < array_size(desc->templates); i++) {
+ tmpl = &desc->templates[i];
+
+ if (tmpl->len == 0)
+ return false;
+
+ /* Is this inferred offset within this header field? */
+ if (tmpl->offset + tmpl->len >= offset) {
+ /* Infer shift to reach this header field. */
+ if ((tmpl->offset % (2 * BITS_PER_BYTE)) < 8) {
+ shift = BITS_PER_BYTE - (tmpl->offset % BITS_PER_BYTE + tmpl->len);
+ shift += BITS_PER_BYTE;
+ } else {
+ shift = (2 * BITS_PER_BYTE) - (tmpl->offset % (2 * BITS_PER_BYTE) + tmpl->len);
+ }
+
+ /* Build bitmask to fetch this header field. */
+ mpz_init2(bitmask, payload->len);
+ mpz_bitmask(bitmask, tmpl->len);
+ if (shift)
+ mpz_lshift_ui(bitmask, shift);
+
+ /* Check if mask expression falls within this header
+ * bitfield, if the mask expression is over this header
+ * field, then skip this delinearization, this could be
+ * a raw expression.
+ */
+ switch (expr->op) {
+ case OP_AND:
+ /* Inverted bitmask to fetch untouched bits. */
+ mpz_init_bitmask(tmp, payload->len);
+ mpz_xor(tmp, bitmask, tmp);
+
+ /* Get untouched bits out of the header field. */
+ mpz_init2(tmp2, payload->len);
+ mpz_and(tmp2, mask->value, tmp);
+
+ /* Modified any bits out of the header field? */
+ if (mpz_cmp(tmp, tmp2) != 0) {
+ mpz_clear(tmp);
+ mpz_clear(tmp2);
+ mpz_clear(bitmask);
+ return false;
+ }
+ mpz_clear(tmp2);
+ break;
+ case OP_OR:
+ case OP_XOR:
+ mpz_init2(tmp, payload->len);
+
+ /* Get modified bits in header field. */
+ mpz_and(tmp, mask->value, bitmask);
+
+ /* Modified any bits out of the header field? */
+ if (mpz_cmp(tmp, mask->value) != 0) {
+ mpz_clear(tmp);
+ mpz_clear(bitmask);
+ return false;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ mpz_clear(tmp);
+
+ /* Clear unrelated bits for this header field. Shrink
+ * to "real size". Shift bits when needed.
+ */
+ mpz_and(mask->value, bitmask, mask->value);
+ mpz_clear(bitmask);
+
+ mask->len -= (tmpl->offset - payload->payload.offset);
+ if (shift) {
+ mask->len -= shift;
+ mpz_rshift_ui(mask->value, shift);
+ }
+ payload->payload.offset = tmpl->offset;
+ payload->len = tmpl->len;
+
+ expr_free(stmt->payload.expr);
+ stmt->payload.expr = expr_get(payload);
+
+ if (expr->op == OP_AND) {
+ /* Reduce 'expr AND 0x0', otherwise listing
+ * shows:
+ *
+ * ip dscp set ip dscp & 0x0
+ *
+ * instead of the more compact:
+ *
+ * ip dscp set 0x0
+ */
+ if (mpz_cmp_ui(mask->value, 0) == 0) {
+ expr = stmt->payload.val;
+ stmt->payload.val = expr_get(mask);
+ expr_free(expr);
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
* payload_expr_expand - expand raw merged adjacent payload expressions into its
* original components
*
@@ -1058,8 +1370,9 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
assert(expr->etype == EXPR_PAYLOAD);
desc = ctx->protocol[expr->payload.base].desc;
- if (desc == NULL)
+ if (desc == NULL || desc == &proto_unknown)
goto raw;
+
assert(desc->base == expr->payload.base);
desc = get_stacked_desc(ctx, desc, expr, &total);
@@ -1075,7 +1388,8 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
continue;
if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
- ctx->th_dep.icmp.type != icmp_dep_to_type(tmpl->icmp_dep))
+ !icmp_dep_type_match(tmpl->icmp_dep,
+ ctx->th_dep.icmp.type))
continue;
if (tmpl->len <= expr->len) {
@@ -1098,6 +1412,10 @@ raw:
new = payload_expr_alloc(&expr->location, NULL, 0);
payload_init_raw(new, expr->payload.base, payload_offset,
expr->len);
+
+ if (expr->payload.inner_desc)
+ new->dtype = &integer_type;
+
list_add_tail(&new->list, list);
}
@@ -1119,6 +1437,9 @@ bool payload_can_merge(const struct expr *e1, const struct expr *e2)
{
unsigned int total;
+ if (e1->payload.inner_desc != e2->payload.inner_desc)
+ return false;
+
if (!payload_is_adjacent(e1, e2))
return false;
@@ -1175,6 +1496,8 @@ struct expr *payload_expr_join(const struct expr *e1, const struct expr *e2)
expr->payload.base = e1->payload.base;
expr->payload.offset = e1->payload.offset;
expr->len = e1->len + e2->len;
+ expr->payload.inner_desc = e1->payload.inner_desc;
+
return expr;
}
@@ -1223,9 +1546,42 @@ __payload_gen_icmp_echo_dependency(struct eval_ctx *ctx, const struct expr *expr
return expr_stmt_alloc(&dep->location, dep);
}
+static struct stmt *
+__payload_gen_icmp6_addr_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ const struct proto_desc *desc)
+{
+ static const uint8_t icmp_addr_types[] = {
+ MLD_LISTENER_QUERY,
+ MLD_LISTENER_REPORT,
+ MLD_LISTENER_REDUCTION,
+ ND_NEIGHBOR_SOLICIT,
+ ND_NEIGHBOR_ADVERT,
+ ND_REDIRECT
+ };
+ struct expr *left, *right, *dep, *set;
+ size_t i;
+
+ left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
+
+ set = set_expr_alloc(&expr->location, NULL);
+
+ for (i = 0; i < array_size(icmp_addr_types); ++i) {
+ right = constant_expr_alloc(&expr->location, &icmp6_type_type,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE,
+ constant_data_ptr(icmp_addr_types[i],
+ BITS_PER_BYTE));
+ right = set_elem_expr_alloc(&expr->location, right);
+ compound_expr_add(set, right);
+ }
+
+ dep = relational_expr_alloc(&expr->location, OP_IMPLICIT, left, set);
+ return expr_stmt_alloc(&dep->location, dep);
+}
+
int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
struct stmt *stmt = NULL;
@@ -1242,11 +1598,11 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
break;
case PROTO_ICMP_ECHO:
/* do not test ICMP_ECHOREPLY here: its 0 */
- if (ctx->pctx.th_dep.icmp.type == ICMP_ECHO)
+ if (pctx->th_dep.icmp.type == ICMP_ECHO)
goto done;
type = ICMP_ECHO;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
@@ -1257,21 +1613,21 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
case PROTO_ICMP_MTU:
case PROTO_ICMP_ADDRESS:
type = icmp_dep_to_type(tmpl->icmp_dep);
- if (ctx->pctx.th_dep.icmp.type == type)
+ if (pctx->th_dep.icmp.type == type)
goto done;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
&icmp_type_type,
desc, type);
break;
case PROTO_ICMP6_ECHO:
- if (ctx->pctx.th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
- ctx->pctx.th_dep.icmp.type == ICMP6_ECHO_REPLY)
+ if (pctx->th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
+ pctx->th_dep.icmp.type == ICMP6_ECHO_REPLY)
goto done;
type = ICMP6_ECHO_REQUEST;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
@@ -1280,13 +1636,23 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
&icmp6_type_type,
desc);
break;
+ case PROTO_ICMP6_ADDRESS:
+ if (icmp_dep_type_match(PROTO_ICMP6_ADDRESS,
+ pctx->th_dep.icmp.type))
+ goto done;
+ type = ND_NEIGHBOR_SOLICIT;
+ if (pctx->th_dep.icmp.type)
+ goto bad_proto;
+ stmt = __payload_gen_icmp6_addr_dependency(ctx, expr, desc);
+ break;
+ case PROTO_ICMP6_REDIRECT:
case PROTO_ICMP6_MTU:
case PROTO_ICMP6_MGMQ:
case PROTO_ICMP6_PPTR:
type = icmp_dep_to_type(tmpl->icmp_dep);
- if (ctx->pctx.th_dep.icmp.type == type)
+ if (pctx->th_dep.icmp.type == type)
goto done;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
&icmp6_type_type,
@@ -1297,16 +1663,54 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
BUG("Unhandled icmp dependency code");
}
- ctx->pctx.th_dep.icmp.type = type;
+ pctx->th_dep.icmp.type = type;
- if (stmt_evaluate(ctx, stmt) < 0)
- return expr_error(ctx->msgs, expr,
- "icmp dependency statement is invalid");
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
done:
*res = stmt;
return 0;
bad_proto:
return expr_error(ctx->msgs, expr, "incompatible icmp match: rule has %d, need %u",
- ctx->pctx.th_dep.icmp.type, type);
+ pctx->th_dep.icmp.type, type);
+}
+
+int payload_gen_inner_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ struct stmt **res)
+{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ const struct proto_hdr_template *tmpl;
+ const struct proto_desc *desc, *inner_desc;
+ struct expr *left, *right, *dep;
+ struct stmt *stmt = NULL;
+ int protocol;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ inner_desc = expr->payload.inner_desc;
+ desc = pctx->protocol[inner_desc->base - 1].desc;
+ if (desc == NULL)
+ desc = &proto_ip;
+
+ tmpl = &inner_desc->templates[0];
+ assert(tmpl);
+
+ protocol = proto_find_num(desc, inner_desc);
+ if (protocol < 0)
+ return expr_error(ctx->msgs, expr,
+ "conflicting protocols specified: %s vs. %s",
+ desc->name, inner_desc->name);
+
+ left = meta_expr_alloc(&expr->location, tmpl->meta_key);
+
+ right = constant_expr_alloc(&expr->location, tmpl->dtype,
+ tmpl->dtype->byteorder, tmpl->len,
+ constant_data_ptr(protocol, tmpl->len));
+
+ dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+ stmt = expr_stmt_alloc(&dep->location, dep);
+
+ *res = stmt;
+ return 0;
}
diff --git a/src/preprocess.c b/src/preprocess.c
new file mode 100644
index 00000000..619f67a1
--- /dev/null
+++ b/src/preprocess.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013-2024 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <utils.h>
+
+#include "list.h"
+#include "parser.h"
+#include "erec.h"
+
+struct str_buf {
+ uint8_t *str;
+ uint32_t len;
+ uint32_t size;
+};
+
+#define STR_BUF_LEN 128
+
+static struct str_buf *str_buf_alloc(void)
+{
+ struct str_buf *buf;
+
+ buf = xzalloc(sizeof(*buf));
+ buf->str = xzalloc_array(1, STR_BUF_LEN);
+ buf->size = STR_BUF_LEN;
+
+ return buf;
+}
+
+static int str_buf_add(struct str_buf *buf, const char *str, uint32_t len)
+{
+ uint8_t *tmp;
+
+ if (len + buf->len > buf->size) {
+ buf->size = (len + buf->len) * 2;
+ tmp = xrealloc(buf->str, buf->size);
+ buf->str = tmp;
+ }
+
+ memcpy(&buf->str[buf->len], str, len);
+ buf->len += len;
+
+ return 0;
+}
+
+struct str_chunk {
+ struct list_head list;
+ char *str;
+ uint32_t len;
+ bool is_sym;
+};
+
+static void add_str_chunk(const char *x, int from, int to, struct list_head *list, bool is_sym)
+{
+ struct str_chunk *chunk;
+ int len = to - from;
+
+ chunk = xzalloc_array(1, sizeof(*chunk));
+ chunk->str = xzalloc_array(1, len + 1);
+ chunk->is_sym = is_sym;
+ chunk->len = len;
+ memcpy(chunk->str, &x[from], len);
+
+ list_add_tail(&chunk->list, list);
+}
+
+static void free_str_chunk(struct str_chunk *chunk)
+{
+ free(chunk->str);
+ free(chunk);
+}
+
+const char *str_preprocess(struct parser_state *state, struct location *loc,
+ struct scope *scope, const char *x,
+ struct error_record **erec)
+{
+ struct str_chunk *chunk, *next;
+ struct str_buf *buf;
+ const char *str;
+ int i, j, start;
+ LIST_HEAD(list);
+
+ start = 0;
+ i = 0;
+ while (1) {
+ if (x[i] == '\0') {
+ i++;
+ break;
+ }
+
+ if (x[i] != '$') {
+ i++;
+ continue;
+ }
+
+ if (isdigit(x[++i]))
+ continue;
+
+ j = i;
+ while (1) {
+ if (isalpha(x[i]) ||
+ isdigit(x[i]) ||
+ x[i] == '_') {
+ i++;
+ continue;
+ }
+ break;
+ }
+ add_str_chunk(x, start, j-1, &list, false);
+ add_str_chunk(x, j, i, &list, true);
+ start = i;
+ }
+ if (start != i)
+ add_str_chunk(x, start, i, &list, false);
+
+ buf = str_buf_alloc();
+
+ list_for_each_entry_safe(chunk, next, &list, list) {
+ if (chunk->is_sym) {
+ struct symbol *sym;
+
+ sym = symbol_lookup(scope, chunk->str);
+ if (!sym) {
+ sym = symbol_lookup_fuzzy(scope, chunk->str);
+ if (sym) {
+ *erec = error(loc, "unknown identifier '%s'; "
+ "did you mean identifier '%s'?",
+ chunk->str, sym->identifier);
+ } else {
+ *erec = error(loc, "unknown identifier '%s'",
+ chunk->str);
+ }
+ goto err;
+ }
+ str_buf_add(buf, sym->expr->identifier,
+ strlen(sym->expr->identifier));
+ } else {
+ str_buf_add(buf, chunk->str, chunk->len);
+ }
+ list_del(&chunk->list);
+ free_str_chunk(chunk);
+ }
+
+ str = (char *)buf->str;
+
+ free(buf);
+
+ return (char *)str;
+err:
+ list_for_each_entry_safe(chunk, next, &list, list) {
+ list_del(&chunk->list);
+ free_str_chunk(chunk);
+ }
+ free(buf->str);
+ free(buf);
+
+ return NULL;
+}
diff --git a/src/print.c b/src/print.c
index d1b25e8b..8aefa961 100644
--- a/src/print.c
+++ b/src/print.c
@@ -2,11 +2,12 @@
* Copyright (c) 2017 Phil Sutter <phil@nwl.cc>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <stdarg.h>
#include <nftables.h>
#include <utils.h>
diff --git a/src/proto.c b/src/proto.c
index c8b3361b..05ddb070 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -9,10 +9,9 @@
*
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
@@ -88,6 +87,30 @@ int proto_find_num(const struct proto_desc *base,
return -1;
}
+static const struct proto_desc *inner_protocols[] = {
+ &proto_vxlan,
+ &proto_geneve,
+ &proto_gre,
+ &proto_gretap,
+};
+
+const struct proto_desc *proto_find_inner(uint32_t type, uint32_t hdrsize,
+ uint32_t flags)
+{
+ const struct proto_desc *desc;
+ unsigned int i;
+
+ for (i = 0; i < array_size(inner_protocols); i++) {
+ desc = inner_protocols[i];
+ if (desc->inner.type == type &&
+ desc->inner.hdrsize == hdrsize &&
+ desc->inner.flags == flags)
+ return inner_protocols[i];
+ }
+
+ return &proto_unknown;
+}
+
static const struct dev_proto_desc dev_proto_desc[] = {
DEV_PROTO_DESC(ARPHRD_ETHER, &proto_eth),
};
@@ -160,7 +183,9 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
pr_debug(" %s", ctx->stacked_ll[i]->name);
}
- pr_debug("update %s protocol context:\n", proto_base_names[base]);
+ pr_debug("update %s protocol context%s:\n",
+ proto_base_names[base], ctx->inner ? " (inner)" : "");
+
for (i = PROTO_BASE_LL_HDR; i <= PROTO_BASE_MAX; i++) {
pr_debug(" %-20s: %s",
proto_base_names[i],
@@ -181,7 +206,7 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
* @debug_mask: display debugging information
*/
void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
- unsigned int debug_mask)
+ unsigned int debug_mask, bool inner)
{
const struct hook_proto_desc *h = &hook_proto_desc[family];
@@ -189,6 +214,7 @@ void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
ctx->family = family;
ctx->protocol[h->base].desc = h->desc;
ctx->debug_mask = debug_mask;
+ ctx->inner = inner;
proto_ctx_debug(ctx, h->base, debug_mask);
}
@@ -228,6 +254,8 @@ void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
ctx->protocol[base].protos[i].location = *loc;
}
break;
+ case PROTO_BASE_INNER_HDR:
+ break;
default:
BUG("unknown protocol base %d", base);
}
@@ -276,6 +304,8 @@ const struct proto_desc *proto_ctx_find_conflict(struct proto_ctx *ctx,
#define HDR_FIELD(__name, __struct, __member) \
HDR_TEMPLATE(__name, &integer_type, __struct, __member)
+#define HDR_HEX_FIELD(__name, __struct, __member) \
+ HDR_TEMPLATE(__name, &xinteger_type, __struct, __member)
#define HDR_BITFIELD(__name, __dtype, __offset, __len) \
PROTO_HDR_TEMPLATE(__name, __dtype, BYTEORDER_BIG_ENDIAN, \
__offset, __len)
@@ -408,10 +438,10 @@ const struct datatype icmp_type_type = {
.sym_tbl = &icmp_type_tbl,
};
-#define ICMP46HDR_FIELD(__token, __struct, __member, __dep) \
+#define ICMP46HDR_FIELD(__token, __dtype, __struct, __member, __dep) \
{ \
.token = (__token), \
- .dtype = &integer_type, \
+ .dtype = &__dtype, \
.byteorder = BYTEORDER_BIG_ENDIAN, \
.offset = offsetof(__struct, __member) * 8, \
.len = field_sizeof(__struct, __member) * 8, \
@@ -419,7 +449,7 @@ const struct datatype icmp_type_type = {
}
#define ICMPHDR_FIELD(__token, __member, __dep) \
- ICMP46HDR_FIELD(__token, struct icmphdr, __member, __dep)
+ ICMP46HDR_FIELD(__token, integer_type, struct icmphdr, __member, __dep)
#define ICMPHDR_TYPE(__name, __type, __member) \
HDR_TYPE(__name, __type, struct icmphdr, __member)
@@ -505,14 +535,16 @@ const struct proto_desc proto_udp = {
.name = "udp",
.id = PROTO_DESC_UDP,
.base = PROTO_BASE_TRANSPORT_HDR,
- .checksum_key = UDPHDR_CHECKSUM,
- .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[UDPHDR_SPORT] = INET_SERVICE("sport", struct udphdr, source),
[UDPHDR_DPORT] = INET_SERVICE("dport", struct udphdr, dest),
[UDPHDR_LENGTH] = UDPHDR_FIELD("length", len),
[UDPHDR_CHECKSUM] = UDPHDR_FIELD("checksum", check),
},
+ .protocols = {
+ PROTO_LINK(0, &proto_vxlan),
+ PROTO_LINK(0, &proto_geneve),
+ },
};
const struct proto_desc proto_udplite = {
@@ -742,6 +774,42 @@ const struct datatype ecn_type = {
.sym_tbl = &ecn_type_tbl,
};
+#define GREHDR_TEMPLATE(__name, __dtype, __member) \
+ HDR_TEMPLATE(__name, __dtype, struct grehdr, __member)
+#define GREHDR_TYPE(__name, __member) \
+ GREHDR_TEMPLATE(__name, &ethertype_type, __member)
+
+const struct proto_desc proto_gre = {
+ .name = "gre",
+ .id = PROTO_DESC_GRE,
+ .base = PROTO_BASE_TRANSPORT_HDR,
+ .templates = {
+ [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
+ [GREHDR_FLAGS] = HDR_BITFIELD("flags", &integer_type, 0, 5),
+ [GREHDR_VERSION] = HDR_BITFIELD("version", &integer_type, 13, 3),
+ [GREHDR_PROTOCOL] = HDR_BITFIELD("protocol", &ethertype_type, 16, 16),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct grehdr),
+ .flags = NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE + 1,
+ },
+};
+
+const struct proto_desc proto_gretap = {
+ .name = "gretap",
+ .id = PROTO_DESC_GRETAP,
+ .base = PROTO_BASE_TRANSPORT_HDR,
+ .templates = {
+ [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct grehdr),
+ .flags = NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE + 2,
+ },
+};
+
#define IPHDR_FIELD(__name, __member) \
HDR_FIELD(__name, struct iphdr, __member)
#define IPHDR_ADDR(__name, __member) \
@@ -765,6 +833,8 @@ const struct proto_desc proto_ip = {
PROTO_LINK(IPPROTO_TCP, &proto_tcp),
PROTO_LINK(IPPROTO_DCCP, &proto_dccp),
PROTO_LINK(IPPROTO_SCTP, &proto_sctp),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -774,7 +844,7 @@ const struct proto_desc proto_ip = {
[IPHDR_ECN] = HDR_BITFIELD("ecn", &ecn_type, 14, 2),
[IPHDR_LENGTH] = IPHDR_FIELD("length", tot_len),
[IPHDR_ID] = IPHDR_FIELD("id", id),
- [IPHDR_FRAG_OFF] = IPHDR_FIELD("frag-off", frag_off),
+ [IPHDR_FRAG_OFF] = HDR_HEX_FIELD("frag-off", struct iphdr, frag_off),
[IPHDR_TTL] = IPHDR_FIELD("ttl", ttl),
[IPHDR_PROTOCOL] = INET_PROTOCOL("protocol", struct iphdr, protocol),
[IPHDR_CHECKSUM] = IPHDR_FIELD("checksum", check),
@@ -841,7 +911,7 @@ const struct datatype icmp6_type_type = {
};
#define ICMP6HDR_FIELD(__token, __member, __dep) \
- ICMP46HDR_FIELD(__token, struct icmp6_hdr, __member, __dep)
+ ICMP46HDR_FIELD(__token, integer_type, struct icmp6_hdr, __member, __dep)
#define ICMP6HDR_TYPE(__name, __type, __member) \
HDR_TYPE(__name, __type, struct icmp6_hdr, __member)
@@ -861,6 +931,12 @@ const struct proto_desc proto_icmp6 = {
[ICMP6HDR_ID] = ICMP6HDR_FIELD("id", icmp6_id, PROTO_ICMP6_ECHO),
[ICMP6HDR_SEQ] = ICMP6HDR_FIELD("sequence", icmp6_seq, PROTO_ICMP6_ECHO),
[ICMP6HDR_MAXDELAY] = ICMP6HDR_FIELD("max-delay", icmp6_maxdelay, PROTO_ICMP6_MGMQ),
+ [ICMP6HDR_TADDR] = ICMP46HDR_FIELD("taddr", ip6addr_type,
+ struct nd_neighbor_solicit, nd_ns_target,
+ PROTO_ICMP6_ADDRESS),
+ [ICMP6HDR_DADDR] = ICMP46HDR_FIELD("daddr", ip6addr_type,
+ struct nd_redirect, nd_rd_dst,
+ PROTO_ICMP6_REDIRECT),
},
};
@@ -891,6 +967,8 @@ const struct proto_desc proto_ip6 = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -956,6 +1034,8 @@ const struct proto_desc proto_inet_service = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -1136,6 +1216,57 @@ const struct proto_desc proto_eth = {
};
/*
+ * VXLAN
+ */
+
+const struct proto_desc proto_vxlan = {
+ .name = "vxlan",
+ .id = PROTO_DESC_VXLAN,
+ .base = PROTO_BASE_INNER_HDR,
+ .templates = {
+ [VXLANHDR_FLAGS] = HDR_BITFIELD("flags", &bitmask_type, 0, 8),
+ [VXLANHDR_VNI] = HDR_BITFIELD("vni", &integer_type, (4 * BITS_PER_BYTE), 24),
+ },
+ .protocols = {
+ PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip),
+ PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp),
+ PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6),
+ PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct vxlanhdr),
+ .flags = NFT_INNER_HDRSIZE | NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_VXLAN,
+ },
+};
+
+/*
+ * GENEVE
+ */
+
+const struct proto_desc proto_geneve = {
+ .name = "geneve",
+ .id = PROTO_DESC_GENEVE,
+ .base = PROTO_BASE_INNER_HDR,
+ .templates = {
+ [GNVHDR_TYPE] = HDR_TYPE("type", &ethertype_type, struct gnvhdr, type),
+ [GNVHDR_VNI] = HDR_BITFIELD("vni", &integer_type, (4 * BITS_PER_BYTE), 24),
+ },
+ .protocols = {
+ PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip),
+ PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp),
+ PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6),
+ PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct gnvhdr),
+ .flags = NFT_INNER_HDRSIZE | NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE,
+ },
+};
+
+
+/*
* Dummy protocol for netdev tables.
*/
const struct proto_desc proto_netdev = {
@@ -1153,7 +1284,7 @@ const struct proto_desc proto_netdev = {
},
};
-static const struct proto_desc *proto_definitions[PROTO_DESC_MAX + 1] = {
+static const struct proto_desc *const proto_definitions[PROTO_DESC_MAX + 1] = {
[PROTO_DESC_AH] = &proto_ah,
[PROTO_DESC_ESP] = &proto_esp,
[PROTO_DESC_COMP] = &proto_comp,
@@ -1171,6 +1302,10 @@ static const struct proto_desc *proto_definitions[PROTO_DESC_MAX + 1] = {
[PROTO_DESC_ARP] = &proto_arp,
[PROTO_DESC_VLAN] = &proto_vlan,
[PROTO_DESC_ETHER] = &proto_eth,
+ [PROTO_DESC_VXLAN] = &proto_vxlan,
+ [PROTO_DESC_GENEVE] = &proto_geneve,
+ [PROTO_DESC_GRE] = &proto_gre,
+ [PROTO_DESC_GRETAP] = &proto_gretap,
};
const struct proto_desc *proto_find_desc(enum proto_desc_id desc_id)
diff --git a/src/rt.c b/src/rt.c
index d7aa5edd..9320b832 100644
--- a/src/rt.c
+++ b/src/rt.c
@@ -8,12 +8,11 @@
* published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <errno.h>
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
@@ -26,7 +25,7 @@
void realm_table_rt_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.realm = rt_symbol_table_init("/etc/iproute2/rt_realms");
+ ctx->output.tbl.realm = rt_symbol_table_init("rt_realms");
}
void realm_table_rt_exit(struct nft_ctx *ctx)
@@ -46,16 +45,22 @@ static struct error_record *realm_type_parse(struct parse_ctx *ctx,
return symbolic_constant_parse(ctx, sym, ctx->tbl->realm, res);
}
+static void realm_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, "rt_realms",
+ octx->tbl.realm, &realm_type);
+}
+
const struct datatype realm_type = {
.type = TYPE_REALM,
.name = "realm",
.desc = "routing realm",
+ .describe = realm_type_describe,
.byteorder = BYTEORDER_HOST_ENDIAN,
.size = 4 * BITS_PER_BYTE,
.basetype = &integer_type,
.print = realm_type_print,
.parse = realm_type_parse,
- .flags = DTYPE_F_PREFIX,
};
const struct rt_template rt_templates[] = {
diff --git a/src/rule.c b/src/rule.c
index 9c9eaec0..c0f7570e 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -8,11 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <inttypes.h>
#include <errno.h>
@@ -27,6 +26,7 @@
#include <cache.h>
#include <owner.h>
#include <intervals.h>
+#include "nftutils.h"
#include <libnftnl/common.h>
#include <libnftnl/ruleset.h>
@@ -76,7 +76,7 @@ static uint32_t udp_dflt_timeout[] = {
[NFTNL_CTTIMEOUT_UDP_REPLIED] = 120,
};
-struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
+struct timeout_protocol timeout_protocol[UINT8_MAX + 1] = {
[IPPROTO_TCP] = {
.array_size = NFTNL_CTTIMEOUT_TCP_MAX,
.state_to_name = tcp_state_to_name,
@@ -104,11 +104,11 @@ int timeout_str2num(uint16_t l4proto, struct timeout_state *ts)
void handle_free(struct handle *h)
{
- xfree(h->table.name);
- xfree(h->chain.name);
- xfree(h->set.name);
- xfree(h->flowtable.name);
- xfree(h->obj.name);
+ free_const(h->table.name);
+ free_const(h->chain.name);
+ free_const(h->set.name);
+ free_const(h->flowtable.name);
+ free_const(h->obj.name);
}
void handle_merge(struct handle *dst, const struct handle *src)
@@ -146,11 +146,12 @@ struct set *set_alloc(const struct location *loc)
{
struct set *set;
+ assert(loc);
+
set = xzalloc(sizeof(*set));
set->refcnt = 1;
set->handle.set_id = ++set_id;
- if (loc != NULL)
- set->location = *loc;
+ set->location = *loc;
init_list_head(&set->stmt_list);
@@ -161,7 +162,7 @@ struct set *set_clone(const struct set *set)
{
struct set *new_set;
- new_set = set_alloc(NULL);
+ new_set = set_alloc(&internal_location);
handle_merge(&new_set->handle, &set->handle);
new_set->flags = set->flags;
new_set->gc_int = set->gc_int;
@@ -190,16 +191,16 @@ void set_free(struct set *set)
if (--set->refcnt > 0)
return;
- if (set->init != NULL)
- expr_free(set->init);
+
+ expr_free(set->init);
if (set->comment)
- xfree(set->comment);
+ free_const(set->comment);
handle_free(&set->handle);
list_for_each_entry_safe(stmt, next, &set->stmt_list, list)
stmt_free(stmt);
expr_free(set->key);
expr_free(set->data);
- xfree(set);
+ free(set);
}
struct set *set_lookup_fuzzy(const char *set_name,
@@ -210,6 +211,9 @@ struct set *set_lookup_fuzzy(const char *set_name,
struct table *table;
struct set *set;
+ if (!set_name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -334,10 +338,13 @@ static void set_print_declaration(const struct set *set,
}
if (set->desc.size > 0) {
- nft_print(octx, "%s%ssize %u%s",
+ nft_print(octx, "%s%ssize %u",
opts->tab, opts->tab,
- set->desc.size,
- opts->stmt_separator);
+ set->desc.size);
+ if (set->count > 0)
+ nft_print(octx, "%s# count %u", opts->tab,
+ set->count);
+ nft_print(octx, "%s", opts->stmt_separator);
}
}
@@ -419,7 +426,16 @@ static void do_set_print(const struct set *set, struct print_fmt_options *opts,
if (set->init != NULL && set->init->size > 0) {
nft_print(octx, "%s%selements = ", opts->tab, opts->tab);
+
+ if (set->timeout || set->elem_has_comment ||
+ (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT |
+ NFT_SET_TIMEOUT | NFT_SET_CONCAT)) ||
+ !list_empty(&set->stmt_list))
+ octx->force_newline = true;
+
expr_print(set->init, octx);
+ octx->force_newline = false;
+
nft_print(octx, "%s", opts->nl);
}
nft_print(octx, "%s}%s", opts->tab, opts->nl);
@@ -453,6 +469,8 @@ struct rule *rule_alloc(const struct location *loc, const struct handle *h)
{
struct rule *rule;
+ assert(loc);
+
rule = xzalloc(sizeof(*rule));
rule->location = *loc;
init_list_head(&rule->list);
@@ -476,16 +494,18 @@ void rule_free(struct rule *rule)
return;
stmt_list_free(&rule->stmts);
handle_free(&rule->handle);
- xfree(rule->comment);
- xfree(rule);
+ free_const(rule->comment);
+ free(rule);
}
void rule_print(const struct rule *rule, struct output_ctx *octx)
{
+ const struct stmt_ops *ops;
const struct stmt *stmt;
list_for_each_entry(stmt, &rule->stmts, list) {
- stmt->ops->print(stmt, octx);
+ ops = stmt_ops(stmt);
+ ops->print(stmt, octx);
if (!list_is_last(&stmt->list, &rule->stmts))
nft_print(octx, " ");
}
@@ -554,16 +574,16 @@ void scope_release(const struct scope *scope)
list_for_each_entry_safe(sym, next, &scope->symbols, list) {
assert(sym->refcnt == 1);
list_del(&sym->list);
- xfree(sym->identifier);
+ free_const(sym->identifier);
expr_free(sym->expr);
- xfree(sym);
+ free(sym);
}
}
void scope_free(struct scope *scope)
{
scope_release(scope);
- xfree(scope);
+ free(scope);
}
void symbol_bind(struct scope *scope, const char *identifier, struct expr *expr)
@@ -594,9 +614,9 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier)
static void symbol_put(struct symbol *sym)
{
if (--sym->refcnt == 0) {
- xfree(sym->identifier);
+ free_const(sym->identifier);
expr_free(sym->expr);
- xfree(sym);
+ free(sym);
}
}
@@ -695,17 +715,16 @@ const char *chain_hookname_lookup(const char *name)
/* 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_alloc(void)
{
struct chain *chain;
chain = xzalloc(sizeof(*chain));
+ chain->location = internal_location;
chain->refcnt = 1;
chain->handle.chain_id = ++chain_id;
init_list_head(&chain->rules);
init_list_head(&chain->scope.symbols);
- if (name != NULL)
- chain->handle.chain.name = xstrdup(name);
chain->policy = NULL;
return chain;
@@ -727,16 +746,20 @@ void chain_free(struct chain *chain)
list_for_each_entry_safe(rule, next, &chain->rules, list)
rule_free(rule);
handle_free(&chain->handle);
- scope_release(&chain->scope);
- xfree(chain->type.str);
+ free_const(chain->type.str);
expr_free(chain->dev_expr);
for (i = 0; i < chain->dev_array_len; i++)
- xfree(chain->dev_array[i]);
- xfree(chain->dev_array);
+ free_const(chain->dev_array[i]);
+ free(chain->dev_array);
expr_free(chain->priority.expr);
expr_free(chain->policy);
- xfree(chain->comment);
- xfree(chain);
+ free_const(chain->comment);
+
+ /* MUST be released after all expressions, they could
+ * hold refcounts.
+ */
+ scope_release(&chain->scope);
+ free(chain);
}
struct chain *chain_binding_lookup(const struct table *table,
@@ -864,7 +887,7 @@ struct prio_tag {
const char *str;
};
-const static struct prio_tag std_prios[] = {
+static const struct prio_tag std_prios[] = {
{ NF_IP_PRI_RAW, "raw" },
{ NF_IP_PRI_MANGLE, "mangle" },
{ NF_IP_PRI_NAT_DST, "dstnat" },
@@ -873,7 +896,7 @@ const static struct prio_tag std_prios[] = {
{ NF_IP_PRI_NAT_SRC, "srcnat" },
};
-const static struct prio_tag bridge_std_prios[] = {
+static const struct prio_tag bridge_std_prios[] = {
{ NF_BR_PRI_NAT_DST_BRIDGED, "dstnat" },
{ NF_BR_PRI_FILTER_BRIDGED, "filter" },
{ NF_BR_PRI_NAT_DST_OTHER, "out" },
@@ -927,7 +950,8 @@ static bool std_prio_family_hook_compat(int prio, int family, int hook)
case NFPROTO_INET:
case NFPROTO_IPV4:
case NFPROTO_IPV6:
- if (hook == NF_INET_PRE_ROUTING)
+ if (hook == NF_INET_PRE_ROUTING ||
+ hook == NF_INET_LOCAL_OUT)
return true;
}
break;
@@ -936,7 +960,8 @@ static bool std_prio_family_hook_compat(int prio, int family, int hook)
case NFPROTO_INET:
case NFPROTO_IPV4:
case NFPROTO_IPV6:
- if (hook == NF_INET_POST_ROUTING)
+ if (hook == NF_INET_LOCAL_IN ||
+ hook == NF_INET_POST_ROUTING)
return true;
}
}
@@ -969,10 +994,11 @@ static const char *prio2str(const struct output_ctx *octx,
const struct expr *expr)
{
const struct prio_tag *prio_arr;
- int std_prio, offset, prio;
+ const uint32_t reach = 10;
const char *std_prio_str;
- const int reach = 10;
+ int std_prio, prio;
size_t i, arr_size;
+ int64_t offset;
mpz_export_data(&prio, expr->value, BYTEORDER_HOST_ENDIAN, sizeof(int));
if (family == NFPROTO_BRIDGE) {
@@ -987,19 +1013,21 @@ static const char *prio2str(const struct output_ctx *octx,
for (i = 0; i < arr_size; ++i) {
std_prio = prio_arr[i].val;
std_prio_str = prio_arr[i].str;
- if (abs(prio - std_prio) <= reach) {
+
+ offset = (int64_t)prio - std_prio;
+ if (llabs(offset) <= reach) {
if (!std_prio_family_hook_compat(std_prio,
family, hook))
break;
- offset = prio - std_prio;
+
strncpy(buf, std_prio_str, bufsize);
if (offset > 0)
snprintf(buf + strlen(buf),
- bufsize - strlen(buf), " + %d",
+ bufsize - strlen(buf), " + %" PRIu64,
offset);
else if (offset < 0)
snprintf(buf + strlen(buf),
- bufsize - strlen(buf), " - %d",
+ bufsize - strlen(buf), " - %" PRIu64,
-offset);
return buf;
}
@@ -1025,8 +1053,10 @@ static void chain_print_declaration(const struct chain *chain,
nft_print(octx, "\n\t\tcomment \"%s\"", chain->comment);
nft_print(octx, "\n");
if (chain->flags & CHAIN_F_BASECHAIN) {
- nft_print(octx, "\t\ttype %s hook %s", chain->type.str,
- hooknum2str(chain->handle.family, chain->hook.num));
+ if (chain->type.str)
+ nft_print(octx, "\t\ttype %s hook %s", chain->type.str,
+ hooknum2str(chain->handle.family, chain->hook.num));
+
if (chain->dev_array_len == 1) {
nft_print(octx, " device \"%s\"", chain->dev_array[0]);
} else if (chain->dev_array_len > 1) {
@@ -1038,10 +1068,12 @@ static void chain_print_declaration(const struct chain *chain,
}
nft_print(octx, " }");
}
- nft_print(octx, " priority %s;",
- prio2str(octx, priobuf, sizeof(priobuf),
- chain->handle.family, chain->hook.num,
- chain->priority.expr));
+
+ if (chain->priority.expr)
+ nft_print(octx, " priority %s;",
+ prio2str(octx, priobuf, sizeof(priobuf),
+ chain->handle.family, chain->hook.num,
+ chain->priority.expr));
if (chain->policy) {
mpz_export_data(&policy, chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
@@ -1058,13 +1090,19 @@ static void chain_print_declaration(const struct chain *chain,
void chain_rules_print(const struct chain *chain, struct output_ctx *octx,
const char *indent)
{
+ unsigned int flags = octx->flags;
struct rule *rule;
+ if (chain->flags & CHAIN_F_BINDING)
+ octx->flags &= ~NFT_CTX_OUTPUT_HANDLE;
+
list_for_each_entry(rule, &chain->rules, list) {
nft_print(octx, "\t\t%s", indent ? : "");
rule_print(rule, octx);
nft_print(octx, "\n");
}
+
+ octx->flags = flags;
}
static void chain_print(const struct chain *chain, struct output_ctx *octx)
@@ -1085,8 +1123,21 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx)
if (chain->flags & CHAIN_F_BASECHAIN) {
mpz_export_data(&policy, chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
- nft_print(octx, " { type %s hook %s priority %s; policy %s; }",
- chain->type.str, chain->hook.name,
+ nft_print(octx, " { type %s hook %s ",
+ chain->type.str, chain->hook.name);
+
+ if (chain->dev_array_len > 0) {
+ int i;
+
+ nft_print(octx, "devices = { ");
+ for (i = 0; i < chain->dev_array_len; i++) {
+ nft_print(octx, "%s", chain->dev_array[i]);
+ if (i + 1 != chain->dev_array_len)
+ nft_print(octx, ", ");
+ }
+ nft_print(octx, " } ");
+ }
+ nft_print(octx, "priority %s; policy %s; }",
prio2str(octx, priobuf, sizeof(priobuf),
chain->handle.family, chain->hook.num,
chain->priority.expr),
@@ -1101,6 +1152,7 @@ struct table *table_alloc(void)
struct table *table;
table = xzalloc(sizeof(*table));
+ table->location = internal_location;
init_list_head(&table->chains);
init_list_head(&table->sets);
init_list_head(&table->objs);
@@ -1127,7 +1179,7 @@ void table_free(struct table *table)
if (--table->refcnt > 0)
return;
if (table->comment)
- xfree(table->comment);
+ free_const(table->comment);
list_for_each_entry_safe(chain, next, &table->chains, list)
chain_free(chain);
list_for_each_entry_safe(chain, next, &table->chain_bindings, cache.list)
@@ -1157,7 +1209,7 @@ void table_free(struct table *table)
cache_free(&table->set_cache);
cache_free(&table->obj_cache);
cache_free(&table->ft_cache);
- xfree(table);
+ free(table);
}
struct table *table_get(struct table *table)
@@ -1172,6 +1224,9 @@ struct table *table_lookup_fuzzy(const struct handle *h,
struct string_misspell_state st;
struct table *table;
+ if (!h->table.name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -1184,6 +1239,7 @@ struct table *table_lookup_fuzzy(const struct handle *h,
static const char *table_flags_name[TABLE_FLAGS_MAX] = {
"dormant",
"owner",
+ "persist",
};
const char *table_flag_name(uint32_t flag)
@@ -1194,6 +1250,17 @@ const char *table_flag_name(uint32_t flag)
return table_flags_name[flag];
}
+unsigned int parse_table_flag(const char *name)
+{
+ int i;
+
+ for (i = 0; i < TABLE_FLAGS_MAX; i++) {
+ if (!strcmp(name, table_flags_name[i]))
+ return 1 << i;
+ }
+ return 0;
+}
+
static void table_print_flags(const struct table *table, const char **delim,
struct output_ctx *octx)
{
@@ -1227,6 +1294,11 @@ static void table_print(const struct table *table, struct output_ctx *octx)
const char *delim = "";
const char *family = family2str(table->handle.family);
+ if (table->has_xt_stmts)
+ fprintf(octx->error_fp,
+ "# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
+ family, table->handle.table.name);
+
nft_print(octx, "table %s %s {", family, table->handle.table.name);
if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER)
nft_print(octx, " #");
@@ -1272,6 +1344,8 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
{
struct cmd *cmd;
+ assert(loc);
+
cmd = xzalloc(sizeof(*cmd));
init_list_head(&cmd->list);
cmd->op = op;
@@ -1282,185 +1356,10 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
cmd->attr = xzalloc_array(NFT_NLATTR_LOC_MAX,
sizeof(struct nlerr_loc));
cmd->attr_array_len = NFT_NLATTR_LOC_MAX;
- init_list_head(&cmd->collapse_list);
return cmd;
}
-void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
-{
- if (cmd->num_attrs >= cmd->attr_array_len) {
- cmd->attr_array_len *= 2;
- cmd->attr = xrealloc(cmd->attr, sizeof(struct nlerr_loc) * cmd->attr_array_len);
- }
-
- cmd->attr[cmd->num_attrs].offset = offset;
- cmd->attr[cmd->num_attrs].location = loc;
- cmd->num_attrs++;
-}
-
-void nft_cmd_expand(struct cmd *cmd)
-{
- struct list_head new_cmds;
- struct flowtable *ft;
- struct table *table;
- struct chain *chain;
- struct rule *rule;
- struct set *set;
- struct obj *obj;
- struct cmd *new;
- struct handle h;
-
- init_list_head(&new_cmds);
-
- switch (cmd->obj) {
- case CMD_OBJ_TABLE:
- table = cmd->table;
- if (!table)
- return;
-
- 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);
- }
- list_for_each_entry(obj, &table->objs, list) {
- handle_merge(&obj->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &obj->handle);
- new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h,
- &obj->location, obj_get(obj));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(set, &table->sets, list) {
- handle_merge(&set->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &set->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h,
- &set->location, set_get(set));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(ft, &table->flowtables, list) {
- handle_merge(&ft->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &ft->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h,
- &ft->location, flowtable_get(ft));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(chain, &table->chains, list) {
- 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));
- list_add_tail(&new->list, &new_cmds);
- }
- }
- list_splice(&new_cmds, &cmd->list);
- break;
- case CMD_OBJ_SET:
- case CMD_OBJ_MAP:
- set = cmd->set;
- if (!set->init)
- break;
-
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &set->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h,
- &set->location, set_get(set));
- list_add(&new->list, &cmd->list);
- break;
- default:
- break;
- }
-}
-
-bool nft_cmd_collapse(struct list_head *cmds)
-{
- struct cmd *cmd, *next, *elems = NULL;
- struct expr *expr, *enext;
- bool collapse = false;
-
- list_for_each_entry_safe(cmd, next, cmds, list) {
- if (cmd->op != CMD_ADD &&
- cmd->op != CMD_CREATE) {
- elems = NULL;
- continue;
- }
-
- if (cmd->obj != CMD_OBJ_ELEMENTS) {
- elems = NULL;
- continue;
- }
-
- if (!elems) {
- elems = cmd;
- continue;
- }
-
- if (cmd->op != elems->op) {
- elems = cmd;
- continue;
- }
-
- if (strcmp(elems->handle.table.name, cmd->handle.table.name) ||
- strcmp(elems->handle.set.name, cmd->handle.set.name)) {
- elems = cmd;
- continue;
- }
-
- collapse = true;
- list_for_each_entry_safe(expr, enext, &cmd->expr->expressions, list) {
- expr->cmd = cmd;
- list_move_tail(&expr->list, &elems->expr->expressions);
- }
- elems->expr->size += cmd->expr->size;
- list_move_tail(&cmd->list, &elems->collapse_list);
- }
-
- return collapse;
-}
-
-void nft_cmd_uncollapse(struct list_head *cmds)
-{
- struct cmd *cmd, *cmd_next, *collapse_cmd, *collapse_cmd_next;
- struct expr *expr, *next;
-
- list_for_each_entry_safe(cmd, cmd_next, cmds, list) {
- if (list_empty(&cmd->collapse_list))
- continue;
-
- assert(cmd->obj == CMD_OBJ_ELEMENTS);
-
- list_for_each_entry_safe(expr, next, &cmd->expr->expressions, list) {
- if (!expr->cmd)
- continue;
-
- list_move_tail(&expr->list, &expr->cmd->expr->expressions);
- cmd->expr->size--;
- expr->cmd = NULL;
- }
-
- list_for_each_entry_safe(collapse_cmd, collapse_cmd_next, &cmd->collapse_list, list) {
- if (cmd->elem.set)
- collapse_cmd->elem.set = set_get(cmd->elem.set);
-
- list_add(&collapse_cmd->list, &cmd->list);
- }
- }
-}
-
struct markup *markup_alloc(uint32_t format)
{
struct markup *markup;
@@ -1473,7 +1372,7 @@ struct markup *markup_alloc(uint32_t format)
void markup_free(struct markup *m)
{
- xfree(m);
+ free(m);
}
struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event)
@@ -1491,12 +1390,15 @@ struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event)
void monitor_free(struct monitor *m)
{
- xfree(m->event);
- xfree(m);
+ free_const(m->event);
+ free(m);
}
void cmd_free(struct cmd *cmd)
{
+ if (cmd == NULL)
+ return;
+
handle_free(&cmd->handle);
if (cmd->data != NULL) {
switch (cmd->obj) {
@@ -1506,6 +1408,8 @@ void cmd_free(struct cmd *cmd)
set_free(cmd->elem.set);
break;
case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ case CMD_OBJ_METER:
case CMD_OBJ_SETELEMS:
set_free(cmd->set);
break;
@@ -1544,9 +1448,9 @@ void cmd_free(struct cmd *cmd)
BUG("invalid command object type %u\n", cmd->obj);
}
}
- xfree(cmd->attr);
- xfree(cmd->arg);
- xfree(cmd);
+ free(cmd->attr);
+ free_const(cmd->arg);
+ free(cmd);
}
#include <netlink.h>
@@ -1598,7 +1502,13 @@ static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
return -1;
}
- return mnl_nft_set_add(ctx, cmd, flags);
+ if (mnl_nft_set_add(ctx, cmd, flags) < 0)
+ return -1;
+
+ if (set_is_anonymous(set->flags))
+ return __do_add_elements(ctx, cmd, set, set->init, flags);
+
+ return 0;
}
static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
@@ -1667,14 +1577,14 @@ static int do_command_insert(struct netlink_ctx *ctx, struct cmd *cmd)
static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ const struct set *set = cmd->elem.set;
struct expr *expr = cmd->elem.expr;
- struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
set_to_intervals(set, expr, false) < 0)
return -1;
- if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0)
+ if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, set, cmd->elem.expr) < 0)
return -1;
return 0;
@@ -1716,8 +1626,7 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
}
}
-static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd,
- struct table *table)
+static int do_list_table(struct netlink_ctx *ctx, struct table *table)
{
table_print(table, &ctx->nft->output);
return 0;
@@ -1725,11 +1634,6 @@ static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct print_fmt_options opts = {
- .tab = "\t",
- .nl = "\n",
- .stmt_separator = "\n",
- };
struct table *table;
struct set *set;
@@ -1747,13 +1651,12 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
!set_is_literal(set->flags))
continue;
if (cmd->obj == CMD_OBJ_METERS &&
- !set_is_meter(set->flags))
+ !set_is_meter_compat(set->flags))
continue;
if (cmd->obj == CMD_OBJ_MAPS &&
!map_is_literal(set->flags))
continue;
- set_print_declaration(set, &opts, &ctx->nft->output);
- nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl);
+ set_print(set, &ctx->nft->output);
}
nft_print(&ctx->nft->output, "}\n");
@@ -1765,9 +1668,10 @@ struct obj *obj_alloc(const struct location *loc)
{
struct obj *obj;
+ assert(loc);
+
obj = xzalloc(sizeof(*obj));
- if (loc != NULL)
- obj->location = *loc;
+ obj->location = *loc;
obj->refcnt = 1;
return obj;
@@ -1783,18 +1687,18 @@ void obj_free(struct obj *obj)
{
if (--obj->refcnt > 0)
return;
- xfree(obj->comment);
+ free_const(obj->comment);
handle_free(&obj->handle);
if (obj->type == NFT_OBJECT_CT_TIMEOUT) {
struct timeout_state *ts, *next;
list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) {
list_del(&ts->head);
- xfree(ts->timeout_str);
- xfree(ts);
+ free_const(ts->timeout_str);
+ free(ts);
}
}
- xfree(obj);
+ free(obj);
}
struct obj *obj_lookup_fuzzy(const char *obj_name,
@@ -1805,6 +1709,9 @@ struct obj *obj_lookup_fuzzy(const char *obj_name,
struct table *table;
struct obj *obj;
+ if (!obj_name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -1819,10 +1726,10 @@ struct obj *obj_lookup_fuzzy(const char *obj_name,
static void print_proto_name_proto(uint8_t l4, struct output_ctx *octx)
{
- const struct protoent *p = getprotobynumber(l4);
+ char name[NFT_PROTONAME_MAXSIZE];
- if (p)
- nft_print(octx, "%s", p->p_name);
+ if (nft_getprotobynumber(l4, name, sizeof(name)))
+ nft_print(octx, "%s", name);
else
nft_print(octx, "%d", l4);
}
@@ -1837,11 +1744,14 @@ static void print_proto_timeout_policy(uint8_t l4, const uint32_t *timeout,
nft_print(octx, "%s%spolicy = { ", opts->tab, opts->tab);
for (i = 0; i < timeout_protocol[l4].array_size; i++) {
if (timeout[i] != timeout_protocol[l4].dflt_timeout[i]) {
+ uint64_t timeout_ms;
+
if (comma)
nft_print(octx, ", ");
- nft_print(octx, "%s : %u",
- timeout_protocol[l4].state_to_name[i],
- timeout[i]);
+ timeout_ms = timeout[i] * 1000u;
+ nft_print(octx, "%s : ",
+ timeout_protocol[l4].state_to_name[i]);
+ time_print(timeout_ms, octx);
comma = true;
}
}
@@ -2063,7 +1973,7 @@ static const char * const obj_type_name_array[] = {
[NFT_OBJECT_CT_EXPECT] = "ct expectation",
};
-const char *obj_type_name(enum stmt_types type)
+const char *obj_type_name(unsigned int type)
{
assert(type <= NFT_OBJECT_MAX && obj_type_name_array[type]);
@@ -2081,7 +1991,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = {
[NFT_OBJECT_CT_EXPECT] = CMD_OBJ_CT_EXPECT,
};
-uint32_t obj_type_to_cmd(uint32_t type)
+enum cmd_obj obj_type_to_cmd(uint32_t type)
{
assert(type <= NFT_OBJECT_MAX && obj_type_cmd_array[type]);
@@ -2173,9 +2083,10 @@ struct flowtable *flowtable_alloc(const struct location *loc)
{
struct flowtable *flowtable;
+ assert(loc);
+
flowtable = xzalloc(sizeof(*flowtable));
- if (loc != NULL)
- flowtable->location = *loc;
+ flowtable->location = *loc;
flowtable->refcnt = 1;
return flowtable;
@@ -2199,10 +2110,10 @@ void flowtable_free(struct flowtable *flowtable)
if (flowtable->dev_array != NULL) {
for (i = 0; i < flowtable->dev_array_len; i++)
- xfree(flowtable->dev_array[i]);
- xfree(flowtable->dev_array);
+ free_const(flowtable->dev_array[i]);
+ free(flowtable->dev_array);
}
- xfree(flowtable);
+ free(flowtable);
}
static void flowtable_print_declaration(const struct flowtable *flowtable,
@@ -2225,12 +2136,15 @@ static void flowtable_print_declaration(const struct flowtable *flowtable,
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, flowtable->handle.handle.id);
nft_print(octx, "%s", opts->nl);
- nft_print(octx, "%s%shook %s priority %s%s",
- opts->tab, opts->tab,
- hooknum2str(NFPROTO_NETDEV, flowtable->hook.num),
- prio2str(octx, priobuf, sizeof(priobuf), NFPROTO_NETDEV,
- flowtable->hook.num, flowtable->priority.expr),
- opts->stmt_separator);
+
+ if (flowtable->priority.expr) {
+ nft_print(octx, "%s%shook %s priority %s%s",
+ opts->tab, opts->tab,
+ hooknum2str(NFPROTO_NETDEV, flowtable->hook.num),
+ prio2str(octx, priobuf, sizeof(priobuf), NFPROTO_NETDEV,
+ flowtable->hook.num, flowtable->priority.expr),
+ opts->stmt_separator);
+ }
if (flowtable->dev_array_len > 0) {
nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
@@ -2270,6 +2184,21 @@ void flowtable_print(const struct flowtable *s, struct output_ctx *octx)
do_flowtable_print(s, &opts, octx);
}
+void flowtable_print_plain(const struct flowtable *ft, struct output_ctx *octx)
+{
+ struct print_fmt_options opts = {
+ .tab = "",
+ .nl = " ",
+ .table = ft->handle.table.name,
+ .family = family2str(ft->handle.family),
+ .stmt_separator = "; ",
+ };
+
+ flowtable_print_declaration(ft, &opts, octx);
+ nft_print(octx, "}");
+}
+
+
struct flowtable *flowtable_lookup_fuzzy(const char *ft_name,
const struct nft_cache *cache,
const struct table **t)
@@ -2278,6 +2207,9 @@ struct flowtable *flowtable_lookup_fuzzy(const char *ft_name,
struct table *table;
struct flowtable *ft;
+ if (!ft_name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -2348,15 +2280,10 @@ static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd)
table->handle.family != family)
continue;
- cmd->handle.family = table->handle.family;
- cmd->handle.table.name = table->handle.table.name;
-
- if (do_list_table(ctx, cmd, table) < 0)
+ if (do_list_table(ctx, table) < 0)
return -1;
}
- cmd->handle.table.name = NULL;
-
return 0;
}
@@ -2380,9 +2307,14 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd)
static void table_print_declaration(struct table *table,
struct output_ctx *octx)
{
- nft_print(octx, "table %s %s {\n",
- family2str(table->handle.family),
- table->handle.table.name);
+ const char *family = family2str(table->handle.family);
+
+ if (table->has_xt_stmts)
+ fprintf(octx->error_fp,
+ "# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
+ family, table->handle.table.name);
+
+ nft_print(octx, "table %s %s {\n", family, table->handle.table.name);
}
static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -2440,11 +2372,13 @@ static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
struct table *table)
{
- struct set *set;
+ struct set *set = cmd->set;
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return -1;
+ if (!set) {
+ set = set_cache_find(table, cmd->handle.set.name);
+ if (set == NULL)
+ return -1;
+ }
__do_list_set(ctx, cmd, set);
@@ -2454,12 +2388,8 @@ static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_list_hooks(struct netlink_ctx *ctx, struct cmd *cmd)
{
const char *devname = cmd->handle.obj.name;
- int hooknum = -1;
-
- if (cmd->handle.chain.name)
- hooknum = cmd->handle.chain_id;
- return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, hooknum, devname);
+ return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, devname);
}
static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
@@ -2469,15 +2399,21 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
if (nft_output_json(&ctx->nft->output))
return do_command_list_json(ctx, cmd);
- if (cmd->handle.table.name != NULL)
+ if (cmd->handle.table.name != NULL) {
table = table_cache_find(&ctx->nft->cache.table_cache,
cmd->handle.table.name,
cmd->handle.family);
+ if (!table) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
switch (cmd->obj) {
case CMD_OBJ_TABLE:
if (!cmd->handle.table.name)
return do_list_tables(ctx, cmd);
- return do_list_table(ctx, cmd, table);
+ return do_list_table(ctx, table);
case CMD_OBJ_CHAIN:
return do_list_chain(ctx, cmd, table);
case CMD_OBJ_CHAINS:
@@ -2487,6 +2423,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SET:
return do_list_set(ctx, cmd, table);
case CMD_OBJ_RULESET:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
return do_list_ruleset(ctx, cmd);
case CMD_OBJ_METERS:
return do_list_sets(ctx, cmd);
@@ -2506,8 +2444,10 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_CT_HELPERS:
return do_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);
case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
return do_list_obj(ctx, cmd, NFT_OBJECT_CT_TIMEOUT);
case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_CT_EXPECTATIONS:
return do_list_obj(ctx, cmd, NFT_OBJECT_CT_EXPECT);
case CMD_OBJ_LIMIT:
case CMD_OBJ_LIMITS:
@@ -2524,14 +2464,22 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
return do_list_flowtables(ctx, cmd);
case CMD_OBJ_HOOKS:
return do_list_hooks(ctx, cmd);
- default:
- BUG("invalid command object type %u\n", cmd->obj);
+ case CMD_OBJ_MONITOR:
+ case CMD_OBJ_MARKUP:
+ case CMD_OBJ_SETELEMS:
+ case CMD_OBJ_EXPR:
+ case CMD_OBJ_ELEMENTS:
+ errno = EOPNOTSUPP;
+ return -1;
+ case CMD_OBJ_INVALID:
+ break;
}
+ BUG("invalid command object type %u\n", cmd->obj);
return 0;
}
-static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
+static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, bool reset)
{
struct set *set, *new_set;
struct expr *init;
@@ -2549,7 +2497,7 @@ 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,
- cmd->elem.set, new_set, init);
+ cmd->elem.set, new_set, init, reset);
if (err >= 0)
__do_list_set(ctx, cmd, new_set);
@@ -2565,7 +2513,7 @@ static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- return do_get_setelems(ctx, cmd);
+ return do_get_setelems(ctx, cmd, false);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -2575,41 +2523,12 @@ static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd)
static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct obj *obj, *next;
- struct table *table;
- bool dump = false;
- uint32_t type;
- int ret;
-
switch (cmd->obj) {
- case CMD_OBJ_COUNTERS:
- dump = true;
- /* fall through */
- case CMD_OBJ_COUNTER:
- type = NFT_OBJECT_COUNTER;
- break;
- case CMD_OBJ_QUOTAS:
- dump = true;
- /* fall through */
- case CMD_OBJ_QUOTA:
- type = NFT_OBJECT_QUOTA;
- break;
+ case CMD_OBJ_ELEMENTS:
+ return do_get_setelems(ctx, cmd, true);
default:
- BUG("invalid command object type %u\n", cmd->obj);
- }
-
- ret = netlink_reset_objs(ctx, cmd, type, dump);
- list_for_each_entry_safe(obj, next, &ctx->list, list) {
- table = table_cache_find(&ctx->nft->cache.table_cache,
- obj->handle.table.name,
- obj->handle.family);
- if (!obj_cache_find(table, obj->handle.obj.name, obj->type)) {
- list_del(&obj->list);
- obj_cache_add(obj, table);
- }
+ break;
}
- if (ret < 0)
- return ret;
return do_command_list(ctx, cmd);
}
@@ -2711,6 +2630,7 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_REPLACE:
return do_command_replace(ctx, cmd);
case CMD_DELETE:
+ case CMD_DESTROY:
return do_command_delete(ctx, cmd);
case CMD_GET:
return do_command_get(ctx, cmd);
@@ -2856,11 +2776,9 @@ static void stmt_reduce(const struct rule *rule)
}
/* Must not merge across other statements */
- if (stmt->ops->type != STMT_EXPRESSION) {
- if (idx < 2)
- continue;
-
- payload_do_merge(sa, idx);
+ if (stmt->type != STMT_EXPRESSION) {
+ if (idx >= 2)
+ payload_do_merge(sa, idx);
idx = 0;
continue;
}
@@ -2874,7 +2792,6 @@ static void stmt_reduce(const struct rule *rule)
switch (stmt->expr->op) {
case OP_EQ:
case OP_IMPLICIT:
- case OP_NEQ:
break;
default:
continue;
@@ -2885,7 +2802,8 @@ static void stmt_reduce(const struct rule *rule)
switch (stmt->expr->op) {
case OP_EQ:
case OP_IMPLICIT:
- if (stmt->expr->left->meta.key == NFT_META_PROTOCOL) {
+ if (stmt->expr->left->meta.key == NFT_META_PROTOCOL &&
+ !stmt->expr->left->meta.inner_desc) {
uint16_t protocol;
protocol = mpz_get_uint16(stmt->expr->right->value);
diff --git a/src/scanner.l b/src/scanner.l
index 1371cd04..b69d8e81 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -10,12 +10,15 @@
%{
+#include <nft.h>
+
#include <limits.h>
#include <glob.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/types.h>
#include <linux/netfilter.h>
+#include <sys/stat.h>
#include <nftables.h>
#include <erec.h>
@@ -77,7 +80,6 @@ static void update_pos(struct parser_state *state, struct location *loc,
{
loc->indesc = state->indesc;
loc->first_line = state->indesc->lineno;
- loc->last_line = state->indesc->lineno;
loc->first_column = state->indesc->column;
loc->last_column = state->indesc->column + len - 1;
state->indesc->column += len;
@@ -86,9 +88,15 @@ static void update_pos(struct parser_state *state, struct location *loc,
static void update_offset(struct parser_state *state, struct location *loc,
unsigned int len)
{
+ uint32_t line_offset;
+
state->indesc->token_offset += len;
- loc->token_offset = state->indesc->token_offset;
- loc->line_offset = state->indesc->line_offset;
+ if (state->indesc->line_offset > UINT32_MAX)
+ line_offset = UINT32_MAX;
+ else
+ line_offset = state->indesc->line_offset;
+
+ loc->line_offset = line_offset;
}
static void reset_pos(struct parser_state *state, struct location *loc)
@@ -113,64 +121,64 @@ extern void yyset_column(int, yyscan_t);
space [ ]
tab \t
+newline_crlf \r\n
newline \n
digit [0-9]
hexdigit [0-9a-fA-F]
decstring {digit}+
hexstring 0[xX]{hexdigit}+
-numberstring ({decstring}|{hexstring})
letter [a-zA-Z]
string ({letter}|[_.])({letter}|{digit}|[/\-_\.])*
quotedstring \"[^"]*\"
asteriskstring ({string}\*|{string}\\\*|\\\*|{string}\\\*{string})
comment #.*$
+comment_line ^[ \t]*#.*\n
slash \/
timestring ([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?
hex4 ([[:xdigit:]]{1,4})
+rfc4291_broader (((:{hex4}){2})|(:{ip4addr}))
v680 (({hex4}:){7}{hex4})
-v670 ((:)((:{hex4}){7}))
-v671 ((({hex4}:){1})((:{hex4}){6}))
-v672 ((({hex4}:){2})((:{hex4}){5}))
-v673 ((({hex4}:){3})((:{hex4}){4}))
-v674 ((({hex4}:){4})((:{hex4}){3}))
-v675 ((({hex4}:){5})((:{hex4}){2}))
+v670 ((:)((:{hex4}){5}){rfc4291_broader})
+v671 ((({hex4}:){1})((:{hex4}){4}){rfc4291_broader})
+v672 ((({hex4}:){2})((:{hex4}){3}){rfc4291_broader})
+v673 ((({hex4}:){3})((:{hex4}){2}){rfc4291_broader})
+v674 ((({hex4}:){4})((:{hex4}){1}){rfc4291_broader})
+v675 ((({hex4}:){5}){rfc4291_broader})
v676 ((({hex4}:){6})(:{hex4}{1}))
v677 ((({hex4}:){7})(:))
v67 ({v670}|{v671}|{v672}|{v673}|{v674}|{v675}|{v676}|{v677})
-v660 ((:)((:{hex4}){6}))
-v661 ((({hex4}:){1})((:{hex4}){5}))
-v662 ((({hex4}:){2})((:{hex4}){4}))
-v663 ((({hex4}:){3})((:{hex4}){3}))
-v664 ((({hex4}:){4})((:{hex4}){2}))
+v660 ((:)((:{hex4}){4}){rfc4291_broader})
+v661 ((({hex4}:){1})((:{hex4}){3}){rfc4291_broader})
+v662 ((({hex4}:){2})((:{hex4}){2}){rfc4291_broader})
+v663 ((({hex4}:){3})((:{hex4}){1}){rfc4291_broader})
+v664 ((({hex4}:){4}){rfc4291_broader})
v665 ((({hex4}:){5})((:{hex4}){1}))
v666 ((({hex4}:){6})(:))
v66 ({v660}|{v661}|{v662}|{v663}|{v664}|{v665}|{v666})
-v650 ((:)((:{hex4}){5}))
-v651 ((({hex4}:){1})((:{hex4}){4}))
-v652 ((({hex4}:){2})((:{hex4}){3}))
-v653 ((({hex4}:){3})((:{hex4}){2}))
+v650 ((:)((:{hex4}){3}){rfc4291_broader})
+v651 ((({hex4}:){1})((:{hex4}){2}){rfc4291_broader})
+v652 ((({hex4}:){2})((:{hex4}){1}){rfc4291_broader})
+v653 ((({hex4}:){3}){rfc4291_broader})
v654 ((({hex4}:){4})(:{hex4}{1}))
v655 ((({hex4}:){5})(:))
v65 ({v650}|{v651}|{v652}|{v653}|{v654}|{v655})
-v640 ((:)((:{hex4}){4}))
-v641 ((({hex4}:){1})((:{hex4}){3}))
-v642 ((({hex4}:){2})((:{hex4}){2}))
+v640 ((:)((:{hex4}){2}){rfc4291_broader})
+v641 ((({hex4}:){1})((:{hex4}){1}){rfc4291_broader})
+v642 ((({hex4}:){2}){rfc4291_broader})
v643 ((({hex4}:){3})((:{hex4}){1}))
v644 ((({hex4}:){4})(:))
v64 ({v640}|{v641}|{v642}|{v643}|{v644})
-v630 ((:)((:{hex4}){3}))
-v631 ((({hex4}:){1})((:{hex4}){2}))
+v630 ((:)((:{hex4}){1}){rfc4291_broader})
+v631 ((({hex4}:){1}){rfc4291_broader})
v632 ((({hex4}:){2})((:{hex4}){1}))
v633 ((({hex4}:){3})(:))
v63 ({v630}|{v631}|{v632}|{v633})
-v620 ((:)((:{hex4}){2}))
-v620_rfc4291 ((:)(:{ip4addr}))
+v620 ((:){rfc4291_broader})
v621 ((({hex4}:){1})((:{hex4}){1}))
v622 ((({hex4}:){2})(:))
-v62_rfc4291 ((:)(:[fF]{4})(:{ip4addr}))
-v62 ({v620}|{v621}|{v622}|{v62_rfc4291}|{v620_rfc4291})
+v62 ({v620}|{v621}|{v622})
v610 ((:)(:{hex4}{1}))
v611 ((({hex4}:){1})(:))
v61 ({v610}|{v611})
@@ -201,10 +209,12 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%s SCANSTATE_CT
%s SCANSTATE_COUNTER
%s SCANSTATE_ETH
+%s SCANSTATE_GRE
%s SCANSTATE_ICMP
%s SCANSTATE_IGMP
%s SCANSTATE_IP
%s SCANSTATE_IP6
+%s SCANSTATE_LAST
%s SCANSTATE_LIMIT
%s SCANSTATE_META
%s SCANSTATE_POLICY
@@ -214,6 +224,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%s SCANSTATE_TCP
%s SCANSTATE_TYPE
%s SCANSTATE_VLAN
+%s SCANSTATE_XT
+%s SCANSTATE_CMD_DESTROY
%s SCANSTATE_CMD_EXPORT
%s SCANSTATE_CMD_IMPORT
%s SCANSTATE_CMD_LIST
@@ -357,6 +369,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"import" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_IMPORT); return IMPORT; }
"export" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_EXPORT); return EXPORT; }
"monitor" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_MONITOR); return MONITOR; }
+"destroy" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_DESTROY); return DESTROY; }
+
"position" { return POSITION; }
"index" { return INDEX; }
@@ -397,9 +411,15 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT>"packets" { return PACKETS; }
<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"bytes" { return BYTES; }
+"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
+<SCANSTATE_LAST>{
+ "never" { return NEVER; }
+}
+
<SCANSTATE_CMD_LIST,SCANSTATE_CMD_RESET>{
"counters" { return COUNTERS; }
"quotas" { return QUOTAS; }
+ "rules" { return RULES; }
}
"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; }
@@ -431,10 +451,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
<SCANSTATE_QUOTA>{
- "used" { return USED; }
"until" { return UNTIL; }
}
+<SCANSTATE_QUOTA,SCANSTATE_LAST>"used" { return USED; }
+
"hour" { return HOUR; }
"day" { return DAY; }
@@ -491,7 +512,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; }
-<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF>{
+<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF,SCANSTATE_GRE>{
"version" { return HDRVERSION; }
}
<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP>{
@@ -508,7 +529,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_EXPR_OSF,SCANSTATE_IP>{
"ttl" { return TTL; }
}
-<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE>"protocol" { return PROTOCOL; }
+<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE,SCANSTATE_GRE>"protocol" { return PROTOCOL; }
<SCANSTATE_EXPR_MH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE,SCANSTATE_ICMP,SCANSTATE_IGMP,SCANSTATE_IP,SCANSTATE_SCTP,SCANSTATE_TCP>{
"checksum" { return CHECKSUM; }
}
@@ -573,6 +594,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"param-problem" { return PPTR; }
"max-delay" { return MAXDELAY; }
"mtu" { return MTU; }
+ "taddr" { return TADDR; }
+ "daddr" { return DADDR; }
}
<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_ICMP,SCANSTATE_TCP>{
"sequence" { return SEQUENCE; }
@@ -619,6 +642,17 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_CT,SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{
"dport" { return DPORT; }
}
+<SCANSTATE_EXPR_DCCP>{
+ "option" { return OPTION; }
+}
+
+"vxlan" { return VXLAN; }
+"vni" { return VNI; }
+
+"geneve" { return GENEVE; }
+
+"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; }
+"gretap" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; }
"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; }
@@ -761,6 +795,10 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"fib" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; }
+<SCANSTATE_EXPR_FIB>{
+ "check" { return CHECK; }
+}
+
"osf" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; }
"synproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; }
@@ -799,6 +837,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"secmark" { scanner_push_start_cond(yyscanner, SCANSTATE_SECMARK); return SECMARK; }
+"xt" { scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; }
+
{addrstring} {
yylval->string = xstrdup(yytext);
return STRING;
@@ -815,9 +855,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
return STRING;
}
-{numberstring} {
+{hexstring} {
errno = 0;
- yylval->val = strtoull(yytext, NULL, 0);
+ yylval->val = strtoull(yytext, NULL, 16);
if (errno != 0) {
yylval->string = xstrdup(yytext);
return STRING;
@@ -825,6 +865,19 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
return NUM;
}
+{decstring} {
+ int base = yytext[0] == '0' ? 8 : 10;
+ char *end;
+
+ errno = 0;
+ yylval->val = strtoull(yytext, &end, base);
+ if (errno != 0 || *end) {
+ yylval->string = xstrdup(yytext);
+ return STRING;
+ }
+ return NUM;
+ }
+
{classid}/[ \t\n:\-},] {
yylval->string = xstrdup(yytext);
return STRING;
@@ -846,6 +899,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
return STRING;
}
+{newline_crlf} { return CRLF; }
+
\\{newline} {
reset_pos(yyget_extra(yyscanner), yylloc);
}
@@ -857,6 +912,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
{tab}+
{space}+
+{comment_line} {
+ reset_pos(yyget_extra(yyscanner), yylloc);
+ }
{comment}
<<EOF>> {
@@ -928,9 +986,59 @@ static void scanner_push_file(struct nft_ctx *nft, void *scanner,
scanner_push_indesc(state, indesc);
}
+enum nft_include_type {
+ NFT_INCLUDE,
+ NFT_CMDLINE,
+};
+
+static bool __is_useable(unsigned int type, enum nft_include_type t)
+{
+ type &= S_IFMT;
+ switch (type) {
+ case S_IFREG: return true;
+ case S_IFIFO:
+ return t == NFT_CMDLINE; /* disallow include /path/to/fifo */
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/* need to use stat() to, fopen() will block for named fifos */
+static bool filename_is_useable(const char *name)
+{
+ struct stat sb;
+ int err;
+
+ err = stat(name, &sb);
+ if (err)
+ return false;
+
+ return __is_useable(sb.st_mode, NFT_INCLUDE);
+}
+
+static bool fp_is_useable(FILE *fp, enum nft_include_type t)
+{
+ int fd = fileno(fp);
+ struct stat sb;
+ int err;
+
+ if (fd < 0)
+ return false;
+
+ err = fstat(fd, &sb);
+ if (err < 0)
+ return false;
+
+ return __is_useable(sb.st_mode, t);
+}
+
static int include_file(struct nft_ctx *nft, void *scanner,
const char *filename, const struct location *loc,
- const struct input_descriptor *parent_indesc)
+ const struct input_descriptor *parent_indesc,
+ enum nft_include_type includetype)
+
{
struct parser_state *state = yyget_extra(scanner);
struct error_record *erec;
@@ -942,12 +1050,24 @@ static int include_file(struct nft_ctx *nft, void *scanner,
goto err;
}
+ if (includetype == NFT_INCLUDE && !filename_is_useable(filename)) {
+ erec = error(loc, "Not a regular file: \"%s\"\n", filename);
+ goto err;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
erec = error(loc, "Could not open file \"%s\": %s\n",
filename, strerror(errno));
goto err;
}
+
+ if (!fp_is_useable(f, includetype)) {
+ fclose(f);
+ erec = error(loc, "Not a regular file: \"%s\"\n", filename);
+ goto err;
+ }
+
scanner_push_file(nft, scanner, f, filename, loc, parent_indesc);
return 0;
err:
@@ -1008,7 +1128,7 @@ static int include_glob(struct nft_ctx *nft, void *scanner, const char *pattern,
ret = glob(pattern, flags, NULL, &glob_data);
if (ret == 0) {
char *path;
- int len;
+ size_t len;
/* reverse alphabetical order due to stack */
for (i = glob_data.gl_pathc; i > 0; i--) {
@@ -1020,7 +1140,7 @@ static int include_glob(struct nft_ctx *nft, void *scanner, const char *pattern,
if (len == 0 || path[len - 1] == '/')
continue;
- ret = include_file(nft, scanner, path, loc, indesc);
+ ret = include_file(nft, scanner, path, loc, indesc, NFT_INCLUDE);
if (ret != 0)
goto err;
}
@@ -1057,7 +1177,7 @@ err:
int scanner_read_file(struct nft_ctx *nft, const char *filename,
const struct location *loc)
{
- return include_file(nft, nft->scanner, filename, loc, NULL);
+ return include_file(nft, nft->scanner, filename, loc, NULL, NFT_CMDLINE);
}
static bool search_in_include_path(const char *filename)
@@ -1067,39 +1187,58 @@ static bool search_in_include_path(const char *filename)
filename[0] != '/');
}
+static int include_path_glob(struct nft_ctx *nft, void *scanner,
+ const char *include_path, const char *filename,
+ const struct location *loc)
+{
+ struct parser_state *state = yyget_extra(scanner);
+ struct error_record *erec;
+ char buf[PATH_MAX];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf), "%s/%s", include_path, filename);
+ if (ret < 0 || ret >= PATH_MAX) {
+ erec = error(loc, "Too long file path \"%s/%s\"\n",
+ include_path, filename);
+ erec_queue(erec, state->msgs);
+ return -1;
+ }
+
+ ret = include_glob(nft, scanner, buf, loc);
+
+ /* error was already handled */
+ if (ret == -1)
+ return -1;
+ /* no wildcards and file was processed: break early. */
+ if (ret == 0)
+ return 0;
+
+ /* else 1 (no wildcards) or 2 (wildcards): keep
+ * searching.
+ */
+ return ret;
+}
+
int scanner_include_file(struct nft_ctx *nft, void *scanner,
const char *filename, const struct location *loc)
{
struct parser_state *state = yyget_extra(scanner);
struct error_record *erec;
- char buf[PATH_MAX];
unsigned int i;
int ret = -1;
if (search_in_include_path(filename)) {
for (i = 0; i < nft->num_include_paths; i++) {
- ret = snprintf(buf, sizeof(buf), "%s/%s",
- nft->include_paths[i], filename);
- if (ret < 0 || ret >= PATH_MAX) {
- erec = error(loc, "Too long file path \"%s/%s\"\n",
- nft->include_paths[i], filename);
- erec_queue(erec, state->msgs);
- return -1;
- }
-
- ret = include_glob(nft, scanner, buf, loc);
-
- /* error was already handled */
- if (ret == -1)
- return -1;
- /* no wildcards and file was processed: break early. */
- if (ret == 0)
- return 0;
-
- /* else 1 (no wildcards) or 2 (wildcards): keep
- * searching.
- */
+ ret = include_path_glob(nft, scanner,
+ nft->include_paths[i],
+ filename, loc);
+ if (ret <= 0)
+ return ret;
}
+ ret = include_path_glob(nft, scanner, DEFAULT_INCLUDE_PATH,
+ filename, loc);
+ if (ret <= 0)
+ return ret;
} else {
/* an absolute path (starts with '/') */
ret = include_glob(nft, scanner, filename, loc);
@@ -1152,8 +1291,8 @@ void *scanner_init(struct parser_state *state)
static void input_descriptor_destroy(const struct input_descriptor *indesc)
{
if (indesc->name)
- xfree(indesc->name);
- xfree(indesc);
+ free_const(indesc->name);
+ free_const(indesc);
}
static void input_descriptor_list_destroy(struct parser_state *state)
@@ -1175,7 +1314,7 @@ void scanner_destroy(struct nft_ctx *nft)
struct parser_state *state = yyget_extra(nft->scanner);
input_descriptor_list_destroy(state);
- xfree(state->startcond_active);
+ free(state->startcond_active);
yylex_destroy(nft->scanner);
}
diff --git a/src/sctp_chunk.c b/src/sctp_chunk.c
index 6e73e72f..24a07e20 100644
--- a/src/sctp_chunk.c
+++ b/src/sctp_chunk.c
@@ -6,10 +6,11 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <exthdr.h>
#include <sctp_chunk.h>
-#include <string.h>
#define PHT(__token, __offset, __len) \
PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \
diff --git a/src/segtree.c b/src/segtree.c
index c36497ce..00db8810 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -8,8 +8,8 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <stdlib.h>
-#include <string.h>
+#include <nft.h>
+
#include <inttypes.h>
#include <arpa/inet.h>
@@ -110,36 +110,50 @@ struct expr *get_set_intervals(const struct set *set, const struct expr *init)
return new_init;
}
+static struct expr *expr_value(struct expr *expr)
+{
+ switch (expr->etype) {
+ case EXPR_MAPPING:
+ return expr->left->key;
+ case EXPR_SET_ELEM:
+ return expr->key;
+ case EXPR_VALUE:
+ return expr;
+ default:
+ BUG("invalid expression type %s\n", expr_name(expr));
+ }
+}
+
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 expr *i;
+ struct expr *i, *key;
mpz_t val;
mpz_init2(val, set->key->len);
list_for_each_entry(i, &set->init->expressions, list) {
- switch (i->key->etype) {
+ key = expr_value(i);
+ switch (key->etype) {
case EXPR_VALUE:
if (expr_basetype(i->key)->type != TYPE_STRING)
break;
- /* string type, check if its a range (wildcard), so
- * fall through.
- */
+ /* string type, check if its a range (wildcard). */
+ /* fall-through */
case EXPR_PREFIX:
case EXPR_RANGE:
range_expr_value_low(val, i);
- if (left && mpz_cmp(left->key->value, val))
+ if (left && mpz_cmp(expr_value(left)->value, val))
break;
range_expr_value_high(val, i);
- if (right && mpz_cmp(right->key->value, val))
+ if (right && mpz_cmp(expr_value(right)->value, val))
break;
- range = expr_clone(i->key);
+ range = expr_clone(i);
goto out;
default:
break;
@@ -151,18 +165,6 @@ out:
return range;
}
-static struct expr *expr_value(struct expr *expr)
-{
- switch (expr->etype) {
- case EXPR_MAPPING:
- return expr->left->key;
- case EXPR_SET_ELEM:
- return expr->key;
- default:
- BUG("invalid expression type %s\n", expr_name(expr));
- }
-}
-
static struct expr *__expr_to_set_elem(struct expr *low, struct expr *expr)
{
struct expr *elem = set_elem_expr_alloc(&low->location, expr);
@@ -205,6 +207,27 @@ static struct expr *expr_to_set_elem(struct expr *e)
return __expr_to_set_elem(e, expr);
}
+static void set_compound_expr_add(struct expr *compound, struct expr *expr, struct expr *orig)
+{
+ struct expr *elem;
+
+ switch (expr->etype) {
+ case EXPR_SET_ELEM:
+ list_splice_init(&orig->stmt_list, &expr->stmt_list);
+ compound_expr_add(compound, expr);
+ break;
+ case EXPR_MAPPING:
+ list_splice_init(&orig->left->stmt_list, &expr->left->stmt_list);
+ compound_expr_add(compound, expr);
+ break;
+ default:
+ elem = set_elem_expr_alloc(&orig->location, expr);
+ list_splice_init(&orig->stmt_list, &elem->stmt_list);
+ compound_expr_add(compound, elem);
+ break;
+ }
+}
+
int get_set_decompose(struct set *cache_set, struct set *set)
{
struct expr *i, *next, *range;
@@ -226,20 +249,23 @@ int get_set_decompose(struct set *cache_set, struct set *set)
errno = ENOENT;
return -1;
}
+
+ set_compound_expr_add(new_init, range, left);
+
expr_free(left);
expr_free(i);
- compound_expr_add(new_init, range);
left = NULL;
} else {
if (left) {
range = get_set_interval_find(cache_set,
left, NULL);
+
if (range)
- compound_expr_add(new_init, range);
+ set_compound_expr_add(new_init, range, left);
else
- compound_expr_add(new_init,
- expr_to_set_elem(left));
+ set_compound_expr_add(new_init,
+ expr_to_set_elem(left), left);
}
left = i;
}
@@ -247,9 +273,9 @@ int get_set_decompose(struct set *cache_set, struct set *set)
if (left) {
range = get_set_interval_find(cache_set, left, NULL);
if (range)
- compound_expr_add(new_init, range);
+ set_compound_expr_add(new_init, range, left);
else
- compound_expr_add(new_init, expr_to_set_elem(left));
+ set_compound_expr_add(new_init, expr_to_set_elem(left), left);
}
expr_free(set->init);
@@ -401,7 +427,7 @@ void concat_range_aggregate(struct expr *set)
}
if (prefix_len < 0 ||
- !(r1->dtype->flags & DTYPE_F_PREFIX)) {
+ !datatype_prefix_notation(r1->dtype)) {
tmp = range_expr_alloc(&r1->location, r1,
r2);
@@ -470,7 +496,7 @@ static struct expr *interval_to_string(struct expr *low, struct expr *i, const m
expr = constant_expr_alloc(&low->location, low->dtype,
BYTEORDER_HOST_ENDIAN,
- (str_len + 1) * BITS_PER_BYTE, data);
+ len * BITS_PER_BYTE, data);
return __expr_to_set_elem(low, expr);
}
@@ -493,12 +519,49 @@ static struct expr *interval_to_range(struct expr *low, struct expr *i, mpz_t ra
return __expr_to_set_elem(low, tmp);
}
+static void
+add_interval(struct expr *set, struct expr *low, struct expr *i)
+{
+ struct expr *expr;
+ mpz_t range, p;
+
+ mpz_init(range);
+ mpz_init(p);
+
+ mpz_sub(range, expr_value(i)->value, expr_value(low)->value);
+ if (i->etype != EXPR_VALUE)
+ mpz_sub_ui(range, range, 1);
+
+ mpz_and(p, expr_value(low)->value, range);
+
+ if (!mpz_cmp_ui(range, 0)) {
+ if (expr_basetype(low)->type == TYPE_STRING)
+ mpz_switch_byteorder(expr_value(low)->value,
+ expr_value(low)->len / BITS_PER_BYTE);
+ low->flags |= EXPR_F_KERNEL;
+ expr = expr_get(low);
+ } else if (range_is_prefix(range) && !mpz_cmp_ui(p, 0)) {
+
+ if (datatype_prefix_notation(i->dtype))
+ expr = interval_to_prefix(low, i, range);
+ else if (expr_basetype(i)->type == TYPE_STRING)
+ expr = interval_to_string(low, i, range);
+ else
+ expr = interval_to_range(low, i, range);
+ } else
+ expr = interval_to_range(low, i, range);
+
+ compound_expr_add(set, expr);
+
+ mpz_clear(range);
+ mpz_clear(p);
+}
+
void interval_map_decompose(struct expr *set)
{
struct expr *i, *next, *low = NULL, *end, *catchall = NULL, *key;
struct expr **elements, **ranges;
unsigned int n, m, size;
- mpz_t range, p;
bool interval;
if (set->size == 0)
@@ -507,9 +570,6 @@ void interval_map_decompose(struct expr *set)
elements = xmalloc_array(set->size, sizeof(struct expr *));
ranges = xmalloc_array(set->size * 2, sizeof(struct expr *));
- mpz_init(range);
- mpz_init(p);
-
/* Sort elements */
n = 0;
list_for_each_entry_safe(i, next, &set->expressions, list) {
@@ -568,32 +628,7 @@ void interval_map_decompose(struct expr *set)
}
}
- mpz_sub(range, expr_value(i)->value, expr_value(low)->value);
- mpz_sub_ui(range, range, 1);
-
- mpz_and(p, expr_value(low)->value, range);
-
- if (!mpz_cmp_ui(range, 0)) {
- if (expr_basetype(low)->type == TYPE_STRING)
- mpz_switch_byteorder(expr_value(low)->value, expr_value(low)->len / BITS_PER_BYTE);
- low->flags |= EXPR_F_KERNEL;
- compound_expr_add(set, expr_get(low));
- } else if (range_is_prefix(range) && !mpz_cmp_ui(p, 0)) {
- struct expr *expr;
-
- if (i->dtype->flags & DTYPE_F_PREFIX)
- expr = interval_to_prefix(low, i, range);
- else if (expr_basetype(i)->type == TYPE_STRING)
- expr = interval_to_string(low, i, range);
- else
- expr = interval_to_range(low, i, range);
-
- compound_expr_add(set, expr);
- } else {
- struct expr *expr = interval_to_range(low, i, range);
-
- compound_expr_add(set, expr);
- }
+ add_interval(set, low, i);
if (i->flags & EXPR_F_INTERVAL_END) {
expr_free(low);
@@ -610,32 +645,20 @@ void interval_map_decompose(struct expr *set)
mpz_bitmask(i->value, i->len);
if (!mpz_cmp(i->value, expr_value(low)->value)) {
- expr_free(i);
- i = low;
+ compound_expr_add(set, low);
} else {
- 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,
- expr_clone(low->right));
- interval_expr_copy(i->left, low->left);
- } else {
- interval_expr_copy(i, low);
- }
- i->flags |= EXPR_F_KERNEL;
-
+ add_interval(set, low, i);
expr_free(low);
}
- compound_expr_add(set, i);
+ expr_free(i);
+
out:
- if (catchall)
+ if (catchall) {
+ catchall->flags |= EXPR_F_KERNEL;
compound_expr_add(set, catchall);
+ }
- mpz_clear(range);
- mpz_clear(p);
-
- xfree(ranges);
- xfree(elements);
+ free(ranges);
+ free(elements);
}
diff --git a/src/socket.c b/src/socket.c
index eb075153..8a149e63 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -4,10 +4,12 @@
* Copyright (c) 2018 Máté Eckl <ecklm94@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <socket.h>
diff --git a/src/statement.c b/src/statement.c
index 30caf9c7..695b57a6 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -8,12 +8,11 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
#include <inttypes.h>
-#include <string.h>
#include <syslog.h>
#include <rule.h>
@@ -34,25 +33,28 @@
#include <linux/netfilter/nf_log.h>
#include <linux/netfilter/nf_synproxy.h>
-struct stmt *stmt_alloc(const struct location *loc,
- const struct stmt_ops *ops)
+struct stmt *stmt_alloc(const struct location *loc, const struct stmt_ops *ops)
{
struct stmt *stmt;
stmt = xzalloc(sizeof(*stmt));
init_list_head(&stmt->list);
stmt->location = *loc;
- stmt->ops = ops;
+ stmt->type = ops->type;
return stmt;
}
void stmt_free(struct stmt *stmt)
{
+ const struct stmt_ops *ops;
+
if (stmt == NULL)
return;
- if (stmt->ops->destroy)
- stmt->ops->destroy(stmt);
- xfree(stmt);
+
+ ops = stmt_ops(stmt);
+ if (ops->destroy)
+ ops->destroy(stmt);
+ free(stmt);
}
void stmt_list_free(struct list_head *list)
@@ -67,7 +69,9 @@ void stmt_list_free(struct list_head *list)
void stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
- stmt->ops->print(stmt, octx);
+ const struct stmt_ops *ops = stmt_ops(stmt);
+
+ ops->print(stmt, octx);
}
static void expr_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
@@ -184,7 +188,7 @@ static void meter_stmt_destroy(struct stmt *stmt)
expr_free(stmt->meter.key);
expr_free(stmt->meter.set);
stmt_free(stmt->meter.stmt);
- xfree(stmt->meter.name);
+ free_const(stmt->meter.name);
}
static const struct stmt_ops meter_stmt_ops = {
@@ -249,6 +253,37 @@ struct stmt *counter_stmt_alloc(const struct location *loc)
return stmt;
}
+static void last_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ nft_print(octx, "last");
+
+ if (nft_output_stateless(octx))
+ return;
+
+ nft_print(octx, " used ");
+
+ if (stmt->last.set)
+ time_print(stmt->last.used, octx);
+ else
+ nft_print(octx, "never");
+}
+
+static const struct stmt_ops last_stmt_ops = {
+ .type = STMT_LAST,
+ .name = "last",
+ .print = last_stmt_print,
+ .json = last_stmt_json,
+};
+
+struct stmt *last_stmt_alloc(const struct location *loc)
+{
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &last_stmt_ops);
+ stmt->flags |= STMT_F_STATEFUL;
+ return stmt;
+}
+
static const char *objref_type[NFT_OBJECT_MAX + 1] = {
[NFT_OBJECT_COUNTER] = "counter",
[NFT_OBJECT_QUOTA] = "quota",
@@ -347,12 +382,8 @@ 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) {
- char prefix[NF_LOG_PREFIXLEN] = {};
-
- expr_to_string(stmt->log.prefix, prefix);
- nft_print(octx, " prefix \"%s\"", prefix);
- }
+ if (stmt->log.flags & STMT_LOG_PREFIX)
+ nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
if (stmt->log.flags & STMT_LOG_GROUP)
nft_print(octx, " group %u", stmt->log.group);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -389,7 +420,7 @@ static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void log_stmt_destroy(struct stmt *stmt)
{
- expr_free(stmt->log.prefix);
+ free_const(stmt->log.prefix);
}
static const struct stmt_ops log_stmt_ops = {
@@ -455,9 +486,7 @@ static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, "limit rate %s%" PRIu64 "/%s",
inv ? "over " : "", stmt->limit.rate,
get_unit(stmt->limit.unit));
- if (stmt->limit.burst && stmt->limit.burst != 5)
- nft_print(octx, " burst %u packets",
- stmt->limit.burst);
+ nft_print(octx, " burst %u packets", stmt->limit.burst);
break;
case NFT_LIMIT_PKT_BYTES:
data_unit = get_rate(stmt->limit.rate, &rate);
@@ -465,7 +494,7 @@ static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, "limit rate %s%" PRIu64 " %s/%s",
inv ? "over " : "", rate, data_unit,
get_unit(stmt->limit.unit));
- if (stmt->limit.burst != 5) {
+ if (stmt->limit.burst != 0) {
uint64_t burst;
data_unit = get_rate(stmt->limit.burst, &burst);
@@ -819,6 +848,7 @@ static const struct stmt_ops map_stmt_ops = {
.name = "map",
.print = map_stmt_print,
.destroy = map_stmt_destroy,
+ .json = map_stmt_json,
};
struct stmt *map_stmt_alloc(const struct location *loc)
@@ -960,7 +990,7 @@ static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
expr_print(stmt->tproxy.addr, octx);
}
}
- if (stmt->tproxy.port && stmt->tproxy.port->etype == EXPR_VALUE) {
+ if (stmt->tproxy.port) {
if (!stmt->tproxy.addr)
nft_print(octx, " ");
nft_print(octx, ":");
@@ -997,6 +1027,7 @@ static const struct stmt_ops xt_stmt_ops = {
.name = "xt",
.print = xt_stmt_print,
.destroy = xt_stmt_destroy,
+ .json = xt_stmt_json,
};
struct stmt *xt_stmt_alloc(const struct location *loc)
@@ -1053,3 +1084,59 @@ struct stmt *synproxy_stmt_alloc(const struct location *loc)
{
return stmt_alloc(loc, &synproxy_stmt_ops);
}
+
+/* For src/optimize.c */
+static struct stmt_ops invalid_stmt_ops = {
+ .type = STMT_INVALID,
+ .name = "unsupported",
+};
+
+static const struct stmt_ops *__stmt_ops_by_type(enum stmt_types type)
+{
+ switch (type) {
+ case STMT_INVALID: return &invalid_stmt_ops;
+ case STMT_EXPRESSION: return &expr_stmt_ops;
+ case STMT_VERDICT: return &verdict_stmt_ops;
+ case STMT_METER: return &meter_stmt_ops;
+ case STMT_COUNTER: return &counter_stmt_ops;
+ case STMT_PAYLOAD: return &payload_stmt_ops;
+ case STMT_META: return &meta_stmt_ops;
+ case STMT_LIMIT: return &limit_stmt_ops;
+ case STMT_LOG: return &log_stmt_ops;
+ case STMT_REJECT: return &reject_stmt_ops;
+ case STMT_NAT: return &nat_stmt_ops;
+ case STMT_TPROXY: return &tproxy_stmt_ops;
+ case STMT_QUEUE: return &queue_stmt_ops;
+ case STMT_CT: return &ct_stmt_ops;
+ case STMT_SET: return &set_stmt_ops;
+ case STMT_DUP: return &dup_stmt_ops;
+ case STMT_FWD: return &fwd_stmt_ops;
+ case STMT_XT: return &xt_stmt_ops;
+ case STMT_QUOTA: return &quota_stmt_ops;
+ case STMT_NOTRACK: return &notrack_stmt_ops;
+ case STMT_OBJREF: return &objref_stmt_ops;
+ case STMT_EXTHDR: return &exthdr_stmt_ops;
+ case STMT_FLOW_OFFLOAD: return &flow_offload_stmt_ops;
+ case STMT_CONNLIMIT: return &connlimit_stmt_ops;
+ case STMT_MAP: return &map_stmt_ops;
+ case STMT_SYNPROXY: return &synproxy_stmt_ops;
+ case STMT_CHAIN: return &chain_stmt_ops;
+ case STMT_OPTSTRIP: return &optstrip_stmt_ops;
+ case STMT_LAST: return &last_stmt_ops;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+const struct stmt_ops *stmt_ops(const struct stmt *stmt)
+{
+ const struct stmt_ops *ops;
+
+ ops = __stmt_ops_by_type(stmt->type);
+ if (!ops)
+ BUG("Unknown statement type %d\n", stmt->type);
+
+ return ops;
+}
diff --git a/src/tcpopt.c b/src/tcpopt.c
index c3e07d78..d8139337 100644
--- a/src/tcpopt.c
+++ b/src/tcpopt.c
@@ -1,8 +1,7 @@
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
@@ -109,6 +108,31 @@ static const struct exthdr_desc tcpopt_md5sig = {
},
};
+static const struct symbol_table mptcp_subtype_tbl = {
+ .base = BASE_DECIMAL,
+ .symbols = {
+ SYMBOL("mp-capable", 0),
+ SYMBOL("mp-join", 1),
+ SYMBOL("dss", 2),
+ SYMBOL("add-addr", 3),
+ SYMBOL("remove-addr", 4),
+ SYMBOL("mp-prio", 5),
+ SYMBOL("mp-fail", 6),
+ SYMBOL("mp-fastclose", 7),
+ SYMBOL("mp-tcprst", 8),
+ SYMBOL_LIST_END
+ },
+};
+
+/* alias of integer_type to parse mptcp subtypes */
+const struct datatype mptcpopt_subtype = {
+ .type = TYPE_INTEGER,
+ .name = "integer",
+ .desc = "mptcp option subtype",
+ .size = 4,
+ .basetype = &integer_type,
+ .sym_tbl = &mptcp_subtype_tbl,
+};
static const struct exthdr_desc tcpopt_mptcp = {
.name = "mptcp",
@@ -116,7 +140,17 @@ static const struct exthdr_desc tcpopt_mptcp = {
.templates = {
[TCPOPT_MPTCP_KIND] = PHT("kind", 0, 8),
[TCPOPT_MPTCP_LENGTH] = PHT("length", 8, 8),
- [TCPOPT_MPTCP_SUBTYPE] = PHT("subtype", 16, 4),
+ [TCPOPT_MPTCP_SUBTYPE] = PROTO_HDR_TEMPLATE("subtype",
+ &mptcpopt_subtype,
+ BYTEORDER_BIG_ENDIAN,
+ 16, 4),
+ },
+};
+
+static const struct exthdr_desc tcpopt_fallback = {
+ .templates = {
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
},
};
#undef PHT
@@ -134,6 +168,17 @@ const struct exthdr_desc *tcpopt_protocols[] = {
[TCPOPT_KIND_FASTOPEN] = &tcpopt_fastopen,
};
+static void tcpopt_assign_tmpl(struct expr *expr,
+ const struct proto_hdr_template *tmpl,
+ const struct exthdr_desc *desc)
+{
+ expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+
+ expr->exthdr.desc = desc;
+ expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
+}
+
/**
* tcpopt_expr_alloc - allocate tcp option extension expression
*
@@ -183,32 +228,38 @@ struct expr *tcpopt_expr_alloc(const struct location *loc,
desc = tcpopt_protocols[kind];
if (!desc) {
- if (field != TCPOPT_COMMON_KIND || kind > 255)
+ if (kind > 255)
return NULL;
+ desc = &tcpopt_fallback;
+
+ switch (field) {
+ case TCPOPT_COMMON_KIND:
+ case TCPOPT_COMMON_LENGTH:
+ tmpl = &desc->templates[field];
+ break;
+ default:
+ tmpl = &tcpopt_unknown_template;
+ break;
+ }
+
expr = expr_alloc(loc, EXPR_EXTHDR, &integer_type,
BYTEORDER_BIG_ENDIAN, 8);
- desc = tcpopt_protocols[TCPOPT_NOP];
- tmpl = &desc->templates[field];
- expr->exthdr.desc = desc;
- expr->exthdr.tmpl = tmpl;
- expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
expr->exthdr.raw_type = kind;
+ tcpopt_assign_tmpl(expr, tmpl, desc);
return expr;
}
tmpl = &desc->templates[field];
- if (!tmpl)
+ if (!tmpl || !tmpl->dtype)
return NULL;
expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
BYTEORDER_BIG_ENDIAN, tmpl->len);
- expr->exthdr.desc = desc;
- expr->exthdr.tmpl = tmpl;
- expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+
expr->exthdr.raw_type = desc->type;
- expr->exthdr.offset = tmpl->offset;
+ tcpopt_assign_tmpl(expr, tmpl, desc);
return expr;
}
diff --git a/src/utils.c b/src/utils.c
index 925841c5..2aa1eb4e 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -8,12 +8,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
-#include <string.h>
#include <nftables.h>
#include <utils.h>
@@ -24,11 +24,6 @@ void __noreturn __memory_allocation_error(const char *filename, uint32_t line)
exit(NFT_EXIT_NOMEM);
}
-void xfree(const void *ptr)
-{
- free((void *)ptr);
-}
-
void *xmalloc(size_t size)
{
void *ptr;
@@ -100,3 +95,8 @@ void xstrunescape(const char *in, char *out)
}
out[k++] = '\0';
}
+
+int round_pow_2(unsigned int n)
+{
+ return 1UL << fls(n - 1);
+}
diff --git a/src/xfrm.c b/src/xfrm.c
index 80f0ea03..b32b2a1d 100644
--- a/src/xfrm.c
+++ b/src/xfrm.c
@@ -1,11 +1,15 @@
/*
* XFRM (ipsec) expression
*
+ * Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de>
+ *
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <erec.h>
#include <expression.h>
@@ -13,7 +17,6 @@
#include <datatype.h>
#include <gmputil.h>
#include <utils.h>
-#include <string.h>
#include <netinet/ip.h>
#include <linux/netfilter.h>
diff --git a/src/xt.c b/src/xt.c
index 789de992..f7bee216 100644
--- a/src/xt.c
+++ b/src/xt.c
@@ -2,14 +2,14 @@
* Copyright (c) 2013-2015 Pablo Neira Ayuso <pablo@netfilter.org>
* Copyright (c) 2015 Arturo Borrero Gonzalez <arturo@debian.org>
*
- * This program is free software; you can redistribute it and/or modifyi
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
-#include <stdlib.h>
+#include <nft.h>
+
#include <time.h>
-#include <string.h>
#include <net/if.h>
#include <getopt.h>
#include <ctype.h> /* for isspace */
@@ -28,85 +28,108 @@
#ifdef HAVE_LIBXTABLES
#include <xtables.h>
+
+static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af);
#endif
void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx)
{
+ static const char *typename[NFT_XT_MAX] = {
+ [NFT_XT_MATCH] = "match",
+ [NFT_XT_TARGET] = "target",
+ [NFT_XT_WATCHER] = "watcher",
+ };
+ int rc = 0;
#ifdef HAVE_LIBXTABLES
struct xt_xlate *xl = xt_xlate_alloc(10240);
+ struct xtables_target *tg;
+ struct xt_entry_target *t;
+ struct xtables_match *mt;
+ struct xt_entry_match *m;
+ size_t size;
+ void *entry;
+
+ xtables_set_nfproto(stmt->xt.family);
+ entry = xt_entry_alloc(&stmt->xt, stmt->xt.family);
switch (stmt->xt.type) {
case NFT_XT_MATCH:
- if (stmt->xt.match->xlate) {
+ mt = xtables_find_match(stmt->xt.name, XTF_TRY_LOAD, NULL);
+ if (!mt) {
+ fprintf(octx->error_fp,
+ "# Warning: XT match %s not found\n",
+ stmt->xt.name);
+ break;
+ }
+ size = XT_ALIGN(sizeof(*m)) + stmt->xt.infolen;
+
+ m = xzalloc(size);
+ memcpy(&m->data, stmt->xt.info, stmt->xt.infolen);
+
+ m->u.match_size = size;
+ m->u.user.revision = stmt->xt.rev;
+
+ if (mt->xlate) {
struct xt_xlate_mt_params params = {
- .ip = stmt->xt.entry,
- .match = stmt->xt.match->m,
- .numeric = 0,
+ .ip = entry,
+ .match = m,
+ .numeric = 1,
};
- stmt->xt.match->xlate(xl, &params);
- nft_print(octx, "%s", xt_xlate_get(xl));
- } else if (stmt->xt.match->print) {
- printf("#");
- stmt->xt.match->print(&stmt->xt.entry,
- stmt->xt.match->m, 0);
+ rc = mt->xlate(xl, &params);
}
+ free(m);
break;
case NFT_XT_WATCHER:
case NFT_XT_TARGET:
- if (stmt->xt.target->xlate) {
+ tg = xtables_find_target(stmt->xt.name, XTF_TRY_LOAD);
+ if (!tg) {
+ fprintf(octx->error_fp,
+ "# Warning: XT target %s not found\n",
+ stmt->xt.name);
+ break;
+ }
+ size = XT_ALIGN(sizeof(*t)) + stmt->xt.infolen;
+
+ t = xzalloc(size);
+ memcpy(&t->data, stmt->xt.info, stmt->xt.infolen);
+
+ t->u.target_size = size;
+ t->u.user.revision = stmt->xt.rev;
+
+ strcpy(t->u.user.name, tg->name);
+
+ if (tg->xlate) {
struct xt_xlate_tg_params params = {
- .ip = stmt->xt.entry,
- .target = stmt->xt.target->t,
- .numeric = 0,
+ .ip = entry,
+ .target = t,
+ .numeric = 1,
};
- stmt->xt.target->xlate(xl, &params);
- nft_print(octx, "%s", xt_xlate_get(xl));
- } else if (stmt->xt.target->print) {
- printf("#");
- stmt->xt.target->print(NULL, stmt->xt.target->t, 0);
+ rc = tg->xlate(xl, &params);
}
- break;
- default:
+ free(t);
break;
}
+ if (rc == 1)
+ nft_print(octx, "%s", xt_xlate_get(xl));
xt_xlate_free(xl);
-#else
- nft_print(octx, "# xt_%s", stmt->xt.name);
+ free(entry);
#endif
+ if (!rc)
+ nft_print(octx, "xt %s \"%s\"",
+ typename[stmt->xt.type], stmt->xt.name);
}
void xt_stmt_destroy(struct stmt *stmt)
{
-#ifdef HAVE_LIBXTABLES
- switch (stmt->xt.type) {
- case NFT_XT_MATCH:
- if (!stmt->xt.match)
- break;
- if (stmt->xt.match->m)
- xfree(stmt->xt.match->m);
- xfree(stmt->xt.match);
- break;
- case NFT_XT_WATCHER:
- case NFT_XT_TARGET:
- if (!stmt->xt.target)
- break;
- if (stmt->xt.target->t)
- xfree(stmt->xt.target->t);
- xfree(stmt->xt.target);
- break;
- default:
- break;
- }
-#endif
- xfree(stmt->xt.entry);
- xfree(stmt->xt.name);
+ free_const(stmt->xt.name);
+ free(stmt->xt.info);
}
#ifdef HAVE_LIBXTABLES
-static void *xt_entry_alloc(struct xt_stmt *xt, uint32_t af)
+static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af)
{
union nft_entry {
struct ipt_entry ipt;
@@ -173,24 +196,6 @@ static uint32_t xt_proto(const struct proto_ctx *pctx)
return 0;
}
-
-static struct xtables_target *xt_target_clone(struct xtables_target *t)
-{
- struct xtables_target *clone;
-
- clone = xzalloc(sizeof(struct xtables_target));
- memcpy(clone, t, sizeof(struct xtables_target));
- return clone;
-}
-
-static struct xtables_match *xt_match_clone(struct xtables_match *m)
-{
- struct xtables_match *clone;
-
- clone = xzalloc(sizeof(struct xtables_match));
- memcpy(clone, m, sizeof(struct xtables_match));
- return clone;
-}
#endif
/*
@@ -201,43 +206,23 @@ void netlink_parse_match(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- struct stmt *stmt;
- const char *name;
-#ifdef HAVE_LIBXTABLES
- struct xtables_match *mt;
const char *mtinfo;
- struct xt_entry_match *m;
+ struct stmt *stmt;
uint32_t mt_len;
- xtables_set_nfproto(ctx->table->handle.family);
-
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME);
-
- mt = xtables_find_match(name, XTF_TRY_LOAD, NULL);
- if (!mt) {
- fprintf(stderr, "XT match %s not found\n", name);
- return;
- }
mtinfo = nftnl_expr_get(nle, NFTNL_EXPR_MT_INFO, &mt_len);
- m = xzalloc(sizeof(struct xt_entry_match) + mt_len);
- memcpy(&m->data, mtinfo, mt_len);
-
- m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
- m->u.user.revision = nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV);
-
stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
+ stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME));
stmt->xt.type = NFT_XT_MATCH;
- stmt->xt.match = xt_match_clone(mt);
- stmt->xt.match->m = m;
-#else
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME);
+ stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV);
+ stmt->xt.family = ctx->table->handle.family;
- stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
- stmt->xt.type = NFT_XT_MATCH;
-#endif
+ stmt->xt.infolen = mt_len;
+ stmt->xt.info = xmalloc(mt_len);
+ memcpy(stmt->xt.info, mtinfo, mt_len);
+
+ ctx->table->has_xt_stmts = true;
rule_stmt_append(ctx->rule, stmt);
}
@@ -245,44 +230,23 @@ void netlink_parse_target(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- struct stmt *stmt;
- const char *name;
-#ifdef HAVE_LIBXTABLES
- struct xtables_target *tg;
const void *tginfo;
- struct xt_entry_target *t;
- size_t size;
+ struct stmt *stmt;
uint32_t tg_len;
- xtables_set_nfproto(ctx->table->handle.family);
-
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME);
- tg = xtables_find_target(name, XTF_TRY_LOAD);
- if (!tg) {
- fprintf(stderr, "XT target %s not found\n", name);
- return;
- }
tginfo = nftnl_expr_get(nle, NFTNL_EXPR_TG_INFO, &tg_len);
- size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
- t = xzalloc(size);
- memcpy(&t->data, tginfo, tg_len);
- t->u.target_size = size;
- t->u.user.revision = nftnl_expr_get_u32(nle, NFTNL_EXPR_TG_REV);
- strcpy(t->u.user.name, tg->name);
-
stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
+ stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME));
stmt->xt.type = NFT_XT_TARGET;
- stmt->xt.target = xt_target_clone(tg);
- stmt->xt.target->t = t;
-#else
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME);
+ stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_TG_REV);
+ stmt->xt.family = ctx->table->handle.family;
- stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
- stmt->xt.type = NFT_XT_TARGET;
-#endif
+ stmt->xt.infolen = tg_len;
+ stmt->xt.info = xmalloc(tg_len);
+ memcpy(stmt->xt.info, tginfo, tg_len);
+
+ ctx->table->has_xt_stmts = true;
rule_stmt_append(ctx->rule, stmt);
}
@@ -305,11 +269,12 @@ static bool is_watcher(uint32_t family, struct stmt *stmt)
void stmt_xt_postprocess(struct rule_pp_ctx *rctx, struct stmt *stmt,
struct rule *rule)
{
- if (is_watcher(rctx->pctx.family, stmt))
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
+
+ if (is_watcher(dl->pctx.family, stmt))
stmt->xt.type = NFT_XT_WATCHER;
- stmt->xt.proto = xt_proto(&rctx->pctx);
- stmt->xt.entry = xt_entry_alloc(&stmt->xt, rctx->pctx.family);
+ stmt->xt.proto = xt_proto(&dl->pctx);
}
static int nft_xt_compatible_revision(const char *name, uint8_t rev, int opt)
@@ -383,7 +348,7 @@ err:
}
static struct option original_opts[] = {
- { NULL },
+ { },
};
static struct xtables_globals xt_nft_globals = {
@@ -395,7 +360,18 @@ static struct xtables_globals xt_nft_globals = {
void xt_init(void)
{
- /* Default to IPv4, but this changes in runtime */
- xtables_init_all(&xt_nft_globals, NFPROTO_IPV4);
+ static bool init_once;
+
+ if (!init_once) {
+ /* libxtables is full of global variables and cannot be used
+ * concurrently by multiple threads. Hence, it's fine that the
+ * "init_once" guard is not thread-safe either.
+ * Don't link against xtables if you want thread safety.
+ */
+ init_once = true;
+
+ /* Default to IPv4, but this changes in runtime */
+ xtables_init_all(&xt_nft_globals, NFPROTO_IPV4);
+ }
}
#endif