summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am118
-rw-r--r--src/cache.c1147
-rw-r--r--src/cli.c133
-rw-r--r--src/cmd.c368
-rw-r--r--src/ct.c35
-rw-r--r--src/datatype.c571
-rw-r--r--src/dccpopt.c277
-rw-r--r--src/erec.c104
-rw-r--r--src/evaluate.c2508
-rw-r--r--src/expression.c250
-rw-r--r--src/exthdr.c157
-rw-r--r--src/fib.c7
-rw-r--r--src/gmputil.c27
-rw-r--r--src/hash.c6
-rw-r--r--src/iface.c58
-rw-r--r--src/intervals.c755
-rw-r--r--src/ipopt.c30
-rw-r--r--src/json.c497
-rw-r--r--src/libnftables.c402
-rw-r--r--src/libnftables.map17
-rw-r--r--src/main.c118
-rw-r--r--src/mergesort.c80
-rw-r--r--src/meta.c251
-rw-r--r--src/mini-gmp.c4
-rw-r--r--src/misspell.c14
-rw-r--r--src/mnl.c1315
-rw-r--r--src/monitor.c161
-rw-r--r--src/netlink.c988
-rw-r--r--src/netlink_delinearize.c1252
-rw-r--r--src/netlink_linearize.c471
-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.c1406
-rw-r--r--src/osf.c11
-rw-r--r--src/owner.c182
-rw-r--r--src/parser_bison.y2301
-rw-r--r--src/parser_json.c1016
-rw-r--r--src/payload.c699
-rw-r--r--src/print.c7
-rw-r--r--src/proto.c290
-rw-r--r--src/rbtree.c388
-rw-r--r--src/rt.c15
-rw-r--r--src/rule.c1060
-rw-r--r--src/scanner.l835
-rw-r--r--src/sctp_chunk.c262
-rw-r--r--src/segtree.c990
-rw-r--r--src/socket.c30
-rw-r--r--src/statement.c155
-rw-r--r--src/tcpopt.c301
-rw-r--r--src/utils.c24
-rw-r--r--src/xfrm.c9
-rw-r--r--src/xt.c248
54 files changed, 16957 insertions, 5523 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index 3041a933..00000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,118 +0,0 @@
-include $(top_srcdir)/Make_global.am
-
-sbin_PROGRAMS = nft
-
-CLEANFILES = scanner.c parser_bison.c
-
-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 \
- ipopt.c \
- meta.c \
- rt.c \
- numgen.c \
- ct.c \
- xfrm.c \
- netlink.c \
- netlink_linearize.c \
- netlink_delinearize.c \
- misspell.c \
- monitor.c \
- segtree.c \
- rbtree.c \
- gmputil.c \
- utils.c \
- erec.c \
- mnl.c \
- iface.c \
- mergesort.c \
- osf.c \
- nfnl_osf.c \
- tcpopt.c \
- socket.c \
- print.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} \
- --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 7797ff6b..c000e32c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -6,22 +6,49 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <expression.h>
#include <statement.h>
#include <rule.h>
#include <erec.h>
#include <utils.h>
#include <cache.h>
+#include <netlink.h>
+#include <mnl.h>
+#include <libnftnl/chain.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
{
+ struct set *set;
+
switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ if (!cmd->table)
+ break;
+
+ flags |= NFT_CACHE_TABLE |
+ NFT_CACHE_CHAIN |
+ NFT_CACHE_SET |
+ NFT_CACHE_OBJECT |
+ NFT_CACHE_FLOWTABLE;
+ list_for_each_entry(set, &cmd->table->sets, list) {
+ if (set->automerge)
+ flags |= NFT_CACHE_SETELEM_MAYBE;
+ }
+ break;
case CMD_OBJ_CHAIN:
case CMD_OBJ_SET:
case CMD_OBJ_COUNTER:
case CMD_OBJ_QUOTA:
case CMD_OBJ_LIMIT:
case CMD_OBJ_SECMARK:
+ case CMD_OBJ_CT_HELPER:
+ case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_SYNPROXY:
case CMD_OBJ_FLOWTABLE:
flags |= NFT_CACHE_TABLE;
break;
@@ -30,7 +57,7 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
NFT_CACHE_CHAIN |
NFT_CACHE_SET |
NFT_CACHE_OBJECT |
- NFT_CACHE_SETELEM;
+ NFT_CACHE_SETELEM_MAYBE;
break;
case CMD_OBJ_RULE:
flags |= NFT_CACHE_TABLE |
@@ -54,7 +81,7 @@ static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- flags |= NFT_CACHE_SETELEM;
+ flags |= NFT_CACHE_SETELEM_MAYBE;
break;
default:
break;
@@ -78,13 +105,74 @@ static unsigned int evaluate_cache_get(struct cmd *cmd, unsigned int flags)
return flags;
}
-static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags)
+struct nft_cache_filter *nft_cache_filter_init(void)
+{
+ struct nft_cache_filter *filter;
+ int i;
+
+ filter = xzalloc(sizeof(struct nft_cache_filter));
+ memset(&filter->list, 0, sizeof(filter->list));
+ for (i = 0; i < NFT_CACHE_HSIZE; i++)
+ init_list_head(&filter->obj[i].head);
+
+ return filter;
+}
+
+void nft_cache_filter_fini(struct nft_cache_filter *filter)
+{
+ int i;
+
+ for (i = 0; i < NFT_CACHE_HSIZE; i++) {
+ struct nft_filter_obj *obj, *next;
+
+ list_for_each_entry_safe(obj, next, &filter->obj[i].head, list)
+ free(obj);
+ }
+ free(filter);
+}
+
+static void cache_filter_add(struct nft_cache_filter *filter,
+ const struct cmd *cmd)
+{
+ struct nft_filter_obj *obj;
+ uint32_t hash;
+
+ obj = xmalloc(sizeof(struct nft_filter_obj));
+ obj->family = cmd->handle.family;
+ obj->table = cmd->handle.table.name;
+ obj->set = cmd->handle.set.name;
+
+ hash = djb_hash(cmd->handle.set.name) % NFT_CACHE_HSIZE;
+ list_add_tail(&obj->list, &filter->obj[hash].head);
+}
+
+static bool cache_filter_find(const struct nft_cache_filter *filter,
+ const struct handle *handle)
+{
+ struct nft_filter_obj *obj;
+ uint32_t hash;
+
+ hash = djb_hash(handle->set.name) % NFT_CACHE_HSIZE;
+
+ list_for_each_entry(obj, &filter->obj[hash].head, list) {
+ if (obj->family == handle->family &&
+ !strcmp(obj->table, handle->table.name) &&
+ !strcmp(obj->set, handle->set.name))
+ return true;
+ }
+
+ return false;
+}
+
+static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags,
+ struct nft_cache_filter *filter)
{
switch (cmd->obj) {
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
case CMD_OBJ_METER:
flags |= NFT_CACHE_SET;
+ cache_filter_add(filter, cmd);
break;
case CMD_OBJ_RULESET:
flags |= NFT_CACHE_FLUSHED;
@@ -109,12 +197,229 @@ static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags)
return flags;
}
-unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
+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)
+ filter->list.family = cmd->handle.family;
+ if (!cmd->handle.table.name) {
+ flags |= NFT_CACHE_TABLE;
+ break;
+ } else if (filter) {
+ filter->list.table = cmd->handle.table.name;
+ }
+ flags |= NFT_CACHE_FULL;
+ break;
+ case CMD_OBJ_CHAIN:
+ if (filter && 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) {
+ 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
+ flags |= NFT_CACHE_FULL;
+ break;
+ case CMD_OBJ_CHAINS:
+ flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN;
+ break;
+ 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 &&
+ cmd->handle.flowtable.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.ft = cmd->handle.flowtable.name;
+ }
+ /* fall through */
+ case CMD_OBJ_FLOWTABLES:
+ flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
+ break;
+ case CMD_OBJ_RULESET:
+ 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_RULES:
+ case CMD_OBJ_RULE:
+ if (filter) {
+ 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;
+ }
+ flags |= NFT_CACHE_SET | NFT_CACHE_FLOWTABLE |
+ NFT_CACHE_OBJECT | NFT_CACHE_CHAIN;
+ break;
+ case CMD_OBJ_ELEMENTS:
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ flags |= NFT_CACHE_SET;
+ break;
+ default:
+ flags |= NFT_CACHE_TABLE;
+ break;
+ }
+
+ return flags;
+}
+
+static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
+{
+ const struct handle *h = &cmd->handle;
+ const struct location *loc;
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_RULE:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_CHAIN:
+ case CMD_OBJ_CHAINS:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->chain.name &&
+ strlen(h->chain.name) > NFT_NAME_MAXLEN) {
+ loc = &h->chain.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_ELEMENTS:
+ case CMD_OBJ_SET:
+ case CMD_OBJ_SETS:
+ case CMD_OBJ_MAP:
+ case CMD_OBJ_MAPS:
+ case CMD_OBJ_METER:
+ case CMD_OBJ_METERS:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->set.name &&
+ strlen(h->set.name) > NFT_NAME_MAXLEN) {
+ loc = &h->set.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_FLOWTABLE:
+ case CMD_OBJ_FLOWTABLES:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->flowtable.name &&
+ strlen(h->flowtable.name) > NFT_NAME_MAXLEN) {
+ loc = &h->flowtable.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_INVALID:
+ case CMD_OBJ_EXPR:
+ case CMD_OBJ_RULESET:
+ case CMD_OBJ_MARKUP:
+ case CMD_OBJ_MONITOR:
+ case CMD_OBJ_SETELEMS:
+ case CMD_OBJ_HOOKS:
+ break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ case CMD_OBJ_LIMIT:
+ case CMD_OBJ_LIMITS:
+ case CMD_OBJ_SECMARK:
+ case CMD_OBJ_SECMARKS:
+ case CMD_OBJ_SYNPROXY:
+ case CMD_OBJ_SYNPROXYS:
+ 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;
+ goto err_name_too_long;
+ }
+ if (h->obj.name &&
+ strlen(h->obj.name) > NFT_NAME_MAXLEN) {
+ loc = &h->obj.location;
+ goto err_name_too_long;
+ }
+ break;
+ }
+
+ return 0;
+
+err_name_too_long:
+ erec_queue(error(loc, "name too long, %d characters maximum allowed",
+ NFT_NAME_MAXLEN),
+ msgs);
+ return -1;
+}
+
+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;
struct cmd *cmd;
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));
+
switch (cmd->op) {
case CMD_ADD:
case CMD_INSERT:
@@ -127,6 +432,7 @@ unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
flags = NFT_CACHE_FULL;
break;
case CMD_DELETE:
+ case CMD_DESTROY:
flags |= NFT_CACHE_TABLE |
NFT_CACHE_CHAIN |
NFT_CACHE_SET |
@@ -139,28 +445,851 @@ unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
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:
- case CMD_EXPORT:
- flags |= NFT_CACHE_FULL | NFT_CACHE_REFRESH;
+ flags |= evaluate_cache_list(nft, cmd, flags, filter);
break;
case CMD_MONITOR:
flags |= NFT_CACHE_FULL;
break;
case CMD_FLUSH:
- flags = evaluate_cache_flush(cmd, flags);
+ flags = evaluate_cache_flush(cmd, flags, filter);
break;
case CMD_RENAME:
flags = evaluate_cache_rename(cmd, flags);
break;
case CMD_DESCRIBE:
case CMD_IMPORT:
+ case CMD_EXPORT:
break;
default:
break;
}
}
+ *pflags = flags;
- return 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);
+}
+
+void table_cache_del(struct table *table)
+{
+ cache_del(&table->cache);
+}
+
+struct table *table_cache_find(const struct cache *cache,
+ const char *name, uint32_t family)
+{
+ struct table *table;
+ uint32_t hash;
+
+ if (!name)
+ return NULL;
+
+ hash = djb_hash(name) % NFT_CACHE_HSIZE;
+ list_for_each_entry(table, &cache->ht[hash], cache.hlist) {
+ if (table->handle.family == family &&
+ !strcmp(table->handle.table.name, name))
+ return table;
+ }
+
+ return NULL;
+}
+
+struct chain_cache_dump_ctx {
+ struct netlink_ctx *nlctx;
+ struct table *table;
+};
+
+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;
+
+ table_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE);
+ family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
+
+ if (family != ctx->table->handle.family ||
+ strcmp(table_name, ctx->table->handle.table.name))
+ 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);
+
+ 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);
+ }
+
+ nftnl_chain_list_del(nlc);
+ nftnl_chain_free(nlc);
+
+ return 0;
+}
+
+static int chain_cache_init(struct netlink_ctx *ctx, struct table *table,
+ struct nftnl_chain_list *chain_list)
+{
+ struct chain_cache_dump_ctx dump_ctx = {
+ .nlctx = ctx,
+ .table = table,
+ };
+ nftnl_chain_list_foreach(chain_list, chain_cache_cb, &dump_ctx);
+
+ return 0;
+}
+
+static struct nftnl_chain_list *
+chain_cache_dump(struct netlink_ctx *ctx,
+ const struct nft_cache_filter *filter, int *err)
+{
+ struct nftnl_chain_list *chain_list;
+ const char *table = NULL;
+ const char *chain = NULL;
+ int family = NFPROTO_UNSPEC;
+
+ if (filter && filter->list.table && filter->list.chain) {
+ family = filter->list.family;
+ table = filter->list.table;
+ chain = filter->list.chain;
+ }
+
+ chain_list = mnl_nft_chain_dump(ctx, family, table, chain);
+ if (chain_list == NULL) {
+ if (errno == EINTR) {
+ *err = -1;
+ return NULL;
+ }
+ *err = 0;
+ return NULL;
+ }
+
+ return chain_list;
+}
+
+void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table,
+ const char *chain)
+{
+ struct nftnl_chain_list *chain_list;
+
+ chain_list = mnl_nft_chain_dump(ctx, table->handle.family,
+ table->handle.table.name, chain);
+ if (!chain_list)
+ return;
+
+ chain_cache_init(ctx, table, chain_list);
+
+ nftnl_chain_list_free(chain_list);
+}
+
+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);
+}
+
+void chain_cache_del(struct chain *chain)
+{
+ cache_del(&chain->cache);
+}
+
+struct chain *chain_cache_find(const struct table *table, const char *name)
+{
+ struct chain *chain;
+ uint32_t hash;
+
+ 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))
+ return chain;
+ }
+
+ return NULL;
+}
+
+static int list_rule_cb(struct nftnl_rule *nlr, void *data)
+{
+ struct netlink_ctx *ctx = data;
+ const struct handle *h = ctx->data;
+ const char *table, *chain;
+ struct rule *rule;
+ uint32_t family;
+
+ family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
+ table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
+ chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
+
+ 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;
+}
+
+int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter,
+ bool dump, bool reset)
+{
+ struct nftnl_rule_list *rule_cache;
+ const char *table = NULL;
+ const char *chain = NULL;
+ uint64_t rule_handle = 0;
+
+ if (filter) {
+ table = filter->list.table;
+ chain = filter->list.chain;
+ rule_handle = filter->list.rule_handle;
+ }
+
+ rule_cache = mnl_nft_rule_dump(ctx, h->family,
+ table, chain, rule_handle, dump, reset);
+ if (rule_cache == NULL) {
+ if (errno == EINTR)
+ return -1;
+
+ return 0;
+ }
+
+ ctx->data = h;
+ nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx);
+ nftnl_rule_list_free(rule_cache);
+ return 0;
+}
+
+struct set_cache_dump_ctx {
+ struct netlink_ctx *nlctx;
+ struct table *table;
+};
+
+static int set_cache_cb(struct nftnl_set *nls, void *arg)
+{
+ struct set_cache_dump_ctx *ctx = arg;
+ const char *set_table;
+ 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);
+
+ if (set_family != ctx->table->handle.family ||
+ strcmp(set_table, ctx->table->handle.table.name))
+ return 0;
+
+ set = netlink_delinearize_set(ctx->nlctx, nls);
+ if (!set)
+ 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);
+
+ nftnl_set_list_del(nls);
+ nftnl_set_free(nls);
+ return 0;
+}
+
+static int set_cache_init(struct netlink_ctx *ctx, struct table *table,
+ struct nftnl_set_list *set_list)
+{
+ struct set_cache_dump_ctx dump_ctx = {
+ .nlctx = ctx,
+ .table = table,
+ };
+
+ nftnl_set_list_foreach(set_list, set_cache_cb, &dump_ctx);
+
+ return 0;
+}
+
+static struct nftnl_set_list *
+set_cache_dump(struct netlink_ctx *ctx,
+ const struct nft_cache_filter *filter, int *err)
+{
+ struct nftnl_set_list *set_list;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *set = NULL;
+
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ set = filter->list.set;
+ }
+
+ set_list = mnl_nft_set_dump(ctx, family, table, set);
+ if (!set_list) {
+ if (errno == EINTR) {
+ *err = -1;
+ return NULL;
+ }
+ *err = 0;
+ return NULL;
+ }
+
+ return set_list;
+}
+
+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);
+}
+
+void set_cache_del(struct set *set)
+{
+ cache_del(&set->cache);
+}
+
+struct set *set_cache_find(const struct table *table, const char *name)
+{
+ struct set *set;
+ uint32_t hash;
+
+ 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))
+ return set;
+ }
+
+ return NULL;
+}
+
+struct obj_cache_dump_ctx {
+ struct netlink_ctx *nlctx;
+ struct table *table;
+};
+
+static int obj_cache_cb(struct nftnl_obj *nlo, void *arg)
+{
+ struct obj_cache_dump_ctx *ctx = arg;
+ const char *obj_name;
+ struct obj *obj;
+ uint32_t hash;
+
+ obj = netlink_delinearize_obj(ctx->nlctx, nlo);
+ if (!obj)
+ return -1;
+
+ 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);
+
+ return 0;
+}
+
+static int obj_cache_init(struct netlink_ctx *ctx, struct table *table,
+ struct nftnl_obj_list *obj_list)
+{
+ struct obj_cache_dump_ctx dump_ctx = {
+ .nlctx = ctx,
+ .table = table,
+ };
+ nftnl_obj_list_foreach(obj_list, obj_cache_cb, &dump_ctx);
+
+ return 0;
+}
+
+static struct nftnl_obj_list *obj_cache_dump(struct netlink_ctx *ctx,
+ const struct table *table)
+{
+ struct nftnl_obj_list *obj_list;
+
+ obj_list = mnl_nft_obj_dump(ctx, table->handle.family,
+ table->handle.table.name, NULL,
+ 0, true, false);
+ if (!obj_list) {
+ if (errno == EINTR)
+ return NULL;
+
+ /* old kernels do not support this, provide an empty list. */
+ obj_list = nftnl_obj_list_alloc();
+ if (!obj_list)
+ memory_allocation_error();
+
+ return obj_list;
+ }
+
+ return obj_list;
+}
+
+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);
+}
+
+void obj_cache_del(struct obj *obj)
+{
+ cache_del(&obj->cache);
+}
+
+struct obj *obj_cache_find(const struct table *table, const char *name,
+ uint32_t obj_type)
+{
+ struct obj *obj;
+ uint32_t hash;
+
+ 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) &&
+ obj->type == obj_type)
+ return obj;
+ }
+
+ return NULL;
+}
+
+struct ft_cache_dump_ctx {
+ struct netlink_ctx *nlctx;
+ struct table *table;
+};
+
+static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg)
+{
+ struct ft_cache_dump_ctx *ctx = arg;
+ struct flowtable *ft;
+ 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);
+
+ if (ft_family != ctx->table->handle.family ||
+ strcmp(ft_table, ctx->table->handle.table.name))
+ return 0;
+
+ ft = netlink_delinearize_flowtable(ctx->nlctx, nlf);
+ if (!ft)
+ 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);
+
+ nftnl_flowtable_list_del(nlf);
+ nftnl_flowtable_free(nlf);
+ return 0;
+}
+
+static int ft_cache_init(struct netlink_ctx *ctx, struct table *table,
+ struct nftnl_flowtable_list *ft_list)
+{
+ struct ft_cache_dump_ctx dump_ctx = {
+ .nlctx = ctx,
+ .table = table,
+ };
+ nftnl_flowtable_list_foreach(ft_list, ft_cache_cb, &dump_ctx);
+
+ return 0;
+}
+
+static struct nftnl_flowtable_list *
+ft_cache_dump(struct netlink_ctx *ctx, const struct nft_cache_filter *filter)
+{
+ struct nftnl_flowtable_list *ft_list;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *ft = NULL;
+
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ ft = filter->list.ft;
+ }
+
+ ft_list = mnl_nft_flowtable_dump(ctx, family, table, ft);
+ if (!ft_list) {
+ if (errno == EINTR)
+ return NULL;
+
+ /* old kernels do not support this, provide an empty list. */
+ ft_list = nftnl_flowtable_list_alloc();
+ if (!ft_list)
+ memory_allocation_error();
+
+ return ft_list;
+ }
+
+ return ft_list;
+}
+
+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);
+}
+
+void ft_cache_del(struct flowtable *ft)
+{
+ cache_del(&ft->cache);
+}
+
+struct flowtable *ft_cache_find(const struct table *table, const char *name)
+{
+ struct flowtable *ft;
+ uint32_t hash;
+
+ 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))
+ return ft;
+ }
+
+ return NULL;
+}
+
+static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h,
+ struct nft_cache *cache,
+ const struct nft_cache_filter *filter)
+{
+ struct table *table, *next;
+ int ret;
+
+ ret = netlink_list_tables(ctx, h, filter);
+ if (ret < 0)
+ return -1;
+
+ list_for_each_entry_safe(table, next, &ctx->list, list) {
+ list_del(&table->list);
+ table_cache_add(table, cache);
+ }
+
+ return 0;
+}
+
+static int rule_init_cache(struct netlink_ctx *ctx, struct table *table,
+ const struct nft_cache_filter *filter)
+{
+ struct rule *rule, *nrule;
+ struct chain *chain;
+ int ret;
+
+ ret = rule_cache_dump(ctx, &table->handle, filter, true, false);
+
+ list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
+ chain = chain_cache_find(table, rule->handle.chain.name);
+ if (!chain)
+ chain = chain_binding_lookup(table,
+ rule->handle.chain.name);
+ if (!chain)
+ goto err_ctx_list;
+
+ list_move_tail(&rule->list, &chain->rules);
+ }
+
+ return ret;
+
+err_ctx_list:
+ list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
+ list_del(&rule->list);
+ rule_free(rule);
+ }
+ errno = EINTR;
+
+ return -1;
+}
+
+static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table,
+ const char *chain_name)
+{
+ struct nft_cache_filter filter;
+ struct chain *chain;
+ int ret = 0;
+
+ list_for_each_entry(chain, &table->chain_bindings, cache.list) {
+ filter.list = (typeof(filter.list)) {
+ .table = table->handle.table.name,
+ .chain = chain->handle.chain.name,
+ };
+ ret = rule_init_cache(ctx, table, &filter);
+ }
+
+ return ret;
+}
+
+static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
+ const struct nft_cache_filter *filter)
+{
+ 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 table *table;
+ struct set *set;
+ int ret = 0;
+
+ if (flags & NFT_CACHE_CHAIN_BIT) {
+ chain_list = chain_cache_dump(ctx, filter, &ret);
+ if (!chain_list)
+ return -1;
+ }
+ if (flags & NFT_CACHE_SET_BIT) {
+ set_list = set_cache_dump(ctx, filter, &ret);
+ if (!set_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
+ if (flags & NFT_CACHE_FLOWTABLE_BIT) {
+ ft_list = ft_cache_dump(ctx, filter);
+ if (!ft_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
+
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
+ if (flags & NFT_CACHE_SET_BIT) {
+ ret = set_cache_init(ctx, table, set_list);
+ if (ret < 0)
+ goto cache_fails;
+ }
+ if (flags & NFT_CACHE_SETELEM_BIT) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
+ if (cache_filter_find(filter, &set->handle))
+ continue;
+ if (!set_is_anonymous(set->flags) &&
+ flags & NFT_CACHE_TERSE)
+ continue;
+
+ ret = netlink_list_setelems(ctx, &set->handle,
+ set, false);
+ if (ret < 0)
+ goto cache_fails;
+ }
+ } else if (flags & NFT_CACHE_SETELEM_MAYBE) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
+ if (cache_filter_find(filter, &set->handle))
+ continue;
+
+ if (!set_is_non_concat_range(set))
+ continue;
+
+ ret = netlink_list_setelems(ctx, &set->handle,
+ set, false);
+ if (ret < 0)
+ goto cache_fails;
+ }
+ }
+ if (flags & NFT_CACHE_CHAIN_BIT) {
+ ret = chain_cache_init(ctx, table, chain_list);
+ if (ret < 0)
+ goto cache_fails;
+ }
+ if (flags & NFT_CACHE_FLOWTABLE_BIT) {
+ ret = ft_cache_init(ctx, table, ft_list);
+ if (ret < 0)
+ 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;
+ }
+
+ if (flags & NFT_CACHE_RULE_BIT) {
+ ret = rule_init_cache(ctx, table, filter);
+ if (ret < 0)
+ goto cache_fails;
+
+ if (filter && filter->list.table && filter->list.chain) {
+ ret = implicit_chain_cache(ctx, table, filter->list.chain);
+ if (ret < 0)
+ goto cache_fails;
+ }
+ }
+ }
+
+cache_fails:
+ if (set_list)
+ nftnl_set_list_free(set_list);
+ if (ft_list)
+ nftnl_flowtable_list_free(ft_list);
+
+ if (flags & NFT_CACHE_CHAIN_BIT)
+ nftnl_chain_list_free(chain_list);
+
+ return ret;
+}
+
+static int nft_cache_init(struct netlink_ctx *ctx, unsigned int flags,
+ const struct nft_cache_filter *filter)
+{
+ struct handle handle = {
+ .family = NFPROTO_UNSPEC,
+ };
+ int ret;
+
+ if (flags == NFT_CACHE_EMPTY)
+ return 0;
+
+ /* assume NFT_CACHE_TABLE is always set. */
+ ret = cache_init_tables(ctx, &handle, &ctx->nft->cache, filter);
+ if (ret < 0)
+ return ret;
+ ret = cache_init_objects(ctx, flags, filter);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+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)
+{
+ return cache->flags & NFT_CACHE_REFRESH;
+}
+
+static bool nft_cache_is_updated(struct nft_cache *cache, uint16_t genid)
+{
+ return genid && genid == cache->genid;
+}
+
+bool nft_cache_needs_update(struct nft_cache *cache)
+{
+ return cache->flags & NFT_CACHE_UPDATE;
+}
+
+int nft_cache_update(struct nft_ctx *nft, unsigned int flags,
+ struct list_head *msgs,
+ const struct nft_cache_filter *filter)
+{
+ struct netlink_ctx ctx = {
+ .list = LIST_HEAD_INIT(ctx.list),
+ .nft = nft,
+ .msgs = msgs,
+ };
+ struct nft_cache *cache = &nft->cache;
+ uint32_t genid, genid_stop, oldflags;
+ int ret;
+replay:
+ ctx.seqnum = cache->seqnum++;
+ genid = mnl_genid_get(&ctx);
+ if (!nft_cache_needs_refresh(cache) &&
+ nft_cache_is_complete(cache, flags) &&
+ nft_cache_is_updated(cache, genid))
+ return 0;
+
+ if (cache->genid)
+ nft_cache_release(cache);
+
+ if (flags & NFT_CACHE_FLUSHED) {
+ oldflags = flags;
+ flags = NFT_CACHE_EMPTY;
+ if (oldflags & NFT_CACHE_UPDATE)
+ flags |= NFT_CACHE_UPDATE;
+ goto skip;
+ }
+
+ ret = nft_cache_init(&ctx, flags, filter);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ nft_cache_release(cache);
+ goto replay;
+ }
+
+ erec_queue(error(&netlink_location, "cache initialization failed: %s",
+ strerror(errno)),
+ msgs);
+ nft_cache_release(cache);
+
+ return -1;
+ }
+
+ genid_stop = mnl_genid_get(&ctx);
+ if (genid != genid_stop) {
+ nft_cache_release(cache);
+ goto replay;
+ }
+skip:
+ cache->genid = genid;
+ cache->flags = flags;
+ return 0;
+}
+
+static void nft_cache_flush(struct cache *table_cache)
+{
+ struct table *table, *next;
+
+ list_for_each_entry_safe(table, next, &table_cache->list, cache.list) {
+ table_cache_del(table);
+ table_free(table);
+ }
+}
+
+void nft_cache_release(struct nft_cache *cache)
+{
+ nft_cache_flush(&cache->table_cache);
+ cache->genid = 0;
+ cache->flags = NFT_CACHE_EMPTY;
+}
+
+void cache_init(struct cache *cache)
+{
+ int i;
+
+ cache->ht = xmalloc(sizeof(struct list_head) * NFT_CACHE_HSIZE);
+ for (i = 0; i < NFT_CACHE_HSIZE; i++)
+ init_list_head(&cache->ht[i]);
+
+ init_list_head(&cache->list);
+}
+
+void cache_free(struct cache *cache)
+{
+ free(cache->ht);
+}
+
+void cache_add(struct cache_item *item, struct cache *cache, uint32_t hash)
+{
+ list_add_tail(&item->hlist, &cache->ht[hash]);
+ list_add_tail(&item->list, &cache->list);
+}
+
+void cache_del(struct cache_item *item)
+{
+ list_del(&item->hlist);
+ list_del(&item->list);
}
diff --git a/src/cli.c b/src/cli.c
index 4c0c3e9d..448c25c2 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -12,18 +12,19 @@
* 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
#include <readline/readline.h>
#include <readline/history.h>
+#elif defined(HAVE_LIBEDIT)
+#include <editline/readline.h>
#else
#include <linenoise.h>
#endif
@@ -35,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
@@ -48,7 +58,26 @@ init_histfile(void)
snprintf(histfile, sizeof(histfile), "%s/%s", home, CMDLINE_HISTFILE);
}
-#ifdef HAVE_LIBREADLINE
+#if defined(HAVE_LIBREADLINE)
+static void nft_rl_prompt_save(void)
+{
+ rl_save_prompt();
+ rl_clear_message();
+ rl_set_prompt(".... ");
+}
+#define nft_rl_prompt_restore rl_restore_prompt
+#elif defined(HAVE_LIBEDIT)
+static void nft_rl_prompt_save(void)
+{
+ rl_set_prompt(".... ");
+}
+static void nft_rl_prompt_restore(void)
+{
+ rl_set_prompt("nft> ");
+}
+#endif
+
+#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
static struct nft_ctx *cli_nft;
static char *multiline;
@@ -72,17 +101,15 @@ static char *cli_append_multiline(char *line)
if (multiline == NULL) {
multiline = line;
- rl_save_prompt();
- rl_clear_message();
- rl_set_prompt(".... ");
+ nft_rl_prompt_save();
} else {
len += strlen(multiline);
s = malloc(len + 1);
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);
@@ -93,7 +120,7 @@ static char *cli_append_multiline(char *line)
if (complete) {
line = multiline;
multiline = NULL;
- rl_restore_prompt();
+ nft_rl_prompt_restore();
}
return line;
}
@@ -106,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);
@@ -120,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);
@@ -133,6 +157,9 @@ static void cli_complete(char *line)
nft_run_cmd_from_buffer(cli_nft, line);
free(line);
}
+#endif
+
+#if defined(HAVE_LIBREADLINE)
static char **cli_completion(const char *text, int start, int end)
{
@@ -142,7 +169,7 @@ static char **cli_completion(const char *text, int start, int end)
int cli_init(struct nft_ctx *nft)
{
cli_nft = nft;
- rl_readline_name = "nft";
+ rl_readline_name = (char *)"nft";
rl_instream = stdin;
rl_outstream = stdout;
@@ -154,45 +181,95 @@ 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)
+
+int cli_init(struct nft_ctx *nft)
+{
+ char *line;
+
+ cli_nft = nft;
+ rl_readline_name = (char *)"nft";
+ rl_instream = stdin;
+ rl_outstream = stdout;
+
+ init_histfile();
+
+ read_history(histfile);
+ history_set_pos(history_length);
+
+ rl_set_prompt(CMDLINE_PROMPT);
+ while (!cli_quit) {
+ line = readline(rl_prompt);
+ if (!line) {
+ cli_exit(0);
+ break;
+ }
+ line = cli_append_multiline(line);
+ if (!line)
+ continue;
+
+ cli_complete(line);
+ }
+
+ return cli_rc;
+}
+
+void cli_exit(int rc)
+{
+ rl_deprep_terminal();
+ write_history(histfile);
+
+ __cli_exit(rc);
}
-#else /* !HAVE_LIBREADLINE */
+#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_LIBREADLINE */
+#endif /* HAVE_LINENOISE */
diff --git a/src/cmd.c b/src/cmd.c
index e0cf3e77..14cb1b51 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,36 +15,78 @@
#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, 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++;
+}
static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
struct table *table;
+ if (!cmd->handle.table.name)
+ return 0;
+
table = table_lookup_fuzzy(&cmd->handle, &ctx->nft->cache);
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;
}
+static int table_fuzzy_check(struct netlink_ctx *ctx, const struct cmd *cmd,
+ const struct table *table)
+{
+ if (table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name, cmd->handle.family))
+ return 0;
+
+ 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?",
+ strerror(ENOENT), table->handle.table.name,
+ family2str(table->handle.family));
+ return 1;
+ }
+
+ return 0;
+}
+
static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
- const struct table *table;
+ const struct table *table = NULL;
struct chain *chain;
+ if (!cmd->handle.chain.name)
+ return 0;
+
chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
+ /* check table first. */
+ if (!table)
+ return 0;
+
+ if (table_fuzzy_check(ctx, cmd, table))
+ return 1;
+
if (!chain)
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 chain '%s' in table %s '%s'?",
strerror(ENOENT), chain->handle.chain.name,
family2str(table->handle.family),
table->handle.table.name);
@@ -42,29 +94,29 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
}
static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
unsigned int flags = NFT_CACHE_TABLE |
NFT_CACHE_CHAIN;
- const struct table *table;
+ const struct table *table = NULL;
struct chain *chain;
- if (cache_update(ctx->nft, flags, ctx->msgs) < 0)
+ if (nft_cache_update(ctx->nft, flags, ctx->msgs, NULL) < 0)
return 0;
- table = table_lookup_fuzzy(&cmd->handle, &ctx->nft->cache);
- if (table && strcmp(cmd->handle.table.name, table->handle.table.name)) {
- netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?",
- strerror(ENOENT), table->handle.table.name,
- family2str(table->handle.family));
+ chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
+ /* check table first. */
+ if (!table)
+ return 0;
+
+ if (table_fuzzy_check(ctx, cmd, table))
return 1;
- } else if (!table) {
+
+ if (!chain)
return 0;
- }
- chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
- if (chain && strcmp(cmd->handle.chain.name, chain->handle.chain.name)) {
- netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ 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'?",
strerror(ENOENT),
chain->handle.chain.name,
family2str(table->handle.family),
@@ -76,16 +128,26 @@ static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
}
static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
- const struct table *table;
+ const struct table *table = NULL;
struct set *set;
+ if (!cmd->handle.set.name)
+ return 0;
+
set = set_lookup_fuzzy(cmd->handle.set.name, &ctx->nft->cache, &table);
+ /* check table first. */
+ if (!table)
+ return 0;
+
+ if (table_fuzzy_check(ctx, cmd, table))
+ return 1;
+
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,
@@ -95,16 +157,26 @@ static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
}
static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
- const struct table *table;
+ const struct table *table = NULL;
struct obj *obj;
+ if (!cmd->handle.obj.name)
+ return 0;
+
obj = obj_lookup_fuzzy(cmd->handle.obj.name, &ctx->nft->cache, &table);
+ /* check table first. */
+ if (!table)
+ return 0;
+
+ if (table_fuzzy_check(ctx, cmd, table))
+ return 1;
+
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);
@@ -112,17 +184,28 @@ static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
}
static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
- const struct cmd *cmd, struct location *loc)
+ const struct cmd *cmd,
+ const struct location *loc)
{
- const struct table *table;
+ const struct table *table = NULL;
struct flowtable *ft;
+ if (!cmd->handle.flowtable.name)
+ return 0;
+
ft = flowtable_lookup_fuzzy(cmd->handle.flowtable.name,
&ctx->nft->cache, &table);
+ /* check table first. */
+ if (!table)
+ return 0;
+
+ if (table_fuzzy_check(ctx, cmd, table))
+ return 1;
+
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);
@@ -130,7 +213,7 @@ static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
}
static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc, int err)
+ const struct location *loc, int err)
{
int ret = 0;
@@ -170,11 +253,38 @@ 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;
+ 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");
+
+ 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)
{
- struct location *loc = NULL;
- int i;
+ const struct location *loc = NULL;
+ uint32_t i;
for (i = 0; i < cmd->num_attrs; i++) {
if (!cmd->attr[i].offset)
@@ -192,6 +302,200 @@ 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);
+ }
+}
+
+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;
+ }
+}
+
+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 (elems->handle.family != cmd->handle.family ||
+ 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);
+ }
+ }
+}
diff --git a/src/ct.c b/src/ct.c
index 0842c838..67934648 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",
@@ -176,7 +176,7 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
{
const struct symbolic_constant *s;
const struct datatype *dtype;
- uint8_t data[CT_LABEL_BIT_SIZE];
+ uint8_t data[CT_LABEL_BIT_SIZE / BITS_PER_BYTE];
uint64_t bit;
mpz_t value;
@@ -211,16 +211,22 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
mpz_export_data(data, value, BYTEORDER_HOST_ENDIAN, sizeof(data));
*res = constant_expr_alloc(&sym->location, dtype,
- dtype->byteorder, sizeof(data),
- data);
+ dtype->byteorder, CT_LABEL_BIT_SIZE, data);
mpz_clear(value);
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,
@@ -272,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,
@@ -351,9 +357,11 @@ static void ct_expr_clone(struct expr *new, const struct expr *expr)
new->ct = expr->ct;
}
-static void ct_expr_pctx_update(struct proto_ctx *ctx, const struct expr *expr)
+static void ct_expr_pctx_update(struct proto_ctx *ctx,
+ const struct location *loc,
+ const struct expr *left,
+ const struct expr *right)
{
- const struct expr *left = expr->left, *right = expr->right;
const struct proto_desc *base = NULL, *desc;
uint32_t nhproto;
@@ -366,7 +374,7 @@ static void ct_expr_pctx_update(struct proto_ctx *ctx, const struct expr *expr)
if (!desc)
return;
- proto_ctx_update(ctx, left->ct.base + 1, &expr->location, desc);
+ proto_ctx_update(ctx, left->ct.base + 1, loc, desc);
}
#define NFTNL_UDATA_CT_KEY 0
@@ -569,7 +577,7 @@ 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 = {
@@ -577,6 +585,7 @@ static const struct stmt_ops flow_offload_stmt_ops = {
.name = "flow_offload",
.print = flow_offload_stmt_print,
.destroy = flow_offload_stmt_destroy,
+ .json = flow_offload_stmt_json,
};
struct stmt *flow_offload_stmt_alloc(const struct location *loc,
diff --git a/src/datatype.c b/src/datatype.c
index 7382307e..d398a9c8 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>
@@ -18,6 +18,8 @@
#include <linux/types.h>
#include <linux/netfilter.h>
#include <linux/icmpv6.h>
+#include <dirent.h>
+#include <sys/stat.h>
#include <nftables.h>
#include <datatype.h>
@@ -26,6 +28,8 @@
#include <erec.h>
#include <netlink.h>
#include <json.h>
+#include <misspell.h>
+#include "nftutils.h"
#include <netinet/ip_icmp.h>
@@ -60,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,
@@ -69,11 +74,13 @@ 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,
[TYPE_TIME_HOUR] = &hour_type,
[TYPE_TIME_DAY] = &day_type,
+ [TYPE_CGROUPV2] = &cgroupv2_type,
};
const struct datatype *datatype_lookup(enum datatypes type)
@@ -120,6 +127,7 @@ 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);
@@ -133,11 +141,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,
@@ -160,8 +211,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;
@@ -318,15 +377,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 = {
@@ -334,7 +411,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 = {
@@ -407,6 +484,22 @@ const struct datatype integer_type = {
.parse = integer_type_parse,
};
+static void xinteger_type_print(const struct expr *expr, struct output_ctx *octx)
+{
+ nft_gmp_print(octx, "0x%Zx", expr->value);
+}
+
+/* Alias of integer_type to print raw payload expressions in hexadecimal. */
+const struct datatype xinteger_type = {
+ .type = TYPE_INTEGER,
+ .name = "integer",
+ .desc = "integer",
+ .basetype = &integer_type,
+ .print = xinteger_type_print,
+ .json = integer_type_json,
+ .parse = integer_type_parse,
+};
+
static void string_type_print(const struct expr *expr, struct output_ctx *octx)
{
unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
@@ -509,27 +602,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;
- 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_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;
- if (ai->ai_next != NULL) {
+ 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");
+ }
+ 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;
}
@@ -568,27 +668,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;
+
+ 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));
- 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;
}
@@ -607,23 +715,36 @@ const struct datatype ip6addr_type = {
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;
}
}
integer_type_print(expr, octx);
}
+static void inet_protocol_type_describe(struct output_ctx *octx)
+{
+ uint8_t protonum;
+
+ for (protonum = 0; protonum < UINT8_MAX; protonum++) {
+ char name[NFT_PROTONAME_MAXSIZE];
+
+ if (!nft_getprotobynumber(protonum, name, sizeof(name)))
+ continue;
+
+ nft_print(octx, "\t%-30s\t%u\n", name, protonum);
+ }
+}
+
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;
@@ -636,11 +757,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,
@@ -658,22 +781,24 @@ const struct datatype inet_protocol_type = {
.print = inet_protocol_type_print,
.json = inet_protocol_type_json,
.parse = inet_protocol_type_parse,
+ .describe = inet_protocol_type_describe,
};
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;
}
@@ -703,7 +828,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);
}
@@ -727,19 +857,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;
@@ -749,12 +908,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;
}
@@ -771,6 +933,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;
}
@@ -780,13 +944,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)
@@ -806,10 +997,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,
@@ -817,9 +1015,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 = {
@@ -835,16 +1033,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 = {
@@ -858,16 +1057,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 = {
@@ -879,6 +1079,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",
@@ -886,12 +1140,18 @@ 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);
+ return;
+ }
days = ms / 86400000;
ms %= 86400000;
@@ -905,16 +1165,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 {
@@ -1029,6 +1302,7 @@ static struct error_record *time_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct error_record *erec;
+ uint32_t s32;
uint64_t s;
erec = time_parse(&sym->location, sym->identifier, &s);
@@ -1038,9 +1312,10 @@ static struct error_record *time_type_parse(struct parse_ctx *ctx,
if (s > UINT32_MAX)
return error(&sym->location, "value too large");
+ s32 = s;
*res = constant_expr_alloc(&sym->location, &time_type,
BYTEORDER_HOST_ENDIAN,
- sizeof(uint32_t) * BITS_PER_BYTE, &s);
+ sizeof(uint32_t) * BITS_PER_BYTE, &s32);
return NULL;
}
@@ -1049,7 +1324,7 @@ const struct datatype time_type = {
.name = "time",
.desc = "relative time",
.byteorder = BYTEORDER_HOST_ENDIAN,
- .size = 8 * BITS_PER_BYTE,
+ .size = 4 * BITS_PER_BYTE,
.basetype = &integer_type,
.print = time_type_print,
.json = time_type_json,
@@ -1064,17 +1339,18 @@ 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->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;
@@ -1087,24 +1363,31 @@ struct datatype *datatype_get(const struct datatype *ptr)
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));
}
-static 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->refcnt = 1;
return dtype;
}
@@ -1117,12 +1400,15 @@ void datatype_free(const struct datatype *ptr)
return;
if (!(dtype->flags & DTYPE_F_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)
@@ -1151,7 +1437,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;
@@ -1163,15 +1449,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;
@@ -1316,3 +1602,92 @@ const struct datatype policy_type = {
.desc = "policy type",
.parse = policy_type_parse,
};
+
+#define SYSFS_CGROUPSV2_PATH "/sys/fs/cgroup"
+
+static char *cgroupv2_get_path(const char *path, uint64_t id)
+{
+ char dent_name[PATH_MAX + 1];
+ char *cgroup_path = NULL;
+ struct dirent *dent;
+ struct stat st;
+ DIR *d;
+
+ d = opendir(path);
+ if (!d)
+ return NULL;
+
+ while ((dent = readdir(d)) != NULL) {
+ if (!strcmp(dent->d_name, ".") ||
+ !strcmp(dent->d_name, ".."))
+ continue;
+
+ snprintf(dent_name, sizeof(dent_name), "%s/%s",
+ path, dent->d_name);
+ dent_name[sizeof(dent_name) - 1] = '\0';
+
+ if (dent->d_ino == id) {
+ cgroup_path = xstrdup(dent_name);
+ break;
+ }
+
+ if (stat(dent_name, &st) >= 0 && S_ISDIR(st.st_mode)) {
+ cgroup_path = cgroupv2_get_path(dent_name, id);
+ if (cgroup_path)
+ break;
+ }
+ }
+ closedir(d);
+
+ return cgroup_path;
+}
+
+static void cgroupv2_type_print(const struct expr *expr,
+ struct output_ctx *octx)
+{
+ uint64_t id = mpz_get_uint64(expr->value);
+ char *cgroup_path;
+
+ cgroup_path = cgroupv2_get_path(SYSFS_CGROUPSV2_PATH, id);
+ if (cgroup_path)
+ nft_print(octx, "\"%s\"",
+ &cgroup_path[strlen(SYSFS_CGROUPSV2_PATH) + 1]);
+ else
+ nft_print(octx, "%" PRIu64, id);
+
+ free(cgroup_path);
+}
+
+static struct error_record *cgroupv2_type_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ char cgroupv2_path[PATH_MAX + 1];
+ struct stat st;
+ uint64_t ino;
+
+ snprintf(cgroupv2_path, sizeof(cgroupv2_path), "%s/%s",
+ SYSFS_CGROUPSV2_PATH, sym->identifier);
+ cgroupv2_path[sizeof(cgroupv2_path) - 1] = '\0';
+
+ if (stat(cgroupv2_path, &st) < 0)
+ return error(&sym->location, "cgroupv2 path fails: %s",
+ strerror(errno));
+
+ ino = st.st_ino;
+ *res = constant_expr_alloc(&sym->location, &cgroupv2_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(ino) * BITS_PER_BYTE, &ino);
+ return NULL;
+}
+
+const struct datatype cgroupv2_type = {
+ .type = TYPE_CGROUPV2,
+ .name = "cgroupsv2",
+ .desc = "cgroupsv2 path",
+ .byteorder = BYTEORDER_HOST_ENDIAN,
+ .size = 8 * BITS_PER_BYTE,
+ .basetype = &integer_type,
+ .print = cgroupv2_type_print,
+ .parse = cgroupv2_type_parse,
+};
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 c550a596..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>
@@ -38,14 +36,15 @@ void erec_add_location(struct error_record *erec, const struct location *loc)
{
assert(erec->num_locations < EREC_LOCATIONS_MAX);
erec->locations[erec->num_locations] = *loc;
- erec->locations[erec->num_locations].indesc = loc->indesc;
+ erec->locations[erec->num_locations].indesc = loc->indesc ?
+ : &internal_indesc;
erec->num_locations++;
}
void erec_destroy(struct error_record *erec)
{
- xfree(erec->msg);
- xfree(erec);
+ free(erec->msg);
+ free(erec);
}
__attribute__((format(printf, 3, 0)))
@@ -80,11 +79,58 @@ struct error_record *erec_create(enum error_record_types type,
return erec;
}
+void print_location(FILE *f, const struct input_descriptor *indesc,
+ const struct location *loc)
+{
+ const struct input_descriptor *tmp;
+ const struct location *iloc;
+
+ if (indesc->location.indesc != NULL) {
+ const char *prefix = "In file included from";
+ iloc = &indesc->location;
+ for (tmp = iloc->indesc;
+ tmp != NULL && tmp->type != INDESC_INTERNAL;
+ tmp = iloc->indesc) {
+ fprintf(f, "%s %s:%u:%u-%u:\n", prefix,
+ tmp->name,
+ iloc->first_line, iloc->first_column,
+ iloc->last_column);
+ prefix = " from";
+ iloc = &tmp->location;
+ }
+ }
+ if (indesc->type != INDESC_BUFFER && indesc->name) {
+ fprintf(f, "%s:%u:%u-%u: ", indesc->name,
+ loc->first_line, loc->first_column,
+ loc->last_column);
+ }
+}
+
+const char *line_location(const struct input_descriptor *indesc,
+ const struct location *loc, char *buf, size_t bufsiz)
+{
+ const char *line = NULL;
+ FILE *f;
+
+ f = fopen(indesc->name, "r");
+ if (!f)
+ return NULL;
+
+ if (!fseek(f, loc->line_offset, SEEK_SET) &&
+ fread(buf, 1, bufsiz - 1, f) > 0) {
+ *strchrnul(buf, '\n') = '\0';
+ line = buf;
+ }
+ fclose(f);
+
+ return line;
+}
+
void erec_print(struct output_ctx *octx, const struct error_record *erec,
unsigned int debug_mask)
{
- const struct location *loc = erec->locations, *iloc;
- const struct input_descriptor *indesc = loc->indesc, *tmp;
+ const struct location *loc = erec->locations;
+ const struct input_descriptor *indesc = loc->indesc;
const char *line = NULL;
char buf[1024] = {};
char *pbuf = NULL;
@@ -98,17 +144,13 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
line = indesc->data;
*strchrnul(line, '\n') = '\0';
break;
+ case INDESC_STDIN:
+ line = indesc->data;
+ line += loc->line_offset;
+ *strchrnul(line, '\n') = '\0';
+ break;
case INDESC_FILE:
- f = fopen(indesc->name, "r");
- if (!f)
- break;
-
- if (!fseek(f, loc->line_offset, SEEK_SET) &&
- fread(buf, 1, sizeof(buf) - 1, f) > 0) {
- *strchrnul(buf, '\n') = '\0';
- line = buf;
- }
- fclose(f);
+ line = line_location(indesc, loc, buf, sizeof(buf));
break;
case INDESC_INTERNAL:
case INDESC_NETLINK:
@@ -126,29 +168,15 @@ 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;
}
- if (indesc->location.indesc != NULL) {
- const char *prefix = "In file included from";
- iloc = &indesc->location;
- for (tmp = iloc->indesc;
- tmp != NULL && tmp->type != INDESC_INTERNAL;
- tmp = iloc->indesc) {
- fprintf(f, "%s %s:%u:%u-%u:\n", prefix,
- tmp->name,
- iloc->first_line, iloc->first_column,
- iloc->last_column);
- prefix = " from";
- iloc = &tmp->location;
- }
- }
- if (indesc->name != NULL)
- fprintf(f, "%s:%u:%u-%u: ", indesc->name,
- loc->first_line, loc->first_column,
- loc->last_column);
+ print_location(f, indesc, loc);
+
if (error_record_names[erec->type])
fprintf(f, "%s: ", error_record_names[erec->type]);
fprintf(f, "%s\n", erec->msg);
@@ -175,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 b64ed3c0..1682ba58 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>
@@ -29,6 +28,7 @@
#include <expression.h>
#include <statement.h>
+#include <intervals.h>
#include <netlink.h>
#include <time.h>
#include <rule.h>
@@ -38,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[] = {
@@ -67,6 +74,33 @@ static int __fmtstring(3, 4) set_error(struct eval_ctx *ctx,
return -1;
}
+static const char *stmt_name(const struct stmt *stmt)
+{
+ switch (stmt->ops->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->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;
@@ -74,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);
@@ -82,7 +116,8 @@ 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;
@@ -92,17 +127,25 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
key_fix_dtype_byteorder(key);
set = set_alloc(&expr->location);
- set->flags = NFT_SET_ANONYMOUS | expr->set_flags;
+ set->flags = expr->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;
+ handle_merge(&set->handle, &ctx->cmd->handle);
+
+ if (set_evaluate(ctx, set) < 0) {
+ if (set->flags & NFT_SET_MAP)
+ 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;
@@ -111,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);
}
@@ -138,6 +179,7 @@ static enum ops byteorder_conversion_op(struct expr *expr,
static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
enum byteorder byteorder)
{
+ enum datatypes basetype;
enum ops op;
assert(!expr_is_constant(*expr) || expr_is_singleton(*expr));
@@ -146,16 +188,54 @@ 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;
+ }
- if (expr_basetype(*expr)->type != TYPE_INTEGER)
+ basetype = expr_basetype(*expr)->type;
+ switch (basetype) {
+ case TYPE_INTEGER:
+ break;
+ case TYPE_STRING:
+ return 0;
+ default:
return expr_error(ctx->msgs, *expr,
- "Byteorder mismatch: expected %s, got %s",
+ "Byteorder mismatch: %s expected %s, %s got %s",
byteorder_names[byteorder],
+ expr_name(*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);
@@ -166,20 +246,6 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
return 0;
}
-static struct table *table_lookup_global(struct eval_ctx *ctx)
-{
- struct table *table;
-
- if (ctx->table != NULL)
- return ctx->table;
-
- table = table_lookup(&ctx->cmd->handle, &ctx->nft->cache);
- if (table == NULL)
- return NULL;
-
- return table;
-}
-
static int table_not_found(struct eval_ctx *ctx)
{
struct table *table;
@@ -190,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));
}
@@ -206,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);
@@ -223,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,11 +304,11 @@ static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc,
struct flowtable *ft;
ft = flowtable_lookup_fuzzy(ft_name, &ctx->nft->cache, &table);
- if (ft == NULL)
+ if (!ft)
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);
@@ -253,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;
@@ -269,12 +338,14 @@ static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
}
break;
case SYMBOL_SET:
- table = table_lookup_global(ctx);
+ 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);
- set = set_lookup(table, (*expr)->identifier);
- if (set == NULL)
+ set = set_cache_find(table, (*expr)->identifier);
+ if (set == NULL || !set->key)
return set_not_found(ctx, &(*expr)->location,
(*expr)->identifier);
@@ -324,8 +395,11 @@ static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp)
return 0;
}
- if (datalen >= 1 &&
- data[datalen - 1] == '\\') {
+ if (datalen == 0)
+ return expr_error(ctx->msgs, expr,
+ "All-wildcard strings are not supported");
+
+ if (data[datalen - 1] == '\\') {
char unescaped_str[data_len];
memset(unescaped_str, 0, sizeof(unescaped_str));
@@ -338,15 +412,18 @@ static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp)
*exprp = value;
return 0;
}
+
+ data[datalen] = 0;
value = constant_expr_alloc(&expr->location, ctx->ectx.dtype,
BYTEORDER_HOST_ENDIAN,
- datalen * BITS_PER_BYTE, data);
+ expr->len, data);
prefix = prefix_expr_alloc(&expr->location, value,
datalen * BITS_PER_BYTE);
datatype_set(prefix, ctx->ectx.dtype);
prefix->flags |= EXPR_F_CONSTANT;
prefix->byteorder = BYTEORDER_HOST_ENDIAN;
+ prefix->len = expr->len;
expr_free(expr);
*exprp = prefix;
@@ -357,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 &&
@@ -365,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;
}
@@ -414,20 +497,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);
@@ -437,10 +534,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;
}
@@ -461,10 +561,11 @@ 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;
@@ -474,7 +575,7 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
&extra_len);
break;
case EXPR_EXTHDR:
- shift = expr_offset_shift(expr, expr->exthdr.tmpl->offset,
+ shift = expr_offset_shift(expr, expr->exthdr.offset,
&extra_len);
break;
default:
@@ -482,7 +583,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);
@@ -499,6 +603,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,
@@ -506,7 +620,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;
@@ -515,10 +629,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)
@@ -527,18 +644,22 @@ static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
if (expr_evaluate_primary(ctx, exprp) < 0)
return -1;
- if (expr->exthdr.tmpl->offset % BITS_PER_BYTE != 0 ||
- expr->len % BITS_PER_BYTE != 0)
- expr_evaluate_bits(ctx, exprp);
+ ctx->ectx.key = key;
+
+ if (expr->exthdr.offset % BITS_PER_BYTE != 0 ||
+ 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: {
static const unsigned int max_tcpoptlen = (15 * 4 - 20) * BITS_PER_BYTE;
- unsigned int totlen = 0;
+ unsigned int totlen;
- totlen += expr->exthdr.tmpl->offset;
- totlen += expr->exthdr.tmpl->len;
- totlen += expr->exthdr.offset;
+ totlen = expr->exthdr.tmpl->len + expr->exthdr.offset;
if (totlen > max_tcpoptlen)
return expr_error(ctx->msgs, expr,
@@ -548,11 +669,9 @@ static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
}
case NFT_EXTHDR_OP_IPV4: {
static const unsigned int max_ipoptlen = 40 * BITS_PER_BYTE;
- unsigned int totlen = 0;
+ unsigned int totlen;
- totlen += expr->exthdr.tmpl->offset;
- totlen += expr->exthdr.tmpl->len;
- totlen += expr->exthdr.offset;
+ totlen = expr->exthdr.offset + expr->exthdr.tmpl->len;
if (totlen > max_ipoptlen)
return expr_error(ctx->msgs, expr,
@@ -577,13 +696,14 @@ 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:
- dependency = &proto_tcp;
- pb = PROTO_BASE_TRANSPORT_HDR;
- break;
+ case NFT_EXTHDR_OP_SCTP:
+ case NFT_EXTHDR_OP_DCCP:
+ return __expr_evaluate_exthdr(ctx, exprp);
case NFT_EXTHDR_OP_IPV4:
dependency = &proto_ip;
break;
@@ -595,7 +715,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);
@@ -613,7 +734,7 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
/* dependency supersede.
*
- * 'inet' is a 'phony' l2 dependency used by NFPROTO_INET to fulfill network
+ * 'inet' is a 'phony' l2 dependency used by NFPROTO_INET to fulfil network
* header dependency, i.e. ensure that 'ip saddr 1.2.3.4' only sees ip headers.
*
* If a match expression that depends on a particular L2 header, e.g. ethernet,
@@ -625,7 +746,7 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
* Example: inet filter in ip saddr 1.2.3.4 ether saddr a:b:c:d:e:f
*
* ip saddr adds meta dependency on ipv4 packets
- * ether saddr adds another dependeny on ethernet frames.
+ * ether saddr adds another dependency on ethernet frames.
*/
static int meta_iiftype_gen_dependency(struct eval_ctx *ctx,
struct expr *payload, struct stmt **res)
@@ -639,9 +760,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;
@@ -652,34 +775,53 @@ 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 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 &&
- proto_is_dummy(desc)) {
- err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
- if (err < 0)
- return err;
+ assert(base == PROTO_BASE_LL_HDR);
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ 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;
+
+ desc = payload->payload.desc;
+ 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 < 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)
+ if (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;
- payload->payload.offset += ctx->pctx.protocol[base].offset;
+ 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);
return 0;
@@ -695,45 +837,102 @@ 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);
- } else {
- /* No conflict: Same payload protocol as context, adjust offset
- * if needed.
- */
- if (desc == payload->payload.desc) {
- payload->payload.offset +=
- ctx->pctx.protocol[base].offset;
- 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 (err <= 0)
- return err;
- desc = ctx->pctx.protocol[base].desc;
- if (desc == payload->payload.desc)
- return 0;
+ 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;
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ return 1;
}
+ goto check_icmp;
+ }
+
+ if (payload->payload.base == desc->base &&
+ proto_ctx_is_ambiguous(pctx, base)) {
+ desc = proto_ctx_find_conflict(pctx, base, payload->payload.desc);
+ assert(desc);
+
return expr_error(ctx->msgs, payload,
"conflicting protocols specified: %s vs. %s",
- ctx->pctx.protocol[base].desc->name,
+ desc->name,
payload->payload.desc->name);
}
- return 0;
+
+ /* No conflict: Same payload protocol as context, adjust offset
+ * if needed.
+ */
+ if (desc == payload->payload.desc) {
+ const struct proto_hdr_template *tmpl;
+
+ if (desc->base == PROTO_BASE_LL_HDR) {
+ unsigned int i;
+
+ 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)
+ return 0;
+
+ tmpl = expr->payload.tmpl;
+
+ if (!tmpl || !tmpl->icmp_dep)
+ return 0;
+
+ if (payload_gen_icmp_dependency(ctx, expr, &nstmt) < 0)
+ return -1;
+
+ if (nstmt)
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ return 0;
+ }
+ /* If we already have context and this payload is on the same
+ * base, try to resolve the protocol conflict.
+ */
+ if (base == PROTO_BASE_LL_HDR) {
+ err = resolve_ll_protocol_conflict(ctx, desc, payload);
+ if (err <= 0)
+ return err;
+
+ desc = pctx->protocol[base].desc;
+ if (desc == payload->payload.desc)
+ return 0;
+ }
+ return expr_error(ctx->msgs, payload,
+ "conflicting %s protocols specified: %s vs. %s",
+ proto_base_names[base],
+ pctx->protocol[base].desc->name,
+ payload->payload.desc->name);
}
static bool payload_needs_adjustment(const struct expr *expr)
@@ -744,6 +943,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)
@@ -755,14 +955,81 @@ 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;
+
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ 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.
*/
@@ -770,20 +1037,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:
@@ -798,8 +1067,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:
@@ -809,7 +1080,7 @@ 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)
@@ -831,8 +1102,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;
@@ -845,7 +1116,7 @@ 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);
@@ -861,8 +1132,10 @@ 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:
@@ -887,13 +1160,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);
}
@@ -940,7 +1213,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;
}
@@ -951,7 +1223,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;
@@ -1002,9 +1274,8 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
left = range->left;
right = range->right;
- if (mpz_cmp(left->value, right->value) >= 0)
- return expr_error(ctx->msgs, range,
- "Range has zero or negative size");
+ if (mpz_cmp(left->value, right->value) > 0)
+ return expr_error(ctx->msgs, range, "Range negative size");
datatype_set(range, left->dtype);
range->flags |= EXPR_F_CONSTANT;
@@ -1017,12 +1288,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);
@@ -1041,7 +1310,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;
+ unary->dtype = datatype_clone(arg->dtype);
unary->byteorder = byteorder;
unary->len = arg->len;
return 0;
@@ -1108,14 +1377,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);
+
+ 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 (mpz_get_uint32(right->value) >= 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)
@@ -1124,9 +1403,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);
@@ -1136,13 +1415,32 @@ 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;
+
+ /* Both sides need to be in host byte order */
+ if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
+ return -1;
- if (byteorder_conversion(ctx, &op->right, left->byteorder) < 0)
+ 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))
return constant_binop_simplify(ctx, expr);
@@ -1159,14 +1457,27 @@ 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 >= USHRT_MAX)
+ return expr_binary_error(ctx->msgs, op, NULL,
+ "Binary operation limit %u reached ",
+ ctx->recursion);
+ ctx->recursion++;
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;
@@ -1199,20 +1510,51 @@ static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr)
"for %s expressions",
sym, expr_name(right));
- /* The grammar guarantees this */
- assert(expr_basetype(left) == expr_basetype(right));
+ if (!datatype_equal(expr_basetype(left), expr_basetype(right)))
+ return expr_binary_error(ctx->msgs, left, op,
+ "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);
+ ret = expr_evaluate_shift(ctx, expr);
+ break;
case OP_AND:
case OP_XOR:
case OP_OR:
- return expr_evaluate_bitwise(ctx, expr);
+ ret = expr_evaluate_bitwise(ctx, expr);
+ break;
default:
BUG("invalid binary operation %u\n", op->op);
}
+
+
+ if (ctx->recursion == 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 == 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)
@@ -1227,17 +1569,39 @@ static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr)
return err;
}
-static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr,
- bool eval)
+static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
{
const struct datatype *dtype = ctx->ectx.dtype, *tmp;
uint32_t type = dtype ? dtype->type : 0, ntype = 0;
int off = dtype ? dtype->subtypes : 0;
unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
- struct expr *i, *next;
+ 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) {
- unsigned dsize_bytes;
+ 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))
+ return expr_error(ctx->msgs, i,
+ "specify either ip or ip6 for address matching");
if (expr_is_constant(*expr) && dtype && off == 0)
return expr_binary_error(ctx->msgs, i, *expr,
@@ -1245,32 +1609,77 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr,
"expecting %s",
dtype->desc);
- if (dtype == NULL)
+ if (key) {
+ tmp = key->dtype;
+ dsize = key->len;
+ bo = key->byteorder;
+ off--;
+ } else if (dtype == NULL || off == 0) {
tmp = datatype_lookup(TYPE_INVALID);
- else
+ } else {
tmp = concat_subtype_lookup(type, --off);
- expr_set_context(&ctx->ectx, tmp, tmp->size);
+ dsize = tmp->size;
+ bo = tmp->byteorder;
+ }
- if (eval && list_member_evaluate(ctx, &i) < 0)
+ __expr_set_context(&ctx->ectx, tmp, bo, dsize, 0);
+ ctx->ectx.key = i;
+
+ if (list_member_evaluate(ctx, &i) < 0)
return -1;
+
+ if (i->etype == EXPR_SET)
+ 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 = datatype_clone(i->dtype);
+ clone->size = i->len;
+ clone->byteorder = i->byteorder;
+ __datatype_set(i, clone);
+ }
+
if (dtype == NULL && i->dtype->size == 0)
return expr_binary_error(ctx->msgs, i, *expr,
"can not use variable sized "
"data types (%s) in concat "
"expressions",
i->dtype->name);
+ if (dsize == 0) /* reload after evaluation or clone above */
+ dsize = i->dtype->size;
ntype = concat_subtype_add(ntype, i->dtype->type);
- dsize_bytes = div_round_up(i->dtype->size, BITS_PER_BYTE);
+ dsize_bytes = div_round_up(dsize, BITS_PER_BYTE);
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
+ size += netlink_padded_len(dsize);
+ 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));
- (*expr)->len = (*expr)->dtype->size;
+ __datatype_set(*expr, concat_type_alloc(ntype));
+ (*expr)->len = size;
if (off > 0)
return expr_error(ctx->msgs, *expr,
@@ -1279,6 +1688,10 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr,
dtype->desc, (*expr)->dtype->desc);
expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
+ if (!key_ctx)
+ ctx->ectx.key = *expr;
+ else
+ ctx->ectx.key = key_ctx;
return 0;
}
@@ -1290,16 +1703,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);
}
@@ -1313,27 +1732,68 @@ static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
-static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
+static int __expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr *elem)
{
+ int num_elem_exprs = 0, num_set_exprs = 0;
struct set *set = ctx->set;
- struct expr *elem = *expr;
+ struct stmt *stmt;
+
+ list_for_each_entry(stmt, &elem->stmt_list, list)
+ num_elem_exprs++;
+ list_for_each_entry(stmt, &set->stmt_list, list)
+ num_set_exprs++;
+
+ if (num_elem_exprs > 0) {
+ struct stmt *set_stmt, *elem_stmt;
+
+ if (num_set_exprs > 0 && num_elem_exprs != num_set_exprs) {
+ return expr_error(ctx->msgs, elem,
+ "number of statements mismatch, set expects %d "
+ "but element has %d", num_set_exprs,
+ num_elem_exprs);
+ } else if (num_set_exprs == 0) {
+ 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",
+ set_is_map(set->flags) ? "map" : "set");
+ }
+ return 0;
+ }
- if (elem->stmt) {
- if (set->stmt && set->stmt->ops != elem->stmt->ops) {
- return stmt_error(ctx, elem->stmt,
- "statement mismatch, element expects %s, "
- "but %s has type %s",
- elem->stmt->ops->name,
- set_is_map(set->flags) ? "map" : "set",
- set->stmt->ops->name);
- } else if (!set->stmt && !(set->flags & NFT_SET_EVAL)) {
- return stmt_error(ctx, elem->stmt,
- "missing %s statement in %s definition",
- elem->stmt->ops->name,
- set_is_map(set->flags) ? "map" : "set");
+ 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) {
+ return stmt_error(ctx, elem_stmt,
+ "statement mismatch, element expects %s, "
+ "but %s has type %s",
+ elem_stmt->ops->name,
+ set_is_map(set->flags) ? "map" : "set",
+ set_stmt->ops->name);
+ }
+ set_stmt = list_next_entry(set_stmt, list);
}
}
+ return 0;
+}
+
+static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
+{
+ struct expr *elem = *expr;
+ const struct expr *key;
+
+ if (ctx->set) {
+ if (__expr_evaluate_set_elem(ctx, elem) < 0)
+ return -1;
+
+ key = ctx->set->key;
+ __expr_set_context(&ctx->ectx, key->dtype, key->byteorder, key->len, 0);
+ ctx->ectx.key = key;
+ }
+
if (expr_evaluate(ctx, &elem->key) < 0)
return -1;
@@ -1342,9 +1802,19 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
switch (elem->key->etype) {
case EXPR_PREFIX:
case EXPR_RANGE:
- return expr_error(ctx->msgs, elem,
- "You must add 'flags interval' to your %s declaration if you want to add %s elements",
- set_is_map(ctx->set->flags) ? "map" : "set", expr_name(elem->key));
+ key = elem->key;
+ goto err_missing_flag;
+ case EXPR_CONCAT:
+ list_for_each_entry(key, &elem->key->expressions, list) {
+ switch (key->etype) {
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ goto err_missing_flag;
+ default:
+ break;
+ }
+ }
+ break;
default:
break;
}
@@ -1353,30 +1823,109 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
datatype_set(elem, elem->key->dtype);
elem->len = elem->key->len;
elem->flags = elem->key->flags;
+
return 0;
+
+err_missing_flag:
+ return expr_error(ctx->msgs, key,
+ "You must add 'flags interval' to your %s declaration if you want to add %s elements",
+ set_is_map(ctx->set->flags) ? "map" : "set", expr_name(key));
+}
+
+static const struct expr *expr_set_elem(const struct expr *expr)
+{
+ if (expr->etype == EXPR_MAPPING)
+ return expr->left;
+
+ return expr;
+}
+
+static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
+ struct expr *init)
+{
+ int ret;
+
+ if (!init)
+ return 0;
+
+ ret = 0;
+ 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);
+ } else {
+ ret = set_overlap(ctx->msgs, set, init);
+ }
+ break;
+ case CMD_DELETE:
+ case CMD_DESTROY:
+ ret = set_delete(ctx->msgs, ctx->cmd, set, init,
+ ctx->nft->debug_mask);
+ break;
+ case CMD_GET:
+ break;
+ default:
+ BUG("unhandled op %d\n", ctx->cmd->op);
+ break;
+ }
+
+ 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;
+ const struct expr *elem;
list_for_each_entry_safe(i, next, &set->expressions, list) {
if (list_member_evaluate(ctx, &i) < 0)
return -1;
- if (i->etype == EXPR_SET_ELEM &&
- i->key->etype == EXPR_SET_REF)
+ if (i->etype == EXPR_MAPPING &&
+ i->left->etype == EXPR_SET_ELEM &&
+ i->left->key->etype == EXPR_SET) {
+ struct expr *new, *j;
+
+ list_for_each_entry(j, &i->left->key->expressions, list) {
+ new = mapping_expr_alloc(&i->location,
+ expr_get(j),
+ expr_get(i->right));
+ list_add_tail(&new->list, &set->expressions);
+ set->size++;
+ }
+ list_del(&i->list);
+ expr_free(i);
+ continue;
+ }
+
+ elem = expr_set_elem(i);
+
+ if (elem->etype == EXPR_SET_ELEM &&
+ elem->key->etype == EXPR_SET_REF)
return expr_error(ctx->msgs, i,
"Set reference cannot be part of another set");
- if (i->etype == EXPR_SET_ELEM &&
- i->key->etype == EXPR_SET) {
- struct expr *new = expr_clone(i->key);
+ if (elem->etype == EXPR_SET_ELEM &&
+ elem->key->etype == EXPR_SET) {
+ struct expr *new = expr_get(elem->key);
- set->set_flags |= i->key->set_flags;
+ set->set_flags |= elem->key->set_flags;
list_replace(&i->list, &new->list);
expr_free(i);
i = new;
+ elem = expr_set_elem(i);
}
if (!expr_is_constant(i))
@@ -1392,30 +1941,114 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
expr_free(i);
} else if (!expr_is_singleton(i)) {
set->set_flags |= NFT_SET_INTERVAL;
- if (i->key->etype == EXPR_CONCAT)
+ if (elem->key->etype == EXPR_CONCAT)
set->set_flags |= NFT_SET_CONCAT;
}
}
- if (ctx->set && (ctx->set->flags & NFT_SET_CONCAT))
- set->set_flags |= NFT_SET_CONCAT;
+ if (ctx->set) {
+ if (ctx->set->flags & NFT_SET_CONCAT)
+ set->set_flags |= NFT_SET_CONCAT;
+ }
set->set_flags |= NFT_SET_CONSTANT;
datatype_set(set, ctx->ectx.dtype);
set->len = ctx->ectx.len;
set->flags |= EXPR_F_CONSTANT;
+
return 0;
}
static int binop_transfer(struct eval_ctx *ctx, struct expr **expr);
+
+static void map_set_concat_info(struct expr *map)
+{
+ map->mappings->set->flags |= map->mappings->set->init->set_flags;
+
+ if (map->mappings->set->flags & NFT_SET_INTERVAL &&
+ map->map->etype == EXPR_CONCAT) {
+ memcpy(&map->mappings->set->desc.field_len, &map->map->field_len,
+ sizeof(map->mappings->set->desc.field_len));
+ map->mappings->set->desc.field_count = map->map->field_count;
+ map->mappings->flags |= NFT_SET_CONCAT;
+ }
+}
+
+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;
struct expr *key, *data;
+ if (map->map->etype == EXPR_CT &&
+ (map->map->ct.key == NFT_CT_SRC ||
+ map->map->ct.key == NFT_CT_DST))
+ return expr_error(ctx->msgs, map->map,
+ "specify either ip or ip6 for address matching");
+ else if (map->map->etype == EXPR_CONCAT) {
+ struct expr *i;
+
+ list_for_each_entry(i, &map->map->expressions, list) {
+ if (i->etype == EXPR_CT &&
+ (i->ct.key == NFT_CT_SRC ||
+ i->ct.key == NFT_CT_DST))
+ return expr_error(ctx->msgs, i,
+ "specify either ip or ip6 for address matching");
+ }
+ }
+
expr_set_context(&ctx->ectx, NULL, 0);
if (expr_evaluate(ctx, &map->map) < 0)
return -1;
@@ -1423,23 +2056,44 @@ 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_SET:
- key = constant_expr_alloc(&map->location,
- ctx->ectx.dtype,
- ctx->ectx.byteorder,
- ctx->ectx.len, NULL);
+ if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
+ key = expr_clone(ctx->ectx.key);
+ } else {
+ key = constant_expr_alloc(&map->location,
+ ctx->ectx.dtype,
+ ctx->ectx.byteorder,
+ ctx->ectx.len, NULL);
+ }
+
+ 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 {
+ const struct datatype *dtype;
- dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
- data = constant_expr_alloc(&netlink_location, dtype,
- dtype->byteorder, ectx.len, NULL);
+ 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);
+ if (!mappings)
+ return -1;
if (ectx.len && mappings->set->data->len != ectx.len)
BUG("%d vs %d\n", mappings->set->data->len, ectx.len);
@@ -1449,17 +2103,28 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->set = mappings->set;
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+
+ 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)
+ return -1;
+
expr_set_context(&ctx->ectx, ctx->set->key->dtype, ctx->set->key->len);
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;
- map->mappings->set->flags |= map->mappings->set->init->set_flags;
+
+ map_set_concat_info(map);
break;
case EXPR_SYMBOL:
if (expr_evaluate(ctx, &map->mappings) < 0)
@@ -1469,12 +2134,18 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
return expr_error(ctx->msgs, map->mappings,
"Expression is not a map");
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",
@@ -1492,6 +2163,26 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+static bool data_mapping_has_interval(struct expr *data)
+{
+ struct expr *i;
+
+ if (data->etype == EXPR_RANGE ||
+ data->etype == EXPR_PREFIX)
+ return true;
+
+ if (data->etype != EXPR_CONCAT)
+ return false;
+
+ list_for_each_entry(i, &data->expressions, list) {
+ if (i->etype == EXPR_RANGE ||
+ i->etype == EXPR_PREFIX)
+ return true;
+ }
+
+ return false;
+}
+
static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *mapping = *expr;
@@ -1512,17 +2203,14 @@ 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;
-
- expr_set_context(&ctx->ectx, set->data->dtype, datalen);
- } else {
- assert((set->flags & NFT_SET_MAP) == 0);
- }
+ assert(set->data != NULL);
+ 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;
@@ -1531,15 +2219,23 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
"Value must be a constant");
if (set_is_anonymous(set->flags) &&
- (mapping->right->etype == EXPR_RANGE ||
- mapping->right->etype == EXPR_PREFIX))
+ 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;
}
@@ -1565,17 +2261,20 @@ static void expr_dtype_integer_compatible(struct eval_ctx *ctx,
static int expr_evaluate_numgen(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp;
+ unsigned int maxval;
expr_dtype_integer_compatible(ctx, expr);
+ maxval = expr->numgen.mod + expr->numgen.offset - 1;
__expr_set_context(&ctx->ectx, expr->dtype, expr->byteorder, expr->len,
- expr->numgen.mod - 1);
+ maxval);
return 0;
}
static int expr_evaluate_hash(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp;
+ unsigned int maxval;
expr_dtype_integer_compatible(ctx, expr);
@@ -1588,8 +2287,9 @@ static int expr_evaluate_hash(struct eval_ctx *ctx, struct expr **exprp)
* expression to be hashed. Since this input is transformed to a 4 bytes
* integer, restore context to the datatype that results from hashing.
*/
+ maxval = expr->hash.mod + expr->hash.offset - 1;
__expr_set_context(&ctx->ectx, expr->dtype, expr->byteorder, expr->len,
- expr->hash.mod - 1);
+ maxval);
return 0;
}
@@ -1658,8 +2358,6 @@ static int binop_transfer_one(struct eval_ctx *ctx,
return 0;
}
- expr_get(*right);
-
switch (left->op) {
case OP_LSHIFT:
(*right) = binop_expr_alloc(&(*right)->location, OP_RSHIFT,
@@ -1772,7 +2470,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;
@@ -1781,7 +2479,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;
@@ -1798,16 +2496,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)
@@ -1825,10 +2561,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;
}
}
@@ -1868,6 +2604,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:
@@ -1875,11 +2624,22 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
* Update protocol context for payload and meta iiftype
* equality expressions.
*/
- if (expr_is_singleton(right))
- relational_expr_pctx_update(&ctx->pctx, rel);
+ relational_expr_pctx_update(pctx, rel);
/* fall through */
case OP_NEQ:
+ case OP_NEG:
+ if (rel->op == OP_NEG) {
+ if (left->etype == EXPR_BINOP)
+ return expr_binary_error(ctx->msgs, left, right,
+ "cannot combine negation with binary expression");
+ if (right->etype != EXPR_VALUE ||
+ 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. Did you mean \"!=\"?");
+ }
+
switch (right->etype) {
case EXPR_RANGE:
if (byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0)
@@ -1904,7 +2664,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,
+ NFT_SET_ANONYMOUS);
+ if (!right)
+ return -1;
+
/* fall through */
case EXPR_SET_REF:
if (rel->left->etype == EXPR_CT &&
@@ -1976,11 +2740,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");
@@ -1998,9 +2763,11 @@ static int expr_evaluate_meta(struct eval_ctx *ctx, struct expr **exprp)
static int expr_evaluate_socket(struct eval_ctx *ctx, struct expr **expr)
{
+ enum nft_socket_keys key = (*expr)->socket.key;
int maxval = 0;
- if((*expr)->socket.key == NFT_SOCKET_TRANSPARENT)
+ if (key == NFT_SOCKET_TRANSPARENT ||
+ key == NFT_SOCKET_WILDCARD)
maxval = 1;
__expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->byteorder,
(*expr)->len, maxval);
@@ -2021,7 +2788,16 @@ static int expr_evaluate_osf(struct eval_ctx *ctx, struct expr **expr)
static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
{
- struct expr *new = expr_clone((*exprp)->sym->expr);
+ struct symbol *sym = (*exprp)->sym;
+ struct expr *new;
+
+ /* If variable is reused from different locations in the ruleset, then
+ * clone expression.
+ */
+ if (sym->refcnt > 2)
+ new = expr_clone(sym->expr);
+ else
+ new = expr_get(sym->expr);
if (expr_evaluate(ctx, &new) < 0) {
expr_free(new);
@@ -2036,9 +2812,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:
@@ -2051,6 +2828,54 @@ 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)
+{
+ struct expr *expr = *exprp, *binop, *rel;
+
+ if (expr->op != OP_EQ &&
+ expr->op != OP_NEQ)
+ return expr_error(ctx->msgs, expr, "either == or != is allowed");
+
+ 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;
+
+ return expr_evaluate(ctx, exprp);
+}
+
+static int verdict_validate_chainlen(struct eval_ctx *ctx,
+ struct expr *chain)
+{
+ if (chain->len > NFT_CHAIN_MAXNAMELEN * BITS_PER_BYTE)
+ return expr_error(ctx->msgs, chain,
+ "chain name too long (%u, max %u)",
+ chain->len / BITS_PER_BYTE,
+ NFT_CHAIN_MAXNAMELEN);
+
+ return 0;
+}
+
+static int expr_evaluate_verdict(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct expr *expr = *exprp;
+
+ switch (expr->verdict) {
+ case NFT_GOTO:
+ case NFT_JUMP:
+ if (expr->chain->etype == EXPR_VALUE &&
+ verdict_validate_chainlen(ctx, expr->chain))
+ return -1;
+
+ break;
+ }
+
+ return expr_evaluate_primary(ctx, exprp);
+}
+
static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
{
if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) {
@@ -2069,13 +2894,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:
@@ -2085,7 +2911,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:
@@ -2099,7 +2925,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_BINOP:
return expr_evaluate_binop(ctx, expr);
case EXPR_CONCAT:
- return expr_evaluate_concat(ctx, expr, true);
+ return expr_evaluate_concat(ctx, expr);
case EXPR_LIST:
return expr_evaluate_list(ctx, expr);
case EXPR_SET:
@@ -2118,6 +2944,10 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
return expr_evaluate_hash(ctx, 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);
default:
BUG("unknown expression type %s\n", expr_name(*expr));
}
@@ -2188,13 +3018,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:
@@ -2209,6 +3044,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;
}
@@ -2227,6 +3066,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)
@@ -2237,12 +3095,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_chainlen(ctx, stmt->expr->chain))
+ return -1;
}
break;
case EXPR_MAP:
@@ -2257,6 +3119,9 @@ static bool stmt_evaluate_payload_need_csum(const struct expr *payload)
{
const struct proto_desc *desc;
+ if (payload->payload.base == PROTO_BASE_INNER_HDR)
+ return true;
+
desc = payload->payload.desc;
return desc && desc->checksum_key;
@@ -2265,14 +3130,22 @@ static bool stmt_evaluate_payload_need_csum(const struct expr *payload)
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)
@@ -2285,6 +3158,11 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
mpz_t bitmask, ff;
bool need_csum;
+ 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;
@@ -2298,6 +3176,9 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
payload->byteorder) < 0)
return -1;
+ if (stmt->payload.val->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->payload.val);
+
need_csum = stmt_evaluate_payload_need_csum(payload);
if (!payload_needs_adjustment(payload)) {
@@ -2317,6 +3198,11 @@ 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 (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 (need_csum && payload_byte_size & 1) {
payload_byte_size++;
@@ -2355,9 +3241,9 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
mpz_clear(ff);
assert(sizeof(data) * BITS_PER_BYTE >= masklen);
- mpz_export_data(data, bitmask, BYTEORDER_HOST_ENDIAN, sizeof(data));
+ mpz_export_data(data, bitmask, payload->byteorder, payload_byte_size);
mask = constant_expr_alloc(&payload->location, expr_basetype(payload),
- BYTEORDER_HOST_ENDIAN, masklen, data);
+ payload->byteorder, masklen, data);
mpz_clear(bitmask);
payload_bytes = payload_expr_alloc(&payload->location, NULL, 0);
@@ -2365,6 +3251,7 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
payload_byte_offset * BITS_PER_BYTE,
payload_byte_size * BITS_PER_BYTE);
+ payload_bytes->payload.is_raw = 1;
payload_bytes->payload.desc = payload->payload.desc;
payload_bytes->byteorder = payload->byteorder;
@@ -2391,6 +3278,24 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
{
struct expr *key, *set, *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)
+ 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)
@@ -2410,7 +3315,9 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
set->set_flags |= NFT_SET_TIMEOUT;
setref = implicit_set_declaration(ctx, stmt->meter.name,
- expr_get(key), NULL, set);
+ expr_get(key), NULL, set, 0);
+ if (!setref)
+ return -1;
setref->set->desc.size = stmt->meter.size;
stmt->meter.set = setref;
@@ -2426,26 +3333,45 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
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;
}
@@ -2453,9 +3379,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,
@@ -2467,9 +3394,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;
@@ -2528,7 +3456,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;
}
@@ -2536,6 +3464,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;
@@ -2545,23 +3474,26 @@ 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:
+ case __constant_htons(ETH_P_IP):
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:
- BUG("unsupported family");
+ return stmt_error(ctx, stmt,
+ "cannot infer ICMP reject variant to use: explicit value required.\n");
}
break;
}
@@ -2572,9 +3504,10 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
static int stmt_evaluate_reject_inet(struct eval_ctx *ctx, struct stmt *stmt,
struct expr *expr)
{
+ 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;
@@ -2589,13 +3522,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):
@@ -2603,29 +3537,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;
@@ -2637,15 +3571,15 @@ static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx,
static int stmt_evaluate_reject_bridge(struct eval_ctx *ctx, struct stmt *stmt,
struct expr *expr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc;
- desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
- if (desc != &proto_eth && desc != &proto_vlan)
- return stmt_binary_error(ctx,
- &ctx->pctx.protocol[PROTO_BASE_LL_HDR],
- stmt, "unsupported link layer protocol");
+ 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;
@@ -2659,7 +3593,9 @@ static int stmt_evaluate_reject_bridge(struct eval_ctx *ctx, struct stmt *stmt,
static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
struct expr *expr)
{
- 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:
@@ -2673,13 +3609,14 @@ static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
return stmt_binary_error(ctx, stmt->reject.expr, 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,
"conflicting protocols specified: ip vs ip6");
}
break;
case NFPROTO_BRIDGE:
+ case NFPROTO_NETDEV:
if (stmt_evaluate_reject_bridge(ctx, stmt, expr) < 0)
return -1;
break;
@@ -2696,49 +3633,53 @@ 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:
+ case __constant_htons(ETH_P_IP):
stmt->reject.family = NFPROTO_IPV4;
stmt->reject.icmp_code = ICMP_PORT_UNREACH;
break;
case NFPROTO_IPV6:
+ case __constant_htons(ETH_P_IPV6):
stmt->reject.family = NFPROTO_IPV6;
stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
break;
}
break;
case NFPROTO_BRIDGE:
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ case NFPROTO_NETDEV:
+ 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):
@@ -2757,7 +3698,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;
@@ -2766,6 +3710,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);
@@ -2774,9 +3725,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)
@@ -2793,7 +3744,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;
@@ -2821,22 +3772,24 @@ static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *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)
+ if (!stmt->nat.addr) {
+ stmt->nat.family = NFPROTO_INET;
return 0;
-
+ }
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;
@@ -2865,7 +3818,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);
@@ -2875,51 +3828,133 @@ static int evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
expr);
}
+static bool nat_evaluate_addr_has_th_expr(const struct expr *map)
+{
+ const struct expr *i, *concat;
+
+ if (!map || map->etype != EXPR_MAP)
+ return false;
+
+ concat = map->map;
+ if (concat ->etype != EXPR_CONCAT)
+ return false;
+
+ list_for_each_entry(i, &concat->expressions, list) {
+ enum proto_bases base;
+
+ if (i->etype == EXPR_PAYLOAD &&
+ i->payload.base == PROTO_BASE_TRANSPORT_HDR &&
+ i->payload.desc != &proto_th)
+ return true;
+
+ if ((i->flags & EXPR_F_PROTOCOL) == 0)
+ continue;
+
+ switch (i->etype) {
+ case EXPR_META:
+ base = i->meta.base;
+ break;
+ case EXPR_PAYLOAD:
+ base = i->payload.base;
+ break;
+ default:
+ return false;
+ }
+
+ if (base == PROTO_BASE_NETWORK_HDR)
+ return true;
+ }
+
+ return false;
+}
+
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;
- if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL)
+ 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))
return stmt_binary_error(ctx, *expr, 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);
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);
@@ -2932,9 +3967,15 @@ 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 = 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.family == NFPROTO_INET)
+ expr_family_infer(pctx, stmt->nat.addr, &stmt->nat.family);
switch (stmt->nat.family) {
case NFPROTO_IPV4:
@@ -2944,53 +3985,117 @@ 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 (stmt->nat.addr->etype != EXPR_MAP)
- return 0;
+ if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
+ !nat_evaluate_addr_has_th_expr(stmt->nat.addr)) {
+ 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) {
+ err = 0;
+ goto out;
+ }
data = stmt->nat.addr->mappings->set->data;
+ if (data->flags & EXPR_F_INTERVAL)
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
+
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;
}
+static bool nat_concat_map(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct expr *i;
+
+ if (stmt->nat.addr->etype != EXPR_MAP)
+ return false;
+
+ switch (stmt->nat.addr->mappings->etype) {
+ case EXPR_SET:
+ list_for_each_entry(i, &stmt->nat.addr->mappings->expressions, list) {
+ if (i->etype == EXPR_MAPPING &&
+ i->right->etype == EXPR_CONCAT) {
+ stmt->nat.type_flags |= STMT_NAT_F_CONCAT;
+ return true;
+ }
+ }
+ break;
+ case EXPR_SYMBOL:
+ /* expr_evaluate_map() see EXPR_SET_REF after this evaluation. */
+ 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;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
{
int err;
@@ -3004,7 +4109,9 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
if (err < 0)
return err;
- if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) {
+ if (nat_concat_map(ctx, stmt) ||
+ stmt->nat.type_flags & STMT_NAT_F_CONCAT) {
+
err = stmt_evaluate_nat_map(ctx, stmt);
if (err < 0)
return err;
@@ -3013,32 +4120,12 @@ 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;
}
- if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) {
- switch (stmt->nat.addr->etype) {
- case EXPR_MAP:
- if (!(stmt->nat.addr->mappings->set->data->flags & EXPR_F_INTERVAL))
- return expr_error(ctx->msgs, stmt->nat.addr,
- "map is not defined as interval");
- break;
- case EXPR_RANGE:
- case EXPR_PREFIX:
- break;
- default:
- return expr_error(ctx->msgs, stmt->nat.addr,
- "neither prefix, range nor map expression");
- }
-
- stmt->flags |= STMT_F_TERMINAL;
-
- return 0;
- }
-
if (stmt->nat.proto != NULL) {
err = nat_evaluate_transport(ctx, stmt, &stmt->nat.proto);
if (err < 0)
@@ -3053,13 +4140,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;
@@ -3068,7 +4156,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");
@@ -3080,22 +4168,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;
@@ -3132,7 +4220,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;
@@ -3147,13 +4235,14 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
struct eval_ctx rule_ctx = {
.nft = ctx->nft,
.msgs = ctx->msgs,
+ .cmd = ctx->cmd,
};
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)
@@ -3170,11 +4259,17 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int stmt_evaluate_optstrip(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ return expr_evaluate(ctx, &stmt->optstrip.expr);
+}
+
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)
@@ -3191,6 +4286,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:
@@ -3209,15 +4307,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,
@@ -3229,6 +4332,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:
@@ -3247,6 +4353,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:
@@ -3263,14 +4372,16 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
BYTEORDER_HOST_ENDIAN,
&stmt->queue.queue) < 0)
return -1;
- if (!expr_is_constant(stmt->queue.queue))
- return expr_error(ctx->msgs, stmt->queue.queue,
- "queue number is not constant");
- if (stmt->queue.queue->etype != EXPR_RANGE &&
- (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT))
+
+ if ((stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT) &&
+ stmt->queue.queue->etype != EXPR_RANGE)
return expr_error(ctx->msgs, stmt->queue.queue,
"fanout requires a range to be "
"specified");
+
+ if (ctx->ectx.maxval > USHRT_MAX)
+ return expr_error(ctx->msgs, stmt->queue.queue,
+ "queue expression max value exceeds %u", USHRT_MAX);
}
stmt->flags |= STMT_F_TERMINAL;
return 0;
@@ -3278,15 +4389,25 @@ 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;
+ char tmp[NF_LOG_PREFIXLEN] = {};
+ char prefix[NF_LOG_PREFIXLEN];
+ size_t len = sizeof(prefix);
+ size_t offset = 0;
struct expr *expr;
- size_t size = 0;
- if (stmt->log.prefix->etype != EXPR_LIST)
+ if (stmt->log.prefix->etype != EXPR_LIST) {
+ if (stmt->log.prefix &&
+ div_round_up(stmt->log.prefix->len, BITS_PER_BYTE) >= NF_LOG_PREFIXLEN)
+ return expr_error(ctx->msgs, stmt->log.prefix, "log prefix is too long");
+
return 0;
+ }
+
+ prefix[0] = '\0';
list_for_each_entry(expr, &stmt->log.prefix->expressions, list) {
+ int ret;
+
switch (expr->etype) {
case EXPR_VALUE:
expr_to_string(expr, tmp);
@@ -3297,13 +4418,13 @@ static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
expr->sym->expr->identifier);
break;
default:
- BUG("unknown expresion type %s\n", expr_name(expr));
+ BUG("unknown expression type %s\n", expr_name(expr));
break;
}
- SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ SNPRINTF_BUFFER_SIZE(ret, &len, &offset);
}
- if (len == NF_LOG_PREFIXLEN)
+ if (len == 0)
return stmt_error(ctx, stmt, "log prefix is too long");
expr = constant_expr_alloc(&stmt->log.prefix->location, &string_type,
@@ -3341,6 +4462,9 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct set *this_set;
+ struct stmt *this;
+
expr_set_context(&ctx->ectx, NULL, 0);
if (expr_evaluate(ctx, &stmt->set.set) < 0)
return -1;
@@ -3348,7 +4472,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,
@@ -3360,19 +4484,30 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->set.key->comment != NULL)
return expr_error(ctx->msgs, stmt->set.key,
"Key expression comments are not supported");
- if (stmt->set.stmt) {
- if (stmt_evaluate(ctx, stmt->set.stmt) < 0)
+ list_for_each_entry(this, &stmt->set.stmt_list, list) {
+ if (stmt_evaluate(ctx, this) < 0)
return -1;
- if (!(stmt->set.stmt->flags & STMT_F_STATEFUL))
- return stmt_binary_error(ctx, stmt->set.stmt, stmt,
- "meter statement must be stateful");
+ if (!(this->flags & STMT_F_STATEFUL))
+ return stmt_error(ctx, this,
+ "statement must be stateful");
}
+ this_set = stmt->set.set->set;
+
+ /* Make sure EVAL flag is set on set definition so that kernel
+ * picks a set that allows updates from the packet path.
+ *
+ * Alternatively we could error out in case 'flags dynamic' was
+ * not given, but we can repair this here.
+ */
+ this_set->flags |= NFT_SET_EVAL;
return 0;
}
static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct stmt *this;
+
expr_set_context(&ctx->ectx, NULL, 0);
if (expr_evaluate(ctx, &stmt->map.set) < 0)
return -1;
@@ -3380,7 +4515,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,
@@ -3405,13 +4544,16 @@ 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");
- if (stmt->map.stmt) {
- if (stmt_evaluate(ctx, stmt->map.stmt) < 0)
+ list_for_each_entry(this, &stmt->map.stmt_list, list) {
+ if (stmt_evaluate(ctx, this) < 0)
return -1;
- if (!(stmt->map.stmt->flags & STMT_F_STATEFUL))
- return stmt_binary_error(ctx, stmt->map.stmt, stmt,
- "meter statement must be stateful");
+ if (!(this->flags & STMT_F_STATEFUL))
+ return stmt_error(ctx, this,
+ "statement must be stateful");
}
return 0;
@@ -3441,7 +4583,10 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
ctx->ectx.len, NULL);
mappings = implicit_set_declaration(ctx, "__objmap%d",
- key, NULL, mappings);
+ key, NULL, mappings,
+ NFT_SET_ANONYMOUS);
+ if (!mappings)
+ return -1;
mappings->set->objtype = stmt->objref.type;
map->mappings = mappings;
@@ -3449,10 +4594,15 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
ctx->set = mappings->set;
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+
+ 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)
+ return -1;
+
ctx->set = NULL;
- map->mappings->set->flags |=
- map->mappings->set->init->set_flags;
+ map_set_concat_info(map);
/* fall through */
case EXPR_SYMBOL:
if (expr_evaluate(ctx, &map->mappings) < 0)
@@ -3469,7 +4619,7 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
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",
@@ -3517,9 +4667,12 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
erec_destroy(erec);
}
+ ctx->stmt_len = 0;
+
switch (stmt->ops->type) {
case STMT_CONNLIMIT:
case STMT_COUNTER:
+ case STMT_LAST:
case STMT_LIMIT:
case STMT_QUOTA:
case STMT_NOTRACK:
@@ -3563,6 +4716,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_evaluate_synproxy(ctx, stmt);
case STMT_CHAIN:
return stmt_evaluate_chain(ctx, stmt);
+ case STMT_OPTSTRIP:
+ return stmt_evaluate_optstrip(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
@@ -3573,22 +4728,37 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
struct table *table;
struct set *set;
- table = table_lookup_global(ctx);
+ 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);
- set = set_lookup(table, ctx->cmd->handle.set.name);
+ set = set_cache_find(table, ctx->cmd->handle.set.name);
if (set == NULL)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
+ if (set->key == NULL)
+ return -1;
+
+ set->existing_set = set;
ctx->set = set;
expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
if (expr_evaluate(ctx, &cmd->expr) < 0)
return -1;
- ctx->set = NULL;
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;
+
+ assert(cmd->expr->etype == EXPR_SET);
+ cmd->expr->set_flags |= NFT_SET_INTERVAL;
+ }
+
+ ctx->set = NULL;
return 0;
}
@@ -3607,27 +4777,137 @@ static int set_key_data_error(struct eval_ctx *ctx, const struct set *set,
dtype->name, name, hint);
}
+static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
+{
+ unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+ uint32_t ntype = 0, size = 0;
+ struct expr *i, *next;
+
+ list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
+ unsigned dsize_bytes;
+
+ if (i->etype == EXPR_CT &&
+ (i->ct.key == NFT_CT_SRC ||
+ i->ct.key == NFT_CT_DST))
+ return expr_error(ctx->msgs, i,
+ "specify either ip or ip6 for address matching");
+
+ if (i->etype == EXPR_PAYLOAD &&
+ i->dtype->type == TYPE_INTEGER) {
+ struct datatype *dtype;
+
+ dtype = datatype_clone(i->dtype);
+ dtype->size = i->len;
+ dtype->byteorder = i->byteorder;
+ __datatype_set(i, dtype);
+ }
+
+ if (i->dtype->size == 0 && i->len == 0)
+ return expr_binary_error(ctx->msgs, i, *expr,
+ "can not use variable sized "
+ "data types (%s) in concat "
+ "expressions",
+ i->dtype->name);
+
+ 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));
+ (*expr)->len = size;
+
+ expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
+ ctx->ectx.key = *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 int set_evaluate(struct eval_ctx *ctx, struct set *set)
{
+ struct set *existing_set = NULL;
+ unsigned int num_stmts = 0;
struct table *table;
+ struct stmt *stmt;
const char *type;
- table = table_lookup_global(ctx);
- if (table == NULL)
- return table_not_found(ctx);
+ 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,
+ set->handle.family);
+ if (table == NULL)
+ return table_not_found(ctx);
+
+ existing_set = set_cache_find(table, set->handle.set.name);
+ if (!existing_set)
+ set_cache_add(set_get(set), table);
+
+ if (existing_set && 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;
+ }
+ }
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);
if (set->key->len == 0) {
if (set->key->etype == EXPR_CONCAT &&
- expr_evaluate_concat(ctx, &set->key, false) < 0)
+ set_expr_evaluate_concat(ctx, &set->key) < 0)
return -1;
if (set->key->len == 0)
@@ -3642,18 +4922,33 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
set->flags |= NFT_SET_CONCAT;
}
+ 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)) {
if (set->data == NULL)
return set_error(ctx, set, "map definition does not "
"specify mapping data type");
- if (set->data->flags & EXPR_F_INTERVAL)
- set->data->len *= 2;
-
if (set->data->etype == EXPR_CONCAT &&
- expr_evaluate_concat(ctx, &set->data, false) < 0)
+ set_expr_evaluate_concat(ctx, &set->data) < 0)
return -1;
+ if (set->data->flags & EXPR_F_INTERVAL)
+ set->data->len *= 2;
+
if (set->data->len == 0 && set->data->dtype->type != TYPE_VERDICT)
return set_key_data_error(ctx, set,
set->data->dtype, type);
@@ -3670,20 +4965,22 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (set->timeout)
set->flags |= NFT_SET_TIMEOUT;
- if (set_is_anonymous(set->flags))
- return 0;
+ list_for_each_entry(stmt, &set->stmt_list, list)
+ num_stmts++;
- 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)
+ if (num_stmts > 1)
+ set->flags |= NFT_SET_EXPR;
+
+ if (set_is_anonymous(set->flags)) {
+ if (set_is_interval(set->init->set_flags) &&
+ !(set->init->set_flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, set, set->init) < 0)
return -1;
+
+ return 0;
}
- ctx->set = NULL;
- if (set_lookup(table, set->handle.set.name) == NULL)
- set_add_hash(set_get(set), table);
+ set->existing_set = existing_set;
return 0;
}
@@ -3698,8 +4995,8 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
int prio_snd;
char op;
- ctx->ectx.dtype = &priority_type;
- ctx->ectx.len = NFT_NAME_MAXLEN * BITS_PER_BYTE;
+ expr_set_context(&ctx->ectx, &priority_type, NFT_NAME_MAXLEN * BITS_PER_BYTE);
+
if (expr_evaluate(ctx, &prio->expr) < 0)
return false;
if (prio->expr->etype != EXPR_VALUE) {
@@ -3714,7 +5011,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;
@@ -3789,7 +5086,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;
}
@@ -3806,10 +5103,19 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
{
struct table *table;
- table = table_lookup_global(ctx);
+ 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);
+ 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);
if (ft->hook.num == NF_INET_NUMHOOKS)
@@ -3851,11 +5157,13 @@ static int rule_cache_update(struct eval_ctx *ctx, enum cmd_ops op)
struct table *table;
struct chain *chain;
- table = table_lookup(&rule->handle, &ctx->nft->cache);
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ rule->handle.table.name,
+ rule->handle.family);
if (!table)
return table_not_found(ctx);
- chain = chain_lookup(table, &rule->handle);
+ chain = chain_cache_find(table, rule->handle.chain.name);
if (!chain)
return chain_not_found(ctx);
@@ -3916,7 +5224,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;
@@ -3931,6 +5241,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);
@@ -3939,7 +5251,7 @@ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule,
return -1;
}
- if (cache_needs_update(&ctx->nft->cache))
+ if (nft_cache_needs_update(&ctx->nft->cache))
return rule_cache_update(ctx, op);
return 0;
@@ -3951,10 +5263,13 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
return NF_INET_NUMHOOKS;
switch (family) {
+ case NFPROTO_INET:
+ if (!strcmp(hook, "ingress"))
+ return NF_INET_INGRESS;
+ /* fall through */
case NFPROTO_IPV4:
case NFPROTO_BRIDGE:
case NFPROTO_IPV6:
- case NFPROTO_INET:
/* These families have overlapping values for each hook */
if (!strcmp(hook, "prerouting"))
return NF_INET_PRE_ROUTING;
@@ -3978,6 +5293,8 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
case NFPROTO_NETDEV:
if (!strcmp(hook, "ingress"))
return NF_NETDEV_INGRESS;
+ else if (!strcmp(hook, "egress"))
+ return NF_NETDEV_EGRESS;
break;
default:
break;
@@ -3989,22 +5306,23 @@ 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_lookup_global(ctx);
+ 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);
if (chain == NULL) {
- if (chain_lookup(table, &ctx->cmd->handle) == NULL) {
- chain = chain_alloc(NULL);
+ if (!chain_cache_find(table, ctx->cmd->handle.chain.name)) {
+ chain = chain_alloc();
handle_merge(&chain->handle, &ctx->cmd->handle);
- chain_add_hash(chain, table);
+ chain_cache_add(chain, table);
}
return 0;
} else if (!(chain->flags & CHAIN_F_BINDING)) {
- if (chain_lookup(table, &chain->handle) == NULL)
- chain_add_hash(chain_get(chain), table);
+ if (!chain_cache_find(table, chain->handle.chain.name))
+ chain_cache_add(chain_get(chain), table);
}
if (chain->flags & CHAIN_F_BASECHAIN) {
@@ -4027,13 +5345,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->handle.family == NFPROTO_NETDEV) {
- if (!chain->dev_expr)
- return __stmt_binary_error(ctx, &chain->loc, NULL,
- "Missing `device' in this chain definition");
+ if (chain->dev_expr) {
+ if (!(chain->flags & CHAIN_F_BASECHAIN))
+ chain->flags |= CHAIN_F_BASECHAIN;
- if (!evaluate_device_expr(ctx, &chain->dev_expr))
+ if (chain->handle.family == NFPROTO_NETDEV ||
+ (chain->handle.family == NFPROTO_INET &&
+ chain->hook.num == NF_INET_INGRESS)) {
+ 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,
@@ -4041,11 +5363,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;
}
@@ -4079,8 +5396,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;
@@ -4088,6 +5405,17 @@ static int ct_timeout_evaluate(struct eval_ctx *ctx, struct obj *obj)
static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj)
{
+ struct table *table;
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ ctx->cmd->handle.table.name,
+ ctx->cmd->handle.family);
+ if (!table)
+ return table_not_found(ctx);
+
+ if (!obj_cache_find(table, obj->handle.obj.name, obj->type))
+ obj_cache_add(obj_get(obj), table);
+
switch (obj->type) {
case NFT_OBJECT_CT_TIMEOUT:
return ct_timeout_evaluate(ctx, obj);
@@ -4102,49 +5430,18 @@ 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_lookup(&ctx->cmd->handle, &ctx->nft->cache) == NULL) {
- if (table == NULL) {
+ if (!table_cache_find(&ctx->nft->cache.table_cache,
+ ctx->cmd->handle.table.name,
+ ctx->cmd->handle.family)) {
+ if (!table) {
table = table_alloc();
handle_merge(&table->handle, &ctx->cmd->handle);
- table_add_hash(table, &ctx->nft->cache);
+ table_cache_add(table, &ctx->nft->cache);
} else {
- table_add_hash(table_get(table), &ctx->nft->cache);
+ table_cache_add(table_get(table), &ctx->nft->cache);
}
}
- 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;
}
@@ -4156,6 +5453,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);
@@ -4174,6 +5473,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SECMARK:
case CMD_OBJ_CT_EXPECT:
case CMD_OBJ_SYNPROXY:
+ handle_merge(&cmd->object->handle, &cmd->handle);
return obj_evaluate(ctx, cmd->object);
default:
BUG("invalid command object type %u\n", cmd->obj);
@@ -4184,35 +5484,149 @@ static void table_del_cache(struct eval_ctx *ctx, struct cmd *cmd)
{
struct table *table;
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
+ if (!cmd->handle.table.name)
+ return;
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
if (!table)
return;
- list_del(&table->list);
+ table_cache_del(table);
table_free(table);
}
+static void chain_del_cache(struct eval_ctx *ctx, struct cmd *cmd)
+{
+ struct table *table;
+ struct chain *chain;
+
+ if (!cmd->handle.chain.name)
+ return;
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
+ return;
+
+ chain = chain_cache_find(table, cmd->handle.chain.name);
+ if (!chain)
+ return;
+
+ chain_cache_del(chain);
+ chain_free(chain);
+}
+
+static void set_del_cache(struct eval_ctx *ctx, struct cmd *cmd)
+{
+ struct table *table;
+ struct set *set;
+
+ if (!cmd->handle.set.name)
+ return;
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
+ return;
+
+ set = set_cache_find(table, cmd->handle.set.name);
+ if (!set)
+ return;
+
+ set_cache_del(set);
+ set_free(set);
+}
+
+static void ft_del_cache(struct eval_ctx *ctx, struct cmd *cmd)
+{
+ struct flowtable *ft;
+ struct table *table;
+
+ if (!cmd->handle.flowtable.name)
+ return;
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
+ return;
+
+ ft = ft_cache_find(table, cmd->handle.flowtable.name);
+ if (!ft)
+ return;
+
+ ft_cache_del(ft);
+ flowtable_free(ft);
+}
+
+static void obj_del_cache(struct eval_ctx *ctx, struct cmd *cmd, int type)
+{
+ struct table *table;
+ struct obj *obj;
+
+ if (!cmd->handle.obj.name)
+ return;
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
+ return;
+
+ obj = obj_cache_find(table, cmd->handle.obj.name, type);
+ if (!obj)
+ return;
+
+ obj_cache_del(obj);
+ obj_free(obj);
+}
+
static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
return setelem_evaluate(ctx, cmd);
case CMD_OBJ_SET:
+ set_del_cache(ctx, cmd);
+ return 0;
case CMD_OBJ_RULE:
+ return 0;
case CMD_OBJ_CHAIN:
+ chain_del_cache(ctx, cmd);
return 0;
case CMD_OBJ_TABLE:
table_del_cache(ctx, cmd);
return 0;
case CMD_OBJ_FLOWTABLE:
+ ft_del_cache(ctx, cmd);
+ return 0;
case CMD_OBJ_COUNTER:
+ obj_del_cache(ctx, cmd, NFT_OBJECT_COUNTER);
+ return 0;
case CMD_OBJ_QUOTA:
+ obj_del_cache(ctx, cmd, NFT_OBJECT_QUOTA);
+ return 0;
case CMD_OBJ_CT_HELPER:
+ obj_del_cache(ctx, cmd, NFT_OBJECT_CT_HELPER);
+ return 0;
case CMD_OBJ_CT_TIMEOUT:
+ obj_del_cache(ctx, cmd, NFT_OBJECT_CT_TIMEOUT);
+ return 0;
case CMD_OBJ_LIMIT:
+ obj_del_cache(ctx, cmd, NFT_OBJECT_LIMIT);
+ return 0;
case CMD_OBJ_SECMARK:
+ obj_del_cache(ctx, cmd, NFT_OBJECT_SECMARK);
+ return 0;
case CMD_OBJ_CT_EXPECT:
+ obj_del_cache(ctx, cmd, NFT_OBJECT_CT_EXPECT);
+ return 0;
case CMD_OBJ_SYNPROXY:
+ obj_del_cache(ctx, cmd, NFT_OBJECT_SYNPROXY);
return 0;
default:
BUG("invalid command object type %u\n", cmd->obj);
@@ -4240,7 +5654,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);
@@ -4254,11 +5668,13 @@ static int cmd_evaluate_list_obj(struct eval_ctx *ctx, const struct cmd *cmd,
if (obj_type == NFT_OBJECT_UNSPEC)
obj_type = NFT_OBJECT_COUNTER;
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
if (table == NULL)
return table_not_found(ctx);
- if (obj_lookup(table, cmd->handle.obj.name, obj_type) == NULL)
+ if (!obj_cache_find(table, cmd->handle.obj.name, obj_type))
return obj_not_found(ctx, &cmd->handle.obj.location,
cmd->handle.obj.name);
@@ -4276,69 +5692,54 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
if (cmd->handle.table.name == NULL)
return 0;
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
return table_not_found(ctx);
return 0;
case CMD_OBJ_SET:
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
- return table_not_found(ctx);
-
- set = set_lookup(table, cmd->handle.set.name);
- if (set == NULL)
- 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_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
- return table_not_found(ctx);
-
- set = set_lookup(table, cmd->handle.set.name);
- if (set == NULL)
- 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:
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
+ 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_lookup(table, cmd->handle.set.name);
+ 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 (!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_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
return table_not_found(ctx);
- if (chain_lookup(table, &cmd->handle) == NULL)
+ if (!chain_cache_find(table, cmd->handle.chain.name))
return chain_not_found(ctx);
return 0;
case CMD_OBJ_FLOWTABLE:
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
return table_not_found(ctx);
- ft = flowtable_lookup(table, cmd->handle.flowtable.name);
- if (ft == NULL)
+ ft = ft_cache_find(table, cmd->handle.flowtable.name);
+ if (!ft)
return flowtable_not_found(ctx, &ctx->cmd->handle.flowtable.location,
ctx->cmd->handle.flowtable.name);
@@ -4367,9 +5768,13 @@ 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_lookup(&cmd->handle, &ctx->nft->cache) == NULL)
+ if (!table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family))
return table_not_found(ctx);
return 0;
@@ -4378,6 +5783,16 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_METERS:
case CMD_OBJ_MAPS:
return 0;
+ case CMD_OBJ_HOOKS:
+ if (cmd->handle.chain.name) {
+ int hooknum = str2hooknum(cmd->handle.family, cmd->handle.chain.name);
+
+ if (hooknum == NF_INET_NUMHOOKS)
+ return chain_not_found(ctx);
+
+ cmd->handle.chain_id = hooknum;
+ }
+ return 0;
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -4390,12 +5805,21 @@ 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_lookup(&cmd->handle, &ctx->nft->cache) == NULL)
+ if (!table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family))
return table_not_found(ctx);
return 0;
+ case CMD_OBJ_ELEMENTS:
+ return setelem_evaluate(ctx, cmd);
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ return cmd_evaluate_list(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -4411,6 +5835,7 @@ static void __flush_set_cache(struct set *set)
static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
{
+ struct cache *table_cache = &ctx->nft->cache.table_cache;
struct table *table;
struct set *set;
@@ -4425,11 +5850,12 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
/* Chains don't hold sets */
break;
case CMD_OBJ_SET:
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
+ table = table_cache_find(table_cache, cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
return table_not_found(ctx);
- set = set_lookup(table, cmd->handle.set.name);
+ 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);
@@ -4441,11 +5867,12 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
return 0;
case CMD_OBJ_MAP:
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
+ table = table_cache_find(table_cache, cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
return table_not_found(ctx);
- set = set_lookup(table, cmd->handle.set.name);
+ 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);
@@ -4457,15 +5884,16 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
return 0;
case CMD_OBJ_METER:
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
+ table = table_cache_find(table_cache, cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
return table_not_found(ctx);
- set = set_lookup(table, cmd->handle.set.name);
+ 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))
+ else if (!set_is_meter_compat(set->flags))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
@@ -4484,11 +5912,13 @@ static int cmd_evaluate_rename(struct eval_ctx *ctx, struct cmd *cmd)
switch (cmd->obj) {
case CMD_OBJ_CHAIN:
- table = table_lookup(&ctx->cmd->handle, &ctx->nft->cache);
- if (table == NULL)
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
+ if (!table)
return table_not_found(ctx);
- if (chain_lookup(table, &ctx->cmd->handle) == NULL)
+ if (!chain_cache_find(table, ctx->cmd->handle.chain.name))
return chain_not_found(ctx);
break;
@@ -4626,11 +6056,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];
@@ -4658,6 +6089,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);
diff --git a/src/expression.c b/src/expression.c
index fe529f98..cb2573fe 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -8,16 +8,16 @@
* 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>
#include <statement.h>
#include <datatype.h>
+#include <netlink.h>
#include <rule.h>
#include <gmputil.h>
#include <utils.h>
@@ -28,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;
@@ -93,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)
@@ -138,6 +139,11 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
} else {
nft_print(octx, "%s expression, datatype %s (%s)",
expr_name(expr), dtype->name, dtype->desc);
+
+ if (dtype == &invalid_type) {
+ nft_print(octx, "\n");
+ return;
+ }
}
if (dtype->basetype != NULL) {
@@ -172,6 +178,8 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, "(in hexadecimal):\n");
symbol_table_print(edtype->sym_tbl, edtype,
expr->byteorder, octx);
+ } else if (edtype->describe) {
+ edtype->describe(octx);
}
}
@@ -252,6 +260,22 @@ static void verdict_expr_destroy(struct expr *expr)
expr_free(expr->chain);
}
+static int verdict_expr_build_udata(struct nftnl_udata_buf *udbuf,
+ const struct expr *expr)
+{
+ return 0;
+}
+
+static struct expr *verdict_expr_parse_udata(const struct nftnl_udata *attr)
+{
+ struct expr *e;
+
+ e = symbol_expr_alloc(&internal_location, SYMBOL_VALUE, NULL, "verdict");
+ e->dtype = &verdict_type;
+ e->len = NFT_REG_SIZE * BITS_PER_BYTE;
+ return e;
+}
+
static const struct expr_ops verdict_expr_ops = {
.type = EXPR_VERDICT,
.name = "verdict",
@@ -260,6 +284,8 @@ static const struct expr_ops verdict_expr_ops = {
.cmp = verdict_expr_cmp,
.clone = verdict_expr_clone,
.destroy = verdict_expr_destroy,
+ .build_udata = verdict_expr_build_udata,
+ .parse_udata = verdict_expr_parse_udata,
};
struct expr *verdict_expr_alloc(const struct location *loc,
@@ -278,8 +304,7 @@ struct expr *verdict_expr_alloc(const struct location *loc,
static void symbol_expr_print(const struct expr *expr, struct output_ctx *octx)
{
- nft_print(octx, "%s%s", expr->scope != NULL ? "$" : "",
- expr->identifier);
+ nft_print(octx, "%s", expr->identifier);
}
static void symbol_expr_clone(struct expr *new, const struct expr *expr)
@@ -291,7 +316,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 = {
@@ -560,6 +585,7 @@ const char *expr_op_symbols[] = {
[OP_GT] = ">",
[OP_LTE] = "<=",
[OP_GTE] = ">=",
+ [OP_NEG] = "!",
};
static void unary_expr_print(const struct expr *expr, struct output_ctx *octx)
@@ -708,16 +734,26 @@ struct expr *relational_expr_alloc(const struct location *loc, enum ops op,
void relational_expr_pctx_update(struct proto_ctx *ctx,
const struct expr *expr)
{
- const struct expr *left = expr->left;
+ const struct expr *left = expr->left, *right = expr->right;
const struct expr_ops *ops;
+ const struct expr *i;
assert(expr->etype == EXPR_RELATIONAL);
assert(expr->op == OP_EQ || expr->op == OP_IMPLICIT);
ops = expr_ops(left);
if (ops->pctx_update &&
- (left->flags & EXPR_F_PROTOCOL))
- ops->pctx_update(ctx, expr);
+ (left->flags & EXPR_F_PROTOCOL)) {
+ if (expr_is_singleton(right))
+ ops->pctx_update(ctx, &expr->location, left, right);
+ else if (right->etype == EXPR_SET) {
+ list_for_each_entry(i, &right->expressions, list) {
+ if (i->etype == EXPR_SET_ELEM &&
+ i->key->etype == EXPR_VALUE)
+ ops->pctx_update(ctx, &expr->location, left, i->key);
+ }
+ }
+ }
}
static void range_expr_print(const struct expr *expr, struct output_ctx *octx)
@@ -845,17 +881,30 @@ static void concat_expr_print(const struct expr *expr, struct output_ctx *octx)
#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA 1
#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX 2
+static struct expr *expr_build_udata_recurse(struct expr *e)
+{
+ switch (e->etype) {
+ case EXPR_BINOP:
+ return e->left;
+ default:
+ break;
+ }
+
+ return e;
+}
+
static int concat_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct expr *concat_expr)
{
struct nftnl_udata *nest;
+ struct expr *expr, *tmp;
unsigned int i = 0;
- struct expr *expr;
- list_for_each_entry(expr, &concat_expr->expressions, list) {
+ list_for_each_entry_safe(expr, tmp, &concat_expr->expressions, list) {
struct nftnl_udata *nest_expr;
int err;
+ expr = expr_build_udata_recurse(expr);
if (!expr_ops(expr)->build_udata || i >= NFT_REG32_SIZE)
return -1;
@@ -917,7 +966,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX] = {};
const struct datatype *dtype;
struct expr *concat_expr;
- uint32_t dt = 0;
+ uint32_t dt = 0, len = 0;
unsigned int i;
int err;
@@ -947,7 +996,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;
@@ -958,14 +1007,15 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
dt = concat_subtype_add(dt, expr->dtype->type);
compound_expr_add(concat_expr, expr);
+ len += netlink_padded_len(expr->len);
}
dtype = concat_type_alloc(dt);
if (!dtype)
goto err_free;
- concat_expr->dtype = datatype_get(dtype);
- concat_expr->len = dtype->size;
+ __datatype_set(concat_expr, dtype);
+ concat_expr->len = len;
return concat_expr;
@@ -1153,14 +1203,40 @@ struct expr *mapping_expr_alloc(const struct location *loc,
return expr;
}
+static bool __set_expr_is_vmap(const struct expr *mappings)
+{
+ const struct expr *mapping;
+
+ if (list_empty(&mappings->expressions))
+ return false;
+
+ mapping = list_first_entry(&mappings->expressions, struct expr, list);
+ if (mapping->etype == EXPR_MAPPING &&
+ mapping->right->etype == EXPR_VERDICT)
+ return true;
+
+ return false;
+}
+
+static bool set_expr_is_vmap(const struct expr *expr)
+{
+
+ if (expr->mappings->etype == EXPR_SET)
+ return __set_expr_is_vmap(expr->mappings);
+
+ return false;
+}
+
static void map_expr_print(const struct expr *expr, struct output_ctx *octx)
{
expr_print(expr->map, octx);
- if (expr->mappings->etype == EXPR_SET_REF &&
- expr->mappings->set->data->dtype->type == TYPE_VERDICT)
+ if ((expr->mappings->etype == EXPR_SET_REF &&
+ expr->mappings->set->data->dtype->type == TYPE_VERDICT) ||
+ set_expr_is_vmap(expr))
nft_print(octx, " vmap ");
else
nft_print(octx, " map ");
+
expr_print(expr->mappings, octx);
}
@@ -1238,7 +1314,13 @@ struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set)
static void set_elem_expr_print(const struct expr *expr,
struct output_ctx *octx)
{
+ struct stmt *stmt;
+
expr_print(expr->key, octx);
+ list_for_each_entry(stmt, &expr->stmt_list, list) {
+ nft_print(octx, " ");
+ stmt_print(stmt, octx);
+ }
if (expr->timeout) {
nft_print(octx, " timeout ");
time_print(expr->timeout, octx);
@@ -1247,28 +1329,33 @@ static void set_elem_expr_print(const struct expr *expr,
nft_print(octx, " expires ");
time_print(expr->expiration, octx);
}
- if (expr->stmt) {
- nft_print(octx, " ");
- stmt_print(expr->stmt, octx);
- }
if (expr->comment)
nft_print(octx, " comment \"%s\"", expr->comment);
}
static void set_elem_expr_destroy(struct expr *expr)
{
- xfree(expr->comment);
+ struct stmt *stmt, *next;
+
+ free_const(expr->comment);
expr_free(expr->key);
- stmt_free(expr->stmt);
+ 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)
new->comment = xstrdup(expr->comment);
+ 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 const struct expr_ops set_elem_expr_ops = {
@@ -1287,6 +1374,93 @@ struct expr *set_elem_expr_alloc(const struct location *loc, struct expr *key)
expr = expr_alloc(loc, EXPR_SET_ELEM, key->dtype,
key->byteorder, key->len);
expr->key = key;
+ init_list_head(&expr->stmt_list);
+
+ return expr;
+}
+
+static void set_elem_catchall_expr_print(const struct expr *expr,
+ struct output_ctx *octx)
+{
+ 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)
+{
+ struct expr *expr;
+
+ expr = expr_alloc(loc, EXPR_SET_ELEM_CATCHALL, &invalid_type,
+ BYTEORDER_INVALID, 0);
+ expr->flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+
+ 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;
}
@@ -1317,6 +1491,7 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
return mpz_set(rop, expr->value);
case EXPR_PREFIX:
range_expr_value_low(rop, expr->prefix);
+ assert(expr->len >= expr->prefix_len);
mpz_init_bitmask(tmp, expr->len - expr->prefix_len);
mpz_add(rop, rop, tmp);
mpz_clear(tmp);
@@ -1335,9 +1510,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;
@@ -1365,22 +1538,27 @@ static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
case EXPR_RT: return &rt_expr_ops;
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;
}
- 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 0b23e0d3..60c7cd1e 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>
@@ -22,6 +21,7 @@
#include <headers.h>
#include <expression.h>
#include <statement.h>
+#include <sctp_chunk.h>
static const struct exthdr_desc *exthdr_definitions[PROTO_DESC_MAX + 1] = {
[EXTHDR_DESC_HBH] = &exthdr_hbh,
@@ -45,6 +45,9 @@ static const struct exthdr_desc *exthdr_find_desc(enum exthdr_desc_id desc_id)
static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
{
+ const char *name = expr->exthdr.desc ?
+ expr->exthdr.desc->name : "unknown-exthdr";
+
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
/* Offset calculation is a bit hacky at this point.
* There might be a tcp option one day with another
@@ -52,23 +55,42 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
*/
unsigned int offset = expr->exthdr.offset / 64;
- nft_print(octx, "tcp option %s", expr->exthdr.desc->name);
+ if (expr->exthdr.desc == NULL) {
+ if (expr->exthdr.offset == 0 &&
+ expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) {
+ nft_print(octx, "tcp option %d", expr->exthdr.raw_type);
+ return;
+ }
+
+ nft_print(octx, "tcp option @%u,%u,%u", expr->exthdr.raw_type,
+ expr->exthdr.offset, expr->len);
+ return;
+ }
+
+ nft_print(octx, "tcp option %s", name);
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
if (offset)
nft_print(octx, "%d", offset);
nft_print(octx, " %s", expr->exthdr.tmpl->token);
} else if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
- nft_print(octx, "ip option %s", expr->exthdr.desc->name);
+ nft_print(octx, "ip option %s", name);
+ 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_SCTP) {
+ nft_print(octx, "sctp chunk %s", expr->exthdr.desc->name);
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", expr->exthdr.desc->name);
+ nft_print(octx, "exthdr %s", name);
else {
- nft_print(octx, "%s %s",
- expr->exthdr.desc ? expr->exthdr.desc->name : "unknown-exthdr",
+ nft_print(octx, "%s %s", name,
expr->exthdr.tmpl->token);
}
}
@@ -79,6 +101,7 @@ static bool exthdr_expr_cmp(const struct expr *e1, const struct expr *e2)
return e1->exthdr.desc == e2->exthdr.desc &&
e1->exthdr.tmpl == e2->exthdr.tmpl &&
e1->exthdr.op == e2->exthdr.op &&
+ e1->exthdr.raw_type == e2->exthdr.raw_type &&
e1->exthdr.flags == e2->exthdr.flags;
}
@@ -89,11 +112,13 @@ static void exthdr_expr_clone(struct expr *new, const struct expr *expr)
new->exthdr.offset = expr->exthdr.offset;
new->exthdr.op = expr->exthdr.op;
new->exthdr.flags = expr->exthdr.flags;
+ new->exthdr.raw_type = expr->exthdr.raw_type;
}
#define NFTNL_UDATA_EXTHDR_DESC 0
#define NFTNL_UDATA_EXTHDR_TYPE 1
-#define NFTNL_UDATA_EXTHDR_MAX 2
+#define NFTNL_UDATA_EXTHDR_OP 2
+#define NFTNL_UDATA_EXTHDR_MAX 3
static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
{
@@ -104,6 +129,7 @@ static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_EXTHDR_DESC:
case NFTNL_UDATA_EXTHDR_TYPE:
+ case NFTNL_UDATA_EXTHDR_OP:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -118,6 +144,7 @@ static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_EXTHDR_MAX + 1] = {};
+ enum nft_exthdr_op op = NFT_EXTHDR_OP_IPV6;
const struct exthdr_desc *desc;
unsigned int type;
uint32_t desc_id;
@@ -132,22 +159,39 @@ static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
!ud[NFTNL_UDATA_EXTHDR_TYPE])
return NULL;
- desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]);
- desc = exthdr_find_desc(desc_id);
- if (!desc)
- return NULL;
+ if (ud[NFTNL_UDATA_EXTHDR_OP])
+ op = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_OP]);
+ desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]);
type = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_TYPE]);
- return exthdr_expr_alloc(&internal_location, desc, type);
+ switch (op) {
+ case NFT_EXTHDR_OP_IPV6:
+ desc = exthdr_find_desc(desc_id);
+
+ return exthdr_expr_alloc(&internal_location, desc, type);
+ case NFT_EXTHDR_OP_TCPOPT:
+ return tcpopt_expr_alloc(&internal_location,
+ desc_id, type);
+ case NFT_EXTHDR_OP_IPV4:
+ return ipopt_expr_alloc(&internal_location,
+ desc_id, type);
+ 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;
+ }
+
+ return NULL;
}
static unsigned int expr_exthdr_type(const struct exthdr_desc *desc,
const struct proto_hdr_template *tmpl)
{
- unsigned int offset = (unsigned int)(tmpl - &desc->templates[0]);
-
- return offset / sizeof(*tmpl);
+ return (unsigned int)(tmpl - &desc->templates[0]);
}
static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
@@ -156,9 +200,23 @@ static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct proto_hdr_template *tmpl = expr->exthdr.tmpl;
const struct exthdr_desc *desc = expr->exthdr.desc;
unsigned int type = expr_exthdr_type(desc, tmpl);
+ enum nft_exthdr_op op = expr->exthdr.op;
- nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id);
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_TYPE, type);
+ switch (op) {
+ case NFT_EXTHDR_OP_IPV6:
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id);
+ break;
+ 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;
+ default:
+ return -1;
+ }
return 0;
}
@@ -192,7 +250,9 @@ struct expr *exthdr_expr_alloc(const struct location *loc,
expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
BYTEORDER_BIG_ENDIAN, tmpl->len);
expr->exthdr.desc = desc;
+ expr->exthdr.raw_type = desc ? desc->type : 0;
expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
return expr;
}
@@ -228,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,
@@ -269,18 +329,23 @@ void exthdr_init_raw(struct expr *expr, uint8_t type,
unsigned int i;
assert(expr->etype == EXPR_EXTHDR);
+ expr->exthdr.raw_type = type;
+
if (op == NFT_EXTHDR_OP_TCPOPT)
return tcpopt_init_raw(expr, type, offset, len, flags);
if (op == NFT_EXTHDR_OP_IPV4)
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;
@@ -322,16 +387,7 @@ static unsigned int mask_length(const struct expr *mask)
bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned int *shift)
{
unsigned int off, mask_offset, mask_len;
-
- if (expr->exthdr.op != NFT_EXTHDR_OP_IPV4 &&
- expr->exthdr.tmpl != &exthdr_unknown_template)
- return false;
-
- /* In case we are handling tcp options instead of the default ipv6
- * extension headers.
- */
- if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT)
- return tcpopt_find_template(expr, mask, shift);
+ bool found;
mask_offset = mpz_scan1(mask->value, 0);
mask_len = mask_length(mask);
@@ -340,24 +396,31 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i
off += round_up(mask->len, BITS_PER_BYTE) - mask_len;
/* Handle ip options after the offset and mask have been calculated. */
- if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
- if (ipopt_find_template(expr, off, mask_len - mask_offset)) {
- *shift = mask_offset;
- return true;
- } else {
+ switch (expr->exthdr.op) {
+ case NFT_EXTHDR_OP_IPV4:
+ found = ipopt_find_template(expr, off, mask_len - mask_offset);
+ break;
+ case NFT_EXTHDR_OP_TCPOPT:
+ found = tcpopt_find_template(expr, off, mask_len - mask_offset);
+ break;
+ case NFT_EXTHDR_OP_IPV6:
+ exthdr_init_raw(expr, expr->exthdr.raw_type,
+ off, mask_len - mask_offset, expr->exthdr.op, 0);
+
+ /* still failed to find a template... Bug. */
+ if (expr->exthdr.tmpl == &exthdr_unknown_template)
return false;
- }
+ found = true;
+ break;
+ default:
+ found = false;
+ break;
}
- exthdr_init_raw(expr, expr->exthdr.desc->type,
- off, mask_len - mask_offset, expr->exthdr.op, 0);
-
- /* still failed to find a template... Bug. */
- if (expr->exthdr.tmpl == &exthdr_unknown_template)
- return false;
+ if (found)
+ *shift = mask_offset;
- *shift = mask_offset;
- return true;
+ return found;
}
#define HDR_TEMPLATE(__name, __dtype, __type, __member) \
@@ -391,7 +454,6 @@ const struct exthdr_desc exthdr_rt2 = {
.name = "rt2",
.id = EXTHDR_DESC_RT2,
.type = IPPROTO_ROUTING,
- .proto_key = 2,
.templates = {
[RT2HDR_RESERVED] = {},
[RT2HDR_ADDR] = {},
@@ -405,7 +467,6 @@ const struct exthdr_desc exthdr_rt0 = {
.name = "rt0",
.id = EXTHDR_DESC_RT0,
.type = IPPROTO_ROUTING,
- .proto_key = 0,
.templates = {
[RT0HDR_RESERVED] = RT0_FIELD("reserved", ip6r0_reserved, &integer_type),
[RT0HDR_ADDR_1] = RT0_FIELD("addr[1]", ip6r0_addr[0], &ip6addr_type),
@@ -421,7 +482,6 @@ const struct exthdr_desc exthdr_rt4 = {
.name = "srh",
.id = EXTHDR_DESC_SRH,
.type = IPPROTO_ROUTING,
- .proto_key = 4,
.templates = {
[RT4HDR_LASTENT] = RT4_FIELD("last-entry", ip6r4_last_entry, &integer_type),
[RT4HDR_FLAGS] = RT4_FIELD("flags", ip6r4_flags, &integer_type),
@@ -440,7 +500,6 @@ const struct exthdr_desc exthdr_rt = {
.name = "rt",
.id = EXTHDR_DESC_RT,
.type = IPPROTO_ROUTING,
- .proto_key = -1,
#if 0
.protocol_key = RTHDR_TYPE,
.protocols = {
diff --git a/src/fib.c b/src/fib.c
index c6ad0f9c..e95271c9 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>
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 d0e1834c..428acaae 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>
@@ -59,13 +59,13 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
-void iface_cache_update(void)
+static int iface_mnl_talk(struct mnl_socket *nl, uint32_t portid)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
- struct mnl_socket *nl;
struct nlmsghdr *nlh;
struct rtgenmsg *rt;
- uint32_t seq, portid;
+ bool eintr = false;
+ uint32_t seq;
int ret;
nlh = mnl_nlmsg_put_header(buf);
@@ -75,6 +75,38 @@ void iface_cache_update(void)
rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
rt->rtgen_family = AF_PACKET;
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
+ return -1;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret == 0)
+ break;
+ if (ret < 0) {
+ if (errno != EINTR)
+ return ret;
+
+ /* process all pending messages before reporting EINTR */
+ eintr = true;
+ }
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+
+ if (eintr) {
+ ret = -1;
+ errno = EINTR;
+ }
+
+ return ret;
+}
+
+void iface_cache_update(void)
+{
+ struct mnl_socket *nl;
+ uint32_t portid;
+ int ret;
+
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
netlink_init_error();
@@ -84,16 +116,10 @@ void iface_cache_update(void)
portid = mnl_socket_get_portid(nl);
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
- netlink_init_error();
+ do {
+ ret = iface_mnl_talk(nl, portid);
+ } while (ret < 0 && errno == EINTR);
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
- if (ret <= MNL_CB_STOP)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
if (ret == -1)
netlink_init_error();
diff --git a/src/intervals.c b/src/intervals.c
new file mode 100644
index 00000000..6c3f36fe
--- /dev/null
+++ b/src/intervals.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) 2022 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 <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;
+ mpz_t rop;
+
+ assert(expr->etype == EXPR_SET_ELEM);
+
+ switch (expr->key->etype) {
+ case EXPR_SET_ELEM_CATCHALL:
+ case EXPR_RANGE:
+ 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);
+ 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;
+ case EXPR_VALUE:
+ 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));
+ expr_free(expr->key);
+ expr->key = key;
+ break;
+ default:
+ BUG("unhandled key type %d\n", expr->key->etype);
+ }
+}
+
+struct set_automerge_ctx {
+ struct set *set;
+ struct expr *init;
+ struct expr *purge;
+ unsigned int debug_mask;
+};
+
+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);
+ }
+ list_move_tail(&i->list, &ctx->purge->expressions);
+}
+
+static void remove_overlapping_range(struct set_automerge_ctx *ctx,
+ struct expr *prev, struct expr *i)
+{
+ if (i->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, i);
+ return;
+ }
+ list_del(&i->list);
+ expr_free(i);
+ ctx->init->size--;
+}
+
+struct range {
+ mpz_t low;
+ mpz_t high;
+};
+
+static bool merge_ranges(struct set_automerge_ctx *ctx,
+ struct expr *prev, struct expr *i,
+ struct range *prev_range, struct range *range)
+{
+ if (prev->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, prev);
+ expr_free(i->key->left);
+ i->key->left = expr_get(prev->key->left);
+ mpz_set(prev_range->high, range->high);
+ return true;
+ } else if (i->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, i);
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->right);
+ mpz_set(prev_range->high, range->high);
+ } else {
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->right);
+ mpz_set(prev_range->high, range->high);
+ list_del(&i->list);
+ expr_free(i);
+ ctx->init->size--;
+ }
+ return false;
+}
+
+static void set_sort_splice(struct expr *init, struct set *set)
+{
+ struct set *existing_set = set->existing_set;
+
+ set_to_range(init);
+ list_expr_sort(&init->expressions);
+
+ if (!existing_set || existing_set->errors)
+ return;
+
+ if (existing_set->init) {
+ set_to_range(existing_set->init);
+ list_splice_sorted(&existing_set->init->expressions,
+ &init->expressions);
+ init_list_head(&existing_set->init->expressions);
+ } else {
+ existing_set->init = set_expr_alloc(&internal_location, set);
+ }
+}
+
+static void setelem_automerge(struct set_automerge_ctx *ctx)
+{
+ struct expr *i, *next, *prev = NULL;
+ struct range range, prev_range;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ mpz_init(range.high);
+ mpz_init(rop);
+
+ list_for_each_entry_safe(i, next, &ctx->init->expressions, list) {
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ range_expr_value_low(range.low, i);
+ 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);
+ continue;
+ }
+
+ if (mpz_cmp(prev_range.low, range.low) <= 0 &&
+ mpz_cmp(prev_range.high, range.high) >= 0) {
+ remove_overlapping_range(ctx, prev, i);
+ continue;
+ } else if (mpz_cmp(range.low, prev_range.high) <= 0) {
+ if (merge_ranges(ctx, prev, i, &prev_range, &range))
+ prev = i;
+ continue;
+ } else if (ctx->set->automerge) {
+ mpz_sub(rop, range.low, prev_range.high);
+ /* two contiguous ranges */
+ if (mpz_cmp_ui(rop, 1) == 0) {
+ if (merge_ranges(ctx, prev, i, &prev_range, &range))
+ prev = i;
+ continue;
+ }
+ }
+
+ prev = i;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ }
+
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+}
+
+static struct expr *interval_expr_key(struct expr *i)
+{
+ struct expr *elem;
+
+ switch (i->etype) {
+ case EXPR_MAPPING:
+ elem = i->left;
+ break;
+ case EXPR_SET_ELEM:
+ elem = i;
+ break;
+ default:
+ BUG("unhandled expression type %d\n", i->etype);
+ return NULL;
+ }
+
+ return elem;
+}
+
+static void set_to_range(struct expr *init)
+{
+ struct expr *i, *elem;
+
+ list_for_each_entry(i, &init->expressions, list) {
+ elem = interval_expr_key(i);
+ setelem_expr_to_range(elem);
+ }
+}
+
+int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set *existing_set = set->existing_set;
+ struct set_automerge_ctx ctx = {
+ .set = set,
+ .init = init,
+ .debug_mask = debug_mask,
+ };
+ struct expr *i, *next, *clone;
+ struct cmd *purge_cmd;
+ struct handle h = {};
+
+ if (set->flags & NFT_SET_MAP) {
+ set_to_range(init);
+ list_expr_sort(&init->expressions);
+ return 0;
+ }
+
+ set_sort_splice(init, set);
+
+ ctx.purge = set_expr_alloc(&internal_location, set);
+
+ setelem_automerge(&ctx);
+
+ list_for_each_entry_safe(i, next, &init->expressions, list) {
+ if (i->flags & EXPR_F_KERNEL) {
+ list_move_tail(&i->list, &existing_set->init->expressions);
+ } 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);
+ }
+ clone = expr_clone(i);
+ clone->flags |= EXPR_F_KERNEL;
+ list_add_tail(&clone->list, &existing_set->init->expressions);
+ }
+ }
+
+ if (list_empty(&ctx.purge->expressions)) {
+ expr_free(ctx.purge);
+ return 0;
+ }
+
+ handle_merge(&h, &set->handle);
+ purge_cmd = cmd_alloc(CMD_DELETE, CMD_OBJ_ELEMENTS, &h, &init->location, ctx.purge);
+ purge_cmd->elem.set = set_get(set);
+ list_add_tail(&purge_cmd->list, &cmd->list);
+
+ return 0;
+}
+
+static void remove_elem(struct expr *prev, struct set *set, struct expr *purge)
+{
+ struct expr *clone;
+
+ if (prev->flags & EXPR_F_KERNEL) {
+ clone = expr_clone(prev);
+ list_move_tail(&clone->list, &purge->expressions);
+ }
+}
+
+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);
+ 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)
+{
+ remove_elem(prev, set, purge);
+ __adjust_elem_left(set, prev, i);
+
+ list_del(&i->list);
+ expr_free(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);
+ 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)
+{
+ remove_elem(prev, set, purge);
+ __adjust_elem_right(set, prev, i);
+
+ list_del(&i->list);
+ expr_free(i);
+}
+
+static void split_range(struct set *set, struct expr *prev, struct expr *i,
+ struct expr *purge)
+{
+ struct expr *clone;
+
+ if (prev->flags & EXPR_F_KERNEL) {
+ clone = expr_clone(prev);
+ list_move_tail(&clone->list, &purge->expressions);
+ }
+
+ 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);
+ 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);
+ list_move(&prev->list, &set->existing_set->init->expressions);
+
+ list_del(&i->list);
+ expr_free(i);
+}
+
+static int setelem_adjust(struct set *set, struct expr *purge,
+ struct range *prev_range, struct range *range,
+ struct expr *prev, struct expr *i)
+{
+ if (mpz_cmp(prev_range->low, range->low) == 0 &&
+ mpz_cmp(prev_range->high, range->high) > 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ adjust_elem_left(set, prev, i, purge);
+ } else if (mpz_cmp(prev_range->low, range->low) < 0 &&
+ mpz_cmp(prev_range->high, range->high) == 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ adjust_elem_right(set, prev, i, purge);
+ } else if (mpz_cmp(prev_range->low, range->low) < 0 &&
+ mpz_cmp(prev_range->high, range->high) > 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ split_range(set, prev, i, purge);
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+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 range range, prev_range;
+ int err = 0;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ 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;
+
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+
+ if (!prev && i->flags & EXPR_F_REMOVE) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+
+ if (!(i->flags & EXPR_F_REMOVE)) {
+ prev = i;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ continue;
+ }
+
+ 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)
+ list_move_tail(&prev->list, &purge->expressions);
+
+ list_del(&i->list);
+ expr_free(i);
+ }
+ } 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 (i->flags & EXPR_F_REMOVE) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+ prev = NULL;
+ }
+err:
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+
+ return err;
+}
+
+static void automerge_delete(struct list_head *msgs, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set_automerge_ctx ctx = {
+ .set = set,
+ .init = init,
+ .debug_mask = debug_mask,
+ };
+
+ ctx.purge = set_expr_alloc(&internal_location, set);
+ list_expr_sort(&init->expressions);
+ setelem_automerge(&ctx);
+ expr_free(ctx.purge);
+}
+
+static int __set_delete(struct list_head *msgs, struct expr *i, struct set *set,
+ struct expr *init, struct set *existing_set,
+ unsigned int debug_mask)
+{
+ i->flags |= EXPR_F_REMOVE;
+ 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);
+}
+
+/* detection for unexisting intervals already exists in Linux kernels >= 5.7. */
+int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set *existing_set = set->existing_set;
+ struct expr *i, *next, *add, *clone;
+ struct handle h = {};
+ struct cmd *add_cmd;
+ LIST_HEAD(del_list);
+ int err;
+
+ set_to_range(init);
+ if (set->automerge)
+ automerge_delete(msgs, set, init, debug_mask);
+
+ if (existing_set->init) {
+ set_to_range(existing_set->init);
+ } else {
+ existing_set->init = set_expr_alloc(&internal_location, set);
+ }
+
+ list_splice_init(&init->expressions, &del_list);
+
+ list_for_each_entry_safe(i, next, &del_list, list) {
+ err = __set_delete(msgs, i, set, init, existing_set, debug_mask);
+ if (err < 0) {
+ list_splice(&del_list, &init->expressions);
+ return err;
+ }
+ }
+
+ add = set_expr_alloc(&internal_location, set);
+ list_for_each_entry(i, &existing_set->init->expressions, list) {
+ if (!(i->flags & EXPR_F_KERNEL)) {
+ clone = expr_clone(i);
+ list_add_tail(&clone->list, &add->expressions);
+ i->flags |= EXPR_F_KERNEL;
+ }
+ }
+
+ 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);
+ list_for_each_entry(i, &add->expressions, list)
+ pr_gmp_debug("add: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ 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);
+ }
+
+ if (list_empty(&add->expressions)) {
+ expr_free(add);
+ return 0;
+ }
+
+ handle_merge(&h, &cmd->handle);
+ add_cmd = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &h, &cmd->location, add);
+ add_cmd->elem.set = set_get(set);
+ list_add(&add_cmd->list, &cmd->list);
+
+ return 0;
+}
+
+static int setelem_overlap(struct list_head *msgs, struct set *set,
+ struct expr *init)
+{
+ struct expr *i, *next, *elem, *prev = NULL;
+ struct range range, prev_range;
+ int err = 0;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ mpz_init(range.high);
+ mpz_init(rop);
+
+ list_for_each_entry_safe(elem, next, &init->expressions, list) {
+ i = interval_expr_key(elem);
+
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+
+ if (!prev) {
+ prev = elem;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ continue;
+ }
+
+ if (mpz_cmp(prev_range.low, range.low) == 0 &&
+ mpz_cmp(prev_range.high, range.high) == 0)
+ goto next;
+
+ if (mpz_cmp(prev_range.low, range.low) <= 0 &&
+ mpz_cmp(prev_range.high, range.high) >= 0) {
+ if (prev->flags & EXPR_F_KERNEL)
+ expr_error(msgs, i, "interval overlaps with an existing one");
+ else if (elem->flags & EXPR_F_KERNEL)
+ expr_error(msgs, prev, "interval overlaps with an existing one");
+ else
+ expr_binary_error(msgs, i, prev,
+ "conflicting intervals specified");
+ err = -1;
+ goto err_out;
+ } else if (mpz_cmp(range.low, prev_range.high) <= 0) {
+ if (prev->flags & EXPR_F_KERNEL)
+ expr_error(msgs, i, "interval overlaps with an existing one");
+ else if (elem->flags & EXPR_F_KERNEL)
+ expr_error(msgs, prev, "interval overlaps with an existing one");
+ else
+ expr_binary_error(msgs, i, prev,
+ "conflicting intervals specified");
+ err = -1;
+ goto err_out;
+ }
+next:
+ prev = elem;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ }
+
+err_out:
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+
+ return err;
+}
+
+/* overlap detection for intervals already exists in Linux kernels >= 5.7. */
+int set_overlap(struct list_head *msgs, struct set *set, struct expr *init)
+{
+ struct set *existing_set = set->existing_set;
+ struct expr *i, *n, *clone;
+ int err;
+
+ set_sort_splice(init, set);
+
+ err = setelem_overlap(msgs, set, init);
+
+ list_for_each_entry_safe(i, n, &init->expressions, list) {
+ if (i->flags & EXPR_F_KERNEL)
+ list_move_tail(&i->list, &existing_set->init->expressions);
+ else if (existing_set) {
+ clone = expr_clone(i);
+ clone->flags |= EXPR_F_KERNEL;
+ list_add_tail(&clone->list, &existing_set->init->expressions);
+ }
+ }
+
+ return err;
+}
+
+static bool segtree_needs_first_segment(const struct set *set,
+ const struct expr *init, bool add)
+{
+ if (add && !set->root) {
+ /* Add the first segment in four situations:
+ *
+ * 1) This is an anonymous set.
+ * 2) This set exists and it is empty.
+ * 3) New empty set and, separately, new elements are added.
+ * 4) This set is created with a number of initial elements.
+ */
+ if ((set_is_anonymous(set->flags)) ||
+ (set->init && set->init->size == 0) ||
+ (set->init == NULL && init) ||
+ (set->init == init)) {
+ return true;
+ }
+ }
+ /* This is an update for a set that already contains elements, so don't
+ * add the first non-matching elements otherwise we hit EEXIST.
+ */
+ return false;
+}
+
+int set_to_intervals(const struct set *set, struct expr *init, bool add)
+{
+ struct expr *i, *n, *prev = NULL, *elem, *newelem = NULL, *root, *expr;
+ LIST_HEAD(intervals);
+ uint32_t flags;
+ mpz_t p, q;
+
+ mpz_init2(p, set->key->len);
+ mpz_init2(q, set->key->len);
+
+ 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)) {
+ 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);
+ root = set_elem_expr_alloc(&internal_location, expr);
+ if (i->etype == EXPR_MAPPING) {
+ root = mapping_expr_alloc(&internal_location,
+ root,
+ expr_get(i->right));
+ }
+ root->flags |= EXPR_F_INTERVAL_END;
+ list_add(&root->list, &intervals);
+ init->size++;
+ }
+
+ 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);
+
+ 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(&expr->location, expr);
+ if (i->etype == EXPR_MAPPING) {
+ newelem = mapping_expr_alloc(&expr->location,
+ newelem,
+ expr_get(i->right));
+ }
+ newelem->flags |= EXPR_F_INTERVAL_END;
+ } else {
+ flags = NFTNL_SET_ELEM_F_INTERVAL_OPEN;
+ }
+
+ expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
+ set->key->byteorder, set->key->len, NULL);
+
+ 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);
+
+ expr_free(elem->key);
+ elem->key = expr;
+ i->elem_flags |= flags;
+ init->size++;
+ list_move_tail(&i->list, &intervals);
+
+ prev = i;
+ }
+
+ if (newelem)
+ list_add_tail(&newelem->list, &intervals);
+
+ list_splice_init(&intervals, &init->expressions);
+
+ mpz_clear(p);
+ mpz_clear(q);
+
+ return 0;
+}
diff --git a/src/ipopt.c b/src/ipopt.c
index b3d0279d..37f779d4 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>
@@ -66,27 +67,8 @@ const struct exthdr_desc *ipopt_protocols[UINT8_MAX] = {
[IPOPT_RA] = &ipopt_ra,
};
-static unsigned int calc_offset(const struct exthdr_desc *desc,
- const struct proto_hdr_template *tmpl,
- unsigned int arg)
-{
- if (!desc || tmpl == &ipopt_unknown_template)
- return 0;
-
- switch (desc->type) {
- case IPOPT_RR:
- case IPOPT_LSRR:
- case IPOPT_SSRR:
- if (tmpl == &desc->templates[IPOPT_FIELD_ADDR_0])
- return (tmpl->offset < 24) ? 0 : arg;
- return 0;
- default:
- return 0;
- }
-}
-
struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
- uint8_t field, uint8_t ptr)
+ uint8_t field)
{
const struct proto_hdr_template *tmpl;
const struct exthdr_desc *desc;
@@ -97,12 +79,16 @@ struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
if (!tmpl)
return NULL;
+ if (!tmpl->len)
+ 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_IPV4;
- expr->exthdr.offset = calc_offset(desc, tmpl, ptr);
+ expr->exthdr.offset = tmpl->offset;
+ expr->exthdr.raw_type = desc->type;
return expr;
}
diff --git a/src/json.c b/src/json.c
index 888cb371..37530171 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1,11 +1,21 @@
-#define _GNU_SOURCE
-#include <string.h>
+/*
+ * 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 <expression.h>
#include <list.h>
#include <netlink.h>
#include <rule.h>
#include <rt.h>
+#include "nftutils.h"
#include <netdb.h>
#include <netinet/icmp6.h>
@@ -42,7 +52,8 @@ static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx)
if (ops->json)
return ops->json(expr, octx);
- printf("warning: expr ops %s have no json callback\n", expr_name(expr));
+ fprintf(stderr, "warning: expr ops %s have no json callback\n",
+ expr_name(expr));
fp = octx->output_fp;
octx->output_fp = fmemopen(buf, 1024, "w");
@@ -59,33 +70,75 @@ 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(xstrdup(tok));
+ json_t *jtok = json_string(tok);
if (!root)
root = jtok;
else if (json_is_string(root))
root = 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 *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ char buf[1024];
+ FILE *fp;
+
+ 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 *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 {
@@ -98,8 +151,11 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
"table", set->handle.table.name,
"type", set_dtype_json(set->key),
"handle", set->handle.handle.id);
+
+ if (set->comment)
+ json_object_set_new(root, "comment", json_string(set->comment));
if (datatype_ext)
- json_object_set_new(root, "map", json_string(datatype_ext));
+ json_object_set_new(root, "map", datatype_ext);
if (!(set->flags & (NFT_SET_CONSTANT))) {
if (set->policy != NFT_SET_POL_PERFORMANCE) {
@@ -119,6 +175,8 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_array_append_new(tmp, json_pack("s", "interval"));
if (set->flags & NFT_SET_TIMEOUT)
json_array_append_new(tmp, json_pack("s", "timeout"));
+ if (set->flags & NFT_SET_EVAL)
+ json_array_append_new(tmp, json_pack("s", "dynamic"));
if (json_array_size(tmp) > 0) {
json_object_set_new(root, "flags", tmp);
@@ -136,8 +194,10 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
tmp = 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 (set->init && set->init->size > 0) {
+ if (!nft_output_terse(octx) && set->init && set->init->size > 0) {
json_t *array = json_array();
const struct expr *i;
@@ -147,6 +207,11 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_object_set_new(root, "elem", array);
}
+ if (!list_empty(&set->stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&set->stmt_list, octx));
+ }
+
return json_pack("{s:o}", type, root);
}
@@ -163,34 +228,6 @@ static json_t *element_print_json(struct output_ctx *octx,
"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);
-
- printf("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)
{
@@ -222,9 +259,8 @@ static json_t *rule_print_json(struct output_ctx *octx,
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}",
"family", family2str(chain->handle.family),
@@ -232,28 +268,33 @@ static json_t *chain_print_json(const struct chain *chain)
"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}",
- "type", chain->type,
+ "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 = 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);
}
@@ -263,10 +304,10 @@ static json_t *chain_print_json(const struct chain *chain)
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);
}
@@ -300,6 +341,12 @@ static json_t *obj_print_json(const struct obj *obj)
"table", obj->handle.table.name,
"handle", obj->handle.handle.id);
+ if (obj->comment) {
+ tmp = 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}",
@@ -441,7 +488,7 @@ static json_t *table_flags_json(const struct table *table)
while (flags) {
if (flags & 0x1) {
- tmp = json_string(table_flags_name[i]);
+ tmp = json_string(table_flag_name(i));
json_array_append_new(root, tmp);
}
flags >>= 1;
@@ -452,7 +499,7 @@ static json_t *table_flags_json(const struct table *table)
json_decref(root);
return NULL;
case 1:
- json_unpack(root, "[o]", &tmp);
+ json_unpack(root, "[O]", &tmp);
json_decref(root);
root = tmp;
break;
@@ -473,14 +520,44 @@ static json_t *table_print_json(const struct table *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);
}
+json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx)
+{
+ json_t *left;
+
+ 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));
+}
+
+static json_t *
+__binop_expr_json(int op, const struct expr *expr, struct output_ctx *octx)
+{
+ json_t *a = json_array();
+
+ if (expr->etype == EXPR_BINOP && expr->op == op) {
+ json_array_extend(a, __binop_expr_json(op, expr->left, octx));
+ json_array_extend(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 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)
@@ -516,15 +593,23 @@ 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
+ if (payload_is_known(expr)) {
+ if (expr->payload.inner_desc) {
+ root = 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 = 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}",
"base", proto_base_tokens[expr->payload.base],
"offset", expr->payload.offset,
"len", expr->len);
+ }
return json_pack("{s:o}", "payload", root);
}
@@ -580,13 +665,15 @@ json_t *set_ref_expr_json(const struct expr *expr, struct output_ctx *octx)
json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
{
json_t *root = expr_print_json(expr->key, octx);
+ struct stmt *stmt;
json_t *tmp;
if (!root)
return NULL;
/* these element attributes require formal set elem syntax */
- if (expr->timeout || expr->expiration || expr->comment) {
+ if (expr->timeout || expr->expiration || expr->comment ||
+ !list_empty(&expr->stmt_list)) {
root = json_pack("{s:o}", "val", root);
if (expr->timeout) {
@@ -601,6 +688,14 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
tmp = json_string(expr->comment);
json_object_set_new(root, "comment", tmp);
}
+ list_for_each_entry(stmt, &expr->stmt_list, list) {
+ tmp = stmt_print_json(stmt, octx);
+ /* XXX: detect and complain about clashes? */
+ json_object_update_missing(root, tmp);
+ json_decref(tmp);
+ /* TODO: only one statement per element. */
+ break;
+ }
return json_pack("{s:o}", "elem", root);
}
@@ -656,47 +751,51 @@ json_t *map_expr_json(const struct expr *expr, struct output_ctx *octx)
json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx)
{
const char *desc = expr->exthdr.desc ?
- expr->exthdr.desc->name :
- "unknown-exthdr";
+ expr->exthdr.desc->name : NULL;
const char *field = expr->exthdr.tmpl->token;
json_t *root;
bool is_exists = expr->exthdr.flags & NFT_EXTHDR_F_PRESENT;
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
+ static const char *offstrs[] = { "", "1", "2", "3" };
unsigned int offset = expr->exthdr.offset / 64;
+ const char *offstr = "";
- if (offset) {
- const char *offstrs[] = { "0", "1", "2", "3" };
- const char *offstr = "";
-
+ if (desc) {
if (offset < 4)
offstr = offstrs[offset];
root = json_pack("{s:s+}", "name", desc, offstr);
+
+ if (!is_exists)
+ json_object_set_new(root, "field", json_string(field));
} else {
- root = json_pack("{s:s}", "name", desc);
+ root = json_pack("{s:i, s:i, s:i}",
+ "base", expr->exthdr.raw_type,
+ "offset", expr->exthdr.offset,
+ "len", expr->len);
}
- if (!is_exists)
- json_object_set_new(root, "field", json_string(field));
-
return json_pack("{s:o}", "tcp option", root);
}
- if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
- root = json_pack("{s:s}", "name", desc);
-
- if (!is_exists)
- json_object_set_new(root, "field", json_string(field));
- return json_pack("{s:o}", "ip option", root);
+ if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) {
+ root = json_pack("{s:i}", "type", expr->exthdr.raw_type);
+ return json_pack("{s:o}", "dccp option", root);
}
- root = json_pack("{s:s}",
- "name", desc);
+ root = json_pack("{s:s}", "name", desc);
if (!is_exists)
json_object_set_new(root, "field", json_string(field));
- return json_pack("{s:o}", "exthdr", root);
+ switch (expr->exthdr.op) {
+ case NFT_EXTHDR_OP_IPV4:
+ return json_pack("{s:o}", "ip option", root);
+ case NFT_EXTHDR_OP_SCTP:
+ return json_pack("{s:o}", "sctp chunk", root);
+ default:
+ return json_pack("{s:o}", "exthdr", root);
+ }
}
json_t *verdict_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -862,6 +961,11 @@ static json_t *symbolic_constant_json(const struct symbol_table *tbl,
return json_string(s->identifier);
}
+json_t *set_elem_catchall_expr_json(const struct expr *expr, struct output_ctx *octx)
+{
+ return json_string("*");
+}
+
static json_t *datatype_json(const struct expr *expr, struct output_ctx *octx)
{
const struct datatype *dtype = expr->dtype;
@@ -1009,12 +1113,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);
}
@@ -1022,13 +1125,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)
@@ -1089,6 +1192,13 @@ json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return expr_print_json(stmt->expr, octx);
}
+json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ return 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",
@@ -1153,19 +1263,17 @@ 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:s}",
+ root = json_pack("{s:I, s:I, s:s}",
"rate", rate,
+ "burst", burst,
"per", get_unit(stmt->limit.unit));
if (inv)
json_object_set_new(root, "inv", json_boolean(inv));
if (rate_unit)
json_object_set_new(root, "rate_unit", json_string(rate_unit));
- if (burst && burst != 5) {
- json_object_set_new(root, "burst", json_integer(burst));
- if (burst_unit)
- json_object_set_new(root, "burst_unit",
- json_string(burst_unit));
- }
+ if (burst_unit)
+ json_object_set_new(root, "burst_unit",
+ json_string(burst_unit));
return json_pack("{s:o}", "limit", root);
}
@@ -1279,7 +1387,7 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return json_pack("{s:o}", "log", root);
}
-static json_t *nat_flags_json(int flags)
+static json_t *nat_flags_json(uint32_t flags)
{
json_t *array = json_array();
@@ -1289,9 +1397,33 @@ static json_t *nat_flags_json(int flags)
json_array_append_new(array, json_string("fully-random"));
if (flags & NF_NAT_RANGE_PERSISTENT)
json_array_append_new(array, json_string("persistent"));
+ if (flags & NF_NAT_RANGE_NETMAP)
+ json_array_append_new(array, json_string("netmap"));
return array;
}
+static json_t *nat_type_flags_json(uint32_t type_flags)
+{
+ json_t *array = json_array();
+
+ if (type_flags & STMT_NAT_F_PREFIX)
+ json_array_append_new(array, json_string("prefix"));
+
+ 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();
@@ -1313,13 +1445,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));
- if (json_array_size(array) > 1) {
- json_object_set_new(root, "flags", array);
- } else {
- if (json_array_size(array))
- json_object_set(root, "flags",
- json_array_get(array, 0));
- json_decref(array);
+ nat_stmt_add_array(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);
}
if (!json_object_size(root)) {
@@ -1340,24 +1471,16 @@ json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
type = "tcp reset";
break;
case NFT_REJECT_ICMPX_UNREACH:
- if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)
- break;
type = "icmpx";
jexpr = expr_print_json(stmt->reject.expr, octx);
break;
case NFT_REJECT_ICMP_UNREACH:
switch (stmt->reject.family) {
case NFPROTO_IPV4:
- if (!stmt->reject.verbose_print &&
- stmt->reject.icmp_code == ICMP_PORT_UNREACH)
- break;
type = "icmp";
jexpr = expr_print_json(stmt->reject.expr, octx);
break;
case NFPROTO_IPV6:
- if (!stmt->reject.verbose_print &&
- stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
- break;
type = "icmpv6";
jexpr = expr_print_json(stmt->reject.expr, octx);
break;
@@ -1386,12 +1509,49 @@ json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
"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 json_pack("{s:n}", "last");
+
+ return 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 = 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 json_pack("{s:o}", "set", root);
+}
+
+json_t *map_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ json_t *root;
+
+ root = 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 json_pack("{s:o}", "map", root);
}
json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1525,10 +1685,29 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return 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",
+ 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 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)
{
- json_t *root = json_array(), *tmp;
+ json_t *root = json_array(), *rules = json_array(), *tmp;
struct flowtable *flowtable;
struct chain *chain;
struct rule *rule;
@@ -1538,30 +1717,35 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
tmp = table_print_json(table);
json_array_append_new(root, tmp);
- list_for_each_entry(obj, &table->objs, list) {
+ /* 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);
}
- list_for_each_entry(set, &table->sets, list) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
if (set_is_anonymous(set->flags))
continue;
tmp = set_print_json(&ctx->nft->output, set);
json_array_append_new(root, tmp);
}
- list_for_each_entry(flowtable, &table->flowtables, list) {
+ list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) {
tmp = flowtable_print_json(flowtable);
json_array_append_new(root, tmp);
}
- list_for_each_entry(chain, &table->chains, list) {
- tmp = chain_print_json(chain);
- json_array_append_new(root, tmp);
-
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
list_for_each_entry(rule, &chain->rules, list) {
tmp = rule_print_json(&ctx->nft->output, rule);
- json_array_append_new(root, tmp);
+ json_array_append_new(rules, tmp);
}
}
+ json_array_extend(root, rules);
+ json_decref(rules);
+
return root;
}
@@ -1571,7 +1755,7 @@ static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
json_t *root = json_array(), *tmp;
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (family != NFPROTO_UNSPEC &&
table->handle.family != family)
continue;
@@ -1590,7 +1774,7 @@ static json_t *do_list_tables_json(struct netlink_ctx *ctx, struct cmd *cmd)
json_t *root = json_array();
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (family != NFPROTO_UNSPEC &&
table->handle.family != family)
continue;
@@ -1614,7 +1798,7 @@ static json_t *do_list_chain_json(struct netlink_ctx *ctx,
struct chain *chain;
struct rule *rule;
- list_for_each_entry(chain, &table->chains, list) {
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
if (chain->handle.family != cmd->handle.family ||
strcmp(cmd->handle.chain.name, chain->handle.chain.name))
continue;
@@ -1637,12 +1821,12 @@ static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd)
struct table *table;
struct chain *chain;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
- list_for_each_entry(chain, &table->chains, list) {
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
json_t *tmp = chain_print_json(chain);
json_array_append_new(root, tmp);
@@ -1655,10 +1839,13 @@ 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_lookup(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));
}
@@ -1670,12 +1857,12 @@ static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd)
struct table *table;
struct set *set;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
- list_for_each_entry(set, &table->sets, list) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
if (cmd->obj == CMD_OBJ_SETS &&
!set_is_literal(set->flags))
continue;
@@ -1699,7 +1886,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx,
struct table *table;
struct obj *obj;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
@@ -1708,7 +1895,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx,
strcmp(cmd->handle.table.name, table->handle.table.name))
continue;
- list_for_each_entry(obj, &table->objs, list) {
+ list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
if (obj->type != type ||
(cmd->handle.obj.name &&
strcmp(cmd->handle.obj.name, obj->handle.obj.name)))
@@ -1727,8 +1914,8 @@ static json_t *do_list_flowtable_json(struct netlink_ctx *ctx,
json_t *root = json_array();
struct flowtable *ft;
- ft = flowtable_lookup(table, cmd->handle.flowtable.name);
- if (ft == NULL)
+ ft = ft_cache_find(table, cmd->handle.flowtable.name);
+ if (!ft)
return json_null();
json_array_append_new(root, flowtable_print_json(ft));
@@ -1742,12 +1929,12 @@ static json_t *do_list_flowtables_json(struct netlink_ctx *ctx, struct cmd *cmd)
struct flowtable *flowtable;
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
- list_for_each_entry(flowtable, &table->flowtables, list) {
+ list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) {
tmp = flowtable_print_json(flowtable);
json_array_append_new(root, tmp);
}
@@ -1770,7 +1957,9 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
json_t *root;
if (cmd->handle.table.name)
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
switch (cmd->obj) {
case CMD_OBJ_TABLE:
@@ -1792,6 +1981,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;
@@ -1857,9 +2047,15 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
static void monitor_print_json(struct netlink_mon_handler *monh,
const char *cmd, json_t *obj)
{
+ struct nft_ctx *nft = monh->ctx->nft;
+
obj = json_pack("{s:o}", cmd, obj);
- json_dumpf(obj, monh->ctx->nft->output.output_fp, 0);
- json_decref(obj);
+ if (nft_output_echo(&nft->output) && !nft->json_root) {
+ json_array_append_new(nft->json_echo, obj);
+ } else {
+ json_dumpf(obj, nft->output.output_fp, 0);
+ json_decref(obj);
+ }
}
void monitor_print_table_json(struct netlink_mon_handler *monh,
@@ -1903,3 +2099,10 @@ void monitor_print_rule_json(struct netlink_mon_handler *monh,
monitor_print_json(monh, cmd, rule_print_json(octx, r));
}
+
+void json_alloc_echo(struct nft_ctx *nft)
+{
+ nft->json_echo = json_array();
+ if (!nft->json_echo)
+ memory_allocation_error();
+}
diff --git a/src/libnftables.c b/src/libnftables.c
index 668e3fc4..0dee1bac 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,14 +16,12 @@
#include <iface.h>
#include <cmd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
+#include <sys/stat.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, num_cmds = 0;
+ uint32_t batch_seqnum, seqnum = 0, last_seqnum = UINT32_MAX, num_cmds = 0;
struct netlink_ctx ctx = {
.nft = nft,
.msgs = msgs,
@@ -56,6 +56,13 @@ static int nft_netlink(struct nft_ctx *nft,
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;
@@ -65,7 +72,14 @@ static int nft_netlink(struct nft_ctx *nft,
ret = -1;
list_for_each_entry_safe(err, tmp, &err_list, head) {
- list_for_each_entry(cmd, cmds, list) {
+ /* cmd seqnums are monotonic: only reset the starting position
+ * if the error seqnum is lower than the previous one.
+ */
+ if (err->seqnum < last_seqnum)
+ 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 ||
err->seqnum == batch_seqnum) {
nft_cmd_error(&ctx, cmd, err);
@@ -76,7 +90,18 @@ static int nft_netlink(struct nft_ctx *nft,
}
}
}
+
+ if (&cmd->list == cmds) {
+ /* not found, rewind */
+ last_seqnum = UINT32_MAX;
+ }
}
+ /* nfnetlink uses the first netlink message header in the batch whose
+ * sequence number is zero to report for EOPNOTSUPP and EPERM errors in
+ * some scenarios. Now it is safe to release pending errors here.
+ */
+ list_for_each_entry_safe(err, tmp, &err_list, head)
+ mnl_err_list_free(err);
out:
mnl_batch_reset(ctx.batch);
return ret;
@@ -92,21 +117,57 @@ static void nft_init(struct nft_ctx *ctx)
static void nft_exit(struct nft_ctx *ctx)
{
+ cache_free(&ctx->cache.table_cache);
ct_label_table_exit(ctx);
realm_table_rt_exit(ctx);
devgroup_table_exit(ctx);
mark_table_exit(ctx);
}
+EXPORT_SYMBOL(nft_ctx_add_var);
+int nft_ctx_add_var(struct nft_ctx *ctx, const char *var)
+{
+ char *separator = strchr(var, '=');
+ int pcount = ctx->num_vars;
+ struct nft_vars *tmp;
+ const char *value;
+
+ if (!separator)
+ return -1;
+
+ tmp = xrealloc(ctx->vars, (pcount + 1) * sizeof(struct nft_vars));
+
+ *separator = '\0';
+ value = separator + 1;
+
+ ctx->vars = tmp;
+ ctx->vars[pcount].key = xstrdup(var);
+ ctx->vars[pcount].value = xstrdup(value);
+ ctx->num_vars++;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(nft_ctx_clear_vars);
+void nft_ctx_clear_vars(struct nft_ctx *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ctx->num_vars; i++) {
+ free_const(ctx->vars[i].key);
+ free_const(ctx->vars[i].value);
+ }
+ ctx->num_vars = 0;
+ free(ctx->vars);
+}
+
EXPORT_SYMBOL(nft_ctx_add_include_path);
int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
{
char **tmp;
int pcount = ctx->num_include_paths;
- tmp = realloc(ctx->include_paths, (pcount + 1) * sizeof(char *));
- if (!tmp)
- return -1;
+ tmp = xrealloc(ctx->include_paths, (pcount + 1) * sizeof(char *));
ctx->include_paths = tmp;
@@ -121,30 +182,20 @@ 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);
@@ -152,14 +203,14 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
ctx->state = xzalloc(sizeof(struct parser_state));
nft_ctx_add_include_path(ctx, DEFAULT_INCLUDE_PATH);
ctx->parser_max_errors = 10;
- init_list_head(&ctx->cache.list);
+ cache_init(&ctx->cache.table_cache);
ctx->top_scope = scope_alloc();
ctx->flags = flags;
ctx->output.output_fp = stdout;
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;
}
@@ -283,18 +334,18 @@ 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);
iface_cache_release();
- cache_release(&ctx->cache);
+ nft_cache_release(&ctx->cache);
+ 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);
@@ -335,6 +386,34 @@ void nft_ctx_set_dry_run(struct nft_ctx *ctx, bool dry)
ctx->check = dry;
}
+EXPORT_SYMBOL(nft_ctx_get_optimize);
+uint32_t nft_ctx_get_optimize(struct nft_ctx *ctx)
+{
+ return ctx->optimize_flags;
+}
+
+EXPORT_SYMBOL(nft_ctx_set_optimize);
+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)
{
@@ -364,13 +443,14 @@ static const struct input_descriptor indesc_cmdline = {
};
static int nft_parse_bison_buffer(struct nft_ctx *nft, const char *buf,
- struct list_head *msgs, struct list_head *cmds)
+ struct list_head *msgs, struct list_head *cmds,
+ const struct input_descriptor *indesc)
{
int ret;
parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->scanner = scanner_init(nft->state);
- scanner_push_buffer(nft->scanner, &indesc_cmdline, buf);
+ scanner_push_buffer(nft->scanner, indesc, buf);
ret = nft_parse(nft, nft->scanner, nft->state);
if (ret != 0 || nft->state->nerrs > 0)
@@ -379,11 +459,42 @@ static int nft_parse_bison_buffer(struct nft_ctx *nft, const char *buf,
return 0;
}
+static char *stdin_to_buffer(void)
+{
+ unsigned int bufsiz = 16384, consumed = 0;
+ int numbytes;
+ char *buf;
+
+ buf = xmalloc(bufsiz);
+
+ numbytes = read(STDIN_FILENO, buf, bufsiz);
+ while (numbytes > 0) {
+ consumed += numbytes;
+ if (consumed == bufsiz) {
+ bufsiz *= 2;
+ buf = xrealloc(buf, bufsiz);
+ }
+ numbytes = read(STDIN_FILENO, buf + consumed, bufsiz - consumed);
+ }
+ buf[consumed] = '\0';
+
+ return buf;
+}
+
+static const struct input_descriptor indesc_stdin = {
+ .type = INDESC_STDIN,
+ .name = "/dev/stdin",
+};
+
static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename,
struct list_head *msgs, struct list_head *cmds)
{
int ret;
+ if (nft->stdin_buf)
+ return nft_parse_bison_buffer(nft, nft->stdin_buf, msgs, cmds,
+ &indesc_stdin);
+
parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->scanner = scanner_init(nft->state);
if (scanner_read_file(nft, filename, &internal_location) < 0)
@@ -399,32 +510,53 @@ static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename,
static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
struct list_head *cmds)
{
+ struct nft_cache_filter *filter;
+ struct cmd *cmd, *next;
+ bool collapsed = false;
unsigned int flags;
- struct cmd *cmd;
+ int err = 0;
- flags = cache_evaluate(nft, cmds);
- if (cache_update(nft, flags, msgs) < 0)
+ filter = nft_cache_filter_init();
+ if (nft_cache_evaluate(nft, cmds, msgs, filter, &flags) < 0) {
+ nft_cache_filter_fini(filter);
return -1;
+ }
+ if (nft_cache_update(nft, flags, msgs, filter) < 0) {
+ nft_cache_filter_fini(filter);
+ return -1;
+ }
+
+ 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 = {
.nft = nft,
.msgs = msgs,
};
+
if (cmd_evaluate(&ectx, cmd) < 0 &&
- ++nft->state->nerrs == nft->parser_max_errors)
- return -1;
+ ++nft->state->nerrs == nft->parser_max_errors) {
+ err = -1;
+ break;
+ }
}
- if (nft->state->nerrs)
- return -1;
-
- list_for_each_entry(cmd, cmds, list) {
- if (cmd->op != CMD_ADD)
- continue;
+ if (collapsed)
+ nft_cmd_uncollapse(cmds);
- nft_cmd_expand(cmd);
- }
+ if (err < 0 || nft->state->nerrs)
+ return -1;
return 0;
}
@@ -441,23 +573,29 @@ 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);
+ rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds,
+ &indesc_cmdline);
parser_rc = rc;
rc = nft_evaluate(nft, &msgs, &cmds);
- if (rc < 0)
+ if (rc < 0) {
+ if (errno == EPERM) {
+ fprintf(stderr, "%s (you must be root)\n",
+ strerror(errno));
+ }
goto err;
+ }
if (parser_rc) {
rc = parser_rc;
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);
@@ -476,30 +614,109 @@ err:
nft_output_json(&nft->output) &&
nft_output_echo(&nft->output))
json_print_echo(nft);
- if (rc)
- cache_release(&nft->cache);
+
+ if (rc || nft->check)
+ nft_cache_release(&nft->cache);
+
return rc;
}
-EXPORT_SYMBOL(nft_run_cmd_from_filename);
-int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
+static int load_cmdline_vars(struct nft_ctx *ctx, struct list_head *msgs)
{
+ unsigned int bufsize, ret, i, offset = 0;
+ LIST_HEAD(cmds);
+ char *buf;
+ int rc;
+
+ if (ctx->num_vars == 0)
+ return 0;
+
+ bufsize = 1024;
+ buf = xzalloc(bufsize + 1);
+ for (i = 0; i < ctx->num_vars; i++) {
+retry:
+ ret = snprintf(buf + offset, bufsize - offset,
+ "define %s=%s; ",
+ ctx->vars[i].key, ctx->vars[i].value);
+ if (ret >= bufsize - offset) {
+ bufsize *= 2;
+ buf = xrealloc(buf, bufsize + 1);
+ goto retry;
+ }
+ offset += ret;
+ }
+ snprintf(buf + offset, bufsize - offset, "\n");
+
+ rc = nft_parse_bison_buffer(ctx, buf, msgs, &cmds, &indesc_cmdline);
+
+ assert(list_empty(&cmds));
+ /* Stash the buffer that contains the variable definitions and zap the
+ * list of input descriptors before releasing the scanner state,
+ * otherwise error reporting path walks over released objects.
+ */
+ ctx->vars_ctx.buf = buf;
+ list_splice_init(&ctx->state->indesc_list, &ctx->vars_ctx.indesc_list);
+ scanner_destroy(ctx);
+ ctx->scanner = NULL;
+
+ return rc;
+}
+
+/* need to use stat() to, fopen() will block for named fifos and
+ * libjansson makes no checks before or after open either.
+ */
+static struct error_record *filename_is_useable(struct nft_ctx *nft, const char *name)
+{
+ unsigned int type;
+ struct stat sb;
+ int err;
+
+ 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;
+
+ if (type == S_IFCHR && 0 == strcmp(name, "/dev/stdin"))
+ 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);
- if (!strcmp(filename, "-"))
- filename = "/dev/stdin";
+ 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);
parser_rc = rc;
+ if (nft->optimize_flags)
+ nft_optimize(nft, &cmds);
+
rc = nft_evaluate(nft, &msgs, &cmds);
if (rc < 0)
goto err;
@@ -509,7 +726,7 @@ 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);
@@ -522,12 +739,73 @@ err:
scanner_destroy(nft);
nft->scanner = NULL;
}
+ if (!list_empty(&nft->vars_ctx.indesc_list)) {
+ struct input_descriptor *indesc, *next;
+
+ list_for_each_entry_safe(indesc, next, &nft->vars_ctx.indesc_list, list) {
+ if (indesc->name)
+ free_const(indesc->name);
+
+ free(indesc);
+ }
+ }
+ free_const(nft->vars_ctx.buf);
if (!rc &&
nft_output_json(&nft->output) &&
nft_output_echo(&nft->output))
json_print_echo(nft);
- if (rc)
- cache_release(&nft->cache);
+
+ if (rc || nft->check)
+ nft_cache_release(&nft->cache);
+
+ scope_release(nft->state->scopes[0]);
+
return rc;
}
+
+static int nft_run_optimized_file(struct nft_ctx *nft, const char *filename)
+{
+ uint32_t optimize_flags;
+ bool check;
+ int ret;
+
+ check = nft->check;
+ nft->check = true;
+ optimize_flags = nft->optimize_flags;
+ nft->optimize_flags = 0;
+
+ /* First check the original ruleset loads fine as is. */
+ ret = __nft_run_cmd_from_filename(nft, filename);
+ if (ret < 0)
+ return ret;
+
+ nft->check = check;
+ nft->optimize_flags = optimize_flags;
+
+ return __nft_run_cmd_from_filename(nft, filename);
+}
+
+EXPORT_SYMBOL(nft_run_cmd_from_filename);
+int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
+{
+ int ret;
+
+ if (!strcmp(filename, "-"))
+ filename = "/dev/stdin";
+
+ if (!strcmp(filename, "/dev/stdin") &&
+ !nft_output_json(&nft->output))
+ nft->stdin_buf = stdin_to_buffer();
+
+ if (nft->optimize_flags) {
+ ret = nft_run_optimized_file(nft, filename);
+ free_const(nft->stdin_buf);
+ return ret;
+ }
+
+ ret = __nft_run_cmd_from_filename(nft, filename);
+ free_const(nft->stdin_buf);
+
+ return ret;
+}
diff --git a/src/libnftables.map b/src/libnftables.map
index 955af380..9369f44f 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -1,7 +1,7 @@
LIBNFTABLES_1 {
global:
nft_ctx_add_include_path;
- nft_ctx_clear_include_pat;
+ nft_ctx_clear_include_paths;
nft_ctx_new;
nft_ctx_buffer_output;
nft_ctx_unbuffer_output;
@@ -23,3 +23,18 @@ global:
local: *;
};
+
+LIBNFTABLES_2 {
+ nft_ctx_add_var;
+ nft_ctx_clear_vars;
+} LIBNFTABLES_1;
+
+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 3c26f510..d3491cda 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>
@@ -32,10 +32,12 @@ enum opt_indices {
/* Ruleset input handling */
IDX_FILE,
#define IDX_RULESET_INPUT_START IDX_FILE
+ IDX_DEFINE,
IDX_INTERACTIVE,
IDX_INCLUDEPATH,
IDX_CHECK,
-#define IDX_RULESET_INPUT_END IDX_CHECK
+ IDX_OPTIMIZE,
+#define IDX_RULESET_INPUT_END IDX_OPTIMIZE
/* Ruleset list formatting */
IDX_HANDLE,
#define IDX_RULESET_LIST_START IDX_HANDLE
@@ -63,6 +65,7 @@ enum opt_vals {
OPT_VERSION_LONG = 'V',
OPT_CHECK = 'c',
OPT_FILE = 'f',
+ OPT_DEFINE = 'D',
OPT_INTERACTIVE = 'i',
OPT_INCLUDEPATH = 'I',
OPT_JSON = 'j',
@@ -78,6 +81,7 @@ enum opt_vals {
OPT_NUMERIC_PROTO = 'p',
OPT_NUMERIC_TIME = 'T',
OPT_TERSE = 't',
+ OPT_OPTIMIZE = 'o',
OPT_INVALID = '?',
};
@@ -100,6 +104,8 @@ static const struct nft_opt nft_options[] = {
"Show extended version information"),
[IDX_FILE] = NFT_OPT("file", OPT_FILE, "<filename>",
"Read input from <filename>"),
+ [IDX_DEFINE] = NFT_OPT("define", OPT_DEFINE, "<name=value>",
+ "Define variable, e.g. --define foo=1.2.3.4"),
[IDX_INTERACTIVE] = NFT_OPT("interactive", OPT_INTERACTIVE, NULL,
"Read input from interactive CLI"),
[IDX_INCLUDEPATH] = NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>",
@@ -132,6 +138,8 @@ static const struct nft_opt nft_options[] = {
"Format output in JSON"),
[IDX_DEBUG] = NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>",
"Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
+ [IDX_OPTIMIZE] = NFT_OPT("optimize", OPT_OPTIMIZE, NULL,
+ "Optimize ruleset"),
};
#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0]))
@@ -175,16 +183,17 @@ static const struct option *get_options(void)
static void print_option(const struct nft_opt *opt)
{
- char optbuf[33] = "";
+ char optbuf[35] = "";
int i;
i = snprintf(optbuf, sizeof(optbuf), " -%c", opt->val);
if (opt->name)
- i += snprintf(optbuf + i, sizeof(optbuf) - i, ", %s", opt->name);
+ i += snprintf(optbuf + i, sizeof(optbuf) - i, ", --%s",
+ opt->name);
if (opt->arg)
i += snprintf(optbuf + i, sizeof(optbuf) - i, " %s", opt->arg);
- printf("%-32s%s\n", optbuf, opt->help);
+ printf("%-34s%s\n", optbuf, opt->help);
}
static void show_help(const char *name)
@@ -229,6 +238,8 @@ static void show_version(void)
#if defined(HAVE_LIBREADLINE)
cli = "readline";
+#elif defined(HAVE_LIBEDIT)
+ cli = "editline";
#elif defined(HAVE_LIBLINENOISE)
cli = "linenoise";
#else
@@ -314,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;
@@ -322,21 +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' ||
- !strcmp(argv[i], "--debug") ||
- !strcmp(argv[i], "--includepath") ||
- !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;
+ }
}
}
@@ -346,13 +359,17 @@ static bool nft_options_check(int argc, char * const argv[])
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;
- bool interactive = false;
+ 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())
+ _exit(111);
if (!nft_options_check(argc, argv))
exit(EXIT_FAILURE);
@@ -367,21 +384,40 @@ 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",
+ optarg);
+ goto out_fail;
+ }
+ define = true;
+ break;
case OPT_CHECK:
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:
@@ -389,7 +425,7 @@ int main(int argc, char * const *argv)
fprintf(stderr,
"Failed to add include path '%s'\n",
optarg);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
break;
case OPT_NUMERIC:
@@ -424,7 +460,7 @@ int main(int argc, char * const *argv)
if (i == array_size(debug_param)) {
fprintf(stderr, "invalid debug parameter `%s'\n",
optarg);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
if (end == NULL)
@@ -444,7 +480,7 @@ int main(int argc, char * const *argv)
output_flags |= NFT_CTX_OUTPUT_JSON;
#else
fprintf(stderr, "JSON support not compiled-in\n");
- exit(EXIT_FAILURE);
+ goto out_fail;
#endif
break;
case OPT_GUID:
@@ -462,14 +498,24 @@ int main(int argc, char * const *argv)
case OPT_TERSE:
output_flags |= NFT_CTX_OUTPUT_TERSE;
break;
+ case OPT_OPTIMIZE:
+ 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");
+ 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(" ");
@@ -477,7 +523,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]);
@@ -485,22 +531,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 649b7806..5e676be1 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -2,53 +2,75 @@
* 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 int expr_msort_cmp(const struct expr *e1, const struct expr *e2);
-
-static int concat_expr_msort_cmp(const struct expr *e1, const struct expr *e2)
+static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
{
- struct list_head *l = (&e2->expressions)->next;
- const struct expr *i1, *i2;
- int ret;
-
- list_for_each_entry(i1, &e1->expressions, list) {
- i2 = list_entry(l, typeof(struct expr), list);
-
- ret = expr_msort_cmp(i1, i2);
- if (ret)
- return ret;
-
- l = l->next;
+ unsigned int len = 0, ilen;
+ const struct expr *i;
+ char data[512];
+
+ list_for_each_entry(i, &expr->expressions, list) {
+ ilen = div_round_up(i->len, BITS_PER_BYTE);
+ mpz_export_data(data + len, i->value, i->byteorder, ilen);
+ len += ilen;
}
- return false;
+ mpz_import_data(value, data, BYTEORDER_HOST_ENDIAN, len);
}
-static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
+static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value)
{
- switch (e1->etype) {
+ switch (expr->etype) {
case EXPR_SET_ELEM:
- return expr_msort_cmp(e1->key, e2->key);
+ return expr_msort_value(expr->key, value);
+ case EXPR_BINOP:
+ case EXPR_MAPPING:
+ case EXPR_RANGE:
+ return expr_msort_value(expr->left, value);
case EXPR_VALUE:
- return mpz_cmp(e1->value, e2->value);
+ return expr->value;
case EXPR_CONCAT:
- return concat_expr_msort_cmp(e1, e2);
- case EXPR_MAPPING:
- return expr_msort_cmp(e1->left, e2->left);
+ concat_expr_msort_value(expr, value);
+ break;
+ case EXPR_SET_ELEM_CATCHALL:
+ /* max value to ensure listing shows it in the last position */
+ mpz_bitmask(value, expr->len);
+ break;
default:
- BUG("Unknown expression %s\n", expr_name(e1));
+ BUG("Unknown expression %s\n", expr_name(expr));
}
+ return value;
+}
+
+static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
+{
+ mpz_srcptr value1;
+ mpz_srcptr value2;
+ mpz_t value1_tmp;
+ mpz_t value2_tmp;
+ int ret;
+
+ 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_tmp);
+ mpz_clear(value2_tmp);
+
+ return ret;
}
-static void list_splice_sorted(struct list_head *list, struct list_head *head)
+void list_splice_sorted(struct list_head *list, struct list_head *head)
{
struct list_head *h = head->next;
struct list_head *l = list->next;
@@ -56,7 +78,7 @@ static 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 d92d0d32..a17bacf0 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);
}
@@ -220,18 +205,20 @@ static struct error_record *uid_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct passwd *pw;
- uint64_t uid;
+ uid_t uid;
char *endptr = NULL;
pw = getpwnam(sym->identifier);
if (pw != NULL)
uid = pw->pw_uid;
else {
- uid = strtoull(sym->identifier, &endptr, 10);
- if (uid > UINT32_MAX)
+ uint64_t _uid = strtoull(sym->identifier, &endptr, 10);
+
+ if (_uid > UINT32_MAX)
return error(&sym->location, "Value too large");
else if (*endptr)
return error(&sym->location, "User does not exist");
+ uid = _uid;
}
*res = constant_expr_alloc(&sym->location, sym->dtype,
@@ -274,18 +261,20 @@ static struct error_record *gid_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct group *gr;
- uint64_t gid;
+ gid_t gid;
char *endptr = NULL;
gr = getgrnam(sym->identifier);
if (gr != NULL)
gid = gr->gr_gid;
else {
- gid = strtoull(sym->identifier, &endptr, 0);
- if (gid > UINT32_MAX)
+ uint64_t _gid = strtoull(sym->identifier, &endptr, 0);
+
+ if (_gid > UINT32_MAX)
return error(&sym->location, "Value too large");
else if (*endptr)
return error(&sym->location, "Group does not exist");
+ gid = _gid;
}
*res = constant_expr_alloc(&sym->location, sym->dtype,
@@ -336,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)
@@ -357,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 = {
@@ -381,51 +376,43 @@ 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;
-
- if (!nft_output_seconds(octx)) {
- /* Obtain current tm, to add tm_gmtoff to the timestamp */
- cur_tm = localtime((time_t *) &tstamp);
-
- if (cur_tm)
- tstamp += cur_tm->tm_gmtoff;
+ tstamp64 /= 1000000000L;
- if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
- strftime(timestr, sizeof(timestr) - 1, "%F %T", tm))
- nft_print(octx, "\"%s\"", timestr);
- else
- nft_print(octx, "Error converting timestamp to printed time");
-
- return;
- }
+ /* Obtain current tm, to add tm_gmtoff to the timestamp */
+ tstamp = tstamp64;
+ if (localtime_r(&tstamp, &tm))
+ tstamp64 += tm.tm_gmtoff;
- /*
- * Do our own printing. The default print function will print in
- * nanoseconds, which is ugly.
- */
- nft_print(octx, "%lu", tstamp);
+ 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");
}
-static time_t parse_iso_date(const char *sym)
+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));
- if (strptime(sym, "%F %T", &tm))
+ if (strptime(sym, "%Y-%m-%d %T", &tm))
goto success;
- if (strptime(sym, "%F %R", &tm))
+ if (strptime(sym, "%Y-%m-%d %R", &tm))
goto success;
- if (strptime(sym, "%F", &tm))
+ if (strptime(sym, "%Y-%m-%d", &tm))
goto success;
- return -1;
+ return false;
success:
/*
@@ -435,14 +422,17 @@ 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 */
- return ts - cur_tm->tm_gmtoff;
+ *tstamp = ts - cur_tm.tm_gmtoff;
+
+ return true;
}
static struct error_record *date_type_parse(struct parse_ctx *ctx,
@@ -450,9 +440,9 @@ static struct error_record *date_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
const char *endptr = sym->identifier;
- time_t tstamp;
+ uint64_t tstamp;
- if ((tstamp = parse_iso_date(sym->identifier)) != -1)
+ if (parse_iso_date(&tstamp, sym->identifier))
goto success;
tstamp = strtoul(sym->identifier, (char **) &endptr, 10);
@@ -495,21 +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;
- if (nft_output_seconds(octx)) {
- expr_basetype(expr)->print(expr, octx);
- return;
- }
-
/* 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;
@@ -526,9 +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;
- uint64_t result = 0;
+ 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));
@@ -542,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')
@@ -555,8 +551,8 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
if (endptr && *endptr)
return error(&sym->location, "Can't parse trailing input: \"%s\"\n", endptr);
- if ((er = time_parse(&sym->location, sym->identifier, &result)) == NULL) {
- result /= 1000;
+ if ((er = time_parse(&sym->location, sym->identifier, &tmp)) == NULL) {
+ result = tmp / 1000;
goto convert;
}
@@ -610,7 +606,7 @@ const struct datatype hour_type = {
.name = "hour",
.desc = "Hour of day of packet reception",
.byteorder = BYTEORDER_HOST_ENDIAN,
- .size = sizeof(uint64_t) * BITS_PER_BYTE,
+ .size = sizeof(uint32_t) * BITS_PER_BYTE,
.basetype = &integer_type,
.print = hour_type_print,
.parse = hour_type_parse,
@@ -706,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)
@@ -725,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)
@@ -742,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;
}
/**
@@ -753,10 +756,11 @@ static void meta_expr_clone(struct expr *new, const struct expr *expr)
* Update LL protocol context based on IIFTYPE meta match in non-LL hooks.
*/
static void meta_expr_pctx_update(struct proto_ctx *ctx,
- const struct expr *expr)
+ const struct location *loc,
+ const struct expr *left,
+ const struct expr *right)
{
const struct hook_proto_desc *h = &hook_proto_desc[ctx->family];
- const struct expr *left = expr->left, *right = expr->right;
const struct proto_desc *desc;
uint8_t protonum;
@@ -771,10 +775,15 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
if (desc == NULL)
desc = &proto_unknown;
- proto_ctx_update(ctx, PROTO_BASE_LL_HDR, &expr->location, desc);
+ proto_ctx_update(ctx, PROTO_BASE_LL_HDR, loc, desc);
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;
@@ -784,7 +793,7 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
desc = h->desc;
}
- proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, &expr->location, desc);
+ proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, loc, desc);
break;
case NFT_META_L4PROTO:
desc = proto_find_upper(&proto_inet_service,
@@ -792,7 +801,7 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
if (desc == NULL)
desc = &proto_unknown;
- proto_ctx_update(ctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, desc);
+ proto_ctx_update(ctx, PROTO_BASE_TRANSPORT_HDR, loc, desc);
break;
case NFT_META_PROTOCOL:
if (h->base != PROTO_BASE_LL_HDR)
@@ -806,7 +815,7 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
if (desc == NULL)
desc = &proto_unknown;
- proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, &expr->location, desc);
+ proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, loc, desc);
break;
default:
break;
@@ -814,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;
}
@@ -832,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;
@@ -846,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;
@@ -859,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 = {
@@ -908,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);
}
@@ -938,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;
}
@@ -967,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))
@@ -994,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;
@@ -1004,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 e5e88f3b..9e4bfcd9 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -2,12 +2,14 @@
* 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 <libmnl/libmnl.h>
#include <libnftnl/common.h>
#include <libnftnl/ruleset.h>
@@ -22,18 +24,32 @@
#include <libnftnl/udata.h>
#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_hook.h>
#include <linux/netfilter/nf_tables.h>
#include <mnl.h>
-#include <string.h>
+#include <cmd.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>
+#include <linux/netfilter_arp.h>
+
+struct basehook {
+ struct list_head list;
+ const char *module_name;
+ const char *hookfn;
+ const char *table;
+ const char *chain;
+ int family;
+ int chain_family;
+ uint32_t num;
+ int prio;
+};
struct mnl_socket *nft_mnl_socket_open(void)
{
@@ -52,13 +68,6 @@ struct mnl_socket *nft_mnl_socket_open(void)
return nf_sock;
}
-struct mnl_socket *nft_mnl_socket_reopen(struct mnl_socket *nf_sock)
-{
- mnl_socket_close(nf_sock);
-
- return nft_mnl_socket_open();
-}
-
uint32_t mnl_seqnum_alloc(unsigned int *seqnum)
{
return (*seqnum)++;
@@ -77,20 +86,31 @@ nft_mnl_recv(struct netlink_ctx *ctx, uint32_t portid,
int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data)
{
char buf[NFT_NLMSG_MAXSIZE];
+ bool eintr = false;
int ret;
ret = mnl_socket_recvfrom(ctx->nft->nf_sock, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, ctx->seqnum, portid, cb, cb_data);
- if (ret <= 0)
- goto out;
+ if (ret == 0)
+ break;
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ ret = 0;
+ break;
+ }
+ if (errno != EINTR)
+ break;
+ /* process all pending messages before reporting EINTR */
+ eintr = true;
+ }
ret = mnl_socket_recvfrom(ctx->nft->nf_sock, buf, sizeof(buf));
}
-out:
- if (ret < 0 && errno == EAGAIN)
- return 0;
-
+ if (eintr) {
+ ret = -1;
+ errno = EINTR;
+ }
return ret;
}
@@ -156,11 +176,11 @@ static int check_genid(const struct nlmsghdr *nlh)
* Batching
*/
-/* selected batch page is 256 Kbytes long to load ruleset of
- * half a million rules without hitting -EMSGSIZE due to large
- * iovec.
+/* Selected batch page is 2 Mbytes long to support loading a ruleset of 3.5M
+ * rules matching on source and destination address as well as input and output
+ * interfaces. This is what legacy iptables supports.
*/
-#define BATCH_PAGE_SIZE getpagesize() * 32
+#define BATCH_PAGE_SIZE 2 * 1024 * 1024
struct nftnl_batch *mnl_batch_init(void)
{
@@ -222,12 +242,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;
@@ -240,9 +261,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;
@@ -273,24 +300,16 @@ static int mnl_set_rcvbuffer(const struct mnl_socket *nl, socklen_t bufsiz)
return ret;
}
-static size_t mnl_nft_batch_to_msg(struct netlink_ctx *ctx, struct msghdr *msg,
- const struct sockaddr_nl *snl,
- struct iovec *iov, unsigned int iov_len)
+static void mnl_nft_batch_to_msg(struct netlink_ctx *ctx, struct msghdr *msg,
+ const struct sockaddr_nl *snl,
+ struct iovec *iov, unsigned int iov_len)
{
- unsigned int i;
- size_t len = 0;
-
msg->msg_name = (struct sockaddr_nl *)snl;
msg->msg_namelen = sizeof(*snl);
msg->msg_iov = iov;
msg->msg_iovlen = iov_len;
nftnl_batch_iovec(ctx->batch, iov, iov_len);
-
- for (i = 0; i < iov_len; i++)
- len += msg->msg_iov[i].iov_len;
-
- return len;
}
static ssize_t mnl_nft_socket_sendmsg(struct netlink_ctx *ctx,
@@ -366,7 +385,10 @@ static int mnl_batch_extack_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_ERROR;
}
-#define NFT_MNL_ECHO_RCVBUFF_DEFAULT (MNL_SOCKET_BUFFER_SIZE * 1024)
+#define NFT_MNL_ECHO_RCVBUFF_DEFAULT (MNL_SOCKET_BUFFER_SIZE * 1024U)
+#define NFT_MNL_ACK_MAXSIZE ((sizeof(struct nlmsghdr) + \
+ sizeof(struct nfgenmsg) + (1 << 16)) + \
+ MNL_SOCKET_BUFFER_SIZE)
int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
uint32_t num_cmds)
@@ -374,7 +396,7 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
struct mnl_socket *nl = ctx->nft->nf_sock;
int ret, fd = mnl_socket_get_fd(nl), portid = mnl_socket_get_portid(nl);
uint32_t iov_len = nftnl_batch_iovec_len(ctx->batch);
- char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
+ char rcv_buf[NFT_MNL_ACK_MAXSIZE];
const struct sockaddr_nl snl = {
.nl_family = AF_NETLINK
};
@@ -385,7 +407,6 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
struct iovec iov[iov_len];
struct msghdr msg = {};
unsigned int rcvbufsiz;
- size_t batch_size;
fd_set readfds;
static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = {
[NLMSG_ERROR] = mnl_batch_extack_cb,
@@ -395,16 +416,14 @@ 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);
- batch_size = mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
+ mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
+ rcvbufsiz = num_cmds * 1024;
if (nft_output_echo(&ctx->nft->output)) {
- rcvbufsiz = num_cmds * 1024;
if (rcvbufsiz < NFT_MNL_ECHO_RCVBUFF_DEFAULT)
rcvbufsiz = NFT_MNL_ECHO_RCVBUFF_DEFAULT;
- } else {
- rcvbufsiz = num_cmds * div_round_up(batch_size, num_cmds) * 4;
}
mnl_set_rcvbuffer(ctx->nft->nf_sock, rcvbufsiz);
@@ -437,13 +456,55 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
return 0;
}
+struct mnl_nft_rule_build_ctx {
+ struct netlink_linearize_ctx *lctx;
+ struct nlmsghdr *nlh;
+ struct cmd *cmd;
+};
+
+static int mnl_nft_expr_build_cb(struct nftnl_expr *nle, void *data)
+{
+ struct mnl_nft_rule_build_ctx *ctx = data;
+ struct nlmsghdr *nlh = ctx->nlh;
+ struct cmd *cmd = ctx->cmd;
+ struct nft_expr_loc *eloc;
+ struct nlattr *nest;
+
+ eloc = nft_expr_loc_find(nle, ctx->lctx);
+ if (eloc)
+ cmd_add_loc(cmd, nlh->nlmsg_len, eloc->loc);
+
+ nest = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ nftnl_expr_build_payload(nlh, nle);
+ mnl_attr_nest_end(nlh, nest);
+
+ nftnl_rule_del_expr(nle);
+ nftnl_expr_free(nle);
+
+ return 0;
+}
+
+static void mnl_nft_rule_build_ctx_init(struct mnl_nft_rule_build_ctx *rule_ctx,
+ struct nlmsghdr *nlh,
+ struct cmd *cmd,
+ struct netlink_linearize_ctx *lctx)
+{
+ memset(rule_ctx, 0, sizeof(*rule_ctx));
+ rule_ctx->nlh = nlh;
+ rule_ctx->cmd = cmd;
+ rule_ctx->lctx = lctx;
+}
+
int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
+ struct mnl_nft_rule_build_ctx rule_ctx;
+ struct netlink_linearize_ctx lctx;
struct rule *rule = cmd->rule;
struct handle *h = &rule->handle;
struct nftnl_rule *nlr;
struct nlmsghdr *nlh;
+ struct nlattr *nest;
nlr = nftnl_rule_alloc();
if (!nlr)
@@ -457,7 +518,8 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
if (h->position_id)
nftnl_rule_set_u32(nlr, NFTNL_RULE_POSITION_ID, h->position_id);
- netlink_linearize_rule(ctx, nlr, rule);
+ netlink_linearize_init(&lctx, nlr);
+ netlink_linearize_rule(ctx, rule, &lctx);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWRULE,
cmd->handle.family,
@@ -472,8 +534,15 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
else
mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
+ mnl_nft_rule_build_ctx_init(&rule_ctx, nlh, cmd, &lctx);
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS);
+ nftnl_expr_foreach(nlr, mnl_nft_expr_build_cb, &rule_ctx);
+ mnl_attr_nest_end(nlh, nest);
+
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
+ netlink_linearize_fini(&lctx);
mnl_nft_batch_continue(ctx->batch);
@@ -482,11 +551,14 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ struct mnl_nft_rule_build_ctx rule_ctx;
+ struct netlink_linearize_ctx lctx;
struct rule *rule = cmd->rule;
struct handle *h = &rule->handle;
unsigned int flags = 0;
struct nftnl_rule *nlr;
struct nlmsghdr *nlh;
+ struct nlattr *nest;
if (nft_output_echo(&ctx->nft->output))
flags |= NLM_F_ECHO;
@@ -497,7 +569,8 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family);
- netlink_linearize_rule(ctx, nlr, rule);
+ netlink_linearize_init(&lctx, nlr);
+ netlink_linearize_rule(ctx, rule, &lctx);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWRULE,
cmd->handle.family,
@@ -510,8 +583,15 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
cmd_add_loc(cmd, nlh->nlmsg_len, &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);
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS);
+ nftnl_expr_foreach(nlr, mnl_nft_expr_build_cb, &rule_ctx);
+ mnl_attr_nest_end(nlh, nest);
+
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
+ netlink_linearize_fini(&lctx);
mnl_nft_batch_continue(ctx->batch);
@@ -520,6 +600,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;
@@ -530,8 +611,11 @@ 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);
@@ -581,20 +665,45 @@ err_free:
return MNL_CB_OK;
}
-struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx,
- int family)
+struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
+ 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();
+ if (!nlr)
+ memory_allocation_error();
+
+ 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);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, rule_cb, nlr_list);
if (ret < 0)
@@ -609,17 +718,98 @@ 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];
+
+ 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].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->nlmsg_len, 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->nlmsg_len, 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)
{
- int priority, policy, i = 0;
+ struct nftnl_udata_buf *udbuf;
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)
@@ -632,45 +822,15 @@ 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);
- }
- 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)
+ memory_allocation_error();
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_CHAIN_COMMENT, cmd->chain->comment))
+ memory_allocation_error();
+ nftnl_chain_set_data(nlc, NFTNL_CHAIN_USERDATA, nftnl_udata_buf_data(udbuf),
+ nftnl_udata_buf_len(udbuf));
+ nftnl_udata_buf_free(udbuf);
}
}
netlink_dump_chain(nlc, ctx);
@@ -703,7 +863,33 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
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->nlmsg_len, &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);
@@ -743,6 +929,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;
@@ -752,8 +939,11 @@ 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);
@@ -768,6 +958,15 @@ int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
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);
@@ -800,10 +999,12 @@ err_free:
}
struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx,
- int family)
+ int family, const char *table,
+ const char *chain)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_chain_list *nlc_list;
+ struct nftnl_chain *nlc = NULL;
struct nlmsghdr *nlh;
int ret;
@@ -811,11 +1012,24 @@ struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx,
if (nlc_list == NULL)
memory_allocation_error();
+ if (table && chain) {
+ nlc = nftnl_chain_alloc();
+ if (!nlc)
+ memory_allocation_error();
+
+ nftnl_chain_set_str(nlc, NFTNL_CHAIN_TABLE, table);
+ nftnl_chain_set_str(nlc, NFTNL_CHAIN_NAME, chain);
+ }
+
nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family,
- NLM_F_DUMP, ctx->seqnum);
+ nlc ? NLM_F_ACK : NLM_F_DUMP, ctx->seqnum);
+ if (nlc) {
+ nftnl_chain_nlmsg_build_payload(nlh, nlc);
+ nftnl_chain_free(nlc);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, chain_cb, nlc_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nlc_list;
@@ -830,6 +1044,7 @@ err:
int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
+ struct nftnl_udata_buf *udbuf;
struct nftnl_table *nlt;
struct nlmsghdr *nlh;
@@ -838,10 +1053,22 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
memory_allocation_error();
nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family);
- if (cmd->table)
+ if (cmd->table) {
nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, cmd->table->flags);
- else
+
+ if (cmd->table->comment) {
+ udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
+ if (!udbuf)
+ memory_allocation_error();
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_TABLE_COMMENT, cmd->table->comment))
+ memory_allocation_error();
+ nftnl_table_set_data(nlt, NFTNL_TABLE_USERDATA, nftnl_udata_buf_data(udbuf),
+ nftnl_udata_buf_len(udbuf));
+ nftnl_udata_buf_free(udbuf);
+ }
+ } else {
nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, 0);
+ }
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWTABLE,
@@ -860,6 +1087,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;
@@ -869,17 +1097,18 @@ 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);
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);
- mnl_attr_put_u64(nlh, NFTA_TABLE_NAME,
+ mnl_attr_put_u64(nlh, NFTA_TABLE_HANDLE,
htobe64(cmd->handle.handle.id));
}
nftnl_table_nlmsg_build_payload(nlh, nlt);
@@ -914,10 +1143,12 @@ err_free:
}
struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
- int family)
+ int family, const char *table)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_table_list *nlt_list;
+ struct nftnl_table *nlt = NULL;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
int ret;
@@ -925,11 +1156,25 @@ struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
if (nlt_list == NULL)
return NULL;
+ if (table) {
+ nlt = nftnl_table_alloc();
+ if (!nlt)
+ memory_allocation_error();
+
+ nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, family);
+ nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, table);
+ flags = NLM_F_ACK;
+ }
+
nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, family,
- NLM_F_DUMP, ctx->seqnum);
+ flags, ctx->seqnum);
+ if (nlt) {
+ nftnl_table_nlmsg_build_payload(nlh, nlt);
+ nftnl_table_free(nlt);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, table_cb, nlt_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nlt_list;
@@ -945,9 +1190,7 @@ static void set_key_expression(struct netlink_ctx *ctx,
{
struct nftnl_udata *nest1, *nest2;
- if (expr->flags & EXPR_F_CONSTANT ||
- set_flags & NFT_SET_ANONYMOUS ||
- !expr_ops(expr)->build_udata)
+ if (!expr_ops(expr)->build_udata)
return;
nest1 = nftnl_udata_nest_start(udbuf, type);
@@ -969,6 +1212,8 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
struct set *set = cmd->set;
struct nftnl_set *nls;
struct nlmsghdr *nlh;
+ struct stmt *stmt;
+ int num_stmts = 0;
nls = nftnl_set_alloc();
if (!nls)
@@ -1042,13 +1287,27 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
sizeof(set->desc.field_len[0]));
}
+ if (set->comment) {
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_SET_COMMENT, set->comment))
+ memory_allocation_error();
+ }
+
nftnl_set_set_data(nls, NFTNL_SET_USERDATA, nftnl_udata_buf_data(udbuf),
nftnl_udata_buf_len(udbuf));
nftnl_udata_buf_free(udbuf);
- if (set->stmt) {
- nftnl_set_set_data(nls, NFTNL_SET_EXPR,
- netlink_gen_stmt_stateful(set->stmt), 0);
+ list_for_each_entry(stmt, &set->stmt_list, list)
+ num_stmts++;
+
+ if (num_stmts == 1) {
+ list_for_each_entry(stmt, &set->stmt_list, list) {
+ nftnl_set_set_data(nls, NFTNL_SET_EXPR,
+ netlink_gen_stmt_stateful(stmt), 0);
+ break;
+ }
+ } else if (num_stmts > 1) {
+ list_for_each_entry(stmt, &set->stmt_list, list)
+ nftnl_set_add_expr(nls, netlink_gen_stmt_stateful(stmt));
}
netlink_dump_set(nls, ctx);
@@ -1076,6 +1335,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;
@@ -1086,8 +1346,11 @@ 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);
@@ -1134,10 +1397,12 @@ err_free:
}
struct nftnl_set_list *
-mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
+mnl_nft_set_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *set)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_set_list *nls_list;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
struct nftnl_set *s;
int ret;
@@ -1146,10 +1411,15 @@ mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
if (s == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
- NLM_F_DUMP, ctx->seqnum);
if (table != NULL)
nftnl_set_set_str(s, NFTNL_SET_TABLE, table);
+ if (set) {
+ nftnl_set_set_str(s, NFTNL_SET_NAME, set);
+ flags = NLM_F_ACK;
+ }
+
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
+ flags, ctx->seqnum);
nftnl_set_nlmsg_build_payload(nlh, s);
nftnl_set_free(s);
@@ -1158,7 +1428,7 @@ mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
memory_allocation_error();
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_cb, nls_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nls_list;
@@ -1171,6 +1441,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct obj *obj = cmd->object;
+ struct nftnl_udata_buf *udbuf;
struct nftnl_obj *nlo;
struct nlmsghdr *nlh;
@@ -1181,6 +1452,17 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family);
nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, obj->type);
+ if (obj->comment) {
+ udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
+ if (!udbuf)
+ memory_allocation_error();
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_OBJ_COMMENT, obj->comment))
+ memory_allocation_error();
+ nftnl_obj_set_data(nlo, NFTNL_OBJ_USERDATA, nftnl_udata_buf_data(udbuf),
+ nftnl_udata_buf_len(udbuf));
+ nftnl_udata_buf_free(udbuf);
+ }
+
switch (obj->type) {
case NFT_OBJECT_COUNTER:
nftnl_obj_set_u64(nlo, NFTNL_OBJ_CTR_PKTS,
@@ -1272,6 +1554,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;
@@ -1282,8 +1565,11 @@ 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);
@@ -1388,39 +1674,114 @@ static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
-static int mnl_nft_setelem_batch(struct nftnl_set *nls,
+static bool mnl_nft_attr_nest_overflow(struct nlmsghdr *nlh,
+ const struct nlattr *from,
+ const struct nlattr *to)
+{
+ int len = (void *)to + to->nla_len - (void *)from;
+
+ /* The attribute length field is 16 bits long, thus the maximum payload
+ * that an attribute can convey is UINT16_MAX. In case of overflow,
+ * discard the last attribute that did not fit into the nest.
+ */
+ if (len > UINT16_MAX) {
+ nlh->nlmsg_len -= to->nla_len;
+ return true;
+ }
+ return false;
+}
+
+static void netlink_dump_setelem(const struct nftnl_set_elem *nlse,
+ struct netlink_ctx *ctx)
+{
+ FILE *fp = ctx->nft->output.output_fp;
+ char buf[4096];
+
+ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
+ return;
+
+ nftnl_set_elem_snprintf(buf, sizeof(buf), nlse, NFTNL_OUTPUT_DEFAULT, 0);
+ fprintf(fp, "\t%s", buf);
+}
+
+static void netlink_dump_setelem_done(struct netlink_ctx *ctx)
+{
+ FILE *fp = ctx->nft->output.output_fp;
+
+ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
+ return;
+
+ fprintf(fp, "\n");
+}
+
+static int mnl_nft_setelem_batch(const struct nftnl_set *nls, struct cmd *cmd,
struct nftnl_batch *batch,
- enum nf_tables_msg_types cmd,
- unsigned int flags, uint32_t seqnum)
+ enum nf_tables_msg_types msg_type,
+ unsigned int flags, uint32_t seqnum,
+ const struct expr *set,
+ struct netlink_ctx *ctx)
{
+ struct nlattr *nest1, *nest2;
+ struct nftnl_set_elem *nlse;
struct nlmsghdr *nlh;
- struct nftnl_set_elems_iter *iter;
- int ret;
-
- iter = nftnl_set_elems_iter_create(nls);
- if (iter == NULL)
- memory_allocation_error();
+ struct expr *expr = NULL;
+ int i = 0;
- if (cmd == NFT_MSG_NEWSETELEM)
+ if (msg_type == NFT_MSG_NEWSETELEM)
flags |= NLM_F_CREATE;
- while (nftnl_set_elems_iter_cur(iter)) {
- nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), cmd,
- nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
- flags, seqnum);
- ret = nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter);
- mnl_nft_batch_continue(batch);
- if (ret <= 0)
- break;
+ if (set)
+ expr = list_first_entry(&set->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);
+
+ if (nftnl_set_is_set(nls, NFTNL_SET_TABLE)) {
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_TABLE,
+ nftnl_set_get_str(nls, NFTNL_SET_TABLE));
}
+ if (nftnl_set_is_set(nls, NFTNL_SET_NAME)) {
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_SET,
+ nftnl_set_get_str(nls, NFTNL_SET_NAME));
+ }
+ if (nftnl_set_is_set(nls, NFTNL_SET_ID)) {
+ mnl_attr_put_u32(nlh, NFTA_SET_ELEM_LIST_SET_ID,
+ htonl(nftnl_set_get_u32(nls, NFTNL_SET_ID)));
+ }
+
+ if (!set || list_empty(&set->expressions))
+ return 0;
- nftnl_set_elems_iter_destroy(iter);
+ 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);
+
+ 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);
+
+ netlink_dump_setelem(nlse, ctx);
+ nftnl_set_elem_free(nlse);
+ if (mnl_nft_attr_nest_overflow(nlh, nest1, nest2)) {
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_nft_batch_continue(batch);
+ goto next;
+ }
+ }
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_nft_batch_continue(batch);
+ netlink_dump_setelem_done(ctx);
return 0;
}
-int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
- const struct expr *expr, unsigned int flags)
+int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct set *set, const struct expr *expr,
+ unsigned int flags)
{
const struct handle *h = &set->handle;
struct nftnl_set *nls;
@@ -1435,12 +1796,14 @@ int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
nftnl_set_set_str(nls, NFTNL_SET_NAME, h->set.name);
if (h->set_id)
nftnl_set_set_u32(nls, NFTNL_SET_ID, h->set_id);
+ if (set_is_datamap(set->flags))
+ nftnl_set_set_u32(nls, NFTNL_SET_DATA_TYPE,
+ dtype_map_to_kernel(set->data->dtype));
- alloc_setelem_cache(expr, nls);
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_NEWSETELEM, flags,
- ctx->seqnum);
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_NEWSETELEM,
+ flags, ctx->seqnum, expr, ctx);
nftnl_set_free(nls);
return err;
@@ -1476,9 +1839,10 @@ int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd)
return 0;
}
-int mnl_nft_setelem_del(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 = &cmd->handle;
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSETELEM;
struct nftnl_set *nls;
int err;
@@ -1493,26 +1857,34 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct cmd *cmd)
else if (h->handle.id)
nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id);
- if (cmd->expr)
- alloc_setelem_cache(cmd->expr, nls);
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_DELSETELEM, 0,
- ctx->seqnum);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYSETELEM;
+
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, msg_type, 0,
+ ctx->seqnum, 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);
@@ -1535,12 +1907,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);
@@ -1572,11 +1951,13 @@ err_free:
}
struct nftnl_flowtable_list *
-mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
+mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *ft)
{
struct nftnl_flowtable_list *nln_list;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_flowtable *n;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
int ret;
@@ -1584,10 +1965,14 @@ mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
if (n == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family,
- NLM_F_DUMP, ctx->seqnum);
if (table != NULL)
nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_TABLE, table);
+ if (ft) {
+ nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_NAME, ft);
+ flags = NLM_F_ACK;
+ }
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family,
+ flags, ctx->seqnum);
nftnl_flowtable_nlmsg_build_payload(nlh, n);
nftnl_flowtable_free(n);
@@ -1596,7 +1981,7 @@ mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
memory_allocation_error();
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, flowtable_cb, nln_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nln_list;
@@ -1605,48 +1990,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);
-
- 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);
+ const struct expr *dev_expr = cmd->flowtable->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);
+ nest_dev = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK_DEVS);
+ for (i = 0; i < num_devs; i++) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, 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();
@@ -1656,24 +2023,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);
@@ -1689,6 +2038,21 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
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);
@@ -1698,9 +2062,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)
@@ -1709,18 +2074,11 @@ 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);
@@ -1738,6 +2096,14 @@ int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
}
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);
@@ -1756,7 +2122,7 @@ int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
void *cb_data)
{
/* Set netlink socket buffer size to 16 Mbytes to reduce chances of
- * message loss due to ENOBUFS.
+ * message loss due to ENOBUFS.
*/
unsigned int bufsiz = NFTABLES_NLEVENT_BUFSIZ;
int fd = mnl_socket_get_fd(nf_sock);
@@ -1800,3 +2166,504 @@ int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
}
return ret;
}
+
+static struct basehook *basehook_alloc(void)
+{
+ return xzalloc(sizeof(struct basehook));
+}
+
+static void basehook_free(struct basehook *b)
+{
+ list_del(&b->list);
+ free_const(b->module_name);
+ free_const(b->hookfn);
+ free_const(b->chain);
+ free_const(b->table);
+ free(b);
+}
+
+static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
+{
+ struct basehook *hook;
+
+ list_for_each_entry(hook, head, list) {
+ if (hook->family != b->family)
+ continue;
+ if (hook->num != b->num)
+ continue;
+ if (hook->prio < b->prio)
+ continue;
+
+ list_add(&b->list, &hook->list);
+ return;
+ }
+
+ list_add_tail(&b->list, head);
+}
+
+static int dump_nf_attr_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_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFNLA_HOOK_HOOKNUM:
+ case NFNLA_HOOK_PRIORITY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case NFNLA_HOOK_DEV:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case NFNLA_HOOK_MODULE_NAME:
+ case NFNLA_HOOK_FUNCTION_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case NFNLA_HOOK_CHAIN_INFO:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ return MNL_CB_ERROR;
+ break;
+ default:
+ return MNL_CB_OK;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int dump_nf_chain_info_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_INFO_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFNLA_HOOK_INFO_DESC:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case NFNLA_HOOK_INFO_TYPE:
+ 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;
+}
+
+static int dump_nf_attr_chain_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_CHAIN_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFNLA_CHAIN_TABLE:
+ case NFNLA_CHAIN_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case NFNLA_CHAIN_FAMILY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ return MNL_CB_ERROR;
+ break;
+ default:
+ return MNL_CB_OK;
+ }
+
+ tb[type] = attr;
+ 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;
+ int family;
+};
+
+static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
+{
+ const struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *tb[NFNLA_HOOK_MAX + 1] = {};
+ struct dump_nf_hook_data *data = _data;
+ struct basehook *hook;
+
+ /* NB: Don't check the nft generation ID, this is not
+ * an nftables subsystem.
+ */
+ if (mnl_attr_parse(nlh, sizeof(*nfg), dump_nf_attr_cb, tb) < 0)
+ return -1;
+
+ if (!tb[NFNLA_HOOK_PRIORITY])
+ netlink_abi_error();
+
+ hook = basehook_alloc();
+ hook->prio = ntohl(mnl_attr_get_u32(tb[NFNLA_HOOK_PRIORITY]));
+
+ if (tb[NFNLA_HOOK_FUNCTION_NAME])
+ hook->hookfn = xstrdup(mnl_attr_get_str(tb[NFNLA_HOOK_FUNCTION_NAME]));
+
+ if (tb[NFNLA_HOOK_MODULE_NAME])
+ hook->module_name = xstrdup(mnl_attr_get_str(tb[NFNLA_HOOK_MODULE_NAME]));
+
+ if (tb[NFNLA_HOOK_CHAIN_INFO]) {
+ 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) {
+ 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) {
+ basehook_free(hook);
+ return -1;
+ }
+
+ tablename = mnl_attr_get_str(info[NFNLA_CHAIN_TABLE]);
+ chainname = mnl_attr_get_str(info[NFNLA_CHAIN_NAME]);
+ if (tablename && chainname) {
+ hook->table = xstrdup(tablename);
+ 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])
+ hook->num = ntohl(mnl_attr_get_u32(tb[NFNLA_HOOK_HOOKNUM]));
+
+ 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;
+}
+
+static struct nlmsghdr *nf_hook_dump_request(char *buf, uint8_t family, uint32_t seq)
+{
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct nfgenmsg *nfg;
+
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_type = NFNL_SUBSYS_HOOK << 8;
+ nlh->nlmsg_seq = seq;
+
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = family;
+ nfg->version = NFNETLINK_V0;
+
+ return nlh;
+}
+
+static int __mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, uint8_t query_family,
+ uint8_t family, uint8_t hooknum,
+ const char *devname,
+ struct list_head *hook_list)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct dump_nf_hook_data data = {
+ .hook_list = hook_list,
+ .family = query_family,
+ };
+ struct nlmsghdr *nlh;
+
+ nlh = nf_hook_dump_request(buf, family, ctx->seqnum);
+ if (devname)
+ mnl_attr_put_strz(nlh, NFNLA_HOOK_DEV, devname);
+
+ mnl_attr_put_u32(nlh, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
+
+ return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, dump_nf_hooks, &data);
+}
+
+static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *hook_list)
+{
+ struct basehook *hook, *tmp, *prev = NULL;
+ bool same, family_in_use = false;
+ int prio;
+ FILE *fp;
+
+ fp = ctx->nft->output.output_fp;
+
+ list_for_each_entry_safe(hook, tmp, hook_list, list) {
+ if (hook->family == family) {
+ family_in_use = true;
+ break;
+ }
+ }
+
+ if (!family_in_use)
+ return;
+
+ fprintf(fp, "family %s {\n", family2str(family));
+
+ list_for_each_entry_safe(hook, tmp, hook_list, list) {
+ if (hook->family != family)
+ continue;
+
+ if (prev) {
+ if (prev->num == hook->num) {
+ fprintf(fp, "\n");
+ same = true;
+ } else {
+ same = false;
+ fprintf(fp, "\n\t}\n");
+ }
+ } else {
+ same = false;
+ }
+ prev = hook;
+
+ if (!same) {
+ fprintf(fp, "\thook %s {\n",
+ hooknum2str(family, hook->num));
+ }
+
+ prio = hook->prio;
+ if (prio < 0)
+ fprintf(fp, "\t\t%011d", prio); /* outputs a '-' sign */
+ else if (prio == 0)
+ fprintf(fp, "\t\t %010u", prio);
+ else
+ fprintf(fp, "\t\t+%010u", prio);
+
+ 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);
+ }
+ if (hook->module_name)
+ fprintf(fp, " [%s]", hook->module_name);
+ }
+
+ fprintf(fp, "\n\t}\n");
+ 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)
+{
+ 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;
+
+ /* 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;
+
+ 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)
+{
+ int err;
+
+ err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
+ if (err < 0)
+ *ret = err;
+
+ return err;
+}
+
+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)
+{
+ 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;
+
+#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;
+ }
+ }
+
+ return err;
+}
+
+static void release_hook_list(struct list_head *hook_list)
+{
+ struct basehook *hook, *next;
+
+ list_for_each_entry_safe(hook, next, hook_list, list)
+ basehook_free(hook);
+}
+
+int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, int hook, const char *devname)
+{
+ LIST_HEAD(hook_list);
+ unsigned int i;
+ int ret;
+
+ 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;
+ 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);
+ break;
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_BRIDGE:
+ mnl_nft_dump_nf(ctx, family, hook, devname, &hook_list, &ret);
+ break;
+ case NFPROTO_ARP:
+ mnl_nft_dump_nf_arp(ctx, family, hook, devname, &hook_list, &ret);
+ 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;
+ }
+
+ 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;
+ }
+
+ release_hook_list(&hook_list);
+ ret = 0;
+
+ return ret;
+}
diff --git a/src/monitor.c b/src/monitor.c
index 3872ebcf..2fc16d67 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>
@@ -40,6 +40,13 @@
#include <iface.h>
#include <json.h>
+enum {
+ NFT_OF_EVENT_ADD,
+ NFT_OF_EVENT_INSERT,
+ NFT_OF_EVENT_DEL,
+ NFT_OF_EVENT_CREATE,
+};
+
#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__)
struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh)
@@ -120,17 +127,24 @@ struct nftnl_obj *netlink_obj_alloc(const struct nlmsghdr *nlh)
return nlo;
}
-static uint32_t netlink_msg2nftnl_of(uint32_t msg)
+static uint32_t netlink_msg2nftnl_of(uint32_t type, uint16_t flags)
{
- switch (msg) {
+ switch (type) {
+ case NFT_MSG_NEWRULE:
+ if (flags & NLM_F_APPEND)
+ return NFT_OF_EVENT_ADD;
+ else
+ return NFT_OF_EVENT_INSERT;
case NFT_MSG_NEWTABLE:
case NFT_MSG_NEWCHAIN:
case NFT_MSG_NEWSET:
case NFT_MSG_NEWSETELEM:
- case NFT_MSG_NEWRULE:
case NFT_MSG_NEWOBJ:
case NFT_MSG_NEWFLOWTABLE:
- return NFTNL_OF_EVENT_NEW;
+ if (flags & NLM_F_EXCL)
+ return NFT_OF_EVENT_CREATE;
+ else
+ return NFT_OF_EVENT_ADD;
case NFT_MSG_DELTABLE:
case NFT_MSG_DELCHAIN:
case NFT_MSG_DELSET:
@@ -147,18 +161,22 @@ static uint32_t netlink_msg2nftnl_of(uint32_t msg)
static const char *nftnl_of2cmd(uint32_t of)
{
switch (of) {
- case NFTNL_OF_EVENT_NEW:
+ case NFT_OF_EVENT_ADD:
return "add";
- case NFTNL_OF_EVENT_DEL:
+ case NFT_OF_EVENT_CREATE:
+ return "create";
+ case NFT_OF_EVENT_INSERT:
+ return "insert";
+ case NFT_OF_EVENT_DEL:
return "delete";
default:
return "???";
}
}
-static const char *netlink_msg2cmd(uint32_t msg)
+static const char *netlink_msg2cmd(uint32_t type, uint16_t flags)
{
- return nftnl_of2cmd(netlink_msg2nftnl_of(msg));
+ return nftnl_of2cmd(netlink_msg2nftnl_of(type, flags));
}
static void nlr_for_each_set(struct nftnl_rule *nlr,
@@ -206,7 +224,7 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
nlt = netlink_table_alloc(nlh);
t = netlink_delinearize_table(monh->ctx, nlt);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -221,12 +239,14 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
if (nft_output_handle(&monh->ctx->nft->output))
nft_mon_print(monh, " # handle %" PRIu64 "",
t->handle.handle.id);
+ nft_mon_print(monh, "\n");
break;
case NFTNL_OUTPUT_JSON:
monitor_print_table_json(monh, cmd, t);
+ if (!nft_output_echo(&monh->ctx->nft->output))
+ nft_mon_print(monh, "\n");
break;
}
- nft_mon_print(monh, "\n");
table_free(t);
nftnl_table_free(nlt);
return MNL_CB_OK;
@@ -241,7 +261,7 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type,
nlc = netlink_chain_alloc(nlh);
c = netlink_delinearize_chain(monh->ctx, nlc);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -252,18 +272,23 @@ 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");
break;
case NFTNL_OUTPUT_JSON:
monitor_print_chain_json(monh, cmd, c);
+ if (!nft_output_echo(&monh->ctx->nft->output))
+ nft_mon_print(monh, "\n");
break;
}
- nft_mon_print(monh, "\n");
chain_free(c);
nftnl_chain_free(nlc);
return MNL_CB_OK;
@@ -288,7 +313,7 @@ static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type,
return MNL_CB_ERROR;
}
family = family2str(set->handle.family);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -304,12 +329,14 @@ static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type,
set->handle.set.name);
break;
}
+ nft_mon_print(monh, "\n");
break;
case NFTNL_OUTPUT_JSON:
monitor_print_set_json(monh, cmd, set);
+ if (!nft_output_echo(&monh->ctx->nft->output))
+ nft_mon_print(monh, "\n");
break;
}
- nft_mon_print(monh, "\n");
set_free(set);
out:
nftnl_set_free(nls);
@@ -363,13 +390,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;
}
@@ -388,7 +421,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
table = nftnl_set_get_str(nls, NFTNL_SET_TABLE);
setname = nftnl_set_get_str(nls, NFTNL_SET_NAME);
family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
set = set_lookup_global(family, table, setname, &monh->ctx->nft->cache);
if (set == NULL) {
@@ -404,11 +437,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)
@@ -441,6 +476,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
nft_mon_print(monh, "%s element %s %s %s ",
cmd, family2str(family), table, setname);
expr_print(dummyset->init, &monh->ctx->nft->output);
+ nft_mon_print(monh, "\n");
break;
case NFTNL_OUTPUT_JSON:
dummyset->handle.family = family;
@@ -450,9 +486,10 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
/* prevent set_free() from trying to free those */
dummyset->handle.set.name = NULL;
dummyset->handle.table.name = NULL;
+ if (!nft_output_echo(&monh->ctx->nft->output))
+ nft_mon_print(monh, "\n");
break;
}
- nft_mon_print(monh, "\n");
set_free(dummyset);
out:
nftnl_set_free(nls);
@@ -469,12 +506,12 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
nlo = netlink_obj_alloc(nlh);
obj = netlink_delinearize_obj(monh->ctx, nlo);
- if (obj == NULL) {
+ if (!obj) {
nftnl_obj_free(nlo);
return MNL_CB_ERROR;
}
family = family2str(obj->handle.family);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -492,12 +529,14 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
obj->handle.obj.name);
break;
}
+ nft_mon_print(monh, "\n");
break;
case NFTNL_OUTPUT_JSON:
monitor_print_obj_json(monh, cmd, obj);
+ if (!nft_output_echo(&monh->ctx->nft->output))
+ nft_mon_print(monh, "\n");
break;
}
- nft_mon_print(monh, "\n");
obj_free(obj);
nftnl_obj_free(nlo);
return MNL_CB_OK;
@@ -505,8 +544,13 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
static void rule_map_decompose_cb(struct set *s, void *data)
{
- if (set_is_interval(s->flags) && set_is_anonymous(s->flags))
+ if (!set_is_anonymous(s->flags))
+ return;
+
+ if (set_is_non_concat_range(s))
interval_map_decompose(s->init);
+ else if (set_is_interval(s->flags))
+ concat_range_aggregate(s->init);
}
static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
@@ -518,9 +562,13 @@ 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);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -531,7 +579,10 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
family,
r->handle.table.name,
r->handle.chain.name);
-
+ if (r->handle.position.id) {
+ nft_mon_print(monh, "handle %" PRIu64" ",
+ r->handle.position.id);
+ }
switch (type) {
case NFT_MSG_NEWRULE:
rule_print(r, &monh->ctx->nft->output);
@@ -542,13 +593,16 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
r->handle.handle.id);
break;
}
+ nft_mon_print(monh, "\n");
break;
case NFTNL_OUTPUT_JSON:
monitor_print_rule_json(monh, cmd, r);
+ if (!nft_output_echo(&monh->ctx->nft->output))
+ nft_mon_print(monh, "\n");
break;
}
- nft_mon_print(monh, "\n");
rule_free(r);
+out_free_nlr:
nftnl_rule_free(nlr);
return MNL_CB_OK;
}
@@ -563,7 +617,7 @@ static void netlink_events_cache_addtable(struct netlink_mon_handler *monh,
t = netlink_delinearize_table(monh->ctx, nlt);
nftnl_table_free(nlt);
- table_add_hash(t, &monh->ctx->nft->cache);
+ table_cache_add(t, &monh->ctx->nft->cache);
}
static void netlink_events_cache_deltable(struct netlink_mon_handler *monh,
@@ -577,11 +631,12 @@ static void netlink_events_cache_deltable(struct netlink_mon_handler *monh,
h.family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY);
h.table.name = nftnl_table_get_str(nlt, NFTNL_TABLE_NAME);
- t = table_lookup(&h, &monh->ctx->nft->cache);
+ t = table_cache_find(&monh->ctx->nft->cache.table_cache,
+ h.table.name, h.family);
if (t == NULL)
goto out;
- list_del(&t->list);
+ table_cache_del(t);
table_free(t);
out:
nftnl_table_free(nlt);
@@ -599,6 +654,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);
@@ -607,7 +663,8 @@ static void netlink_events_cache_addset(struct netlink_mon_handler *monh,
goto out;
s->init = set_expr_alloc(monh->loc, s);
- t = table_lookup(&s->handle, &monh->ctx->nft->cache);
+ t = table_cache_find(&monh->ctx->nft->cache.table_cache,
+ s->handle.table.name, s->handle.family);
if (t == NULL) {
fprintf(stderr, "W: Unable to cache set: table not found.\n");
set_free(s);
@@ -620,7 +677,7 @@ static void netlink_events_cache_addset(struct netlink_mon_handler *monh,
goto out;
}
- set_add_hash(s, t);
+ set_cache_add(s, t);
out:
nftnl_set_free(nls);
}
@@ -675,7 +732,7 @@ out:
static void netlink_events_cache_delset_cb(struct set *s,
void *data)
{
- list_del(&s->list);
+ set_cache_del(s);
set_free(s);
}
@@ -708,14 +765,15 @@ static void netlink_events_cache_addobj(struct netlink_mon_handler *monh,
if (obj == NULL)
goto out;
- t = table_lookup(&obj->handle, &monh->ctx->nft->cache);
+ t = table_cache_find(&monh->ctx->nft->cache.table_cache,
+ obj->handle.table.name, obj->handle.family);
if (t == NULL) {
fprintf(stderr, "W: Unable to cache object: table not found.\n");
obj_free(obj);
goto out;
}
- obj_add_hash(obj, t);
+ obj_cache_add(obj, t);
out:
nftnl_obj_free(nlo);
}
@@ -738,19 +796,20 @@ static void netlink_events_cache_delobj(struct netlink_mon_handler *monh,
type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE);
h.handle.id = nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE);
- t = table_lookup(&h, &monh->ctx->nft->cache);
+ t = table_cache_find(&monh->ctx->nft->cache.table_cache,
+ h.table.name, h.family);
if (t == NULL) {
fprintf(stderr, "W: Unable to cache object: table not found.\n");
goto out;
}
- obj = obj_lookup(t, name, type);
+ obj = obj_cache_find(t, name, type);
if (obj == NULL) {
fprintf(stderr, "W: Unable to find object in cache\n");
goto out;
}
- list_del(&obj->list);
+ obj_cache_del(obj);
obj_free(obj);
out:
nftnl_obj_free(nlo);
@@ -830,6 +889,9 @@ static int netlink_events_newgen_cb(const struct nlmsghdr *nlh, int type,
char name[256] = "";
int genid = -1, pid = -1;
+ if (monh->format != NFTNL_OUTPUT_DEFAULT)
+ return MNL_CB_OK;
+
mnl_attr_for_each(attr, nlh, sizeof(struct nfgenmsg)) {
switch (mnl_attr_get_type(attr)) {
case NFTA_GEN_ID:
@@ -912,6 +974,8 @@ int netlink_echo_callback(const struct nlmsghdr *nlh, void *data)
{
struct netlink_cb_data *nl_cb_data = data;
struct netlink_ctx *ctx = nl_cb_data->nl_ctx;
+ struct nft_ctx *nft = ctx->nft;
+
struct netlink_mon_handler echo_monh = {
.format = NFTNL_OUTPUT_DEFAULT,
.ctx = ctx,
@@ -922,8 +986,13 @@ int netlink_echo_callback(const struct nlmsghdr *nlh, void *data)
if (!nft_output_echo(&echo_monh.ctx->nft->output))
return MNL_CB_OK;
- if (nft_output_json(&ctx->nft->output))
- return json_events_cb(nlh, &echo_monh);
+ if (nft_output_json(&nft->output)) {
+ if (nft->json_root)
+ return json_events_cb(nlh, &echo_monh);
+ if (!nft->json_echo)
+ json_alloc_echo(nft);
+ echo_monh.format = NFTNL_OUTPUT_JSON;
+ }
return netlink_events_cb(nlh, &echo_monh);
}
diff --git a/src/netlink.c b/src/netlink.c
index 2f1dbe17..0088b742 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>
@@ -59,7 +59,7 @@ void __noreturn __netlink_abi_error(const char *file, int line,
{
fprintf(stderr, "E: Contact urgently your Linux kernel vendor. "
"Netlink ABI is broken: %s:%d %s\n", file, line, reason);
- exit(NFT_EXIT_FAILURE);
+ abort();
}
int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,
@@ -96,14 +96,21 @@ struct nftnl_expr *alloc_nft_expr(const char *name)
return nle;
}
+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);
-static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
- const struct expr *expr)
+struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
+ const struct expr *expr)
{
const struct expr *elem, *data;
struct nftnl_set_elem *nlse;
struct nft_data_linearize nld;
struct nftnl_udata_buf *udbuf = NULL;
+ uint32_t flags = 0;
+ int num_exprs = 0;
+ struct stmt *stmt;
struct expr *key;
nlse = nftnl_set_elem_alloc();
@@ -118,18 +125,34 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
} else {
elem = expr;
}
+ if (elem->etype != EXPR_SET_ELEM)
+ BUG("Unexpected expression type: got %d\n", elem->etype);
+
key = elem->key;
- netlink_gen_data(key, &nld);
- nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
+ switch (key->etype) {
+ case EXPR_SET_ELEM_CATCHALL:
+ break;
+ default:
+ 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);
- if (set->set_flags & NFT_SET_INTERVAL && key->field_count > 1) {
- key->flags |= EXPR_F_INTERVAL_END;
- netlink_gen_data(key, &nld);
- key->flags &= ~EXPR_F_INTERVAL_END;
+ key->flags |= EXPR_F_INTERVAL_END;
+ 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);
+ 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)
@@ -138,9 +161,20 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
if (elem->expiration)
nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_EXPIRATION,
elem->expiration);
- if (elem->stmt)
- nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_EXPR,
- netlink_gen_stmt_stateful(elem->stmt), 0);
+ list_for_each_entry(stmt, &elem->stmt_list, list)
+ num_exprs++;
+
+ if (num_exprs == 1) {
+ list_for_each_entry(stmt, &elem->stmt_list, list) {
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_EXPR,
+ netlink_gen_stmt_stateful(stmt), 0);
+ }
+ } else if (num_exprs > 1) {
+ list_for_each_entry(stmt, &elem->stmt_list, list) {
+ nftnl_set_elem_add_expr(nlse,
+ netlink_gen_stmt_stateful(stmt));
+ }
+ }
if (elem->comment || expr->elem_flags) {
udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udbuf)
@@ -163,7 +197,7 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
nftnl_udata_buf_free(udbuf);
}
if (set_is_datamap(set->set_flags) && data != NULL) {
- netlink_gen_data(data, &nld);
+ __netlink_gen_data(data, &nld, !(data->flags & EXPR_F_SINGLETON));
switch (data->etype) {
case EXPR_VERDICT:
nftnl_set_elem_set_u32(nlse, NFTNL_SET_ELEM_VERDICT,
@@ -193,8 +227,12 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
}
if (expr->flags & EXPR_F_INTERVAL_END)
- nftnl_set_elem_set_u32(nlse, NFTNL_SET_ELEM_FLAGS,
- NFT_SET_ELEM_INTERVAL_END);
+ flags |= NFT_SET_ELEM_INTERVAL_END;
+ if (key->etype == EXPR_SET_ELEM_CATCHALL)
+ flags |= NFT_SET_ELEM_CATCHALL;
+
+ if (flags)
+ nftnl_set_elem_set_u32(nlse, NFTNL_SET_ELEM_FLAGS, flags);
return nlse;
}
@@ -216,8 +254,91 @@ 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;
+
+ switch (i->etype) {
+ case EXPR_RANGE:
+ if (flags & EXPR_F_INTERVAL_END)
+ expr = i->right;
+ else
+ expr = i->left;
+
+ if (expr_basetype(expr)->type == TYPE_INTEGER &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ byteorder_switch_expr_value(expr->value, expr);
+
+ i = expr;
+ break;
+ case EXPR_PREFIX:
+ 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)
+ byteorder_switch_expr_value(v, i);
+
+ mpz_add(v, i->prefix->value, v);
+ count = netlink_export_pad(data, v, i);
+ mpz_clear(v);
+ return count;
+ }
+ return netlink_export_pad(data, i->prefix->value, i);
+ case EXPR_VALUE:
+ /* Switch byteorder only once for singleton values when the set
+ * contains concatenation of intervals.
+ */
+ if (!(flags & EXPR_F_INTERVAL))
+ break;
+
+ expr = (struct expr *)i;
+ if (expr_basetype(expr)->type == TYPE_INTEGER &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ byteorder_switch_expr_value(expr->value, expr);
+ break;
+ default:
+ BUG("invalid expression type '%s' in set", expr_ops(i)->name);
+ }
+
+ return netlink_export_pad(data, i->value, i);
+}
+
+static void nft_data_memcpy(struct nft_data_linearize *nld,
+ const void *src, unsigned int 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 = expr->len / BITS_PER_BYTE, offset = 0;
+ unsigned char data[len];
+ const struct expr *i;
+
+ memset(data, 0, len);
+
+ list_for_each_entry(i, &expr->expressions, list)
+ offset += __netlink_gen_concat_key(expr->flags, i, data + offset);
+
+ nft_data_memcpy(nld, data, len);
+}
+
+static int __netlink_gen_concat_data(int end, const struct expr *i,
+ unsigned char *data)
{
switch (i->etype) {
case EXPR_RANGE:
@@ -244,21 +365,46 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i,
return netlink_export_pad(data, i->value, i);
}
-static void netlink_gen_concat_data(const struct expr *expr,
- struct nft_data_linearize *nld)
+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];
+ const struct expr *i;
+
+ memset(data, 0, len);
+
+ list_for_each_entry(i, &expr->expressions, list)
+ offset += __netlink_gen_concat_data(false, i, data + offset);
+
+ list_for_each_entry(i, &expr->expressions, list)
+ offset += __netlink_gen_concat_data(true, i, data + offset);
+
+ nft_data_memcpy(nld, data, len);
+}
+
+static void __netlink_gen_concat(const struct expr *expr,
+ struct nft_data_linearize *nld)
{
unsigned int len = expr->len / BITS_PER_BYTE, offset = 0;
- int end = expr->flags & EXPR_F_INTERVAL_END;
unsigned char data[len];
const struct expr *i;
memset(data, 0, len);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(end, i, data + offset);
+ offset += __netlink_gen_concat_data(expr->flags, i, data + offset);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static void netlink_gen_concat_data(const struct expr *expr,
+ struct nft_data_linearize *nld, bool expand)
+{
+ if (expand)
+ __netlink_gen_concat_expand(expr, nld);
+ else
+ __netlink_gen_concat(expr, nld);
}
static void netlink_gen_constant_data(const struct expr *expr,
@@ -318,35 +464,54 @@ static void netlink_gen_range(const struct expr *expr,
memset(data, 0, len);
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_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);
}
-void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
+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_data(expr, data);
+ 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));
+ }
+}
+
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand)
+{
+ switch (expr->etype) {
+ case EXPR_VALUE:
+ return netlink_gen_constant_data(expr, data);
+ case EXPR_CONCAT:
+ return netlink_gen_concat_data(expr, data, expand);
case EXPR_VERDICT:
return netlink_gen_verdict(expr, data);
case EXPR_RANGE:
@@ -358,6 +523,11 @@ void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
}
}
+void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
+{
+ __netlink_gen_data(expr, data, false);
+}
+
struct expr *netlink_alloc_value(const struct location *loc,
const struct nft_data_delinearize *nld)
{
@@ -419,71 +589,61 @@ void netlink_dump_expr(const struct nftnl_expr *nle,
fprintf(fp, "\n");
}
-static int list_rule_cb(struct nftnl_rule *nlr, void *arg)
+void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx)
{
- struct netlink_ctx *ctx = arg;
- const struct handle *h = ctx->data;
- struct rule *rule;
- const char *table, *chain;
- uint32_t family;
-
- family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
- 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 ||
- (h->chain.name && strcmp(chain, h->chain.name) != 0))
- return 0;
+ FILE *fp = ctx->nft->output.output_fp;
- netlink_dump_rule(nlr, ctx);
- rule = netlink_delinearize_rule(ctx, nlr);
- list_add_tail(&rule->list, &ctx->list);
+ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
+ return;
- return 0;
+ nftnl_chain_fprintf(fp, nlc, 0, 0);
+ fprintf(fp, "\n");
}
-int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h)
+static int chain_parse_udata_cb(const struct nftnl_udata *attr, void *data)
{
- struct nftnl_rule_list *rule_cache;
-
- rule_cache = mnl_nft_rule_dump(ctx, h->family);
- if (rule_cache == NULL) {
- if (errno == EINTR)
- return -1;
+ unsigned char *value = nftnl_udata_get(attr);
+ uint8_t type = nftnl_udata_type(attr);
+ const struct nftnl_udata **tb = data;
+ uint8_t len = nftnl_udata_len(attr);
- return 0;
+ switch (type) {
+ case NFTNL_UDATA_CHAIN_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
+ default:
+ return 0;
}
-
- ctx->data = h;
- nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx);
- nftnl_rule_list_free(rule_cache);
+ tb[type] = attr;
return 0;
}
-void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx)
+static int qsort_device_cmp(const void *a, const void *b)
{
- FILE *fp = ctx->nft->output.output_fp;
-
- if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
- return;
+ const char **x = (const char **)a;
+ const char **y = (const char **)b;
- nftnl_chain_fprintf(fp, nlc, 0, 0);
- fprintf(fp, "\n");
+ return strcmp(*x, *y);
}
struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
const struct nftnl_chain *nlc)
{
+ 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))
@@ -504,7 +664,7 @@ struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
BYTEORDER_HOST_ENDIAN,
sizeof(int) * BITS_PER_BYTE,
&priority);
- chain->type =
+ chain->type.str =
xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TYPE));
policy = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_POLICY);
chain->policy = constant_expr_alloc(&netlink_location,
@@ -532,67 +692,71 @@ struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
chain->dev_array_len = len;
}
chain->flags |= CHAIN_F_BASECHAIN;
- }
-
- return chain;
-}
-static int list_chain_cb(struct nftnl_chain *nlc, void *arg)
-{
- struct netlink_ctx *ctx = arg;
- const struct handle *h = ctx->data;
- const char *table;
- const char *name;
- struct chain *chain;
- uint32_t family;
-
- table = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE);
- name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME);
- family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
-
- if (h->family != family || strcmp(table, h->table.name) != 0)
- return 0;
- if (h->chain.name && strcmp(name, h->chain.name) != 0)
- return 0;
+ if (chain->dev_array_len) {
+ qsort(chain->dev_array, chain->dev_array_len,
+ sizeof(char *), qsort_device_cmp);
+ }
+ }
- chain = netlink_delinearize_chain(ctx, nlc);
- if (chain->flags & CHAIN_F_BINDING)
- list_add_tail(&chain->list, &ctx->list_bindings);
- else
- list_add_tail(&chain->list, &ctx->list);
+ if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_USERDATA)) {
+ udata = nftnl_chain_get_data(nlc, NFTNL_CHAIN_USERDATA, &ulen);
+ if (nftnl_udata_parse(udata, ulen, chain_parse_udata_cb, ud) < 0) {
+ netlink_io_error(ctx, NULL, "Cannot parse userdata");
+ chain_free(chain);
+ return NULL;
+ }
+ if (ud[NFTNL_UDATA_CHAIN_COMMENT])
+ chain->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_CHAIN_COMMENT]));
+ }
- return 0;
+ return chain;
}
-int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h)
+static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data)
{
- struct nftnl_chain_list *chain_cache;
-
- chain_cache = mnl_nft_chain_dump(ctx, h->family);
- if (chain_cache == NULL) {
- if (errno == EINTR)
- return -1;
+ unsigned char *value = nftnl_udata_get(attr);
+ const struct nftnl_udata **tb = data;
+ uint8_t type = nftnl_udata_type(attr);
+ uint8_t len = nftnl_udata_len(attr);
- return 0;
+ switch (type) {
+ case NFTNL_UDATA_TABLE_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
+ default:
+ return 0;
}
-
- ctx->data = h;
- nftnl_chain_list_foreach(chain_cache, list_chain_cb, ctx);
- nftnl_chain_list_free(chain_cache);
-
+ tb[type] = attr;
return 0;
}
struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
const struct nftnl_table *nlt)
{
+ const struct nftnl_udata *ud[NFTNL_UDATA_TABLE_MAX + 1] = {};
struct table *table;
+ const char *udata;
+ uint32_t ulen;
table = table_alloc();
table->handle.family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY);
table->handle.table.name = xstrdup(nftnl_table_get_str(nlt, NFTNL_TABLE_NAME));
table->flags = nftnl_table_get_u32(nlt, NFTNL_TABLE_FLAGS);
table->handle.handle.id = nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE);
+ table->owner = nftnl_table_get_u32(nlt, NFTNL_TABLE_OWNER);
+
+ if (nftnl_table_is_set(nlt, NFTNL_TABLE_USERDATA)) {
+ udata = nftnl_table_get_data(nlt, NFTNL_TABLE_USERDATA, &ulen);
+ if (nftnl_udata_parse(udata, ulen, table_parse_udata_cb, ud) < 0) {
+ netlink_io_error(ctx, NULL, "Cannot parse userdata");
+ table_free(table);
+ return NULL;
+ }
+ if (ud[NFTNL_UDATA_TABLE_COMMENT])
+ table->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_TABLE_COMMENT]));
+ }
return table;
}
@@ -608,16 +772,24 @@ static int list_table_cb(struct nftnl_table *nlt, void *arg)
return 0;
}
-int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h)
+int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter)
{
struct nftnl_table_list *table_cache;
+ uint32_t family = h->family;
+ const char *table = NULL;
+
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ }
- table_cache = mnl_nft_table_dump(ctx, h->family);
+ table_cache = mnl_nft_table_dump(ctx, family, table);
if (table_cache == NULL) {
if (errno == EINTR)
return -1;
- return 0;
+ return -1;
}
ctx->data = h;
@@ -638,13 +810,17 @@ 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);
}
}
@@ -661,6 +837,7 @@ void netlink_dump_set(const struct nftnl_set *nls, struct netlink_ctx *ctx)
static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data)
{
+ unsigned char *value = nftnl_udata_get(attr);
const struct nftnl_udata **tb = data;
uint8_t type = nftnl_udata_type(attr);
uint8_t len = nftnl_udata_len(attr);
@@ -678,6 +855,10 @@ static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data)
if (len < 3)
return -1;
break;
+ case NFTNL_UDATA_SET_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
default:
return 0;
}
@@ -709,8 +890,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)
@@ -726,7 +907,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)
+ return NULL;
expr = ops->parse_udata(ud[NFTNL_UDATA_SET_TYPEOF_DATA]);
if (!expr)
@@ -735,7 +918,7 @@ static struct expr *set_make_key(const struct nftnl_udata *attr)
return expr;
}
-static bool set_udata_key_valid(const struct expr *e, const struct datatype *d, uint32_t len)
+static bool set_udata_key_valid(const struct expr *e, uint32_t len)
{
if (!e)
return false;
@@ -743,26 +926,46 @@ static bool set_udata_key_valid(const struct expr *e, const struct datatype *d,
return div_round_up(e->len, BITS_PER_BYTE) == len / BITS_PER_BYTE;
}
+struct setelem_parse_ctx {
+ struct set *set;
+ struct nft_cache *cache;
+ struct list_head stmt_list;
+};
+
+static int set_elem_parse_expressions(struct nftnl_expr *e, void *data)
+{
+ struct setelem_parse_ctx *setelem_parse_ctx = data;
+ struct nft_cache *cache = setelem_parse_ctx->cache;
+ struct set *set = setelem_parse_ctx->set;
+ struct stmt *stmt;
+
+ stmt = netlink_parse_set_expr(set, cache, e);
+ list_add_tail(&stmt->list, &setelem_parse_ctx->stmt_list);
+
+ return 0;
+}
+
struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
const struct nftnl_set *nls)
{
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;
- const char *udata;
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) {
@@ -783,6 +986,8 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
typeof_expr_key = set_make_key(ud[NFTNL_UDATA_SET_KEY_TYPEOF]);
if (ud[NFTNL_UDATA_SET_DATA_TYPEOF])
typeof_expr_data = set_make_key(ud[NFTNL_UDATA_SET_DATA_TYPEOF]);
+ if (ud[NFTNL_UDATA_SET_COMMENT])
+ comment = nftnl_udata_get(ud[NFTNL_UDATA_SET_COMMENT]);
}
key = nftnl_set_get_u32(nls, NFTNL_SET_KEY_TYPE);
@@ -803,8 +1008,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;
}
}
@@ -819,25 +1024,48 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
set->handle.table.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_TABLE));
set->handle.set.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_NAME));
set->automerge = automerge;
+ if (comment)
+ set->comment = xstrdup(comment);
+
+ init_list_head(&set_parse_ctx.stmt_list);
if (nftnl_set_is_set(nls, NFTNL_SET_EXPR)) {
const struct nftnl_expr *nle;
+ struct stmt *stmt;
nle = nftnl_set_get(nls, NFTNL_SET_EXPR);
- set->stmt = netlink_parse_set_expr(set, &ctx->nft->cache, nle);
+ stmt = netlink_parse_set_expr(set, &ctx->nft->cache, nle);
+ list_add_tail(&stmt->list, &set_parse_ctx.stmt_list);
+ } else if (nftnl_set_is_set(nls, NFTNL_SET_EXPRESSIONS)) {
+ set_parse_ctx.cache = &ctx->nft->cache;
+ set_parse_ctx.set = set;
+ nftnl_set_expr_foreach(nls, set_elem_parse_expressions,
+ &set_parse_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, dtype, 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);
@@ -848,29 +1076,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, dtype, klen)) {
- datatype_free(datatype_get(dtype));
+ if (set_udata_key_valid(typeof_expr_key, klen)) {
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;
@@ -897,40 +1117,16 @@ 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;
}
-static int list_set_cb(struct nftnl_set *nls, void *arg)
-{
- struct netlink_ctx *ctx = arg;
- struct set *set;
-
- set = netlink_delinearize_set(ctx, nls);
- if (set == NULL)
- return -1;
- list_add_tail(&set->list, &ctx->list);
- return 0;
-}
-
-int netlink_list_sets(struct netlink_ctx *ctx, const struct handle *h)
-{
- struct nftnl_set_list *set_cache;
- int err;
-
- set_cache = mnl_nft_set_dump(ctx, h->family, h->table.name);
- if (set_cache == NULL) {
- if (errno == EINTR)
- return -1;
-
- return 0;
- }
-
- ctx->data = h;
- err = nftnl_set_list_foreach(set_cache, list_set_cb, ctx);
- nftnl_set_list_free(set_cache);
- return err;
-}
-
void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls)
{
struct nftnl_set_elem *nlse;
@@ -942,53 +1138,80 @@ void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls)
}
}
-static bool mpz_bitmask_is_prefix(mpz_t bitmask, uint32_t len)
+static bool range_expr_is_prefix(const struct expr *range, uint32_t *prefix_len)
{
+ const struct expr *right = range->right;
+ const struct expr *left = range->left;
+ uint32_t len = left->len;
unsigned long n1, n2;
+ uint32_t plen;
+ mpz_t bitmask;
- n1 = mpz_scan0(bitmask, 0);
- if (n1 == ULONG_MAX)
- return false;
+ mpz_init2(bitmask, left->len);
+ mpz_xor(bitmask, left->value, right->value);
- n2 = mpz_scan1(bitmask, n1 + 1);
- if (n2 < len)
- return false;
+ n1 = mpz_scan0(bitmask, 0);
+ if (n1 == ULONG_MAX)
+ goto not_a_prefix;
- return true;
-}
+ n2 = mpz_scan1(bitmask, n1 + 1);
+ if (n2 < len)
+ goto not_a_prefix;
-static uint32_t mpz_bitmask_to_prefix(mpz_t bitmask, uint32_t len)
-{
- return len - mpz_scan0(bitmask, 0);
+ plen = len - n1;
+
+ if (mpz_scan1(left->value, 0) < len - plen)
+ goto not_a_prefix;
+
+ mpz_clear(bitmask);
+ *prefix_len = plen;
+
+ return true;
+
+not_a_prefix:
+ mpz_clear(bitmask);
+
+ return false;
}
struct expr *range_expr_to_prefix(struct expr *range)
{
- struct expr *left = range->left, *right = range->right, *prefix;
- uint32_t len = left->len, prefix_len;
- mpz_t bitmask;
-
- mpz_init2(bitmask, len);
- mpz_xor(bitmask, left->value, right->value);
+ struct expr *prefix;
+ uint32_t prefix_len;
- if (mpz_bitmask_is_prefix(bitmask, len)) {
- prefix_len = mpz_bitmask_to_prefix(bitmask, len);
- prefix = prefix_expr_alloc(&range->location, expr_get(left),
+ if (range_expr_is_prefix(range, &prefix_len)) {
+ prefix = prefix_expr_alloc(&range->location,
+ expr_get(range->left),
prefix_len);
- mpz_clear(bitmask);
expr_free(range);
-
return prefix;
}
- mpz_clear(bitmask);
return range;
}
-static struct expr *netlink_parse_interval_elem(const struct datatype *dtype,
+static struct expr *range_expr_reduce(struct expr *range)
+{
+ struct expr *expr;
+
+ if (!mpz_cmp(range->left->value, range->right->value)) {
+ expr = expr_get(range->left);
+ expr_free(range);
+ return expr;
+ }
+
+ if (range->left->dtype->type != TYPE_IPADDR &&
+ range->left->dtype->type != TYPE_IP6ADDR)
+ return range;
+
+ return range_expr_to_prefix(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);
+ const struct datatype *dtype = set->data->dtype;
struct expr *range, *left, *right;
char data[len];
@@ -1005,31 +1228,103 @@ static struct expr *netlink_parse_interval_elem(const struct datatype *dtype,
return range_expr_to_prefix(range);
}
-static struct expr *netlink_parse_concat_elem(const struct datatype *dtype,
- struct expr *data)
+static struct expr *concat_elem_expr(const struct set *set, struct expr *key,
+ const struct datatype *dtype,
+ struct expr *data, int *off)
{
const struct datatype *subtype;
- struct expr *concat, *expr;
+ unsigned int sub_length;
+ struct expr *expr;
+
+ if (key) {
+ (*off)--;
+ sub_length = round_up(key->len, BITS_PER_BYTE);
+
+ expr = constant_expr_splice(data, sub_length);
+ expr->dtype = datatype_get(key->dtype);
+ expr->byteorder = key->byteorder;
+ expr->len = key->len;
+ } else {
+ subtype = concat_subtype_lookup(dtype->type, --(*off));
+ sub_length = round_up(subtype->size, BITS_PER_BYTE);
+ expr = constant_expr_splice(data, sub_length);
+ expr->dtype = subtype;
+ expr->byteorder = subtype->byteorder;
+ }
+
+ 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 &&
+ expr->dtype->basetype->type == TYPE_BITMASK)
+ expr = bitmask_expr_to_binops(expr);
+
+ data->len -= netlink_padding_len(sub_length);
+
+ return expr;
+}
+
+static struct expr *netlink_parse_concat_elem_key(const struct set *set,
+ struct expr *data)
+{
+ const struct datatype *dtype = set->key->dtype;
+ struct expr *concat, *expr, *n = NULL;
int off = dtype->subtypes;
+ if (set->key->etype == EXPR_CONCAT)
+ n = list_first_entry(&set->key->expressions, struct expr, list);
+
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- subtype = concat_subtype_lookup(dtype->type, --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);
+ }
- expr = constant_expr_splice(data, subtype->size);
- expr->dtype = subtype;
- expr->byteorder = subtype->byteorder;
+ expr_free(data);
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+ return concat;
+}
- if (expr->dtype->basetype != NULL &&
- expr->dtype->basetype->type == TYPE_BITMASK)
- expr = bitmask_expr_to_binops(expr);
+static struct expr *netlink_parse_concat_elem(const struct set *set,
+ struct expr *data)
+{
+ const struct datatype *dtype = set->data->dtype;
+ struct expr *concat, *expr, *left, *range;
+ struct list_head expressions;
+ int off = dtype->subtypes;
- compound_expr_add(concat, expr);
- data->len -= netlink_padding_len(expr->len);
+ init_list_head(&expressions);
+
+ concat = concat_expr_alloc(&data->location);
+ while (off > 0) {
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
+ list_add_tail(&expr->list, &expressions);
}
+
+ if (set->data->flags & EXPR_F_INTERVAL) {
+ assert(!list_empty(&expressions));
+
+ off = dtype->subtypes;
+
+ while (off > 0) {
+ left = list_first_entry(&expressions, struct expr, list);
+
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
+ list_del(&left->list);
+
+ range = range_expr_alloc(&data->location, left, expr);
+ range = range_expr_reduce(range);
+ compound_expr_add(concat, range);
+ }
+ assert(list_empty(&expressions));
+ } else {
+ list_splice_tail(&expressions, &concat->expressions);
+ }
+
expr_free(data);
return concat;
@@ -1080,31 +1375,48 @@ static void set_elem_parse_udata(struct nftnl_set_elem *nlse,
int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
struct set *set, struct nft_cache *cache)
{
+ struct setelem_parse_ctx setelem_parse_ctx = {
+ .set = set,
+ .cache = cache,
+ };
struct nft_data_delinearize nld;
struct expr *expr, *key, *data;
uint32_t flags = 0;
- nld.value =
- nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_KEY, &nld.len);
+ init_list_head(&setelem_parse_ctx.stmt_list);
+
+ if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_KEY))
+ nld.value = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_KEY, &nld.len);
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_FLAGS))
flags = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_FLAGS);
key_end:
- key = netlink_alloc_value(&netlink_location, &nld);
- datatype_set(key, set->key->dtype);
- key->byteorder = set->key->byteorder;
- if (set->key->dtype->subtypes)
- key = netlink_parse_concat_elem(set->key->dtype, key);
-
- if (!(set->flags & NFT_SET_INTERVAL) &&
- key->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(key->value, key->len / BITS_PER_BYTE);
-
- if (key->dtype->basetype != NULL &&
- key->dtype->basetype->type == TYPE_BITMASK)
- key = bitmask_expr_to_binops(key);
+ if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_KEY)) {
+ key = netlink_alloc_value(&netlink_location, &nld);
+ datatype_set(key, set->key->dtype);
+ key->byteorder = set->key->byteorder;
+ if (set->key->dtype->subtypes)
+ key = netlink_parse_concat_elem_key(set, key);
+
+ if (!(set->flags & NFT_SET_INTERVAL) &&
+ key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(key->value, key->len / BITS_PER_BYTE);
+
+ if (key->dtype->basetype != NULL &&
+ key->dtype->basetype->type == TYPE_BITMASK)
+ key = bitmask_expr_to_binops(key);
+ } else if (flags & NFT_SET_ELEM_CATCHALL) {
+ key = set_elem_catchall_expr_alloc(&netlink_location);
+ datatype_set(key, set->key->dtype);
+ key->byteorder = set->key->byteorder;
+ key->len = set->key->len;
+ } else {
+ BUG("Unexpected set element with no key\n");
+ }
expr = set_elem_expr_alloc(&netlink_location, key);
+ expr->flags |= EXPR_F_KERNEL;
+
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT))
expr->timeout = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_TIMEOUT);
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_EXPIRATION))
@@ -1113,10 +1425,17 @@ key_end:
set_elem_parse_udata(nlse, expr);
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);
- expr->stmt = netlink_parse_set_expr(set, cache, nle);
+ stmt = netlink_parse_set_expr(set, 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,
+ &setelem_parse_ctx);
}
+ list_splice_tail_init(&setelem_parse_ctx.stmt_list, &expr->stmt_list);
+
if (flags & NFT_SET_ELEM_INTERVAL_END) {
expr->flags |= EXPR_F_INTERVAL_END;
if (mpz_cmp_ui(set->key->value, 0) == 0)
@@ -1141,10 +1460,10 @@ key_end:
datatype_set(data, set->data->dtype);
data->byteorder = set->data->byteorder;
- if (set->data->flags & EXPR_F_INTERVAL)
- data = netlink_parse_interval_elem(set->data->dtype, data);
- else if (set->data->dtype->subtypes)
- data = netlink_parse_concat_elem(set->data->dtype, data);
+ if (set->data->dtype->subtypes) {
+ data = netlink_parse_concat_elem(set, data);
+ } else if (set->data->flags & EXPR_F_INTERVAL)
+ data = netlink_parse_interval_elem(set, data);
if (data->byteorder == BYTEORDER_HOST_ENDIAN)
mpz_switch_byteorder(data->value, data->len / BITS_PER_BYTE);
@@ -1185,8 +1504,44 @@ static int list_setelem_cb(struct nftnl_set_elem *nlse, void *arg)
return netlink_delinearize_setelem(nlse, ctx->set, &ctx->nft->cache);
}
+static int list_setelem_debug_cb(struct nftnl_set_elem *nlse, void *arg)
+{
+ int r;
+
+ r = list_setelem_cb(nlse, arg);
+ if (r == 0) {
+ struct netlink_ctx *ctx = arg;
+ FILE *fp = ctx->nft->output.output_fp;
+
+ fprintf(fp, "\t");
+ nftnl_set_elem_fprintf(fp, nlse, 0, 0);
+ fprintf(fp, "\n");
+ }
+
+ return r;
+}
+
+static int list_setelements(struct nftnl_set *s, struct netlink_ctx *ctx)
+{
+ FILE *fp = ctx->nft->output.output_fp;
+
+ if (fp && (ctx->nft->debug_mask & NFT_DEBUG_NETLINK)) {
+ const char *table, *name;
+ uint32_t family = nftnl_set_get_u32(s, NFTNL_SET_FAMILY);
+
+ table = nftnl_set_get_str(s, NFTNL_SET_TABLE);
+ name = nftnl_set_get_str(s, NFTNL_SET_NAME);
+
+ fprintf(fp, "%s %s @%s\n", family2str(family), table, name);
+
+ return nftnl_set_elem_foreach(s, list_setelem_debug_cb, ctx);
+ }
+
+ return nftnl_set_elem_foreach(s, list_setelem_cb, 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;
@@ -1201,7 +1556,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)
@@ -1212,7 +1567,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
ctx->set = set;
set->init = set_expr_alloc(&internal_location, set);
- nftnl_set_elem_foreach(nls, list_setelem_cb, ctx);
+ list_setelements(nls, ctx);
if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1)
concat_range_aggregate(set->init);
@@ -1229,7 +1584,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;
@@ -1248,7 +1603,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;
@@ -1256,7 +1611,7 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
ctx->set = set;
set->init = set_expr_alloc(loc, set);
- nftnl_set_elem_foreach(nls_out, list_setelem_cb, ctx);
+ list_setelements(nls_out, ctx);
if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1)
concat_range_aggregate(set->init);
@@ -1283,11 +1638,33 @@ void netlink_dump_obj(struct nftnl_obj *nln, struct netlink_ctx *ctx)
fprintf(fp, "\n");
}
+static int obj_parse_udata_cb(const struct nftnl_udata *attr, void *data)
+{
+ unsigned char *value = nftnl_udata_get(attr);
+ uint8_t type = nftnl_udata_type(attr);
+ const struct nftnl_udata **tb = data;
+ uint8_t len = nftnl_udata_len(attr);
+
+ switch (type) {
+ case NFTNL_UDATA_OBJ_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
+ default:
+ return 0;
+ }
+ tb[type] = attr;
+ return 0;
+}
+
struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
struct nftnl_obj *nlo)
{
+ const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {};
+ const char *udata;
struct obj *obj;
uint32_t type;
+ uint32_t ulen;
obj = obj_alloc(&netlink_location);
obj->handle.family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY);
@@ -1297,6 +1674,16 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
xstrdup(nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME));
obj->handle.handle.id =
nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE);
+ if (nftnl_obj_is_set(nlo, NFTNL_OBJ_USERDATA)) {
+ udata = nftnl_obj_get_data(nlo, NFTNL_OBJ_USERDATA, &ulen);
+ if (nftnl_udata_parse(udata, ulen, obj_parse_udata_cb, ud) < 0) {
+ netlink_io_error(ctx, NULL, "Cannot parse userdata");
+ obj_free(obj);
+ return NULL;
+ }
+ if (ud[NFTNL_UDATA_OBJ_COMMENT])
+ obj->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_OBJ_COMMENT]));
+ }
type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE);
switch (type) {
@@ -1325,6 +1712,7 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
obj->ct_helper.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_HELPER_L4PROTO);
break;
case NFT_OBJECT_CT_TIMEOUT:
+ 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,
@@ -1393,25 +1781,6 @@ static int list_obj_cb(struct nftnl_obj *nls, void *arg)
return 0;
}
-int netlink_list_objs(struct netlink_ctx *ctx, const struct handle *h)
-{
- struct nftnl_obj_list *obj_cache;
- int err;
-
- obj_cache = mnl_nft_obj_dump(ctx, h->family,
- h->table.name, NULL, 0, true, false);
- if (obj_cache == NULL) {
- if (errno == EINTR)
- return -1;
-
- return 0;
- }
-
- err = nftnl_obj_list_foreach(obj_cache, list_obj_cb, ctx);
- nftnl_obj_list_free(obj_cache);
- return err;
-}
-
int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd,
uint32_t type, bool dump)
{
@@ -1429,7 +1798,56 @@ int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd,
return err;
}
-static struct flowtable *
+int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd,
+ bool dump)
+{
+ const struct handle *h = &cmd->handle;
+ struct nft_cache_filter f = {
+ .list.table = h->table.name,
+ .list.chain = h->chain.name,
+ .list.rule_handle = h->handle.id,
+ };
+ struct rule *rule, *next, *crule, *cnext;
+ struct table *table;
+ struct chain *chain;
+ int ret;
+
+ ret = rule_cache_dump(ctx, h, &f, dump, true);
+
+ list_for_each_entry_safe(rule, next, &ctx->list, list) {
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ rule->handle.table.name,
+ rule->handle.family);
+ if (!table)
+ continue;
+
+ chain = chain_cache_find(table, rule->handle.chain.name);
+ if (!chain)
+ continue;
+
+ list_del(&rule->list);
+ list_for_each_entry_safe(crule, cnext, &chain->rules, list) {
+ if (crule->handle.handle.id != rule->handle.handle.id)
+ continue;
+
+ list_replace(&crule->list, &rule->list);
+ rule_free(crule);
+ rule = NULL;
+ break;
+ }
+ if (rule) {
+ list_add_tail(&rule->list, &chain->rules);
+ }
+ }
+ list_for_each_entry_safe(rule, next, &ctx->list, list) {
+ list_del(&rule->list);
+ rule_free(rule);
+ }
+
+ return ret;
+}
+
+struct flowtable *
netlink_delinearize_flowtable(struct netlink_ctx *ctx,
struct nftnl_flowtable *nlo)
{
@@ -1446,16 +1864,24 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
xstrdup(nftnl_flowtable_get_str(nlo, NFTNL_FLOWTABLE_NAME));
flowtable->handle.handle.id =
nftnl_flowtable_get_u64(nlo, NFTNL_FLOWTABLE_HANDLE);
+ 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])
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]);
flowtable->dev_array_len = len;
+ if (flowtable->dev_array_len) {
+ qsort(flowtable->dev_array, flowtable->dev_array_len,
+ sizeof(char *), qsort_device_cmp);
+ }
+
priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO);
flowtable->priority.expr =
constant_expr_alloc(&netlink_location,
@@ -1489,7 +1915,8 @@ int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h)
struct nftnl_flowtable_list *flowtable_cache;
int err;
- flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family, h->table.name);
+ flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family,
+ h->table.name, NULL);
if (flowtable_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -1588,11 +2015,11 @@ static struct rule *trace_lookup_rule(const struct nftnl_trace *nlt,
if (!h.table.name)
return NULL;
- table = table_lookup(&h, cache);
+ table = table_cache_find(&cache->table_cache, h.table.name, h.family);
if (!table)
return NULL;
- chain = chain_lookup(table, &h);
+ chain = chain_cache_find(table, h.chain.name);
if (!chain)
return NULL;
@@ -1635,7 +2062,6 @@ static void trace_gen_stmts(struct list_head *stmts,
const void *hdr;
uint32_t hlen;
unsigned int n;
- bool stacked;
if (!nftnl_trace_is_set(nlt, attr))
return;
@@ -1690,6 +2116,8 @@ restart:
n = 0;
next:
list_for_each_entry(stmt, &unordered, list) {
+ enum proto_bases b = base;
+
rel = stmt->expr;
lhs = rel->left;
@@ -1702,16 +2130,14 @@ next:
list_move_tail(&stmt->list, stmts);
n++;
- stacked = payload_is_stacked(desc, rel);
+ if (payload_is_stacked(desc, rel))
+ b--;
- if (lhs->flags & EXPR_F_PROTOCOL &&
- pctx->pbase == PROTO_BASE_INVALID) {
- payload_dependency_store(pctx, stmt, base - stacked);
- } else {
+ /* Don't strip 'icmp type' from payload dump. */
+ if (pctx->icmp_type == 0)
payload_dependency_kill(pctx, lhs, ctx->family);
- if (lhs->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(pctx, stmt, base - stacked);
- }
+ if (lhs->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(pctx, stmt, b);
goto next;
}
@@ -1740,7 +2166,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)) {
@@ -1786,12 +2212,12 @@ int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type,
if (nftnl_trace_nlmsg_parse(nlh, nlt) < 0)
netlink_abi_error();
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) ||
+ nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER))
+ trace_print_packet(nlt, &monh->ctx->nft->output);
+
switch (nftnl_trace_get_u32(nlt, NFTNL_TRACE_TYPE)) {
case NFT_TRACETYPE_RULE:
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) ||
- nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER))
- trace_print_packet(nlt, &monh->ctx->nft->output);
-
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE))
trace_print_rule(nlt, &monh->ctx->nft->output,
&monh->ctx->nft->cache);
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 9e3ed53d..da9f7a91 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>
@@ -27,8 +26,19 @@
#include <erec.h>
#include <sys/socket.h>
#include <libnftnl/udata.h>
+#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);
@@ -71,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;
}
@@ -99,7 +108,7 @@ static void netlink_release_registers(struct netlink_parse_ctx *ctx)
{
int i;
- for (i = 0; i < MAX_REGS; i++)
+ for (i = 0; i <= MAX_REGS; i++)
expr_free(ctx->registers[i]);
}
@@ -133,6 +142,50 @@ err:
return NULL;
}
+static struct expr *netlink_parse_concat_key(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ unsigned int reg,
+ const struct expr *key)
+{
+ uint32_t type = key->dtype->type;
+ unsigned int n, len = key->len;
+ struct expr *concat, *expr;
+ unsigned int consumed;
+
+ concat = concat_expr_alloc(loc);
+ n = div_round_up(fls(type), TYPE_BITS);
+
+ while (len > 0) {
+ const struct datatype *i;
+
+ expr = netlink_get_register(ctx, loc, reg);
+ if (expr == NULL) {
+ netlink_error(ctx, loc,
+ "Concat expression size mismatch");
+ goto err;
+ }
+
+ if (n > 0 && concat_subtype_id(type, --n)) {
+ i = concat_subtype_lookup(type, n);
+
+ expr_set_type(expr, i, i->byteorder);
+ }
+
+ compound_expr_add(concat, expr);
+
+ consumed = netlink_padded_len(expr->len);
+ assert(consumed > 0);
+ len -= consumed;
+ reg += netlink_register_space(expr->len);
+ }
+
+ return concat;
+
+err:
+ expr_free(concat);
+ return NULL;
+}
+
static struct expr *netlink_parse_concat_data(struct netlink_parse_ctx *ctx,
const struct location *loc,
unsigned int reg,
@@ -155,6 +208,7 @@ static struct expr *netlink_parse_concat_data(struct netlink_parse_ctx *ctx,
len -= netlink_padded_len(expr->len);
reg += netlink_register_space(expr->len);
+ expr_free(expr);
}
return concat;
@@ -173,6 +227,13 @@ static void netlink_parse_chain_verdict(struct netlink_parse_ctx *ctx,
expr_chain_export(expr->chain, chain_name);
chain = chain_binding_lookup(ctx->table, chain_name);
+
+ /* Special case: 'nft list chain x y' needs to pull in implicit chains */
+ if (!chain && !strncmp(chain_name, "__chain", strlen("__chain"))) {
+ nft_chain_cache_update(ctx->nlctx, ctx->table, chain_name);
+ chain = chain_binding_lookup(ctx->table, chain_name);
+ }
+
if (chain) {
ctx->stmt = chain_stmt_alloc(loc, chain, verdict);
expr_free(expr);
@@ -321,8 +382,9 @@ static void netlink_parse_cmp(struct netlink_parse_ctx *ctx,
if (left->len > right->len &&
expr_basetype(left) != &string_type) {
- netlink_error(ctx, loc, "Relational expression size mismatch");
- goto err_free;
+ mpz_lshift_ui(right->value, left->len - right->len);
+ right = prefix_expr_alloc(loc, right, right->len);
+ right->prefix->len = left->len;
} else if (left->len > 0 && left->len < right->len) {
expr_free(left);
left = netlink_parse_concat_expr(ctx, loc, sreg, right->len);
@@ -354,7 +416,7 @@ static void netlink_parse_lookup(struct netlink_parse_ctx *ctx,
uint32_t flag;
name = nftnl_expr_get_str(nle, NFTNL_EXPR_LOOKUP_SET);
- set = set_lookup(ctx->table, name);
+ set = set_cache_find(ctx->table, name);
if (set == NULL)
return netlink_error(ctx, loc,
"Unknown set '%s' in lookup expression",
@@ -425,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 {
@@ -473,7 +535,7 @@ static struct expr *netlink_parse_bitwise_shift(struct netlink_parse_ctx *ctx,
right->byteorder = BYTEORDER_HOST_ENDIAN;
expr = binop_expr_alloc(loc, op, left, right);
- expr->len = left->len;
+ expr->len = nftnl_expr_get_u32(nle, NFTNL_EXPR_BITWISE_LEN) * BITS_PER_BYTE;
return expr;
}
@@ -559,6 +621,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;
@@ -566,9 +632,80 @@ 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:
+ assert(0);
+ break;
+ }
+
+ 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)
@@ -635,7 +772,7 @@ static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx,
sreg = netlink_parse_register(nle, NFTNL_EXPR_EXTHDR_SREG);
val = netlink_get_register(ctx, loc, sreg);
if (val == NULL) {
- xfree(expr);
+ expr_free(expr);
return netlink_error(ctx, loc,
"exthdr statement has no expression");
}
@@ -644,6 +781,10 @@ static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx,
stmt = exthdr_stmt_alloc(loc, expr, val);
rule_stmt_append(ctx->rule, stmt);
+ } else {
+ struct stmt *stmt = optstrip_stmt_alloc(loc, expr);
+
+ rule_stmt_append(ctx->rule, stmt);
}
}
@@ -677,7 +818,7 @@ static void netlink_parse_hash(struct netlink_parse_ctx *ctx,
len = nftnl_expr_get_u32(nle,
NFTNL_EXPR_HASH_LEN) * BITS_PER_BYTE;
if (hexpr->len < len) {
- xfree(hexpr);
+ expr_free(hexpr);
hexpr = netlink_parse_concat_expr(ctx, loc, sreg, len);
if (hexpr == NULL)
goto out_err;
@@ -689,7 +830,7 @@ static void netlink_parse_hash(struct netlink_parse_ctx *ctx,
netlink_set_register(ctx, dreg, expr);
return;
out_err:
- xfree(expr);
+ expr_free(expr);
}
static void netlink_parse_fib(struct netlink_parse_ctx *ctx,
@@ -721,6 +862,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);
}
@@ -729,11 +873,12 @@ static void netlink_parse_socket(struct netlink_parse_ctx *ctx,
const struct nftnl_expr *nle)
{
enum nft_registers dreg;
- uint32_t key;
+ uint32_t key, level;
struct expr * expr;
key = nftnl_expr_get_u32(nle, NFTNL_EXPR_SOCKET_KEY);
- expr = socket_expr_alloc(loc, key);
+ level = nftnl_expr_get_u32(nle, NFTNL_EXPR_SOCKET_LEVEL);
+ expr = socket_expr_alloc(loc, key, level);
dreg = netlink_parse_register(nle, NFTNL_EXPR_SOCKET_DREG);
netlink_set_register(ctx, dreg, expr);
@@ -773,7 +918,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;
}
@@ -807,8 +954,8 @@ static void netlink_parse_numgen(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- enum nft_registers dreg;
uint32_t type, until, offset;
+ enum nft_registers dreg;
struct expr *expr;
type = nftnl_expr_get_u32(nle, NFTNL_EXPR_NG_TYPE);
@@ -920,6 +1067,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)
@@ -1012,7 +1172,8 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
-static bool is_nat_addr_map(const struct expr *addr, uint8_t family)
+static bool is_nat_addr_map(const struct expr *addr, uint8_t family,
+ struct stmt *stmt)
{
const struct expr *mappings, *data;
const struct set *set;
@@ -1031,14 +1192,31 @@ static bool is_nat_addr_map(const struct expr *addr, uint8_t family)
if (!(data->flags & EXPR_F_INTERVAL))
return false;
+ stmt->nat.family = family;
+
/* if we're dealing with an address:address map,
* the length will be bit_sizeof(addr) + 32 (one register).
*/
switch (family) {
case NFPROTO_IPV4:
- return data->len == 32 + 32;
+ if (data->len == 32 + 32) {
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
+ return true;
+ } else if (data->len == 32 + 32 + 32 + 32) {
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL |
+ STMT_NAT_F_CONCAT;
+ return true;
+ }
+ break;
case NFPROTO_IPV6:
- return data->len == 128 + 128;
+ if (data->len == 128 + 128) {
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
+ return true;
+ } else if (data->len == 128 + 32 + 128 + 32) {
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL |
+ STMT_NAT_F_CONCAT;
+ return true;
+ }
}
return false;
@@ -1114,9 +1292,8 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
stmt->nat.addr = addr;
}
- if (is_nat_addr_map(addr, family)) {
+ if (is_nat_addr_map(addr, family, stmt)) {
stmt->nat.family = family;
- stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
ctx->stmt = stmt;
return;
}
@@ -1180,7 +1357,7 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_synproxy(struct netlink_parse_ctx *ctx,
@@ -1244,7 +1421,7 @@ static void netlink_parse_tproxy(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_masq(struct netlink_parse_ctx *ctx,
@@ -1291,7 +1468,7 @@ static void netlink_parse_masq(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_redir(struct netlink_parse_ctx *ctx,
@@ -1342,7 +1519,7 @@ static void netlink_parse_redir(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_dup(struct netlink_parse_ctx *ctx,
@@ -1395,7 +1572,7 @@ static void netlink_parse_dup(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_fwd(struct netlink_parse_ctx *ctx,
@@ -1457,49 +1634,88 @@ static void netlink_parse_fwd(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_queue(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- struct expr *expr, *high;
- struct stmt *stmt;
- uint16_t num, total;
+ struct expr *expr;
+ uint16_t flags;
- num = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_NUM);
- total = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_TOTAL);
+ if (nftnl_expr_is_set(nle, NFTNL_EXPR_QUEUE_SREG_QNUM)) {
+ enum nft_registers reg = netlink_parse_register(nle, NFTNL_EXPR_QUEUE_SREG_QNUM);
- expr = constant_expr_alloc(loc, &integer_type,
- BYTEORDER_HOST_ENDIAN, 16, &num);
- if (total > 1) {
- total += num - 1;
- high = constant_expr_alloc(loc, &integer_type,
+ expr = netlink_get_register(ctx, loc, reg);
+ if (!expr) {
+ netlink_error(ctx, loc, "queue statement has no sreg expression");
+ return;
+ }
+ } else {
+ uint16_t total = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_TOTAL);
+ uint16_t num = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_NUM);
+
+ expr = constant_expr_alloc(loc, &integer_type,
+ BYTEORDER_HOST_ENDIAN, 16, &num);
+
+ if (total > 1) {
+ struct expr *high;
+
+ total += num - 1;
+ high = constant_expr_alloc(loc, &integer_type,
BYTEORDER_HOST_ENDIAN, 16, &total);
- expr = range_expr_alloc(loc, expr, high);
+ expr = range_expr_alloc(loc, expr, high);
+ }
}
- stmt = queue_stmt_alloc(loc);
- stmt->queue.queue = expr;
- stmt->queue.flags = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_FLAGS);
+ flags = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_FLAGS);
+ ctx->stmt = queue_stmt_alloc(loc, expr, flags);
+}
- ctx->stmt = stmt;
+struct dynset_parse_ctx {
+ struct netlink_parse_ctx *nlctx;
+ const struct location *loc;
+ struct list_head stmt_list;
+};
+
+static int dynset_parse_expressions(struct nftnl_expr *e, void *data)
+{
+ struct dynset_parse_ctx *dynset_parse_ctx = data;
+ struct netlink_parse_ctx *ctx = dynset_parse_ctx->nlctx;
+ const struct location *loc = dynset_parse_ctx->loc;
+ struct stmt *stmt;
+
+ if (netlink_parse_expr(e, ctx) < 0 || !ctx->stmt) {
+ netlink_error(ctx, loc, "Could not parse dynset stmt");
+ return -1;
+ }
+ stmt = ctx->stmt;
+
+ list_add_tail(&stmt->list, &dynset_parse_ctx->stmt_list);
+
+ return 0;
}
static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
+ struct dynset_parse_ctx dynset_parse_ctx = {
+ .nlctx = ctx,
+ .loc = loc,
+ };
struct expr *expr, *expr_data = NULL;
enum nft_registers sreg, sreg_data;
+ struct stmt *stmt, *dstmt, *next;
const struct nftnl_expr *dnle;
- struct stmt *stmt, *dstmt;
struct set *set;
const char *name;
+ init_list_head(&dynset_parse_ctx.stmt_list);
+
name = nftnl_expr_get_str(nle, NFTNL_EXPR_DYNSET_SET_NAME);
- set = set_lookup(ctx->table, name);
+ set = set_cache_find(ctx->table, name);
if (set == NULL)
return netlink_error(ctx, loc,
"Unknown set '%s' in dynset statement",
@@ -1513,29 +1729,48 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
if (expr->len < set->key->len) {
expr_free(expr);
- expr = netlink_parse_concat_expr(ctx, loc, sreg, set->key->len);
+ 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);
expr->timeout = nftnl_expr_get_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT);
- dstmt = NULL;
- dnle = nftnl_expr_get(nle, NFTNL_EXPR_DYNSET_EXPR, NULL);
- if (dnle != NULL) {
- if (netlink_parse_expr(dnle, ctx) < 0)
- goto out_err;
- if (ctx->stmt == NULL) {
- netlink_error(ctx, loc, "Could not parse dynset stmt");
- goto out_err;
+ if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_EXPR)) {
+ dstmt = NULL;
+ dnle = nftnl_expr_get(nle, NFTNL_EXPR_DYNSET_EXPR, NULL);
+ if (dnle != NULL) {
+ if (netlink_parse_expr(dnle, ctx) < 0)
+ goto out_err;
+ if (ctx->stmt == NULL) {
+ netlink_error(ctx, loc,
+ "Could not parse dynset stmt");
+ goto out_err;
+ }
+ dstmt = ctx->stmt;
+ list_add_tail(&dstmt->list,
+ &dynset_parse_ctx.stmt_list);
}
- dstmt = ctx->stmt;
+ } else if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS)) {
+ if (nftnl_expr_expr_foreach(nle, dynset_parse_expressions,
+ &dynset_parse_ctx) < 0)
+ goto out_err;
}
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) {
@@ -1544,28 +1779,35 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
stmt->map.set = set_ref_expr_alloc(loc, set);
stmt->map.key = expr;
stmt->map.data = expr_data;
- stmt->map.stmt = dstmt;
stmt->map.op = nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP);
+ list_splice_tail(&dynset_parse_ctx.stmt_list,
+ &stmt->map.stmt_list);
} else {
- if (dstmt != NULL && set->flags & NFT_SET_ANONYMOUS) {
+ if (!list_empty(&dynset_parse_ctx.stmt_list) &&
+ set_is_anonymous(set->flags)) {
stmt = meter_stmt_alloc(loc);
stmt->meter.set = set_ref_expr_alloc(loc, set);
stmt->meter.key = expr;
- stmt->meter.stmt = dstmt;
+ stmt->meter.stmt = list_first_entry(&dynset_parse_ctx.stmt_list,
+ struct stmt, list);
stmt->meter.size = set->desc.size;
} else {
stmt = set_stmt_alloc(loc);
stmt->set.set = set_ref_expr_alloc(loc, set);
stmt->set.op = nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP);
stmt->set.key = expr;
- stmt->set.stmt = dstmt;
+ list_splice_tail(&dynset_parse_ctx.stmt_list,
+ &stmt->set.stmt_list);
}
}
ctx->stmt = stmt;
return;
out_err:
- xfree(expr);
+ list_for_each_entry_safe(dstmt, next, &dynset_parse_ctx.stmt_list, list)
+ stmt_free(dstmt);
+
+ expr_free(expr);
}
static void netlink_parse_objref(struct netlink_parse_ctx *ctx,
@@ -1592,7 +1834,7 @@ static void netlink_parse_objref(struct netlink_parse_ctx *ctx,
struct set *set;
name = nftnl_expr_get_str(nle, NFTNL_EXPR_OBJREF_SET_NAME);
- set = set_lookup(ctx->table, name);
+ set = set_cache_find(ctx->table, name);
if (set == NULL)
return netlink_error(ctx, loc,
"Unknown set '%s' in objref expression",
@@ -1627,18 +1869,21 @@ static void netlink_parse_objref(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
-static const struct {
+struct expr_handler {
const char *name;
void (*parse)(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle);
-} netlink_parsers[] = {
+};
+
+static const struct expr_handler netlink_parsers[] = {
{ .name = "immediate", .parse = netlink_parse_immediate },
{ .name = "cmp", .parse = netlink_parse_cmp },
{ .name = "lookup", .parse = netlink_parse_lookup },
{ .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 },
@@ -1647,6 +1892,7 @@ static const struct {
{ .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 },
@@ -1687,11 +1933,13 @@ static int netlink_parse_expr(const struct nftnl_expr *nle,
for (i = 0; i < array_size(netlink_parsers); i++) {
if (strcmp(type, netlink_parsers[i].name))
continue;
+
netlink_parsers[i].parse(ctx, &loc, nle);
+
return 0;
}
-
netlink_error(ctx, &loc, "unknown expression type '%s'", type);
+
return 0;
}
@@ -1719,7 +1967,9 @@ struct stmt *netlink_parse_set_expr(const struct set *set,
handle_merge(&h, &set->handle);
pctx->rule = rule_alloc(&netlink_location, &h);
- pctx->table = table_lookup(&set->handle, cache);
+ pctx->table = table_cache_find(&cache->table_cache,
+ set->handle.table.name,
+ set->handle.family);
assert(pctx->table != NULL);
if (netlink_parse_expr(nle, pctx) < 0)
@@ -1731,6 +1981,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,
@@ -1739,21 +2020,27 @@ 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;
- bool stacked;
+ 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);
expr_set_type(tmp, left->dtype, left->byteorder);
+ if (left->payload.tmpl && (left->len < left->payload.tmpl->len)) {
+ mpz_lshift_ui(tmp->value, left->payload.tmpl->len - left->len);
+ tmp->len = left->payload.tmpl->len;
+ tmp = prefix_expr_alloc(&tmp->location, tmp, left->len);
+ }
+
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);
@@ -1762,36 +2049,95 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
assert(left->payload.base);
assert(base == left->payload.base);
- stacked = 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.
*/
- if (ctx->pdctx.pbase == PROTO_BASE_INVALID &&
- expr->op == OP_EQ &&
- left->flags & EXPR_F_PROTOCOL) {
- payload_dependency_store(&ctx->pdctx, nstmt, base - stacked);
- } else {
- payload_dependency_kill(&ctx->pdctx, nexpr->left,
- ctx->pctx.family);
- if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, nstmt, base - stacked);
- }
+ payload_dependency_kill(&dl->pdctx, nexpr->left,
+ dl->pctx.family);
+ if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(&dl->pdctx, nstmt, base);
}
list_del(&ctx->stmt->list);
stmt_free(ctx->stmt);
ctx->stmt = NULL;
}
+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;
+ unsigned int i;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+ assert(value->etype == EXPR_VALUE);
+
+ if (expr->payload.base != PROTO_BASE_TRANSPORT_HDR)
+ return;
+
+ /* 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 || dl->pctx.th_dep.icmp.type)
+ return;
+
+ desc = dl->pctx.protocol[expr->payload.base].desc;
+ if (desc == NULL)
+ return;
+
+ /* not icmp? ignore. */
+ if (desc != &proto_icmp && desc != &proto_icmp6)
+ return;
+
+ assert(desc->base == expr->payload.base);
+
+ icmp_type = mpz_get_uint8(value->value);
+
+ for (i = 1; i < array_size(desc->templates); i++) {
+ tmpl = &desc->templates[i];
+
+ if (tmpl->len == 0)
+ return;
+
+ if (tmpl->offset != expr->payload.offset ||
+ tmpl->len != expr->len)
+ continue;
+
+ /* Matches but doesn't load a protocol key -> ignore. */
+ if (desc->protocol_key != i)
+ return;
+
+ expr->payload.desc = desc;
+ expr->payload.tmpl = tmpl;
+ dl->pctx.th_dep.icmp.type = icmp_type;
+ return;
+ }
+}
+
static void payload_match_postprocess(struct rule_pp_ctx *ctx,
struct expr *expr,
struct expr *payload)
{
- enum proto_bases base = payload->payload.base;
-
- assert(payload->payload.offset >= ctx->pctx.protocol[base].offset);
- payload->payload.offset -= ctx->pctx.protocol[base].offset;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
switch (expr->op) {
case OP_EQ:
@@ -1799,19 +2145,82 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
if (expr->right->etype == EXPR_VALUE) {
payload_match_expand(ctx, expr, payload);
break;
+ } else if (expr->right->etype == EXPR_SET_REF) {
+ struct set *set = expr->right->set;
+
+ if (set_is_anonymous(set->flags) &&
+ set->init &&
+ !list_empty(&set->init->expressions)) {
+ struct expr *elem;
+
+ elem = list_first_entry(&set->init->expressions, struct expr, list);
+
+ if (elem->etype == EXPR_SET_ELEM &&
+ elem->key->etype == EXPR_VALUE)
+ payload_icmp_check(ctx, payload, elem->key);
+ }
}
/* 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;
+ }
+}
+
+static uint8_t ether_type_to_nfproto(uint16_t l3proto)
+{
+ switch(l3proto) {
+ case ETH_P_IP:
+ return NFPROTO_IPV4;
+ case ETH_P_IPV6:
+ return NFPROTO_IPV6;
+ default:
+ break;
+ }
+
+ return NFPROTO_UNSPEC;
+}
+
+static bool __meta_dependency_may_kill(const struct expr *dep, uint8_t *nfproto)
+{
+ uint16_t l3proto;
+
+ switch (dep->left->etype) {
+ case EXPR_META:
+ switch (dep->left->meta.key) {
+ case NFT_META_NFPROTO:
+ *nfproto = mpz_get_uint8(dep->right->value);
+ break;
+ case NFT_META_PROTOCOL:
+ l3proto = mpz_get_uint16(dep->right->value);
+ *nfproto = ether_type_to_nfproto(l3proto);
+ break;
+ default:
+ return true;
+ }
+ break;
+ case EXPR_PAYLOAD:
+ if (dep->left->payload.base != PROTO_BASE_LL_HDR)
+ return true;
+
+ if (dep->left->dtype != &ethertype_type)
+ return true;
+
+ l3proto = mpz_get_uint16(dep->right->value);
+ *nfproto = ether_type_to_nfproto(l3proto);
break;
+ default:
+ return true;
}
+
+ return false;
}
/* We have seen a protocol key expression that restricts matching at the network
- * base, leave it in place since this is meaninful in bridge, inet and netdev
+ * 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.
*/
@@ -1819,11 +2228,13 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
unsigned int family,
const struct expr *expr)
{
- struct expr *dep = ctx->pdep->expr;
- uint16_t l3proto;
- uint8_t l4proto;
+ uint8_t l4proto, nfproto = NFPROTO_UNSPEC;
+ struct expr *dep = payload_dependency_get(ctx, PROTO_BASE_NETWORK_HDR);
+
+ if (!dep)
+ return true;
- if (ctx->pbase != PROTO_BASE_NETWORK_HDR)
+ if (__meta_dependency_may_kill(dep, &nfproto))
return true;
switch (family) {
@@ -1831,6 +2242,9 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
case NFPROTO_NETDEV:
case NFPROTO_BRIDGE:
break;
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ return family == nfproto;
default:
return true;
}
@@ -1838,32 +2252,6 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
if (expr->left->meta.key != NFT_META_L4PROTO)
return true;
- l3proto = mpz_get_uint16(dep->right->value);
-
- switch (dep->left->etype) {
- case EXPR_META:
- if (dep->left->meta.key != NFT_META_NFPROTO)
- return true;
- break;
- case EXPR_PAYLOAD:
- if (dep->left->payload.base != PROTO_BASE_LL_HDR)
- return true;
-
- switch(l3proto) {
- case ETH_P_IP:
- l3proto = NFPROTO_IPV4;
- break;
- case ETH_P_IPV6:
- l3proto = NFPROTO_IPV6;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
l4proto = mpz_get_uint8(expr->right->value);
switch (l4proto) {
@@ -1874,8 +2262,8 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
return false;
}
- if ((l3proto == NFPROTO_IPV4 && l4proto == IPPROTO_ICMPV6) ||
- (l3proto == NFPROTO_IPV6 && l4proto == IPPROTO_ICMP))
+ if ((nfproto == NFPROTO_IPV4 && l4proto == IPPROTO_ICMPV6) ||
+ (nfproto == NFPROTO_IPV6 && l4proto == IPPROTO_ICMP))
return false;
return true;
@@ -1885,6 +2273,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;
@@ -1898,18 +2287,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(&dl->pdctx, base) &&
+ meta_may_dependency_kill(&dl->pdctx,
+ dl->pctx.family, expr))
+ payload_dependency_release(&dl->pdctx, base);
- if (ctx->pdctx.pbase == PROTO_BASE_INVALID &&
- left->flags & EXPR_F_PROTOCOL) {
- payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
- } else if (ctx->pdctx.pbase < 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);
if (left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
+ payload_dependency_store(&dl->pdctx, ctx->stmt, base);
}
break;
default:
@@ -1994,8 +2381,8 @@ static void binop_adjust_one(const struct expr *binop, struct expr *value,
}
}
-static void __binop_adjust(const struct expr *binop, struct expr *right,
- unsigned int shift)
+static void binop_adjust(const struct expr *binop, struct expr *right,
+ unsigned int shift)
{
struct expr *i;
@@ -2004,6 +2391,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:
@@ -2014,7 +2404,7 @@ static void __binop_adjust(const struct expr *binop, struct expr *right,
binop_adjust_one(binop, i->key->right, shift);
break;
case EXPR_SET_ELEM:
- __binop_adjust(binop, i->key->key, shift);
+ binop_adjust(binop, i->key->key, shift);
break;
default:
BUG("unknown expression type %s\n", expr_name(i->key));
@@ -2031,22 +2421,24 @@ static void __binop_adjust(const struct expr *binop, struct expr *right,
}
}
-static void binop_adjust(struct expr *expr, unsigned int shift)
+static void __binop_postprocess(struct rule_pp_ctx *ctx,
+ struct expr *expr,
+ struct expr *left,
+ struct expr *mask,
+ struct expr **expr_binop)
{
- __binop_adjust(expr->left, expr->right, shift);
-}
-
-static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
-{
- struct expr *binop = expr->left;
- struct expr *left = binop->left;
- struct expr *mask = binop->right;
+ 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;
+
/* mask is implicit, binop needs to be removed.
*
* Fix all values of the expression according to the mask
@@ -2056,59 +2448,106 @@ static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
* Finally, convert the expression to 1) by replacing
* the binop with the binop payload/exthdr expression.
*/
- binop_adjust(expr, shift);
+ switch (expr->etype) {
+ case EXPR_BINOP:
+ case EXPR_RELATIONAL:
+ right = expr->right;
+ binop_adjust(binop, right, shift);
+ break;
+ case EXPR_MAP:
+ right = expr->mappings;
+ binop_adjust(binop, right, shift);
+ break;
+ default:
+ break;
+ }
- assert(expr->left->etype == EXPR_BINOP);
assert(binop->left == left);
- expr->left = expr_get(left);
- expr_free(binop);
+ *expr_binop = expr_get(left);
+
if (left->etype == EXPR_PAYLOAD)
payload_match_postprocess(ctx, expr, left);
- else if (left->etype == EXPR_EXTHDR)
- expr_set_type(expr->right, left->dtype, left->byteorder);
+ else if (left->etype == EXPR_EXTHDR && right)
+ expr_set_type(right, left->dtype, left->byteorder);
+
+ expr_free(binop);
}
}
+static void 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);
+}
+
static void map_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
{
- struct expr *binop = expr->left;
+ struct expr *binop = expr->map;
if (binop->op != OP_AND)
return;
if (binop->left->etype == EXPR_PAYLOAD &&
binop->right->etype == EXPR_VALUE)
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->map);
}
-static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
+static bool is_shift_by_zero(const struct expr *binop)
{
- struct expr *binop = expr->left, *value = expr->right;
+ struct expr *rhs;
+
+ if (binop->op != OP_RSHIFT && binop->op != OP_LSHIFT)
+ return false;
+
+ rhs = binop->right;
+ if (rhs->etype != EXPR_VALUE || rhs->len > 64)
+ return false;
- if (binop->op == OP_AND && expr->op == OP_NEQ &&
- value->dtype->basetype &&
- value->dtype->basetype->type == TYPE_BITMASK &&
- !mpz_cmp_ui(value->value, 0)) {
+ return mpz_get_uint64(rhs->value) == 0;
+}
+
+static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
+ struct expr **exprp)
+{
+ struct expr *expr = *exprp, *binop = expr->left, *right = expr->right;
+
+ if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) &&
+ right->dtype->basetype &&
+ 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(value);
+ expr_free(right);
expr->left = expr_get(binop->left);
expr->right = binop_tree_to_list(NULL, binop->right);
- expr->op = OP_IMPLICIT;
-
+ 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->left->dtype->flags & DTYPE_F_PREFIX &&
binop->op == OP_AND && expr->right->etype == EXPR_VALUE &&
expr_mask_is_prefix(binop->right)) {
expr->left = expr_get(binop->left);
expr->right = prefix_expr_alloc(&expr->location,
- expr_get(value),
+ expr_get(right),
expr_mask_to_prefix(binop->right));
- expr_free(value);
+ expr_free(right);
expr_free(binop);
} else if (binop->op == OP_AND &&
binop->right->etype == EXPR_VALUE) {
@@ -2135,8 +2574,58 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *e
* payload_expr_trim will figure out if the mask is needed to match
* templates.
*/
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->left);
+ } else if (binop->op == OP_RSHIFT && binop->left->op == OP_AND &&
+ binop->right->etype == EXPR_VALUE && binop->left->right->etype == EXPR_VALUE) {
+ /* Handle 'ip version @s4' and similar, i.e. set lookups where the lhs needs
+ * fixups to mask out unwanted bits AND a shift.
+ */
+
+ binop_postprocess(ctx, binop, &binop->left);
+ if (is_shift_by_zero(binop)) {
+ struct expr *lhs = binop->left;
+
+ expr_get(lhs);
+ expr_free(binop);
+ expr->left = lhs;
+ }
+ }
+}
+
+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;
+
+ if (expr->left->left->etype != EXPR_PAYLOAD)
+ return false;
+
+ 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,
@@ -2156,56 +2645,33 @@ static struct expr *string_wildcard_expr_alloc(struct location *loc,
expr->len + BITS_PER_BYTE, data);
}
-static void escaped_string_wildcard_expr_alloc(struct expr **exprp,
- unsigned int len)
-{
- struct expr *expr = *exprp, *tmp;
- char data[len + 3];
- int pos;
-
- mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
- pos = div_round_up(len, BITS_PER_BYTE);
- data[pos - 1] = '\\';
- data[pos] = '*';
-
- tmp = constant_expr_alloc(&expr->location, expr->dtype,
- BYTEORDER_HOST_ENDIAN,
- expr->len + BITS_PER_BYTE, data);
- expr_free(expr);
- *exprp = tmp;
-}
-
/* This calculates the string length and checks if it is nul-terminated, this
* function is quite a hack :)
*/
static bool __expr_postprocess_string(struct expr **exprp)
{
struct expr *expr = *exprp;
- unsigned int len = expr->len;
- bool nulterminated = false;
- mpz_t tmp;
-
- mpz_init(tmp);
- while (len >= BITS_PER_BYTE) {
- mpz_bitmask(tmp, BITS_PER_BYTE);
- mpz_lshift_ui(tmp, len - BITS_PER_BYTE);
- mpz_and(tmp, tmp, expr->value);
- if (mpz_cmp_ui(tmp, 0))
- break;
- else
- nulterminated = true;
- len -= BITS_PER_BYTE;
- }
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ char data[len + 1];
- mpz_rshift_ui(tmp, len - BITS_PER_BYTE);
+ mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
- if (nulterminated &&
- mpz_cmp_ui(tmp, '*') == 0)
- escaped_string_wildcard_expr_alloc(exprp, len);
+ if (data[len - 1] != '\0')
+ return false;
- mpz_clear(tmp);
+ len = strlen(data);
+ if (len && data[len - 1] == '*') {
+ data[len - 1] = '\\';
+ data[len] = '*';
+ data[len + 1] = '\0';
+ expr = constant_expr_alloc(&expr->location, expr->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (len + 2) * BITS_PER_BYTE, data);
+ expr_free(*exprp);
+ *exprp = expr;
+ }
- return nulterminated;
+ return true;
}
static struct expr *expr_postprocess_string(struct expr *expr)
@@ -2219,14 +2685,62 @@ static struct expr *expr_postprocess_string(struct expr *expr)
mask = constant_expr_alloc(&expr->location, &integer_type,
BYTEORDER_HOST_ENDIAN,
expr->len + BITS_PER_BYTE, NULL);
+ mpz_clear(mask->value);
mpz_init_bitmask(mask->value, expr->len);
out = string_wildcard_expr_alloc(&expr->location, mask, expr);
+ expr_free(expr);
expr_free(mask);
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) {
@@ -2250,28 +2764,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_for_each_entry(i, &expr->expressions, list) {
- if (type) {
- dtype = concat_subtype_lookup(type, --off);
- expr_set_type(i, dtype, dtype->byteorder);
- }
- expr_postprocess(ctx, &i);
-
- ntype = concat_subtype_add(ntype, i->dtype->type);
- }
- 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:
@@ -2279,20 +2782,89 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
expr_set_type(expr->right, &integer_type,
BYTEORDER_HOST_ENDIAN);
break;
+ case OP_AND:
+ 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.
+ *
+ * Removal needs to be performed as part of the relational
+ * operation because the RHS constant might need to be adjusted
+ * (shifted).
+ *
+ * This is different in set element context or concatenations:
+ * There is no relational operation (eq, neq and so on), thus
+ * it needs to be processed right away.
+ */
+ if ((ctx->flags & RULE_PP_REMOVE_OP_AND) &&
+ expr->left->etype == EXPR_PAYLOAD &&
+ expr->right->etype == EXPR_VALUE) {
+ __binop_postprocess(ctx, expr, expr->left, expr->right, exprp);
+ return;
+ }
+ break;
+ default:
+ 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);
+
+ switch (expr->op) {
+ case OP_LSHIFT:
+ case OP_RSHIFT:
+ expr_set_type(expr, &xinteger_type,
+ BYTEORDER_HOST_ENDIAN);
+ break;
default:
- expr_set_type(expr->right, expr->left->dtype,
+ expr_set_type(expr, expr->left->dtype,
expr->left->byteorder);
}
- expr_postprocess(ctx, &expr->right);
- 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;
@@ -2309,41 +2881,40 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
meta_match_postprocess(ctx, expr);
break;
case EXPR_BINOP:
- relational_binop_postprocess(ctx, expr);
+ relational_binop_postprocess(ctx, exprp);
break;
default:
break;
}
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);
expr_postprocess(ctx, &expr->right);
+ break;
case EXPR_PREFIX:
expr_postprocess(ctx, &expr->prefix);
break;
case EXPR_SET_ELEM:
+ ctx->flags |= RULE_PP_IN_SET_ELEM;
expr_postprocess(ctx, &expr->key);
+ 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:
@@ -2360,7 +2931,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));
@@ -2369,48 +2940,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:
- if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH) {
- datatype_set(stmt->reject.expr, &icmpx_code_type);
- break;
- }
- base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
- protocol = proto_find_num(base, desc);
- switch (protocol) {
- case NFPROTO_IPV4:
- datatype_set(stmt->reject.expr, &icmp_code_type);
- break;
- case NFPROTO_IPV6:
- datatype_set(stmt->reject.expr, &icmpv6_code_type);
- break;
- }
- stmt->reject.family = protocol;
- break;
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;
}
@@ -2419,25 +2977,27 @@ 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 __constant_htons(ETH_P_IP):
+ 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 __constant_htons(ETH_P_IPV6):
+ 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:
break;
@@ -2480,23 +3040,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);
@@ -2505,26 +3066,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->ops->type == dl->pdctx.prev->ops->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)) {
- __binop_adjust(binop, mask, shift);
- payload_expr_complete(payload, &ctx->pctx);
+ if (payload_expr_trim(payload, mask, &dl->pctx, &shift)) {
+ binop_adjust(binop, mask, shift);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(mask, payload->dtype,
payload->byteorder);
}
@@ -2618,7 +3181,7 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
mpz_set(mask->value, bitmask);
mpz_clear(bitmask);
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->left);
if (!payload_is_known(payload)) {
mpz_set(mask->value, tmp);
mpz_clear(tmp);
@@ -2679,20 +3242,34 @@ 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);
+
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);
}
+static void stmt_queue_postprocess(struct rule_pp_ctx *ctx)
+{
+ struct stmt *stmt = ctx->stmt;
+ struct expr *e = stmt->queue.queue;
+
+ if (e == NULL || e->etype == EXPR_VALUE ||
+ e->etype == EXPR_RANGE)
+ return;
+
+ expr_postprocess(ctx, &stmt->queue.queue);
+}
+
/*
* We can only remove payload dependencies if they occur without
* a statement with side effects in between.
@@ -2714,18 +3291,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->ops->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 rule_pp_ctx rctx;
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;
rctx.stmt = stmt;
+ dl = rule_update_dl_proto_ctx(&rctx);
switch (type) {
case STMT_EXPRESSION:
@@ -2746,24 +3380,24 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
expr_postprocess(&rctx, &stmt->ct.expr);
if (stmt->ct.expr->etype == EXPR_BINOP &&
- stmt->ct.key == NFT_CT_EVENTMASK)
- stmt->ct.expr = binop_tree_to_list(NULL,
- stmt->ct.expr);
+ stmt->ct.key == NFT_CT_EVENTMASK) {
+ expr = binop_tree_to_list(NULL, stmt->ct.expr);
+ expr_free(stmt->ct.expr);
+ stmt->ct.expr = expr;
+ }
}
break;
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;
@@ -2794,13 +3428,16 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
case STMT_OBJREF:
expr_postprocess(&rctx, &stmt->objref.expr);
break;
+ case STMT_QUEUE:
+ stmt_queue_postprocess(&rctx);
+ break;
default:
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);
}
}
@@ -2852,6 +3489,7 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
memset(&_ctx, 0, sizeof(_ctx));
_ctx.msgs = ctx->msgs;
_ctx.debug_mask = ctx->nft->debug_mask;
+ _ctx.nlctx = ctx;
memset(&h, 0, sizeof(h));
h.family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
@@ -2863,8 +3501,12 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
h.position.id = nftnl_rule_get_u64(nlr, NFTNL_RULE_POSITION);
pctx->rule = rule_alloc(&netlink_location, &h);
- pctx->table = table_lookup(&h, &ctx->nft->cache);
- assert(pctx->table != NULL);
+ pctx->table = table_cache_find(&ctx->nft->cache.table_cache,
+ h.table.name, h.family);
+ 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 846df46b..6204d8fd 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>
@@ -24,11 +25,34 @@
#include <linux/netfilter.h>
#include <libnftnl/udata.h>
+struct nft_expr_loc *nft_expr_loc_find(const struct nftnl_expr *nle,
+ struct netlink_linearize_ctx *ctx)
+{
+ struct nft_expr_loc *eloc;
+ uint32_t hash;
+
+ hash = (uint64_t)nle % NFT_EXPR_LOC_HSIZE;
+ list_for_each_entry(eloc, &ctx->expr_loc_htable[hash], hlist) {
+ if (eloc->nle == nle)
+ return eloc;
+ }
+
+ return NULL;
+}
-struct netlink_linearize_ctx {
- struct nftnl_rule *nlr;
- unsigned int reg_low;
-};
+static void nft_expr_loc_add(const struct nftnl_expr *nle,
+ const struct location *loc,
+ struct netlink_linearize_ctx *ctx)
+{
+ struct nft_expr_loc *eloc;
+ uint32_t hash;
+
+ eloc = xmalloc(sizeof(*eloc));
+ eloc->nle = nle;
+ eloc->loc = loc;
+ hash = (uint64_t)nle % NFT_EXPR_LOC_HSIZE;
+ list_add_tail(&eloc->hlist, &ctx->expr_loc_htable[hash]);
+}
static void netlink_put_register(struct nftnl_expr *nle,
uint32_t attr, uint32_t reg)
@@ -105,6 +129,14 @@ static void netlink_gen_concat(struct netlink_linearize_ctx *ctx,
}
}
+static void nft_rule_add_expr(struct netlink_linearize_ctx *ctx,
+ struct nftnl_expr *nle,
+ const struct location *loc)
+{
+ nft_expr_loc_add(nle, loc, ctx);
+ nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
static void netlink_gen_fib(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
@@ -116,7 +148,7 @@ static void netlink_gen_fib(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_RESULT, expr->fib.result);
nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_FLAGS, expr->fib.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
@@ -144,12 +176,11 @@ static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_SEED, expr->hash.seed);
nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_OFFSET, expr->hash.offset);
nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_TYPE, expr->hash.type);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ 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;
@@ -162,26 +193,92 @@ 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));
- nftnl_rule_add_expr(ctx->nlr, nle);
+ 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);
}
static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
{
- unsigned int offset = expr->exthdr.tmpl->offset + expr->exthdr.offset;
+ unsigned int offset = expr->exthdr.offset;
struct nftnl_expr *nle;
nle = alloc_nft_expr("exthdr");
netlink_put_register(nle, NFTNL_EXPR_EXTHDR_DREG, dreg);
nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE,
- expr->exthdr.desc->type);
+ expr->exthdr.raw_type);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_FLAGS, expr->exthdr.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
@@ -190,10 +287,13 @@ 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);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ 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);
}
static void netlink_gen_rt(struct netlink_linearize_ctx *ctx,
@@ -205,7 +305,7 @@ static void netlink_gen_rt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("rt");
netlink_put_register(nle, NFTNL_EXPR_RT_DREG, dreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_RT_KEY, expr->rt.key);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_socket(struct netlink_linearize_ctx *ctx,
@@ -217,7 +317,8 @@ static void netlink_gen_socket(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("socket");
netlink_put_register(nle, NFTNL_EXPR_SOCKET_DREG, dreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_KEY, expr->socket.key);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_LEVEL, expr->socket.level);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_osf(struct netlink_linearize_ctx *ctx,
@@ -230,7 +331,7 @@ static void netlink_gen_osf(struct netlink_linearize_ctx *ctx,
netlink_put_register(nle, NFTNL_EXPR_OSF_DREG, dreg);
nftnl_expr_set_u8(nle, NFTNL_EXPR_OSF_TTL, expr->osf.ttl);
nftnl_expr_set_u32(nle, NFTNL_EXPR_OSF_FLAGS, expr->osf.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_numgen(struct netlink_linearize_ctx *ctx,
@@ -244,7 +345,7 @@ static void netlink_gen_numgen(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_TYPE, expr->numgen.type);
nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_MODULUS, expr->numgen.mod);
nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_OFFSET, expr->numgen.offset);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_ct(struct netlink_linearize_ctx *ctx,
@@ -260,7 +361,7 @@ static void netlink_gen_ct(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u8(nle, NFTNL_EXPR_CT_DIR,
expr->ct.direction);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
@@ -298,7 +399,7 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
if (dreg == NFT_REG_VERDICT)
release_register(ctx, expr->map);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx,
@@ -324,7 +425,7 @@ static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_LOOKUP_FLAGS, NFT_LOOKUP_F_INV);
release_register(ctx, expr->left);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static enum nft_cmp_ops netlink_gen_cmp_op(enum ops op)
@@ -359,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;
@@ -370,7 +472,7 @@ static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, nld.len);
nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld.value, nld.len);
nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &zero.value, zero.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
return expr->right->prefix;
}
@@ -400,7 +502,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
netlink_gen_data(range->right, &nld);
nftnl_expr_set(nle, NFTNL_EXPR_RANGE_TO_DATA,
nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
break;
case OP_EQ:
case OP_IMPLICIT:
@@ -410,7 +512,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
netlink_gen_cmp_op(OP_GTE));
netlink_gen_data(range->left, &nld);
nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
nle = alloc_nft_expr("cmp");
netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
@@ -418,7 +520,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
netlink_gen_cmp_op(OP_LTE));
netlink_gen_data(range->right, &nld);
nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
break;
default:
BUG("invalid range operation %u\n", expr->op);
@@ -449,19 +551,31 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx,
netlink_gen_raw_data(zero, expr->right->byteorder, len, &nld);
netlink_gen_data(expr->right, &nld2);
- nle = alloc_nft_expr("bitwise");
- netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg);
- netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
- nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len);
- nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ if (expr->left->etype == EXPR_BINOP) {
+ nle = alloc_nft_expr("cmp");
+ netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ);
+ nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld2.value, nld2.len);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+ } else {
+ nle = alloc_nft_expr("bitwise");
+ netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg);
+ netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
+ nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len);
+ nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &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, NFT_CMP_NEQ);
- nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nle = alloc_nft_expr("cmp");
+ netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
+ if (expr->op == OP_NEG)
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ);
+ else
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ);
+
+ nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+ }
mpz_clear(zero);
release_register(ctx, expr->left);
@@ -487,6 +601,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
case OP_GT:
case OP_LTE:
case OP_GTE:
+ case OP_NEG:
break;
default:
BUG("invalid relational operation %u\n", expr->op);
@@ -502,7 +617,10 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
return netlink_gen_flagcmp(ctx, expr, dreg);
case EXPR_PREFIX:
sreg = get_register(ctx, expr->left);
- if (expr_basetype(expr->left)->type != TYPE_STRING) {
+ if (expr_basetype(expr->left)->type != TYPE_STRING &&
+ (expr->right->byteorder != BYTEORDER_BIG_ENDIAN ||
+ !expr->right->prefix_len ||
+ expr->right->prefix_len % BITS_PER_BYTE)) {
len = div_round_up(expr->right->len, BITS_PER_BYTE);
netlink_gen_expr(ctx, expr->left, sreg);
right = netlink_gen_prefix(ctx, expr, sreg);
@@ -514,7 +632,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
}
break;
default:
- if (expr->op == OP_IMPLICIT &&
+ if ((expr->op == OP_IMPLICIT || expr->op == OP_NEG) &&
expr->right->dtype->basetype != NULL &&
expr->right->dtype->basetype->type == TYPE_BITMASK)
return netlink_gen_flagcmp(ctx, expr, dreg);
@@ -534,7 +652,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, len);
release_register(ctx, expr->left);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void combine_binop(mpz_t mask, mpz_t xor, const mpz_t m, const mpz_t x)
@@ -570,17 +688,17 @@ static void netlink_gen_shift(struct netlink_linearize_ctx *ctx,
nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_DATA, nld.value,
nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ 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)
{
+ 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;
@@ -592,16 +710,18 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
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))
+ (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) {
@@ -640,7 +760,7 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
mpz_clear(xor);
mpz_clear(mask);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
@@ -677,6 +797,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)
@@ -690,20 +812,21 @@ 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,
netlink_gen_unary_op(expr->op));
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
{
- struct nftnl_expr *nle;
+ const struct location *loc = &expr->location;
struct nft_data_linearize nld;
+ struct nftnl_expr *nle;
nle = alloc_nft_expr("immediate");
netlink_put_register(nle, NFTNL_EXPR_IMM_DREG, dreg);
@@ -716,6 +839,7 @@ static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
if (expr->chain) {
nftnl_expr_set_str(nle, NFTNL_EXPR_IMM_CHAIN,
nld.chain);
+ loc = &expr->chain->location;
} else if (expr->chain_id) {
nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_CHAIN_ID,
nld.chain_id);
@@ -725,7 +849,7 @@ static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
default:
break;
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, loc);
}
static void netlink_gen_xfrm(struct netlink_linearize_ctx *ctx,
@@ -739,7 +863,7 @@ static void netlink_gen_xfrm(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_KEY, expr->xfrm.key);
nftnl_expr_set_u8(nle, NFTNL_EXPR_XFRM_DIR, expr->xfrm.direction);
nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_SPNUM, expr->xfrm.spnum);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
@@ -822,7 +946,7 @@ static void netlink_gen_objref_stmt(struct netlink_linearize_ctx *ctx,
default:
BUG("unsupported expression %u\n", expr->etype);
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static struct nftnl_expr *netlink_gen_connlimit_stmt(const struct stmt *stmt)
@@ -883,6 +1007,17 @@ 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) {
@@ -894,6 +1029,8 @@ 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);
}
@@ -931,17 +1068,17 @@ static void netlink_gen_exthdr_stmt(struct netlink_linearize_ctx *ctx,
expr = stmt->exthdr.expr;
- offset = expr->exthdr.tmpl->offset + expr->exthdr.offset;
+ offset = expr->exthdr.offset;
nle = alloc_nft_expr("exthdr");
netlink_put_register(nle, NFTNL_EXPR_EXTHDR_SREG, sreg);
nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE,
- expr->exthdr.desc->type);
+ expr->exthdr.raw_type);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
@@ -974,16 +1111,17 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
expr->len / BITS_PER_BYTE);
if (csum_off) {
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_TYPE,
- NFT_PAYLOAD_CSUM_INET);
+ desc->checksum_type);
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_OFFSET,
csum_off / BITS_PER_BYTE);
}
- if (expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
- payload_needs_l4csum_update_pseudohdr(expr, desc))
+ if ((expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
+ payload_needs_l4csum_update_pseudohdr(expr, desc)) ||
+ expr->payload.base == PROTO_BASE_INNER_HDR)
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS,
NFT_PAYLOAD_L4CSUM_PSEUDOHDR);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx,
@@ -999,7 +1137,7 @@ static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("meta");
netlink_put_register(nle, NFTNL_EXPR_META_SREG, sreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, stmt->meta.key);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
@@ -1030,7 +1168,7 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_LOG_FLAGS,
stmt->log.logflags);
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
@@ -1044,7 +1182,7 @@ static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u8(nle, NFTNL_EXPR_REJECT_CODE,
stmt->reject.icmp_code);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static unsigned int nat_addrlen(uint8_t family)
@@ -1124,11 +1262,14 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
amin_reg);
if (stmt->nat.addr->etype == EXPR_MAP &&
stmt->nat.addr->mappings->set->data->flags & EXPR_F_INTERVAL) {
- amax_reg = get_register(ctx, NULL);
- registers++;
amin_reg += netlink_register_space(nat_addrlen(family));
- netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX,
- amin_reg);
+ if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) {
+ netlink_put_register(nle, nftnl_reg_pmin,
+ amin_reg);
+ } else {
+ netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX,
+ amin_reg);
+ }
}
}
@@ -1140,6 +1281,12 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
pmin_reg = amin_reg;
+ if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) {
+ pmin_reg += netlink_register_space(nat_addrlen(family));
+ netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX,
+ pmin_reg);
+ }
+
/* if STMT_NAT_F_CONCAT is set, the mapped type is a
* concatenation of 'addr . inet_service'.
* The map lookup will then return the
@@ -1148,7 +1295,10 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
* will hold the inet_service part.
*/
pmin_reg += netlink_register_space(nat_addrlen(family));
- netlink_put_register(nle, nftnl_reg_pmin, pmin_reg);
+ if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL)
+ netlink_put_register(nle, nftnl_reg_pmax, pmin_reg);
+ else
+ netlink_put_register(nle, nftnl_reg_pmin, pmin_reg);
}
}
@@ -1175,7 +1325,7 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
registers--;
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx,
@@ -1214,7 +1364,7 @@ static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx,
registers--;
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx,
@@ -1229,7 +1379,7 @@ static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_SYNPROXY_FLAGS,
stmt->synproxy.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx,
@@ -1260,7 +1410,7 @@ static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx,
if (stmt->dup.to != NULL)
release_register(ctx, stmt->dup.to);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx,
@@ -1287,32 +1437,62 @@ static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_FWD_NFPROTO,
stmt->fwd.family);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
+}
+
+static void netlink_gen_optstrip_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ struct nftnl_expr *nle = alloc_nft_expr("exthdr");
+ struct expr *expr = stmt->optstrip.expr;
+
+ nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE,
+ expr->exthdr.raw_type);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
+ enum nft_registers sreg = 0;
struct nftnl_expr *nle;
uint16_t total_queues;
+ struct expr *expr;
mpz_t low, high;
mpz_init2(low, 16);
mpz_init2(high, 16);
- if (stmt->queue.queue != NULL) {
- range_expr_value_low(low, stmt->queue.queue);
- range_expr_value_high(high, stmt->queue.queue);
+
+ expr = stmt->queue.queue;
+
+ if (expr) {
+ if (expr->etype == EXPR_RANGE || expr->etype == EXPR_VALUE) {
+ range_expr_value_low(low, stmt->queue.queue);
+ range_expr_value_high(high, stmt->queue.queue);
+ } else {
+ sreg = get_register(ctx, expr);
+ netlink_gen_expr(ctx, expr, sreg);
+ release_register(ctx, expr);
+ }
}
+
total_queues = mpz_get_uint16(high) - mpz_get_uint16(low) + 1;
nle = alloc_nft_expr("queue");
- nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_NUM, mpz_get_uint16(low));
- nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_TOTAL, total_queues);
+
+ if (sreg) {
+ netlink_put_register(nle, NFTNL_EXPR_QUEUE_SREG_QNUM, sreg);
+ } else {
+ nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_NUM, mpz_get_uint16(low));
+ nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_TOTAL, total_queues);
+ }
+
if (stmt->queue.flags)
nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_FLAGS,
stmt->queue.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
mpz_clear(low);
mpz_clear(high);
@@ -1335,7 +1515,7 @@ static void netlink_gen_ct_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u8(nle, NFTNL_EXPR_CT_DIR,
stmt->ct.direction);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_notrack_stmt(struct netlink_linearize_ctx *ctx,
@@ -1344,7 +1524,7 @@ static void netlink_gen_notrack_stmt(struct netlink_linearize_ctx *ctx,
struct nftnl_expr *nle;
nle = alloc_nft_expr("notrack");
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx,
@@ -1355,15 +1535,17 @@ static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("flow_offload");
nftnl_expr_set_str(nle, NFTNL_EXPR_FLOW_TABLE_NAME,
stmt->flow.table_name);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
struct set *set = stmt->meter.set->set;
- struct nftnl_expr *nle;
enum nft_registers sreg_key;
+ struct nftnl_expr *nle;
+ int num_stmts = 0;
+ struct stmt *this;
sreg_key = get_register(ctx, stmt->set.key->key);
netlink_gen_expr(ctx, stmt->set.key->key, sreg_key);
@@ -1377,11 +1559,24 @@ static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->set.op);
nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name);
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
+
+ list_for_each_entry(this, &stmt->set.stmt_list, list)
+ num_stmts++;
- if (stmt->set.stmt)
- nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
- netlink_gen_stmt_stateful(stmt->set.stmt), 0);
+ if (num_stmts == 1) {
+ list_for_each_entry(this, &stmt->set.stmt_list, list) {
+ nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
+ netlink_gen_stmt_stateful(this), 0);
+ }
+ } else if (num_stmts > 1) {
+ list_for_each_entry(this, &stmt->set.stmt_list, list) {
+ nftnl_expr_add_expr(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS,
+ netlink_gen_stmt_stateful(this));
+ }
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_FLAGS,
+ NFT_DYNSET_F_EXPR);
+ }
}
static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
@@ -1391,14 +1586,16 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
enum nft_registers sreg_data;
enum nft_registers sreg_key;
struct nftnl_expr *nle;
+ 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);
- release_register(ctx, stmt->map.key);
+ release_register(ctx, stmt->map.key->key);
release_register(ctx, stmt->map.data);
nle = alloc_nft_expr("dynset");
@@ -1408,12 +1605,26 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->map.op);
nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name);
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.stmt)
- nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
- netlink_gen_stmt_stateful(stmt->map.stmt), 0);
+ if (stmt->map.key->timeout > 0)
+ nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT,
+ stmt->map.key->timeout);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ list_for_each_entry(this, &stmt->map.stmt_list, list)
+ num_stmts++;
+
+ if (num_stmts == 1) {
+ list_for_each_entry(this, &stmt->map.stmt_list, list) {
+ nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
+ netlink_gen_stmt_stateful(this), 0);
+ }
+ } else if (num_stmts > 1) {
+ list_for_each_entry(this, &stmt->map.stmt_list, list) {
+ nftnl_expr_add_expr(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS,
+ netlink_gen_stmt_stateful(this));
+ }
+ }
}
static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx,
@@ -1444,7 +1655,7 @@ static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
netlink_gen_stmt_stateful(stmt->meter.stmt), 0);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_chain_stmt(struct netlink_linearize_ctx *ctx,
@@ -1495,8 +1706,9 @@ 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);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
break;
case STMT_NOTRACK:
return netlink_gen_notrack_stmt(ctx, stmt);
@@ -1508,23 +1720,47 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_map_stmt(ctx, stmt);
case STMT_CHAIN:
return netlink_gen_chain_stmt(ctx, stmt);
+ case STMT_OPTSTRIP:
+ return netlink_gen_optstrip_stmt(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
}
-void netlink_linearize_rule(struct netlink_ctx *ctx, struct nftnl_rule *nlr,
- const struct rule *rule)
+void netlink_linearize_init(struct netlink_linearize_ctx *lctx,
+ struct nftnl_rule *nlr)
{
- struct netlink_linearize_ctx lctx;
- const struct stmt *stmt;
+ int i;
+
+ memset(lctx, 0, sizeof(*lctx));
+ lctx->reg_low = NFT_REG_1;
+ lctx->nlr = nlr;
+ lctx->expr_loc_htable =
+ xmalloc(sizeof(struct list_head) * NFT_EXPR_LOC_HSIZE);
+ for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++)
+ init_list_head(&lctx->expr_loc_htable[i]);
+}
- memset(&lctx, 0, sizeof(lctx));
- lctx.reg_low = NFT_REG_1;
- lctx.nlr = nlr;
+void netlink_linearize_fini(struct netlink_linearize_ctx *lctx)
+{
+ struct nft_expr_loc *eloc, *next;
+ int i;
+
+ for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) {
+ list_for_each_entry_safe(eloc, next, &lctx->expr_loc_htable[i], hlist)
+ free(eloc);
+ }
+ free(lctx->expr_loc_htable);
+}
+
+void netlink_linearize_rule(struct netlink_ctx *ctx,
+ const struct rule *rule,
+ struct netlink_linearize_ctx *lctx)
+{
+ const struct stmt *stmt;
list_for_each_entry(stmt, &rule->stmts, list)
- netlink_gen_stmt(&lctx, stmt);
+ netlink_gen_stmt(lctx, stmt);
if (rule->comment) {
struct nftnl_udata_buf *udata;
@@ -1536,12 +1772,23 @@ void netlink_linearize_rule(struct netlink_ctx *ctx, struct nftnl_rule *nlr,
if (!nftnl_udata_put_strz(udata, NFTNL_UDATA_RULE_COMMENT,
rule->comment))
memory_allocation_error();
- nftnl_rule_set_data(nlr, NFTNL_RULE_USERDATA,
+ nftnl_rule_set_data(lctx->nlr, NFTNL_RULE_USERDATA,
nftnl_udata_buf_data(udata),
nftnl_udata_buf_len(udata));
nftnl_udata_buf_free(udata);
}
- netlink_dump_rule(nlr, ctx);
+ if (ctx->nft->debug_mask & NFT_DEBUG_NETLINK) {
+ nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_TABLE,
+ rule->handle.table.name);
+ if (rule->handle.chain.name)
+ nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_CHAIN,
+ rule->handle.chain.name);
+
+ netlink_dump_rule(lctx->nlr, ctx);
+
+ nftnl_rule_unset(lctx->nlr, NFTNL_RULE_CHAIN);
+ nftnl_rule_unset(lctx->nlr, NFTNL_RULE_TABLE);
+ }
}
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
new file mode 100644
index 00000000..b90dd995
--- /dev/null
+++ b/src/optimize.c
@@ -0,0 +1,1406 @@
+/*
+ * 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.
+ */
+
+/* Funded through the NGI0 PET Fund established by NLnet (https://nlnet.nl)
+ * with support from the European Commission's Next Generation Internet
+ * programme.
+ */
+
+#include <nft.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <nftables.h>
+#include <parser.h>
+#include <expression.h>
+#include <statement.h>
+#include <utils.h>
+#include <erec.h>
+#include <linux/netfilter.h>
+
+#define MAX_STMTS 32
+
+struct optimize_ctx {
+ struct stmt *stmt[MAX_STMTS];
+ uint32_t num_stmts;
+
+ struct stmt ***stmt_matrix;
+ struct rule **rule;
+ uint32_t num_rules;
+};
+
+static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
+{
+ if (expr_a->etype != expr_b->etype)
+ return false;
+
+ switch (expr_a->etype) {
+ case EXPR_PAYLOAD:
+ if (expr_a->payload.base != expr_b->payload.base)
+ return false;
+ if (expr_a->payload.offset != expr_b->payload.offset)
+ 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;
+ case EXPR_EXTHDR:
+ if (expr_a->exthdr.desc != expr_b->exthdr.desc)
+ return false;
+ if (expr_a->exthdr.tmpl != expr_b->exthdr.tmpl)
+ return false;
+ break;
+ case EXPR_META:
+ if (expr_a->meta.key != expr_b->meta.key)
+ return false;
+ if (expr_a->meta.base != expr_b->meta.base)
+ return false;
+ break;
+ case EXPR_CT:
+ if (expr_a->ct.key != expr_b->ct.key)
+ return false;
+ if (expr_a->ct.base != expr_b->ct.base)
+ return false;
+ if (expr_a->ct.direction != expr_b->ct.direction)
+ return false;
+ if (expr_a->ct.nfproto != expr_b->ct.nfproto)
+ return false;
+ break;
+ case EXPR_RT:
+ if (expr_a->rt.key != expr_b->rt.key)
+ return false;
+ break;
+ case EXPR_SOCKET:
+ if (expr_a->socket.key != expr_b->socket.key)
+ return false;
+ if (expr_a->socket.level != expr_b->socket.level)
+ return false;
+ break;
+ case EXPR_OSF:
+ if (expr_a->osf.ttl != expr_b->osf.ttl)
+ return false;
+ if (expr_a->osf.flags != expr_b->osf.flags)
+ return false;
+ break;
+ case EXPR_XFRM:
+ if (expr_a->xfrm.key != expr_b->xfrm.key)
+ return false;
+ if (expr_a->xfrm.direction != expr_b->xfrm.direction)
+ return false;
+ break;
+ case EXPR_FIB:
+ if (expr_a->fib.flags != expr_b->fib.flags)
+ return false;
+ if (expr_a->fib.result != expr_b->fib.result)
+ return false;
+ break;
+ case EXPR_NUMGEN:
+ if (expr_a->numgen.type != expr_b->numgen.type)
+ return false;
+ if (expr_a->numgen.mod != expr_b->numgen.mod)
+ return false;
+ if (expr_a->numgen.offset != expr_b->numgen.offset)
+ return false;
+ break;
+ case EXPR_HASH:
+ if (expr_a->hash.mod != expr_b->hash.mod)
+ return false;
+ if (expr_a->hash.seed_set != expr_b->hash.seed_set)
+ return false;
+ if (expr_a->hash.seed != expr_b->hash.seed)
+ return false;
+ if (expr_a->hash.offset != expr_b->hash.offset)
+ return false;
+ if (expr_a->hash.type != expr_b->hash.type)
+ return false;
+ break;
+ case EXPR_BINOP:
+ return __expr_cmp(expr_a->left, expr_b->left);
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool stmt_expr_supported(const struct expr *expr)
+{
+ switch (expr->right->etype) {
+ case EXPR_SYMBOL:
+ case EXPR_RANGE:
+ case EXPR_PREFIX:
+ case EXPR_SET:
+ case EXPR_LIST:
+ case EXPR_VALUE:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool expr_symbol_set(const struct expr *expr)
+{
+ return expr->right->etype == EXPR_SYMBOL &&
+ expr->right->symtype == SYMBOL_SET;
+}
+
+static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
+ bool fully_compare)
+{
+ struct expr *expr_a, *expr_b;
+
+ if (stmt_a->ops->type != stmt_b->ops->type)
+ return false;
+
+ switch (stmt_a->ops->type) {
+ case STMT_EXPRESSION:
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+
+ if (expr_a->op != expr_b->op)
+ return false;
+ if (expr_a->op != OP_IMPLICIT && expr_a->op != OP_EQ)
+ return false;
+
+ if (fully_compare) {
+ if (!stmt_expr_supported(expr_a) ||
+ !stmt_expr_supported(expr_b))
+ return false;
+
+ if (expr_symbol_set(expr_a) ||
+ expr_symbol_set(expr_b))
+ return false;
+ }
+
+ return __expr_cmp(expr_a->left, expr_b->left);
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ break;
+ case STMT_VERDICT:
+ if (!fully_compare)
+ break;
+
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+
+ if (expr_a->etype != expr_b->etype)
+ return false;
+
+ if (expr_a->etype == EXPR_MAP &&
+ expr_b->etype == EXPR_MAP)
+ return __expr_cmp(expr_a->map, expr_b->map);
+ break;
+ case STMT_LOG:
+ if (stmt_a->log.snaplen != stmt_b->log.snaplen ||
+ stmt_a->log.group != stmt_b->log.group ||
+ stmt_a->log.qthreshold != stmt_b->log.qthreshold ||
+ stmt_a->log.level != stmt_b->log.level ||
+ stmt_a->log.logflags != stmt_b->log.logflags ||
+ stmt_a->log.flags != stmt_b->log.flags)
+ return false;
+
+ if (!!stmt_a->log.prefix ^ !!stmt_b->log.prefix)
+ return false;
+
+ 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))
+ return false;
+ break;
+ case STMT_REJECT:
+ if (stmt_a->reject.family != stmt_b->reject.family ||
+ stmt_a->reject.type != stmt_b->reject.type ||
+ stmt_a->reject.icmp_code != stmt_b->reject.icmp_code)
+ return false;
+
+ if (!!stmt_a->reject.expr ^ !!stmt_b->reject.expr)
+ return false;
+
+ if (!stmt_a->reject.expr)
+ return true;
+
+ 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)
+ 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. */
+ return false;
+ }
+
+ return true;
+}
+
+static bool expr_verdict_eq(const struct expr *expr_a, const struct expr *expr_b)
+{
+ if (expr_a->verdict != expr_b->verdict)
+ return false;
+ if (expr_a->chain && expr_b->chain) {
+ if (expr_a->chain->etype != expr_b->chain->etype)
+ return false;
+ if (expr_a->chain->etype == EXPR_VALUE &&
+ strcmp(expr_a->chain->identifier, expr_b->chain->identifier))
+ return false;
+ } else if (expr_a->chain || expr_b->chain) {
+ return false;
+ }
+
+ return true;
+}
+
+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);
+
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+ if (expr_a->etype == EXPR_VERDICT &&
+ expr_b->etype == EXPR_VERDICT)
+ return expr_verdict_eq(expr_a, expr_b);
+
+ if (expr_a->etype == EXPR_MAP &&
+ expr_b->etype == EXPR_MAP)
+ return __expr_cmp(expr_a->map, expr_b->map);
+
+ return false;
+}
+
+static bool stmt_type_find(struct optimize_ctx *ctx, const struct stmt *stmt)
+{
+ bool unsupported_exists = false;
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ unsupported_exists = true;
+
+ if (__stmt_type_eq(stmt, ctx->stmt[i], false))
+ return true;
+ }
+
+ switch (stmt->ops->type) {
+ case STMT_EXPRESSION:
+ case STMT_VERDICT:
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ case STMT_LOG:
+ case STMT_NAT:
+ case STMT_REJECT:
+ break;
+ default:
+ /* add unsupported statement only once to statement matrix. */
+ if (unsupported_exists)
+ return true;
+ break;
+ }
+
+ 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)
+{
+ struct stmt *stmt, *clone;
+
+ list_for_each_entry(stmt, &rule->stmts, list) {
+ if (stmt_type_find(ctx, stmt))
+ continue;
+
+ /* 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) {
+ case STMT_EXPRESSION:
+ if (stmt->expr->op != OP_IMPLICIT &&
+ stmt->expr->op != OP_EQ) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+ if (stmt->expr->left->etype == EXPR_CONCAT) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+ /* fall-through */
+ case STMT_VERDICT:
+ clone->expr = expr_get(stmt->expr);
+ break;
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ break;
+ case STMT_LOG:
+ memcpy(&clone->log, &stmt->log, sizeof(clone->log));
+ if (stmt->log.prefix)
+ clone->log.prefix = expr_get(stmt->log.prefix);
+ break;
+ case STMT_NAT:
+ if ((stmt->nat.addr &&
+ stmt->nat.addr->etype == EXPR_MAP) ||
+ (stmt->nat.proto &&
+ stmt->nat.proto->etype == EXPR_MAP)) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+ clone->nat.type = stmt->nat.type;
+ clone->nat.family = stmt->nat.family;
+ if (stmt->nat.addr)
+ clone->nat.addr = expr_clone(stmt->nat.addr);
+ if (stmt->nat.proto)
+ clone->nat.proto = expr_clone(stmt->nat.proto);
+ clone->nat.flags = stmt->nat.flags;
+ clone->nat.type_flags = stmt->nat.type_flags;
+ break;
+ case STMT_REJECT:
+ if (stmt->reject.expr)
+ clone->reject.expr = expr_get(stmt->reject.expr);
+ clone->reject.type = stmt->reject.type;
+ clone->reject.icmp_code = stmt->reject.icmp_code;
+ clone->reject.family = stmt->reject.family;
+ break;
+ default:
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+
+ ctx->stmt[ctx->num_stmts++] = clone;
+ if (ctx->num_stmts >= MAX_STMTS)
+ return -1;
+ }
+
+ return 0;
+}
+
+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)
+ return i;
+ }
+ /* this should not happen. */
+ return -1;
+}
+
+static int cmd_stmt_find_in_stmt_matrix(struct optimize_ctx *ctx, struct stmt *stmt)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (__stmt_type_eq(stmt, ctx->stmt[i], false))
+ return i;
+ }
+
+ return -1;
+}
+
+static struct stmt unsupported_stmt = {
+ .ops = &unsupported_stmt_ops,
+};
+
+static void rule_build_stmt_matrix_stmts(struct optimize_ctx *ctx,
+ struct rule *rule, uint32_t *i)
+{
+ struct stmt *stmt;
+ int k;
+
+ list_for_each_entry(stmt, &rule->stmts, list) {
+ k = cmd_stmt_find_in_stmt_matrix(ctx, stmt);
+ if (k < 0) {
+ k = unsupported_in_stmt_matrix(ctx);
+ assert(k >= 0);
+ ctx->stmt_matrix[*i][k] = &unsupported_stmt;
+ continue;
+ }
+ ctx->stmt_matrix[*i][k] = stmt;
+ }
+ ctx->rule[(*i)++] = rule;
+}
+
+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)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+struct merge {
+ /* interval of rules to be merged */
+ uint32_t rule_from;
+ uint32_t num_rules;
+ /* statements to be merged (index relative to statement matrix) */
+ uint32_t stmt[MAX_STMTS];
+ uint32_t num_stmts;
+};
+
+static void merge_expr_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct stmt *stmt_a)
+{
+ struct expr *expr_a, *expr_b, *set, *elem;
+ struct stmt *stmt_b;
+ uint32_t i;
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ expr_a = stmt_a->expr->right;
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr_a));
+ compound_expr_add(set, elem);
+
+ for (i = from + 1; i <= to; i++) {
+ stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+ expr_b = stmt_b->expr->right;
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr_b));
+ compound_expr_add(set, elem);
+ }
+
+ expr_free(stmt_a->expr->right);
+ stmt_a->expr->right = set;
+}
+
+static void merge_vmap(const struct optimize_ctx *ctx,
+ struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ struct expr *mappings, *mapping, *expr;
+
+ mappings = stmt_b->expr->mappings;
+ list_for_each_entry(expr, &mappings->expressions, list) {
+ mapping = expr_clone(expr);
+ compound_expr_add(stmt_a->expr->mappings, mapping);
+ }
+}
+
+static void merge_verdict_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct stmt *stmt_a)
+{
+ struct stmt *stmt_b;
+ uint32_t i;
+
+ for (i = from + 1; i <= to; i++) {
+ stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+ switch (stmt_b->ops->type) {
+ case STMT_VERDICT:
+ switch (stmt_b->expr->etype) {
+ case EXPR_MAP:
+ merge_vmap(ctx, stmt_a, stmt_b);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+}
+
+static void merge_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to, const struct merge *merge)
+{
+ struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
+
+ switch (stmt_a->ops->type) {
+ case STMT_EXPRESSION:
+ merge_expr_stmts(ctx, from, to, merge, stmt_a);
+ break;
+ case STMT_VERDICT:
+ merge_verdict_stmts(ctx, from, to, merge, stmt_a);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+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:
+ clone = expr_clone(stmt_a->expr->right);
+ compound_expr_add(concat, clone);
+ 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 stmt *stmt, *stmt_a;
+ struct expr *concat, *set;
+ uint32_t i, k;
+
+ stmt = ctx->stmt_matrix[from][merge->stmt[0]];
+ /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */
+ concat = concat_expr_alloc(&internal_location);
+
+ for (k = 0; k < merge->num_stmts; k++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[k]];
+ compound_expr_add(concat, expr_get(stmt_a->expr->left));
+ }
+ expr_free(stmt->expr->left);
+ stmt->expr->left = concat;
+
+ /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 } */
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++)
+ __merge_concat_stmts(ctx, i, merge, set);
+
+ expr_free(stmt->expr->right);
+ stmt->expr->right = set;
+
+ for (k = 1; k < merge->num_stmts; k++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[k]];
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ }
+}
+
+static void build_verdict_map(struct expr *expr, struct stmt *verdict,
+ struct expr *set, struct stmt *counter)
+{
+ struct expr *item, *elem, *mapping;
+
+ 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)
+ list_add_tail(&counter->list, &elem->stmt_list);
+
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+ 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)
+ list_add_tail(&counter->list, &elem->stmt_list);
+
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+ break;
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ 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);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static void remove_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->ops->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->ops->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)
+{
+ 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);
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ expr_a = stmt_a->expr->right;
+ 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];
+ 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);
+
+ list_add(&stmt->list, &stmt_a->list);
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ list_del(&verdict_a->list);
+ 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;
+ LIST_HEAD(concat_list);
+ struct stmt *counter;
+
+ 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)
+ list_add_tail(&counter->list, &elem->stmt_list);
+
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+}
+
+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 stmt *stmt, *stmt_a, *verdict;
+ struct expr *concat_a, *expr, *set;
+ uint32_t i;
+ int k;
+
+ k = stmt_verdict_find(ctx);
+ assert(k >= 0);
+
+ /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */
+ concat_a = concat_expr_alloc(&internal_location);
+ for (i = 0; i < merge->num_stmts; i++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[i]];
+ compound_expr_add(concat_a, expr_get(stmt_a->expr->left));
+ }
+
+ /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 : accept } */
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++) {
+ verdict = ctx->stmt_matrix[i][k];
+ __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);
+
+ list_add(&stmt->list, &orig_stmt->list);
+ list_del(&orig_stmt->list);
+ stmt_free(orig_stmt);
+
+ for (i = 1; i < merge->num_stmts; i++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[i]];
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ }
+
+ verdict = ctx->stmt_matrix[from][k];
+ list_del(&verdict->list);
+ stmt_free(verdict);
+}
+
+static bool stmt_verdict_cmp(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to)
+{
+ struct stmt *stmt_a, *stmt_b;
+ uint32_t i;
+ int k;
+
+ k = stmt_verdict_find(ctx);
+ if (k < 0)
+ return true;
+
+ for (i = from; i + 1 <= to; i++) {
+ stmt_a = ctx->stmt_matrix[i][k];
+ stmt_b = ctx->stmt_matrix[i + 1][k];
+ if (!stmt_a && !stmt_b)
+ continue;
+ if (!stmt_a || !stmt_b)
+ return false;
+ if (!stmt_verdict_eq(stmt_a, stmt_b))
+ return false;
+ }
+
+ return true;
+}
+
+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]->ops->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 ||
+ ctx->stmt[i]->nat.type != nat_type)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+static struct expr *stmt_nat_expr(struct stmt *nat_stmt)
+{
+ struct expr *nat_expr;
+
+ assert(nat_stmt->ops->type == STMT_NAT);
+
+ if (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;
+}
+
+static void merge_nat(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ 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;
+
+ k = stmt_nat_find(ctx, from);
+ assert(k >= 0);
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++) {
+ stmt = ctx->stmt_matrix[i][merge->stmt[0]];
+ expr = stmt->expr->right;
+
+ nat_stmt = ctx->stmt_matrix[i][k];
+ nat_expr = stmt_nat_expr(nat_stmt);
+
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr));
+ mapping = mapping_expr_alloc(&internal_location, elem, nat_expr);
+ compound_expr_add(set, mapping);
+ }
+
+ 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);
+ 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);
+ stmt_free(stmt);
+}
+
+static void merge_concat_nat(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ 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;
+
+ k = stmt_nat_find(ctx, from);
+ assert(k >= 0);
+
+ 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 (j = 0; j < merge->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[i][merge->stmt[j]];
+ expr = stmt->expr->right;
+ compound_expr_add(concat, expr_get(expr));
+ }
+
+ nat_stmt = ctx->stmt_matrix[i][k];
+ nat_expr = stmt_nat_expr(nat_stmt);
+
+ elem = set_elem_expr_alloc(&internal_location, concat);
+ mapping = mapping_expr_alloc(&internal_location, elem, nat_expr);
+ compound_expr_add(set, mapping);
+ }
+
+ concat = concat_expr_alloc(&internal_location);
+ 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;
+
+ remove_counter(ctx, from);
+ for (j = 0; j < merge->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[from][merge->stmt[j]];
+ list_del(&stmt->list);
+ stmt_free(stmt);
+ }
+}
+
+static void rule_optimize_print(struct output_ctx *octx,
+ const struct rule *rule)
+{
+ const struct location *loc = &rule->location;
+ const struct input_descriptor *indesc = loc->indesc;
+ const char *line = "";
+ char buf[1024];
+
+ switch (indesc->type) {
+ case INDESC_BUFFER:
+ case INDESC_CLI:
+ line = indesc->data;
+ *strchrnul(line, '\n') = '\0';
+ break;
+ case INDESC_STDIN:
+ line = indesc->data;
+ line += loc->line_offset;
+ *strchrnul(line, '\n') = '\0';
+ break;
+ case INDESC_FILE:
+ line = line_location(indesc, loc, buf, sizeof(buf));
+ break;
+ case INDESC_INTERNAL:
+ case INDESC_NETLINK:
+ break;
+ default:
+ BUG("invalid input descriptor type %u\n", indesc->type);
+ }
+
+ print_location(octx->error_fp, indesc, loc);
+ fprintf(octx->error_fp, "%s\n", line);
+}
+
+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)
+{
+ const struct stmt *stmt;
+ uint32_t i, j;
+
+ 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->ops->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;
+ }
+ }
+ }
+
+ /* merge by verdict, even if no verdict is specified. */
+ return MERGE_BY_VERDICT;
+}
+
+static void merge_rules(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct output_ctx *octx)
+{
+ uint32_t merge_type;
+ bool same_verdict;
+ uint32_t i;
+
+ merge_type = merge_stmt_type(ctx, from, to);
+
+ switch (merge_type) {
+ case MERGE_BY_VERDICT:
+ same_verdict = stmt_verdict_cmp(ctx, from, to);
+ if (merge->num_stmts > 1) {
+ if (same_verdict)
+ merge_concat_stmts(ctx, from, to, merge);
+ else
+ merge_concat_stmts_vmap(ctx, from, to, merge);
+ } else {
+ if (same_verdict)
+ merge_stmts(ctx, from, to, merge);
+ else
+ merge_stmts_vmap(ctx, from, to, merge);
+ }
+ break;
+ 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) {
+ free_const(ctx->rule[from]->comment);
+ ctx->rule[from]->comment = NULL;
+ }
+
+ octx->flags |= NFT_CTX_OUTPUT_STATELESS;
+
+ fprintf(octx->error_fp, "Merging:\n");
+ rule_optimize_print(octx, ctx->rule[from]);
+
+ for (i = from + 1; i <= to; i++) {
+ rule_optimize_print(octx, ctx->rule[i]);
+ list_del(&ctx->rule[i]->list);
+ rule_free(ctx->rule[i]);
+ }
+
+ fprintf(octx->error_fp, "into:\n\t");
+ rule_print(ctx->rule[from], octx);
+ fprintf(octx->error_fp, "\n");
+
+ octx->flags &= ~NFT_CTX_OUTPUT_STATELESS;
+}
+
+static bool stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ if (!stmt_a && !stmt_b)
+ return true;
+ else if (!stmt_a)
+ return false;
+ else if (!stmt_b)
+ return false;
+
+ return __stmt_type_eq(stmt_a, stmt_b, true);
+}
+
+static bool stmt_is_mergeable(const struct stmt *stmt)
+{
+ if (!stmt)
+ return false;
+
+ switch (stmt->ops->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, 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;
+}
+
+static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
+{
+ struct optimize_ctx *ctx;
+ uint32_t num_merges = 0;
+ struct merge *merge;
+ uint32_t i, j, m, k;
+ struct rule *rule;
+ int ret;
+
+ ctx = xzalloc(sizeof(*ctx));
+
+ /* Step 1: collect statements in rules */
+ list_for_each_entry(rule, rules, list) {
+ ret = rule_collect_stmts(ctx, rule);
+ if (ret < 0)
+ goto err;
+
+ 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_array(MAX_STMTS,
+ sizeof(**ctx->stmt_matrix));
+
+ merge = xzalloc(sizeof(*merge) * ctx->num_rules);
+
+ /* Step 2: Build matrix of statements */
+ i = 0;
+ list_for_each_entry(rule, rules, list)
+ rule_build_stmt_matrix_stmts(ctx, rule, &i);
+
+ /* Step 3: Look for common selectors for possible rule mergers */
+ for (i = 0; i < ctx->num_rules; i++) {
+ for (j = i + 1; j < ctx->num_rules; j++) {
+ if (!rules_eq(ctx, i, j)) {
+ if (merge[num_merges].num_rules > 0)
+ num_merges++;
+
+ i = j - 1;
+ break;
+ }
+ if (merge[num_merges].num_rules > 0) {
+ merge[num_merges].num_rules++;
+ } else {
+ merge[num_merges].rule_from = i;
+ merge[num_merges].num_rules = 2;
+ }
+ }
+ if (j == ctx->num_rules && merge[num_merges].num_rules > 0) {
+ num_merges++;
+ break;
+ }
+ }
+
+ /* Step 4: Infer how to merge the candidate rules */
+ for (k = 0; k < num_merges; k++) {
+ 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) {
+ case STMT_EXPRESSION:
+ merge[k].stmt[merge[k].num_stmts++] = m;
+ break;
+ case STMT_VERDICT:
+ if (ctx->stmt_matrix[i][m]->expr->etype == EXPR_MAP)
+ merge[k].stmt[merge[k].num_stmts++] = m;
+ break;
+ default:
+ break;
+ }
+ }
+
+ j = merge[k].num_rules - 1;
+ merge_rules(ctx, i, i + j, &merge[k], &nft->output);
+ }
+ ret = 0;
+ for (i = 0; i < ctx->num_rules; i++)
+ free(ctx->stmt_matrix[i]);
+
+ free(ctx->stmt_matrix);
+ free(merge);
+err:
+ for (i = 0; i < ctx->num_stmts; i++)
+ stmt_free(ctx->stmt[i]);
+
+ free(ctx->rule);
+ free(ctx);
+
+ return ret;
+}
+
+static int cmd_optimize(struct nft_ctx *nft, struct cmd *cmd)
+{
+ struct table *table;
+ struct chain *chain;
+ int ret = 0;
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ table = cmd->table;
+ if (!table)
+ break;
+
+ list_for_each_entry(chain, &table->chains, list) {
+ if (chain->flags & CHAIN_F_HW_OFFLOAD)
+ continue;
+
+ chain_optimize(nft, &chain->rules);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int nft_optimize(struct nft_ctx *nft, struct list_head *cmds)
+{
+ struct cmd *cmd;
+ int ret = 0;
+
+ list_for_each_entry(cmd, cmds, list) {
+ switch (cmd->op) {
+ case CMD_ADD:
+ ret = cmd_optimize(nft, cmd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
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
new file mode 100644
index 00000000..65eaad3e
--- /dev/null
+++ b/src/owner.c
@@ -0,0 +1,182 @@
+/*
+ * 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 <sys/time.h>
+#include <time.h>
+#include <inttypes.h>
+#include <dirent.h>
+
+#include <netlink.h>
+#include <owner.h>
+
+static char *pid2name(pid_t pid)
+{
+ char procname[256], *prog;
+ FILE *fp;
+ int ret;
+
+ ret = snprintf(procname, sizeof(procname), "/proc/%lu/stat", (unsigned long)pid);
+ if (ret < 0 || ret > (int)sizeof(procname))
+ return NULL;
+
+ fp = fopen(procname, "r");
+ if (!fp)
+ return NULL;
+
+ ret = fscanf(fp, "%*u (%m[^)]", &prog);
+
+ fclose(fp);
+
+ if (ret == 1)
+ return prog;
+
+ return NULL;
+}
+
+static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode)
+{
+ const struct dirent *ent;
+ char procname[256];
+ DIR *dir;
+ int ret;
+
+ ret = snprintf(procname, sizeof(procname), "/proc/%lu/fd/", (unsigned long)pid);
+ if (ret < 0 || ret >= (int)sizeof(procname))
+ return NULL;
+
+ dir = opendir(procname);
+ if (!dir)
+ return NULL;
+
+ for (;;) {
+ unsigned long ino;
+ char tmp[128];
+ ssize_t rl;
+
+ ent = readdir(dir);
+ if (!ent)
+ break;
+
+ if (ent->d_type != DT_LNK)
+ continue;
+
+ ret = snprintf(procname, sizeof(procname), "/proc/%d/fd/%s",
+ pid, ent->d_name);
+ if (ret < 0 || ret >= (int)sizeof(procname))
+ continue;
+
+ rl = readlink(procname, tmp, sizeof(tmp));
+ if (rl <= 0 || rl >= (ssize_t)sizeof(tmp))
+ continue;
+
+ tmp[rl] = 0;
+
+ ret = sscanf(tmp, "socket:[%lu]", &ino);
+ if (ret == 1 && ino == inode) {
+ closedir(dir);
+ return pid2name(pid);
+ }
+ }
+
+ closedir(dir);
+ return NULL;
+}
+
+static char *name_by_portid(uint32_t portid, unsigned long inode)
+{
+ const struct dirent *ent;
+ char *prog;
+ DIR *dir;
+
+ /* Many netlink users use their process ID to allocate the first port id. */
+ prog = portid2name(portid, portid, inode);
+ if (prog)
+ return prog;
+
+ /* no luck, search harder. */
+ dir = opendir("/proc");
+ if (!dir)
+ return NULL;
+
+ for (;;) {
+ unsigned long pid;
+ char *end;
+
+ ent = readdir(dir);
+ if (!ent)
+ break;
+
+ if (ent->d_type != DT_DIR)
+ continue;
+
+ pid = strtoul(ent->d_name, &end, 10);
+ if (pid <= 1 || *end)
+ continue;
+
+ if (pid == portid) /* already tried */
+ continue;
+
+ prog = portid2name(pid, portid, inode);
+ if (prog)
+ break;
+ }
+
+ closedir(dir);
+ return prog;
+}
+
+char *get_progname(uint32_t portid)
+{
+ FILE *fp = fopen("/proc/net/netlink", "r");
+ uint32_t portid_check;
+ unsigned long inode;
+ int ret, prot;
+
+ if (!fp)
+ return NULL;
+
+ for (;;) {
+ char line[256];
+
+ if (!fgets(line, sizeof(line), fp))
+ break;
+
+ ret = sscanf(line, "%*x %d %u %*x %*d %*d %*x %*d %*u %lu\n",
+ &prot, &portid_check, &inode);
+
+ if (ret == EOF)
+ break;
+
+ if (ret == 3 && portid_check == portid && prot == NETLINK_NETFILTER) {
+ static uint32_t last_portid;
+ static uint32_t last_inode;
+ static char *last_program;
+ char *prog;
+
+ fclose(fp);
+
+ if (last_portid == portid && last_inode == inode)
+ return last_program;
+
+ prog = name_by_portid(portid, inode);
+
+ free(last_program);
+ last_program = prog;
+ last_portid = portid;
+ last_inode = inode;
+ return prog;
+ }
+ }
+
+ fclose(fp);
+ return NULL;
+}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 167c3158..53f45315 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -9,11 +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>
@@ -38,6 +41,7 @@
#include <utils.h>
#include <parser.h>
#include <erec.h>
+#include <sctp_chunk.h>
#include "parser_bison.h"
@@ -63,16 +67,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--;
}
@@ -121,6 +135,62 @@ static struct expr *handle_concat_expr(const struct location *loc,
return expr;
}
+static bool already_set(const void *attr, const struct location *loc,
+ struct parser_state *state)
+{
+ if (!attr)
+ return false;
+
+ erec_queue(error(loc, "You can only specify this once. This statement is duplicated."),
+ state->msgs);
+ 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) \
@@ -173,7 +243,12 @@ int nft_lex(void *, void *, void *);
struct handle_spec handle_spec;
struct position_spec position_spec;
struct prio_spec prio_spec;
- const struct exthdr_desc *exthdr_desc;
+ struct limit_rate limit_rate;
+ struct tcp_kind_field {
+ 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"
@@ -213,6 +288,8 @@ int nft_lex(void *, void *, void *);
%token SOCKET "socket"
%token TRANSPARENT "transparent"
+%token WILDCARD "wildcard"
+%token CGROUPV2 "cgroupv2"
%token TPROXY "tproxy"
@@ -221,11 +298,11 @@ int nft_lex(void *, void *, void *);
%token SYNPROXY "synproxy"
%token MSS "mss"
%token WSCALE "wscale"
-%token SACKPERM "sack-perm"
%token TYPEOF "typeof"
%token HOOK "hook"
+%token HOOKS "hooks"
%token DEVICE "device"
%token DEVICES "devices"
%token TABLE "table"
@@ -261,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"
@@ -298,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"
@@ -314,6 +393,7 @@ int nft_lex(void *, void *, void *);
%token VLAN "vlan"
%token ID "id"
%token CFI "cfi"
+%token DEI "dei"
%token PCP "pcp"
%token ARP "arp"
@@ -362,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"
@@ -388,25 +469,69 @@ int nft_lex(void *, void *, void *);
%token OPTION "option"
%token ECHO "echo"
%token EOL "eol"
-%token MAXSEG "maxseg"
-%token NOOP "noop"
+%token MPTCP "mptcp"
+%token NOP "nop"
%token SACK "sack"
%token SACK0 "sack0"
%token SACK1 "sack1"
%token SACK2 "sack2"
%token SACK3 "sack3"
-%token SACK_PERMITTED "sack-permitted"
+%token SACK_PERM "sack-permitted"
+%token FASTOPEN "fastopen"
+%token MD5SIG "md5sig"
%token TIMESTAMP "timestamp"
-%token KIND "kind"
%token COUNT "count"
%token LEFT "left"
%token RIGHT "right"
%token TSVAL "tsval"
%token TSECR "tsecr"
+%token SUBTYPE "subtype"
%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"
+%token INIT "init"
+%token INIT_ACK "init-ack"
+%token HEARTBEAT "heartbeat"
+%token HEARTBEAT_ACK "heartbeat-ack"
+%token ABORT "abort"
+%token SHUTDOWN "shutdown"
+%token SHUTDOWN_ACK "shutdown-ack"
+%token ERROR "error"
+%token COOKIE_ECHO "cookie-echo"
+%token COOKIE_ACK "cookie-ack"
+%token ECNE "ecne"
+%token CWR "cwr"
+%token SHUTDOWN_COMPLETE "shutdown-complete"
+%token ASCONF_ACK "asconf-ack"
+%token FORWARD_TSN "forward-tsn"
+%token ASCONF "asconf"
+%token TSN "tsn"
+%token STREAM "stream"
+%token SSN "ssn"
+%token PPID "ppid"
+%token INIT_TAG "init-tag"
+%token A_RWND "a-rwnd"
+%token NUM_OSTREAMS "num-outbound-streams"
+%token NUM_ISTREAMS "num-inbound-streams"
+%token INIT_TSN "initial-tsn"
+%token CUM_TSN_ACK "cum-tsn-ack"
+%token NUM_GACK_BLOCKS "num-gap-ack-blocks"
+%token NUM_DUP_TSNS "num-dup-tsns"
+%token LOWEST_TSN "lowest-tsn"
+%token SEQNO "seqno"
+%token NEW_CUM_TSN "new-cum-tsn"
+
%token VTAG "vtag"
%token RT "rt"
@@ -477,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"
@@ -502,9 +630,6 @@ int nft_lex(void *, void *, void *);
%token SECMARK "secmark"
%token SECMARKS "secmarks"
-%token NANOSECOND "nanosecond"
-%token MICROSECOND "microsecond"
-%token MILLISECOND "millisecond"
%token SECOND "second"
%token MINUTE "minute"
%token HOUR "hour"
@@ -556,19 +681,21 @@ int nft_lex(void *, void *, void *);
%token EXTHDR "exthdr"
%token IPSEC "ipsec"
-%token MODE "mode"
%token REQID "reqid"
%token SPNUM "spnum"
-%token TRANSPORT "transport"
-%token TUNNEL "tunnel"
%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 quota_used
%type <expr> data_type_expr data_type_atom_expr
%destructor { expr_free($$); } data_type_expr data_type_atom_expr
@@ -576,29 +703,47 @@ 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
+%type <handle> chain_spec chainid_spec chain_or_id_spec
+%destructor { handle_free(&$$); } chain_spec chainid_spec chain_or_id_spec
+
+%type <handle> flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec
+%destructor { handle_free(&$$); } flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec
+%type <handle> set_spec setid_spec set_or_id_spec
+%destructor { handle_free(&$$); } set_spec setid_spec set_or_id_spec
+%type <handle> obj_spec objid_spec obj_or_id_spec
+%destructor { handle_free(&$$); } obj_spec objid_spec obj_or_id_spec
+
+%type <handle> set_identifier flowtableid_spec flowtable_identifier obj_identifier
+%destructor { handle_free(&$$); } set_identifier flowtableid_spec obj_identifier
+
+%type <handle> basehook_spec
+%destructor { handle_free(&$$); } basehook_spec
-%type <handle> table_spec tableid_spec chain_spec chainid_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec
-%destructor { handle_free(&$$); } table_spec tableid_spec chain_spec chainid_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec
-%type <handle> set_spec setid_spec set_identifier flowtableid_spec flowtable_identifier obj_spec objid_spec obj_identifier
-%destructor { handle_free(&$$); } set_spec setid_spec set_identifier flowtableid_spec obj_spec objid_spec obj_identifier
%type <val> family_spec family_spec_explicit
%type <val32> int_num chain_policy
%type <prio_spec> extended_prio_spec prio_spec
-%type <string> extended_prio_name quota_unit
-%destructor { xfree($$); } extended_prio_name quota_unit
+%destructor { expr_free($$.expr); } extended_prio_spec prio_spec
+
+%type <string> 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
%type <chain> chain_block_alloc chain_block subchain_block
-%destructor { close_scope(state); chain_free($$); } chain_block_alloc subchain_block
+%destructor { close_scope(state); chain_free($$); } chain_block_alloc
%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
@@ -608,6 +753,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 map_block_obj_typeof map_block_data_interval
%type <flowtable> flowtable_block_alloc flowtable_block
%destructor { flowtable_free($$); } flowtable_block_alloc
@@ -615,12 +761,15 @@ int nft_lex(void *, void *, void *);
%type <obj> obj_block_alloc counter_block quota_block ct_helper_block ct_timeout_block ct_expect_block limit_block secmark_block synproxy_block
%destructor { obj_free($$); } obj_block_alloc
-%type <list> stmt_list
-%destructor { stmt_list_free($$); xfree($$); } stmt_list
-%type <stmt> stmt match_stmt verdict_stmt
-%destructor { stmt_free($$); } stmt match_stmt verdict_stmt
-%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt
-%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt
+%type <list> 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 last_stmt
+%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt last_stmt
+%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
@@ -632,7 +781,7 @@ int nft_lex(void *, void *, void *);
%type <val> level_type log_flags log_flags_tcp log_flag_tcp
%type <stmt> limit_stmt quota_stmt connlimit_stmt
%destructor { stmt_free($$); } limit_stmt quota_stmt connlimit_stmt
-%type <val> limit_burst_pkts limit_burst_bytes limit_mode time_unit quota_mode
+%type <val> limit_burst_pkts limit_burst_bytes limit_mode limit_bytes time_unit quota_mode
%type <stmt> reject_stmt reject_stmt_alloc
%destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
%type <stmt> nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
@@ -646,8 +795,10 @@ int nft_lex(void *, void *, void *);
%destructor { stmt_free($$); } chain_stmt
%type <val> chain_stmt_type
-%type <stmt> queue_stmt queue_stmt_alloc
-%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc
+%type <stmt> queue_stmt queue_stmt_alloc queue_stmt_compat
+%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc queue_stmt_compat
+%type <expr> queue_stmt_expr_simple queue_stmt_expr queue_expr reject_with_expr
+%destructor { expr_free($$); } queue_stmt_expr_simple queue_stmt_expr queue_expr reject_with_expr
%type <val> queue_stmt_flags queue_stmt_flag
%type <stmt> dup_stmt
%destructor { stmt_free($$); } dup_stmt
@@ -663,8 +814,8 @@ int nft_lex(void *, void *, void *);
%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
-%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr
+%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_key_expr typeof_verdict_expr
+%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_key_expr typeof_verdict_expr
%type <expr> exclusive_or_expr inclusive_or_expr
%destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr
%type <expr> basic_expr
@@ -682,8 +833,8 @@ int nft_lex(void *, void *, void *);
%type <expr> multiton_stmt_expr
%destructor { expr_free($$); } multiton_stmt_expr
-%type <expr> prefix_stmt_expr range_stmt_expr wildcard_expr
-%destructor { expr_free($$); } prefix_stmt_expr range_stmt_expr wildcard_expr
+%type <expr> prefix_stmt_expr range_stmt_expr
+%destructor { expr_free($$); } prefix_stmt_expr range_stmt_expr
%type <expr> primary_stmt_expr basic_stmt_expr
%destructor { expr_free($$); } primary_stmt_expr basic_stmt_expr
@@ -734,6 +885,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
@@ -753,9 +906,12 @@ int nft_lex(void *, void *, void *);
%type <expr> udp_hdr_expr udplite_hdr_expr
%destructor { expr_free($$); } udp_hdr_expr udplite_hdr_expr
%type <val> udp_hdr_field udplite_hdr_field
-%type <expr> dccp_hdr_expr sctp_hdr_expr
-%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr
+%type <expr> dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc
+%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc
%type <val> dccp_hdr_field sctp_hdr_field
+%type <val> sctp_chunk_type sctp_chunk_common_field
+%type <val> sctp_chunk_data_field sctp_chunk_init_field
+%type <val> sctp_chunk_sack_field
%type <expr> th_hdr_expr
%destructor { expr_free($$); } th_hdr_expr
%type <val> th_hdr_field
@@ -800,7 +956,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
@@ -808,7 +964,23 @@ int nft_lex(void *, void *, void *);
%type <expr> tcp_hdr_expr
%destructor { expr_free($$); } tcp_hdr_expr
%type <val> tcp_hdr_field
-%type <val> tcp_hdr_option_type tcp_hdr_option_field
+%type <val> tcp_hdr_option_type
+%type <val> tcp_hdr_option_sack
+%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
@@ -818,15 +990,21 @@ int nft_lex(void *, void *, void *);
%destructor { expr_free($$); } exthdr_exists_expr
%type <val> exthdr_key
-%type <val> ct_l4protoname ct_obj_type
+%type <val> ct_l4protoname ct_obj_type ct_cmd_type ct_obj_type_map
+
+%type <timeout_state> timeout_state
+%destructor { timeout_state_free($$); } timeout_state
-%type <list> timeout_states timeout_state
-%destructor { xfree($$); } timeout_states 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
%destructor { expr_free($$); } xfrm_expr
+%type <expr> set_elem_key_expr
+%destructor { expr_free($$); } set_elem_key_expr
+
%%
input : /* empty */
@@ -847,13 +1025,69 @@ opt_newline : NEWLINE
| /* empty */
;
+close_scope_ah : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_AH); };
+close_scope_arp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ARP); };
+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); };
+close_scope_eth : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ETH); };
+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); };
+close_scope_ip6 : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP6); };
+close_scope_vlan : { scanner_pop_start_cond(nft->scanner, PARSER_SC_VLAN); };
+close_scope_icmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ICMP); };
+close_scope_igmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IGMP); };
+close_scope_import : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_IMPORT); };
+close_scope_ipsec : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_IPSEC); };
+close_scope_list : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_LIST); };
+close_scope_limit : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LIMIT); };
+close_scope_meta : { scanner_pop_start_cond(nft->scanner, PARSER_SC_META); };
+close_scope_mh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_MH); };
+close_scope_monitor : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_MONITOR); };
+close_scope_nat : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_NAT); };
+close_scope_numgen : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_NUMGEN); };
+close_scope_osf : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_OSF); };
+close_scope_policy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_POLICY); };
+close_scope_quota : { scanner_pop_start_cond(nft->scanner, PARSER_SC_QUOTA); };
+close_scope_queue : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_QUEUE); };
+close_scope_reject : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_REJECT); };
+close_scope_reset : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_RESET); };
+close_scope_rt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_RT); };
+close_scope_sctp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SCTP); };
+close_scope_sctp_chunk : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SCTP_CHUNK); };
+close_scope_secmark : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SECMARK); };
+close_scope_socket : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SOCKET); }
+close_scope_tcp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TCP); };
+close_scope_tproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_TPROXY); };
+close_scope_type : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TYPE); };
+close_scope_th : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_TH); };
+close_scope_udp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDP); };
+close_scope_udplite : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPLITE); };
+
+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
{
@@ -863,19 +1097,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
{
@@ -884,9 +1118,10 @@ common_block : INCLUDE QUOTED_STRING stmt_separator
if (symbol_unbind(scope, $2) < 0) {
erec_queue(error(&@2, "undefined symbol '%s'", $2),
state->msgs);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
| error stmt_separator
{
@@ -926,14 +1161,15 @@ base_cmd : /* empty */ add_cmd { $$ = $1; }
| INSERT insert_cmd { $$ = $2; }
| DELETE delete_cmd { $$ = $2; }
| GET get_cmd { $$ = $2; }
- | LIST list_cmd { $$ = $2; }
- | RESET reset_cmd { $$ = $2; }
+ | LIST list_cmd close_scope_list { $$ = $2; }
+ | RESET reset_cmd close_scope_reset { $$ = $2; }
| FLUSH flush_cmd { $$ = $2; }
| RENAME rename_cmd { $$ = $2; }
- | IMPORT import_cmd { $$ = $2; }
- | EXPORT export_cmd { $$ = $2; }
- | MONITOR monitor_cmd { $$ = $2; }
+ | IMPORT import_cmd close_scope_import { $$ = $2; }
+ | 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
@@ -992,7 +1228,7 @@ add_cmd : TABLE table_spec
handle_merge(&$3->handle, &$2);
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
}
- | COUNTER obj_spec
+ | COUNTER obj_spec close_scope_counter
{
struct obj *obj;
@@ -1001,35 +1237,55 @@ add_cmd : TABLE table_spec
handle_merge(&obj->handle, &$2);
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, obj);
}
- | COUNTER obj_spec counter_obj counter_config
+ | COUNTER obj_spec counter_obj counter_config close_scope_counter
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, $3);
}
- | QUOTA obj_spec quota_obj quota_config
+ | COUNTER obj_spec counter_obj '{' counter_block '}' close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, $3);
+ }
+ | QUOTA obj_spec quota_obj quota_config close_scope_quota
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &$2, &@$, $3);
}
- | CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}'
+ | QUOTA obj_spec quota_obj '{' quota_block '}' close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &$2, &@$, $3);
+ }
+ | CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}' close_scope_ct
{
$$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_HELPER, &$3, &@$, $4);
}
- | CT TIMEOUT obj_spec ct_obj_alloc '{' ct_timeout_block '}'
+ | CT TIMEOUT obj_spec ct_obj_alloc '{' ct_timeout_block '}' close_scope_ct
{
$$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_TIMEOUT, &$3, &@$, $4);
}
- | CT EXPECTATION obj_spec ct_obj_alloc '{' ct_expect_block '}'
+ | CT EXPECTATION obj_spec ct_obj_alloc '{' ct_expect_block '}' close_scope_ct
{
$$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_EXPECT, &$3, &@$, $4);
}
- | LIMIT obj_spec limit_obj limit_config
+ | LIMIT obj_spec limit_obj limit_config close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);
+ }
+ | LIMIT obj_spec limit_obj '{' limit_block '}' close_scope_limit
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);
}
- | SECMARK obj_spec secmark_obj secmark_config
+ | SECMARK obj_spec secmark_obj secmark_config close_scope_secmark
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
}
- | SYNPROXY obj_spec synproxy_obj synproxy_config
+ | SECMARK obj_spec secmark_obj '{' secmark_block '}' close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
+ }
+ | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
+ }
+ | SYNPROXY obj_spec synproxy_obj '{' synproxy_block '}' close_scope_synproxy
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
@@ -1089,7 +1345,7 @@ create_cmd : TABLE table_spec
handle_merge(&$3->handle, &$2);
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
}
- | COUNTER obj_spec
+ | COUNTER obj_spec close_scope_counter
{
struct obj *obj;
@@ -1098,35 +1354,35 @@ create_cmd : TABLE table_spec
handle_merge(&obj->handle, &$2);
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_COUNTER, &$2, &@$, obj);
}
- | COUNTER obj_spec counter_obj counter_config
+ | COUNTER obj_spec counter_obj counter_config close_scope_counter
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_COUNTER, &$2, &@$, $3);
}
- | QUOTA obj_spec quota_obj quota_config
+ | QUOTA obj_spec quota_obj quota_config close_scope_quota
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_QUOTA, &$2, &@$, $3);
}
- | CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}'
+ | CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}' close_scope_ct
{
$$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_HELPER, &$3, &@$, $4);
}
- | CT TIMEOUT obj_spec ct_obj_alloc '{' ct_timeout_block '}'
+ | CT TIMEOUT obj_spec ct_obj_alloc '{' ct_timeout_block '}' close_scope_ct
{
$$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_TIMEOUT, &$3, &@$, $4);
}
- | CT EXPECTATION obj_spec ct_obj_alloc '{' ct_expect_block '}'
+ | CT EXPECTATION obj_spec ct_obj_alloc '{' ct_expect_block '}' close_scope_ct
{
$$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_EXPECT, &$3, &@$, $4);
}
- | LIMIT obj_spec limit_obj limit_config
+ | LIMIT obj_spec limit_obj limit_config close_scope_limit
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_LIMIT, &$2, &@$, $3);
}
- | SECMARK obj_spec secmark_obj secmark_config
+ | SECMARK obj_spec secmark_obj secmark_config close_scope_secmark
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3);
}
- | SYNPROXY obj_spec synproxy_obj synproxy_config
+ | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
@@ -1138,31 +1394,43 @@ insert_cmd : RULE rule_position rule
}
;
-delete_cmd : TABLE table_spec
- {
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_TABLE, &$2, &@$, NULL);
- }
- | TABLE tableid_spec
+table_or_id_spec : table_spec
+ | tableid_spec
+ ;
+
+chain_or_id_spec : chain_spec
+ | chainid_spec
+ ;
+
+set_or_id_spec : set_spec
+ | setid_spec
+ ;
+
+obj_or_id_spec : obj_spec
+ | objid_spec
+ ;
+
+delete_cmd : TABLE table_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_TABLE, &$2, &@$, NULL);
}
- | CHAIN chain_spec
+ | CHAIN chain_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, NULL);
}
- | CHAIN chainid_spec
+ | CHAIN chain_spec chain_block_alloc
+ '{' chain_block '}'
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ $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);
}
- | SET set_spec
- {
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
- }
- | SET setid_spec
+ | SET set_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
}
@@ -1189,52 +1457,102 @@ delete_cmd : TABLE table_spec
handle_merge(&$3->handle, &$2);
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
}
- | COUNTER obj_spec
+ | COUNTER obj_or_id_spec close_scope_counter
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL);
}
- | COUNTER objid_spec
+ | QUOTA obj_or_id_spec close_scope_quota
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_QUOTA, &$2, &@$, NULL);
}
- | QUOTA obj_spec
+ | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ $$ = cmd_alloc_obj_ct(CMD_DELETE, $2, &$3, &@$, $4);
+ if ($2 == NFT_OBJECT_CT_TIMEOUT)
+ init_list_head(&$4->ct_timeout.timeout_list);
}
- | QUOTA objid_spec
+ | LIMIT obj_or_id_spec close_scope_limit
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &$2, &@$, NULL);
}
- | CT ct_obj_type obj_spec ct_obj_alloc
+ | SECMARK obj_or_id_spec close_scope_secmark
{
- $$ = cmd_alloc_obj_ct(CMD_DELETE, $2, &$3, &@$, $4);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL);
}
- | LIMIT obj_spec
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
}
- | LIMIT objid_spec
+ ;
+
+destroy_cmd : TABLE table_or_id_spec
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_TABLE, &$2, &@$, NULL);
}
- | SECMARK obj_spec
+ | CHAIN chain_or_id_spec
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_CHAIN, &$2, &@$, NULL);
}
- | SECMARK objid_spec
+ | RULE ruleid_spec
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_RULE, &$2, &@$, NULL);
}
- | SYNPROXY obj_spec
+ | SET set_or_id_spec
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
}
- | SYNPROXY objid_spec
+ | MAP set_spec
{
- $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ $$ = 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);
@@ -1277,7 +1595,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$3, &@$, NULL);
}
- | COUNTER obj_spec
+ | COUNTER obj_spec close_scope_counter
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &$2, &@$, NULL);
}
@@ -1289,7 +1607,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$3, &@$, NULL);
}
- | QUOTA obj_spec
+ | QUOTA obj_spec close_scope_quota
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &$2, &@$, NULL);
}
@@ -1301,7 +1619,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$3, &@$, NULL);
}
- | LIMIT obj_spec
+ | LIMIT obj_spec close_scope_limit
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMIT, &$2, &@$, NULL);
}
@@ -1313,7 +1631,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$3, &@$, NULL);
}
- | SECMARK obj_spec
+ | SECMARK obj_spec close_scope_secmark
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARK, &$2, &@$, NULL);
}
@@ -1325,7 +1643,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$3, &@$, NULL);
}
- | SYNPROXY obj_spec
+ | SYNPROXY obj_spec close_scope_synproxy
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
}
@@ -1365,21 +1683,37 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_MAP, &$2, &@$, NULL);
}
- | CT ct_obj_type obj_spec
+ | CT ct_obj_type obj_spec close_scope_ct
{
$$ = cmd_alloc_obj_ct(CMD_LIST, $2, &$3, &@$, NULL);
}
- | CT HELPERS TABLE table_spec
+ | CT ct_cmd_type TABLE table_spec close_scope_ct
{
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CT_HELPERS, &$4, &@$, NULL);
+ $$ = cmd_alloc(CMD_LIST, $2, &$4, &@$, NULL);
}
- | CT TIMEOUT TABLE table_spec
+ | HOOKS basehook_spec
{
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CT_TIMEOUT, &$4, &@$, NULL);
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_HOOKS, &$2, &@$, NULL);
}
- | CT EXPECTATION TABLE table_spec
+ ;
+
+basehook_device_name : DEVICE STRING
+ {
+ $$ = $2;
+ }
+ ;
+
+basehook_spec : ruleset_spec
{
- $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CT_EXPECT, &$4, &@$, NULL);
+ $$ = $1;
+ }
+ | ruleset_spec basehook_device_name
+ {
+ if ($2) {
+ $1.obj.name = $2;
+ $1.obj.location = @2;
+ }
+ $$ = $1;
}
;
@@ -1387,11 +1721,16 @@ reset_cmd : COUNTERS ruleset_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
}
+ | COUNTERS table_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
+ }
| COUNTERS TABLE table_spec
{
+ /* alias of previous rule. */
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$3, &@$, NULL);
}
- | COUNTER obj_spec
+ | COUNTER obj_spec close_scope_counter
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTER, &$2,&@$, NULL);
}
@@ -1403,10 +1742,53 @@ reset_cmd : COUNTERS ruleset_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$3, &@$, NULL);
}
- | QUOTA obj_spec
+ | QUOTAS table_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$2, &@$, 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 table_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES TABLE table_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ }
+ | RULES chain_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES CHAIN chain_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$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_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &$2, &@$, NULL);
+ }
;
flush_cmd : TABLE table_spec
@@ -1517,19 +1899,41 @@ 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 {
- erec_queue(error(&@2, "unknown table option %s", $2),
+ $<table>0->flags |= $2;
+ }
+ | comment_spec
+ {
+ if (already_set($<table>0->comment, &@$, state)) {
+ 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);
+ free_const($1);
+ if ($$ == 0) {
+ erec_queue(error(&@1, "unknown table option %s", $1),
state->msgs);
- xfree($2);
YYERROR;
}
}
@@ -1583,7 +1987,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
}
| table_block COUNTER obj_identifier
obj_block_alloc '{' counter_block '}'
- stmt_separator
+ stmt_separator close_scope_counter
{
$4->location = @3;
$4->type = NFT_OBJECT_COUNTER;
@@ -1594,7 +1998,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
}
| table_block QUOTA obj_identifier
obj_block_alloc '{' quota_block '}'
- stmt_separator
+ stmt_separator close_scope_quota
{
$4->location = @3;
$4->type = NFT_OBJECT_QUOTA;
@@ -1603,7 +2007,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 '}' 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;
@@ -1612,7 +2016,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 '}' 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;
@@ -1621,7 +2025,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 '}' 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;
@@ -1632,7 +2036,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
}
| table_block LIMIT obj_identifier
obj_block_alloc '{' limit_block '}'
- stmt_separator
+ stmt_separator close_scope_limit
{
$4->location = @3;
$4->type = NFT_OBJECT_LIMIT;
@@ -1643,7 +2047,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
}
| table_block SECMARK obj_identifier
obj_block_alloc '{' secmark_block '}'
- stmt_separator
+ stmt_separator close_scope_secmark
{
$4->location = @3;
$4->type = NFT_OBJECT_SECMARK;
@@ -1654,7 +2058,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
}
| table_block SYNPROXY obj_identifier
obj_block_alloc '{' synproxy_block '}'
- stmt_separator
+ stmt_separator close_scope_synproxy
{
$4->location = @3;
$4->type = NFT_OBJECT_SYNPROXY;
@@ -1667,8 +2071,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++;
+ }
}
;
@@ -1683,6 +2091,23 @@ 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)) {
+ free_const($2);
+ YYERROR;
+ }
+ $1->comment = $2;
+ }
;
subchain_block : /* empty */ { $$ = $<chain>-1; }
@@ -1694,6 +2119,49 @@ subchain_block : /* empty */ { $$ = $<chain>-1; }
}
;
+typeof_verdict_expr : primary_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);
+ expr_free(e);
+ YYERROR;
+ }
+ $$ = e;
+ }
+ | typeof_expr DOT primary_expr
+ {
+ struct location rhs[] = {
+ [1] = @2,
+ [2] = @3,
+ };
+
+ $$ = handle_concat_expr(&@$, $$, $1, $3, rhs);
+ }
+ ;
+
+typeof_data_expr : INTERVAL typeof_expr
+ {
+ $2->flags |= EXPR_F_INTERVAL;
+ $$ = $2;
+ }
+ | typeof_verdict_expr
+ {
+ $$ = $1;
+ }
+ ;
+
typeof_expr : primary_expr
{
if (expr_ops($1)->build_udata == NULL) {
@@ -1719,22 +2187,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
- {
- $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
@@ -1752,13 +2223,18 @@ set_block : /* empty */ { $$ = $<set>-1; }
$1->gc_int = $3;
$$ = $1;
}
- | set_block COUNTER stmt_separator
+ | set_block stateful_stmt_list stmt_separator
{
- $1->stmt = counter_stmt_alloc(&@$);
+ list_splice_tail($2, &$1->stmt_list);
$$ = $1;
+ free($2);
}
| set_block ELEMENTS '=' set_block_expr
{
+ if (already_set($1->init, &@2, state)) {
+ expr_free($4);
+ YYERROR;
+ }
$1->init = $4;
$$ = $1;
}
@@ -1768,6 +2244,15 @@ set_block : /* empty */ { $$ = $<set>-1; }
$$ = $1;
}
| set_block set_mechanism stmt_separator
+ | set_block comment_spec stmt_separator
+ {
+ if (already_set($1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $1->comment = $2;
+ $$ = $1;
+ }
;
set_block_expr : set_expr
@@ -1789,10 +2274,29 @@ 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; }
+ | SECMARK close_scope_secmark { $$ = NFT_OBJECT_SECMARK; }
+ | 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
@@ -1801,100 +2305,103 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->timeout = $3;
$$ = $1;
}
- | map_block TYPE
- data_type_expr COLON data_type_expr
- stmt_separator
+ | 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
- stmt_separator
+ 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;
}
| map_block TYPEOF
- typeof_expr COLON typeof_expr
+ 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 COUNTER
- stmt_separator
+ 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 = NFT_OBJECT_COUNTER;
+ $1->objtype = $5;
$1->flags |= NFT_SET_OBJECT;
$$ = $1;
}
- | map_block TYPE
- data_type_expr COLON QUOTA
+ | map_block TYPEOF
+ typeof_expr COLON map_block_obj_typeof
stmt_separator
{
$1->key = $3;
- $1->objtype = NFT_OBJECT_QUOTA;
+ $1->objtype = $5;
$1->flags |= NFT_SET_OBJECT;
$$ = $1;
}
- | map_block TYPE
- data_type_expr COLON LIMIT
- stmt_separator
+ | map_block FLAGS set_flag_list stmt_separator
{
- $1->key = $3;
- $1->objtype = NFT_OBJECT_LIMIT;
- $1->flags |= NFT_SET_OBJECT;
+ $1->flags |= $3;
$$ = $1;
}
- | map_block TYPE
- data_type_expr COLON SECMARK
- stmt_separator
+ | map_block stateful_stmt_list stmt_separator
{
- $1->key = $3;
- $1->objtype = NFT_OBJECT_SECMARK;
- $1->flags |= NFT_SET_OBJECT;
+ list_splice_tail($2, &$1->stmt_list);
$$ = $1;
+ free($2);
}
- | map_block FLAGS set_flag_list stmt_separator
+ | map_block ELEMENTS '=' set_block_expr
{
- $1->flags |= $3;
+ $1->init = $4;
$$ = $1;
}
- | map_block ELEMENTS '=' set_block_expr
+ | map_block comment_spec stmt_separator
{
- $1->init = $4;
+ if (already_set($1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $1->comment = $2;
$$ = $1;
}
| map_block set_mechanism stmt_separator
;
-set_mechanism : POLICY set_policy_spec
+set_mechanism : POLICY set_policy_spec close_scope_policy
{
$<set>0->policy = $2;
}
@@ -1910,7 +2417,7 @@ set_policy_spec : PERFORMANCE { $$ = NFT_SET_POL_PERFORMANCE; }
flowtable_block_alloc : /* empty */
{
- $$ = flowtable_alloc(NULL);
+ $$ = flowtable_alloc(&internal_location);
}
;
@@ -1924,10 +2431,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;
}
@@ -1935,10 +2442,14 @@ flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
{
$$->dev_expr = $4;
}
- | flowtable_block COUNTER
+ | flowtable_block COUNTER close_scope_counter
{
$$->flags |= NFT_FLOWTABLE_COUNTER;
}
+ | flowtable_block FLAGS OFFLOAD stmt_separator
+ {
+ $$->flags |= FLOWTABLE_F_HW_OFFLOAD;
+ }
;
flowtable_expr : '{' flowtable_list_expr '}'
@@ -1966,12 +2477,23 @@ flowtable_list_expr : flowtable_expr_member
| flowtable_list_expr COMMA opt_newline
;
-flowtable_expr_member : STRING
+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
+ {
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
}
| variable_expr
{
@@ -1986,11 +2508,12 @@ data_type_atom_expr : type_identifier
if (dtype == NULL) {
erec_queue(error(&@1, "unknown datatype %s", $1),
state->msgs);
+ free_const($1);
YYERROR;
}
$$ = constant_expr_alloc(&@1, dtype, dtype->byteorder,
dtype->size, NULL);
- xfree($1);
+ free_const($1);
}
| TIME
{
@@ -2013,7 +2536,7 @@ data_type_expr : data_type_atom_expr
obj_block_alloc : /* empty */
{
- $$ = obj_alloc(NULL);
+ $$ = obj_alloc(&internal_location);
}
;
@@ -2024,6 +2547,14 @@ counter_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | counter_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
quota_block : /* empty */ { $$ = $<obj>-1; }
@@ -2033,6 +2564,14 @@ quota_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | quota_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
ct_helper_block : /* empty */ { $$ = $<obj>-1; }
@@ -2042,12 +2581,21 @@ ct_helper_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | ct_helper_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
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
@@ -2055,6 +2603,14 @@ ct_timeout_block : /*empty */
{
$$ = $1;
}
+ | ct_timeout_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
ct_expect_block : /*empty */ { $$ = $<obj>-1; }
@@ -2064,6 +2620,14 @@ ct_expect_block : /*empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | ct_expect_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
limit_block : /* empty */ { $$ = $<obj>-1; }
@@ -2073,6 +2637,14 @@ limit_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | limit_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
secmark_block : /* empty */ { $$ = $<obj>-1; }
@@ -2082,6 +2654,14 @@ secmark_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | secmark_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
synproxy_block : /* empty */ { $$ = $<obj>-1; }
@@ -2091,6 +2671,14 @@ synproxy_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | synproxy_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ free_const($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
type_identifier : STRING { $$ = $1; }
@@ -2100,32 +2688,38 @@ type_identifier : STRING { $$ = $1; }
| CLASSID { $$ = xstrdup("classid"); }
;
-hook_spec : TYPE STRING HOOK STRING dev_spec prio_spec
+hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
{
- const char *chain_type = chain_type_name_lookup($2);
+ const char *chain_type = chain_type_name_lookup($3);
if (chain_type == NULL) {
- erec_queue(error(&@2, "unknown chain type"),
+ erec_queue(error(&@3, "unknown chain type"),
state->msgs);
- xfree($2);
+ free_const($3);
+ free_const($5);
+ expr_free($6);
+ expr_free($7.expr);
YYERROR;
}
- $<chain>0->type = xstrdup(chain_type);
- xfree($2);
+ $<chain>0->type.loc = @3;
+ $<chain>0->type.str = xstrdup(chain_type);
+ free_const($3);
$<chain>0->loc = @$;
- $<chain>0->hook.loc = @4;
- $<chain>0->hook.name = chain_hookname_lookup($4);
+ $<chain>0->hook.loc = @5;
+ $<chain>0->hook.name = chain_hookname_lookup($5);
if ($<chain>0->hook.name == NULL) {
- erec_queue(error(&@4, "unknown chain hook"),
+ erec_queue(error(&@5, "unknown chain hook"),
state->msgs);
- xfree($4);
+ free_const($5);
+ expr_free($6);
+ expr_free($7.expr);
YYERROR;
}
- xfree($4);
+ free_const($5);
- $<chain>0->dev_expr = $5;
- $<chain>0->priority = $6;
+ $<chain>0->dev_expr = $6;
+ $<chain>0->priority = $7;
$<chain>0->flags |= CHAIN_F_BASECHAIN;
}
;
@@ -2169,7 +2763,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
@@ -2182,7 +2776,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
@@ -2195,7 +2789,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen(str) * BITS_PER_BYTE,
str);
- xfree($1);
+ free_const($1);
$$ = spec;
}
;
@@ -2206,12 +2800,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);
@@ -2235,7 +2828,7 @@ flags_spec : FLAGS OFFLOAD
}
;
-policy_spec : POLICY policy_expr
+policy_spec : POLICY policy_expr close_scope_policy
{
if ($<chain>0->policy) {
erec_queue(error(&@$, "you cannot set chain policy twice"),
@@ -2267,6 +2860,7 @@ chain_policy : ACCEPT { $$ = NF_ACCEPT; }
;
identifier : STRING
+ | LAST { $$ = xstrdup("last"); }
;
string : STRING
@@ -2280,7 +2874,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;
@@ -2289,16 +2883,21 @@ 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
;
-family_spec_explicit : IP { $$ = NFPROTO_IPV4; }
- | IP6 { $$ = NFPROTO_IPV6; }
- | INET { $$ = NFPROTO_INET; }
- | ARP { $$ = NFPROTO_ARP; }
- | BRIDGE { $$ = NFPROTO_BRIDGE; }
- | NETDEV { $$ = NFPROTO_NETDEV; }
+family_spec_explicit : IP close_scope_ip { $$ = NFPROTO_IPV4; }
+ | IP6 close_scope_ip6 { $$ = NFPROTO_IPV6; }
+ | INET { $$ = NFPROTO_INET; }
+ | ARP close_scope_arp { $$ = NFPROTO_ARP; }
+ | BRIDGE { $$ = NFPROTO_BRIDGE; }
+ | NETDEV { $$ = NFPROTO_NETDEV; }
;
table_spec : family_spec identifier
@@ -2476,6 +3075,7 @@ comment_spec : COMMENT string
erec_queue(error(&@2, "comment too long, %d characters maximum allowed",
NFTNL_UDATA_COMMENT_MAXLEN),
state->msgs);
+ free_const($2);
YYERROR;
}
$$ = $2;
@@ -2512,7 +3112,7 @@ rule_alloc : stmt_list
list_for_each_entry(i, $1, list)
$$->num_stmts++;
list_splice_tail($1, &$$->stmts);
- xfree($1);
+ free($1);
}
;
@@ -2529,10 +3129,78 @@ stmt_list : stmt
}
;
-stateful_stmt : counter_stmt
+stateful_stmt_list : stateful_stmt
+ {
+ $$ = xmalloc(sizeof(*$$));
+ init_list_head($$);
+ list_add_tail(&$1->list, $$);
+ }
+ | stateful_stmt_list stateful_stmt
+ {
+ $$ = $1;
+ list_add_tail(&$2->list, $1);
+ }
+ ;
+
+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
+ | last_stmt close_scope_last
;
stmt : verdict_stmt
@@ -2541,20 +3209,34 @@ stmt : verdict_stmt
| payload_stmt
| stateful_stmt
| meta_stmt
- | log_stmt
- | reject_stmt
- | nat_stmt
- | tproxy_stmt
+ | log_stmt close_scope_log
+ | reject_stmt close_scope_reject
+ | nat_stmt close_scope_nat
+ | tproxy_stmt close_scope_tproxy
| queue_stmt
| ct_stmt
- | masq_stmt
- | redir_stmt
- | dup_stmt
- | fwd_stmt
+ | masq_stmt close_scope_nat
+ | redir_stmt close_scope_nat
+ | dup_stmt close_scope_dup
+ | fwd_stmt close_scope_fwd
| set_stmt
| map_stmt
- | synproxy_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; }
@@ -2609,16 +3291,16 @@ verdict_map_list_expr : verdict_map_list_member_expr
verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = mapping_expr_alloc(&@2, $2, $4);
}
;
-connlimit_stmt : CT COUNT NUM
+connlimit_stmt : CT COUNT NUM close_scope_ct
{
$$ = connlimit_stmt_alloc(&@$);
$$->connlimit.count = $3;
}
- | CT COUNT OVER NUM
+ | CT COUNT OVER NUM close_scope_ct
{
$$ = connlimit_stmt_alloc(&@$);
$$->connlimit.count = $4;
@@ -2633,12 +3315,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
@@ -2650,14 +3326,32 @@ counter_args : counter_arg
counter_arg : PACKETS NUM
{
+ assert($<stmt>0->ops->type == STMT_COUNTER);
$<stmt>0->counter.packets = $2;
}
| BYTES NUM
{
+ assert($<stmt>0->ops->type == STMT_COUNTER);
$<stmt>0->counter.bytes = $2;
}
;
+last_stmt : LAST
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED NEVER
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED time_spec
+ {
+ $$ = last_stmt_alloc(&@$);
+ $$->last.used = $3;
+ $$->last.set = true;
+ }
+ ;
+
log_stmt : log_stmt_alloc
| log_stmt_alloc log_args
;
@@ -2692,7 +3386,7 @@ log_arg : PREFIX string
expr = constant_expr_alloc(&@$, &string_type,
BYTEORDER_HOST_ENDIAN,
(strlen($2) + 1) * BITS_PER_BYTE, $2);
- xfree($2);
+ free_const($2);
$<stmt>0->log.prefix = expr;
$<stmt>0->log.flags |= STMT_LOG_PREFIX;
break;
@@ -2766,7 +3460,7 @@ log_arg : PREFIX string
state->msgs);
}
expr_free(expr);
- xfree($2);
+ free_const($2);
YYERROR;
}
item = variable_expr_alloc(&@$, scope, sym);
@@ -2796,7 +3490,7 @@ log_arg : PREFIX string
}
}
- xfree($2);
+ free_const($2);
$<stmt>0->log.prefix = expr;
$<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
@@ -2849,18 +3543,18 @@ level_type : string
else {
erec_queue(error(&@1, "invalid log level"),
state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
-log_flags : TCP log_flags_tcp
+log_flags : TCP log_flags_tcp close_scope_tcp
{
$$ = $2;
}
- | IP OPTIONS
+ | IP OPTIONS close_scope_ip
{
$$ = NF_LOG_IPOPT;
}
@@ -2868,7 +3562,7 @@ log_flags : TCP log_flags_tcp
{
$$ = NF_LOG_UID;
}
- | ETHER
+ | ETHER close_scope_eth
{
$$ = NF_LOG_MACDECODE;
}
@@ -2895,40 +3589,29 @@ log_flag_tcp : SEQUENCE
}
;
-limit_stmt : LIMIT RATE limit_mode NUM SLASH time_unit limit_burst_pkts
+limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
{
+ if ($5 == 0) {
+ erec_queue(error(&@5, "packet limit burst must be > 0"),
+ state->msgs);
+ YYERROR;
+ }
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4;
- $$->limit.unit = $6;
- $$->limit.burst = $7;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKTS;
$$->limit.flags = $3;
}
- | LIMIT RATE limit_mode NUM STRING limit_burst_bytes
+ | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
{
- struct error_record *erec;
- uint64_t rate, unit;
-
- erec = rate_parse(&@$, $5, &rate, &unit);
- xfree($5);
- if (erec != NULL) {
- erec_queue(erec, state->msgs);
- YYERROR;
- }
-
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = rate * $4;
- $$->limit.unit = unit;
- $$->limit.burst = $6;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKT_BYTES;
$$->limit.flags = $3;
}
- | LIMIT NAME stmt_expr
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_LIMIT;
- $$->objref.expr = $3;
- }
;
quota_mode : OVER { $$ = NFT_QUOTA_F_INV; }
@@ -2947,7 +3630,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;
@@ -2956,13 +3639,13 @@ quota_used : /* empty */ { $$ = 0; }
}
;
-quota_stmt : QUOTA quota_mode NUM quota_unit quota_used
+quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
{
struct error_record *erec;
uint64_t rate;
erec = data_unit_parse(&@$, $4, &rate);
- xfree($4);
+ free_const($4);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -2972,12 +3655,6 @@ quota_stmt : QUOTA quota_mode NUM quota_unit quota_used
$$->quota.used = $5;
$$->quota.flags = $2;
}
- | QUOTA NAME stmt_expr
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_QUOTA;
- $$->objref.expr = $3;
- }
;
limit_mode : OVER { $$ = NFT_LIMIT_F_INV; }
@@ -2985,24 +3662,55 @@ limit_mode : OVER { $$ = NFT_LIMIT_F_INV; }
| /* empty */ { $$ = 0; }
;
-limit_burst_pkts : /* empty */ { $$ = 0; }
+limit_burst_pkts : /* empty */ { $$ = 5; }
| BURST NUM PACKETS { $$ = $2; }
;
+limit_rate_pkts : NUM SLASH time_unit
+ {
+ $$.rate = $1;
+ $$.unit = $3;
+ }
+ ;
+
limit_burst_bytes : /* empty */ { $$ = 0; }
- | BURST NUM BYTES { $$ = $2; }
- | BURST NUM STRING
+ | BURST limit_bytes { $$ = $2; }
+ ;
+
+limit_rate_bytes : NUM STRING
+ {
+ struct error_record *erec;
+ uint64_t rate, unit;
+
+ erec = rate_parse(&@$, $2, &rate, &unit);
+ free_const($2);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$.rate = rate * $1;
+ $$.unit = unit;
+ }
+ | limit_bytes SLASH time_unit
+ {
+ $$.rate = $1;
+ $$.unit = $3;
+ }
+ ;
+
+limit_bytes : NUM BYTES { $$ = $1; }
+ | NUM STRING
{
struct error_record *erec;
uint64_t rate;
- erec = data_unit_parse(&@$, $3, &rate);
- xfree($3);
+ erec = data_unit_parse(&@$, $2, &rate);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
}
- $$ = $2 * rate;
+ $$ = $1 * rate;
}
;
@@ -3022,44 +3730,61 @@ reject_stmt_alloc : _REJECT
}
;
+reject_with_expr : STRING
+ {
+ $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state), $1);
+ free_const($1);
+ }
+ | integer_expr { $$ = $1; }
+ ;
+
reject_opts : /* empty */
{
$<stmt>0->reject.type = -1;
$<stmt>0->reject.icmp_code = -1;
}
- | WITH ICMP TYPE STRING
+ | WITH ICMP TYPE reject_with_expr close_scope_type close_scope_icmp
+ {
+ $<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, &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 =
- symbol_expr_alloc(&@$, SYMBOL_VALUE,
- current_scope(state),
- $4);
- datatype_set($<stmt>0->reject.expr, &icmp_code_type);
- xfree($4);
+ $<stmt>0->reject.expr = $3;
+ 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, &reject_icmpv6_code_type);
}
- | WITH ICMP6 TYPE STRING
+ | WITH ICMP6 reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
- $<stmt>0->reject.expr =
- symbol_expr_alloc(&@$, SYMBOL_VALUE,
- current_scope(state),
- $4);
- datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
- xfree($4);
+ $<stmt>0->reject.expr = $3;
+ datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type);
}
- | WITH ICMPX TYPE STRING
+ | WITH ICMPX TYPE reject_with_expr close_scope_type
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
- $<stmt>0->reject.expr =
- symbol_expr_alloc(&@$, SYMBOL_VALUE,
- current_scope(state),
- $4);
- datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
- xfree($4);
+ $<stmt>0->reject.expr = $4;
+ datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type);
}
- | WITH TCP RESET
+ | WITH ICMPX reject_with_expr
+ {
+ $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
+ $<stmt>0->reject.expr = $3;
+ datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type);
+ }
+ | WITH TCP close_scope_tcp RESET close_scope_reset
{
$<stmt>0->reject.type = NFT_REJECT_TCP_RST;
}
@@ -3068,8 +3793,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
@@ -3120,12 +3845,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
@@ -3149,7 +3868,7 @@ synproxy_arg : MSS NUM
{
$<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_TIMESTAMP;
}
- | SACKPERM
+ | SACK_PERM
{
$<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_SACK_PERM;
}
@@ -3204,7 +3923,7 @@ synproxy_ts : /* empty */ { $$ = 0; }
;
synproxy_sack : /* empty */ { $$ = 0; }
- | SACKPERM
+ | SACK_PERM
{
$$ = NF_SYNPROXY_OPT_SACK_PERM;
}
@@ -3295,20 +4014,8 @@ range_stmt_expr : basic_stmt_expr DASH basic_stmt_expr
}
;
-wildcard_expr : ASTERISK
- {
- struct expr *expr;
-
- expr = constant_expr_alloc(&@$, &integer_type,
- BYTEORDER_HOST_ENDIAN,
- 0, NULL);
- $$ = prefix_expr_alloc(&@$, expr, 0);
- }
- ;
-
multiton_stmt_expr : prefix_stmt_expr
| range_stmt_expr
- | wildcard_expr
;
stmt_expr : map_stmt_expr
@@ -3367,28 +4074,24 @@ nat_stmt_args : stmt_expr
{
$<stmt>0->nat.family = $1;
$<stmt>0->nat.addr = $4;
- $<stmt>0->nat.type_flags = STMT_NAT_F_INTERVAL;
}
| INTERVAL TO stmt_expr
{
$<stmt>0->nat.addr = $3;
- $<stmt>0->nat.type_flags = STMT_NAT_F_INTERVAL;
}
| nf_key_proto PREFIX TO stmt_expr
{
$<stmt>0->nat.family = $1;
$<stmt>0->nat.addr = $4;
$<stmt>0->nat.type_flags =
- STMT_NAT_F_PREFIX |
- STMT_NAT_F_INTERVAL;
+ STMT_NAT_F_PREFIX;
$<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP;
}
| PREFIX TO stmt_expr
{
$<stmt>0->nat.addr = $3;
$<stmt>0->nat.type_flags =
- STMT_NAT_F_PREFIX |
- STMT_NAT_F_INTERVAL;
+ STMT_NAT_F_PREFIX;
$<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP;
}
;
@@ -3485,13 +4188,28 @@ nf_nat_flag : RANDOM { $$ = NF_NAT_RANGE_PROTO_RANDOM; }
| PERSISTENT { $$ = NF_NAT_RANGE_PERSISTENT; }
;
-queue_stmt : queue_stmt_alloc
+queue_stmt : queue_stmt_compat close_scope_queue
+ | QUEUE TO queue_stmt_expr close_scope_queue
+ {
+ $$ = queue_stmt_alloc(&@$, $3, 0);
+ }
+ | QUEUE FLAGS queue_stmt_flags TO queue_stmt_expr close_scope_queue
+ {
+ $$ = queue_stmt_alloc(&@$, $5, $3);
+ }
+ | QUEUE FLAGS queue_stmt_flags QUEUENUM queue_stmt_expr_simple close_scope_queue
+ {
+ $$ = queue_stmt_alloc(&@$, $5, $3);
+ }
+ ;
+
+queue_stmt_compat : queue_stmt_alloc
| queue_stmt_alloc queue_stmt_args
;
queue_stmt_alloc : QUEUE
{
- $$ = queue_stmt_alloc(&@$);
+ $$ = queue_stmt_alloc(&@$, NULL, 0);
}
;
@@ -3502,7 +4220,7 @@ queue_stmt_args : queue_stmt_arg
| queue_stmt_args queue_stmt_arg
;
-queue_stmt_arg : QUEUENUM stmt_expr
+queue_stmt_arg : QUEUENUM queue_stmt_expr_simple
{
$<stmt>0->queue.queue = $2;
$<stmt>0->queue.queue->location = @$;
@@ -3513,6 +4231,24 @@ queue_stmt_arg : QUEUENUM stmt_expr
}
;
+queue_expr : variable_expr
+ | integer_expr
+ ;
+
+queue_stmt_expr_simple : integer_expr
+ | variable_expr
+ | queue_expr DASH queue_expr
+ {
+ $$ = range_expr_alloc(&@$, $1, $3);
+ }
+ ;
+
+queue_stmt_expr : numgen_expr
+ | hash_expr
+ | map_expr
+ | queue_stmt_expr_simple
+ ;
+
queue_stmt_flags : queue_stmt_flag
| queue_stmt_flags COMMA queue_stmt_flag
{
@@ -3548,13 +4284,14 @@ set_stmt : SET set_stmt_op set_elem_expr_stmt set_ref_expr
$$->set.key = $4;
$$->set.set = $2;
}
- | set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt '}'
+ | set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt_list '}'
{
$$ = set_stmt_alloc(&@$);
$$->set.op = $1;
$$->set.key = $4;
$$->set.set = $2;
- $$->set.stmt = $5;
+ list_splice_tail($5, &$$->set.stmt_list);
+ free($5);
}
;
@@ -3571,14 +4308,15 @@ map_stmt : set_stmt_op set_ref_expr '{' set_elem_expr_stmt COLON set_elem_expr_
$$->map.data = $6;
$$->map.set = $2;
}
- | set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt COLON set_elem_expr_stmt '}'
+ | set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt_list COLON set_elem_expr_stmt '}'
{
$$ = map_stmt_alloc(&@$);
$$->map.op = $1;
$$->map.key = $4;
$$->map.data = $7;
- $$->map.stmt = $5;
$$->map.set = $2;
+ list_splice_tail($5, &$$->map.stmt_list);
+ free($5);
}
;
@@ -3654,12 +4392,12 @@ variable_expr : '$' identifier
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);
}
;
@@ -3669,7 +4407,7 @@ symbol_expr : variable_expr
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state),
$1);
- xfree($1);
+ free_const($1);
}
;
@@ -3677,12 +4415,12 @@ set_ref_expr : set_ref_symbol_expr
| variable_expr
;
-set_ref_symbol_expr : AT identifier
+set_ref_symbol_expr : AT identifier close_scope_at
{
$$ = symbol_expr_alloc(&@$, SYMBOL_SET,
current_scope(state),
$2);
- xfree($2);
+ free_const($2);
}
;
@@ -3714,7 +4452,7 @@ primary_expr : symbol_expr { $$ = $1; }
| '(' basic_expr ')' { $$ = $2; }
;
-fib_expr : FIB fib_tuple fib_result
+fib_expr : FIB fib_tuple fib_result close_scope_fib
{
if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) {
erec_queue(error(&@2, "fib: need either saddr or daddr"), state->msgs);
@@ -3739,7 +4477,7 @@ fib_expr : FIB fib_tuple fib_result
fib_result : OIF { $$ =NFT_FIB_RESULT_OIF; }
| OIFNAME { $$ =NFT_FIB_RESULT_OIFNAME; }
- | TYPE { $$ =NFT_FIB_RESULT_ADDRTYPE; }
+ | TYPE close_scope_type { $$ =NFT_FIB_RESULT_ADDRTYPE; }
;
fib_flag : SADDR { $$ = NFTA_FIB_F_SADDR; }
@@ -3756,11 +4494,11 @@ fib_tuple : fib_flag DOT fib_tuple
| fib_flag
;
-osf_expr : OSF osf_ttl HDRVERSION
+osf_expr : OSF osf_ttl HDRVERSION close_scope_osf
{
$$ = osf_expr_alloc(&@$, $2, NFT_OSF_F_VERSION);
}
- | OSF osf_ttl NAME
+ | OSF osf_ttl NAME close_scope_osf
{
$$ = osf_expr_alloc(&@$, $2, 0);
}
@@ -3779,8 +4517,10 @@ osf_ttl : /* empty */
else {
erec_queue(error(&@2, "invalid ttl option"),
state->msgs);
+ free_const($2);
YYERROR;
}
+ free_const($2);
}
;
@@ -3888,7 +4628,7 @@ set_list_member_expr : opt_newline set_expr opt_newline
}
| opt_newline set_elem_expr COLON set_rhs_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = mapping_expr_alloc(&@2, $2, $4);
}
;
@@ -3908,9 +4648,25 @@ 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_expr_alloc : set_lhs_expr
+set_elem_key_expr : set_lhs_expr { $$ = $1; }
+ | ASTERISK { $$ = set_elem_catchall_expr_alloc(&@1); }
+ ;
+
+set_elem_expr_alloc : set_elem_key_expr set_elem_stmt_list
+ {
+ $$ = set_elem_expr_alloc(&@1, $1);
+ list_splice_tail($2, &$$->stmt_list);
+ free($2);
+ }
+ | set_elem_key_expr
{
$$ = set_elem_expr_alloc(&@1, $1);
}
@@ -3933,6 +4689,10 @@ set_elem_option : TIMEOUT time_spec
}
| comment_spec
{
+ if (already_set($<expr>0->comment, &@1, state)) {
+ free_const($1);
+ YYERROR;
+ }
$<expr>0->comment = $1;
}
;
@@ -3944,35 +4704,115 @@ set_elem_expr_options : set_elem_expr_option
| set_elem_expr_options set_elem_expr_option
;
-set_elem_expr_option : TIMEOUT time_spec
+set_elem_stmt_list : set_elem_stmt
{
- $<expr>0->timeout = $2;
+ $$ = xmalloc(sizeof(*$$));
+ init_list_head($$);
+ list_add_tail(&$1->list, $$);
}
- | EXPIRES time_spec
+ | set_elem_stmt_list set_elem_stmt
{
- $<expr>0->expiration = $2;
+ $$ = $1;
+ list_add_tail(&$2->list, $1);
}
- | COUNTER
+ ;
+
+set_elem_stmt : COUNTER close_scope_counter
{
- $<expr>0->stmt = counter_stmt_alloc(&@$);
+ $$ = counter_stmt_alloc(&@$);
}
- | COUNTER PACKETS NUM BYTES NUM
+ | COUNTER PACKETS NUM BYTES NUM close_scope_counter
{
- struct stmt *stmt;
+ $$ = 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;
+ }
+ | QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
+ {
+ struct error_record *erec;
+ uint64_t rate;
+
+ erec = data_unit_parse(&@$, $4, &rate);
+ free_const($4);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = quota_stmt_alloc(&@$);
+ $$->quota.bytes = $3 * rate;
+ $$->quota.used = $5;
+ $$->quota.flags = $2;
+ }
+ | LAST USED NEVER close_scope_last
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED time_spec close_scope_last
+ {
+ $$ = last_stmt_alloc(&@$);
+ $$->last.used = $3;
+ $$->last.set = true;
+ }
+ ;
- stmt = counter_stmt_alloc(&@$);
- stmt->counter.packets = $3;
- stmt->counter.bytes = $5;
- $<expr>0->stmt = stmt;
+set_elem_expr_option : TIMEOUT time_spec
+ {
+ $<expr>0->timeout = $2;
+ }
+ | EXPIRES time_spec
+ {
+ $<expr>0->expiration = $2;
}
| comment_spec
{
+ if (already_set($<expr>0->comment, &@1, state)) {
+ free_const($1);
+ YYERROR;
+ }
$<expr>0->comment = $1;
}
;
set_lhs_expr : concat_rhs_expr
- | wildcard_expr
;
set_rhs_expr : concat_rhs_expr
@@ -4017,7 +4857,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;
@@ -4046,10 +4886,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);
}
;
@@ -4065,23 +4905,35 @@ ct_obj_type : HELPER { $$ = NFT_OBJECT_CT_HELPER; }
| EXPECTATION { $$ = NFT_OBJECT_CT_EXPECT; }
;
-ct_l4protoname : TCP { $$ = IPPROTO_TCP; }
- | UDP { $$ = IPPROTO_UDP; }
+ct_cmd_type : HELPERS { $$ = CMD_OBJ_CT_HELPERS; }
+ | 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
+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;
}
@@ -4095,17 +4947,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;
@@ -4114,7 +4965,7 @@ timeout_state : STRING COLON NUM
ts->timeout_value = $3;
ts->location = @1;
init_list_head(&ts->head);
- $$ = &ts->head;
+ $$ = ts;
}
;
@@ -4126,13 +4977,13 @@ ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator
ct = &$<obj>0->ct_timeout;
ct->l4proto = l4proto;
}
- | POLICY '=' '{' timeout_states '}' stmt_separator
+ | POLICY '=' '{' timeout_states '}' stmt_separator close_scope_policy
{
struct ct_timeout *ct;
ct = &$<obj>0->ct_timeout;
list_splice_tail($4, &ct->timeout_list);
- xfree($4);
+ free($4);
}
| L3PROTOCOL family_spec_explicit stmt_separator
{
@@ -4168,33 +5019,25 @@ ct_obj_alloc : /* empty */
}
;
-limit_config : RATE limit_mode NUM SLASH time_unit limit_burst_pkts
+limit_config : RATE limit_mode limit_rate_pkts limit_burst_pkts
{
struct limit *limit;
limit = &$<obj>0->limit;
- limit->rate = $3;
- limit->unit = $5;
- limit->burst = $6;
+ limit->rate = $3.rate;
+ limit->unit = $3.unit;
+ limit->burst = $4;
limit->type = NFT_LIMIT_PKTS;
limit->flags = $2;
}
- | RATE limit_mode NUM STRING limit_burst_bytes
+ | RATE limit_mode limit_rate_bytes limit_burst_bytes
{
struct limit *limit;
- struct error_record *erec;
- uint64_t rate, unit;
-
- erec = rate_parse(&@$, $4, &rate, &unit);
- if (erec != NULL) {
- erec_queue(erec, state->msgs);
- YYERROR;
- }
limit = &$<obj>0->limit;
- limit->rate = rate * $3;
- limit->unit = unit;
- limit->burst = $5;
+ limit->rate = $3.rate;
+ limit->unit = $3.unit;
+ limit->burst = $4;
limit->type = NFT_LIMIT_PKT_BYTES;
limit->flags = $2;
}
@@ -4215,10 +5058,30 @@ relational_expr : expr /* implicit */ rhs_expr
{
$$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2);
}
+ | expr /* implicit */ basic_rhs_expr SLASH list_rhs_expr
+ {
+ $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2);
+ }
+ | expr /* implicit */ list_rhs_expr SLASH list_rhs_expr
+ {
+ $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2);
+ }
+ | expr relational_op basic_rhs_expr SLASH list_rhs_expr
+ {
+ $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3);
+ }
+ | expr relational_op list_rhs_expr SLASH list_rhs_expr
+ {
+ $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3);
+ }
| expr relational_op rhs_expr
{
$$ = relational_expr_alloc(&@2, $2, $1, $3);
}
+ | expr relational_op list_rhs_expr
+ {
+ $$ = relational_expr_alloc(&@2, $2, $1, $3);
+ }
;
list_rhs_expr : basic_rhs_expr COMMA basic_rhs_expr
@@ -4236,7 +5099,6 @@ list_rhs_expr : basic_rhs_expr COMMA basic_rhs_expr
;
rhs_expr : concat_rhs_expr { $$ = $1; }
- | wildcard_expr { $$ = $1; }
| set_expr { $$ = $1; }
| set_ref_symbol_expr { $$ = $1; }
;
@@ -4310,60 +5172,62 @@ boolean_expr : boolean_keys
}
;
-keyword_expr : ETHER { $$ = symbol_value(&@$, "ether"); }
- | IP { $$ = symbol_value(&@$, "ip"); }
- | IP6 { $$ = symbol_value(&@$, "ip6"); }
- | VLAN { $$ = symbol_value(&@$, "vlan"); }
- | ARP { $$ = symbol_value(&@$, "arp"); }
- | DNAT { $$ = symbol_value(&@$, "dnat"); }
- | SNAT { $$ = symbol_value(&@$, "snat"); }
+keyword_expr : ETHER close_scope_eth { $$ = symbol_value(&@$, "ether"); }
+ | IP close_scope_ip { $$ = symbol_value(&@$, "ip"); }
+ | IP6 close_scope_ip6 { $$ = symbol_value(&@$, "ip6"); }
+ | VLAN close_scope_vlan { $$ = symbol_value(&@$, "vlan"); }
+ | ARP close_scope_arp { $$ = symbol_value(&@$, "arp"); }
+ | DNAT close_scope_nat { $$ = symbol_value(&@$, "dnat"); }
+ | SNAT close_scope_nat { $$ = symbol_value(&@$, "snat"); }
| ECN { $$ = symbol_value(&@$, "ecn"); }
- | RESET { $$ = symbol_value(&@$, "reset"); }
+ | 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; }
| integer_expr { $$ = $1; }
| boolean_expr { $$ = $1; }
| keyword_expr { $$ = $1; }
- | TCP
+ | TCP close_scope_tcp
{
uint8_t data = IPPROTO_TCP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | UDP
+ | UDP close_scope_udp
{
uint8_t data = IPPROTO_UDP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | UDPLITE
+ | UDPLITE close_scope_udplite
{
uint8_t data = IPPROTO_UDPLITE;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ESP
+ | ESP close_scope_esp
{
uint8_t data = IPPROTO_ESP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | AH
+ | AH close_scope_ah
{
uint8_t data = IPPROTO_AH;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ICMP
+ | ICMP close_scope_icmp
{
uint8_t data = IPPROTO_ICMP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
@@ -4377,35 +5241,42 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ICMP6
+ | ICMP6 close_scope_icmp
{
uint8_t data = IPPROTO_ICMPV6;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | COMP
+ | 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;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | DCCP
+ | DCCP close_scope_dccp
{
uint8_t data = IPPROTO_DCCP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | SCTP
+ | SCTP close_scope_sctp
{
uint8_t data = IPPROTO_SCTP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | REDIRECT
+ | REDIRECT close_scope_nat
{
uint8_t data = ICMP_REDIRECT;
$$ = constant_expr_alloc(&@$, &icmp_type_type,
@@ -4421,6 +5292,7 @@ relational_op : EQ { $$ = OP_EQ; }
| GT { $$ = OP_GT; }
| GTE { $$ = OP_GTE; }
| LTE { $$ = OP_LTE; }
+ | NOT { $$ = OP_NEG; }
;
verdict_expr : ACCEPT
@@ -4456,11 +5328,11 @@ chain_expr : variable_expr
BYTEORDER_HOST_ENDIAN,
strlen($1) * BITS_PER_BYTE,
$1);
- xfree($1);
+ free_const($1);
}
;
-meta_expr : META meta_key
+meta_expr : META meta_key close_scope_meta
{
$$ = meta_expr_alloc(&@$, $2);
}
@@ -4468,13 +5340,13 @@ meta_expr : META meta_key
{
$$ = meta_expr_alloc(&@$, $1);
}
- | META STRING
+ | META STRING close_scope_meta
{
struct error_record *erec;
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -4492,7 +5364,7 @@ meta_key_qualified : LENGTH { $$ = NFT_META_LEN; }
| PROTOCOL { $$ = NFT_META_PROTOCOL; }
| PRIORITY { $$ = NFT_META_PRIORITY; }
| RANDOM { $$ = NFT_META_PRANDOM; }
- | SECMARK { $$ = NFT_META_SECMARK; }
+ | SECMARK close_scope_secmark { $$ = NFT_META_SECMARK; }
;
meta_key_unqualified : MARK { $$ = NFT_META_MARK; }
@@ -4515,13 +5387,13 @@ meta_key_unqualified : MARK { $$ = NFT_META_MARK; }
| IIFGROUP { $$ = NFT_META_IIFGROUP; }
| OIFGROUP { $$ = NFT_META_OIFGROUP; }
| CGROUP { $$ = NFT_META_CGROUP; }
- | IPSEC { $$ = NFT_META_SECPATH; }
+ | IPSEC close_scope_ipsec { $$ = NFT_META_SECPATH; }
| TIME { $$ = NFT_META_TIME_NS; }
| DAY { $$ = NFT_META_TIME_DAY; }
| HOUR { $$ = NFT_META_TIME_HOUR; }
;
-meta_stmt : META meta_key SET stmt_expr
+meta_stmt : META meta_key SET stmt_expr close_scope_meta
{
switch ($2) {
case NFT_META_SECMARK:
@@ -4545,15 +5417,16 @@ meta_stmt : META meta_key SET stmt_expr
{
$$ = meta_stmt_alloc(&@$, $1, $3);
}
- | META STRING SET stmt_expr
+ | META STRING SET stmt_expr close_scope_meta
{
struct error_record *erec;
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;
}
@@ -4563,24 +5436,29 @@ meta_stmt : META meta_key SET stmt_expr
{
$$ = notrack_stmt_alloc(&@$);
}
- | FLOW OFFLOAD AT string
+ | FLOW OFFLOAD AT string close_scope_at
{
$$ = flow_offload_stmt_alloc(&@$, $4);
}
- | FLOW ADD AT string
+ | FLOW ADD AT string close_scope_at
{
$$ = flow_offload_stmt_alloc(&@$, $4);
}
;
-socket_expr : SOCKET socket_key
+socket_expr : SOCKET socket_key close_scope_socket
{
- $$ = socket_expr_alloc(&@$, $2);
+ $$ = socket_expr_alloc(&@$, $2, 0);
+ }
+ | SOCKET CGROUPV2 LEVEL NUM close_scope_socket
+ {
+ $$ = socket_expr_alloc(&@$, NFT_SOCKET_CGROUPV2, $4);
}
;
socket_key : TRANSPARENT { $$ = NFT_SOCKET_TRANSPARENT; }
| MARK { $$ = NFT_SOCKET_MARK; }
+ | WILDCARD { $$ = NFT_SOCKET_WILDCARD; }
;
offset_opt : /* empty */ { $$ = 0; }
@@ -4591,7 +5469,7 @@ numgen_type : INC { $$ = NFT_NG_INCREMENTAL; }
| RANDOM { $$ = NFT_NG_RANDOM; }
;
-numgen_expr : NUMGEN numgen_type MOD NUM offset_opt
+numgen_expr : NUMGEN numgen_type MOD NUM offset_opt close_scope_numgen
{
$$ = numgen_expr_alloc(&@$, $2, $4, $5);
}
@@ -4613,7 +5491,7 @@ xfrm_state_proto_key : DADDR { $$ = NFT_XFRM_KEY_DADDR_IP4; }
| SADDR { $$ = NFT_XFRM_KEY_SADDR_IP4; }
;
-xfrm_expr : IPSEC xfrm_dir xfrm_spnum xfrm_state_key
+xfrm_expr : IPSEC xfrm_dir xfrm_spnum xfrm_state_key close_scope_ipsec
{
if ($3 > 255) {
erec_queue(error(&@3, "value too large"), state->msgs);
@@ -4621,7 +5499,7 @@ xfrm_expr : IPSEC xfrm_dir xfrm_spnum xfrm_state_key
}
$$ = xfrm_expr_alloc(&@$, $2, $3, $4);
}
- | IPSEC xfrm_dir xfrm_spnum nf_key_proto xfrm_state_proto_key
+ | IPSEC xfrm_dir xfrm_spnum nf_key_proto xfrm_state_proto_key close_scope_ipsec
{
enum nft_xfrm_keys xfrmk = $5;
@@ -4648,31 +5526,31 @@ xfrm_expr : IPSEC xfrm_dir xfrm_spnum xfrm_state_key
}
;
-hash_expr : JHASH expr MOD NUM SEED NUM offset_opt
+hash_expr : JHASH expr MOD NUM SEED NUM offset_opt close_scope_hash
{
$$ = hash_expr_alloc(&@$, $4, true, $6, $7, NFT_HASH_JENKINS);
$$->hash.expr = $2;
}
- | JHASH expr MOD NUM offset_opt
+ | JHASH expr MOD NUM offset_opt close_scope_hash
{
$$ = hash_expr_alloc(&@$, $4, false, 0, $5, NFT_HASH_JENKINS);
$$->hash.expr = $2;
}
- | SYMHASH MOD NUM offset_opt
+ | SYMHASH MOD NUM offset_opt close_scope_hash
{
$$ = hash_expr_alloc(&@$, $3, false, 0, $4, NFT_HASH_SYM);
}
;
-nf_key_proto : IP { $$ = NFPROTO_IPV4; }
- | IP6 { $$ = NFPROTO_IPV6; }
+nf_key_proto : IP close_scope_ip { $$ = NFPROTO_IPV4; }
+ | IP6 close_scope_ip6 { $$ = NFPROTO_IPV6; }
;
-rt_expr : RT rt_key
+rt_expr : RT rt_key close_scope_rt
{
$$ = rt_expr_alloc(&@$, $2, true);
}
- | RT nf_key_proto rt_key
+ | RT nf_key_proto rt_key close_scope_rt
{
enum nft_rt_keys rtk = $3;
@@ -4695,18 +5573,18 @@ rt_expr : RT rt_key
rt_key : CLASSID { $$ = NFT_RT_CLASSID; }
| NEXTHOP { $$ = NFT_RT_NEXTHOP4; }
| MTU { $$ = NFT_RT_TCPMSS; }
- | IPSEC { $$ = NFT_RT_XFRM; }
+ | IPSEC close_scope_ipsec { $$ = NFT_RT_XFRM; }
;
-ct_expr : CT ct_key
+ct_expr : CT ct_key close_scope_ct
{
$$ = ct_expr_alloc(&@$, $2, -1);
}
- | CT ct_dir ct_key_dir
+ | CT ct_dir ct_key_dir close_scope_ct
{
$$ = ct_expr_alloc(&@$, $3, $2);
}
- | CT ct_dir ct_key_proto_field
+ | CT ct_dir ct_key_proto_field close_scope_ct
{
$$ = ct_expr_alloc(&@$, $3, $2);
}
@@ -4730,7 +5608,7 @@ ct_key : L3PROTOCOL { $$ = NFT_CT_L3PROTOCOL; }
| PROTO_DST { $$ = NFT_CT_PROTO_DST; }
| LABEL { $$ = NFT_CT_LABELS; }
| EVENT { $$ = NFT_CT_EVENTMASK; }
- | SECMARK { $$ = NFT_CT_SECMARK; }
+ | SECMARK close_scope_secmark { $$ = NFT_CT_SECMARK; }
| ID { $$ = NFT_CT_ID; }
| ct_key_dir_optional
;
@@ -4744,10 +5622,10 @@ ct_key_dir : SADDR { $$ = NFT_CT_SRC; }
| ct_key_dir_optional
;
-ct_key_proto_field : IP SADDR { $$ = NFT_CT_SRC_IP; }
- | IP DADDR { $$ = NFT_CT_DST_IP; }
- | IP6 SADDR { $$ = NFT_CT_SRC_IP6; }
- | IP6 DADDR { $$ = NFT_CT_DST_IP6; }
+ct_key_proto_field : IP SADDR close_scope_ip { $$ = NFT_CT_SRC_IP; }
+ | IP DADDR close_scope_ip { $$ = NFT_CT_DST_IP; }
+ | IP6 SADDR close_scope_ip6 { $$ = NFT_CT_SRC_IP6; }
+ | IP6 DADDR close_scope_ip6 { $$ = NFT_CT_DST_IP6; }
;
ct_key_dir_optional : BYTES { $$ = NFT_CT_BYTES; }
@@ -4774,7 +5652,7 @@ list_stmt_expr : symbol_stmt_expr COMMA symbol_stmt_expr
}
;
-ct_stmt : CT ct_key SET stmt_expr
+ct_stmt : CT ct_key SET stmt_expr close_scope_ct
{
switch ($2) {
case NFT_CT_HELPER:
@@ -4787,20 +5665,7 @@ ct_stmt : CT ct_key SET stmt_expr
break;
}
}
- | CT TIMEOUT SET stmt_expr
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
- $$->objref.expr = $4;
-
- }
- | CT EXPECTATION SET stmt_expr
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_CT_EXPECT;
- $$->objref.expr = $4;
- }
- | CT ct_dir ct_key_dir_optional SET stmt_expr
+ | CT ct_dir ct_key_dir_optional SET stmt_expr close_scope_ct
{
$$ = ct_stmt_alloc(&@$, $3, $2, $5);
}
@@ -4829,13 +5694,35 @@ payload_expr : payload_raw_expr
| comp_hdr_expr
| udp_hdr_expr
| udplite_hdr_expr
- | tcp_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
| 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
+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);
@@ -4846,10 +5733,21 @@ payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM
payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; }
| NETWORK_HDR { $$ = PROTO_BASE_NETWORK_HDR; }
- | TRANSPORT_HDR { $$ = PROTO_BASE_TRANSPORT_HDR; }
+ | TRANSPORT_HDR close_scope_th { $$ = PROTO_BASE_TRANSPORT_HDR; }
+ | STRING
+ {
+ if (!strcmp($1, "ih")) {
+ $$ = PROTO_BASE_INNER_HDR;
+ } else {
+ erec_queue(error(&@1, "unknown raw payload base"), state->msgs);
+ free_const($1);
+ YYERROR;
+ }
+ free_const($1);
+ }
;
-eth_hdr_expr : ETHER eth_hdr_field
+eth_hdr_expr : ETHER eth_hdr_field close_scope_eth
{
$$ = payload_expr_alloc(&@$, &proto_eth, $2);
}
@@ -4857,10 +5755,10 @@ eth_hdr_expr : ETHER eth_hdr_field
eth_hdr_field : SADDR { $$ = ETHHDR_SADDR; }
| DADDR { $$ = ETHHDR_DADDR; }
- | TYPE { $$ = ETHHDR_TYPE; }
+ | TYPE close_scope_type { $$ = ETHHDR_TYPE; }
;
-vlan_hdr_expr : VLAN vlan_hdr_field
+vlan_hdr_expr : VLAN vlan_hdr_field close_scope_vlan
{
$$ = payload_expr_alloc(&@$, &proto_vlan, $2);
}
@@ -4868,11 +5766,12 @@ vlan_hdr_expr : VLAN vlan_hdr_field
vlan_hdr_field : ID { $$ = VLANHDR_VID; }
| CFI { $$ = VLANHDR_CFI; }
+ | DEI { $$ = VLANHDR_DEI; }
| PCP { $$ = VLANHDR_PCP; }
- | TYPE { $$ = VLANHDR_TYPE; }
+ | TYPE close_scope_type { $$ = VLANHDR_TYPE; }
;
-arp_hdr_expr : ARP arp_hdr_field
+arp_hdr_expr : ARP arp_hdr_field close_scope_arp
{
$$ = payload_expr_alloc(&@$, &proto_arp, $2);
}
@@ -4883,23 +5782,27 @@ arp_hdr_field : HTYPE { $$ = ARPHDR_HRD; }
| HLEN { $$ = ARPHDR_HLN; }
| PLEN { $$ = ARPHDR_PLN; }
| OPERATION { $$ = ARPHDR_OP; }
- | SADDR ETHER { $$ = ARPHDR_SADDR_ETHER; }
- | DADDR ETHER { $$ = ARPHDR_DADDR_ETHER; }
- | SADDR IP { $$ = ARPHDR_SADDR_IP; }
- | DADDR IP { $$ = ARPHDR_DADDR_IP; }
+ | SADDR ETHER close_scope_eth { $$ = ARPHDR_SADDR_ETHER; }
+ | DADDR ETHER close_scope_eth { $$ = ARPHDR_DADDR_ETHER; }
+ | SADDR IP close_scope_ip { $$ = ARPHDR_SADDR_IP; }
+ | DADDR IP close_scope_ip { $$ = ARPHDR_DADDR_IP; }
;
-ip_hdr_expr : IP ip_hdr_field
+ip_hdr_expr : IP ip_hdr_field close_scope_ip
{
$$ = payload_expr_alloc(&@$, &proto_ip, $2);
}
- | IP OPTION ip_option_type ip_option_field
+ | IP OPTION ip_option_type ip_option_field close_scope_ip
{
- $$ = ipopt_expr_alloc(&@$, $3, $4, 0);
+ $$ = ipopt_expr_alloc(&@$, $3, $4);
+ if (!$$) {
+ erec_queue(error(&@1, "unknown ip option type/field"), state->msgs);
+ YYERROR;
+ }
}
- | IP OPTION ip_option_type
+ | IP OPTION ip_option_type close_scope_ip
{
- $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE, 0);
+ $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
;
@@ -4924,20 +5827,20 @@ ip_option_type : LSRR { $$ = IPOPT_LSRR; }
| RA { $$ = IPOPT_RA; }
;
-ip_option_field : TYPE { $$ = IPOPT_FIELD_TYPE; }
+ip_option_field : TYPE close_scope_type { $$ = IPOPT_FIELD_TYPE; }
| LENGTH { $$ = IPOPT_FIELD_LENGTH; }
| VALUE { $$ = IPOPT_FIELD_VALUE; }
| PTR { $$ = IPOPT_FIELD_PTR; }
| ADDR { $$ = IPOPT_FIELD_ADDR_0; }
;
-icmp_hdr_expr : ICMP icmp_hdr_field
+icmp_hdr_expr : ICMP icmp_hdr_field close_scope_icmp
{
$$ = payload_expr_alloc(&@$, &proto_icmp, $2);
}
;
-icmp_hdr_field : TYPE { $$ = ICMPHDR_TYPE; }
+icmp_hdr_field : TYPE close_scope_type { $$ = ICMPHDR_TYPE; }
| CODE { $$ = ICMPHDR_CODE; }
| CHECKSUM { $$ = ICMPHDR_CHECKSUM; }
| ID { $$ = ICMPHDR_ID; }
@@ -4946,19 +5849,19 @@ icmp_hdr_field : TYPE { $$ = ICMPHDR_TYPE; }
| MTU { $$ = ICMPHDR_MTU; }
;
-igmp_hdr_expr : IGMP igmp_hdr_field
+igmp_hdr_expr : IGMP igmp_hdr_field close_scope_igmp
{
$$ = payload_expr_alloc(&@$, &proto_igmp, $2);
}
;
-igmp_hdr_field : TYPE { $$ = IGMPHDR_TYPE; }
+igmp_hdr_field : TYPE close_scope_type { $$ = IGMPHDR_TYPE; }
| CHECKSUM { $$ = IGMPHDR_CHECKSUM; }
| MRT { $$ = IGMPHDR_MRT; }
| GROUP { $$ = IGMPHDR_GROUP; }
;
-ip6_hdr_expr : IP6 ip6_hdr_field
+ip6_hdr_expr : IP6 ip6_hdr_field close_scope_ip6
{
$$ = payload_expr_alloc(&@$, &proto_ip6, $2);
}
@@ -4974,13 +5877,13 @@ ip6_hdr_field : HDRVERSION { $$ = IP6HDR_VERSION; }
| SADDR { $$ = IP6HDR_SADDR; }
| DADDR { $$ = IP6HDR_DADDR; }
;
-icmp6_hdr_expr : ICMP6 icmp6_hdr_field
+icmp6_hdr_expr : ICMP6 icmp6_hdr_field close_scope_icmp
{
$$ = payload_expr_alloc(&@$, &proto_icmp6, $2);
}
;
-icmp6_hdr_field : TYPE { $$ = ICMP6HDR_TYPE; }
+icmp6_hdr_field : TYPE close_scope_type { $$ = ICMP6HDR_TYPE; }
| CODE { $$ = ICMP6HDR_CODE; }
| CHECKSUM { $$ = ICMP6HDR_CHECKSUM; }
| PPTR { $$ = ICMP6HDR_PPTR; }
@@ -4988,9 +5891,11 @@ icmp6_hdr_field : 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
+auth_hdr_expr : AH auth_hdr_field close_scope_ah
{
$$ = payload_expr_alloc(&@$, &proto_ah, $2);
}
@@ -5003,7 +5908,7 @@ auth_hdr_field : NEXTHDR { $$ = AHHDR_NEXTHDR; }
| SEQUENCE { $$ = AHHDR_SEQUENCE; }
;
-esp_hdr_expr : ESP esp_hdr_field
+esp_hdr_expr : ESP esp_hdr_field close_scope_esp
{
$$ = payload_expr_alloc(&@$, &proto_esp, $2);
}
@@ -5013,7 +5918,7 @@ esp_hdr_field : SPI { $$ = ESPHDR_SPI; }
| SEQUENCE { $$ = ESPHDR_SEQUENCE; }
;
-comp_hdr_expr : COMP comp_hdr_field
+comp_hdr_expr : COMP comp_hdr_field close_scope_comp
{
$$ = payload_expr_alloc(&@$, &proto_comp, $2);
}
@@ -5024,7 +5929,7 @@ comp_hdr_field : NEXTHDR { $$ = COMPHDR_NEXTHDR; }
| CPI { $$ = COMPHDR_CPI; }
;
-udp_hdr_expr : UDP udp_hdr_field
+udp_hdr_expr : UDP udp_hdr_field close_scope_udp
{
$$ = payload_expr_alloc(&@$, &proto_udp, $2);
}
@@ -5036,7 +5941,7 @@ udp_hdr_field : SPORT { $$ = UDPHDR_SPORT; }
| CHECKSUM { $$ = UDPHDR_CHECKSUM; }
;
-udplite_hdr_expr : UDPLITE udplite_hdr_field
+udplite_hdr_expr : UDPLITE udplite_hdr_field close_scope_udplite
{
$$ = payload_expr_alloc(&@$, &proto_udplite, $2);
}
@@ -5052,15 +5957,119 @@ tcp_hdr_expr : TCP tcp_hdr_field
{
$$ = payload_expr_alloc(&@$, &proto_tcp, $2);
}
- | TCP OPTION tcp_hdr_option_type tcp_hdr_option_field
- {
- $$ = tcpopt_expr_alloc(&@$, $3, $4);
- }
| TCP OPTION tcp_hdr_option_type
{
- $$ = tcpopt_expr_alloc(&@$, $3, TCPOPTHDR_FIELD_KIND);
+ $$ = tcpopt_expr_alloc(&@$, $3, TCPOPT_COMMON_KIND);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
+ | 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 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(&@$,
+ $4, TCPOPT_COMMON_KIND));
+ }
;
tcp_hdr_field : SPORT { $$ = TCPHDR_SPORT; }
@@ -5075,45 +6084,211 @@ tcp_hdr_field : SPORT { $$ = TCPHDR_SPORT; }
| URGPTR { $$ = TCPHDR_URGPTR; }
;
-tcp_hdr_option_type : EOL { $$ = TCPOPTHDR_EOL; }
- | NOOP { $$ = TCPOPTHDR_NOOP; }
- | MAXSEG { $$ = TCPOPTHDR_MAXSEG; }
- | WINDOW { $$ = TCPOPTHDR_WINDOW; }
- | SACK_PERMITTED { $$ = TCPOPTHDR_SACK_PERMITTED; }
- | SACK { $$ = TCPOPTHDR_SACK0; }
- | SACK0 { $$ = TCPOPTHDR_SACK0; }
- | SACK1 { $$ = TCPOPTHDR_SACK1; }
- | SACK2 { $$ = TCPOPTHDR_SACK2; }
- | SACK3 { $$ = TCPOPTHDR_SACK3; }
- | ECHO { $$ = TCPOPTHDR_ECHO; }
- | TIMESTAMP { $$ = TCPOPTHDR_TIMESTAMP; }
+tcp_hdr_option_kind_and_field : MSS tcpopt_field_maxseg
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MAXSEG, .field = $2 };
+ $$ = kind_field;
+ }
+ | tcp_hdr_option_sack tcpopt_field_sack
+ {
+ struct tcp_kind_field kind_field = { .kind = $1, .field = $2 };
+ $$ = kind_field;
+ }
+ | WINDOW tcpopt_field_window
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_WINDOW, .field = $2 };
+ $$ = kind_field;
+ }
+ | TIMESTAMP tcpopt_field_tsopt
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_TIMESTAMP, .field = $2 };
+ $$ = kind_field;
+ }
+ | tcp_hdr_option_type LENGTH
+ {
+ struct tcp_kind_field kind_field = { .kind = $1, .field = TCPOPT_COMMON_LENGTH };
+ $$ = kind_field;
+ }
+ | MPTCP tcpopt_field_mptcp
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MPTCP, .field = $2 };
+ $$ = kind_field;
+ }
+ ;
+
+tcp_hdr_option_sack : SACK { $$ = TCPOPT_KIND_SACK; }
+ | SACK0 { $$ = TCPOPT_KIND_SACK; }
+ | SACK1 { $$ = TCPOPT_KIND_SACK1; }
+ | SACK2 { $$ = TCPOPT_KIND_SACK2; }
+ | SACK3 { $$ = TCPOPT_KIND_SACK3; }
+ ;
+
+tcp_hdr_option_type : ECHO { $$ = TCPOPT_KIND_ECHO; }
+ | EOL { $$ = TCPOPT_KIND_EOL; }
+ | FASTOPEN { $$ = TCPOPT_KIND_FASTOPEN; }
+ | MD5SIG { $$ = TCPOPT_KIND_MD5SIG; }
+ | MPTCP { $$ = TCPOPT_KIND_MPTCP; }
+ | MSS { $$ = TCPOPT_KIND_MAXSEG; }
+ | NOP { $$ = TCPOPT_KIND_NOP; }
+ | SACK_PERM { $$ = TCPOPT_KIND_SACK_PERMITTED; }
+ | TIMESTAMP { $$ = TCPOPT_KIND_TIMESTAMP; }
+ | WINDOW { $$ = TCPOPT_KIND_WINDOW; }
+ | tcp_hdr_option_sack { $$ = $1; }
+ | NUM {
+ if ($1 > 255) {
+ erec_queue(error(&@1, "value too large"), state->msgs);
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+tcpopt_field_sack : LEFT { $$ = TCPOPT_SACK_LEFT; }
+ | RIGHT { $$ = TCPOPT_SACK_RIGHT; }
+ ;
+
+tcpopt_field_window : COUNT { $$ = TCPOPT_WINDOW_COUNT; }
;
-tcp_hdr_option_field : KIND { $$ = TCPOPTHDR_FIELD_KIND; }
- | LENGTH { $$ = TCPOPTHDR_FIELD_LENGTH; }
- | SIZE { $$ = TCPOPTHDR_FIELD_SIZE; }
- | COUNT { $$ = TCPOPTHDR_FIELD_COUNT; }
- | LEFT { $$ = TCPOPTHDR_FIELD_LEFT; }
- | RIGHT { $$ = TCPOPTHDR_FIELD_RIGHT; }
- | TSVAL { $$ = TCPOPTHDR_FIELD_TSVAL; }
- | TSECR { $$ = TCPOPTHDR_FIELD_TSECR; }
+tcpopt_field_tsopt : TSVAL { $$ = TCPOPT_TS_TSVAL; }
+ | TSECR { $$ = TCPOPT_TS_TSECR; }
;
-dccp_hdr_expr : DCCP dccp_hdr_field
+tcpopt_field_maxseg : SIZE { $$ = TCPOPT_MAXSEG_SIZE; }
+ ;
+
+tcpopt_field_mptcp : SUBTYPE { $$ = TCPOPT_MPTCP_SUBTYPE; }
+ ;
+
+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; }
| DPORT { $$ = DCCPHDR_DPORT; }
- | TYPE { $$ = DCCPHDR_TYPE; }
+ | TYPE close_scope_type { $$ = DCCPHDR_TYPE; }
+ ;
+
+sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; }
+ | INIT { $$ = SCTP_CHUNK_TYPE_INIT; }
+ | INIT_ACK { $$ = SCTP_CHUNK_TYPE_INIT_ACK; }
+ | SACK { $$ = SCTP_CHUNK_TYPE_SACK; }
+ | HEARTBEAT { $$ = SCTP_CHUNK_TYPE_HEARTBEAT; }
+ | HEARTBEAT_ACK { $$ = SCTP_CHUNK_TYPE_HEARTBEAT_ACK; }
+ | ABORT { $$ = SCTP_CHUNK_TYPE_ABORT; }
+ | SHUTDOWN { $$ = SCTP_CHUNK_TYPE_SHUTDOWN; }
+ | SHUTDOWN_ACK { $$ = SCTP_CHUNK_TYPE_SHUTDOWN_ACK; }
+ | ERROR { $$ = SCTP_CHUNK_TYPE_ERROR; }
+ | COOKIE_ECHO { $$ = SCTP_CHUNK_TYPE_COOKIE_ECHO; }
+ | COOKIE_ACK { $$ = SCTP_CHUNK_TYPE_COOKIE_ACK; }
+ | ECNE { $$ = SCTP_CHUNK_TYPE_ECNE; }
+ | CWR { $$ = SCTP_CHUNK_TYPE_CWR; }
+ | SHUTDOWN_COMPLETE { $$ = SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE; }
+ | ASCONF_ACK { $$ = SCTP_CHUNK_TYPE_ASCONF_ACK; }
+ | FORWARD_TSN { $$ = SCTP_CHUNK_TYPE_FORWARD_TSN; }
+ | ASCONF { $$ = SCTP_CHUNK_TYPE_ASCONF; }
+ ;
+
+sctp_chunk_common_field : TYPE close_scope_type { $$ = SCTP_CHUNK_COMMON_TYPE; }
+ | FLAGS { $$ = SCTP_CHUNK_COMMON_FLAGS; }
+ | LENGTH { $$ = SCTP_CHUNK_COMMON_LENGTH; }
;
-sctp_hdr_expr : SCTP sctp_hdr_field
+sctp_chunk_data_field : TSN { $$ = SCTP_CHUNK_DATA_TSN; }
+ | STREAM { $$ = SCTP_CHUNK_DATA_STREAM; }
+ | SSN { $$ = SCTP_CHUNK_DATA_SSN; }
+ | PPID { $$ = SCTP_CHUNK_DATA_PPID; }
+ ;
+
+sctp_chunk_init_field : INIT_TAG { $$ = SCTP_CHUNK_INIT_TAG; }
+ | A_RWND { $$ = SCTP_CHUNK_INIT_RWND; }
+ | NUM_OSTREAMS { $$ = SCTP_CHUNK_INIT_OSTREAMS; }
+ | NUM_ISTREAMS { $$ = SCTP_CHUNK_INIT_ISTREAMS; }
+ | INIT_TSN { $$ = SCTP_CHUNK_INIT_TSN; }
+ ;
+
+sctp_chunk_sack_field : CUM_TSN_ACK { $$ = SCTP_CHUNK_SACK_CTSN_ACK; }
+ | A_RWND { $$ = SCTP_CHUNK_SACK_RWND; }
+ | NUM_GACK_BLOCKS { $$ = SCTP_CHUNK_SACK_GACK_BLOCKS; }
+ | NUM_DUP_TSNS { $$ = SCTP_CHUNK_SACK_DUP_TSNS; }
+ ;
+
+sctp_chunk_alloc : sctp_chunk_type
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, $1, SCTP_CHUNK_COMMON_TYPE);
+ $$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ }
+ | sctp_chunk_type sctp_chunk_common_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, $1, $2);
+ }
+ | DATA sctp_chunk_data_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_DATA, $2);
+ }
+ | INIT sctp_chunk_init_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_INIT, $2);
+ }
+ | INIT_ACK sctp_chunk_init_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_INIT_ACK, $2);
+ }
+ | SACK sctp_chunk_sack_field
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_SACK, $2);
+ }
+ | SHUTDOWN CUM_TSN_ACK
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_SHUTDOWN,
+ SCTP_CHUNK_SHUTDOWN_CTSN_ACK);
+ }
+ | ECNE LOWEST_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ECNE,
+ SCTP_CHUNK_ECNE_CWR_MIN_TSN);
+ }
+ | CWR LOWEST_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_CWR,
+ SCTP_CHUNK_ECNE_CWR_MIN_TSN);
+ }
+ | ASCONF_ACK SEQNO
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ASCONF_ACK,
+ SCTP_CHUNK_ASCONF_SEQNO);
+ }
+ | FORWARD_TSN NEW_CUM_TSN
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_FORWARD_TSN,
+ SCTP_CHUNK_FORWARD_TSN_NCTSN);
+ }
+ | ASCONF SEQNO
+ {
+ $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ASCONF,
+ SCTP_CHUNK_ASCONF_SEQNO);
+ }
+ ;
+
+sctp_hdr_expr : SCTP sctp_hdr_field close_scope_sctp
{
$$ = payload_expr_alloc(&@$, &proto_sctp, $2);
}
+ | SCTP CHUNK sctp_chunk_alloc close_scope_sctp_chunk close_scope_sctp
+ {
+ $$ = $3;
+ }
;
sctp_hdr_field : SPORT { $$ = SCTPHDR_SPORT; }
@@ -5122,7 +6297,7 @@ sctp_hdr_field : SPORT { $$ = SCTPHDR_SPORT; }
| CHECKSUM { $$ = SCTPHDR_CHECKSUM; }
;
-th_hdr_expr : TRANSPORT_HDR th_hdr_field
+th_hdr_expr : TRANSPORT_HDR th_hdr_field close_scope_th
{
$$ = payload_expr_alloc(&@$, &proto_th, $2);
if ($$)
@@ -5144,7 +6319,7 @@ exthdr_expr : hbh_hdr_expr
| mh_hdr_expr
;
-hbh_hdr_expr : HBH hbh_hdr_field
+hbh_hdr_expr : HBH hbh_hdr_field close_scope_hbh
{
$$ = exthdr_expr_alloc(&@$, &exthdr_hbh, $2);
}
@@ -5154,7 +6329,7 @@ hbh_hdr_field : NEXTHDR { $$ = HBHHDR_NEXTHDR; }
| HDRLENGTH { $$ = HBHHDR_HDRLENGTH; }
;
-rt_hdr_expr : RT rt_hdr_field
+rt_hdr_expr : RT rt_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt, $2);
}
@@ -5162,11 +6337,11 @@ rt_hdr_expr : RT rt_hdr_field
rt_hdr_field : NEXTHDR { $$ = RTHDR_NEXTHDR; }
| HDRLENGTH { $$ = RTHDR_HDRLENGTH; }
- | TYPE { $$ = RTHDR_TYPE; }
+ | TYPE close_scope_type { $$ = RTHDR_TYPE; }
| SEG_LEFT { $$ = RTHDR_SEG_LEFT; }
;
-rt0_hdr_expr : RT0 rt0_hdr_field
+rt0_hdr_expr : RT0 rt0_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt0, $2);
}
@@ -5178,7 +6353,7 @@ rt0_hdr_field : ADDR '[' NUM ']'
}
;
-rt2_hdr_expr : RT2 rt2_hdr_field
+rt2_hdr_expr : RT2 rt2_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt2, $2);
}
@@ -5187,7 +6362,7 @@ rt2_hdr_expr : RT2 rt2_hdr_field
rt2_hdr_field : ADDR { $$ = RT2HDR_ADDR; }
;
-rt4_hdr_expr : RT4 rt4_hdr_field
+rt4_hdr_expr : RT4 rt4_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt4, $2);
}
@@ -5202,7 +6377,7 @@ rt4_hdr_field : LAST_ENT { $$ = RT4HDR_LASTENT; }
}
;
-frag_hdr_expr : FRAG frag_hdr_field
+frag_hdr_expr : FRAG frag_hdr_field close_scope_frag
{
$$ = exthdr_expr_alloc(&@$, &exthdr_frag, $2);
}
@@ -5216,7 +6391,7 @@ frag_hdr_field : NEXTHDR { $$ = FRAGHDR_NEXTHDR; }
| ID { $$ = FRAGHDR_ID; }
;
-dst_hdr_expr : DST dst_hdr_field
+dst_hdr_expr : DST dst_hdr_field close_scope_dst
{
$$ = exthdr_expr_alloc(&@$, &exthdr_dst, $2);
}
@@ -5226,7 +6401,7 @@ dst_hdr_field : NEXTHDR { $$ = DSTHDR_NEXTHDR; }
| HDRLENGTH { $$ = DSTHDR_HDRLENGTH; }
;
-mh_hdr_expr : MH mh_hdr_field
+mh_hdr_expr : MH mh_hdr_field close_scope_mh
{
$$ = exthdr_expr_alloc(&@$, &exthdr_mh, $2);
}
@@ -5234,7 +6409,7 @@ mh_hdr_expr : MH mh_hdr_field
mh_hdr_field : NEXTHDR { $$ = MHHDR_NEXTHDR; }
| HDRLENGTH { $$ = MHHDR_HDRLENGTH; }
- | TYPE { $$ = MHHDR_TYPE; }
+ | TYPE close_scope_type { $$ = MHHDR_TYPE; }
| RESERVED { $$ = MHHDR_RESERVED; }
| CHECKSUM { $$ = MHHDR_CHECKSUM; }
;
@@ -5246,18 +6421,18 @@ 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;
}
;
-exthdr_key : HBH { $$ = IPPROTO_HOPOPTS; }
- | RT { $$ = IPPROTO_ROUTING; }
- | FRAG { $$ = IPPROTO_FRAGMENT; }
- | DST { $$ = IPPROTO_DSTOPTS; }
- | MH { $$ = IPPROTO_MH; }
+exthdr_key : HBH close_scope_hbh { $$ = IPPROTO_HOPOPTS; }
+ | RT close_scope_rt { $$ = IPPROTO_ROUTING; }
+ | FRAG close_scope_frag { $$ = IPPROTO_FRAGMENT; }
+ | DST close_scope_dst { $$ = IPPROTO_DSTOPTS; }
+ | MH close_scope_mh { $$ = IPPROTO_MH; }
;
%%
diff --git a/src/parser_json.c b/src/parser_json.c
index 59347168..8b7efaf2 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 <sctp_chunk.h>
#include <socket.h>
#include <netdb.h>
@@ -43,7 +51,6 @@
#define CTX_F_CONCAT (1 << 8) /* inside concat_expr */
struct json_ctx {
- struct input_descriptor indesc;
struct nft_ctx *nft;
struct list_head *msgs;
struct list_head *cmds;
@@ -106,11 +113,12 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root);
/* parsing helpers */
const struct location *int_loc = &internal_location;
+static struct input_descriptor json_indesc;
static void json_lib_error(struct json_ctx *ctx, json_error_t *err)
{
struct location loc = {
- .indesc = &ctx->indesc,
+ .indesc = &json_indesc,
.line_offset = err->position - err->column,
.first_line = err->line,
.last_line = err->line,
@@ -314,15 +322,6 @@ static struct expr *json_parse_constant(struct json_ctx *ctx, const char *name)
return NULL;
}
-static struct expr *wildcard_expr_alloc(void)
-{
- struct expr *expr;
-
- expr = constant_expr_alloc(int_loc, &integer_type,
- BYTEORDER_HOST_ENDIAN, 0, NULL);
- return prefix_expr_alloc(int_loc, expr, 0);
-}
-
/* this is a combination of symbol_expr, integer_expr, boolean_expr ... */
static struct expr *json_parse_immediate(struct json_ctx *ctx, json_t *root)
{
@@ -337,7 +336,7 @@ static struct expr *json_parse_immediate(struct json_ctx *ctx, json_t *root)
symtype = SYMBOL_SET;
str++;
} else if (str[0] == '*' && str[1] == '\0') {
- return wildcard_expr_alloc();
+ return set_elem_catchall_expr_alloc(int_loc);
} else if (is_keyword(str)) {
return symbol_expr_alloc(int_loc,
SYMBOL_VALUE, NULL, str);
@@ -427,13 +426,15 @@ static struct expr *json_parse_socket_expr(struct json_ctx *ctx,
keyval = NFT_SOCKET_TRANSPARENT;
else if (!strcmp(key, "mark"))
keyval = NFT_SOCKET_MARK;
+ else if (!strcmp(key, "wildcard"))
+ keyval = NFT_SOCKET_WILDCARD;
if (keyval == -1) {
json_error(ctx, "Invalid socket key value.");
return NULL;
}
- return socket_expr_alloc(int_loc, keyval);
+ return socket_expr_alloc(int_loc, keyval, 0);
}
static int json_parse_payload_field(const struct proto_desc *desc,
@@ -456,9 +457,9 @@ static int json_parse_tcp_option_type(const char *name, int *val)
{
unsigned int i;
- for (i = 0; i < array_size(tcpopthdr_protocols); i++) {
- if (tcpopthdr_protocols[i] &&
- !strcmp(tcpopthdr_protocols[i]->name, name)) {
+ for (i = 0; i < array_size(tcpopt_protocols); i++) {
+ if (tcpopt_protocols[i] &&
+ !strcmp(tcpopt_protocols[i]->name, name)) {
if (val)
*val = i;
return 0;
@@ -466,8 +467,10 @@ static int json_parse_tcp_option_type(const char *name, int *val)
}
/* special case for sack0 - sack3 */
if (sscanf(name, "sack%u", &i) == 1 && i < 4) {
- if (val)
- *val = TCPOPTHDR_SACK0 + i;
+ if (val && i == 0)
+ *val = TCPOPT_KIND_SACK;
+ else if (val && i > 0)
+ *val = TCPOPT_KIND_SACK1 + i - 1;
return 0;
}
return 1;
@@ -475,12 +478,40 @@ static int json_parse_tcp_option_type(const char *name, int *val)
static int json_parse_tcp_option_field(int type, const char *name, int *val)
{
+ const struct exthdr_desc *desc;
+ unsigned int block = 0;
unsigned int i;
- const struct exthdr_desc *desc = tcpopthdr_protocols[type];
+
+ switch (type) {
+ case TCPOPT_KIND_SACK1:
+ type = TCPOPT_KIND_SACK;
+ block = 1;
+ break;
+ case TCPOPT_KIND_SACK2:
+ type = TCPOPT_KIND_SACK;
+ block = 2;
+ break;
+ case TCPOPT_KIND_SACK3:
+ type = TCPOPT_KIND_SACK;
+ block = 3;
+ break;
+ }
+
+ if (type < 0 || type >= (int)array_size(tcpopt_protocols))
+ return 1;
+
+ desc = tcpopt_protocols[type];
+ if (!desc)
+ return 1;
for (i = 0; i < array_size(desc->templates); i++) {
if (desc->templates[i].token &&
!strcmp(desc->templates[i].token, name)) {
+ if (block) {
+ block--;
+ continue;
+ }
+
if (val)
*val = i;
return 0;
@@ -509,6 +540,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;
@@ -522,7 +574,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;
@@ -534,15 +586,51 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
val = PROTO_BASE_NETWORK_HDR;
} else if (!strcmp(base, "th")) {
val = PROTO_BASE_TRANSPORT_HDR;
+ } else if (!strcmp(base, "ih")) {
+ val = PROTO_BASE_INNER_HDR;
} else {
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);
@@ -571,30 +659,54 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx,
const char *type, json_t *root)
{
+ int fieldval, kind, offset, len;
const char *desc, *field;
- int descval, fieldval;
struct expr *expr;
- if (json_unpack_err(ctx, root, "{s:s}", "name", &desc))
- return NULL;
+ if (!json_unpack(root, "{s:i, s:i, s:i}",
+ "base", &kind, "offset", &offset, "len", &len)) {
+ uint32_t flag = 0;
- if (json_parse_tcp_option_type(desc, &descval)) {
- json_error(ctx, "Unknown tcp option name '%s'.", desc);
- return NULL;
- }
+ if (kind < 0 || kind > 255)
+ return NULL;
- if (json_unpack(root, "{s:s}", "field", &field)) {
- expr = tcpopt_expr_alloc(int_loc, descval,
- TCPOPTHDR_FIELD_KIND);
- expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ 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);
+ if (offset == TCPOPT_COMMON_KIND && len == 8)
+ flag = NFT_EXTHDR_F_PRESENT;
+
+ tcpopt_init_raw(expr, kind, offset, len, flag);
return expr;
+ } else if (!json_unpack(root, "{s:s}", "name", &desc)) {
+ if (json_parse_tcp_option_type(desc, &kind)) {
+ json_error(ctx, "Unknown tcp option name '%s'.", desc);
+ return NULL;
+ }
+
+ if (json_unpack(root, "{s:s}", "field", &field)) {
+ expr = tcpopt_expr_alloc(int_loc, kind,
+ TCPOPT_COMMON_KIND);
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ return expr;
+ }
+
+ if (json_parse_tcp_option_field(kind, field, &fieldval)) {
+ json_error(ctx, "Unknown tcp option field '%s'.", field);
+ return NULL;
+ }
+
+ return tcpopt_expr_alloc(int_loc, kind, fieldval);
}
- if (json_parse_tcp_option_field(descval, field, &fieldval)) {
- json_error(ctx, "Unknown tcp option field '%s'.", field);
- return NULL;
- }
- return tcpopt_expr_alloc(int_loc, descval, fieldval);
+
+ json_error(ctx, "Invalid tcp option expression properties.");
+ return NULL;
}
static int json_parse_ip_option_type(const char *name, int *val)
@@ -629,7 +741,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;
@@ -645,7 +757,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, 0);
+ IPOPT_FIELD_TYPE);
expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
return expr;
@@ -654,7 +766,70 @@ static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
json_error(ctx, "Unknown ip option field '%s'.", field);
return NULL;
}
- return ipopt_expr_alloc(int_loc, descval, fieldval, 0);
+ return ipopt_expr_alloc(int_loc, descval, fieldval);
+}
+
+static int json_parse_sctp_chunk_field(const struct exthdr_desc *desc,
+ const char *name, int *val)
+{
+ unsigned int i;
+
+ for (i = 0; i < array_size(desc->templates); i++) {
+ if (desc->templates[i].token &&
+ !strcmp(desc->templates[i].token, name)) {
+ if (val)
+ *val = i;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static struct expr *json_parse_sctp_chunk_expr(struct json_ctx *ctx,
+ const char *type, json_t *root)
+{
+ const struct exthdr_desc *desc;
+ const char *name, *field;
+ struct expr *expr;
+ int fieldval;
+
+ if (json_unpack_err(ctx, root, "{s:s}", "name", &name))
+ return NULL;
+
+ desc = sctp_chunk_protocol_find(name);
+ if (!desc) {
+ json_error(ctx, "Unknown sctp chunk name '%s'.", name);
+ return NULL;
+ }
+
+ if (json_unpack(root, "{s:s}", "field", &field)) {
+ expr = sctp_chunk_expr_alloc(int_loc, desc->type,
+ SCTP_CHUNK_COMMON_TYPE);
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+
+ return expr;
+ }
+ if (json_parse_sctp_chunk_field(desc, field, &fieldval)) {
+ json_error(ctx, "Unknown sctp chunk field '%s'.", field);
+ return NULL;
+ }
+ 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)
@@ -985,13 +1160,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;
}
@@ -1029,6 +1204,18 @@ 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));
+ right = json_parse_primary_expr(ctx, json_array_get(root, 1));
+ 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);
+ left = binop_expr_alloc(int_loc, thisop, left, right);
+ }
+ return left;
+ }
+
if (json_unpack_err(ctx, root, "[o, o!]", &jleft, &jright))
return NULL;
@@ -1037,7 +1224,7 @@ static struct expr *json_parse_binop_expr(struct json_ctx *ctx,
json_error(ctx, "Failed to parse LHS of binop expression.");
return NULL;
}
- right = json_parse_primary_expr(ctx, jright);
+ right = json_parse_rhs_expr(ctx, jright);
if (!right) {
json_error(ctx, "Failed to parse RHS of binop expression.");
expr_free(left);
@@ -1356,12 +1543,14 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
{ "set", json_parse_set_expr, CTX_F_RHS | CTX_F_STMT }, /* allow this as stmt expr because that allows set references */
{ "map", json_parse_map_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS },
/* below three are multiton_rhs_expr */
- { "prefix", json_parse_prefix_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_CONCAT },
- { "range", json_parse_range_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_CONCAT },
+ { "prefix", json_parse_prefix_expr, CTX_F_RHS | CTX_F_SET_RHS | CTX_F_STMT | CTX_F_CONCAT },
+ { "range", json_parse_range_expr, CTX_F_RHS | CTX_F_SET_RHS | CTX_F_STMT | CTX_F_CONCAT },
{ "payload", json_parse_payload_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "exthdr", json_parse_exthdr_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "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 },
+ { "dccp option", json_parse_dccp_option_expr, CTX_F_PRIMARY },
{ "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 },
{ "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 },
@@ -1560,13 +1749,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);
@@ -1586,7 +1780,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;
@@ -1595,8 +1789,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;
@@ -1614,6 +1808,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)
{
@@ -1627,14 +1842,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);
@@ -1687,7 +1902,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;
@@ -1748,6 +1963,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;
@@ -1811,8 +2029,30 @@ out_err:
return NULL;
}
+static struct stmt *json_parse_flow_offload_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ const char *opstr, *flowtable;
+
+ if (json_unpack_err(ctx, value, "{s:s, s:s}",
+ "op", &opstr, "flowtable", &flowtable))
+ return NULL;
+
+ if (strcmp(opstr, "add")) {
+ json_error(ctx, "Unknown flow offload statement op '%s'.", opstr);
+ return NULL;
+ }
+
+ if (flowtable[0] != '@') {
+ json_error(ctx, "Illegal flowtable reference in flow offload statement.");
+ return NULL;
+ }
+
+ return flow_offload_stmt_alloc(int_loc, xstrdup(flowtable + 1));
+}
+
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);
}
@@ -1849,6 +2089,23 @@ static struct stmt *json_parse_dup_stmt(struct json_ctx *ctx,
return stmt;
}
+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 int json_parse_nat_flag(struct json_ctx *ctx,
json_t *root, int *flags)
{
@@ -1859,6 +2116,7 @@ static int json_parse_nat_flag(struct json_ctx *ctx,
{ "random", NF_NAT_RANGE_PROTO_RANDOM },
{ "fully-random", NF_NAT_RANGE_PROTO_RANDOM_FULLY },
{ "persistent", NF_NAT_RANGE_PERSISTENT },
+ { "netmap", NF_NAT_RANGE_NETMAP },
};
const char *flag;
unsigned int i;
@@ -1903,6 +2161,60 @@ static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root)
return flags;
}
+static int json_parse_nat_type_flag(struct json_ctx *ctx,
+ json_t *root, int *flags)
+{
+ const struct {
+ const char *flag;
+ int val;
+ } flag_tbl[] = {
+ { "interval", STMT_NAT_F_INTERVAL },
+ { "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);
+ }
+ return flags;
+}
+
static int nat_type_parse(const char *type)
{
const char * const nat_etypes[] = {
@@ -1965,11 +2277,21 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx,
}
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;
+ }
+
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;
@@ -2005,7 +2327,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;
@@ -2021,17 +2343,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;
}
}
@@ -2047,13 +2369,36 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
return stmt;
}
+static void json_parse_set_stmt_list(struct json_ctx *ctx,
+ struct list_head *stmt_list,
+ json_t *stmt_json)
+{
+ struct list_head *head;
+ struct stmt *tmp;
+ json_t *value;
+ size_t index;
+
+ if (!stmt_json)
+ return;
+
+ if (!json_is_array(stmt_json))
+ json_error(ctx, "Unexpected object type in stmt");
+
+ head = stmt_list;
+ json_array_foreach(stmt_json, index, value) {
+ tmp = json_parse_stmt(ctx, value);
+ list_add(&tmp->list, head);
+ head = &tmp->list;
+ }
+}
+
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}",
@@ -2088,6 +2433,67 @@ 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);
+
+ return stmt;
+}
+
+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);
+
return stmt;
}
@@ -2327,7 +2733,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);
@@ -2362,7 +2768,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))
@@ -2403,14 +2809,14 @@ static int queue_flag_parse(const char *name, uint16_t *flags)
static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
- struct stmt *stmt = queue_stmt_alloc(int_loc);
+ struct expr *qexpr = NULL;
+ uint16_t flags = 0;
json_t *tmp;
if (!json_unpack(value, "{s:o}", "num", &tmp)) {
- stmt->queue.queue = json_parse_stmt_expr(ctx, tmp);
- if (!stmt->queue.queue) {
+ qexpr = json_parse_stmt_expr(ctx, tmp);
+ if (!qexpr) {
json_error(ctx, "Invalid queue num.");
- stmt_free(stmt);
return NULL;
}
}
@@ -2422,15 +2828,15 @@ static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx,
if (json_is_string(tmp)) {
flag = json_string_value(tmp);
- if (queue_flag_parse(flag, &stmt->queue.flags)) {
+ if (queue_flag_parse(flag, &flags)) {
json_error(ctx, "Invalid queue flag '%s'.",
flag);
- stmt_free(stmt);
+ expr_free(qexpr);
return NULL;
}
} else if (!json_is_array(tmp)) {
json_error(ctx, "Unexpected object type in queue flags.");
- stmt_free(stmt);
+ expr_free(qexpr);
return NULL;
}
@@ -2438,20 +2844,20 @@ static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx,
if (!json_is_string(val)) {
json_error(ctx, "Invalid object in queue flag array at index %zu.",
index);
- stmt_free(stmt);
+ expr_free(qexpr);
return NULL;
}
flag = json_string_value(val);
- if (queue_flag_parse(flag, &stmt->queue.flags)) {
+ if (queue_flag_parse(flag, &flags)) {
json_error(ctx, "Invalid queue flag '%s'.",
flag);
- stmt_free(stmt);
+ expr_free(qexpr);
return NULL;
}
}
}
- return stmt;
+ return queue_stmt_alloc(int_loc, qexpr, flags);
}
static struct stmt *json_parse_connlimit_stmt(struct json_ctx *ctx,
@@ -2472,6 +2878,21 @@ static struct stmt *json_parse_connlimit_stmt(struct json_ctx *ctx,
return stmt;
}
+static struct stmt *json_parse_optstrip_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct expr *expr = json_parse_expr(ctx, value);
+
+ if (!expr ||
+ expr->etype != EXPR_EXTHDR ||
+ expr->exthdr.op != NFT_EXTHDR_OP_TCPOPT) {
+ json_error(ctx, "Illegal TCP optstrip argument");
+ return NULL;
+ }
+
+ return optstrip_stmt_alloc(int_loc, expr);
+}
+
static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{
struct {
@@ -2488,7 +2909,9 @@ 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 },
{ "notrack", json_parse_notrack_stmt },
{ "dup", json_parse_dup_stmt },
@@ -2498,6 +2921,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 },
@@ -2507,6 +2931,8 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "ct count", json_parse_connlimit_stmt },
{ "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;
@@ -2526,6 +2952,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);
@@ -2535,20 +2966,67 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
return NULL;
}
+static int json_parse_table_flags(struct json_ctx *ctx, json_t *root,
+ enum table_flags *flags)
+{
+ json_t *tmp, *tmp2;
+ size_t index;
+ int flag;
+
+ if (json_unpack(root, "{s:o}", "flags", &tmp))
+ return 0;
+
+ if (json_is_string(tmp)) {
+ flag = parse_table_flag(json_string_value(tmp));
+ if (flag) {
+ *flags = flag;
+ return 0;
+ }
+ json_error(ctx, "Invalid table flag '%s'.",
+ json_string_value(tmp));
+ return 1;
+ }
+ if (!json_is_array(tmp)) {
+ json_error(ctx, "Unexpected table flags value.");
+ return 1;
+ }
+ json_array_foreach(tmp, index, tmp2) {
+ if (json_is_string(tmp2)) {
+ flag = parse_table_flag(json_string_value(tmp2));
+
+ if (flag) {
+ *flags |= flag;
+ continue;
+ }
+ }
+ json_error(ctx, "Invalid table flag at index %zu.", index);
+ return 1;
+ }
+ return 0;
+}
+
static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
+ const char *family = "", *comment = NULL;
struct handle h = {
.table.location = *int_loc,
};
- const char *family = "";
+ struct table *table = NULL;
+ enum table_flags 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);
+ if (json_parse_table_flags(ctx, root, &flags))
+ return NULL;
+
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.table.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -2562,10 +3040,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)
@@ -2584,24 +3070,60 @@ 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)) {
+ 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)) {
+ 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;
+ }
+ tmp = constant_expr_alloc(int_loc, &string_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;
- const char name[IFNAMSIZ];
- 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)) {
@@ -2616,16 +3138,24 @@ 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 = xstrdup(type);
+ chain->type.str = xstrdup(type);
chain->priority.expr = constant_expr_alloc(int_loc, &integer_type,
BYTEORDER_HOST_ENDIAN,
sizeof(int) * BITS_PER_BYTE,
@@ -2637,16 +3167,15 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
return NULL;
}
- 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.");
+ chain_free(chain);
+ return NULL;
+ }
}
if (!json_unpack(root, "{s:s}", "policy", &policy)) {
@@ -2686,7 +3215,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;
@@ -2697,7 +3226,7 @@ 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)) {
@@ -2745,14 +3274,18 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
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;
}
@@ -2768,6 +3301,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;
@@ -2782,9 +3316,9 @@ 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;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
@@ -2793,7 +3327,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.");
@@ -2810,14 +3344,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.");
@@ -2833,19 +3369,19 @@ 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);
+ json_error(ctx, "Invalid map type '%s'.",
+ json_dumps(tmp, 0));
set_free(set);
handle_free(&h);
return NULL;
@@ -2894,6 +3430,10 @@ 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);
handle_merge(&set->handle, &h);
@@ -2935,37 +3475,6 @@ static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx,
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 = symbol_expr_alloc(int_loc, SYMBOL_VALUE, NULL, 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 flowtable dev at index %zu.",
- index);
- expr_free(expr);
- return NULL;
- }
- tmp = symbol_expr_alloc(int_loc, SYMBOL_VALUE, NULL, dev);
- compound_expr_add(expr, tmp);
- }
- return expr;
-}
-
static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
json_t *root, enum cmd_ops op,
enum cmd_obj cmd_obj)
@@ -2973,7 +3482,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
const char *family, *hook, *hookstr;
struct flowtable *flowtable;
struct handle h = { 0 };
- json_t *devs;
+ json_t *devs = NULL;
int prio;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
@@ -2984,7 +3493,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.");
@@ -2999,17 +3508,18 @@ 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, s:o}",
+ if (json_unpack_err(ctx, root, "{s:s, s:i}",
"hook", &hook,
- "prio", &prio,
- "dev", &devs)) {
+ "prio", &prio)) {
handle_free(&h);
return NULL;
}
+ json_unpack(root, "{s:o}", "dev", &devs);
+
hookstr = chain_hookname_lookup(hook);
if (!hookstr) {
json_error(ctx, "Invalid flowtable hook '%s'.", hook);
@@ -3024,12 +3534,14 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
BYTEORDER_HOST_ENDIAN,
sizeof(int) * BITS_PER_BYTE, &prio);
- flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs);
- if (!flowtable->dev_expr) {
- json_error(ctx, "Invalid flowtable dev.");
- flowtable_free(flowtable);
- handle_free(&h);
- return NULL;
+ if (devs) {
+ flowtable->dev_expr = json_parse_devs(ctx, devs);
+ if (!flowtable->dev_expr) {
+ json_error(ctx, "Invalid flowtable dev.");
+ flowtable_free(flowtable);
+ handle_free(&h);
+ return NULL;
+ }
}
return cmd_alloc(op, cmd_obj, &h, int_loc, flowtable);
}
@@ -3040,7 +3552,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)) {
@@ -3048,7 +3560,6 @@ static int json_parse_ct_timeout_policy(struct json_ctx *ctx,
return 1;
}
- init_list_head(&obj->ct_timeout.timeout_list);
json_object_foreach(tmp, key, val) {
struct timeout_state *ts;
@@ -3073,8 +3584,8 @@ 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;
@@ -3086,7 +3597,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)) {
@@ -3102,7 +3613,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));
@@ -3111,6 +3622,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;
@@ -3193,8 +3707,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj_free(obj);
return NULL;
}
- 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;
@@ -3221,11 +3736,12 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
return NULL;
}
}
- 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;
@@ -3237,7 +3753,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
}
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")) {
@@ -3255,11 +3771,12 @@ 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)) {
+ "mss", &i, "wscale", &j)) {
obj_free(obj);
return NULL;
}
+ 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)) {
@@ -3303,7 +3820,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;
@@ -3500,6 +4018,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)
{
@@ -3513,6 +4064,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;
@@ -3611,6 +4167,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 }
@@ -3633,13 +4190,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;
@@ -3647,20 +4205,33 @@ static int json_verify_metainfo(struct json_ctx *ctx, json_t *root)
struct json_cmd_assoc {
struct json_cmd_assoc *next;
+ struct hlist_node hnode;
const struct cmd *cmd;
json_t *json;
};
-static struct json_cmd_assoc *json_cmd_list = NULL;
+#define CMD_ASSOC_HSIZE 512
+static struct hlist_head json_cmd_assoc_hash[CMD_ASSOC_HSIZE];
+static struct json_cmd_assoc *json_cmd_assoc_list;
static void json_cmd_assoc_free(void)
{
struct json_cmd_assoc *cur;
+ struct hlist_node *pos, *n;
+ int i;
+
+ while (json_cmd_assoc_list) {
+ cur = json_cmd_assoc_list->next;
+ free(json_cmd_assoc_list);
+ json_cmd_assoc_list = cur;
+ }
- while (json_cmd_list) {
- cur = json_cmd_list;
- json_cmd_list = cur->next;
- free(cur);
+ for (i = 0; i < CMD_ASSOC_HSIZE; i++) {
+ hlist_for_each_entry_safe(cur, pos, n,
+ &json_cmd_assoc_hash[i], hnode) {
+ hlist_del(&cur->hnode);
+ free(cur);
+ }
}
}
@@ -3668,20 +4239,33 @@ static void json_cmd_assoc_add(json_t *json, const struct cmd *cmd)
{
struct json_cmd_assoc *new = xzalloc(sizeof *new);
- new->next = json_cmd_list;
new->json = json;
new->cmd = cmd;
- json_cmd_list = new;
+ new->next = json_cmd_assoc_list;
+
+ json_cmd_assoc_list = new;
}
static json_t *seqnum_to_json(const uint32_t seqnum)
{
- const struct json_cmd_assoc *cur;
+ struct json_cmd_assoc *cur;
+ struct hlist_node *n;
+ int key;
+
+ while (json_cmd_assoc_list) {
+ cur = json_cmd_assoc_list;
+ json_cmd_assoc_list = cur->next;
- for (cur = json_cmd_list; cur; cur = cur->next) {
+ key = cur->cmd->seqnum % 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)
return cur->json;
}
+
return NULL;
}
@@ -3741,16 +4325,16 @@ int nft_parse_json_buffer(struct nft_ctx *nft, const char *buf,
struct list_head *msgs, struct list_head *cmds)
{
struct json_ctx ctx = {
- .indesc = {
- .type = INDESC_BUFFER,
- .data = buf,
- },
.nft = nft,
.msgs = msgs,
.cmds = cmds,
};
int ret;
+ json_indesc.type = INDESC_BUFFER;
+ json_indesc.data = buf;
+
+ parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->json_root = json_loads(buf, 0, NULL);
if (!nft->json_root)
return -EINVAL;
@@ -3768,10 +4352,6 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename,
struct list_head *msgs, struct list_head *cmds)
{
struct json_ctx ctx = {
- .indesc = {
- .type = INDESC_FILE,
- .name = filename,
- },
.nft = nft,
.msgs = msgs,
.cmds = cmds,
@@ -3779,6 +4359,10 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename,
json_error_t err;
int ret;
+ json_indesc.type = INDESC_FILE;
+ json_indesc.name = filename;
+
+ parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->json_root = json_load_file(filename, 0, &err);
if (!nft->json_root)
return -EINVAL;
@@ -3857,8 +4441,11 @@ int json_events_cb(const struct nlmsghdr *nlh, struct netlink_mon_handler *monh)
return MNL_CB_OK;
json = seqnum_to_json(nlh->nlmsg_seq);
- if (!json)
+ if (!json) {
+ json_echo_error(monh, "No JSON command found with seqnum %lu\n",
+ nlh->nlmsg_seq);
return MNL_CB_OK;
+ }
tmp = json_object_get(json, "add");
if (!tmp)
@@ -3884,11 +4471,20 @@ int json_events_cb(const struct nlmsghdr *nlh, struct netlink_mon_handler *monh)
void json_print_echo(struct nft_ctx *ctx)
{
- if (!ctx->json_root)
- return;
-
- json_dumpf(ctx->json_root, ctx->output.output_fp, JSON_PRESERVE_ORDER);
- json_cmd_assoc_free();
- json_decref(ctx->json_root);
- ctx->json_root = NULL;
+ if (!ctx->json_root) {
+ if (!ctx->json_echo)
+ return;
+
+ ctx->json_echo = json_pack("{s:o}", "nftables", ctx->json_echo);
+ json_dumpf(ctx->json_echo, ctx->output.output_fp, JSON_PRESERVE_ORDER);
+ json_decref(ctx->json_echo);
+ ctx->json_echo = NULL;
+ fprintf(ctx->output.output_fp, "\n");
+ fflush(ctx->output.output_fp);
+ } else {
+ json_dumpf(ctx->json_root, ctx->output.output_fp, JSON_PRESERVE_ORDER);
+ json_cmd_assoc_free();
+ json_decref(ctx->json_root);
+ ctx->json_root = NULL;
+ }
}
diff --git a/src/payload.c b/src/payload.c
index 29242537..44aa834c 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -10,15 +10,16 @@
* 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>
#include <linux/if_ether.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
#include <rule.h>
#include <expression.h>
@@ -45,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))
@@ -65,6 +70,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;
@@ -80,9 +86,10 @@ static void payload_expr_clone(struct expr *new, const struct expr *expr)
* Update protocol context for relational payload expressions.
*/
static void payload_expr_pctx_update(struct proto_ctx *ctx,
- const struct expr *expr)
+ const struct location *loc,
+ const struct expr *left,
+ const struct expr *right)
{
- const struct expr *left = expr->left, *right = expr->right;
const struct proto_desc *base, *desc;
unsigned int proto = 0;
@@ -94,24 +101,49 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
base = ctx->protocol[left->payload.base].desc;
desc = proto_find_upper(base, proto);
- if (!desc)
+ if (!desc) {
+ if (base == &proto_icmp) {
+ /* proto 0 is ECHOREPLY, just pretend its ECHO.
+ * Not doing this would need an additional marker
+ * bit to tell when icmp.type was set.
+ */
+ ctx->th_dep.icmp.type = proto ? proto : ICMP_ECHO;
+ } else if (base == &proto_icmp6) {
+ if (proto == ICMP6_ECHO_REPLY)
+ proto = ICMP6_ECHO_REQUEST;
+ ctx->th_dep.icmp.type = proto;
+ }
return;
+ }
assert(desc->base <= PROTO_BASE_MAX);
if (desc->base == base->base) {
- assert(base->length > 0);
- ctx->protocol[base->base].offset += base->length;
+ 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++;
+ }
+ }
}
- proto_ctx_update(ctx, desc->base, &expr->location, desc);
+ proto_ctx_update(ctx, desc->base, loc, desc);
}
#define NFTNL_UDATA_SET_KEY_PAYLOAD_DESC 0
#define NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE 1
-#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 2
+#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_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)
{
+ if (desc->id == PROTO_DESC_UNKNOWN)
+ return 0;
+
return (unsigned int)(tmpl - &desc->templates[0]);
}
@@ -125,10 +157,24 @@ static int payload_expr_build_udata(struct nftnl_udata_buf *udbuf,
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_DESC, desc->id);
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE, type);
+ if (desc->id == 0) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_BASE,
+ expr->payload.base);
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET,
+ expr->payload.offset);
+ }
+ 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));
}
@@ -142,6 +188,10 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_SET_KEY_PAYLOAD_DESC:
case NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE:
+ 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;
@@ -156,8 +206,10 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_PAYLOAD_MAX + 1] = {};
+ unsigned int type, base, offset, len = 0;
const struct proto_desc *desc;
- unsigned int type;
+ bool is_raw = false;
+ struct expr *expr;
int err;
err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr),
@@ -170,12 +222,44 @@ static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr)
return NULL;
desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_DESC]);
- if (!desc)
- return NULL;
+ if (!desc) {
+ if (!ud[NFTNL_UDATA_SET_KEY_PAYLOAD_BASE] ||
+ !ud[NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET])
+ return NULL;
+
+ base = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_BASE]);
+ offset = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET]);
+ is_raw = true;
+ }
type = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE]);
+ if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_LEN])
+ len = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_LEN]);
+
+ expr = payload_expr_alloc(&internal_location, desc, type);
+
+ if (len)
+ expr->len = len;
+
+ if (is_raw) {
+ struct datatype *dtype;
- return payload_expr_alloc(&internal_location, desc, type);
+ expr->payload.base = base;
+ expr->payload.offset = offset;
+ expr->payload.is_raw = true;
+ expr->len = len;
+ dtype = datatype_clone(&xinteger_type);
+ dtype->size = len;
+ dtype->byteorder = BYTEORDER_BIG_ENDIAN;
+ __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;
}
const struct expr_ops payload_expr_ops = {
@@ -252,7 +336,7 @@ void payload_init_raw(struct expr *expr, enum proto_bases base,
expr->payload.base = base;
expr->payload.offset = offset;
expr->len = len;
- expr->dtype = &integer_type;
+ expr->dtype = &xinteger_type;
if (base != PROTO_BASE_TRANSPORT_HDR)
return;
@@ -321,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",
@@ -340,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:
@@ -370,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 ||
@@ -380,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)
@@ -432,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,
@@ -449,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);
@@ -468,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.
*
@@ -493,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:
@@ -543,6 +652,41 @@ void payload_dependency_reset(struct payload_dep_ctx *ctx)
memset(ctx, 0, sizeof(*ctx));
}
+static bool payload_dependency_store_icmp_type(struct payload_dep_ctx *ctx,
+ const struct stmt *stmt)
+{
+ struct expr *dep = stmt->expr;
+ const struct proto_desc *desc;
+ const struct expr *right;
+ uint8_t type;
+
+ if (dep->left->etype != EXPR_PAYLOAD)
+ return false;
+
+ right = dep->right;
+ if (right->etype != EXPR_VALUE || right->len != BITS_PER_BYTE)
+ return false;
+
+ desc = dep->left->payload.desc;
+ if (desc == &proto_icmp) {
+ type = mpz_get_uint8(right->value);
+
+ if (type == ICMP_ECHOREPLY)
+ type = ICMP_ECHO;
+
+ ctx->icmp_type = type;
+
+ return type == ICMP_ECHO;
+ } else if (desc == &proto_icmp6) {
+ type = mpz_get_uint8(right->value);
+
+ ctx->icmp_type = type;
+ return type == ICMP6_ECHO_REQUEST || type == ICMP6_ECHO_REPLY;
+ }
+
+ return false;
+}
+
/**
* payload_dependency_store - store a possibly redundant protocol match
*
@@ -553,8 +697,12 @@ void payload_dependency_reset(struct payload_dep_ctx *ctx)
void payload_dependency_store(struct payload_dep_ctx *ctx,
struct stmt *stmt, enum proto_bases base)
{
- ctx->pbase = base + 1;
- ctx->pdep = stmt;
+ bool ignore_dep = payload_dependency_store_icmp_type(ctx, stmt);
+
+ if (ignore_dep)
+ return;
+
+ ctx->pdeps[base + 1] = stmt;
}
/**
@@ -569,26 +717,145 @@ void payload_dependency_store(struct payload_dep_ctx *ctx,
bool payload_dependency_exists(const struct payload_dep_ctx *ctx,
enum proto_bases base)
{
- return ctx->pbase != PROTO_BASE_INVALID &&
- ctx->pbase == base &&
- ctx->pdep != NULL;
+ if (ctx->pdeps[base])
+ return true;
+
+ return base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR];
}
-void payload_dependency_release(struct payload_dep_ctx *ctx)
+/**
+ * payload_dependency_get - return a payload dependency if available
+ * @ctx: payload dependency context
+ * @base: payload protocol base
+ *
+ * If we have seen a protocol key payload expression for this base, we return
+ * it.
+ */
+struct expr *payload_dependency_get(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
{
- list_del(&ctx->pdep->list);
- stmt_free(ctx->pdep);
+ if (ctx->pdeps[base])
+ return ctx->pdeps[base]->expr;
+
+ if (base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR])
+ return ctx->pdeps[PROTO_BASE_INNER_HDR]->expr;
- ctx->pbase = PROTO_BASE_INVALID;
- if (ctx->pdep == ctx->prev)
+ return NULL;
+}
+
+static void __payload_dependency_release(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
+{
+ list_del(&ctx->pdeps[base]->list);
+ stmt_free(ctx->pdeps[base]);
+
+ if (ctx->pdeps[base] == ctx->prev)
ctx->prev = NULL;
- ctx->pdep = NULL;
+ ctx->pdeps[base] = NULL;
+}
+
+void payload_dependency_release(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
+{
+ if (ctx->pdeps[base])
+ __payload_dependency_release(ctx, base);
+ else if (base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR])
+ __payload_dependency_release(ctx, PROTO_BASE_INNER_HDR);
+}
+
+static uint8_t icmp_dep_to_type(enum icmp_hdr_field_type t)
+{
+ switch (t) {
+ case PROTO_ICMP_ANY:
+ BUG("Invalid map for simple dependency");
+ case PROTO_ICMP_ECHO: return ICMP_ECHO;
+ case PROTO_ICMP6_ECHO: return ICMP6_ECHO_REQUEST;
+ case PROTO_ICMP_MTU: return ICMP_DEST_UNREACH;
+ case PROTO_ICMP_ADDRESS: return ICMP_REDIRECT;
+ 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 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, struct expr *expr)
+{
+ const struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
+ enum icmp_hdr_field_type icmp_dep;
+
+ 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;
+
+ 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_dep_to_type(icmp_dep);
+}
+
+static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct expr *expr)
+{
+ const struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
+
+ /* Never remove a 'vlan type 0x...' expression, they are never added
+ * implicitly
+ */
+ if (dep->left->payload.desc == &proto_vlan)
+ return false;
+
+ /* 'vlan id 2' implies 'ether type 8021Q'. If a different protocol is
+ * tested, this is not a redundant expression.
+ */
+ if (dep->left->payload.desc == &proto_eth &&
+ dep->right->etype == EXPR_VALUE && dep->right->len == 16)
+ return mpz_get_uint16(dep->right->value) == ETH_P_8021Q;
+
+ return true;
}
static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
unsigned int family, struct expr *expr)
{
- struct expr *dep = ctx->pdep->expr;
+ struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
/* Protocol key payload expression at network base such as 'ip6 nexthdr'
* need to be left in place since it implicitly restricts matching to
@@ -613,12 +880,29 @@ static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
* for stacked protocols if we only have protcol type matches.
*/
if (dep->left->etype == EXPR_PAYLOAD && dep->op == OP_EQ &&
- expr->flags & EXPR_F_PROTOCOL &&
- expr->payload.base == dep->left->payload.base)
- return false;
+ expr->payload.base == dep->left->payload.base) {
+ if (expr->flags & EXPR_F_PROTOCOL)
+ return false;
+
+ if (expr->payload.base == PROTO_BASE_LL_HDR)
+ return payload_may_dependency_kill_ll(ctx, expr);
+ }
+
break;
}
+ if (expr->payload.base != PROTO_BASE_TRANSPORT_HDR)
+ return true;
+
+ if (dep->left->payload.base != PROTO_BASE_TRANSPORT_HDR)
+ return true;
+
+ if (dep->left->payload.desc == &proto_icmp)
+ return payload_may_dependency_kill_icmp(ctx, expr);
+
+ if (dep->left->payload.desc == &proto_icmp6)
+ return payload_may_dependency_kill_icmp(ctx, expr);
+
return true;
}
@@ -635,9 +919,10 @@ 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);
+ payload_dependency_release(ctx, expr->payload.base);
}
void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
@@ -646,21 +931,53 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT:
if (payload_dependency_exists(ctx, PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_TRANSPORT_HDR);
break;
case NFT_EXTHDR_OP_IPV6:
if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_NETWORK_HDR);
break;
case NFT_EXTHDR_OP_IPV4:
if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_NETWORK_HDR);
break;
default:
break;
}
}
+static const struct proto_desc *get_stacked_desc(const struct proto_ctx *ctx,
+ const struct proto_desc *top,
+ const struct expr *e,
+ unsigned int *skip)
+{
+ unsigned int i, total, payload_offset = e->payload.offset;
+
+ assert(e->etype == EXPR_PAYLOAD);
+
+ if (e->payload.base != PROTO_BASE_LL_HDR ||
+ payload_offset < top->length) {
+ *skip = 0;
+ return top;
+ }
+
+ for (i = 0, total = 0; i < ctx->stacked_ll_count; i++) {
+ const struct proto_desc *stacked;
+
+ stacked = ctx->stacked_ll[i];
+ if (payload_offset < stacked->length) {
+ *skip = total;
+ return stacked;
+ }
+
+ payload_offset -= stacked->length;
+ total += stacked->length;
+ }
+
+ *skip = total;
+ return top;
+}
+
/**
* payload_expr_complete - fill in type information of a raw payload expr
*
@@ -672,9 +989,10 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
*/
void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
{
+ unsigned int payload_offset = expr->payload.offset;
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
- unsigned int i;
+ unsigned int i, total;
assert(expr->etype == EXPR_PAYLOAD);
@@ -683,13 +1001,26 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
return;
assert(desc->base == expr->payload.base);
+ desc = get_stacked_desc(ctx, desc, expr, &total);
+ payload_offset -= total;
+
for (i = 0; i < array_size(desc->templates); i++) {
tmpl = &desc->templates[i];
- if (tmpl->offset != expr->payload.offset ||
+ if (tmpl->offset != payload_offset ||
tmpl->len != expr->len)
continue;
+
+ if (tmpl->meta_key && i == 0)
+ continue;
+
+ if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
+ !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;
}
@@ -734,6 +1065,7 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
unsigned int payload_len = expr->len;
const struct proto_desc *desc;
unsigned int off, i, len = 0;
+ unsigned int total;
assert(expr->etype == EXPR_PAYLOAD);
@@ -743,10 +1075,8 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
assert(desc->base == expr->payload.base);
- if (ctx->protocol[expr->payload.base].offset) {
- assert(payload_offset >= ctx->protocol[expr->payload.base].offset);
- payload_offset -= ctx->protocol[expr->payload.base].offset;
- }
+ desc = get_stacked_desc(ctx, desc, expr, &total);
+ payload_offset -= total;
off = round_up(mask->len, BITS_PER_BYTE) - mask_len;
payload_offset += off;
@@ -793,25 +1123,35 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
void payload_expr_expand(struct list_head *list, struct expr *expr,
const struct proto_ctx *ctx)
{
+ unsigned int payload_offset = expr->payload.offset;
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
+ unsigned int i, total;
struct expr *new;
- unsigned int i;
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);
+ payload_offset -= total;
+
for (i = 1; i < array_size(desc->templates); i++) {
tmpl = &desc->templates[i];
if (tmpl->len == 0)
break;
- if (tmpl->offset != expr->payload.offset)
+ if (tmpl->offset != payload_offset)
+ continue;
+
+ if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
+ !icmp_dep_type_match(tmpl->icmp_dep,
+ ctx->th_dep.icmp.type))
continue;
if (tmpl->len <= expr->len) {
@@ -819,15 +1159,25 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
list_add_tail(&new->list, list);
expr->len -= tmpl->len;
expr->payload.offset += tmpl->len;
+ payload_offset += tmpl->len;
if (expr->len == 0)
return;
+ } else if (expr->len > 0) {
+ new = payload_expr_alloc(&expr->location, desc, i);
+ new->len = expr->len;
+ list_add_tail(&new->list, list);
+ return;
} else
break;
}
raw:
new = payload_expr_alloc(&expr->location, NULL, 0);
- payload_init_raw(new, expr->payload.base, expr->payload.offset,
+ 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);
}
@@ -849,6 +1199,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;
@@ -905,5 +1258,221 @@ 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;
}
+
+static struct stmt *
+__payload_gen_icmp_simple_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ const struct datatype *icmp_type,
+ const struct proto_desc *desc,
+ uint8_t type)
+{
+ struct expr *left, *right, *dep;
+
+ left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
+ right = constant_expr_alloc(&expr->location, icmp_type,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE,
+ constant_data_ptr(type, BITS_PER_BYTE));
+
+ dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+ return expr_stmt_alloc(&dep->location, dep);
+}
+
+static struct stmt *
+__payload_gen_icmp_echo_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ uint8_t echo, uint8_t reply,
+ const struct datatype *icmp_type,
+ const struct proto_desc *desc)
+{
+ struct expr *left, *right, *dep, *set;
+
+ left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
+
+ set = set_expr_alloc(&expr->location, NULL);
+
+ right = constant_expr_alloc(&expr->location, icmp_type,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE,
+ constant_data_ptr(echo, BITS_PER_BYTE));
+ right = set_elem_expr_alloc(&expr->location, right);
+ compound_expr_add(set, right);
+
+ right = constant_expr_alloc(&expr->location, icmp_type,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE,
+ constant_data_ptr(reply, 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);
+}
+
+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;
+ uint8_t type;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ tmpl = expr->payload.tmpl;
+ desc = expr->payload.desc;
+
+ switch (tmpl->icmp_dep) {
+ case PROTO_ICMP_ANY:
+ BUG("No dependency needed");
+ break;
+ case PROTO_ICMP_ECHO:
+ /* do not test ICMP_ECHOREPLY here: its 0 */
+ if (pctx->th_dep.icmp.type == ICMP_ECHO)
+ goto done;
+
+ type = ICMP_ECHO;
+ if (pctx->th_dep.icmp.type)
+ goto bad_proto;
+
+ stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
+ ICMP_ECHO, ICMP_ECHOREPLY,
+ &icmp_type_type,
+ desc);
+ break;
+ case PROTO_ICMP_MTU:
+ case PROTO_ICMP_ADDRESS:
+ type = icmp_dep_to_type(tmpl->icmp_dep);
+ if (pctx->th_dep.icmp.type == type)
+ goto done;
+ 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 (pctx->th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
+ pctx->th_dep.icmp.type == ICMP6_ECHO_REPLY)
+ goto done;
+
+ type = ICMP6_ECHO_REQUEST;
+ if (pctx->th_dep.icmp.type)
+ goto bad_proto;
+
+ stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
+ ICMP6_ECHO_REQUEST,
+ ICMP6_ECHO_REPLY,
+ &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 (pctx->th_dep.icmp.type == type)
+ goto done;
+ if (pctx->th_dep.icmp.type)
+ goto bad_proto;
+ stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
+ &icmp6_type_type,
+ desc, type);
+ break;
+ break;
+ default:
+ BUG("Unhandled icmp dependency code");
+ }
+
+ pctx->th_dep.icmp.type = type;
+
+ 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",
+ 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/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 7d001501..553b6a44 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>
@@ -28,6 +27,7 @@ const char *proto_base_names[] = {
[PROTO_BASE_LL_HDR] = "link layer",
[PROTO_BASE_NETWORK_HDR] = "network layer",
[PROTO_BASE_TRANSPORT_HDR] = "transport layer",
+ [PROTO_BASE_INNER_HDR] = "payload data",
};
const char *proto_base_tokens[] = {
@@ -35,6 +35,7 @@ const char *proto_base_tokens[] = {
[PROTO_BASE_LL_HDR] = "ll",
[PROTO_BASE_NETWORK_HDR] = "nh",
[PROTO_BASE_TRANSPORT_HDR] = "th",
+ [PROTO_BASE_INNER_HDR] = "ih",
};
const struct proto_hdr_template proto_unknown_template =
@@ -58,6 +59,8 @@ proto_find_upper(const struct proto_desc *base, unsigned int num)
unsigned int i;
for (i = 0; i < array_size(base->protocols); i++) {
+ if (!base->protocols[i].desc)
+ break;
if (base->protocols[i].num == num)
return base->protocols[i].desc;
}
@@ -76,12 +79,38 @@ int proto_find_num(const struct proto_desc *base,
unsigned int i;
for (i = 0; i < array_size(base->protocols); i++) {
+ if (!base->protocols[i].desc)
+ break;
if (base->protocols[i].desc == desc)
return base->protocols[i].num;
}
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),
};
@@ -104,6 +133,8 @@ int proto_dev_type(const struct proto_desc *desc, uint16_t *res)
return 0;
}
for (j = 0; j < array_size(base->protocols); j++) {
+ if (!base->protocols[j].desc)
+ break;
if (base->protocols[j].desc == desc) {
*res = dev_proto_desc[i].type;
return 0;
@@ -146,14 +177,20 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
if (!(debug_mask & NFT_DEBUG_PROTO_CTX))
return;
- pr_debug("update %s protocol context:\n", proto_base_names[base]);
+ if (base == PROTO_BASE_LL_HDR && ctx->stacked_ll_count) {
+ pr_debug(" saved ll headers:");
+ for (i = 0; i < ctx->stacked_ll_count; i++)
+ pr_debug(" %s", ctx->stacked_ll[i]->name);
+ }
+
+ 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],
ctx->protocol[i].desc ? ctx->protocol[i].desc->name :
"none");
- if (ctx->protocol[i].offset)
- pr_debug(" (offset: %u)", ctx->protocol[i].offset);
if (i == base)
pr_debug(" <-");
pr_debug("\n");
@@ -169,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];
@@ -177,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);
}
@@ -193,12 +231,71 @@ void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
const struct location *loc,
const struct proto_desc *desc)
{
+ bool found = false;
+ unsigned int i;
+
+ switch (base) {
+ case PROTO_BASE_LL_HDR:
+ case PROTO_BASE_NETWORK_HDR:
+ break;
+ case PROTO_BASE_TRANSPORT_HDR:
+ if (ctx->protocol[base].num_protos >= PROTO_CTX_NUM_PROTOS)
+ break;
+
+ for (i = 0; i < ctx->protocol[base].num_protos; i++) {
+ if (ctx->protocol[base].protos[i].desc == desc) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ i = ctx->protocol[base].num_protos++;
+ ctx->protocol[base].protos[i].desc = desc;
+ ctx->protocol[base].protos[i].location = *loc;
+ }
+ break;
+ case PROTO_BASE_INNER_HDR:
+ break;
+ default:
+ BUG("unknown protocol base %d", base);
+ }
+
ctx->protocol[base].location = *loc;
ctx->protocol[base].desc = desc;
proto_ctx_debug(ctx, base, ctx->debug_mask);
}
+bool proto_ctx_is_ambiguous(struct proto_ctx *ctx, enum proto_bases base)
+{
+ return ctx->protocol[base].num_protos > 1;
+}
+
+const struct proto_desc *proto_ctx_find_conflict(struct proto_ctx *ctx,
+ enum proto_bases base,
+ const struct proto_desc *desc)
+{
+ unsigned int i;
+
+ switch (base) {
+ case PROTO_BASE_LL_HDR:
+ case PROTO_BASE_NETWORK_HDR:
+ if (desc != ctx->protocol[base].desc)
+ return ctx->protocol[base].desc;
+ break;
+ case PROTO_BASE_TRANSPORT_HDR:
+ for (i = 0; i < ctx->protocol[base].num_protos; i++) {
+ if (desc != ctx->protocol[base].protos[i].desc)
+ return ctx->protocol[base].protos[i].desc;
+ }
+ break;
+ default:
+ BUG("unknown protocol base %d", base);
+ }
+
+ return NULL;
+}
+
#define HDR_TEMPLATE(__name, __dtype, __type, __member) \
PROTO_HDR_TEMPLATE(__name, __dtype, \
BYTEORDER_BIG_ENDIAN, \
@@ -207,6 +304,8 @@ void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
#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)
@@ -339,24 +438,37 @@ const struct datatype icmp_type_type = {
.sym_tbl = &icmp_type_tbl,
};
-#define ICMPHDR_FIELD(__name, __member) \
- HDR_FIELD(__name, struct icmphdr, __member)
+#define ICMP46HDR_FIELD(__token, __dtype, __struct, __member, __dep) \
+ { \
+ .token = (__token), \
+ .dtype = &__dtype, \
+ .byteorder = BYTEORDER_BIG_ENDIAN, \
+ .offset = offsetof(__struct, __member) * 8, \
+ .len = field_sizeof(__struct, __member) * 8, \
+ .icmp_dep = (__dep), \
+ }
+
+#define ICMPHDR_FIELD(__token, __member, __dep) \
+ ICMP46HDR_FIELD(__token, integer_type, struct icmphdr, __member, __dep)
+
#define ICMPHDR_TYPE(__name, __type, __member) \
- HDR_TYPE(__name, __type, struct icmphdr, __member)
+ HDR_TYPE(__name, __type, struct icmphdr, __member)
const struct proto_desc proto_icmp = {
.name = "icmp",
.id = PROTO_DESC_ICMP,
.base = PROTO_BASE_TRANSPORT_HDR,
+ .protocol_key = ICMPHDR_TYPE,
.checksum_key = ICMPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[ICMPHDR_TYPE] = ICMPHDR_TYPE("type", &icmp_type_type, type),
[ICMPHDR_CODE] = ICMPHDR_TYPE("code", &icmp_code_type, code),
- [ICMPHDR_CHECKSUM] = ICMPHDR_FIELD("checksum", checksum),
- [ICMPHDR_ID] = ICMPHDR_FIELD("id", un.echo.id),
- [ICMPHDR_SEQ] = ICMPHDR_FIELD("sequence", un.echo.sequence),
- [ICMPHDR_GATEWAY] = ICMPHDR_FIELD("gateway", un.gateway),
- [ICMPHDR_MTU] = ICMPHDR_FIELD("mtu", un.frag.mtu),
+ [ICMPHDR_CHECKSUM] = ICMPHDR_FIELD("checksum", checksum, PROTO_ICMP_ANY),
+ [ICMPHDR_ID] = ICMPHDR_FIELD("id", un.echo.id, PROTO_ICMP_ECHO),
+ [ICMPHDR_SEQ] = ICMPHDR_FIELD("sequence", un.echo.sequence, PROTO_ICMP_ECHO),
+ [ICMPHDR_GATEWAY] = ICMPHDR_FIELD("gateway", un.gateway, PROTO_ICMP_ADDRESS),
+ [ICMPHDR_MTU] = ICMPHDR_FIELD("mtu", un.frag.mtu, PROTO_ICMP_MTU),
},
};
@@ -402,6 +514,7 @@ const struct proto_desc proto_igmp = {
.id = PROTO_DESC_IGMP,
.base = PROTO_BASE_TRANSPORT_HDR,
.checksum_key = IGMPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[IGMPHDR_TYPE] = IGMPHDR_TYPE("type", &igmp_type_type, igmp_type),
[IGMPHDR_MRT] = IGMPHDR_FIELD("mrt", igmp_code),
@@ -423,12 +536,17 @@ const struct proto_desc proto_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 = {
@@ -482,6 +600,7 @@ const struct proto_desc proto_tcp = {
.id = PROTO_DESC_TCP,
.base = PROTO_BASE_TRANSPORT_HDR,
.checksum_key = TCPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[TCPHDR_SPORT] = INET_SERVICE("sport", struct tcphdr, source),
[TCPHDR_DPORT] = INET_SERVICE("dport", struct tcphdr, dest),
@@ -563,6 +682,8 @@ const struct proto_desc proto_sctp = {
.name = "sctp",
.id = PROTO_DESC_SCTP,
.base = PROTO_BASE_TRANSPORT_HDR,
+ .checksum_key = SCTPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_SCTP,
.templates = {
[SCTPHDR_SPORT] = INET_SERVICE("sport", struct sctphdr, source),
[SCTPHDR_DPORT] = INET_SERVICE("dport", struct sctphdr, dest),
@@ -601,7 +722,9 @@ static const struct symbol_table dscp_type_tbl = {
SYMBOL("cs5", 0x28),
SYMBOL("cs6", 0x30),
SYMBOL("cs7", 0x38),
+ SYMBOL("df", 0x00),
SYMBOL("be", 0x00),
+ SYMBOL("lephb", 0x01),
SYMBOL("af11", 0x0a),
SYMBOL("af12", 0x0c),
SYMBOL("af13", 0x0e),
@@ -614,6 +737,7 @@ static const struct symbol_table dscp_type_tbl = {
SYMBOL("af41", 0x22),
SYMBOL("af42", 0x24),
SYMBOL("af43", 0x26),
+ SYMBOL("va", 0x2c),
SYMBOL("ef", 0x2e),
SYMBOL_LIST_END
},
@@ -652,6 +776,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) \
@@ -662,6 +822,7 @@ const struct proto_desc proto_ip = {
.id = PROTO_DESC_IP,
.base = PROTO_BASE_NETWORK_HDR,
.checksum_key = IPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.protocols = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
@@ -674,6 +835,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),
@@ -683,7 +846,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),
@@ -749,8 +912,8 @@ const struct datatype icmp6_type_type = {
.sym_tbl = &icmp6_type_tbl,
};
-#define ICMP6HDR_FIELD(__name, __member) \
- HDR_FIELD(__name, struct icmp6_hdr, __member)
+#define ICMP6HDR_FIELD(__token, __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)
@@ -758,16 +921,24 @@ const struct proto_desc proto_icmp6 = {
.name = "icmpv6",
.id = PROTO_DESC_ICMPV6,
.base = PROTO_BASE_TRANSPORT_HDR,
+ .protocol_key = ICMP6HDR_TYPE,
.checksum_key = ICMP6HDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[ICMP6HDR_TYPE] = ICMP6HDR_TYPE("type", &icmp6_type_type, icmp6_type),
[ICMP6HDR_CODE] = ICMP6HDR_TYPE("code", &icmpv6_code_type, icmp6_code),
- [ICMP6HDR_CHECKSUM] = ICMP6HDR_FIELD("checksum", icmp6_cksum),
- [ICMP6HDR_PPTR] = ICMP6HDR_FIELD("parameter-problem", icmp6_pptr),
- [ICMP6HDR_MTU] = ICMP6HDR_FIELD("mtu", icmp6_mtu),
- [ICMP6HDR_ID] = ICMP6HDR_FIELD("id", icmp6_id),
- [ICMP6HDR_SEQ] = ICMP6HDR_FIELD("sequence", icmp6_seq),
- [ICMP6HDR_MAXDELAY] = ICMP6HDR_FIELD("max-delay", icmp6_maxdelay),
+ [ICMP6HDR_CHECKSUM] = ICMP6HDR_FIELD("checksum", icmp6_cksum, PROTO_ICMP_ANY),
+ [ICMP6HDR_PPTR] = ICMP6HDR_FIELD("parameter-problem", icmp6_pptr, PROTO_ICMP6_PPTR),
+ [ICMP6HDR_MTU] = ICMP6HDR_FIELD("mtu", icmp6_mtu, PROTO_ICMP6_MTU),
+ [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),
},
};
@@ -798,6 +969,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),
@@ -863,6 +1036,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),
@@ -915,8 +1090,8 @@ const struct proto_desc proto_arp = {
[ARPHDR_PLN] = ARPHDR_FIELD("plen", plen),
[ARPHDR_OP] = ARPHDR_TYPE("operation", &arpop_type, oper),
[ARPHDR_SADDR_ETHER] = ARPHDR_TYPE("saddr ether", &etheraddr_type, sha),
- [ARPHDR_DADDR_ETHER] = ARPHDR_TYPE("daddr ether", &etheraddr_type, tha),
[ARPHDR_SADDR_IP] = ARPHDR_TYPE("saddr ip", &ipaddr_type, spa),
+ [ARPHDR_DADDR_ETHER] = ARPHDR_TYPE("daddr ether", &etheraddr_type, tha),
[ARPHDR_DADDR_IP] = ARPHDR_TYPE("daddr ip", &ipaddr_type, tpa),
},
.format = {
@@ -949,10 +1124,12 @@ const struct proto_desc proto_vlan = {
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),
+ PROTO_LINK(__constant_htons(ETH_P_8021AD), &proto_vlan),
},
.templates = {
[VLANHDR_PCP] = VLANHDR_BITFIELD("pcp", 0, 3),
+ [VLANHDR_DEI] = VLANHDR_BITFIELD("dei", 3, 1),
[VLANHDR_CFI] = VLANHDR_BITFIELD("cfi", 3, 1),
[VLANHDR_VID] = VLANHDR_BITFIELD("id", 4, 12),
[VLANHDR_TYPE] = VLANHDR_TYPE("type", &ethertype_type, vlan_type),
@@ -978,6 +1155,10 @@ static const struct symbol_table ethertype_tbl = {
SYMBOL("ip", __constant_htons(ETH_P_IP)),
SYMBOL("arp", __constant_htons(ETH_P_ARP)),
SYMBOL("ip6", __constant_htons(ETH_P_IPV6)),
+ SYMBOL("8021q", __constant_htons(ETH_P_8021Q)),
+ SYMBOL("8021ad", __constant_htons(ETH_P_8021AD)),
+
+ /* for compatibility with older versions */
SYMBOL("vlan", __constant_htons(ETH_P_8021Q)),
SYMBOL_LIST_END
},
@@ -1021,6 +1202,7 @@ const struct proto_desc proto_eth = {
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),
+ PROTO_LINK(__constant_htons(ETH_P_8021AD), &proto_vlan),
},
.templates = {
[ETHHDR_DADDR] = ETHHDR_ADDR("daddr", ether_dhost),
@@ -1036,6 +1218,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 = {
@@ -1046,13 +1279,14 @@ const struct proto_desc proto_netdev = {
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),
+ PROTO_LINK(__constant_htons(ETH_P_8021AD), &proto_vlan),
},
.templates = {
[0] = PROTO_META_TEMPLATE("protocol", &ethertype_type, NFT_META_PROTOCOL, 16),
},
};
-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,
@@ -1070,6 +1304,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/rbtree.c b/src/rbtree.c
deleted file mode 100644
index 325c0123..00000000
--- a/src/rbtree.c
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Red Black Trees
- * (C) 1999 Andrea Arcangeli <andrea@suse.de>
- * (C) 2002 David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <rbtree.h>
-
-static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *right = node->rb_right;
- struct rb_node *parent = rb_parent(node);
-
- if ((node->rb_right = right->rb_left))
- rb_set_parent(right->rb_left, node);
- right->rb_left = node;
-
- rb_set_parent(right, parent);
-
- if (parent)
- {
- if (node == parent->rb_left)
- parent->rb_left = right;
- else
- parent->rb_right = right;
- }
- else
- root->rb_node = right;
- rb_set_parent(node, right);
-}
-
-static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *left = node->rb_left;
- struct rb_node *parent = rb_parent(node);
-
- if ((node->rb_left = left->rb_right))
- rb_set_parent(left->rb_right, node);
- left->rb_right = node;
-
- rb_set_parent(left, parent);
-
- if (parent)
- {
- if (node == parent->rb_right)
- parent->rb_right = left;
- else
- parent->rb_left = left;
- }
- else
- root->rb_node = left;
- rb_set_parent(node, left);
-}
-
-void rb_insert_color(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *parent, *gparent;
-
- while ((parent = rb_parent(node)) && rb_is_red(parent))
- {
- gparent = rb_parent(parent);
-
- if (parent == gparent->rb_left)
- {
- {
- register struct rb_node *uncle = gparent->rb_right;
- if (uncle && rb_is_red(uncle))
- {
- rb_set_black(uncle);
- rb_set_black(parent);
- rb_set_red(gparent);
- node = gparent;
- continue;
- }
- }
-
- if (parent->rb_right == node)
- {
- register struct rb_node *tmp;
- __rb_rotate_left(parent, root);
- tmp = parent;
- parent = node;
- node = tmp;
- }
-
- rb_set_black(parent);
- rb_set_red(gparent);
- __rb_rotate_right(gparent, root);
- } else {
- {
- register struct rb_node *uncle = gparent->rb_left;
- if (uncle && rb_is_red(uncle))
- {
- rb_set_black(uncle);
- rb_set_black(parent);
- rb_set_red(gparent);
- node = gparent;
- continue;
- }
- }
-
- if (parent->rb_left == node)
- {
- register struct rb_node *tmp;
- __rb_rotate_right(parent, root);
- tmp = parent;
- parent = node;
- node = tmp;
- }
-
- rb_set_black(parent);
- rb_set_red(gparent);
- __rb_rotate_left(gparent, root);
- }
- }
-
- rb_set_black(root->rb_node);
-}
-
-static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
- struct rb_root *root)
-{
- struct rb_node *other;
-
- while ((!node || rb_is_black(node)) && node != root->rb_node)
- {
- if (parent->rb_left == node)
- {
- other = parent->rb_right;
- if (rb_is_red(other))
- {
- rb_set_black(other);
- rb_set_red(parent);
- __rb_rotate_left(parent, root);
- other = parent->rb_right;
- }
- if ((!other->rb_left || rb_is_black(other->rb_left)) &&
- (!other->rb_right || rb_is_black(other->rb_right)))
- {
- rb_set_red(other);
- node = parent;
- parent = rb_parent(node);
- }
- else
- {
- if (!other->rb_right || rb_is_black(other->rb_right))
- {
- struct rb_node *o_left;
- if ((o_left = other->rb_left))
- rb_set_black(o_left);
- rb_set_red(other);
- __rb_rotate_right(other, root);
- other = parent->rb_right;
- }
- rb_set_color(other, rb_color(parent));
- rb_set_black(parent);
- if (other->rb_right)
- rb_set_black(other->rb_right);
- __rb_rotate_left(parent, root);
- node = root->rb_node;
- break;
- }
- }
- else
- {
- other = parent->rb_left;
- if (rb_is_red(other))
- {
- rb_set_black(other);
- rb_set_red(parent);
- __rb_rotate_right(parent, root);
- other = parent->rb_left;
- }
- if ((!other->rb_left || rb_is_black(other->rb_left)) &&
- (!other->rb_right || rb_is_black(other->rb_right)))
- {
- rb_set_red(other);
- node = parent;
- parent = rb_parent(node);
- }
- else
- {
- if (!other->rb_left || rb_is_black(other->rb_left))
- {
- register struct rb_node *o_right;
- if ((o_right = other->rb_right))
- rb_set_black(o_right);
- rb_set_red(other);
- __rb_rotate_left(other, root);
- other = parent->rb_left;
- }
- rb_set_color(other, rb_color(parent));
- rb_set_black(parent);
- if (other->rb_left)
- rb_set_black(other->rb_left);
- __rb_rotate_right(parent, root);
- node = root->rb_node;
- break;
- }
- }
- }
- if (node)
- rb_set_black(node);
-}
-
-void rb_erase(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *child, *parent;
- int color;
-
- if (!node->rb_left)
- child = node->rb_right;
- else if (!node->rb_right)
- child = node->rb_left;
- else
- {
- struct rb_node *old = node, *left;
-
- node = node->rb_right;
- while ((left = node->rb_left) != NULL)
- node = left;
- child = node->rb_right;
- parent = rb_parent(node);
- color = rb_color(node);
-
- if (child)
- rb_set_parent(child, parent);
- if (parent == old) {
- parent->rb_right = child;
- parent = node;
- } else
- parent->rb_left = child;
-
- node->rb_parent_color = old->rb_parent_color;
- node->rb_right = old->rb_right;
- node->rb_left = old->rb_left;
-
- if (rb_parent(old))
- {
- if (rb_parent(old)->rb_left == old)
- rb_parent(old)->rb_left = node;
- else
- rb_parent(old)->rb_right = node;
- } else
- root->rb_node = node;
-
- rb_set_parent(old->rb_left, node);
- if (old->rb_right)
- rb_set_parent(old->rb_right, node);
- goto color;
- }
-
- parent = rb_parent(node);
- color = rb_color(node);
-
- if (child)
- rb_set_parent(child, parent);
- if (parent)
- {
- if (parent->rb_left == node)
- parent->rb_left = child;
- else
- parent->rb_right = child;
- }
- else
- root->rb_node = child;
-
- color:
- if (color == RB_BLACK)
- __rb_erase_color(child, parent, root);
-}
-
-/*
- * This function returns the first node (in sort order) of the tree.
- */
-struct rb_node *rb_first(struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return NULL;
- while (n->rb_left)
- n = n->rb_left;
- return n;
-}
-
-struct rb_node *rb_last(struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return NULL;
- while (n->rb_right)
- n = n->rb_right;
- return n;
-}
-
-struct rb_node *rb_next(struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (rb_parent(node) == node)
- return NULL;
-
- /* If we have a right-hand child, go down and then left as far
- as we can. */
- if (node->rb_right) {
- node = node->rb_right;
- while (node->rb_left)
- node=node->rb_left;
- return node;
- }
-
- /* No right-hand children. Everything down and left is
- smaller than us, so any 'next' node must be in the general
- direction of our parent. Go up the tree; any time the
- ancestor is a right-hand child of its parent, keep going
- up. First time it's a left-hand child of its parent, said
- parent is our 'next' node. */
- while ((parent = rb_parent(node)) && node == parent->rb_right)
- node = parent;
-
- return parent;
-}
-
-struct rb_node *rb_prev(struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (rb_parent(node) == node)
- return NULL;
-
- /* If we have a left-hand child, go down and then right as far
- as we can. */
- if (node->rb_left) {
- node = node->rb_left;
- while (node->rb_right)
- node=node->rb_right;
- return node;
- }
-
- /* No left-hand children. Go up till we find an ancestor which
- is a right-hand child of its parent */
- while ((parent = rb_parent(node)) && node == parent->rb_left)
- node = parent;
-
- return parent;
-}
-
-void rb_replace_node(struct rb_node *victim, struct rb_node *new,
- struct rb_root *root)
-{
- struct rb_node *parent = rb_parent(victim);
-
- /* Set the surrounding nodes to point to the replacement */
- if (parent) {
- if (victim == parent->rb_left)
- parent->rb_left = new;
- else
- parent->rb_right = new;
- } else {
- root->rb_node = new;
- }
- if (victim->rb_left)
- rb_set_parent(victim->rb_left, new);
- if (victim->rb_right)
- rb_set_parent(victim->rb_right, new);
-
- /* Copy the pointers/colour from the victim to the replacement */
- *new = *victim;
-}
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 8dc1792b..65ff0fbb 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>
@@ -25,6 +24,9 @@
#include <misspell.h>
#include <json.h>
#include <cache.h>
+#include <owner.h>
+#include <intervals.h>
+#include "nftutils.h"
#include <libnftnl/common.h>
#include <libnftnl/ruleset.h>
@@ -71,10 +73,10 @@ static uint32_t tcp_dflt_timeout[] = {
static uint32_t udp_dflt_timeout[] = {
[NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30,
- [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180,
+ [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,
@@ -102,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)
@@ -137,189 +139,6 @@ void handle_merge(struct handle *dst, const struct handle *src)
dst->index = src->index;
}
-static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h,
- struct nft_cache *cache)
-{
- int ret;
-
- ret = netlink_list_tables(ctx, h);
- if (ret < 0)
- return -1;
-
- list_splice_tail_init(&ctx->list, &cache->list);
- return 0;
-}
-
-static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
-{
- struct rule *rule, *nrule;
- struct table *table;
- struct chain *chain;
- struct set *set;
- int ret;
-
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
- if (flags & NFT_CACHE_SET_BIT) {
- ret = netlink_list_sets(ctx, &table->handle);
- list_splice_tail_init(&ctx->list, &table->sets);
- if (ret < 0)
- return -1;
- }
- if (flags & NFT_CACHE_SETELEM_BIT) {
- list_for_each_entry(set, &table->sets, list) {
- ret = netlink_list_setelems(ctx, &set->handle,
- set);
- if (ret < 0)
- return -1;
- }
- }
- if (flags & NFT_CACHE_CHAIN_BIT) {
- ret = netlink_list_chains(ctx, &table->handle);
- if (ret < 0)
- return -1;
-
- list_splice_tail_init(&ctx->list, &table->chains);
- list_splice_tail_init(&ctx->list_bindings,
- &table->chain_bindings);
- }
- if (flags & NFT_CACHE_FLOWTABLE_BIT) {
- ret = netlink_list_flowtables(ctx, &table->handle);
- if (ret < 0)
- return -1;
- list_splice_tail_init(&ctx->list, &table->flowtables);
- }
- if (flags & NFT_CACHE_OBJECT_BIT) {
- ret = netlink_list_objs(ctx, &table->handle);
- if (ret < 0)
- return -1;
- list_splice_tail_init(&ctx->list, &table->objs);
- }
-
- if (flags & NFT_CACHE_RULE_BIT) {
- ret = netlink_list_rules(ctx, &table->handle);
- list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
- chain = chain_lookup(table, &rule->handle);
- if (!chain)
- chain = chain_binding_lookup(table,
- rule->handle.chain.name);
- list_move_tail(&rule->list, &chain->rules);
- }
- if (ret < 0)
- return -1;
- }
- }
- return 0;
-}
-
-static int cache_init(struct netlink_ctx *ctx, unsigned int flags)
-{
- struct handle handle = {
- .family = NFPROTO_UNSPEC,
- };
- int ret;
-
- if (flags == NFT_CACHE_EMPTY)
- return 0;
-
- /* assume NFT_CACHE_TABLE is always set. */
- ret = cache_init_tables(ctx, &handle, &ctx->nft->cache);
- if (ret < 0)
- return ret;
- ret = cache_init_objects(ctx, flags);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static bool cache_is_complete(struct nft_cache *cache, unsigned int flags)
-{
- return (cache->flags & flags) == flags;
-}
-
-static bool cache_needs_refresh(struct nft_cache *cache)
-{
- return cache->flags & NFT_CACHE_REFRESH;
-}
-
-static bool cache_is_updated(struct nft_cache *cache, uint16_t genid)
-{
- return genid && genid == cache->genid;
-}
-
-bool cache_needs_update(struct nft_cache *cache)
-{
- return cache->flags & NFT_CACHE_UPDATE;
-}
-
-int cache_update(struct nft_ctx *nft, unsigned int flags, struct list_head *msgs)
-{
- struct netlink_ctx ctx = {
- .list = LIST_HEAD_INIT(ctx.list),
- .list_bindings = LIST_HEAD_INIT(ctx.list_bindings),
- .nft = nft,
- .msgs = msgs,
- };
- struct nft_cache *cache = &nft->cache;
- uint32_t genid, genid_stop, oldflags;
- int ret;
-replay:
- ctx.seqnum = cache->seqnum++;
- genid = mnl_genid_get(&ctx);
- if (!cache_needs_refresh(cache) &&
- cache_is_complete(cache, flags) &&
- cache_is_updated(cache, genid))
- return 0;
-
- if (cache->genid)
- cache_release(cache);
-
- if (flags & NFT_CACHE_FLUSHED) {
- oldflags = flags;
- flags = NFT_CACHE_EMPTY;
- if (oldflags & NFT_CACHE_UPDATE)
- flags |= NFT_CACHE_UPDATE;
- goto skip;
- }
-
- ret = cache_init(&ctx, flags);
- if (ret < 0) {
- cache_release(cache);
- if (errno == EINTR) {
- nft->nf_sock = nft_mnl_socket_reopen(nft->nf_sock);
- goto replay;
- }
- return -1;
- }
-
- genid_stop = mnl_genid_get(&ctx);
- if (genid != genid_stop) {
- cache_release(cache);
- goto replay;
- }
-skip:
- cache->genid = genid;
- cache->flags = flags;
- return 0;
-}
-
-static void __cache_flush(struct list_head *table_list)
-{
- struct table *table, *next;
-
- list_for_each_entry_safe(table, next, table_list, list) {
- list_del(&table->list);
- table_free(table);
- }
-}
-
-void cache_release(struct nft_cache *cache)
-{
- __cache_flush(&cache->list);
- cache->genid = 0;
- cache->flags = NFT_CACHE_EMPTY;
-}
-
/* internal ID to uniquely identify a set in the batch */
static uint32_t set_id;
@@ -327,11 +146,15 @@ 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);
+
return set;
}
@@ -339,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;
@@ -351,6 +174,7 @@ struct set *set_clone(const struct set *set)
new_set->policy = set->policy;
new_set->automerge = set->automerge;
new_set->desc = set->desc;
+ init_list_head(&new_set->stmt_list);
return new_set;
}
@@ -363,31 +187,20 @@ struct set *set_get(struct set *set)
void set_free(struct set *set)
{
+ struct stmt *stmt, *next;
+
if (--set->refcnt > 0)
return;
- if (set->init != NULL)
- expr_free(set->init);
+
+ expr_free(set->init);
+ if (set->comment)
+ free_const(set->comment);
handle_free(&set->handle);
- stmt_free(set->stmt);
+ list_for_each_entry_safe(stmt, next, &set->stmt_list, list)
+ stmt_free(stmt);
expr_free(set->key);
expr_free(set->data);
- xfree(set);
-}
-
-void set_add_hash(struct set *set, struct table *table)
-{
- list_add_tail(&set->list, &table->sets);
-}
-
-struct set *set_lookup(const struct table *table, const char *name)
-{
- struct set *set;
-
- list_for_each_entry(set, &table->sets, list) {
- if (!strcmp(set->handle.set.name, name))
- return set;
- }
- return NULL;
+ free(set);
}
struct set *set_lookup_fuzzy(const char *set_name,
@@ -400,14 +213,10 @@ struct set *set_lookup_fuzzy(const char *set_name,
string_misspell_init(&st);
- list_for_each_entry(table, &cache->list, list) {
- list_for_each_entry(set, &table->sets, list) {
+ list_for_each_entry(table, &cache->table_cache.list, cache.list) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
if (set_is_anonymous(set->flags))
continue;
- if (!strcmp(set->handle.set.name, set_name)) {
- *t = table;
- return set;
- }
if (string_misspell_update(set->handle.set.name,
set_name, set, &st))
*t = table;
@@ -419,17 +228,13 @@ struct set *set_lookup_fuzzy(const char *set_name,
struct set *set_lookup_global(uint32_t family, const char *table,
const char *name, struct nft_cache *cache)
{
- struct handle h;
struct table *t;
- h.family = family;
- h.table.name = table;
-
- t = table_lookup(&h, cache);
+ t = table_cache_find(&cache->table_cache, table, family);
if (t == NULL)
return NULL;
- return set_lookup(t, name);
+ return set_cache_find(t, name);
}
struct print_fmt_options {
@@ -492,6 +297,7 @@ static void set_print_declaration(const struct set *set,
struct output_ctx *octx)
{
const char *delim = "";
+ struct stmt *stmt;
const char *type;
uint32_t flags;
@@ -562,11 +368,19 @@ static void set_print_declaration(const struct set *set,
nft_print(octx, "%s", opts->stmt_separator);
}
- if (set->stmt) {
+ if (!list_empty(&set->stmt_list)) {
+ unsigned int flags = octx->flags;
+
nft_print(octx, "%s%s", opts->tab, opts->tab);
+
octx->flags |= NFT_CTX_OUTPUT_STATELESS;
- stmt_print(set->stmt, octx);
- octx->flags &= ~NFT_CTX_OUTPUT_STATELESS;
+ list_for_each_entry(stmt, &set->stmt_list, list) {
+ stmt_print(stmt, octx);
+ if (!list_is_last(&stmt->list, &set->stmt_list))
+ nft_print(octx, " ");
+ }
+ octx->flags = flags;
+
nft_print(octx, "%s", opts->stmt_separator);
}
@@ -584,6 +398,13 @@ static void set_print_declaration(const struct set *set,
time_print(set->gc_int, octx);
nft_print(octx, "%s", opts->stmt_separator);
}
+
+ if (set->comment) {
+ nft_print(octx, "%s%scomment \"%s\"%s",
+ opts->tab, opts->tab,
+ set->comment,
+ opts->stmt_separator);
+ }
}
static void do_set_print(const struct set *set, struct print_fmt_options *opts,
@@ -633,6 +454,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);
@@ -656,8 +479,8 @@ 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)
@@ -734,16 +557,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)
@@ -774,9 +597,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);
}
}
@@ -856,6 +679,7 @@ static const char * const chain_hookname_str_array[] = {
"postrouting",
"output",
"ingress",
+ "egress",
NULL,
};
@@ -874,17 +698,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;
@@ -906,31 +729,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);
+ 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);
-}
-
-void chain_add_hash(struct chain *chain, struct table *table)
-{
- list_add_tail(&chain->list, &table->chains);
-}
-
-struct chain *chain_lookup(const struct table *table, const struct handle *h)
-{
- struct chain *chain;
+ free_const(chain->comment);
- list_for_each_entry(chain, &table->chains, list) {
- if (!strcmp(chain->handle.chain.name, h->chain.name))
- return chain;
- }
- return NULL;
+ /* 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,
@@ -938,7 +750,7 @@ struct chain *chain_binding_lookup(const struct table *table,
{
struct chain *chain;
- list_for_each_entry(chain, &table->chain_bindings, list) {
+ list_for_each_entry(chain, &table->chain_bindings, cache.list) {
if (!strcmp(chain->handle.chain.name, chain_name))
return chain;
}
@@ -953,14 +765,13 @@ struct chain *chain_lookup_fuzzy(const struct handle *h,
struct table *table;
struct chain *chain;
+ if (!h->chain.name)
+ return NULL;
+
string_misspell_init(&st);
- list_for_each_entry(table, &cache->list, list) {
- list_for_each_entry(chain, &table->chains, list) {
- if (!strcmp(chain->handle.chain.name, h->chain.name)) {
- *t = table;
- return chain;
- }
+ list_for_each_entry(table, &cache->table_cache.list, cache.list) {
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
if (string_misspell_update(chain->handle.chain.name,
h->chain.name, chain, &st))
*t = table;
@@ -1008,6 +819,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum)
return "postrouting";
case NF_INET_LOCAL_OUT:
return "output";
+ case NF_INET_INGRESS:
+ return "ingress";
default:
break;
};
@@ -1020,6 +833,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum)
return "forward";
case NF_ARP_OUT:
return "output";
+ case __NF_ARP_INGRESS:
+ return "ingress";
default:
break;
}
@@ -1028,6 +843,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum)
switch (hooknum) {
case NF_NETDEV_INGRESS:
return "ingress";
+ case NF_NETDEV_EGRESS:
+ return "egress";
}
break;
default:
@@ -1053,7 +870,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" },
@@ -1062,7 +879,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" },
@@ -1116,7 +933,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;
@@ -1125,7 +943,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;
}
}
@@ -1158,10 +977,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) {
@@ -1176,19 +996,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;
}
@@ -1210,9 +1032,11 @@ static void chain_print_declaration(const struct chain *chain,
nft_print(octx, "\tchain %s {", chain->handle.chain.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id);
+ if (chain->comment)
+ 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,
+ 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]);
@@ -1245,13 +1069,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)
@@ -1272,8 +1102,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, 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),
@@ -1288,6 +1131,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);
@@ -1296,6 +1140,11 @@ struct table *table_alloc(void)
init_list_head(&table->scope.symbols);
table->refcnt = 1;
+ cache_init(&table->chain_cache);
+ cache_init(&table->set_cache);
+ cache_init(&table->obj_cache);
+ cache_init(&table->ft_cache);
+
return table;
}
@@ -1308,19 +1157,38 @@ void table_free(struct table *table)
if (--table->refcnt > 0)
return;
+ if (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, list)
+ list_for_each_entry_safe(chain, next, &table->chain_bindings, cache.list)
+ chain_free(chain);
+ /* this is implicitly releasing chains in the hashtable cache */
+ list_for_each_entry_safe(chain, next, &table->chain_cache.list, cache.list)
chain_free(chain);
list_for_each_entry_safe(set, nset, &table->sets, list)
set_free(set);
+ /* this is implicitly releasing sets in the hashtable cache */
+ list_for_each_entry_safe(set, nset, &table->set_cache.list, cache.list)
+ set_free(set);
list_for_each_entry_safe(ft, nft, &table->flowtables, list)
flowtable_free(ft);
+ /* this is implicitly releasing flowtables in the hashtable cache */
+ list_for_each_entry_safe(ft, nft, &table->ft_cache.list, cache.list)
+ flowtable_free(ft);
list_for_each_entry_safe(obj, nobj, &table->objs, list)
obj_free(obj);
+ /* this is implicitly releasing objs in the hashtable cache */
+ list_for_each_entry_safe(obj, nobj, &table->obj_cache.list, cache.list)
+ obj_free(obj);
+
handle_free(&table->handle);
scope_release(&table->scope);
- xfree(table);
+ cache_free(&table->chain_cache);
+ cache_free(&table->set_cache);
+ cache_free(&table->obj_cache);
+ cache_free(&table->ft_cache);
+ free(table);
}
struct table *table_get(struct table *table)
@@ -1329,24 +1197,6 @@ struct table *table_get(struct table *table)
return table;
}
-void table_add_hash(struct table *table, struct nft_cache *cache)
-{
- list_add_tail(&table->list, &cache->list);
-}
-
-struct table *table_lookup(const struct handle *h,
- const struct nft_cache *cache)
-{
- struct table *table;
-
- list_for_each_entry(table, &cache->list, list) {
- if (table->handle.family == h->family &&
- !strcmp(table->handle.table.name, h->table.name))
- return table;
- }
- return NULL;
-}
-
struct table *table_lookup_fuzzy(const struct handle *h,
const struct nft_cache *cache)
{
@@ -1355,39 +1205,60 @@ struct table *table_lookup_fuzzy(const struct handle *h,
string_misspell_init(&st);
- list_for_each_entry(table, &cache->list, list) {
- if (!strcmp(table->handle.table.name, h->table.name))
- return table;
-
+ list_for_each_entry(table, &cache->table_cache.list, cache.list) {
string_misspell_update(table->handle.table.name,
h->table.name, table, &st);
}
return st.obj;
}
-const char *table_flags_name[TABLE_FLAGS_MAX] = {
+static const char *table_flags_name[TABLE_FLAGS_MAX] = {
"dormant",
+ "owner",
+ "persist",
};
-static void table_print_options(const struct table *table, const char **delim,
- struct output_ctx *octx)
+const char *table_flag_name(uint32_t flag)
+{
+ if (flag >= TABLE_FLAGS_MAX)
+ return "unknown";
+
+ 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)
{
uint32_t flags = table->flags;
+ bool comma = false;
int i;
- if (flags) {
- nft_print(octx, "\tflags ");
+ if (!table->flags)
+ return;
- for (i = 0; i < TABLE_FLAGS_MAX; i++) {
- if (flags & 0x1)
- nft_print(octx, "%s", table_flags_name[i]);
- flags >>= 1;
- if (flags)
+ nft_print(octx, "\tflags ");
+ for (i = 0; i < TABLE_FLAGS_MAX; i++) {
+ if (flags & (1 << i)) {
+ if (comma)
nft_print(octx, ",");
+
+ nft_print(octx, "%s", table_flag_name(i));
+ comma = true;
}
- nft_print(octx, "\n");
- *delim = "\n";
}
+ nft_print(octx, "\n");
+ *delim = "\n";
}
static void table_print(const struct table *table, struct output_ctx *octx)
@@ -1399,30 +1270,43 @@ 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, " #");
if (nft_output_handle(octx))
- nft_print(octx, " # handle %" PRIu64, table->handle.handle.id);
+ nft_print(octx, " handle %" PRIu64, table->handle.handle.id);
+ if (table->flags & TABLE_F_OWNER)
+ nft_print(octx, " progname %s", get_progname(table->owner));
+
nft_print(octx, "\n");
- table_print_options(table, &delim, octx);
+ table_print_flags(table, &delim, octx);
+
+ if (table->comment)
+ nft_print(octx, "\tcomment \"%s\"\n", table->comment);
- list_for_each_entry(obj, &table->objs, list) {
+ list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
nft_print(octx, "%s", delim);
obj_print(obj, octx);
delim = "\n";
}
- list_for_each_entry(set, &table->sets, list) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
if (set_is_anonymous(set->flags))
continue;
nft_print(octx, "%s", delim);
set_print(set, octx);
delim = "\n";
}
- list_for_each_entry(flowtable, &table->flowtables, list) {
+ list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) {
nft_print(octx, "%s", delim);
flowtable_print(flowtable, octx);
delim = "\n";
}
- list_for_each_entry(chain, &table->chains, list) {
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
nft_print(octx, "%s", delim);
chain_print(chain, octx);
delim = "\n";
@@ -1436,6 +1320,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;
@@ -1443,103 +1329,12 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
cmd->handle = *h;
cmd->location = *loc;
cmd->data = data;
- return cmd;
-}
-
-void cmd_add_loc(struct cmd *cmd, uint16_t offset, struct location *loc)
-{
- assert(cmd->num_attrs < NFT_NLATTR_LOC_MAX);
- 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 set *set, *newset;
- struct flowtable *ft;
- struct table *table;
- struct chain *chain;
- struct rule *rule;
- struct obj *obj;
- struct cmd *new;
- struct handle h;
+ 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);
- 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;
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &set->handle);
- newset = set_clone(set);
- newset->handle.set_id = set->handle.set_id;
- newset->init = set->init;
- set->init = NULL;
- new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h,
- &set->location, newset);
- list_add(&new->list, &cmd->list);
- break;
- default:
- break;
- }
+ return cmd;
}
struct markup *markup_alloc(uint32_t format)
@@ -1554,7 +1349,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)
@@ -1572,8 +1367,8 @@ 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)
@@ -1587,6 +1382,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;
@@ -1625,65 +1422,66 @@ void cmd_free(struct cmd *cmd)
BUG("invalid command object type %u\n", cmd->obj);
}
}
- xfree(cmd->arg);
- xfree(cmd);
+ free(cmd->attr);
+ free_const(cmd->arg);
+ free(cmd);
}
#include <netlink.h>
#include <mnl.h>
-static int __do_add_setelems(struct netlink_ctx *ctx, struct set *set,
- struct expr *expr, uint32_t flags)
+static int __do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd,
+ struct set *set, struct expr *expr, uint32_t flags)
{
expr->set_flags |= set->flags;
- if (mnl_nft_setelem_add(ctx, set, expr, flags) < 0)
+ if (mnl_nft_setelem_add(ctx, cmd, set, expr, flags) < 0)
return -1;
- if (!set_is_anonymous(set->flags) &&
- set->init != NULL && set->init != expr &&
- set->flags & NFT_SET_INTERVAL &&
- set->desc.field_count <= 1) {
- interval_map_decompose(expr);
- list_splice_tail_init(&expr->expressions, &set->init->expressions);
- set->init->size += expr->size;
- expr->size = 0;
- }
-
return 0;
}
-static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
+static int do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd,
uint32_t flags)
{
struct expr *init = cmd->expr;
struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, init, true,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, init, true) < 0)
return -1;
- return __do_add_setelems(ctx, set, init, flags);
+ return __do_add_elements(ctx, cmd, set, init, flags);
+}
+
+static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
+ uint32_t flags)
+{
+ struct set *set = cmd->set;
+
+ return __do_add_elements(ctx, cmd, set, set->init, flags);
}
static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
- uint32_t flags, bool add)
+ uint32_t flags)
{
struct set *set = cmd->set;
if (set->init != NULL) {
+ /* Update set->init->size (NFTNL_SET_DESC_SIZE) before adding
+ * the set to the kernel. Calling this from do_add_setelems()
+ * comes too late which might result in spurious ENFILE errors.
+ */
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, set->init, true,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, set->init, true) < 0)
return -1;
}
- if (add && mnl_nft_set_add(ctx, cmd, flags) < 0)
+
+ if (mnl_nft_set_add(ctx, cmd, flags) < 0)
return -1;
- if (set->init != NULL) {
- return __do_add_setelems(ctx, set, set->init, flags);
- }
+
+ if (set_is_anonymous(set->flags))
+ return __do_add_elements(ctx, cmd, set, set->init, flags);
+
return 0;
}
@@ -1702,11 +1500,11 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
case CMD_OBJ_RULE:
return mnl_nft_rule_add(ctx, cmd, flags | NLM_F_APPEND);
case CMD_OBJ_SET:
- return do_add_set(ctx, cmd, flags, true);
+ return do_add_set(ctx, cmd, flags);
case CMD_OBJ_SETELEMS:
- return do_add_set(ctx, cmd, flags, false);
- case CMD_OBJ_ELEMENTS:
return do_add_setelems(ctx, cmd, flags);
+ case CMD_OBJ_ELEMENTS:
+ return do_add_elements(ctx, cmd, flags);
case CMD_OBJ_COUNTER:
case CMD_OBJ_QUOTA:
case CMD_OBJ_CT_HELPER:
@@ -1757,12 +1555,10 @@ static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, expr, false,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, expr, false) < 0)
return -1;
- if (mnl_nft_setelem_del(ctx, cmd) < 0)
+ if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0)
return -1;
return 0;
@@ -1804,8 +1600,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;
@@ -1813,15 +1608,10 @@ 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;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
@@ -1830,18 +1620,17 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
family2str(table->handle.family),
table->handle.table.name);
- list_for_each_entry(set, &table->sets, list) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
if (cmd->obj == CMD_OBJ_SETS &&
!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");
@@ -1853,9 +1642,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;
@@ -1871,26 +1661,18 @@ void obj_free(struct obj *obj)
{
if (--obj->refcnt > 0)
return;
+ free_const(obj->comment);
handle_free(&obj->handle);
- xfree(obj);
-}
-
-void obj_add_hash(struct obj *obj, struct table *table)
-{
- list_add_tail(&obj->list, &table->objs);
-}
+ if (obj->type == NFT_OBJECT_CT_TIMEOUT) {
+ struct timeout_state *ts, *next;
-struct obj *obj_lookup(const struct table *table, const char *name,
- uint32_t type)
-{
- struct obj *obj;
-
- list_for_each_entry(obj, &table->objs, list) {
- if (!strcmp(obj->handle.obj.name, name) &&
- obj->type == type)
- return obj;
+ list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) {
+ list_del(&ts->head);
+ free_const(ts->timeout_str);
+ free(ts);
+ }
}
- return NULL;
+ free(obj);
}
struct obj *obj_lookup_fuzzy(const char *obj_name,
@@ -1903,12 +1685,8 @@ struct obj *obj_lookup_fuzzy(const char *obj_name,
string_misspell_init(&st);
- list_for_each_entry(table, &cache->list, list) {
- list_for_each_entry(obj, &table->objs, list) {
- if (!strcmp(obj->handle.obj.name, obj_name)) {
- *t = table;
- return obj;
- }
+ list_for_each_entry(table, &cache->table_cache.list, cache.list) {
+ list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
if (string_misspell_update(obj->handle.obj.name,
obj_name, obj, &st))
*t = table;
@@ -1919,10 +1697,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);
}
@@ -1937,11 +1715,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;
}
}
@@ -1964,6 +1745,16 @@ static const char *synproxy_timestamp_to_str(const uint32_t flags)
return "";
}
+static void obj_print_comment(const struct obj *obj,
+ struct print_fmt_options *opts,
+ struct output_ctx *octx)
+{
+ if (obj->comment)
+ nft_print(octx, "%s%s%scomment \"%s\"",
+ opts->nl, opts->tab, opts->tab,
+ obj->comment);
+}
+
static void obj_print_data(const struct obj *obj,
struct print_fmt_options *opts,
struct output_ctx *octx)
@@ -1973,13 +1764,14 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
- nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
- if (nft_output_stateless(octx)) {
- nft_print(octx, "packets 0 bytes 0");
- break;
- }
- nft_print(octx, "packets %" PRIu64 " bytes %" PRIu64 "%s",
- obj->counter.packets, obj->counter.bytes, opts->nl);
+
+ obj_print_comment(obj, opts, octx);
+ if (nft_output_stateless(octx))
+ nft_print(octx, "%s", opts->nl);
+ else
+ nft_print(octx, "%s%s%spackets %" PRIu64 " bytes %" PRIu64 "%s",
+ opts->nl, opts->tab, opts->tab,
+ obj->counter.packets, obj->counter.bytes, opts->nl);
break;
case NFT_OBJECT_QUOTA: {
const char *data_unit;
@@ -1988,6 +1780,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
data_unit = get_rate(obj->quota.bytes, &bytes);
nft_print(octx, "%s%" PRIu64 " %s",
@@ -2005,6 +1799,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
nft_print(octx, "\"%s\"%s", obj->secmark.ctx, opts->nl);
break;
@@ -2012,6 +1808,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s", opts->nl);
nft_print(octx, "%s%stype \"%s\" protocol ",
opts->tab, opts->tab, obj->ct_helper.name);
@@ -2026,6 +1824,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s", opts->nl);
nft_print(octx, "%s%sprotocol ", opts->tab, opts->tab);
print_proto_name_proto(obj->ct_timeout.l4proto, octx);
@@ -2041,6 +1841,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s", opts->nl);
nft_print(octx, "%s%sprotocol ", opts->tab, opts->tab);
print_proto_name_proto(obj->ct_expect.l4proto, octx);
@@ -2069,6 +1871,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
switch (obj->limit.type) {
case NFT_LIMIT_PKTS:
@@ -2106,6 +1910,8 @@ static void obj_print_data(const struct obj *obj,
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+ obj_print_comment(obj, opts, octx);
+
if (flags & NF_SYNPROXY_OPT_MSS) {
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
nft_print(octx, "mss %u", obj->synproxy.mss);
@@ -2138,7 +1944,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]);
@@ -2156,7 +1962,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]);
@@ -2214,7 +2020,7 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type)
struct table *table;
struct obj *obj;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
@@ -2223,14 +2029,14 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type)
strcmp(cmd->handle.table.name, table->handle.table.name))
continue;
- if (list_empty(&table->objs))
+ if (list_empty(&table->obj_cache.list))
continue;
nft_print(&ctx->nft->output, "table %s %s {\n",
family2str(table->handle.family),
table->handle.table.name);
- list_for_each_entry(obj, &table->objs, list) {
+ list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
if (obj->type != type ||
(cmd->handle.obj.name != NULL &&
strcmp(cmd->handle.obj.name, obj->handle.obj.name)))
@@ -2248,9 +2054,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;
@@ -2274,15 +2081,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);
-}
-
-void flowtable_add_hash(struct flowtable *flowtable, struct table *table)
-{
- list_add_tail(&flowtable->list, &table->flowtables);
+ free(flowtable);
}
static void flowtable_print_declaration(const struct flowtable *flowtable,
@@ -2322,6 +2124,10 @@ static void flowtable_print_declaration(const struct flowtable *flowtable,
nft_print(octx, " }%s", opts->stmt_separator);
}
+ if (flowtable->flags & NFT_FLOWTABLE_HW_OFFLOAD)
+ nft_print(octx, "%s%sflags offload%s", opts->tab, opts->tab,
+ opts->stmt_separator);
+
if (flowtable->flags & NFT_FLOWTABLE_COUNTER)
nft_print(octx, "%s%scounter%s", opts->tab, opts->tab,
opts->stmt_separator);
@@ -2346,17 +2152,6 @@ void flowtable_print(const struct flowtable *s, struct output_ctx *octx)
do_flowtable_print(s, &opts, octx);
}
-struct flowtable *flowtable_lookup(const struct table *table, const char *name)
-{
- struct flowtable *ft;
-
- list_for_each_entry(ft, &table->flowtables, list) {
- if (!strcmp(ft->handle.flowtable.name, name))
- return ft;
- }
- return NULL;
-}
-
struct flowtable *flowtable_lookup_fuzzy(const char *ft_name,
const struct nft_cache *cache,
const struct table **t)
@@ -2367,12 +2162,8 @@ struct flowtable *flowtable_lookup_fuzzy(const char *ft_name,
string_misspell_init(&st);
- list_for_each_entry(table, &cache->list, list) {
- list_for_each_entry(ft, &table->flowtables, list) {
- if (!strcmp(ft->handle.flowtable.name, ft_name)) {
- *t = table;
- return ft;
- }
+ list_for_each_entry(table, &cache->table_cache.list, cache.list) {
+ list_for_each_entry(ft, &table->ft_cache.list, cache.list) {
if (string_misspell_update(ft->handle.flowtable.name,
ft_name, ft, &st))
*t = table;
@@ -2386,8 +2177,8 @@ static int do_list_flowtable(struct netlink_ctx *ctx, struct cmd *cmd,
{
struct flowtable *ft;
- ft = flowtable_lookup(table, cmd->handle.flowtable.name);
- if (ft == NULL)
+ ft = ft_cache_find(table, cmd->handle.flowtable.name);
+ if (!ft)
return -1;
nft_print(&ctx->nft->output, "table %s %s {\n",
@@ -2410,7 +2201,7 @@ static int do_list_flowtables(struct netlink_ctx *ctx, struct cmd *cmd)
struct flowtable *flowtable;
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
@@ -2419,7 +2210,7 @@ static int do_list_flowtables(struct netlink_ctx *ctx, struct cmd *cmd)
family2str(table->handle.family),
table->handle.table.name);
- list_for_each_entry(flowtable, &table->flowtables, list) {
+ list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) {
flowtable_print_declaration(flowtable, &opts, &ctx->nft->output);
nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl);
}
@@ -2434,20 +2225,15 @@ static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd)
unsigned int family = cmd->handle.family;
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (family != NFPROTO_UNSPEC &&
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;
}
@@ -2455,7 +2241,7 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
@@ -2471,9 +2257,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,
@@ -2483,13 +2274,9 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,
table_print_declaration(table, &ctx->nft->output);
- list_for_each_entry(chain, &table->chains, list) {
- if (chain->handle.family != cmd->handle.family ||
- strcmp(cmd->handle.chain.name, chain->handle.chain.name) != 0)
- continue;
-
+ chain = chain_cache_find(table, cmd->handle.chain.name);
+ if (chain)
chain_print(chain, &ctx->nft->output);
- }
nft_print(&ctx->nft->output, "}\n");
@@ -2501,14 +2288,14 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd)
struct table *table;
struct chain *chain;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
table_print_declaration(table, &ctx->nft->output);
- list_for_each_entry(chain, &table->chains, list) {
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
chain_print_declaration(chain, &ctx->nft->output);
nft_print(&ctx->nft->output, "\t}\n");
}
@@ -2535,17 +2322,30 @@ 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_lookup(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);
return 0;
}
+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);
+}
+
static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct table *table = NULL;
@@ -2554,13 +2354,14 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
return do_command_list_json(ctx, cmd);
if (cmd->handle.table.name != NULL)
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
-
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
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:
@@ -2570,6 +2371,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);
@@ -2589,8 +2392,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:
@@ -2605,6 +2410,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
return do_list_flowtable(ctx, cmd, table);
case CMD_OBJ_FLOWTABLES:
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);
}
@@ -2612,7 +2419,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
return 0;
}
-static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
+static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, bool reset)
{
struct set *set, *new_set;
struct expr *init;
@@ -2630,7 +2437,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);
@@ -2646,7 +2453,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);
}
@@ -2675,15 +2482,36 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_QUOTA:
type = NFT_OBJECT_QUOTA;
break;
+ case CMD_OBJ_RULES:
+ ret = netlink_reset_rules(ctx, cmd, true);
+ if (ret < 0)
+ return ret;
+
+ return do_command_list(ctx, cmd);
+ case CMD_OBJ_RULE:
+ return netlink_reset_rules(ctx, cmd, false);
+ case CMD_OBJ_ELEMENTS:
+ return do_get_setelems(ctx, cmd, true);
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ ret = netlink_list_setelems(ctx, &cmd->handle, cmd->set, true);
+ if (ret < 0)
+ return ret;
+
+ return do_command_list(ctx, cmd);
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_lookup(&obj->handle, &ctx->nft->cache);
- if (!obj_lookup(table, obj->handle.obj.name, obj->type))
- list_move(&obj->list, &table->objs);
+ 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);
+ }
}
if (ret < 0)
return ret;
@@ -2711,12 +2539,14 @@ static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd)
static int do_command_rename(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct table *table = table_lookup(&cmd->handle, &ctx->nft->cache);
+ struct table *table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
const struct chain *chain;
switch (cmd->obj) {
case CMD_OBJ_CHAIN:
- chain = chain_lookup(table, &cmd->handle);
+ chain = chain_cache_find(table, cmd->handle.chain.name);
return mnl_nft_chain_rename(ctx, cmd, chain);
default:
@@ -2786,6 +2616,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);
@@ -2901,49 +2732,78 @@ static void payload_do_merge(struct stmt *sa[], unsigned int n)
}
/**
- * payload_try_merge - try to merge consecutive payload match statements
+ * stmt_reduce - reduce statements in rule
*
* @rule: nftables rule
*
+ * This function aims to:
+ *
+ * - remove redundant statement, e.g. remove 'meta protocol ip' if family is ip
+ * - merge consecutive payload match statements
+ *
* Locate sequences of payload match statements referring to adjacent
* header locations and merge those using only equality relations.
*
* As a side-effect, payload match statements are ordered in ascending
* order according to the location of the payload.
*/
-static void payload_try_merge(const struct rule *rule)
+static void stmt_reduce(const struct rule *rule)
{
+ struct stmt *stmt, *dstmt = NULL, *next;
struct stmt *sa[rule->num_stmts];
- struct stmt *stmt, *next;
unsigned int idx = 0;
list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
+ /* delete this redundant statement */
+ if (dstmt) {
+ list_del(&dstmt->list);
+ stmt_free(dstmt);
+ dstmt = NULL;
+ }
+
/* Must not merge across other statements */
- if (stmt->ops->type != STMT_EXPRESSION)
- goto do_merge;
+ if (stmt->ops->type != STMT_EXPRESSION) {
+ if (idx >= 2)
+ payload_do_merge(sa, idx);
+ idx = 0;
+ continue;
+ }
if (stmt->expr->etype != EXPR_RELATIONAL)
continue;
- if (stmt->expr->left->etype != EXPR_PAYLOAD)
- continue;
if (stmt->expr->right->etype != EXPR_VALUE)
continue;
- switch (stmt->expr->op) {
- case OP_EQ:
- case OP_IMPLICIT:
- case OP_NEQ:
- break;
- default:
- continue;
- }
- sa[idx++] = stmt;
- continue;
-do_merge:
- if (idx < 2)
- continue;
- payload_do_merge(sa, idx);
- idx = 0;
+ if (stmt->expr->left->etype == EXPR_PAYLOAD) {
+ switch (stmt->expr->op) {
+ case OP_EQ:
+ case OP_IMPLICIT:
+ break;
+ default:
+ continue;
+ }
+
+ sa[idx++] = stmt;
+ } else if (stmt->expr->left->etype == EXPR_META) {
+ switch (stmt->expr->op) {
+ case OP_EQ:
+ case OP_IMPLICIT:
+ 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);
+ if ((rule->handle.family == NFPROTO_IPV4 &&
+ protocol == ETH_P_IP) ||
+ (rule->handle.family == NFPROTO_IPV6 &&
+ protocol == ETH_P_IPV6))
+ dstmt = stmt;
+ }
+ break;
+ default:
+ break;
+ }
+ }
}
if (idx > 1)
@@ -2952,6 +2812,6 @@ do_merge:
struct error_record *rule_postprocess(struct rule *rule)
{
- payload_try_merge(rule);
+ stmt_reduce(rule);
return NULL;
}
diff --git a/src/scanner.l b/src/scanner.l
index 45699c85..e4d20e69 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>
@@ -98,6 +101,8 @@ static void reset_pos(struct parser_state *state, struct location *loc)
state->indesc->column = 1;
}
+static void scanner_push_start_cond(void *scanner, enum startcond_type type);
+
#define YY_USER_ACTION { \
update_pos(yyget_extra(yyscanner), yylloc, yyleng); \
update_offset(yyget_extra(yyscanner), yylloc, yyleng); \
@@ -116,12 +121,12 @@ 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)?
@@ -193,6 +198,62 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%option yylineno
%option nodefault
%option warn
+%option stack
+%s SCANSTATE_ARP
+%s SCANSTATE_AT
+%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
+%s SCANSTATE_QUOTA
+%s SCANSTATE_SCTP
+%s SCANSTATE_SECMARK
+%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
+%s SCANSTATE_CMD_MONITOR
+%s SCANSTATE_CMD_RESET
+%s SCANSTATE_EXPR_AH
+%s SCANSTATE_EXPR_COMP
+%s SCANSTATE_EXPR_DCCP
+%s SCANSTATE_EXPR_DST
+%s SCANSTATE_EXPR_ESP
+%s SCANSTATE_EXPR_FIB
+%s SCANSTATE_EXPR_FRAG
+%s SCANSTATE_EXPR_HASH
+%s SCANSTATE_EXPR_HBH
+%s SCANSTATE_EXPR_IPSEC
+%s SCANSTATE_EXPR_MH
+%s SCANSTATE_EXPR_NUMGEN
+%s SCANSTATE_EXPR_OSF
+%s SCANSTATE_EXPR_QUEUE
+%s SCANSTATE_EXPR_RT
+%s SCANSTATE_EXPR_SCTP_CHUNK
+%s SCANSTATE_EXPR_SOCKET
+%s SCANSTATE_EXPR_TH
+%s SCANSTATE_EXPR_UDP
+%s SCANSTATE_EXPR_UDPLITE
+
+%s SCANSTATE_STMT_DUP
+%s SCANSTATE_STMT_FWD
+%s SCANSTATE_STMT_LOG
+%s SCANSTATE_STMT_NAT
+%s SCANSTATE_STMT_REJECT
+%s SCANSTATE_STMT_SYNPROXY
+%s SCANSTATE_STMT_TPROXY
%%
@@ -233,7 +294,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"/" { return SLASH; }
"-" { return DASH; }
"*" { return ASTERISK; }
-"@" { return AT; }
+"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
"$" { return '$'; }
"=" { return '='; }
"vmap" { return VMAP; }
@@ -247,29 +308,36 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"describe" { return DESCRIBE; }
+<SCANSTATE_CMD_LIST,SCANSTATE_CMD_MONITOR>{
+ "chains" { return CHAINS; }
+ "sets" { return SETS; }
+ "tables" { return TABLES; }
+}
+<SCANSTATE_CMD_MONITOR>{
+ "rules" { return RULES; }
+ "trace" { return TRACE; }
+}
"hook" { return HOOK; }
"device" { return DEVICE; }
"devices" { return DEVICES; }
"table" { return TABLE; }
-"tables" { return TABLES; }
"chain" { return CHAIN; }
-"chains" { return CHAINS; }
"rule" { return RULE; }
-"rules" { return RULES; }
-"sets" { return SETS; }
"set" { return SET; }
"element" { return ELEMENT; }
"map" { return MAP; }
-"maps" { return MAPS; }
"flowtable" { return FLOWTABLE; }
"handle" { return HANDLE; }
"ruleset" { return RULESET; }
-"trace" { return TRACE; }
-
-"socket" { return SOCKET; }
-"transparent" { return TRANSPARENT;}
-"tproxy" { return TPROXY; }
+"socket" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; }
+<SCANSTATE_EXPR_SOCKET>{
+ "transparent" { return TRANSPARENT; }
+ "wildcard" { return WILDCARD; }
+ "cgroupv2" { return CGROUPV2; }
+ "level" { return LEVEL; }
+}
+"tproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; }
"accept" { return ACCEPT; }
"drop" { return DROP; }
@@ -277,7 +345,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"jump" { return JUMP; }
"goto" { return GOTO; }
"return" { return RETURN; }
-"to" { return TO; }
+<SCANSTATE_EXPR_QUEUE,SCANSTATE_STMT_DUP,SCANSTATE_STMT_FWD,SCANSTATE_STMT_NAT,SCANSTATE_STMT_TPROXY,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_IP is a workaround */
"inet" { return INET; }
"netdev" { return NETDEV; }
@@ -289,13 +357,15 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"insert" { return INSERT; }
"delete" { return DELETE; }
"get" { return GET; }
-"list" { return LIST; }
-"reset" { return RESET; }
+"list" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_LIST); return LIST; }
+"reset" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; }
"flush" { return FLUSH; }
"rename" { return RENAME; }
-"import" { return IMPORT; }
-"export" { return EXPORT; }
-"monitor" { return MONITOR; }
+"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; }
@@ -310,202 +380,342 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"elements" { return ELEMENTS; }
"expires" { return EXPIRES; }
-"policy" { return POLICY; }
+"policy" { scanner_push_start_cond(yyscanner, SCANSTATE_POLICY); return POLICY; }
"size" { return SIZE; }
-"performance" { return PERFORMANCE; }
-"memory" { return MEMORY; }
+<SCANSTATE_POLICY>{
+ "performance" { return PERFORMANCE; }
+ "memory" { return MEMORY; }
+}
"flow" { return FLOW; }
"offload" { return OFFLOAD; }
"meter" { return METER; }
-"meters" { return METERS; }
-
-"flowtables" { return FLOWTABLES; }
-
-"counter" { return COUNTER; }
-"name" { return NAME; }
-"packets" { return PACKETS; }
-"bytes" { return BYTES; }
-"avgpkt" { return AVGPKT; }
-
-"counters" { return COUNTERS; }
-"quotas" { return QUOTAS; }
-"limits" { return LIMITS; }
-"synproxys" { return SYNPROXYS; }
-
-"log" { return LOG; }
-"prefix" { return PREFIX; }
-"group" { return GROUP; }
-"snaplen" { return SNAPLEN; }
-"queue-threshold" { return QUEUE_THRESHOLD; }
-"level" { return LEVEL; }
-
-"queue" { return QUEUE;}
-"num" { return QUEUENUM;}
-"bypass" { return BYPASS;}
-"fanout" { return FANOUT;}
-
-"limit" { return LIMIT; }
-"rate" { return RATE; }
-"burst" { return BURST; }
-"until" { return UNTIL; }
-"over" { return OVER; }
-
-"quota" { return QUOTA; }
-"used" { return USED; }
-
-"nanosecond" { return NANOSECOND; }
-"microsecond" { return MICROSECOND; }
-"millisecond" { return MILLISECOND; }
-"second" { return SECOND; }
-"minute" { return MINUTE; }
+
+<SCANSTATE_CMD_LIST>{
+ "meters" { return METERS; }
+ "flowtables" { return FLOWTABLES; }
+ "limits" { return LIMITS; }
+ "maps" { return MAPS; }
+ "secmarks" { return SECMARKS; }
+ "synproxys" { return SYNPROXYS; }
+ "hooks" { return HOOKS; }
+}
+
+"counter" { scanner_push_start_cond(yyscanner, SCANSTATE_COUNTER); return COUNTER; }
+<SCANSTATE_COUNTER,SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_STMT_SYNPROXY,SCANSTATE_EXPR_OSF>"name" { return NAME; }
+<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; }
+<SCANSTATE_STMT_LOG,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"prefix" { return PREFIX; }
+<SCANSTATE_STMT_LOG>{
+ "snaplen" { return SNAPLEN; }
+ "queue-threshold" { return QUEUE_THRESHOLD; }
+ "level" { return LEVEL; }
+ "group" { return GROUP; }
+}
+
+"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;}
+<SCANSTATE_EXPR_QUEUE>{
+ "num" { return QUEUENUM;}
+ "bypass" { return BYPASS;}
+ "fanout" { return FANOUT;}
+}
+"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; }
+<SCANSTATE_LIMIT>{
+ "rate" { return RATE; }
+ "burst" { return BURST; }
+
+ /* time_unit */
+ "second" { return SECOND; }
+ "minute" { return MINUTE; }
+ "week" { return WEEK; }
+}
+<SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"over" { return OVER; }
+
+"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
+<SCANSTATE_QUOTA>{
+ "until" { return UNTIL; }
+}
+
+<SCANSTATE_QUOTA,SCANSTATE_LAST>"used" { return USED; }
+
"hour" { return HOUR; }
"day" { return DAY; }
-"week" { return WEEK; }
-"reject" { return _REJECT; }
-"with" { return WITH; }
-"icmpx" { return ICMPX; }
+"reject" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; }
+<SCANSTATE_STMT_REJECT>{
+ "with" { return WITH; }
+ "icmpx" { return ICMPX; }
+}
-"snat" { return SNAT; }
-"dnat" { return DNAT; }
-"masquerade" { return MASQUERADE; }
-"redirect" { return REDIRECT; }
+"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
+"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
+"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
+"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
"random" { return RANDOM; }
-"fully-random" { return FULLY_RANDOM; }
-"persistent" { return PERSISTENT; }
+<SCANSTATE_STMT_NAT>{
+ "fully-random" { return FULLY_RANDOM; }
+ "persistent" { return PERSISTENT; }
+ "port" { return PORT; }
+}
-"ll" { return LL_HDR; }
-"nh" { return NETWORK_HDR; }
-"th" { return TRANSPORT_HDR; }
+<SCANSTATE_AT>{
+ "ll" { return LL_HDR; }
+ "nh" { return NETWORK_HDR; }
+}
+"th" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; }
"bridge" { return BRIDGE; }
-"ether" { return ETHER; }
-"saddr" { return SADDR; }
-"daddr" { return DADDR; }
-"type" { return TYPE; }
+"ether" { scanner_push_start_cond(yyscanner, SCANSTATE_ETH); return ETHER; }
+<SCANSTATE_ARP,SCANSTATE_CT,SCANSTATE_ETH,SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_FIB,SCANSTATE_EXPR_IPSEC>{
+ "saddr" { return SADDR; }
+ "daddr" { return DADDR; }
+}
+"type" { scanner_push_start_cond(yyscanner, SCANSTATE_TYPE); return TYPE; }
"typeof" { return TYPEOF; }
-"vlan" { return VLAN; }
-"id" { return ID; }
-"cfi" { return CFI; }
-"pcp" { return PCP; }
-
-"arp" { return ARP; }
-"htype" { return HTYPE; }
-"ptype" { return PTYPE; }
-"hlen" { return HLEN; }
-"plen" { return PLEN; }
-"operation" { return OPERATION; }
-
-"ip" { return IP; }
-"version" { return HDRVERSION; }
-"hdrlength" { return HDRLENGTH; }
-"dscp" { return DSCP; }
+"vlan" { scanner_push_start_cond(yyscanner, SCANSTATE_VLAN); return VLAN; }
+<SCANSTATE_CT,SCANSTATE_EXPR_FRAG,SCANSTATE_VLAN,SCANSTATE_IP,SCANSTATE_ICMP>"id" { return ID; }
+<SCANSTATE_VLAN>{
+ "cfi" { return CFI; }
+ "dei" { return DEI; }
+ "pcp" { return PCP; }
+}
+"8021ad" { yylval->string = xstrdup(yytext); return STRING; }
+"8021q" { yylval->string = xstrdup(yytext); return STRING; }
+
+"arp" { scanner_push_start_cond(yyscanner, SCANSTATE_ARP); return ARP; }
+<SCANSTATE_ARP>{
+ "htype" { return HTYPE; }
+ "ptype" { return PTYPE; }
+ "hlen" { return HLEN; }
+ "plen" { return PLEN; }
+ "operation" { return OPERATION; }
+}
+
+"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; }
+<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>{
+ "hdrlength" { return HDRLENGTH; }
+}
+<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_TYPE>{
+ "dscp" { return DSCP; }
+}
"ecn" { return ECN; }
-"length" { return LENGTH; }
-"frag-off" { return FRAG_OFF; }
-"ttl" { return TTL; }
-"protocol" { return PROTOCOL; }
-"checksum" { return CHECKSUM; }
-
-"lsrr" { return LSRR; }
-"rr" { return RR; }
-"ssrr" { return SSRR; }
-"ra" { return RA; }
-
-"value" { return VALUE; }
-"ptr" { return PTR; }
-
-"echo" { return ECHO; }
-"eol" { return EOL; }
-"maxseg" { return MAXSEG; }
-"noop" { return NOOP; }
-"sack" { return SACK; }
-"sack0" { return SACK0; }
-"sack1" { return SACK1; }
-"sack2" { return SACK2; }
-"sack3" { return SACK3; }
-"sack-permitted" { return SACK_PERMITTED; }
-"timestamp" { return TIMESTAMP; }
-"time" { return TIME; }
+<SCANSTATE_EXPR_UDP,SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_META,SCANSTATE_TCP,SCANSTATE_SCTP,SCANSTATE_EXPR_SCTP_CHUNK>"length" { return LENGTH; }
+<SCANSTATE_EXPR_FRAG,SCANSTATE_IP>{
+ "frag-off" { return FRAG_OFF; }
+}
+<SCANSTATE_EXPR_OSF,SCANSTATE_IP>{
+ "ttl" { return TTL; }
+}
+<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; }
+}
-"kind" { return KIND; }
-"count" { return COUNT; }
-"left" { return LEFT; }
-"right" { return RIGHT; }
-"tsval" { return TSVAL; }
-"tsecr" { return TSECR; }
+<SCANSTATE_IP>{
+ "lsrr" { return LSRR; }
+ "rr" { return RR; }
+ "ssrr" { return SSRR; }
+ "ra" { return RA; }
-"icmp" { return ICMP; }
-"code" { return CODE; }
-"sequence" { return SEQUENCE; }
-"gateway" { return GATEWAY; }
-"mtu" { return MTU; }
+ "ptr" { return PTR; }
+ "value" { return VALUE; }
-"igmp" { return IGMP; }
-"mrt" { return MRT; }
+ "option" { return OPTION; }
+ "options" { return OPTIONS; }
+}
-"ip6" { return IP6; }
-"priority" { return PRIORITY; }
-"flowlabel" { return FLOWLABEL; }
-"nexthdr" { return NEXTHDR; }
-"hoplimit" { return HOPLIMIT; }
+<SCANSTATE_TCP>{
+ /* tcp header fields */
+ "ackseq" { return ACKSEQ; }
+ "doff" { return DOFF; }
+ "window" { return WINDOW; }
+ "urgptr" { return URGPTR; }
+
+ /* tcp option types */
+ "echo" { return ECHO; }
+ "eol" { return EOL; }
+ "maxseg" { return MSS; }
+ "mss" { return MSS; }
+ "nop" { return NOP; }
+ "noop" { return NOP; }
+ "sack" { return SACK; }
+ "sack0" { return SACK0; }
+ "sack1" { return SACK1; }
+ "sack2" { return SACK2; }
+ "sack3" { return SACK3; }
+ "sack-permitted" { return SACK_PERM; }
+ "sack-perm" { return SACK_PERM; }
+ "timestamp" { return TIMESTAMP; }
+ "fastopen" { return FASTOPEN; }
+ "mptcp" { return MPTCP; }
+ "md5sig" { return MD5SIG; }
+
+ /* tcp option fields */
+ "left" { return LEFT; }
+ "right" { return RIGHT; }
+ "count" { return COUNT; }
+ "tsval" { return TSVAL; }
+ "tsecr" { return TSECR; }
+ "subtype" { return SUBTYPE; }
+
+ "options" { return OPTIONS; }
+ "option" { return OPTION; }
+}
+"time" { return TIME; }
-"icmpv6" { return ICMP6; }
-"param-problem" { return PPTR; }
-"max-delay" { return MAXDELAY; }
+"icmp" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP; }
+"icmpv6" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP6; }
+<SCANSTATE_ICMP>{
+ "gateway" { return GATEWAY; }
+ "code" { return CODE; }
+ "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; }
+}
+
+"igmp" { scanner_push_start_cond(yyscanner, SCANSTATE_IGMP); return IGMP; }
+<SCANSTATE_IGMP>{
+ "mrt" { return MRT; }
+ "group" { return GROUP; }
+}
+
+"ip6" { scanner_push_start_cond(yyscanner, SCANSTATE_IP6); return IP6; }
+"priority" { return PRIORITY; }
+<SCANSTATE_IP6>{
+ "flowlabel" { return FLOWLABEL; }
+ "hoplimit" { return HOPLIMIT; }
+}
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_COMP,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP6>{
+ "nexthdr" { return NEXTHDR; }
+}
-"ah" { return AH; }
-"reserved" { return RESERVED; }
-"spi" { return SPI; }
+"ah" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_AH); return AH; }
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_MH,SCANSTATE_TCP>{
+ "reserved" { return RESERVED; }
+}
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_EXPR_IPSEC>"spi" { return SPI; }
-"esp" { return ESP; }
+"esp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_ESP); return ESP; }
-"comp" { return COMP; }
+"comp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_COMP); return COMP; }
+<SCANSTATE_EXPR_COMP>{
+ "cpi" { return CPI; }
+}
"flags" { return FLAGS; }
-"cpi" { return CPI; }
-"udp" { return UDP; }
-"udplite" { return UDPLITE; }
-"sport" { return SPORT; }
-"dport" { return DPORT; }
-"port" { return PORT; }
+"udp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDP); return UDP; }
+"udplite" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDPLITE); return UDPLITE; }
+<SCANSTATE_EXPR_UDPLITE>{
+ "csumcov" { return CSUMCOV; }
+}
+<SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{
+ "sport" { return SPORT; }
+}
+<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; }
+}
-"tcp" { return TCP; }
-"ackseq" { return ACKSEQ; }
-"doff" { return DOFF; }
-"window" { return WINDOW; }
-"urgptr" { return URGPTR; }
-"option" { return OPTION; }
+"vxlan" { return VXLAN; }
+"vni" { return VNI; }
-"dccp" { return DCCP; }
+"geneve" { return GENEVE; }
-"sctp" { return SCTP; }
-"vtag" { return VTAG; }
+"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; }
+"gretap" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; }
-"rt" { return RT; }
-"rt0" { return RT0; }
-"rt2" { return RT2; }
-"srh" { return RT4; }
-"seg-left" { return SEG_LEFT; }
-"addr" { return ADDR; }
-"last-entry" { return LAST_ENT; }
-"tag" { return TAG; }
-"sid" { return SID; }
+"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; }
-"hbh" { return HBH; }
+"dccp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; }
-"frag" { return FRAG; }
-"reserved2" { return RESERVED2; }
-"more-fragments" { return MORE_FRAGMENTS; }
+"sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; }
+
+<SCANSTATE_SCTP>{
+ "chunk" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SCTP_CHUNK); return CHUNK; }
+ "vtag" { return VTAG; }
+}
+
+<SCANSTATE_EXPR_SCTP_CHUNK>{
+ "data" { return DATA; }
+ "init" { return INIT; }
+ "init-ack" { return INIT_ACK; }
+ "heartbeat" { return HEARTBEAT; }
+ "heartbeat-ack" { return HEARTBEAT_ACK; }
+ "abort" { return ABORT; }
+ "shutdown" { return SHUTDOWN; }
+ "shutdown-ack" { return SHUTDOWN_ACK; }
+ "error" { return ERROR; }
+ "cookie-echo" { return COOKIE_ECHO; }
+ "cookie-ack" { return COOKIE_ACK; }
+ "ecne" { return ECNE; }
+ "cwr" { return CWR; }
+ "shutdown-complete" { return SHUTDOWN_COMPLETE; }
+ "asconf-ack" { return ASCONF_ACK; }
+ "forward-tsn" { return FORWARD_TSN; }
+ "asconf" { return ASCONF; }
+
+ "tsn" { return TSN; }
+ "sack" { return SACK; }
+ "stream" { return STREAM; }
+ "ssn" { return SSN; }
+ "ppid" { return PPID; }
+ "init-tag" { return INIT_TAG; }
+ "a-rwnd" { return A_RWND; }
+ "num-outbound-streams" { return NUM_OSTREAMS; }
+ "num-inbound-streams" { return NUM_ISTREAMS; }
+ "initial-tsn" { return INIT_TSN; }
+ "cum-tsn-ack" { return CUM_TSN_ACK; }
+ "num-gap-ack-blocks" { return NUM_GACK_BLOCKS; }
+ "num-dup-tsns" { return NUM_DUP_TSNS; }
+ "lowest-tsn" { return LOWEST_TSN; }
+ "seqno" { return SEQNO; }
+ "new-cum-tsn" { return NEW_CUM_TSN; }
+}
-"dst" { return DST; }
+"rt" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT; }
+"rt0" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT0; }
+"rt2" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT2; }
+"srh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT4; }
+<SCANSTATE_EXPR_RT,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"addr" { return ADDR; }
-"mh" { return MH; }
+"hbh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HBH); return HBH; }
-"meta" { return META; }
+"frag" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FRAG); return FRAG; }
+<SCANSTATE_EXPR_FRAG>{
+ "reserved2" { return RESERVED2; }
+ "more-fragments" { return MORE_FRAGMENTS; }
+}
+
+"dst" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DST); return DST; }
+
+"mh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_MH); return MH; }
+
+"meta" { scanner_push_start_cond(yyscanner, SCANSTATE_META); return META; }
"mark" { return MARK; }
"iif" { return IIF; }
"iifname" { return IIFNAME; }
@@ -527,73 +737,98 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"oifgroup" { return OIFGROUP; }
"cgroup" { return CGROUP; }
-"classid" { return CLASSID; }
-"nexthop" { return NEXTHOP; }
-
-"ct" { return CT; }
-"l3proto" { return L3PROTOCOL; }
-"proto-src" { return PROTO_SRC; }
-"proto-dst" { return PROTO_DST; }
-"zone" { return ZONE; }
-"original" { return ORIGINAL; }
-"reply" { return REPLY; }
-"direction" { return DIRECTION; }
-"event" { return EVENT; }
-"expectation" { return EXPECTATION; }
-"expiration" { return EXPIRATION; }
-"helper" { return HELPER; }
-"helpers" { return HELPERS; }
-"label" { return LABEL; }
-"state" { return STATE; }
-"status" { return STATUS; }
-
-"numgen" { return NUMGEN; }
-"inc" { return INC; }
-"mod" { return MOD; }
-"offset" { return OFFSET; }
-
-"jhash" { return JHASH; }
-"symhash" { return SYMHASH; }
-"seed" { return SEED; }
-
-"dup" { return DUP; }
-"fwd" { return FWD; }
-
-"fib" { return FIB; }
-
-"osf" { return OSF; }
-
-"synproxy" { return SYNPROXY; }
-"mss" { return MSS; }
-"wscale" { return WSCALE; }
-"sack-perm" { return SACKPERM; }
+<SCANSTATE_EXPR_RT>{
+ "nexthop" { return NEXTHOP; }
+ "seg-left" { return SEG_LEFT; }
+ "mtu" { return MTU; }
+ "last-entry" { return LAST_ENT; }
+ "tag" { return TAG; }
+ "sid" { return SID; }
+}
+<SCANSTATE_EXPR_RT,SCANSTATE_TYPE>{
+ "classid" { return CLASSID; }
+}
+
+"ct" { scanner_push_start_cond(yyscanner, SCANSTATE_CT); return CT; }
+<SCANSTATE_CT>{
+ "avgpkt" { return AVGPKT; }
+ "l3proto" { return L3PROTOCOL; }
+ "proto-src" { return PROTO_SRC; }
+ "proto-dst" { return PROTO_DST; }
+ "zone" { return ZONE; }
+ "original" { return ORIGINAL; }
+ "reply" { return REPLY; }
+ "direction" { return DIRECTION; }
+ "event" { return EVENT; }
+ "expectation" { return EXPECTATION; }
+ "expiration" { return EXPIRATION; }
+ "helper" { return HELPER; }
+ "helpers" { return HELPERS; }
+ "label" { return LABEL; }
+ "state" { return STATE; }
+ "status" { return STATUS; }
+ "count" { return COUNT; }
+}
+
+"numgen" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_NUMGEN); return NUMGEN; }
+<SCANSTATE_EXPR_NUMGEN>{
+ "inc" { return INC; }
+}
+
+"jhash" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return JHASH; }
+"symhash" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return SYMHASH; }
+
+<SCANSTATE_EXPR_HASH>{
+ "seed" { return SEED; }
+}
+<SCANSTATE_EXPR_HASH,SCANSTATE_EXPR_NUMGEN>{
+ "mod" { return MOD; }
+ "offset" { return OFFSET; }
+}
+"dup" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_DUP); return DUP; }
+"fwd" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_FWD); return FWD; }
+
+"fib" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; }
+
+"osf" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; }
+
+"synproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; }
+<SCANSTATE_STMT_SYNPROXY>{
+ "wscale" { return WSCALE; }
+ "maxseg" { return MSS; }
+ "mss" { return MSS; }
+ "timestamp" { return TIMESTAMP; }
+ "sack-permitted" { return SACK_PERM; }
+ "sack-perm" { return SACK_PERM; }
+}
"notrack" { return NOTRACK; }
-"options" { return OPTIONS; }
"all" { return ALL; }
-"xml" { return XML; }
-"json" { return JSON; }
-"vm" { return VM; }
+<SCANSTATE_CMD_EXPORT,SCANSTATE_CMD_IMPORT,SCANSTATE_CMD_MONITOR>{
+ "xml" { return XML; }
+ "json" { return JSON; }
+ "vm" { return VM; }
+}
"exists" { return EXISTS; }
"missing" { return MISSING; }
"exthdr" { return EXTHDR; }
-"ipsec" { return IPSEC; }
-"mode" { return MODE; }
-"reqid" { return REQID; }
-"spnum" { return SPNUM; }
-"transport" { return TRANSPORT; }
-"tunnel" { return TUNNEL; }
+"ipsec" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_IPSEC); return IPSEC; }
+<SCANSTATE_EXPR_IPSEC>{
+ "reqid" { return REQID; }
+ "spnum" { return SPNUM; }
+
+ "in" { return IN; }
+ "out" { return OUT; }
+}
-"in" { return IN; }
-"out" { return OUT; }
+"secmark" { scanner_push_start_cond(yyscanner, SCANSTATE_SECMARK); return SECMARK; }
-"secmark" { return SECMARK; }
-"secmarks" { return SECMARKS; }
+"xt" { scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; }
{addrstring} {
yylval->string = xstrdup(yytext);
@@ -611,9 +846,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;
@@ -621,6 +856,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;
@@ -653,6 +901,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
{tab}+
{space}+
+{comment_line} {
+ reset_pos(yyget_extra(yyscanner), yylloc);
+ }
{comment}
<<EOF>> {
@@ -724,9 +975,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;
@@ -738,12 +1039,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:
@@ -804,7 +1117,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--) {
@@ -816,7 +1129,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;
}
@@ -853,7 +1166,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)
@@ -925,7 +1238,7 @@ void scanner_push_buffer(void *scanner, const struct input_descriptor *indesc,
new_indesc = xzalloc(sizeof(struct input_descriptor));
memcpy(new_indesc, indesc, sizeof(*new_indesc));
new_indesc->data = buffer;
- new_indesc->name = NULL;
+ new_indesc->name = xstrdup(indesc->name);
scanner_push_indesc(state, new_indesc);
b = yy_scan_string(buffer, scanner);
@@ -940,14 +1253,16 @@ void *scanner_init(struct parser_state *state)
yylex_init_extra(state, &scanner);
yyset_out(NULL, scanner);
+ state->startcond_active = xzalloc_array(__SC_MAX,
+ sizeof(*state->startcond_active));
return scanner;
}
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)
@@ -969,5 +1284,43 @@ void scanner_destroy(struct nft_ctx *nft)
struct parser_state *state = yyget_extra(nft->scanner);
input_descriptor_list_destroy(state);
+ free(state->startcond_active);
+
yylex_destroy(nft->scanner);
}
+
+static void scanner_push_start_cond(void *scanner, enum startcond_type type)
+{
+ struct parser_state *state = yyget_extra(scanner);
+
+ state->startcond_type = type;
+ state->startcond_active[type]++;
+
+ yy_push_state((int)type, scanner);
+}
+
+void scanner_pop_start_cond(void *scanner, enum startcond_type t)
+{
+ struct parser_state *state = yyget_extra(scanner);
+
+ state->startcond_active[t]--;
+
+ if (state->startcond_type != t) {
+ state->flex_state_pop++;
+ return; /* Can't pop just yet! */
+ }
+
+ while (state->flex_state_pop) {
+ state->flex_state_pop--;
+ state->startcond_type = yy_top_state(scanner);
+ yy_pop_state(scanner);
+
+ t = state->startcond_type;
+ if (state->startcond_active[t])
+ return;
+ }
+
+ state->startcond_type = yy_top_state(scanner);
+
+ yy_pop_state(scanner);
+}
diff --git a/src/sctp_chunk.c b/src/sctp_chunk.c
new file mode 100644
index 00000000..24a07e20
--- /dev/null
+++ b/src/sctp_chunk.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright Red Hat
+ *
+ * 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 <exthdr.h>
+#include <sctp_chunk.h>
+
+
+#define PHT(__token, __offset, __len) \
+ PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \
+ __offset, __len)
+
+static const struct exthdr_desc sctp_chunk_data = {
+ .name = "data",
+ .type = SCTP_CHUNK_TYPE_DATA,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_DATA_TSN] = PHT("tsn", 32, 32),
+ [SCTP_CHUNK_DATA_STREAM] = PHT("stream", 64, 16),
+ [SCTP_CHUNK_DATA_SSN] = PHT("ssn", 80, 16),
+ [SCTP_CHUNK_DATA_PPID] = PHT("ppid", 96, 32),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_init = {
+ .name = "init",
+ .type = SCTP_CHUNK_TYPE_INIT,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_INIT_TAG] = PHT("init-tag", 32, 32),
+ [SCTP_CHUNK_INIT_RWND] = PHT("a-rwnd", 64, 32),
+ [SCTP_CHUNK_INIT_OSTREAMS] = PHT("num-outbound-streams", 96, 16),
+ [SCTP_CHUNK_INIT_ISTREAMS] = PHT("num-inbound-streams", 112, 16),
+ [SCTP_CHUNK_INIT_TSN] = PHT("initial-tsn", 128, 32),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_init_ack = {
+ .name = "init-ack",
+ .type = SCTP_CHUNK_TYPE_INIT_ACK,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_INIT_TAG] = PHT("init-tag", 32, 32),
+ [SCTP_CHUNK_INIT_RWND] = PHT("a-rwnd", 64, 32),
+ [SCTP_CHUNK_INIT_OSTREAMS] = PHT("num-outbound-streams", 96, 16),
+ [SCTP_CHUNK_INIT_ISTREAMS] = PHT("num-inbound-streams", 112, 16),
+ [SCTP_CHUNK_INIT_TSN] = PHT("initial-tsn", 128, 32),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_sack = {
+ .name = "sack",
+ .type = SCTP_CHUNK_TYPE_SACK,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_SACK_CTSN_ACK] = PHT("cum-tsn-ack", 32, 32),
+ [SCTP_CHUNK_SACK_RWND] = PHT("a-rwnd", 64, 32),
+ [SCTP_CHUNK_SACK_GACK_BLOCKS] = PHT("num-gap-ack-blocks", 96, 16),
+ [SCTP_CHUNK_SACK_DUP_TSNS] = PHT("num-dup-tsns", 112, 16),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_shutdown = {
+ .name = "shutdown",
+ .type = SCTP_CHUNK_TYPE_SHUTDOWN,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_SHUTDOWN_CTSN_ACK] = PHT("cum-tsn-ack", 32, 32),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_ecne = {
+ .name = "ecne",
+ .type = SCTP_CHUNK_TYPE_ECNE,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_ECNE_CWR_MIN_TSN] = PHT("lowest-tsn", 32, 32),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_cwr = {
+ .name = "cwr",
+ .type = SCTP_CHUNK_TYPE_CWR,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_ECNE_CWR_MIN_TSN] = PHT("lowest-tsn", 32, 32),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_asconf_ack = {
+ .name = "asconf-ack",
+ .type = SCTP_CHUNK_TYPE_ASCONF_ACK,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_ASCONF_SEQNO] = PHT("seqno", 32, 32),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_forward_tsn = {
+ .name = "forward-tsn",
+ .type = SCTP_CHUNK_TYPE_FORWARD_TSN,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_FORWARD_TSN_NCTSN] = PHT("new-cum-tsn", 32, 32),
+ },
+};
+
+static const struct exthdr_desc sctp_chunk_asconf = {
+ .name = "asconf",
+ .type = SCTP_CHUNK_TYPE_ASCONF,
+ .templates = {
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8),
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8),
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),
+ [SCTP_CHUNK_ASCONF_SEQNO] = PHT("seqno", 32, 32),
+ },
+};
+
+#define SCTP_CHUNK_DESC_GENERATOR(descname, hname, desctype) \
+static const struct exthdr_desc sctp_chunk_##descname = { \
+ .name = #hname, \
+ .type = SCTP_CHUNK_TYPE_##desctype, \
+ .templates = { \
+ [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), \
+ [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), \
+ [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),\
+ }, \
+};
+
+SCTP_CHUNK_DESC_GENERATOR(heartbeat, heartbeat, HEARTBEAT)
+SCTP_CHUNK_DESC_GENERATOR(heartbeat_ack, heartbeat-ack, HEARTBEAT_ACK)
+SCTP_CHUNK_DESC_GENERATOR(abort, abort, ABORT)
+SCTP_CHUNK_DESC_GENERATOR(shutdown_ack, shutdown-ack, SHUTDOWN_ACK)
+SCTP_CHUNK_DESC_GENERATOR(error, error, ERROR)
+SCTP_CHUNK_DESC_GENERATOR(cookie_echo, cookie-echo, COOKIE_ECHO)
+SCTP_CHUNK_DESC_GENERATOR(cookie_ack, cookie-ack, COOKIE_ACK)
+SCTP_CHUNK_DESC_GENERATOR(shutdown_complete, shutdown-complete, SHUTDOWN_COMPLETE)
+
+#undef SCTP_CHUNK_DESC_GENERATOR
+
+static const struct exthdr_desc *sctp_chunk_protocols[] = {
+ [SCTP_CHUNK_TYPE_DATA] = &sctp_chunk_data,
+ [SCTP_CHUNK_TYPE_INIT] = &sctp_chunk_init,
+ [SCTP_CHUNK_TYPE_INIT_ACK] = &sctp_chunk_init_ack,
+ [SCTP_CHUNK_TYPE_SACK] = &sctp_chunk_sack,
+ [SCTP_CHUNK_TYPE_HEARTBEAT] = &sctp_chunk_heartbeat,
+ [SCTP_CHUNK_TYPE_HEARTBEAT_ACK] = &sctp_chunk_heartbeat_ack,
+ [SCTP_CHUNK_TYPE_ABORT] = &sctp_chunk_abort,
+ [SCTP_CHUNK_TYPE_SHUTDOWN] = &sctp_chunk_shutdown,
+ [SCTP_CHUNK_TYPE_SHUTDOWN_ACK] = &sctp_chunk_shutdown_ack,
+ [SCTP_CHUNK_TYPE_ERROR] = &sctp_chunk_error,
+ [SCTP_CHUNK_TYPE_COOKIE_ECHO] = &sctp_chunk_cookie_echo,
+ [SCTP_CHUNK_TYPE_COOKIE_ACK] = &sctp_chunk_cookie_ack,
+ [SCTP_CHUNK_TYPE_ECNE] = &sctp_chunk_ecne,
+ [SCTP_CHUNK_TYPE_CWR] = &sctp_chunk_cwr,
+ [SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE] = &sctp_chunk_shutdown_complete,
+ [SCTP_CHUNK_TYPE_ASCONF_ACK] = &sctp_chunk_asconf_ack,
+ [SCTP_CHUNK_TYPE_FORWARD_TSN] = &sctp_chunk_forward_tsn,
+ [SCTP_CHUNK_TYPE_ASCONF] = &sctp_chunk_asconf,
+};
+
+const struct exthdr_desc *sctp_chunk_protocol_find(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < array_size(sctp_chunk_protocols); i++) {
+ if (sctp_chunk_protocols[i] &&
+ !strcmp(sctp_chunk_protocols[i]->name, name))
+ return sctp_chunk_protocols[i];
+ }
+ return NULL;
+}
+
+struct expr *sctp_chunk_expr_alloc(const struct location *loc,
+ unsigned int type, unsigned int field)
+{
+ const struct proto_hdr_template *tmpl;
+ const struct exthdr_desc *desc = NULL;
+ struct expr *expr;
+
+ if (type < array_size(sctp_chunk_protocols))
+ desc = sctp_chunk_protocols[type];
+
+ if (!desc)
+ return NULL;
+
+ tmpl = &desc->templates[field];
+ if (!tmpl)
+ 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_SCTP;
+ expr->exthdr.raw_type = desc->type;
+ expr->exthdr.offset = tmpl->offset;
+
+ return expr;
+}
+
+void sctp_chunk_init_raw(struct expr *expr, uint8_t type, unsigned int off,
+ unsigned int len, uint32_t flags)
+{
+ const struct proto_hdr_template *tmpl;
+ unsigned int i;
+
+ assert(expr->etype == EXPR_EXTHDR);
+
+ expr->len = len;
+ expr->exthdr.flags = flags;
+ expr->exthdr.offset = off;
+ expr->exthdr.op = NFT_EXTHDR_OP_SCTP;
+
+ if (flags & NFT_EXTHDR_F_PRESENT)
+ datatype_set(expr, &boolean_type);
+ else
+ datatype_set(expr, &integer_type);
+
+ if (type >= array_size(sctp_chunk_protocols))
+ return;
+
+ expr->exthdr.desc = sctp_chunk_protocols[type];
+ expr->exthdr.flags = flags;
+ assert(expr->exthdr.desc != NULL);
+
+ for (i = 0; i < array_size(expr->exthdr.desc->templates); ++i) {
+ tmpl = &expr->exthdr.desc->templates[i];
+ if (tmpl->offset != off || tmpl->len != len)
+ continue;
+
+ if ((flags & NFT_EXTHDR_F_PRESENT) == 0)
+ datatype_set(expr, tmpl->dtype);
+
+ expr->exthdr.tmpl = tmpl;
+ break;
+ }
+}
diff --git a/src/segtree.c b/src/segtree.c
index 3a641bc5..5e6f857f 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>
@@ -19,619 +19,38 @@
#include <expression.h>
#include <gmputil.h>
#include <utils.h>
-#include <rbtree.h>
-
-/**
- * struct seg_tree - segment tree
- *
- * @root: the rbtree's root
- * @type: the datatype of the dimension
- * @dwidth: width of the dimension
- * @byteorder: byteorder of elements
- * @debug_mask: display debugging information
- */
-struct seg_tree {
- struct rb_root root;
- const struct datatype *keytype;
- unsigned int keylen;
- const struct datatype *datatype;
- unsigned int datalen;
- enum byteorder byteorder;
- unsigned int debug_mask;
-};
-
-enum elementary_interval_flags {
- EI_F_INTERVAL_END = 0x1,
- EI_F_INTERVAL_OPEN = 0x2,
-};
-
-/**
- * struct elementary_interval - elementary interval [left, right]
- *
- * @rb_node: seg_tree rb node
- * @list: list node for linearized tree
- * @left: left endpoint
- * @right: right endpoint
- * @size: interval size (right - left)
- * @flags: flags
- * @expr: associated expression
- */
-struct elementary_interval {
- union {
- struct rb_node rb_node;
- struct list_head list;
- };
-
- mpz_t left;
- mpz_t right;
- mpz_t size;
-
- enum elementary_interval_flags flags;
- struct expr *expr;
-};
-
-static void seg_tree_init(struct seg_tree *tree, const struct set *set,
- struct expr *init, unsigned int debug_mask)
-{
- struct expr *first;
-
- first = list_entry(init->expressions.next, struct expr, list);
- tree->root = RB_ROOT;
- tree->keytype = set->key->dtype;
- tree->keylen = set->key->len;
- tree->datatype = NULL;
- tree->datalen = 0;
- if (set->data) {
- tree->datatype = set->data->dtype;
- tree->datalen = set->data->len;
- }
- tree->byteorder = first->byteorder;
- tree->debug_mask = debug_mask;
-}
-
-static struct elementary_interval *ei_alloc(const mpz_t left, const mpz_t right,
- struct expr *expr,
- enum elementary_interval_flags flags)
-{
- struct elementary_interval *ei;
-
- ei = xzalloc(sizeof(*ei));
- mpz_init_set(ei->left, left);
- mpz_init_set(ei->right, right);
- mpz_init(ei->size);
- mpz_sub(ei->size, right, left);
- if (expr != NULL)
- ei->expr = expr_get(expr);
- ei->flags = flags;
- return ei;
-}
-
-static void ei_destroy(struct elementary_interval *ei)
-{
- mpz_clear(ei->left);
- mpz_clear(ei->right);
- mpz_clear(ei->size);
- if (ei->expr != NULL)
- expr_free(ei->expr);
- xfree(ei);
-}
-
-/**
- * ei_lookup - find elementary interval containing point p
- *
- * @tree: segment tree
- * @p: the point
- */
-static struct elementary_interval *ei_lookup(struct seg_tree *tree, const mpz_t p)
-{
- struct rb_node *n = tree->root.rb_node;
- struct elementary_interval *ei;
-
- while (n != NULL) {
- ei = rb_entry(n, struct elementary_interval, rb_node);
-
- if (mpz_cmp(p, ei->left) >= 0 &&
- mpz_cmp(p, ei->right) <= 0)
- return ei;
- else if (mpz_cmp(p, ei->left) <= 0)
- n = n->rb_left;
- else if (mpz_cmp(p, ei->right) > 0)
- n = n->rb_right;
- }
- return NULL;
-}
-
-static void ei_remove(struct seg_tree *tree, struct elementary_interval *ei)
-{
- rb_erase(&ei->rb_node, &tree->root);
-}
-
-static void __ei_insert(struct seg_tree *tree, struct elementary_interval *new)
-{
- struct rb_node **p = &tree->root.rb_node;
- struct rb_node *parent = NULL;
- struct elementary_interval *ei;
-
- while (*p != NULL) {
- parent = *p;
- ei = rb_entry(parent, struct elementary_interval, rb_node);
-
- if (mpz_cmp(new->left, ei->left) >= 0 &&
- mpz_cmp(new->left, ei->right) <= 0)
- break;
- else if (mpz_cmp(new->left, ei->left) <= 0)
- p = &(*p)->rb_left;
- else if (mpz_cmp(new->left, ei->left) > 0)
- p = &(*p)->rb_right;
- }
-
- rb_link_node(&new->rb_node, parent, p);
- rb_insert_color(&new->rb_node, &tree->root);
-}
-
-static bool segtree_debug(unsigned int debug_mask)
-{
- if (debug_mask & NFT_DEBUG_SEGTREE)
- return true;
-
- return false;
-}
-
-/**
- * ei_insert - insert an elementary interval into the tree
- *
- * @tree: the seg_tree
- * @new: the elementary interval
- *
- * New entries take precedence over existing ones. Insertions are assumed to
- * be ordered by descending interval size, meaning the new interval never
- * extends over more than two existing intervals.
- */
-static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
- struct elementary_interval *new, bool merge)
-{
- struct elementary_interval *lei, *rei, *ei;
- struct expr *new_expr, *expr;
- mpz_t p;
-
- mpz_init2(p, tree->keylen);
-
- /*
- * Lookup the intervals containing the left and right endpoints.
- */
- lei = ei_lookup(tree, new->left);
- rei = ei_lookup(tree, new->right);
-
- if (segtree_debug(tree->debug_mask))
- pr_gmp_debug("insert: [%Zx %Zx]\n", new->left, new->right);
-
- if (lei != NULL && rei != NULL && lei == rei) {
- if (!merge) {
- ei = lei;
- goto err;
- }
- /*
- * The new interval is entirely contained in the same interval,
- * split it into two parts:
- *
- * [lei_left, new_left) and (new_right, rei_right]
- */
- if (segtree_debug(tree->debug_mask))
- pr_gmp_debug("split [%Zx %Zx]\n", lei->left, lei->right);
-
- ei_remove(tree, lei);
-
- mpz_sub_ui(p, new->left, 1);
- if (mpz_cmp(lei->left, p) <= 0)
- __ei_insert(tree, ei_alloc(lei->left, p, lei->expr, 0));
-
- mpz_add_ui(p, new->right, 1);
- if (mpz_cmp(p, rei->right) < 0)
- __ei_insert(tree, ei_alloc(p, rei->right, lei->expr, 0));
- ei_destroy(lei);
- } else {
- if (lei != NULL) {
- if (!merge) {
- ei = lei;
- goto err;
- }
- /*
- * Left endpoint is within lei, adjust it so we have:
- *
- * [lei_left, new_left)[new_left, new_right]
- */
- if (segtree_debug(tree->debug_mask)) {
- pr_gmp_debug("adjust left [%Zx %Zx]\n",
- lei->left, lei->right);
- }
-
- mpz_sub_ui(lei->right, new->left, 1);
- mpz_sub(lei->size, lei->right, lei->left);
- if (mpz_sgn(lei->size) < 0) {
- ei_remove(tree, lei);
- ei_destroy(lei);
- }
- }
- if (rei != NULL) {
- if (!merge) {
- ei = rei;
- goto err;
- }
- /*
- * Right endpoint is within rei, adjust it so we have:
- *
- * [new_left, new_right](new_right, rei_right]
- */
- if (segtree_debug(tree->debug_mask)) {
- pr_gmp_debug("adjust right [%Zx %Zx]\n",
- rei->left, rei->right);
- }
-
- mpz_add_ui(rei->left, new->right, 1);
- mpz_sub(rei->size, rei->right, rei->left);
- if (mpz_sgn(rei->size) < 0) {
- ei_remove(tree, rei);
- ei_destroy(rei);
- }
- }
- }
-
- __ei_insert(tree, new);
-
- mpz_clear(p);
-
- return 0;
-err:
- errno = EEXIST;
- if (new->expr->etype == EXPR_MAPPING) {
- new_expr = new->expr->left;
- expr = ei->expr->left;
- } else {
- new_expr = new->expr;
- expr = ei->expr;
- }
-
- return expr_binary_error(msgs, new_expr, expr,
- "conflicting intervals specified");
-}
-
-/*
- * Sort intervals according to their priority, which is defined inversely to
- * their size.
- *
- * The beginning of the interval is used as secondary sorting criterion. This
- * makes sure that overlapping ranges with equal priority are next to each
- * other, allowing to easily detect unsolvable conflicts during insertion.
- *
- * Note: unsolvable conflicts can only occur when using ranges or two identical
- * prefix specifications.
- */
-static int interval_cmp(const void *p1, const void *p2)
-{
- const struct elementary_interval *e1 = *(void * const *)p1;
- const struct elementary_interval *e2 = *(void * const *)p2;
- mpz_t d;
- int ret;
-
- mpz_init(d);
-
- mpz_sub(d, e2->size, e1->size);
- if (mpz_cmp_ui(d, 0))
- ret = mpz_sgn(d);
- else
- ret = mpz_cmp(e1->left, e2->left);
-
- mpz_clear(d);
- return ret;
-}
-
-static unsigned int expr_to_intervals(const struct expr *set,
- unsigned int keylen,
- struct elementary_interval **intervals)
-{
- struct elementary_interval *ei;
- struct expr *i, *next;
- unsigned int n;
- mpz_t low, high;
-
- mpz_init2(low, keylen);
- mpz_init2(high, keylen);
-
- /*
- * Convert elements to intervals.
- */
- n = 0;
- list_for_each_entry_safe(i, next, &set->expressions, list) {
- range_expr_value_low(low, i);
- range_expr_value_high(high, i);
- ei = ei_alloc(low, high, i, 0);
- intervals[n++] = ei;
- }
- mpz_clear(high);
- mpz_clear(low);
-
- return n;
-}
-
-static bool intervals_match(const struct elementary_interval *e1,
- const struct elementary_interval *e2)
-{
- return mpz_cmp(e1->left, e2->left) == 0 &&
- mpz_cmp(e1->right, e2->right) == 0;
-}
-/* This function checks for overlaps in two ways:
- *
- * 1) A new interval end intersects an existing interval.
- * 2) New intervals that are larger than existing ones, that don't intersect
- * at all, but that wrap the existing ones.
- */
-static bool interval_overlap(const struct elementary_interval *e1,
- const struct elementary_interval *e2)
+static enum byteorder get_key_byteorder(const struct expr *e)
{
- if (intervals_match(e1, e2))
- return false;
-
- return (mpz_cmp(e1->left, e2->left) >= 0 &&
- mpz_cmp(e1->left, e2->right) <= 0) ||
- (mpz_cmp(e1->right, e2->left) >= 0 &&
- mpz_cmp(e1->right, e2->right) <= 0) ||
- (mpz_cmp(e1->left, e2->left) <= 0 &&
- mpz_cmp(e1->right, e2->right) >= 0);
-}
-
-static int set_overlap(struct list_head *msgs, const struct set *set,
- struct expr *init, unsigned int keylen, bool add)
-{
- struct elementary_interval *new_intervals[init->size + 1];
- struct elementary_interval *intervals[set->init->size + 1];
- unsigned int n, m, i, j;
- int ret = 0;
-
- n = expr_to_intervals(init, keylen, new_intervals);
- m = expr_to_intervals(set->init, keylen, intervals);
-
- for (i = 0; i < n; i++) {
- bool found = false;
-
- for (j = 0; j < m; j++) {
- if (add && interval_overlap(new_intervals[i],
- intervals[j])) {
- expr_error(msgs, new_intervals[i]->expr,
- "interval overlaps with an existing one");
- errno = EEXIST;
- ret = -1;
- goto out;
- } else if (!add && intervals_match(new_intervals[i],
- intervals[j])) {
- found = true;
- break;
- }
- }
- if (!add && !found) {
- expr_error(msgs, new_intervals[i]->expr,
- "interval not found in set");
- errno = ENOENT;
- ret = -1;
- break;
- }
- }
-out:
- for (i = 0; i < n; i++)
- ei_destroy(new_intervals[i]);
- for (i = 0; i < m; i++)
- ei_destroy(intervals[i]);
-
- return ret;
-}
-
-static int set_to_segtree(struct list_head *msgs, struct set *set,
- struct expr *init, struct seg_tree *tree,
- bool add, bool merge)
-{
- struct elementary_interval *intervals[init->size];
- struct expr *i, *next;
- unsigned int n;
- int err;
-
- /* We are updating an existing set with new elements, check if the new
- * interval overlaps with any of the existing ones.
- */
- if (set->init && set->init != init) {
- err = set_overlap(msgs, set, init, tree->keylen, add);
- if (err < 0)
- return err;
- }
-
- n = expr_to_intervals(init, tree->keylen, intervals);
-
- list_for_each_entry_safe(i, next, &init->expressions, list) {
- list_del(&i->list);
- expr_free(i);
- }
-
- /*
- * Sort intervals by priority.
- */
- qsort(intervals, n, sizeof(intervals[0]), interval_cmp);
-
- /*
- * Insert elements into tree
- */
- for (n = 0; n < init->size; n++) {
- err = ei_insert(msgs, tree, intervals[n], merge);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static bool segtree_needs_first_segment(const struct set *set,
- const struct expr *init, bool add)
-{
- if (add && !set->root) {
- /* Add the first segment in four situations:
- *
- * 1) This is an anonymous set.
- * 2) This set exists and it is empty.
- * 3) New empty set and, separately, new elements are added.
- * 4) This set is created with a number of initial elements.
+ enum datatypes basetype = expr_basetype(e)->type;
+
+ switch (basetype) {
+ case TYPE_INTEGER:
+ /* For ranges, integers MUST be in BYTEORDER_BIG_ENDIAN.
+ * If the LHS (lookup key, e.g. 'meta mark', is host endian,
+ * a byteorder expression is injected to convert the register
+ * content before lookup.
*/
- if ((set_is_anonymous(set->flags)) ||
- (set->init && set->init->size == 0) ||
- (set->init == NULL && init) ||
- (set->init == init)) {
- return true;
- }
- }
- /* This is an update for a set that already contains elements, so don't
- * add the first non-matching elements otherwise we hit EEXIST.
- */
- return false;
-}
-
-static void segtree_linearize(struct list_head *list, const struct set *set,
- const struct expr *init, struct seg_tree *tree,
- bool add, bool merge)
-{
- bool needs_first_segment = segtree_needs_first_segment(set, init, add);
- struct elementary_interval *ei, *nei, *prev = NULL;
- struct rb_node *node, *next;
- mpz_t p, q;
-
- mpz_init2(p, tree->keylen);
- mpz_init2(q, tree->keylen);
-
- /*
- * Convert the tree of open intervals to half-closed map expressions.
- */
- rb_for_each_entry_safe(ei, node, next, &tree->root, rb_node) {
- if (segtree_debug(tree->debug_mask))
- pr_gmp_debug("iter: [%Zx %Zx]\n", ei->left, ei->right);
-
- if (prev == NULL) {
- /*
- * If the first segment doesn't begin at zero, insert a
- * non-matching segment to cover [0, first_left).
- */
- if (needs_first_segment && mpz_cmp_ui(ei->left, 0)) {
- mpz_set_ui(p, 0);
- mpz_sub_ui(q, ei->left, 1);
- nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
- list_add_tail(&nei->list, list);
- }
- } else {
- /*
- * If the previous segment doesn't end directly left to
- * this one, insert a non-matching segment to cover
- * (prev_right, ei_left).
- */
- mpz_add_ui(p, prev->right, 1);
- if (mpz_cmp(p, ei->left) < 0 ||
- (!(set->flags & NFT_SET_ANONYMOUS) && !merge)) {
- mpz_sub_ui(q, ei->left, 1);
- nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
- list_add_tail(&nei->list, list);
- } else if (add && merge &&
- ei->expr->etype != EXPR_MAPPING) {
- /* Merge contiguous segments only in case of
- * new additions.
- */
- mpz_set(prev->right, ei->right);
- ei_remove(tree, ei);
- ei_destroy(ei);
- continue;
- }
- }
-
- ei_remove(tree, ei);
- list_add_tail(&ei->list, list);
-
- prev = ei;
- }
-
- /*
- * If the last segment doesn't end at the right side of the dimension,
- * insert a non-matching segment to cover (last_right, end].
- */
- if (mpz_scan0(prev->right, 0) != tree->keylen) {
- mpz_add_ui(p, prev->right, 1);
- mpz_bitmask(q, tree->keylen);
- nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
- list_add_tail(&nei->list, list);
- } else {
- prev->flags |= EI_F_INTERVAL_OPEN;
- }
-
- mpz_clear(p);
- mpz_clear(q);
-}
-
-static void set_insert_interval(struct expr *set, struct seg_tree *tree,
- const struct elementary_interval *ei)
-{
- struct expr *expr;
-
- expr = constant_expr_alloc(&internal_location, tree->keytype,
- tree->byteorder, tree->keylen, NULL);
- mpz_set(expr->value, ei->left);
- expr = set_elem_expr_alloc(&internal_location, expr);
-
- if (ei->expr != NULL) {
- if (ei->expr->etype == EXPR_MAPPING) {
- if (ei->expr->left->comment)
- expr->comment = xstrdup(ei->expr->left->comment);
- if (ei->expr->left->timeout)
- expr->timeout = ei->expr->left->timeout;
- expr = mapping_expr_alloc(&ei->expr->location, expr,
- expr_get(ei->expr->right));
- } else {
- if (ei->expr->comment)
- expr->comment = xstrdup(ei->expr->comment);
- if (ei->expr->timeout)
- expr->timeout = ei->expr->timeout;
- }
+ return BYTEORDER_BIG_ENDIAN;
+ case TYPE_STRING:
+ return BYTEORDER_HOST_ENDIAN;
+ default:
+ break;
}
- if (ei->flags & EI_F_INTERVAL_END)
- expr->flags |= EXPR_F_INTERVAL_END;
- if (ei->flags & EI_F_INTERVAL_OPEN)
- expr->elem_flags |= NFTNL_SET_ELEM_F_INTERVAL_OPEN;
-
- compound_expr_add(set, expr);
+ return BYTEORDER_INVALID;
}
-int set_to_intervals(struct list_head *errs, struct set *set,
- struct expr *init, bool add, unsigned int debug_mask,
- bool merge, struct output_ctx *octx)
+static void interval_expr_copy(struct expr *dst, struct expr *src)
{
- struct elementary_interval *ei, *next;
- struct seg_tree tree;
- LIST_HEAD(list);
-
- seg_tree_init(&tree, set, init, debug_mask);
- if (set_to_segtree(errs, set, init, &tree, add, merge) < 0)
- return -1;
- segtree_linearize(&list, set, init, &tree, add, merge);
-
- init->size = 0;
- list_for_each_entry_safe(ei, next, &list, list) {
- if (segtree_debug(tree.debug_mask)) {
- pr_gmp_debug("list: [%.*Zx %.*Zx]\n",
- 2 * tree.keylen / BITS_PER_BYTE, ei->left,
- 2 * tree.keylen / BITS_PER_BYTE, ei->right);
- }
- set_insert_interval(init, &tree, ei);
- ei_destroy(ei);
- }
-
- if (segtree_debug(tree.debug_mask)) {
- expr_print(init, octx);
- pr_gmp_debug("\n");
- }
-
- return 0;
+ if (src->comment)
+ dst->comment = xstrdup(src->comment);
+ if (src->timeout)
+ dst->timeout = src->timeout;
+ if (src->expiration)
+ dst->expiration = src->expiration;
+
+ list_splice_init(&src->stmt_list, &dst->stmt_list);
}
static void set_elem_add(const struct set *set, struct expr *init, mpz_t value,
@@ -650,6 +69,7 @@ static void set_elem_add(const struct set *set, struct expr *init, mpz_t value,
struct expr *get_set_intervals(const struct set *set, const struct expr *init)
{
+ enum byteorder byteorder = get_key_byteorder(set->key);
struct expr *new_init;
mpz_t low, high;
struct expr *i;
@@ -663,13 +83,16 @@ struct expr *get_set_intervals(const struct set *set, const struct expr *init)
switch (i->key->etype) {
case EXPR_VALUE:
set_elem_add(set, new_init, i->key->value,
- i->flags, i->byteorder);
+ i->flags, byteorder);
break;
case EXPR_CONCAT:
compound_expr_add(new_init, expr_clone(i));
i->flags |= EXPR_F_INTERVAL_END;
compound_expr_add(new_init, expr_clone(i));
break;
+ case EXPR_SET_ELEM_CATCHALL:
+ compound_expr_add(new_init, expr_clone(i));
+ break;
default:
range_expr_value_low(low, i);
set_elem_add(set, new_init, low, 0, i->byteorder);
@@ -700,6 +123,11 @@ static struct expr *get_set_interval_find(const struct set *cache_set,
list_for_each_entry(i, &set->init->expressions, list) {
switch (i->key->etype) {
+ case EXPR_VALUE:
+ if (expr_basetype(i->key)->type != TYPE_STRING)
+ break;
+ /* string type, check if its a range (wildcard). */
+ /* fall-through */
case EXPR_PREFIX:
case EXPR_RANGE:
range_expr_value_low(val, i);
@@ -722,6 +150,62 @@ 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;
+ case EXPR_VALUE:
+ return expr;
+ 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);
+
+ if (low->etype == EXPR_MAPPING) {
+ interval_expr_copy(elem, low->left);
+
+ elem = mapping_expr_alloc(&low->location, elem,
+ expr_clone(low->right));
+ } else {
+ interval_expr_copy(elem, low);
+ }
+ elem->flags |= EXPR_F_KERNEL;
+
+ return elem;
+}
+
+static struct expr *expr_to_set_elem(struct expr *e)
+{
+ unsigned int len = div_round_up(e->len, BITS_PER_BYTE);
+ unsigned int str_len;
+ char data[len + 1];
+ struct expr *expr;
+
+ if (expr_basetype(expr_value(e))->type != TYPE_STRING)
+ return expr_clone(e);
+
+ mpz_export_data(data, expr_value(e)->value, BYTEORDER_BIG_ENDIAN, len);
+
+ str_len = strnlen(data, len);
+ if (str_len >= len || str_len == 0)
+ return expr_clone(e);
+
+ data[str_len] = '*';
+
+ expr = constant_expr_alloc(&e->location, e->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (str_len + 1) * BITS_PER_BYTE, data);
+
+ return __expr_to_set_elem(e, expr);
+}
+
int get_set_decompose(struct set *cache_set, struct set *set)
{
struct expr *i, *next, *range;
@@ -737,6 +221,8 @@ int get_set_decompose(struct set *cache_set, struct set *set)
mpz_sub_ui(i->key->value, i->key->value, 1);
range = get_set_interval_find(cache_set, left, i);
if (!range) {
+ expr_free(left);
+ expr_free(i);
expr_free(new_init);
errno = ENOENT;
return -1;
@@ -754,7 +240,7 @@ int get_set_decompose(struct set *cache_set, struct set *set)
compound_expr_add(new_init, range);
else
compound_expr_add(new_init,
- expr_clone(left));
+ expr_to_set_elem(left));
}
left = i;
}
@@ -764,7 +250,7 @@ int get_set_decompose(struct set *cache_set, struct set *set)
if (range)
compound_expr_add(new_init, range);
else
- compound_expr_add(new_init, expr_clone(left));
+ compound_expr_add(new_init, expr_to_set_elem(left));
}
expr_free(set->init);
@@ -786,18 +272,6 @@ static bool range_is_prefix(const mpz_t range)
return ret;
}
-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 int expr_value_cmp(const void *p1, const void *p2)
{
struct expr *e1 = *(void * const *)p1;
@@ -826,8 +300,8 @@ static int range_mask_len(const mpz_t start, const mpz_t end, unsigned int len)
mpz_t tmp_start, tmp_end;
int ret;
- mpz_init_set_ui(tmp_start, mpz_get_ui(start));
- mpz_init_set_ui(tmp_end, mpz_get_ui(end));
+ mpz_init_set(tmp_start, start);
+ mpz_init_set(tmp_end, end);
while (mpz_cmp(tmp_start, tmp_end) <= 0 &&
!mpz_tstbit(tmp_start, 0) && mpz_tstbit(tmp_end, 0) &&
@@ -872,6 +346,8 @@ void concat_range_aggregate(struct expr *set)
list_for_each_entry_safe(r1, r1_next,
&expr_value(start)->expressions,
list) {
+ bool string_type = false;
+
mpz_init(range);
mpz_init(p);
@@ -883,16 +359,48 @@ void concat_range_aggregate(struct expr *set)
goto next;
}
+ if (expr_basetype(r1)->type == TYPE_STRING &&
+ expr_basetype(r2)->type == TYPE_STRING) {
+ string_type = true;
+ mpz_switch_byteorder(r1->value, r1->len / BITS_PER_BYTE);
+ mpz_switch_byteorder(r2->value, r2->len / BITS_PER_BYTE);
+ }
+
mpz_sub(range, r2->value, r1->value);
mpz_sub_ui(range, range, 1);
mpz_and(p, r1->value, range);
/* Check if we are forced, or if it's anyway preferable,
- * to express the range as two points instead of a
- * netmask.
+ * to express the range as a wildcard string, or two points
+ * instead of a netmask.
*/
prefix_len = range_mask_len(r1->value, r2->value,
r1->len);
+ if (string_type) {
+ mpz_switch_byteorder(r1->value, r1->len / BITS_PER_BYTE);
+ mpz_switch_byteorder(r2->value, r2->len / BITS_PER_BYTE);
+ }
+
+ if (prefix_len >= 0 &&
+ (prefix_len % BITS_PER_BYTE) == 0 &&
+ string_type) {
+ unsigned int str_len = prefix_len / BITS_PER_BYTE;
+ char data[str_len + 2];
+
+ mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, str_len);
+ data[str_len] = '*';
+
+ tmp = constant_expr_alloc(&r1->location, r1->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (str_len + 1) * BITS_PER_BYTE, data);
+ tmp->len = r2->len;
+ list_replace(&r2->list, &tmp->list);
+ r2_next = tmp->list.next;
+ expr_free(r2);
+ free_r1 = 1;
+ goto next;
+ }
+
if (prefix_len < 0 ||
!(r1->dtype->flags & DTYPE_F_PREFIX)) {
tmp = range_expr_alloc(&r1->location, r1,
@@ -927,12 +435,108 @@ next:
}
}
+static struct expr *interval_to_prefix(struct expr *low, struct expr *i, const mpz_t range)
+{
+ unsigned int prefix_len;
+ struct expr *prefix;
+
+ prefix_len = expr_value(i)->len - mpz_scan0(range, 0);
+ prefix = prefix_expr_alloc(&low->location,
+ expr_clone(expr_value(low)),
+ prefix_len);
+ prefix->len = expr_value(i)->len;
+
+ return __expr_to_set_elem(low, prefix);
+}
+
+static struct expr *interval_to_string(struct expr *low, struct expr *i, const mpz_t range)
+{
+ unsigned int len = div_round_up(i->len, BITS_PER_BYTE);
+ unsigned int prefix_len, str_len;
+ char data[len + 2];
+ struct expr *expr;
+
+ prefix_len = expr_value(i)->len - mpz_scan0(range, 0);
+
+ if (prefix_len > i->len || prefix_len % BITS_PER_BYTE)
+ return interval_to_prefix(low, i, range);
+
+ mpz_export_data(data, expr_value(low)->value, BYTEORDER_BIG_ENDIAN, len);
+
+ str_len = strnlen(data, len);
+ if (str_len >= len || str_len == 0)
+ return interval_to_prefix(low, i, range);
+
+ data[str_len] = '*';
+
+ expr = constant_expr_alloc(&low->location, low->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (str_len + 1) * BITS_PER_BYTE, data);
+
+ return __expr_to_set_elem(low, expr);
+}
+
+static struct expr *interval_to_range(struct expr *low, struct expr *i, mpz_t range)
+{
+ struct expr *tmp;
+
+ tmp = constant_expr_alloc(&low->location, low->dtype,
+ low->byteorder, expr_value(low)->len,
+ NULL);
+
+ mpz_add(range, range, expr_value(low)->value);
+ mpz_set(tmp->value, range);
+
+ tmp = range_expr_alloc(&low->location,
+ expr_clone(expr_value(low)),
+ tmp);
+
+ 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 (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);
+ } 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;
- struct expr *i, *next, *low = NULL, *end;
unsigned int n, m, size;
- mpz_t range, p;
bool interval;
if (set->size == 0)
@@ -941,12 +545,20 @@ 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) {
+ key = NULL;
+ if (i->etype == EXPR_SET_ELEM)
+ key = i->key;
+ else if (i->etype == EXPR_MAPPING)
+ key = i->left->key;
+
+ if (key && key->etype == EXPR_SET_ELEM_CATCHALL) {
+ list_del(&i->list);
+ catchall = i;
+ continue;
+ }
compound_expr_remove(set, i);
elements[n++] = i;
}
@@ -991,99 +603,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))
- compound_expr_add(set, expr_get(low));
- else if ((!range_is_prefix(range) ||
- !(i->dtype->flags & DTYPE_F_PREFIX)) ||
- mpz_cmp_ui(p, 0)) {
- struct expr *tmp;
-
- tmp = constant_expr_alloc(&low->location, low->dtype,
- low->byteorder, expr_value(low)->len,
- NULL);
-
- mpz_add(range, range, expr_value(low)->value);
- mpz_set(tmp->value, range);
-
- tmp = range_expr_alloc(&low->location,
- expr_clone(expr_value(low)),
- tmp);
- tmp = set_elem_expr_alloc(&low->location, tmp);
-
- if (low->etype == EXPR_MAPPING) {
- if (low->left->comment)
- tmp->comment = xstrdup(low->left->comment);
- if (low->left->timeout)
- tmp->timeout = low->left->timeout;
- if (low->left->expiration)
- tmp->expiration = low->left->expiration;
- if (low->left->stmt) {
- tmp->stmt = low->left->stmt;
- low->left->stmt = NULL;
- }
-
- tmp = mapping_expr_alloc(&tmp->location, tmp,
- expr_clone(low->right));
- } else {
- if (low->comment)
- tmp->comment = xstrdup(low->comment);
- if (low->timeout)
- tmp->timeout = low->timeout;
- if (low->expiration)
- tmp->expiration = low->expiration;
- if (low->stmt) {
- tmp->stmt = low->stmt;
- low->stmt = NULL;
- }
- }
-
- compound_expr_add(set, tmp);
- } else {
- struct expr *prefix;
- unsigned int prefix_len;
-
- prefix_len = expr_value(i)->len - mpz_scan0(range, 0);
- prefix = prefix_expr_alloc(&low->location,
- expr_clone(expr_value(low)),
- prefix_len);
- prefix->len = expr_value(i)->len;
-
- prefix = set_elem_expr_alloc(&low->location, prefix);
-
- if (low->etype == EXPR_MAPPING) {
- if (low->left->comment)
- prefix->comment = xstrdup(low->left->comment);
- if (low->left->timeout)
- prefix->timeout = low->left->timeout;
- if (low->left->expiration)
- prefix->expiration = low->left->expiration;
- if (low->left->stmt) {
- prefix->stmt = low->left->stmt;
- low->left->stmt = NULL;
- }
-
- prefix = mapping_expr_alloc(&low->location, prefix,
- expr_clone(low->right));
- } else {
- if (low->comment)
- prefix->comment = xstrdup(low->comment);
- if (low->timeout)
- prefix->timeout = low->timeout;
- if (low->expiration)
- prefix->expiration = low->expiration;
- if (low->stmt) {
- prefix->stmt = low->stmt;
- low->stmt = NULL;
- }
- }
-
- compound_expr_add(set, prefix);
- }
+ add_interval(set, low, i);
if (i->flags & EXPR_F_INTERVAL_END) {
expr_free(low);
@@ -1100,24 +620,18 @@ 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));
-
+ add_interval(set, low, i);
expr_free(low);
}
- compound_expr_add(set, i);
+ expr_free(i);
+
out:
- mpz_clear(range);
- mpz_clear(p);
+ if (catchall)
+ compound_expr_add(set, catchall);
- xfree(ranges);
- xfree(elements);
+ free(ranges);
+ free(elements);
}
diff --git a/src/socket.c b/src/socket.c
index d78a163a..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>
@@ -26,21 +28,37 @@ const struct socket_template socket_templates[] = {
.len = 4 * BITS_PER_BYTE,
.byteorder = BYTEORDER_HOST_ENDIAN,
},
+ [NFT_SOCKET_WILDCARD] = {
+ .token = "wildcard",
+ .dtype = &integer_type,
+ .len = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_HOST_ENDIAN,
+ },
+ [NFT_SOCKET_CGROUPV2] = {
+ .token = "cgroupv2",
+ .dtype = &cgroupv2_type,
+ .len = 8 * BITS_PER_BYTE,
+ .byteorder = BYTEORDER_HOST_ENDIAN,
+ },
};
static void socket_expr_print(const struct expr *expr, struct output_ctx *octx)
{
nft_print(octx, "socket %s", socket_templates[expr->socket.key].token);
+ if (expr->socket.key == NFT_SOCKET_CGROUPV2)
+ nft_print(octx, " level %u", expr->socket.level);
}
static bool socket_expr_cmp(const struct expr *e1, const struct expr *e2)
{
- return e1->socket.key == e2->socket.key;
+ return e1->socket.key == e2->socket.key &&
+ e1->socket.level == e2->socket.level;
}
static void socket_expr_clone(struct expr *new, const struct expr *expr)
{
new->socket.key = expr->socket.key;
+ new->socket.level = expr->socket.level;
}
#define NFTNL_UDATA_SOCKET_KEY 0
@@ -89,7 +107,7 @@ static struct expr *socket_expr_parse_udata(const struct nftnl_udata *attr)
key = nftnl_udata_get_u32(ud[NFTNL_UDATA_SOCKET_KEY]);
- return socket_expr_alloc(&internal_location, key);
+ return socket_expr_alloc(&internal_location, key, 0);
}
const struct expr_ops socket_expr_ops = {
@@ -103,7 +121,8 @@ const struct expr_ops socket_expr_ops = {
.parse_udata = socket_expr_parse_udata,
};
-struct expr *socket_expr_alloc(const struct location *loc, enum nft_socket_keys key)
+struct expr *socket_expr_alloc(const struct location *loc,
+ enum nft_socket_keys key, uint32_t level)
{
const struct socket_template *tmpl = &socket_templates[key];
struct expr *expr;
@@ -111,6 +130,7 @@ struct expr *socket_expr_alloc(const struct location *loc, enum nft_socket_keys
expr = expr_alloc(loc, EXPR_SOCKET, tmpl->dtype,
tmpl->byteorder, tmpl->len);
expr->socket.key = key;
+ expr->socket.level = level;
return expr;
}
diff --git a/src/statement.c b/src/statement.c
index 6fe8e9d9..ab144d63 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>
@@ -23,6 +22,7 @@
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <statement.h>
+#include <tcpopt.h>
#include <utils.h>
#include <list.h>
#include <xt.h>
@@ -51,7 +51,7 @@ void stmt_free(struct stmt *stmt)
return;
if (stmt->ops->destroy)
stmt->ops->destroy(stmt);
- xfree(stmt);
+ free(stmt);
}
void stmt_list_free(struct list_head *list)
@@ -183,7 +183,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 = {
@@ -201,7 +201,7 @@ struct stmt *meter_stmt_alloc(const struct location *loc)
static void connlimit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
- nft_print(octx, "ct count %s%u ",
+ nft_print(octx, "ct count %s%u",
stmt->connlimit.flags ? "over " : "", stmt->connlimit.count);
}
@@ -248,6 +248,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",
@@ -454,9 +485,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);
@@ -464,7 +493,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 > 0) {
+ if (stmt->limit.burst != 0) {
uint64_t burst;
data_unit = get_rate(stmt->limit.burst, &burst);
@@ -493,20 +522,25 @@ struct stmt *limit_stmt_alloc(const struct location *loc)
static void queue_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
- const char *delim = " ";
+ struct expr *e = stmt->queue.queue;
+ const char *delim = " flags ";
nft_print(octx, "queue");
- if (stmt->queue.queue != NULL) {
- nft_print(octx, " num ");
- expr_print(stmt->queue.queue, octx);
- }
+
if (stmt->queue.flags & NFT_QUEUE_FLAG_BYPASS) {
nft_print(octx, "%sbypass", delim);
delim = ",";
}
+
if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT)
nft_print(octx, "%sfanout", delim);
+ if (e) {
+ nft_print(octx, " to ");
+ expr_print(stmt->queue.queue, octx);
+ } else {
+ nft_print(octx, " to 0");
+ }
}
static void queue_stmt_destroy(struct stmt *stmt)
@@ -522,9 +556,15 @@ static const struct stmt_ops queue_stmt_ops = {
.destroy = queue_stmt_destroy,
};
-struct stmt *queue_stmt_alloc(const struct location *loc)
+struct stmt *queue_stmt_alloc(const struct location *loc, struct expr *e, uint16_t flags)
{
- return stmt_alloc(loc, &queue_stmt_ops);
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &queue_stmt_ops);
+ stmt->queue.queue = e;
+ stmt->queue.flags = flags;
+
+ return stmt;
}
static void quota_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
@@ -569,7 +609,7 @@ static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
case NFT_REJECT_ICMPX_UNREACH:
if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)
break;
- nft_print(octx, " with icmpx type ");
+ nft_print(octx, " with icmpx ");
expr_print(stmt->reject.expr, octx);
break;
case NFT_REJECT_ICMP_UNREACH:
@@ -578,14 +618,14 @@ static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
if (!stmt->reject.verbose_print &&
stmt->reject.icmp_code == ICMP_PORT_UNREACH)
break;
- nft_print(octx, " with icmp type ");
+ nft_print(octx, " with icmp ");
expr_print(stmt->reject.expr, octx);
break;
case NFPROTO_IPV6:
if (!stmt->reject.verbose_print &&
stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
break;
- nft_print(octx, " with icmpv6 type ");
+ nft_print(octx, " with icmpv6 ");
expr_print(stmt->reject.expr, octx);
break;
}
@@ -657,12 +697,8 @@ static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
break;
}
- if (stmt->nat.type_flags & STMT_NAT_F_CONCAT)
- nft_print(octx, " addr . port");
- else if (stmt->nat.type_flags & STMT_NAT_F_PREFIX)
+ if (stmt->nat.type_flags & STMT_NAT_F_PREFIX)
nft_print(octx, " prefix");
- else if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL)
- nft_print(octx, " interval");
nft_print(octx, " to");
}
@@ -732,15 +768,16 @@ const char * const set_stmt_op_names[] = {
static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
unsigned int flags = octx->flags;
+ struct stmt *this;
nft_print(octx, "%s ", set_stmt_op_names[stmt->set.op]);
expr_print(stmt->set.set, octx);
nft_print(octx, " { ");
expr_print(stmt->set.key, octx);
- if (stmt->set.stmt) {
+ list_for_each_entry(this, &stmt->set.stmt_list, list) {
nft_print(octx, " ");
octx->flags |= NFT_CTX_OUTPUT_STATELESS;
- stmt_print(stmt->set.stmt, octx);
+ stmt_print(this, octx);
octx->flags = flags;
}
nft_print(octx, " }");
@@ -748,9 +785,12 @@ static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void set_stmt_destroy(struct stmt *stmt)
{
+ struct stmt *this, *next;
+
expr_free(stmt->set.key);
expr_free(stmt->set.set);
- stmt_free(stmt->set.stmt);
+ list_for_each_entry_safe(this, next, &stmt->set.stmt_list, list)
+ stmt_free(this);
}
static const struct stmt_ops set_stmt_ops = {
@@ -763,21 +803,27 @@ static const struct stmt_ops set_stmt_ops = {
struct stmt *set_stmt_alloc(const struct location *loc)
{
- return stmt_alloc(loc, &set_stmt_ops);
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &set_stmt_ops);
+ init_list_head(&stmt->set.stmt_list);
+
+ return stmt;
}
static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
unsigned int flags = octx->flags;
+ struct stmt *this;
nft_print(octx, "%s ", set_stmt_op_names[stmt->map.op]);
expr_print(stmt->map.set, octx);
nft_print(octx, " { ");
expr_print(stmt->map.key, octx);
- if (stmt->map.stmt) {
+ list_for_each_entry(this, &stmt->map.stmt_list, list) {
nft_print(octx, " ");
octx->flags |= NFT_CTX_OUTPUT_STATELESS;
- stmt_print(stmt->map.stmt, octx);
+ stmt_print(this, octx);
octx->flags = flags;
}
nft_print(octx, " : ");
@@ -787,10 +833,13 @@ static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void map_stmt_destroy(struct stmt *stmt)
{
+ struct stmt *this, *next;
+
expr_free(stmt->map.key);
expr_free(stmt->map.data);
expr_free(stmt->map.set);
- stmt_free(stmt->map.stmt);
+ list_for_each_entry_safe(this, next, &stmt->map.stmt_list, list)
+ stmt_free(this);
}
static const struct stmt_ops map_stmt_ops = {
@@ -798,11 +847,17 @@ 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)
{
- return stmt_alloc(loc, &map_stmt_ops);
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &map_stmt_ops);
+ init_list_head(&stmt->map.stmt_list);
+
+ return stmt;
}
static void dup_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
@@ -884,6 +939,37 @@ struct stmt *fwd_stmt_alloc(const struct location *loc)
return stmt_alloc(loc, &fwd_stmt_ops);
}
+static void optstrip_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ const struct expr *expr = stmt->optstrip.expr;
+
+ nft_print(octx, "reset ");
+ expr_print(expr, octx);
+}
+
+static void optstrip_stmt_destroy(struct stmt *stmt)
+{
+ expr_free(stmt->optstrip.expr);
+}
+
+static const struct stmt_ops optstrip_stmt_ops = {
+ .type = STMT_OPTSTRIP,
+ .name = "optstrip",
+ .print = optstrip_stmt_print,
+ .json = optstrip_stmt_json,
+ .destroy = optstrip_stmt_destroy,
+};
+
+struct stmt *optstrip_stmt_alloc(const struct location *loc, struct expr *e)
+{
+ struct stmt *stmt = stmt_alloc(loc, &optstrip_stmt_ops);
+
+ e->exthdr.flags |= NFT_EXTHDR_F_PRESENT;
+ stmt->optstrip.expr = e;
+
+ return stmt;
+}
+
static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
nft_print(octx, "tproxy");
@@ -903,7 +989,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, ":");
@@ -940,6 +1026,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)
diff --git a/src/tcpopt.c b/src/tcpopt.c
index ec305d94..f977e417 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>
@@ -20,221 +19,269 @@ static const struct proto_hdr_template tcpopt_unknown_template =
__offset, __len)
static const struct exthdr_desc tcpopt_eol = {
.name = "eol",
- .type = TCPOPT_EOL,
+ .type = TCPOPT_KIND_EOL,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
},
};
static const struct exthdr_desc tcpopt_nop = {
- .name = "noop",
- .type = TCPOPT_NOP,
+ .name = "nop",
+ .type = TCPOPT_KIND_NOP,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
},
};
static const struct exthdr_desc tcptopt_maxseg = {
.name = "maxseg",
- .type = TCPOPT_MAXSEG,
+ .type = TCPOPT_KIND_MAXSEG,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
- [TCPOPTHDR_FIELD_SIZE] = PHT("size", 16, 16),
+ [TCPOPT_MAXSEG_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_MAXSEG_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_MAXSEG_SIZE] = PHT("size", 16, 16),
},
};
static const struct exthdr_desc tcpopt_window = {
.name = "window",
- .type = TCPOPT_WINDOW,
+ .type = TCPOPT_KIND_WINDOW,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
- [TCPOPTHDR_FIELD_COUNT] = PHT("count", 16, 8),
+ [TCPOPT_WINDOW_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_WINDOW_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_WINDOW_COUNT] = PHT("count", 16, 8),
},
};
static const struct exthdr_desc tcpopt_sack_permitted = {
- .name = "sack-permitted",
- .type = TCPOPT_SACK_PERMITTED,
+ .name = "sack-perm",
+ .type = TCPOPT_KIND_SACK_PERMITTED,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
},
};
static const struct exthdr_desc tcpopt_sack = {
.name = "sack",
- .type = TCPOPT_SACK,
+ .type = TCPOPT_KIND_SACK,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
- [TCPOPTHDR_FIELD_LEFT] = PHT("left", 16, 32),
- [TCPOPTHDR_FIELD_RIGHT] = PHT("right", 48, 32),
+ [TCPOPT_SACK_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_SACK_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_SACK_LEFT] = PHT("left", 16, 32),
+ [TCPOPT_SACK_RIGHT] = PHT("right", 48, 32),
+ [TCPOPT_SACK_LEFT1] = PHT("left", 80, 32),
+ [TCPOPT_SACK_RIGHT1] = PHT("right", 112, 32),
+ [TCPOPT_SACK_LEFT2] = PHT("left", 144, 32),
+ [TCPOPT_SACK_RIGHT2] = PHT("right", 176, 32),
+ [TCPOPT_SACK_LEFT3] = PHT("left", 208, 32),
+ [TCPOPT_SACK_RIGHT3] = PHT("right", 240, 32),
},
};
static const struct exthdr_desc tcpopt_timestamp = {
.name = "timestamp",
- .type = TCPOPT_TIMESTAMP,
+ .type = TCPOPT_KIND_TIMESTAMP,
.templates = {
- [TCPOPTHDR_FIELD_KIND] = PHT("kind", 0, 8),
- [TCPOPTHDR_FIELD_LENGTH] = PHT("length", 8, 8),
- [TCPOPTHDR_FIELD_TSVAL] = PHT("tsval", 16, 32),
- [TCPOPTHDR_FIELD_TSECR] = PHT("tsecr", 48, 32),
+ [TCPOPT_TS_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_TS_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_TS_TSVAL] = PHT("tsval", 16, 32),
+ [TCPOPT_TS_TSECR] = PHT("tsecr", 48, 32),
},
};
-#undef PHT
-#define TCPOPT_OBSOLETE ((struct exthdr_desc *)NULL)
-#define TCPOPT_ECHO 6
-#define TCPOPT_ECHO_REPLY 7
-static const struct exthdr_desc *tcpopt_protocols[] = {
- [TCPOPT_EOL] = &tcpopt_eol,
- [TCPOPT_NOP] = &tcpopt_nop,
- [TCPOPT_MAXSEG] = &tcptopt_maxseg,
- [TCPOPT_WINDOW] = &tcpopt_window,
- [TCPOPT_SACK_PERMITTED] = &tcpopt_sack_permitted,
- [TCPOPT_SACK] = &tcpopt_sack,
- [TCPOPT_ECHO] = TCPOPT_OBSOLETE,
- [TCPOPT_ECHO_REPLY] = TCPOPT_OBSOLETE,
- [TCPOPT_TIMESTAMP] = &tcpopt_timestamp,
+static const struct exthdr_desc tcpopt_fastopen = {
+ .name = "fastopen",
+ .type = TCPOPT_KIND_FASTOPEN,
+ .templates = {
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
+ },
};
-static unsigned int calc_offset(const struct exthdr_desc *desc,
- const struct proto_hdr_template *tmpl,
- unsigned int num)
-{
- if (!desc || tmpl == &tcpopt_unknown_template)
- return 0;
-
- switch (desc->type) {
- case TCPOPT_SACK:
- /* Make sure, offset calculations only apply to left and right
- * fields
- */
- return (tmpl->offset < 16) ? 0 : num * 64;
- default:
- return 0;
- }
-}
+static const struct exthdr_desc tcpopt_md5sig = {
+ .name = "md5sig",
+ .type = TCPOPT_KIND_MD5SIG,
+ .templates = {
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
+ },
+};
-static unsigned int calc_offset_reverse(const struct exthdr_desc *desc,
- const struct proto_hdr_template *tmpl,
- unsigned int offset)
-{
- if (!desc || tmpl == &tcpopt_unknown_template)
- return offset;
-
- switch (desc->type) {
- case TCPOPT_SACK:
- /* We can safely ignore the first left/right field */
- return offset < 80 ? offset : (offset % 64);
- default:
- return offset;
- }
-}
+static const struct exthdr_desc tcpopt_mptcp = {
+ .name = "mptcp",
+ .type = TCPOPT_KIND_MPTCP,
+ .templates = {
+ [TCPOPT_MPTCP_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_MPTCP_LENGTH] = PHT("length", 8, 8),
+ [TCPOPT_MPTCP_SUBTYPE] = PHT("subtype", 16, 4),
+ },
+};
-const struct exthdr_desc *tcpopthdr_protocols[__TCPOPTHDR_MAX] = {
- [TCPOPTHDR_EOL] = &tcpopt_eol,
- [TCPOPTHDR_NOOP] = &tcpopt_nop,
- [TCPOPTHDR_MAXSEG] = &tcptopt_maxseg,
- [TCPOPTHDR_WINDOW] = &tcpopt_window,
- [TCPOPTHDR_SACK_PERMITTED] = &tcpopt_sack_permitted,
- [TCPOPTHDR_SACK0] = &tcpopt_sack,
- [TCPOPTHDR_SACK1] = &tcpopt_sack,
- [TCPOPTHDR_SACK2] = &tcpopt_sack,
- [TCPOPTHDR_SACK3] = &tcpopt_sack,
- [TCPOPTHDR_ECHO] = TCPOPT_OBSOLETE,
- [TCPOPTHDR_ECHO_REPLY] = TCPOPT_OBSOLETE,
- [TCPOPTHDR_TIMESTAMP] = &tcpopt_timestamp,
+static const struct exthdr_desc tcpopt_fallback = {
+ .templates = {
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
+ },
};
+#undef PHT
-static uint8_t tcpopt_optnum[] = {
- [TCPOPTHDR_SACK0] = 0,
- [TCPOPTHDR_SACK1] = 1,
- [TCPOPTHDR_SACK2] = 2,
- [TCPOPTHDR_SACK3] = 3,
+const struct exthdr_desc *tcpopt_protocols[] = {
+ [TCPOPT_KIND_EOL] = &tcpopt_eol,
+ [TCPOPT_KIND_NOP] = &tcpopt_nop,
+ [TCPOPT_KIND_MAXSEG] = &tcptopt_maxseg,
+ [TCPOPT_KIND_WINDOW] = &tcpopt_window,
+ [TCPOPT_KIND_SACK_PERMITTED] = &tcpopt_sack_permitted,
+ [TCPOPT_KIND_SACK] = &tcpopt_sack,
+ [TCPOPT_KIND_TIMESTAMP] = &tcpopt_timestamp,
+ [TCPOPT_KIND_MD5SIG] = &tcpopt_md5sig,
+ [TCPOPT_KIND_MPTCP] = &tcpopt_mptcp,
+ [TCPOPT_KIND_FASTOPEN] = &tcpopt_fastopen,
};
-static uint8_t tcpopt_find_optnum(uint8_t optnum)
+static void tcpopt_assign_tmpl(struct expr *expr,
+ const struct proto_hdr_template *tmpl,
+ const struct exthdr_desc *desc)
{
- if (optnum > TCPOPTHDR_SACK3)
- return 0;
+ expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
- return tcpopt_optnum[optnum];
+ expr->exthdr.desc = desc;
+ expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
}
-struct expr *tcpopt_expr_alloc(const struct location *loc, uint8_t type,
- uint8_t field)
+/**
+ * tcpopt_expr_alloc - allocate tcp option extension expression
+ *
+ * @loc: location from parser
+ * @kind: raw tcp option value to find in packet
+ * @field: highlevel field to find in the option if @kind is present in packet
+ *
+ * Allocate a new tcp option expression.
+ * @kind is the raw option value to find in the packet.
+ * Exception: SACK may use extra OOB data that is mangled here.
+ *
+ * @field is the optional field to extract from the @type option.
+ */
+struct expr *tcpopt_expr_alloc(const struct location *loc,
+ unsigned int kind,
+ unsigned int field)
{
const struct proto_hdr_template *tmpl;
- const struct exthdr_desc *desc;
+ const struct exthdr_desc *desc = NULL;
struct expr *expr;
- uint8_t optnum;
- desc = tcpopthdr_protocols[type];
+ switch (kind) {
+ case TCPOPT_KIND_SACK1:
+ kind = TCPOPT_KIND_SACK;
+ if (field == TCPOPT_SACK_LEFT)
+ field = TCPOPT_SACK_LEFT1;
+ else if (field == TCPOPT_SACK_RIGHT)
+ field = TCPOPT_SACK_RIGHT1;
+ break;
+ case TCPOPT_KIND_SACK2:
+ kind = TCPOPT_KIND_SACK;
+ if (field == TCPOPT_SACK_LEFT)
+ field = TCPOPT_SACK_LEFT2;
+ else if (field == TCPOPT_SACK_RIGHT)
+ field = TCPOPT_SACK_RIGHT2;
+ break;
+ case TCPOPT_KIND_SACK3:
+ kind = TCPOPT_KIND_SACK;
+ if (field == TCPOPT_SACK_LEFT)
+ field = TCPOPT_SACK_LEFT3;
+ else if (field == TCPOPT_SACK_RIGHT)
+ field = TCPOPT_SACK_RIGHT3;
+ break;
+ }
+
+ if (kind < array_size(tcpopt_protocols))
+ desc = tcpopt_protocols[kind];
+
+ if (!desc) {
+ 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);
+
+ 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;
- optnum = tcpopt_find_optnum(type);
-
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.offset = calc_offset(desc, tmpl, optnum);
+
+ expr->exthdr.raw_type = desc->type;
+ tcpopt_assign_tmpl(expr, tmpl, desc);
return expr;
}
-void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset,
+void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off,
unsigned int len, uint32_t flags)
{
const struct proto_hdr_template *tmpl;
- unsigned int i, off;
+ unsigned int i;
assert(expr->etype == EXPR_EXTHDR);
expr->len = len;
expr->exthdr.flags = flags;
- expr->exthdr.offset = offset;
+ expr->exthdr.offset = off;
+ expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+ expr->exthdr.tmpl = &tcpopt_unknown_template;
+
+ if (flags & NFT_EXTHDR_F_PRESENT)
+ datatype_set(expr, &boolean_type);
+ else
+ datatype_set(expr, &integer_type);
+
+ if (type >= array_size(tcpopt_protocols) ||
+ !tcpopt_protocols[type])
+ return;
- assert(type < array_size(tcpopt_protocols));
expr->exthdr.desc = tcpopt_protocols[type];
expr->exthdr.flags = flags;
- assert(expr->exthdr.desc != TCPOPT_OBSOLETE);
+ assert(expr->exthdr.desc != NULL);
for (i = 0; i < array_size(expr->exthdr.desc->templates); ++i) {
tmpl = &expr->exthdr.desc->templates[i];
- /* We have to reverse calculate the offset for the sack options
- * at this point
- */
- off = calc_offset_reverse(expr->exthdr.desc, tmpl, offset);
if (tmpl->offset != off || tmpl->len != len)
continue;
- if (flags & NFT_EXTHDR_F_PRESENT)
- datatype_set(expr, &boolean_type);
- else
+ if ((flags & NFT_EXTHDR_F_PRESENT) == 0)
datatype_set(expr, tmpl->dtype);
+
expr->exthdr.tmpl = tmpl;
- expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
break;
}
}
-bool tcpopt_find_template(struct expr *expr, const struct expr *mask,
- unsigned int *shift)
+bool tcpopt_find_template(struct expr *expr, unsigned int offset, unsigned int len)
{
if (expr->exthdr.tmpl != &tcpopt_unknown_template)
return false;
- tcpopt_init_raw(expr, expr->exthdr.desc->type, expr->exthdr.offset,
- expr->len, 0);
+ tcpopt_init_raw(expr, expr->exthdr.desc->type, offset, len, 0);
if (expr->exthdr.tmpl == &tcpopt_unknown_template)
return false;
diff --git a/src/utils.c b/src/utils.c
index 47f5b791..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;
@@ -50,6 +45,16 @@ void *xmalloc_array(size_t nmemb, size_t size)
return xmalloc(nmemb * size);
}
+void *xzalloc_array(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ ptr = xmalloc_array(nmemb, size);
+ memset(ptr, 0, nmemb * size);
+
+ return ptr;
+}
+
void *xrealloc(void *ptr, size_t size)
{
ptr = realloc(ptr, size);
@@ -90,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 f39acf30..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)
@@ -321,7 +286,7 @@ static int nft_xt_compatible_revision(const char *name, uint8_t rev, int opt)
struct nfgenmsg *nfg;
int ret = 0;
- switch (rev) {
+ switch (opt) {
case IPT_SO_GET_REVISION_MATCH:
family = NFPROTO_IPV4;
type = 0;
@@ -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