summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am119
-rw-r--r--src/cache.c92
-rw-r--r--src/cli.c68
-rw-r--r--src/cmd.c259
-rw-r--r--src/ct.c21
-rw-r--r--src/datatype.c447
-rw-r--r--src/dccpopt.c277
-rw-r--r--src/erec.c12
-rw-r--r--src/evaluate.c1460
-rw-r--r--src/expression.c56
-rw-r--r--src/exthdr.c20
-rw-r--r--src/fib.c7
-rw-r--r--src/gmputil.c27
-rw-r--r--src/hash.c6
-rw-r--r--src/iface.c8
-rw-r--r--src/intervals.c28
-rw-r--r--src/ipopt.c3
-rw-r--r--src/json.c214
-rw-r--r--src/libnftables.c142
-rw-r--r--src/libnftables.map5
-rw-r--r--src/main.c82
-rw-r--r--src/mergesort.c40
-rw-r--r--src/meta.c174
-rw-r--r--src/mini-gmp.c4
-rw-r--r--src/misspell.c14
-rw-r--r--src/mnl.c443
-rw-r--r--src/monitor.c30
-rw-r--r--src/netlink.c313
-rw-r--r--src/netlink_delinearize.c608
-rw-r--r--src/netlink_linearize.c132
-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.c328
-rw-r--r--src/osf.c11
-rw-r--r--src/owner.c13
-rw-r--r--src/parser_bison.y918
-rw-r--r--src/parser_json.c561
-rw-r--r--src/payload.c264
-rw-r--r--src/print.c7
-rw-r--r--src/proto.c159
-rw-r--r--src/rt.c15
-rw-r--r--src/rule.c457
-rw-r--r--src/scanner.l135
-rw-r--r--src/sctp_chunk.c3
-rw-r--r--src/segtree.c13
-rw-r--r--src/socket.c6
-rw-r--r--src/statement.c48
-rw-r--r--src/tcpopt.c51
-rw-r--r--src/utils.c14
-rw-r--r--src/xfrm.c9
-rw-r--r--src/xt.c246
53 files changed, 6189 insertions, 2310 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index 264d981e..00000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,119 +0,0 @@
-include $(top_srcdir)/Make_global.am
-
-sbin_PROGRAMS = nft
-
-AM_CPPFLAGS = -I$(top_srcdir)/include
-AM_CPPFLAGS += -DDEFAULT_INCLUDE_PATH="\"${sysconfdir}\"" \
- ${LIBMNL_CFLAGS} ${LIBNFTNL_CFLAGS}
-if BUILD_DEBUG
-AM_CPPFLAGS += -g -DDEBUG
-endif
-if BUILD_XTABLES
-AM_CPPFLAGS += ${XTABLES_CFLAGS}
-endif
-if BUILD_MINIGMP
-AM_CPPFLAGS += -DHAVE_MINIGMP
-endif
-if BUILD_JSON
-AM_CPPFLAGS += -DHAVE_JSON
-endif
-if BUILD_XTABLES
-AM_CPPFLAGS += -DHAVE_XTABLES
-endif
-
-AM_CFLAGS = -Wall \
- -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \
- -Wdeclaration-after-statement -Wsign-compare -Winit-self \
- -Wformat-nonliteral -Wformat-security -Wmissing-format-attribute \
- -Wcast-align -Wundef -Wbad-function-cast \
- -Waggregate-return -Wunused -Wwrite-strings ${GCC_FVISIBILITY_HIDDEN}
-
-
-AM_YFLAGS = -d -Wno-yacc
-
-BUILT_SOURCES = parser_bison.h
-
-lib_LTLIBRARIES = libnftables.la
-
-libnftables_la_SOURCES = \
- rule.c \
- statement.c \
- cache.c \
- cmd.c \
- datatype.c \
- expression.c \
- evaluate.c \
- proto.c \
- payload.c \
- exthdr.c \
- fib.c \
- hash.c \
- intervals.c \
- ipopt.c \
- meta.c \
- rt.c \
- numgen.c \
- ct.c \
- xfrm.c \
- netlink.c \
- netlink_linearize.c \
- netlink_delinearize.c \
- misspell.c \
- monitor.c \
- owner.c \
- segtree.c \
- gmputil.c \
- utils.c \
- erec.c \
- mnl.c \
- iface.c \
- mergesort.c \
- optimize.c \
- osf.c \
- nfnl_osf.c \
- tcpopt.c \
- socket.c \
- print.c \
- sctp_chunk.c \
- libnftables.c \
- libnftables.map
-
-# yacc and lex generate dirty code
-noinst_LTLIBRARIES = libparser.la
-libparser_la_SOURCES = parser_bison.y scanner.l
-libparser_la_CFLAGS = ${AM_CFLAGS} \
- -Wno-missing-prototypes \
- -Wno-missing-declarations \
- -Wno-implicit-function-declaration \
- -Wno-nested-externs \
- -Wno-undef \
- -Wno-redundant-decls
-
-libnftables_la_LIBADD = ${LIBMNL_LIBS} ${LIBNFTNL_LIBS} libparser.la
-libnftables_la_LDFLAGS = -version-info ${libnftables_LIBVERSION} \
- -Wl,--version-script=$(srcdir)/libnftables.map
-
-if BUILD_MINIGMP
-noinst_LTLIBRARIES += libminigmp.la
-libminigmp_la_SOURCES = mini-gmp.c
-libminigmp_la_CFLAGS = ${AM_CFLAGS} -Wno-sign-compare
-libnftables_la_LIBADD += libminigmp.la
-endif
-
-libnftables_la_SOURCES += xt.c
-if BUILD_XTABLES
-libnftables_la_LIBADD += ${XTABLES_LIBS}
-endif
-
-nft_SOURCES = main.c
-
-if BUILD_CLI
-nft_SOURCES += cli.c
-endif
-
-if BUILD_JSON
-libnftables_la_SOURCES += json.c parser_json.c
-libnftables_la_LIBADD += ${JANSSON_LIBS}
-endif
-
-nft_LDADD = libnftables.la
diff --git a/src/cache.c b/src/cache.c
index 85de970f..c000e32c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -6,6 +6,8 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <expression.h>
#include <statement.h>
#include <rule.h>
@@ -124,9 +126,9 @@ void nft_cache_filter_fini(struct nft_cache_filter *filter)
struct nft_filter_obj *obj, *next;
list_for_each_entry_safe(obj, next, &filter->obj[i].head, list)
- xfree(obj);
+ free(obj);
}
- xfree(filter);
+ free(filter);
}
static void cache_filter_add(struct nft_cache_filter *filter,
@@ -201,8 +203,12 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
{
switch (cmd->obj) {
case CMD_OBJ_TABLE:
- if (filter && cmd->handle.table.name) {
+ 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;
@@ -212,6 +218,10 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
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;
@@ -224,8 +234,6 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
}
if (filter->list.table && filter->list.set)
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM;
- else if (nft_output_terse(&nft->output))
- flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
else
flags |= NFT_CACHE_FULL;
break;
@@ -235,6 +243,8 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
case CMD_OBJ_SETS:
case CMD_OBJ_MAPS:
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET;
+ if (!nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_SETELEM;
break;
case CMD_OBJ_FLOWTABLE:
if (filter &&
@@ -249,17 +259,45 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
break;
case CMD_OBJ_RULESET:
- if (nft_output_terse(&nft->output))
- flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
- else
- flags |= NFT_CACHE_FULL;
- break;
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;
}
@@ -277,6 +315,7 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
}
break;
case CMD_OBJ_RULE:
+ case CMD_OBJ_RULES:
case CMD_OBJ_CHAIN:
case CMD_OBJ_CHAINS:
if (h->table.name &&
@@ -342,7 +381,9 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
case CMD_OBJ_CT_HELPER:
case CMD_OBJ_CT_HELPERS:
case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_CT_EXPECTATIONS:
if (h->table.name &&
strlen(h->table.name) > NFT_NAME_MAXLEN) {
loc = &h->table.location;
@@ -391,6 +432,7 @@ int nft_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 |
@@ -403,7 +445,7 @@ int nft_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:
flags |= evaluate_cache_list(nft, cmd, flags, filter);
@@ -591,8 +633,8 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data)
table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
- if (h->family != family ||
- strcmp(table, h->table.name) != 0 ||
+ if ((h->family != NFPROTO_UNSPEC && h->family != family) ||
+ (h->table.name && strcmp(table, h->table.name) != 0) ||
(h->chain.name && strcmp(chain, h->chain.name) != 0))
return 0;
@@ -604,19 +646,23 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data)
return 0;
}
-static int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
- const struct nft_cache_filter *filter)
+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_cache = mnl_nft_rule_dump(ctx, h->family,
+ table, chain, rule_handle, dump, reset);
if (rule_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -948,7 +994,7 @@ static int rule_init_cache(struct netlink_ctx *ctx, struct table *table,
struct chain *chain;
int ret;
- ret = rule_cache_dump(ctx, &table->handle, filter);
+ 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);
@@ -981,8 +1027,10 @@ static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table,
int ret = 0;
list_for_each_entry(chain, &table->chain_bindings, cache.list) {
- filter.list.table = table->handle.table.name;
- filter.list.chain = chain->handle.chain.name;
+ filter.list = (typeof(filter.list)) {
+ .table = table->handle.table.name,
+ .chain = chain->handle.chain.name,
+ };
ret = rule_init_cache(ctx, table, &filter);
}
@@ -1035,7 +1083,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
+ set, false);
if (ret < 0)
goto cache_fails;
}
@@ -1048,7 +1096,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
+ set, false);
if (ret < 0)
goto cache_fails;
}
@@ -1231,7 +1279,7 @@ void cache_init(struct cache *cache)
void cache_free(struct cache *cache)
{
- xfree(cache->ht);
+ free(cache->ht);
}
void cache_add(struct cache_item *item, struct cache *cache, uint32_t hash)
diff --git a/src/cli.c b/src/cli.c
index 11fc85ab..448c25c2 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -12,13 +12,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <config.h>
-#include <stdlib.h>
+#include <nft.h>
+
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
-#include <string.h>
#include <ctype.h>
#include <limits.h>
#ifdef HAVE_LIBREADLINE
@@ -37,6 +36,15 @@
#define CMDLINE_PROMPT "nft> "
#define CMDLINE_QUIT "quit"
+static bool cli_quit;
+static int cli_rc;
+
+static void __cli_exit(int rc)
+{
+ cli_quit = true;
+ cli_rc = rc;
+}
+
static char histfile[PATH_MAX];
static void
@@ -100,8 +108,8 @@ static char *cli_append_multiline(char *line)
if (!s) {
fprintf(stderr, "%s:%u: Memory allocation failure\n",
__FILE__, __LINE__);
- cli_exit();
- exit(EXIT_FAILURE);
+ cli_exit(EXIT_FAILURE);
+ return NULL;
}
snprintf(s, len + 1, "%s%s", multiline, line);
free(multiline);
@@ -125,8 +133,7 @@ static void cli_complete(char *line)
if (line == NULL) {
printf("\n");
- cli_exit();
- exit(0);
+ return cli_exit(0);
}
line = cli_append_multiline(line);
@@ -139,10 +146,8 @@ static void cli_complete(char *line)
if (*c == '\0')
return;
- if (!strcmp(line, CMDLINE_QUIT)) {
- cli_exit();
- exit(0);
- }
+ if (!strcmp(line, CMDLINE_QUIT))
+ return cli_exit(0);
/* avoid duplicate history entries */
hist = history_get(history_length);
@@ -176,16 +181,19 @@ int cli_init(struct nft_ctx *nft)
read_history(histfile);
history_set_pos(history_length);
- while (true)
+ while (!cli_quit)
rl_callback_read_char();
- return 0;
+
+ return cli_rc;
}
-void cli_exit(void)
+void cli_exit(int rc)
{
rl_callback_handler_remove();
rl_deprep_terminal();
write_history(histfile);
+
+ __cli_exit(rc);
}
#elif defined(HAVE_LIBEDIT)
@@ -205,51 +213,63 @@ int cli_init(struct nft_ctx *nft)
history_set_pos(history_length);
rl_set_prompt(CMDLINE_PROMPT);
- while ((line = readline(rl_prompt)) != NULL) {
+ while (!cli_quit) {
+ line = readline(rl_prompt);
+ if (!line) {
+ cli_exit(0);
+ break;
+ }
line = cli_append_multiline(line);
if (!line)
continue;
cli_complete(line);
}
- cli_exit();
- return 0;
+ return cli_rc;
}
-void cli_exit(void)
+void cli_exit(int rc)
{
rl_deprep_terminal();
write_history(histfile);
+
+ __cli_exit(rc);
}
#else /* HAVE_LINENOISE */
int cli_init(struct nft_ctx *nft)
{
- int quit = 0;
char *line;
init_histfile();
linenoiseHistoryLoad(histfile);
linenoiseSetMultiLine(1);
- while (!quit && (line = linenoise(CMDLINE_PROMPT)) != NULL) {
+ while (!cli_quit) {
+ line = linenoise(CMDLINE_PROMPT);
+ if (!line) {
+ cli_exit(0);
+ break;
+ }
if (strcmp(line, CMDLINE_QUIT) == 0) {
- quit = 1;
+ cli_exit(0);
} else if (line[0] != '\0') {
linenoiseHistoryAdd(line);
nft_run_cmd_from_buffer(nft, line);
}
linenoiseFree(line);
}
- cli_exit();
- exit(0);
+
+ return cli_rc;
}
-void cli_exit(void)
+void cli_exit(int rc)
{
linenoiseHistorySave(histfile);
+
+ __cli_exit(rc);
}
#endif /* HAVE_LINENOISE */
diff --git a/src/cmd.c b/src/cmd.c
index 63692422..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,9 +15,19 @@
#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,
const struct location *loc)
@@ -21,7 +41,7 @@ static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!table)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?",
+ netlink_io_error(ctx, loc, "%s; did you mean table '%s' in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
return 1;
@@ -37,7 +57,7 @@ static int table_fuzzy_check(struct netlink_ctx *ctx, const struct cmd *cmd,
if (strcmp(cmd->handle.table.name, table->handle.table.name) ||
cmd->handle.family != table->handle.family) {
netlink_io_error(ctx, &cmd->handle.table.location,
- "%s; did you mean table ‘%s’ in family %s?",
+ "%s; did you mean table '%s' in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
return 1;
@@ -66,7 +86,7 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!chain)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean chain '%s' in table %s '%s'?",
strerror(ENOENT), chain->handle.chain.name,
family2str(table->handle.family),
table->handle.table.name);
@@ -96,7 +116,7 @@ static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
if (strcmp(cmd->handle.chain.name, chain->handle.chain.name)) {
- netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean chain '%s' in table %s '%s'?",
strerror(ENOENT),
chain->handle.chain.name,
family2str(table->handle.family),
@@ -127,7 +147,7 @@ static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!set)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean %s ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean %s '%s' in table %s '%s'?",
strerror(ENOENT),
set_is_map(set->flags) ? "map" : "set",
set->handle.set.name,
@@ -156,7 +176,7 @@ static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!obj)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean obj ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean obj '%s' in table %s '%s'?",
strerror(ENOENT), obj->handle.obj.name,
family2str(obj->handle.family),
table->handle.table.name);
@@ -185,7 +205,7 @@ static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
if (!ft)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean flowtable '%s' in table %s '%s'?",
strerror(ENOENT), ft->handle.flowtable.name,
family2str(ft->handle.family),
table->handle.table.name);
@@ -233,6 +253,33 @@ 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)
{
@@ -255,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 e246d303..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",
@@ -216,10 +216,17 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
return NULL;
}
-static const struct datatype ct_label_type = {
+static void ct_label_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, CONNLABEL_CONF,
+ octx->tbl.ct_label, &ct_label_type);
+}
+
+const struct datatype ct_label_type = {
.type = TYPE_CT_LABEL,
.name = "ct_label",
.desc = "conntrack label",
+ .describe = ct_label_type_describe,
.byteorder = BYTEORDER_HOST_ENDIAN,
.size = CT_LABEL_BIT_SIZE,
.basetype = &bitmask_type,
@@ -271,10 +278,10 @@ const struct ct_template ct_templates[__NFT_CT_MAX] = {
[NFT_CT_PROTOCOL] = CT_TEMPLATE("protocol", &inet_protocol_type,
BYTEORDER_BIG_ENDIAN,
BITS_PER_BYTE),
- [NFT_CT_PROTO_SRC] = CT_TEMPLATE("proto-src", &invalid_type,
+ [NFT_CT_PROTO_SRC] = CT_TEMPLATE("proto-src", &inet_service_type,
BYTEORDER_BIG_ENDIAN,
2 * BITS_PER_BYTE),
- [NFT_CT_PROTO_DST] = CT_TEMPLATE("proto-dst", &invalid_type,
+ [NFT_CT_PROTO_DST] = CT_TEMPLATE("proto-dst", &inet_service_type,
BYTEORDER_BIG_ENDIAN,
2 * BITS_PER_BYTE),
[NFT_CT_LABELS] = CT_TEMPLATE("label", &ct_label_type,
@@ -570,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 = {
diff --git a/src/datatype.c b/src/datatype.c
index 002ed46a..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>
@@ -28,6 +28,8 @@
#include <erec.h>
#include <netlink.h>
#include <json.h>
+#include <misspell.h>
+#include "nftutils.h"
#include <netinet/ip_icmp.h>
@@ -62,6 +64,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_CT_DIR] = &ct_dir_type,
[TYPE_CT_STATUS] = &ct_status_type,
[TYPE_ICMP6_TYPE] = &icmp6_type_type,
+ [TYPE_CT_LABEL] = &ct_label_type,
[TYPE_PKTTYPE] = &pkttype_type,
[TYPE_ICMP_CODE] = &icmp_code_type,
[TYPE_ICMPV6_CODE] = &icmpv6_code_type,
@@ -71,6 +74,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_ECN] = &ecn_type,
[TYPE_FIB_ADDR] = &fib_addr_type,
[TYPE_BOOLEAN] = &boolean_type,
+ [TYPE_CT_EVENTBIT] = &ct_event_type,
[TYPE_IFNAME] = &ifname_type,
[TYPE_IGMP_TYPE] = &igmp_type_type,
[TYPE_TIME_DATE] = &date_type,
@@ -123,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);
@@ -136,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,
@@ -163,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;
@@ -321,11 +377,41 @@ static void verdict_type_print(const struct expr *expr, struct output_ctx *octx)
}
}
+static struct error_record *verdict_type_error(const struct expr *sym)
+{
+ /* 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 = {
.type = TYPE_VERDICT,
.name = "verdict",
.desc = "netfilter verdict",
.print = verdict_type_print,
+ .err = verdict_type_error,
};
static const struct symbol_table nfproto_tbl = {
@@ -516,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;
}
@@ -575,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) {
+ if (ai->ai_next != NULL) {
+ freeaddrinfo(ai);
+ return error(&sym->location,
+ "Hostname resolves to multiple addresses");
+ }
+
+ 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;
}
@@ -614,12 +715,12 @@ 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;
}
}
@@ -628,15 +729,15 @@ static void inet_protocol_type_print(const struct expr *expr,
static void inet_protocol_type_describe(struct output_ctx *octx)
{
- struct protoent *p;
uint8_t protonum;
for (protonum = 0; protonum < UINT8_MAX; protonum++) {
- p = getprotobynumber(protonum);
- if (!p)
+ char name[NFT_PROTONAME_MAXSIZE];
+
+ if (!nft_getprotobynumber(protonum, name, sizeof(name)))
continue;
- nft_print(octx, "\t%-30s\t%u\n", p->p_name, protonum);
+ nft_print(octx, "\t%-30s\t%u\n", name, protonum);
}
}
@@ -644,7 +745,6 @@ static struct error_record *inet_protocol_type_parse(struct parse_ctx *ctx,
const struct expr *sym,
struct expr **res)
{
- struct protoent *p;
uint8_t proto;
uintmax_t i;
char *end;
@@ -657,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,
@@ -685,17 +787,18 @@ const struct datatype inet_protocol_type = {
static void inet_service_print(const struct expr *expr, struct output_ctx *octx)
{
uint16_t port = mpz_get_be16(expr->value);
- const struct servent *s = getservbyport(port, NULL);
+ char name[NFT_SERVNAME_MAXSIZE];
- if (s == NULL)
+ if (!nft_getservbyport(port, NULL, name, sizeof(name)))
nft_print(octx, "%hu", ntohs(port));
else
- nft_print(octx, "\"%s\"", s->s_name);
+ nft_print(octx, "\"%s\"", name);
}
void inet_service_type_print(const struct expr *expr, struct output_ctx *octx)
{
- if (nft_output_service(octx)) {
+ if (nft_output_service(octx) &&
+ mpz_cmp_ui(expr->value, UINT16_MAX) <= 0) {
inet_service_print(expr, octx);
return;
}
@@ -725,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);
}
@@ -749,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;
@@ -771,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;
}
@@ -793,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;
}
@@ -802,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)
@@ -828,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,
@@ -839,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 = {
@@ -857,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 = {
@@ -880,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 = {
@@ -901,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",
@@ -908,12 +1140,13 @@ const struct datatype icmpx_code_type = {
.size = BITS_PER_BYTE,
.byteorder = BYTEORDER_BIG_ENDIAN,
.basetype = &integer_type,
- .sym_tbl = &icmpx_code_tbl,
+ .parse = icmpx_code_parse,
};
void time_print(uint64_t ms, struct output_ctx *octx)
{
uint64_t days, hours, minutes, seconds;
+ bool printed = false;
if (nft_output_seconds(octx)) {
nft_print(octx, "%" PRIu64 "s", ms / 1000);
@@ -932,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 {
@@ -1093,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;
@@ -1116,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));
}
-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;
}
@@ -1146,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)
@@ -1180,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;
@@ -1192,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;
@@ -1348,10 +1605,10 @@ const struct datatype policy_type = {
#define SYSFS_CGROUPSV2_PATH "/sys/fs/cgroup"
-static const char *cgroupv2_get_path(const char *path, uint64_t id)
+static char *cgroupv2_get_path(const char *path, uint64_t id)
{
- const char *cgroup_path = NULL;
char dent_name[PATH_MAX + 1];
+ char *cgroup_path = NULL;
struct dirent *dent;
struct stat st;
DIR *d;
@@ -1389,7 +1646,7 @@ static void cgroupv2_type_print(const struct expr *expr,
struct output_ctx *octx)
{
uint64_t id = mpz_get_uint64(expr->value);
- const char *cgroup_path;
+ char *cgroup_path;
cgroup_path = cgroupv2_get_path(SYSFS_CGROUPSV2_PATH, id);
if (cgroup_path)
@@ -1398,7 +1655,7 @@ static void cgroupv2_type_print(const struct expr *expr,
else
nft_print(octx, "%" PRIu64, id);
- xfree(cgroup_path);
+ free(cgroup_path);
}
static struct error_record *cgroupv2_type_parse(struct parse_ctx *ctx,
diff --git a/src/dccpopt.c b/src/dccpopt.c
new file mode 100644
index 00000000..ebb645a9
--- /dev/null
+++ b/src/dccpopt.c
@@ -0,0 +1,277 @@
+#include <nft.h>
+
+#include <stddef.h>
+
+#include <datatype.h>
+#include <dccpopt.h>
+#include <expression.h>
+#include <nftables.h>
+#include <utils.h>
+
+#define PHT(__token, __offset, __len) \
+ PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \
+ __offset, __len)
+
+static const struct proto_hdr_template dccpopt_unknown_template =
+ PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0);
+
+/*
+ * Option DCCP- Section
+ * Type Length Meaning Data? Reference
+ * ---- ------ ------- ----- ---------
+ * 0 1 Padding Y 5.8.1
+ * 1 1 Mandatory N 5.8.2
+ * 2 1 Slow Receiver Y 11.6
+ * 3-31 1 Reserved
+ * 32 variable Change L N 6.1
+ * 33 variable Confirm L N 6.2
+ * 34 variable Change R N 6.1
+ * 35 variable Confirm R N 6.2
+ * 36 variable Init Cookie N 8.1.4
+ * 37 3-8 NDP Count Y 7.7
+ * 38 variable Ack Vector [Nonce 0] N 11.4
+ * 39 variable Ack Vector [Nonce 1] N 11.4
+ * 40 variable Data Dropped N 11.7
+ * 41 6 Timestamp Y 13.1
+ * 42 6/8/10 Timestamp Echo Y 13.3
+ * 43 4/6 Elapsed Time N 13.2
+ * 44 6 Data Checksum Y 9.3
+ * 45-127 variable Reserved
+ * 128-255 variable CCID-specific options - 10.3
+ */
+
+static const struct exthdr_desc dccpopt_padding = {
+ .name = "padding",
+ .type = DCCPOPT_PADDING,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_mandatory = {
+ .name = "mandatory",
+ .type = DCCPOPT_MANDATORY,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_slow_receiver = {
+ .name = "slow_receiver",
+ .type = DCCPOPT_SLOW_RECEIVER,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_reserved_short = {
+ .name = "reserved_short",
+ .type = DCCPOPT_RESERVED_SHORT,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_change_l = {
+ .name = "change_l",
+ .type = DCCPOPT_CHANGE_L,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8)
+ },
+};
+
+static const struct exthdr_desc dccpopt_confirm_l = {
+ .name = "confirm_l",
+ .type = DCCPOPT_CONFIRM_L,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_change_r = {
+ .name = "change_r",
+ .type = DCCPOPT_CHANGE_R,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_confirm_r = {
+ .name = "confirm_r",
+ .type = DCCPOPT_CONFIRM_R,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_init_cookie = {
+ .name = "init_cookie",
+ .type = DCCPOPT_INIT_COOKIE,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ndp_count = {
+ .name = "ndp_count",
+ .type = DCCPOPT_NDP_COUNT,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ack_vector_nonce_0 = {
+ .name = "ack_vector_nonce_0",
+ .type = DCCPOPT_ACK_VECTOR_NONCE_0,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ack_vector_nonce_1 = {
+ .name = "ack_vector_nonce_1",
+ .type = DCCPOPT_ACK_VECTOR_NONCE_1,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_data_dropped = {
+ .name = "data_dropped",
+ .type = DCCPOPT_DATA_DROPPED,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_timestamp = {
+ .name = "timestamp",
+ .type = DCCPOPT_TIMESTAMP,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_timestamp_echo = {
+ .name = "timestamp_echo",
+ .type = DCCPOPT_TIMESTAMP_ECHO,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_elapsed_time = {
+ .name = "elapsed_time",
+ .type = DCCPOPT_ELAPSED_TIME,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_data_checksum = {
+ .name = "data_checksum",
+ .type = DCCPOPT_DATA_CHECKSUM,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_reserved_long = {
+ .name = "reserved_long",
+ .type = DCCPOPT_RESERVED_LONG,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ccid_specific = {
+ .name = "ccid_specific",
+ .type = DCCPOPT_CCID_SPECIFIC,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+const struct exthdr_desc *dccpopt_protocols[1 + UINT8_MAX] = {
+ [DCCPOPT_PADDING] = &dccpopt_padding,
+ [DCCPOPT_MANDATORY] = &dccpopt_mandatory,
+ [DCCPOPT_SLOW_RECEIVER] = &dccpopt_slow_receiver,
+ [DCCPOPT_RESERVED_SHORT] = &dccpopt_reserved_short,
+ [DCCPOPT_CHANGE_L] = &dccpopt_change_l,
+ [DCCPOPT_CONFIRM_L] = &dccpopt_confirm_l,
+ [DCCPOPT_CHANGE_R] = &dccpopt_change_r,
+ [DCCPOPT_CONFIRM_R] = &dccpopt_confirm_r,
+ [DCCPOPT_INIT_COOKIE] = &dccpopt_init_cookie,
+ [DCCPOPT_NDP_COUNT] = &dccpopt_ndp_count,
+ [DCCPOPT_ACK_VECTOR_NONCE_0] = &dccpopt_ack_vector_nonce_0,
+ [DCCPOPT_ACK_VECTOR_NONCE_1] = &dccpopt_ack_vector_nonce_1,
+ [DCCPOPT_DATA_DROPPED] = &dccpopt_data_dropped,
+ [DCCPOPT_TIMESTAMP] = &dccpopt_timestamp,
+ [DCCPOPT_TIMESTAMP_ECHO] = &dccpopt_timestamp_echo,
+ [DCCPOPT_ELAPSED_TIME] = &dccpopt_elapsed_time,
+ [DCCPOPT_DATA_CHECKSUM] = &dccpopt_data_checksum,
+ [DCCPOPT_RESERVED_LONG] = &dccpopt_reserved_long,
+ [DCCPOPT_CCID_SPECIFIC] = &dccpopt_ccid_specific,
+};
+
+const struct exthdr_desc *
+dccpopt_find_desc(uint8_t type)
+{
+ enum dccpopt_types proto_idx =
+ 3 <= type && type <= 31 ? DCCPOPT_RESERVED_SHORT :
+ 45 <= type && type <= 127 ? DCCPOPT_RESERVED_LONG :
+ 128 <= type ? DCCPOPT_CCID_SPECIFIC : type;
+
+ return dccpopt_protocols[proto_idx];
+}
+
+struct expr *
+dccpopt_expr_alloc(const struct location *loc, uint8_t type)
+{
+ const struct proto_hdr_template *tmpl;
+ const struct exthdr_desc *desc;
+ struct expr *expr;
+
+ desc = dccpopt_find_desc(type);
+ tmpl = &desc->templates[DCCPOPT_FIELD_TYPE];
+
+ expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE);
+ expr->exthdr.desc = desc;
+ expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
+ expr->exthdr.raw_type = type;
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ expr->exthdr.op = NFT_EXTHDR_OP_DCCP;
+
+ return expr;
+}
+
+void
+dccpopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset,
+ unsigned int len)
+{
+ const struct proto_hdr_template *tmpl;
+ const struct exthdr_desc *desc;
+
+ assert(expr->etype == EXPR_EXTHDR);
+
+ desc = dccpopt_find_desc(type);
+ tmpl = &desc->templates[DCCPOPT_FIELD_TYPE];
+
+ expr->len = len;
+ datatype_set(expr, &boolean_type);
+
+ expr->exthdr.offset = offset;
+ expr->exthdr.desc = desc;
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ expr->exthdr.op = NFT_EXTHDR_OP_DCCP;
+
+ /* Make sure that it's the right template based on offset and
+ * len
+ */
+ if (tmpl->offset != offset || tmpl->len != len)
+ expr->exthdr.tmpl = &dccpopt_unknown_template;
+ else
+ expr->exthdr.tmpl = tmpl;
+}
diff --git a/src/erec.c b/src/erec.c
index aebb8632..fe66abbe 100644
--- a/src/erec.c
+++ b/src/erec.c
@@ -8,12 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#define _GNU_SOURCE
-#include <config.h>
+#include <nft.h>
+
#include <stdio.h>
-#include <string.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <netlink.h>
#include <gmputil.h>
@@ -45,8 +43,8 @@ void erec_add_location(struct error_record *erec, const struct location *loc)
void erec_destroy(struct error_record *erec)
{
- xfree(erec->msg);
- xfree(erec);
+ free(erec->msg);
+ free(erec);
}
__attribute__((format(printf, 3, 0)))
@@ -205,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 0bf6a0d1..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>
@@ -39,6 +38,13 @@
#include <utils.h>
#include <xt.h>
+struct proto_ctx *eval_proto_ctx(struct eval_ctx *ctx)
+{
+ uint8_t idx = ctx->inner_desc ? 1 : 0;
+
+ return &ctx->_pctx[idx];
+}
+
static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr);
static const char * const byteorder_names[] = {
@@ -68,6 +74,33 @@ static int __fmtstring(3, 4) set_error(struct eval_ctx *ctx,
return -1;
}
+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;
@@ -75,7 +108,7 @@ static void key_fix_dtype_byteorder(struct expr *key)
if (dtype->byteorder == key->byteorder)
return;
- datatype_set(key, set_datatype_alloc(dtype, key->byteorder));
+ __datatype_set(key, set_datatype_alloc(dtype, key->byteorder));
}
static int set_evaluate(struct eval_ctx *ctx, struct set *set);
@@ -83,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;
@@ -93,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;
@@ -112,8 +154,6 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
list_add_tail(&cmd->list, &ctx->cmd->list);
}
- set_evaluate(ctx, set);
-
return set_ref_expr_alloc(&expr->location, set);
}
@@ -148,8 +188,38 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
return 0;
/* Conversion for EXPR_CONCAT is handled for single composing ranges */
- if ((*expr)->etype == EXPR_CONCAT)
+ if ((*expr)->etype == EXPR_CONCAT) {
+ struct expr *i, *next, *unary;
+
+ list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
+ if (i->byteorder == BYTEORDER_BIG_ENDIAN)
+ continue;
+
+ basetype = expr_basetype(i)->type;
+ if (basetype == TYPE_STRING)
+ continue;
+
+ assert(basetype == TYPE_INTEGER);
+
+ switch (i->etype) {
+ case EXPR_VALUE:
+ if (i->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(i->value, div_round_up(i->len, BITS_PER_BYTE));
+ break;
+ default:
+ if (div_round_up(i->len, BITS_PER_BYTE) >= 2) {
+ op = byteorder_conversion_op(i, byteorder);
+ unary = unary_expr_alloc(&i->location, op, i);
+ if (expr_evaluate(ctx, &unary) < 0)
+ return -1;
+
+ list_replace(&i->list, &unary->list);
+ }
+ }
+ }
+
return 0;
+ }
basetype = expr_basetype(*expr)->type;
switch (basetype) {
@@ -165,7 +235,7 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
byteorder_names[(*expr)->byteorder]);
}
- if (expr_is_constant(*expr) || (*expr)->len / BITS_PER_BYTE < 2)
+ if (expr_is_constant(*expr) || div_round_up((*expr)->len, BITS_PER_BYTE) < 2)
(*expr)->byteorder = byteorder;
else {
op = byteorder_conversion_op(*expr, byteorder);
@@ -186,7 +256,7 @@ static int table_not_found(struct eval_ctx *ctx)
"%s", strerror(ENOENT));
return cmd_error(ctx, &ctx->cmd->handle.table.location,
- "%s; did you mean table ‘%s’ in family %s?",
+ "%s; did you mean table '%s' in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
}
@@ -202,7 +272,7 @@ static int chain_not_found(struct eval_ctx *ctx)
"%s", strerror(ENOENT));
return cmd_error(ctx, &ctx->cmd->handle.chain.location,
- "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean chain '%s' in table %s '%s'?",
strerror(ENOENT), chain->handle.chain.name,
family2str(chain->handle.family),
table->handle.table.name);
@@ -219,7 +289,7 @@ static int set_not_found(struct eval_ctx *ctx, const struct location *loc,
return cmd_error(ctx, loc, "%s", strerror(ENOENT));
return cmd_error(ctx, loc,
- "%s; did you mean %s ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean %s '%s' in table %s '%s'?",
strerror(ENOENT),
set_is_map(set->flags) ? "map" : "set",
set->handle.set.name,
@@ -238,7 +308,7 @@ static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc,
return cmd_error(ctx, loc, "%s", strerror(ENOENT));
return cmd_error(ctx, loc,
- "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean flowtable '%s' in table %s '%s'?",
strerror(ENOENT), ft->handle.flowtable.name,
family2str(ft->handle.family),
table->handle.table.name);
@@ -249,7 +319,10 @@ static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc,
*/
static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
{
- struct parse_ctx parse_ctx = { .tbl = &ctx->nft->output.tbl, };
+ struct parse_ctx parse_ctx = {
+ .tbl = &ctx->nft->output.tbl,
+ .input = &ctx->nft->input,
+ };
struct error_record *erec;
struct table *table;
struct set *set;
@@ -361,6 +434,7 @@ static int expr_evaluate_integer(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp;
char *valstr, *rangestr;
+ uint32_t masklen;
mpz_t mask;
if (ctx->ectx.maxval > 0 &&
@@ -369,24 +443,29 @@ static int expr_evaluate_integer(struct eval_ctx *ctx, struct expr **exprp)
expr_error(ctx->msgs, expr,
"Value %s exceeds valid range 0-%u",
valstr, ctx->ectx.maxval);
- free(valstr);
+ nft_gmp_free(valstr);
return -1;
}
- mpz_init_bitmask(mask, ctx->ectx.len);
+ if (ctx->stmt_len > ctx->ectx.len)
+ masklen = ctx->stmt_len;
+ else
+ masklen = ctx->ectx.len;
+
+ mpz_init_bitmask(mask, masklen);
if (mpz_cmp(expr->value, mask) > 0) {
valstr = mpz_get_str(NULL, 10, expr->value);
rangestr = mpz_get_str(NULL, 10, mask);
expr_error(ctx->msgs, expr,
"Value %s exceeds valid range 0-%s",
valstr, rangestr);
- free(valstr);
- free(rangestr);
+ nft_gmp_free(valstr);
+ nft_gmp_free(rangestr);
mpz_clear(mask);
return -1;
}
expr->byteorder = ctx->ectx.byteorder;
- expr->len = ctx->ectx.len;
+ expr->len = masklen;
mpz_clear(mask);
return 0;
}
@@ -418,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);
@@ -441,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;
}
@@ -465,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;
@@ -486,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);
@@ -503,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,
@@ -510,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;
@@ -519,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)
@@ -531,9 +644,15 @@ static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
if (expr_evaluate_primary(ctx, exprp) < 0)
return -1;
+ ctx->ectx.key = key;
+
if (expr->exthdr.offset % BITS_PER_BYTE != 0 ||
- expr->len % BITS_PER_BYTE != 0)
- expr_evaluate_bits(ctx, exprp);
+ expr->len % BITS_PER_BYTE != 0) {
+ int err = expr_evaluate_bits(ctx, exprp);
+
+ if (err)
+ return err;
+ }
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT: {
@@ -577,11 +696,13 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
const struct proto_desc *base, *dependency = NULL;
enum proto_bases pb = PROTO_BASE_NETWORK_HDR;
struct expr *expr = *exprp;
+ struct proto_ctx *pctx;
struct stmt *nstmt;
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT:
case NFT_EXTHDR_OP_SCTP:
+ case NFT_EXTHDR_OP_DCCP:
return __expr_evaluate_exthdr(ctx, exprp);
case NFT_EXTHDR_OP_IPV4:
dependency = &proto_ip;
@@ -594,7 +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);
@@ -638,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;
@@ -651,49 +775,52 @@ 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) {
- if (proto_is_dummy(desc)) {
+ assert(base == PROTO_BASE_LL_HDR);
+
+ pctx = eval_proto_ctx(ctx);
+
+ if (proto_is_dummy(desc)) {
+ if (ctx->inner_desc) {
+ proto_ctx_update(pctx, PROTO_BASE_LL_HDR, &payload->location, &proto_eth);
+ } else {
err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
if (err < 0)
return err;
desc = payload->payload.desc;
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
- } else {
- unsigned int i;
+ }
+ } else {
+ unsigned int i;
- /* payload desc stored in the L2 header stack? No conflict. */
- for (i = 0; i < ctx->pctx.stacked_ll_count; i++) {
- if (ctx->pctx.stacked_ll[i] == payload->payload.desc)
- return 0;
- }
+ /* 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;
- if (base == PROTO_BASE_LL_HDR) {
- unsigned int i;
-
- for (i = 0; i < ctx->pctx.stacked_ll_count; i++)
- payload->payload.offset += ctx->pctx.stacked_ll[i]->length;
- }
+ for (i = 0; i < pctx->stacked_ll_count; i++)
+ payload->payload.offset += pctx->stacked_ll[i]->length;
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
@@ -710,19 +837,22 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
struct expr *payload = expr;
enum proto_bases base = payload->payload.base;
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
struct stmt *nstmt;
int err;
if (expr->etype == EXPR_PAYLOAD && expr->payload.is_raw)
return 0;
- desc = ctx->pctx.protocol[base].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[base].desc;
if (desc == NULL) {
if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
return -1;
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
- desc = ctx->pctx.protocol[base].desc;
+
+ desc = pctx->protocol[base].desc;
if (desc == expr->payload.desc)
goto check_icmp;
@@ -732,21 +862,23 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
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 expr_error(ctx->msgs, payload,
"conflicting protocols specified: %s vs. %s",
desc->name,
payload->payload.desc->name);
- payload->payload.offset += ctx->pctx.stacked_ll[0]->length;
+ 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(&ctx->pctx, base)) {
- desc = proto_ctx_find_conflict(&ctx->pctx, base, payload->payload.desc);
+ proto_ctx_is_ambiguous(pctx, base)) {
+ desc = proto_ctx_find_conflict(pctx, base, payload->payload.desc);
assert(desc);
return expr_error(ctx->msgs, payload,
@@ -764,8 +896,8 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
if (desc->base == PROTO_BASE_LL_HDR) {
unsigned int i;
- for (i = 0; i < ctx->pctx.stacked_ll_count; i++)
- payload->payload.offset += ctx->pctx.stacked_ll[i]->length;
+ for (i = 0; i < pctx->stacked_ll_count; i++)
+ payload->payload.offset += pctx->stacked_ll[i]->length;
}
check_icmp:
if (desc != &proto_icmp && desc != &proto_icmp6)
@@ -787,18 +919,19 @@ check_icmp:
/* If we already have context and this payload is on the same
* base, try to resolve the protocol conflict.
*/
- if (payload->payload.base == desc->base) {
- err = resolve_protocol_conflict(ctx, desc, payload);
+ if (base == PROTO_BASE_LL_HDR) {
+ err = resolve_ll_protocol_conflict(ctx, desc, payload);
if (err <= 0)
return err;
- desc = ctx->pctx.protocol[base].desc;
+ desc = pctx->protocol[base].desc;
if (desc == payload->payload.desc)
return 0;
}
return expr_error(ctx->msgs, payload,
- "conflicting protocols specified: %s vs. %s",
- ctx->pctx.protocol[base].desc->name,
+ "conflicting %s protocols specified: %s vs. %s",
+ proto_base_names[base],
+ pctx->protocol[base].desc->name,
payload->payload.desc->name);
}
@@ -810,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)
@@ -821,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.
*/
@@ -836,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:
@@ -864,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:
@@ -875,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)
@@ -897,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;
@@ -911,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);
@@ -927,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:
@@ -953,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);
}
@@ -1006,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;
}
@@ -1017,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;
@@ -1069,8 +1275,8 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
right = range->right;
if (mpz_cmp(left->value, right->value) > 0)
- return expr_error(ctx->msgs, range,
- "Range has zero or negative size");
+ return expr_error(ctx->msgs, range, "Range negative size");
+
datatype_set(range, left->dtype);
range->flags |= EXPR_F_CONSTANT;
return 0;
@@ -1082,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);
@@ -1106,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;
@@ -1173,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)
@@ -1189,8 +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;
+ 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);
@@ -1200,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;
- if (byteorder_conversion(ctx, &op->right, left->byteorder) < 0)
+ /* Both sides need to be in host byte order */
+ if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
+ return -1;
+
+ left = op->left;
+ } else {
+ max_len = left->len;
+ byteorder = left->byteorder;
+ dtype = left->dtype;
+ }
+
+ if (byteorder_conversion(ctx, &op->right, byteorder) < 0)
return -1;
- op->dtype = left->dtype;
- op->byteorder = left->byteorder;
- op->len = left->len;
+ datatype_set(op, dtype);
+ op->byteorder = byteorder;
+ op->len = max_len;
if (expr_is_constant(left))
return constant_binop_simplify(ctx, expr);
@@ -1223,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;
@@ -1263,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(datatype_equal(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)
@@ -1297,20 +1575,28 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
uint32_t type = dtype ? dtype->type : 0, ntype = 0;
int off = dtype ? dtype->subtypes : 0;
unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+ const struct list_head *expressions = NULL;
struct expr *i, *next, *key = NULL;
const struct expr *key_ctx = NULL;
+ bool runaway = false;
uint32_t size = 0;
if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
key_ctx = ctx->ectx.key;
assert(!list_empty(&ctx->ectx.key->expressions));
key = list_first_entry(&ctx->ectx.key->expressions, struct expr, list);
+ expressions = &ctx->ectx.key->expressions;
}
list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
enum byteorder bo = BYTEORDER_INVALID;
unsigned dsize_bytes, dsize = 0;
+ if (runaway) {
+ return expr_binary_error(ctx->msgs, *expr, key_ctx,
+ "too many concatenation components");
+ }
+
if (i->etype == EXPR_CT &&
(i->ct.key == NFT_CT_SRC ||
i->ct.key == NFT_CT_DST))
@@ -1328,7 +1614,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
dsize = key->len;
bo = key->byteorder;
off--;
- } else if (dtype == NULL) {
+ } else if (dtype == NULL || off == 0) {
tmp = datatype_lookup(TYPE_INVALID);
} else {
tmp = concat_subtype_lookup(type, --off);
@@ -1337,19 +1623,30 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
}
__expr_set_context(&ctx->ectx, tmp, bo, dsize, 0);
+ ctx->ectx.key = i;
if (list_member_evaluate(ctx, &i) < 0)
return -1;
+
+ 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 = dtype_clone(i->dtype);
+ clone = datatype_clone(i->dtype);
clone->size = i->len;
clone->byteorder = i->byteorder;
- clone->refcnt = 1;
- i->dtype = clone;
+ __datatype_set(i, clone);
}
if (dtype == NULL && i->dtype->size == 0)
@@ -1366,12 +1663,22 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
dsize_bytes = div_round_up(dsize, BITS_PER_BYTE);
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
size += netlink_padded_len(dsize);
- if (key)
- key = list_next_entry(key, list);
+ if (key && expressions) {
+ if (list_is_last(&key->list, expressions))
+ runaway = true;
+ else
+ key = list_next_entry(key, list);
+ }
+
+ ctx->inner_desc = NULL;
+
+ if (size > NFT_MAX_EXPR_LEN_BITS)
+ return expr_error(ctx->msgs, i, "Concatenation of size %u exceeds maximum size of %u",
+ size, NFT_MAX_EXPR_LEN_BITS);
}
(*expr)->flags |= flags;
- datatype_set(*expr, concat_type_alloc(ntype));
+ __datatype_set(*expr, concat_type_alloc(ntype));
(*expr)->len = size;
if (off > 0)
@@ -1396,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);
}
@@ -1439,7 +1752,8 @@ static int __expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr *elem)
"but element has %d", num_set_exprs,
num_elem_exprs);
} else if (num_set_exprs == 0) {
- if (!(set->flags & NFT_SET_EVAL)) {
+ if (!(set->flags & NFT_SET_ANONYMOUS) &&
+ !(set->flags & NFT_SET_EVAL)) {
elem_stmt = list_first_entry(&elem->stmt_list, struct stmt, list);
return stmt_error(ctx, elem_stmt,
"missing statement in %s declaration",
@@ -1538,6 +1852,7 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
switch (ctx->cmd->op) {
case CMD_CREATE:
case CMD_ADD:
+ case CMD_REPLACE:
case CMD_INSERT:
if (set->automerge) {
ret = set_automerge(ctx->msgs, ctx->cmd, set, init,
@@ -1547,6 +1862,7 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
}
break;
case CMD_DELETE:
+ case CMD_DESTROY:
ret = set_delete(ctx->msgs, ctx->cmd, set, init,
ctx->nft->debug_mask);
break;
@@ -1560,6 +1876,14 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
return ret;
}
+static void expr_evaluate_set_ref(struct eval_ctx *ctx, struct expr *expr)
+{
+ struct set *set = expr->set;
+
+ expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
+ ctx->ectx.key = set->key;
+}
+
static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *set = *expr, *i, *next;
@@ -1625,21 +1949,6 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
if (ctx->set) {
if (ctx->set->flags & NFT_SET_CONCAT)
set->set_flags |= NFT_SET_CONCAT;
- } else if (set->size == 1) {
- i = list_first_entry(&set->expressions, struct expr, list);
- if (i->etype == EXPR_SET_ELEM) {
- switch (i->key->etype) {
- case EXPR_PREFIX:
- case EXPR_RANGE:
- case EXPR_VALUE:
- *expr = i->key;
- i->key = NULL;
- expr_free(set);
- return 0;
- default:
- break;
- }
- }
}
set->set_flags |= NFT_SET_CONSTANT;
@@ -1666,11 +1975,61 @@ static void map_set_concat_info(struct expr *map)
}
}
+static void __mapping_expr_expand(struct expr *i)
+{
+ struct expr *j, *range, *next;
+
+ assert(i->etype == EXPR_MAPPING);
+ switch (i->right->etype) {
+ case EXPR_VALUE:
+ range = range_expr_alloc(&i->location, expr_get(i->right), expr_get(i->right));
+ expr_free(i->right);
+ i->right = range;
+ break;
+ case EXPR_CONCAT:
+ list_for_each_entry_safe(j, next, &i->right->expressions, list) {
+ if (j->etype != EXPR_VALUE)
+ continue;
+
+ range = range_expr_alloc(&j->location, expr_get(j), expr_get(j));
+ list_replace(&j->list, &range->list);
+ expr_free(j);
+ }
+ i->right->flags &= ~EXPR_F_SINGLETON;
+ break;
+ default:
+ break;
+ }
+}
+
+static int mapping_expr_expand(struct eval_ctx *ctx)
+{
+ struct expr *i;
+
+ if (!set_is_anonymous(ctx->set->flags))
+ return 0;
+
+ list_for_each_entry(i, &ctx->set->init->expressions, list) {
+ if (i->etype != EXPR_MAPPING)
+ return expr_error(ctx->msgs, i,
+ "expected mapping, not %s", expr_name(i));
+ __mapping_expr_expand(i);
+ }
+
+ return 0;
+}
+
+static bool datatype_compatible(const struct datatype *a, const struct datatype *b)
+{
+ return (a->type == TYPE_MARK &&
+ datatype_equal(datatype_basetype(a), datatype_basetype(b))) ||
+ datatype_equal(a, b);
+}
+
static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
{
- struct expr_ctx ectx = ctx->ectx;
struct expr *map = *expr, *mappings;
- const struct datatype *dtype;
+ struct expr_ctx ectx = ctx->ectx;
struct expr *key, *data;
if (map->map->etype == EXPR_CT &&
@@ -1697,6 +2056,7 @@ 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;
@@ -1711,16 +2071,29 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->ectx.len, NULL);
}
- dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
- if (dtype->type == TYPE_VERDICT)
+ if (!ectx.dtype) {
+ expr_free(key);
+ return expr_error(ctx->msgs, map,
+ "Implicit map expression without known datatype");
+ }
+
+ if (ectx.dtype->type == TYPE_VERDICT) {
data = verdict_expr_alloc(&netlink_location, 0, NULL);
- else
+ } else {
+ const struct datatype *dtype;
+
+ dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
data = constant_expr_alloc(&netlink_location, dtype,
dtype->byteorder, ectx.len, NULL);
+ datatype_free(dtype);
+ }
mappings = implicit_set_declaration(ctx, "__map%d",
key, data,
- mappings);
+ mappings,
+ NFT_SET_ANONYMOUS);
+ if (!mappings)
+ return -1;
if (ectx.len && mappings->set->data->len != ectx.len)
BUG("%d vs %d\n", mappings->set->data->len, ectx.len);
@@ -1740,9 +2113,13 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
if (binop_transfer(ctx, expr) < 0)
return -1;
- if (ctx->set->data->flags & EXPR_F_INTERVAL)
+ if (ctx->set->data->flags & EXPR_F_INTERVAL) {
ctx->set->data->len *= 2;
+ if (mapping_expr_expand(ctx))
+ return -1;
+ }
+
ctx->set->key->len = ctx->ectx.len;
ctx->set = NULL;
map = *expr;
@@ -1759,13 +2136,16 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
break;
case EXPR_SET_REF:
/* symbol has been already evaluated to set reference */
+ if (!set_is_map(mappings->set->flags))
+ return expr_error(ctx->msgs, map->mappings,
+ "Expression is not a map");
break;
default:
- BUG("invalid mapping expression %s\n",
- expr_name(map->mappings));
+ return expr_binary_error(ctx->msgs, map->mappings, map->map,
+ "invalid mapping expression %s", expr_name(map->mappings));
}
- if (!datatype_equal(map->map->dtype, map->mappings->set->key->dtype))
+ if (!datatype_compatible(map->mappings->set->key->dtype, map->map->dtype))
return expr_binary_error(ctx->msgs, map->mappings, map->map,
"datatype mismatch, map expects %s, "
"mapping expression has type %s",
@@ -1823,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, set->data->byteorder, datalen, 0);
- } 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;
@@ -1845,11 +2222,20 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
data_mapping_has_interval(mapping->right))
set->data->flags |= EXPR_F_INTERVAL;
+ if (!set_is_anonymous(set->flags) &&
+ set->data->flags & EXPR_F_INTERVAL)
+ __mapping_expr_expand(mapping);
+
if (!(set->data->flags & EXPR_F_INTERVAL) &&
!expr_is_singleton(mapping->right))
return expr_error(ctx->msgs, mapping->right,
"Value must be a singleton");
+ if (set_is_objmap(set->flags) && mapping->right->etype != EXPR_VALUE)
+ return expr_error(ctx->msgs, mapping->right,
+ "Object mapping data should be a value, not %s",
+ expr_name(mapping->right));
+
mapping->flags |= EXPR_F_CONSTANT;
return 0;
}
@@ -2084,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;
@@ -2093,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;
@@ -2110,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)
@@ -2137,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;
}
}
@@ -2180,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:
@@ -2187,7 +2624,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
* Update protocol context for payload and meta iiftype
* equality expressions.
*/
- relational_expr_pctx_update(&ctx->pctx, rel);
+ relational_expr_pctx_update(pctx, rel);
/* fall through */
case OP_NEQ:
@@ -2200,7 +2637,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
right->dtype->basetype == NULL ||
right->dtype->basetype->type != TYPE_BITMASK)
return expr_binary_error(ctx->msgs, left, right,
- "negation can only be used with singleton bitmask values");
+ "negation can only be used with singleton bitmask values. Did you mean \"!=\"?");
}
switch (right->etype) {
@@ -2227,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 &&
@@ -2299,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");
@@ -2370,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:
@@ -2404,6 +2847,35 @@ static int expr_evaluate_flagcmp(struct eval_ctx *ctx, struct expr **exprp)
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) {
@@ -2422,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:
@@ -2438,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:
@@ -2545,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:
@@ -2566,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;
}
@@ -2584,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)
@@ -2601,6 +3102,9 @@ static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
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:
@@ -2626,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)
@@ -2646,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;
@@ -2659,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)) {
@@ -2678,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++;
@@ -2753,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)
@@ -2772,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;
@@ -2788,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;
}
@@ -2815,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,
@@ -2829,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;
@@ -2890,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;
}
@@ -2898,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;
@@ -2907,7 +3474,7 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
case NFT_REJECT_ICMPX_UNREACH:
break;
case NFT_REJECT_ICMP_UNREACH:
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
@@ -2915,14 +3482,14 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
if (stmt->reject.family == NFPROTO_IPV4)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
case NFPROTO_IPV6:
case __constant_htons(ETH_P_IPV6):
if (stmt->reject.family == NFPROTO_IPV6)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
default:
return stmt_error(ctx, stmt,
@@ -2937,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;
@@ -2954,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):
@@ -2968,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;
@@ -3002,14 +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;
+ 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;
@@ -3023,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:
@@ -3037,7 +3609,7 @@ 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");
@@ -3061,28 +3633,29 @@ static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
struct stmt *stmt)
{
- int protocol;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc, *base;
+ int protocol;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- stmt->reject.family = ctx->pctx.family;
- if (ctx->pctx.family == NFPROTO_IPV4)
+ stmt->reject.family = pctx->family;
+ if (pctx->family == NFPROTO_IPV4)
stmt->reject.icmp_code = ICMP_PORT_UNREACH;
else
stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
break;
case NFPROTO_INET:
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc == NULL) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
break;
}
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
@@ -3099,14 +3672,14 @@ static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
break;
case NFPROTO_BRIDGE:
case NFPROTO_NETDEV:
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc == NULL) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
break;
}
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
@@ -3125,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;
@@ -3134,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);
@@ -3142,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)
@@ -3161,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;
@@ -3189,13 +3772,14 @@ 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) {
@@ -3205,7 +3789,7 @@ static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->nat.family != NFPROTO_UNSPEC)
return 0;
- nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ nproto = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (nproto == &proto_ip)
stmt->nat.family = NFPROTO_IPV4;
@@ -3234,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);
@@ -3287,7 +3871,14 @@ static bool nat_evaluate_addr_has_th_expr(const struct expr *map)
static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
struct expr **expr)
{
- struct proto_ctx *pctx = &ctx->pctx;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ int err;
+
+ err = stmt_evaluate_arg(ctx, stmt,
+ &inet_service_type, 2 * BITS_PER_BYTE,
+ BYTEORDER_BIG_ENDIAN, expr);
+ if (err < 0)
+ return err;
if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
!nat_evaluate_addr_has_th_expr(stmt->nat.addr))
@@ -3295,41 +3886,75 @@ static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
"transport protocol mapping is only "
"valid after transport protocol match");
- return stmt_evaluate_arg(ctx, stmt,
- &inet_service_type, 2 * BITS_PER_BYTE,
- BYTEORDER_BIG_ENDIAN, expr);
+ return 0;
}
static int stmt_evaluate_l3proto(struct eval_ctx *ctx,
struct stmt *stmt, uint8_t family)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *nproto;
- nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ nproto = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if ((nproto == &proto_ip && family != NFPROTO_IPV4) ||
(nproto == &proto_ip6 && family != NFPROTO_IPV6))
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: %s vs. %s. You must specify ip or ip6 family in %s statement",
- ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name,
+ pctx->protocol[PROTO_BASE_NETWORK_HDR].desc->name,
family2str(family),
stmt->ops->name);
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);
@@ -3342,10 +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 = &ctx->pctx;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
struct expr *one, *two, *data, *tmp;
- const struct datatype *dtype;
- int addr_type, err;
+ const struct datatype *dtype = NULL;
+ const struct datatype *dtype2;
+ int addr_type;
+ int err;
+
+ if (stmt->nat.family == NFPROTO_INET)
+ expr_family_infer(pctx, stmt->nat.addr, &stmt->nat.family);
switch (stmt->nat.family) {
case NFPROTO_IPV4:
@@ -3355,23 +3985,30 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
addr_type = TYPE_IP6ADDR;
break;
default:
- return -1;
+ return stmt_error(ctx, stmt,
+ "specify `%s ip' or '%s ip6' in %s table to disambiguate",
+ stmt_name(stmt), stmt_name(stmt), family2str(pctx->family));
}
dtype = concat_type_alloc((addr_type << TYPE_BITS) | TYPE_INET_SERVICE);
expr_set_context(&ctx->ectx, dtype, dtype->size);
- if (expr_evaluate(ctx, &stmt->nat.addr))
- return -1;
+ if (expr_evaluate(ctx, &stmt->nat.addr)) {
+ err = -1;
+ goto out;
+ }
if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
!nat_evaluate_addr_has_th_expr(stmt->nat.addr)) {
- return stmt_binary_error(ctx, stmt->nat.addr, stmt,
+ err = stmt_binary_error(ctx, stmt->nat.addr, stmt,
"transport protocol mapping is only "
"valid after transport protocol match");
+ goto out;
}
- if (stmt->nat.addr->etype != EXPR_MAP)
- return 0;
+ if (stmt->nat.addr->etype != EXPR_MAP) {
+ err = 0;
+ goto out;
+ }
data = stmt->nat.addr->mappings->set->data;
if (data->flags & EXPR_F_INTERVAL)
@@ -3379,36 +4016,42 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
datatype_set(data, dtype);
- if (expr_ops(data)->type != EXPR_CONCAT)
- return __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ if (expr_ops(data)->type != EXPR_CONCAT) {
+ err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN,
&stmt->nat.addr);
+ goto out;
+ }
one = list_first_entry(&data->expressions, struct expr, list);
two = list_entry(one->list.next, struct expr, list);
- if (one == two || !list_is_last(&two->list, &data->expressions))
- return __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ if (one == two || !list_is_last(&two->list, &data->expressions)) {
+ err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN,
&stmt->nat.addr);
+ goto out;
+ }
- dtype = get_addr_dtype(stmt->nat.family);
+ dtype2 = get_addr_dtype(stmt->nat.family);
tmp = one;
- err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ err = __stmt_evaluate_arg(ctx, stmt, dtype2, dtype2->size,
BYTEORDER_BIG_ENDIAN,
&tmp);
if (err < 0)
- return err;
+ goto out;
if (tmp != one)
BUG("Internal error: Unexpected alteration of l3 expression");
tmp = two;
err = nat_evaluate_transport(ctx, stmt, &tmp);
if (err < 0)
- return err;
+ goto out;
if (tmp != two)
BUG("Internal error: Unexpected alteration of l4 expression");
+out:
+ datatype_free(dtype);
return err;
}
@@ -3434,6 +4077,12 @@ static bool nat_concat_map(struct eval_ctx *ctx, struct stmt *stmt)
if (expr_evaluate(ctx, &stmt->nat.addr->mappings))
return false;
+ if (!set_is_datamap(stmt->nat.addr->mappings->set->flags)) {
+ expr_error(ctx->msgs, stmt->nat.addr->mappings,
+ "Expression is not a map");
+ return false;
+ }
+
if (stmt->nat.addr->mappings->set->data->etype == EXPR_CONCAT ||
stmt->nat.addr->mappings->set->data->dtype->subtypes) {
stmt->nat.type_flags |= STMT_NAT_F_CONCAT;
@@ -3471,7 +4120,7 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
- err = stmt_evaluate_addr(ctx, stmt, stmt->nat.family,
+ err = stmt_evaluate_addr(ctx, stmt, &stmt->nat.family,
&stmt->nat.addr);
if (err < 0)
return err;
@@ -3491,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;
@@ -3506,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");
@@ -3518,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;
@@ -3570,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;
@@ -3590,9 +4240,9 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
struct handle h2 = {};
handle_merge(&rule->handle, &ctx->rule->handle);
- xfree(rule->handle.table.name);
+ free_const(rule->handle.table.name);
rule->handle.table.name = xstrdup(ctx->rule->handle.table.name);
- xfree(rule->handle.chain.name);
+ free_const(rule->handle.chain.name);
rule->handle.chain.name = NULL;
rule->handle.chain_id = chain->handle.chain_id;
if (rule_evaluate(&rule_ctx, rule, CMD_INVALID) < 0)
@@ -3616,9 +4266,10 @@ static int stmt_evaluate_optstrip(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
int err;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
if (stmt->dup.to == NULL)
@@ -3635,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:
@@ -3653,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,
@@ -3673,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:
@@ -3691,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:
@@ -3724,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);
@@ -3746,10 +4421,10 @@ static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
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,
@@ -3797,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,
@@ -3840,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,
@@ -3865,6 +4544,9 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->map.data->comment != NULL)
return expr_error(ctx->msgs, stmt->map.data,
"Data expression comments are not supported");
+ if (stmt->map.data->timeout > 0)
+ return expr_error(ctx->msgs, stmt->map.data,
+ "Data expression timeouts are not supported");
list_for_each_entry(this, &stmt->map.stmt_list, list) {
if (stmt_evaluate(ctx, this) < 0)
@@ -3901,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;
@@ -3934,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",
@@ -3982,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:
@@ -4061,11 +4749,14 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
return -1;
cmd->elem.set = set_get(set);
+ if (set_is_interval(ctx->set->flags)) {
+ if (!(set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, cmd->expr) < 0)
+ return -1;
- if (set_is_interval(ctx->set->flags) &&
- !(set->flags & NFT_SET_CONCAT) &&
- interval_set_eval(ctx, ctx->set, cmd->expr) < 0)
- return -1;
+ assert(cmd->expr->etype == EXPR_SET);
+ cmd->expr->set_flags |= NFT_SET_INTERVAL;
+ }
ctx->set = NULL;
@@ -4105,11 +4796,10 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
i->dtype->type == TYPE_INTEGER) {
struct datatype *dtype;
- dtype = dtype_clone(i->dtype);
+ dtype = datatype_clone(i->dtype);
dtype->size = i->len;
dtype->byteorder = i->byteorder;
- dtype->refcnt = 1;
- i->dtype = dtype;
+ __datatype_set(i, dtype);
}
if (i->dtype->size == 0 && i->len == 0)
@@ -4119,20 +4809,25 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
"expressions",
i->dtype->name);
- if (i->dtype->size)
- assert(i->len == i->dtype->size);
-
flags &= i->flags;
ntype = concat_subtype_add(ntype, i->dtype->type);
dsize_bytes = div_round_up(i->len, BITS_PER_BYTE);
+
+ if (i->dtype->size)
+ assert(dsize_bytes == div_round_up(i->dtype->size, BITS_PER_BYTE));
+
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
size += netlink_padded_len(i->len);
+
+ if (size > NFT_MAX_EXPR_LEN_BITS)
+ return expr_error(ctx->msgs, i, "Concatenation of size %u exceeds maximum size of %u",
+ size, NFT_MAX_EXPR_LEN_BITS);
}
(*expr)->flags |= flags;
- datatype_set(*expr, concat_type_alloc(ntype));
+ __datatype_set(*expr, concat_type_alloc(ntype));
(*expr)->len = size;
expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
@@ -4141,6 +4836,34 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+static int elems_evaluate(struct eval_ctx *ctx, struct set *set)
+{
+ ctx->set = set;
+ if (set->init != NULL) {
+ if (set->key == NULL)
+ return set_error(ctx, set, "set definition does not specify key");
+
+ __expr_set_context(&ctx->ectx, set->key->dtype,
+ set->key->byteorder, set->key->len, 0);
+ if (expr_evaluate(ctx, &set->init) < 0) {
+ set->errors = true;
+ return -1;
+ }
+ if (set->init->etype != EXPR_SET)
+ return expr_error(ctx->msgs, set->init, "Set %s: Unexpected initial type %s, missing { }?",
+ set->handle.set.name, expr_name(set->init));
+ }
+
+ if (set_is_interval(ctx->set->flags) &&
+ !(ctx->set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, set->init) < 0)
+ return -1;
+
+ ctx->set = NULL;
+
+ return 0;
+}
+
static int set_evaluate(struct eval_ctx *ctx, struct set *set)
{
struct set *existing_set = NULL;
@@ -4149,6 +4872,12 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
struct stmt *stmt;
const char *type;
+ type = set_is_map(set->flags) ? "map" : "set";
+
+ if (set->key == NULL)
+ return set_error(ctx, set, "%s definition does not specify key",
+ type);
+
if (!set_is_anonymous(set->flags)) {
table = table_cache_find(&ctx->nft->cache.table_cache,
set->handle.table.name,
@@ -4159,13 +4888,19 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
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);
@@ -4187,6 +4922,21 @@ 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 "
@@ -4231,23 +4981,6 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
}
set->existing_set = existing_set;
- ctx->set = set;
- if (set->init != NULL) {
- __expr_set_context(&ctx->ectx, set->key->dtype,
- set->key->byteorder, set->key->len, 0);
- if (expr_evaluate(ctx, &set->init) < 0)
- return -1;
- if (set->init->etype != EXPR_SET)
- return expr_error(ctx->msgs, set->init, "Set %s: Unexpected initial type %s, missing { }?",
- set->handle.set.name, expr_name(set->init));
- }
-
- if (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;
}
@@ -4278,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;
@@ -4376,8 +5109,12 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
if (table == NULL)
return table_not_found(ctx);
- if (!ft_cache_find(table, ft->handle.flowtable.name))
+ if (!ft_cache_find(table, ft->handle.flowtable.name)) {
+ if (!ft->hook.name && !ft->dev_expr)
+ return chain_error(ctx, ft, "missing hook and priority in flowtable declaration");
+
ft_cache_add(flowtable_get(ft), table);
+ }
if (ft->hook.name) {
ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name);
@@ -4487,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;
@@ -4502,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);
@@ -4565,7 +5306,6 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
{
struct table *table;
- struct rule *rule;
table = table_cache_find(&ctx->nft->cache.table_cache,
ctx->cmd->handle.table.name,
@@ -4575,7 +5315,7 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
if (chain == NULL) {
if (!chain_cache_find(table, ctx->cmd->handle.chain.name)) {
- chain = chain_alloc(NULL);
+ chain = chain_alloc();
handle_merge(&chain->handle, &ctx->cmd->handle);
chain_cache_add(chain, table);
}
@@ -4605,15 +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->dev_expr) {
+ if (!(chain->flags & CHAIN_F_BASECHAIN))
+ chain->flags |= CHAIN_F_BASECHAIN;
if (chain->handle.family == NFPROTO_NETDEV ||
(chain->handle.family == NFPROTO_INET &&
chain->hook.num == NF_INET_INGRESS)) {
- if (!chain->dev_expr)
- return __stmt_binary_error(ctx, &chain->loc, NULL,
- "Missing `device' in this chain definition");
-
- if (!evaluate_device_expr(ctx, &chain->dev_expr))
+ if (chain->dev_expr &&
+ !evaluate_device_expr(ctx, &chain->dev_expr))
return -1;
} else if (chain->dev_expr) {
return __stmt_binary_error(ctx, &chain->dev_expr->location, NULL,
@@ -4621,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;
}
@@ -4659,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;
@@ -4693,11 +5430,6 @@ static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj)
static int table_evaluate(struct eval_ctx *ctx, struct table *table)
{
- struct flowtable *ft;
- struct chain *chain;
- struct set *set;
- struct obj *obj;
-
if (!table_cache_find(&ctx->nft->cache.table_cache,
ctx->cmd->handle.table.name,
ctx->cmd->handle.family)) {
@@ -4710,34 +5442,6 @@ static int table_evaluate(struct eval_ctx *ctx, struct table *table)
}
}
- if (ctx->cmd->table == NULL)
- return 0;
-
- ctx->table = table;
- list_for_each_entry(set, &table->sets, list) {
- expr_set_context(&ctx->ectx, NULL, 0);
- handle_merge(&set->handle, &table->handle);
- if (set_evaluate(ctx, set) < 0)
- return -1;
- }
- list_for_each_entry(chain, &table->chains, list) {
- handle_merge(&chain->handle, &table->handle);
- ctx->cmd->handle.chain.location = chain->location;
- if (chain_evaluate(ctx, chain) < 0)
- return -1;
- }
- list_for_each_entry(ft, &table->flowtables, list) {
- handle_merge(&ft->handle, &table->handle);
- if (flowtable_evaluate(ctx, ft) < 0)
- return -1;
- }
- list_for_each_entry(obj, &table->objs, list) {
- handle_merge(&obj->handle, &table->handle);
- if (obj_evaluate(ctx, obj) < 0)
- return -1;
- }
-
- ctx->table = NULL;
return 0;
}
@@ -4749,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);
@@ -4948,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);
@@ -4994,38 +5700,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
return 0;
case CMD_OBJ_SET:
- table = table_cache_find(&ctx->nft->cache.table_cache,
- cmd->handle.table.name,
- cmd->handle.family);
- if (!table)
- return table_not_found(ctx);
-
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return set_not_found(ctx, &ctx->cmd->handle.set.location,
- ctx->cmd->handle.set.name);
- else if (!set_is_literal(set->flags))
- return cmd_error(ctx, &ctx->cmd->handle.set.location,
- "%s", strerror(ENOENT));
-
- return 0;
- case CMD_OBJ_METER:
- table = table_cache_find(&ctx->nft->cache.table_cache,
- cmd->handle.table.name,
- cmd->handle.family);
- if (!table)
- return table_not_found(ctx);
-
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return set_not_found(ctx, &ctx->cmd->handle.set.location,
- ctx->cmd->handle.set.name);
- else if (!set_is_meter(set->flags))
- return cmd_error(ctx, &ctx->cmd->handle.set.location,
- "%s", strerror(ENOENT));
-
- return 0;
case CMD_OBJ_MAP:
+ case CMD_OBJ_METER:
table = table_cache_find(&ctx->nft->cache.table_cache,
cmd->handle.table.name,
cmd->handle.family);
@@ -5036,10 +5712,13 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
if (set == NULL)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
- else if (!map_is_literal(set->flags))
+ if ((cmd->obj == CMD_OBJ_SET && !set_is_literal(set->flags)) ||
+ (cmd->obj == CMD_OBJ_MAP && !map_is_literal(set->flags)) ||
+ (cmd->obj == CMD_OBJ_METER && !set_is_meter_compat(set->flags)))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+ cmd->set = set_get(set);
return 0;
case CMD_OBJ_CHAIN:
table = table_cache_find(&ctx->nft->cache.table_cache,
@@ -5089,6 +5768,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_FLOWTABLES:
case CMD_OBJ_SECMARKS:
case CMD_OBJ_SYNPROXYS:
+ case CMD_OBJ_CT_TIMEOUTS:
+ case CMD_OBJ_CT_EXPECTATIONS:
if (cmd->handle.table.name == NULL)
return 0;
if (!table_cache_find(&ctx->nft->cache.table_cache,
@@ -5124,6 +5805,8 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_QUOTA:
case CMD_OBJ_COUNTERS:
case CMD_OBJ_QUOTAS:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
if (cmd->handle.table.name == NULL)
return 0;
if (!table_cache_find(&ctx->nft->cache.table_cache,
@@ -5132,6 +5815,11 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
return table_not_found(ctx);
return 0;
+ case CMD_OBJ_ELEMENTS:
+ return setelem_evaluate(ctx, cmd);
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ return cmd_evaluate_list(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -5205,7 +5893,7 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
if (set == NULL)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
- else if (!set_is_meter(set->flags))
+ else if (!set_is_meter_compat(set->flags))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
@@ -5368,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];
@@ -5400,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 7390089c..cb2573fe 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -8,11 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <limits.h>
#include <expression.h>
@@ -29,6 +28,7 @@
extern const struct expr_ops ct_expr_ops;
extern const struct expr_ops fib_expr_ops;
extern const struct expr_ops hash_expr_ops;
+extern const struct expr_ops inner_expr_ops;
extern const struct expr_ops meta_expr_ops;
extern const struct expr_ops numgen_expr_ops;
extern const struct expr_ops osf_expr_ops;
@@ -94,7 +94,7 @@ void expr_free(struct expr *expr)
*/
if (expr->etype != EXPR_INVALID)
expr_destroy(expr);
- xfree(expr);
+ free(expr);
}
void expr_print(const struct expr *expr, struct output_ctx *octx)
@@ -140,8 +140,10 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, "%s expression, datatype %s (%s)",
expr_name(expr), dtype->name, dtype->desc);
- if (dtype == &invalid_type)
+ if (dtype == &invalid_type) {
+ nft_print(octx, "\n");
return;
+ }
}
if (dtype->basetype != NULL) {
@@ -314,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 = {
@@ -994,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;
@@ -1012,7 +1014,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
if (!dtype)
goto err_free;
- concat_expr->dtype = datatype_get(dtype);
+ __datatype_set(concat_expr, dtype);
concat_expr->len = len;
return concat_expr;
@@ -1335,15 +1337,14 @@ static void set_elem_expr_destroy(struct expr *expr)
{
struct stmt *stmt, *next;
- xfree(expr->comment);
+ free_const(expr->comment);
expr_free(expr->key);
list_for_each_entry_safe(stmt, next, &expr->stmt_list, list)
stmt_free(stmt);
}
-static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
+static void __set_elem_expr_clone(struct expr *new, const struct expr *expr)
{
- new->key = expr_clone(expr->key);
new->expiration = expr->expiration;
new->timeout = expr->timeout;
if (expr->comment)
@@ -1351,6 +1352,12 @@ static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
init_list_head(&new->stmt_list);
}
+static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
+{
+ new->key = expr_clone(expr->key);
+ __set_elem_expr_clone(new, expr);
+}
+
static const struct expr_ops set_elem_expr_ops = {
.type = EXPR_SET_ELEM,
.name = "set element",
@@ -1378,11 +1385,17 @@ static void set_elem_catchall_expr_print(const struct expr *expr,
nft_print(octx, "*");
}
+static void set_elem_catchall_expr_clone(struct expr *new, const struct expr *expr)
+{
+ __set_elem_expr_clone(new, expr);
+}
+
static const struct expr_ops set_elem_catchall_expr_ops = {
.type = EXPR_SET_ELEM_CATCHALL,
.name = "catch-all set element",
.print = set_elem_catchall_expr_print,
.json = set_elem_catchall_expr_json,
+ .clone = set_elem_catchall_expr_clone,
};
struct expr *set_elem_catchall_expr_alloc(const struct location *loc)
@@ -1497,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;
@@ -1531,20 +1542,23 @@ static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
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 3e5f5cd8..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>
@@ -84,6 +83,9 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
nft_print(octx, " %s", expr->exthdr.tmpl->token);
+ } else if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) {
+ nft_print(octx, "dccp option %d", expr->exthdr.raw_type);
+ return;
} else {
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
nft_print(octx, "exthdr %s", name);
@@ -177,6 +179,8 @@ static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
case NFT_EXTHDR_OP_SCTP:
return sctp_chunk_expr_alloc(&internal_location,
desc_id, type);
+ case NFT_EXTHDR_OP_DCCP:
+ return dccpopt_expr_alloc(&internal_location, type);
case __NFT_EXTHDR_OP_MAX:
return NULL;
}
@@ -206,6 +210,7 @@ static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
case NFT_EXTHDR_OP_TCPOPT:
case NFT_EXTHDR_OP_IPV4:
case NFT_EXTHDR_OP_SCTP:
+ case NFT_EXTHDR_OP_DCCP:
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_OP, op);
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, expr->exthdr.raw_type);
break;
@@ -283,7 +288,7 @@ struct stmt *exthdr_stmt_alloc(const struct location *loc,
return stmt;
}
-static const struct exthdr_desc *exthdr_protocols[IPPROTO_MAX] = {
+static const struct exthdr_desc *exthdr_protocols[UINT8_MAX + 1] = {
[IPPROTO_HOPOPTS] = &exthdr_hbh,
[IPPROTO_ROUTING] = &exthdr_rt,
[IPPROTO_FRAGMENT] = &exthdr_frag,
@@ -332,14 +337,15 @@ void exthdr_init_raw(struct expr *expr, uint8_t type,
return ipopt_init_raw(expr, type, offset, len, flags, true);
if (op == NFT_EXTHDR_OP_SCTP)
return sctp_chunk_init_raw(expr, type, offset, len, flags);
+ if (op == NFT_EXTHDR_OP_DCCP)
+ return dccpopt_init_raw(expr, type, offset, len);
expr->len = len;
expr->exthdr.flags = flags;
expr->exthdr.offset = offset;
expr->exthdr.desc = NULL;
- if (type < array_size(exthdr_protocols))
- expr->exthdr.desc = exthdr_protocols[type];
+ expr->exthdr.desc = exthdr_protocols[type];
if (expr->exthdr.desc == NULL)
goto out;
@@ -398,7 +404,7 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i
found = tcpopt_find_template(expr, off, mask_len - mask_offset);
break;
case NFT_EXTHDR_OP_IPV6:
- exthdr_init_raw(expr, expr->exthdr.desc->type,
+ exthdr_init_raw(expr, expr->exthdr.raw_type,
off, mask_len - mask_offset, expr->exthdr.op, 0);
/* still failed to find a template... Bug. */
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 c0642e0c..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>
diff --git a/src/intervals.c b/src/intervals.c
index 13009ca1..6c3f36fe 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -6,11 +6,15 @@
* 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];
@@ -24,6 +28,9 @@ static void setelem_expr_to_range(struct expr *expr)
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)
@@ -125,7 +132,7 @@ static void set_sort_splice(struct expr *init, struct set *set)
set_to_range(init);
list_expr_sort(&init->expressions);
- if (!existing_set)
+ if (!existing_set || existing_set->errors)
return;
if (existing_set->init) {
@@ -213,7 +220,7 @@ static struct expr *interval_expr_key(struct expr *i)
return elem;
}
-void set_to_range(struct expr *init)
+static void set_to_range(struct expr *init)
{
struct expr *i, *elem;
@@ -416,11 +423,12 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
list_del(&i->list);
expr_free(i);
}
- } else if (set->automerge &&
- setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) {
- expr_error(msgs, i, "element does not exist");
- err = -1;
- goto err;
+ } else if (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;
@@ -458,7 +466,7 @@ static int __set_delete(struct list_head *msgs, struct expr *i, struct set *set,
unsigned int debug_mask)
{
i->flags |= EXPR_F_REMOVE;
- list_move(&i->list, &existing_set->init->expressions);
+ list_move_tail(&i->list, &existing_set->init->expressions);
list_expr_sort(&existing_set->init->expressions);
return setelem_delete(msgs, set, init, existing_set->init, debug_mask);
@@ -708,9 +716,9 @@ int set_to_intervals(const struct set *set, struct expr *init, bool add)
if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
- newelem = set_elem_expr_alloc(&internal_location, expr);
+ newelem = set_elem_expr_alloc(&expr->location, expr);
if (i->etype == EXPR_MAPPING) {
- newelem = mapping_expr_alloc(&internal_location,
+ newelem = mapping_expr_alloc(&expr->location,
newelem,
expr_get(i->right));
}
diff --git a/src/ipopt.c b/src/ipopt.c
index 67e904ff..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>
diff --git a/src/json.c b/src/json.c
index 6662f808..b4fad0ab 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1,12 +1,21 @@
-#define _GNU_SOURCE
+/*
+ * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <stdio.h>
-#include <string.h>
#include <expression.h>
#include <list.h>
#include <netlink.h>
#include <rule.h>
#include <rt.h>
+#include "nftutils.h"
#include <netdb.h>
#include <netinet/icmp6.h>
@@ -33,6 +42,15 @@
})
#endif
+static int json_array_extend_new(json_t *array, json_t *other_array)
+{
+ int ret;
+
+ ret = json_array_extend(array, other_array);
+ json_decref(other_array);
+ return ret;
+}
+
static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx)
{
const struct expr_ops *ops;
@@ -61,8 +79,9 @@ static json_t *set_dtype_json(const struct expr *key)
{
char *namedup = xstrdup(key->dtype->name), *tok;
json_t *root = NULL;
+ char *tok_safe;
- tok = strtok(namedup, " .");
+ tok = strtok_r(namedup, " .", &tok_safe);
while (tok) {
json_t *jtok = json_string(tok);
if (!root)
@@ -71,9 +90,9 @@ static json_t *set_dtype_json(const struct expr *key)
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;
}
@@ -82,12 +101,6 @@ 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);
@@ -126,15 +139,15 @@ static json_t *set_stmt_list_json(const struct list_head *stmt_list,
static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
{
- json_t *root, *tmp;
- const char *type, *datatype_ext = NULL;
+ 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 {
@@ -151,7 +164,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
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) {
@@ -171,6 +184,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);
@@ -188,6 +203,8 @@ 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 (!nft_output_terse(octx) && set->init && set->init->size > 0) {
json_t *array = json_array();
@@ -251,9 +268,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),
@@ -261,6 +277,9 @@ 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));
@@ -272,17 +291,19 @@ static json_t *chain_print_json(const struct chain *chain)
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);
}
@@ -292,10 +313,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);
}
@@ -487,7 +508,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;
@@ -508,6 +529,9 @@ 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);
}
@@ -525,11 +549,26 @@ json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx)
"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_new(a,
+ __binop_expr_json(op, expr->left, octx));
+ json_array_extend_new(a,
+ __binop_expr_json(op, expr->right, octx));
+ } else {
+ json_array_append_new(a, expr_print_json(expr, octx));
+ }
+ return a;
+}
+
json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx)
{
- return json_pack("{s:[o, o]}", expr_op_symbols[expr->op],
- expr_print_json(expr->left, octx),
- expr_print_json(expr->right, octx));
+ return 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)
@@ -565,15 +604,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);
}
@@ -743,6 +790,11 @@ json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx)
return json_pack("{s:o}", "tcp 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);
if (!is_exists)
json_object_set_new(root, "field", json_string(field));
@@ -1072,12 +1124,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);
}
@@ -1085,13 +1136,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)
@@ -1469,6 +1520,14 @@ 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)
{
json_t *root;
@@ -1487,6 +1546,25 @@ json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *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)
{
const char *name;
@@ -1624,6 +1702,19 @@ json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
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)
{
@@ -1637,6 +1728,11 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
tmp = table_print_json(table);
json_array_append_new(root, tmp);
+ /* both maps and rules may refer to chains, list them first */
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
+ tmp = chain_print_json(chain);
+ json_array_append_new(root, tmp);
+ }
list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
tmp = obj_print_json(obj);
json_array_append_new(root, tmp);
@@ -1652,17 +1748,13 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
json_array_append_new(root, tmp);
}
list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
- tmp = chain_print_json(chain);
- json_array_append_new(root, tmp);
-
list_for_each_entry(rule, &chain->rules, list) {
tmp = rule_print_json(&ctx->nft->output, rule);
json_array_append_new(rules, tmp);
}
}
- json_array_extend(root, rules);
- json_decref(rules);
+ json_array_extend_new(root, rules);
return root;
}
@@ -1670,7 +1762,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
{
unsigned int family = cmd->handle.family;
- json_t *root = json_array(), *tmp;
+ json_t *root = json_array();
struct table *table;
list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
@@ -1678,9 +1770,7 @@ static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
table->handle.family != family)
continue;
- tmp = table_print_json_full(ctx, table);
- json_array_extend(root, tmp);
- json_decref(tmp);
+ json_array_extend_new(root, table_print_json_full(ctx, table));
}
return root;
@@ -1757,10 +1847,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_cache_find(table, cmd->handle.set.name);
+ struct set *set = cmd->set;
- if (set == NULL)
- return json_null();
+ if (!set) {
+ set = set_cache_find(table, cmd->handle.set.name);
+ if (set == NULL)
+ return json_null();
+ }
return json_pack("[o]", set_print_json(&ctx->nft->output, set));
}
@@ -1896,6 +1989,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;
diff --git a/src/libnftables.c b/src/libnftables.c
index a376825d..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,12 +16,10 @@
#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, last_seqnum = UINT32_MAX, num_cmds = 0;
struct netlink_ctx ctx = {
@@ -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;
@@ -147,11 +154,11 @@ void nft_ctx_clear_vars(struct nft_ctx *ctx)
unsigned int i;
for (i = 0; i < ctx->num_vars; i++) {
- xfree(ctx->vars[i].key);
- xfree(ctx->vars[i].value);
+ free_const(ctx->vars[i].key);
+ free_const(ctx->vars[i].value);
}
ctx->num_vars = 0;
- xfree(ctx->vars);
+ free(ctx->vars);
}
EXPORT_SYMBOL(nft_ctx_add_include_path);
@@ -175,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);
@@ -213,8 +210,7 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
ctx->output.error_fp = stderr;
init_list_head(&ctx->vars_ctx.indesc_list);
- if (flags == NFT_CTX_DEFAULT)
- nft_ctx_netlink_init(ctx);
+ ctx->nf_sock = nft_mnl_socket_open();
return ctx;
}
@@ -338,8 +334,7 @@ const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx)
EXPORT_SYMBOL(nft_ctx_free);
void nft_ctx_free(struct nft_ctx *ctx)
{
- if (ctx->nf_sock)
- mnl_socket_close(ctx->nf_sock);
+ mnl_socket_close(ctx->nf_sock);
exit_cookie(&ctx->output.output_cookie);
exit_cookie(&ctx->output.error_cookie);
@@ -348,9 +343,9 @@ void nft_ctx_free(struct nft_ctx *ctx)
nft_ctx_clear_vars(ctx);
nft_ctx_clear_include_paths(ctx);
scope_free(ctx->top_scope);
- xfree(ctx->state);
+ free(ctx->state);
nft_exit(ctx);
- xfree(ctx);
+ free(ctx);
}
EXPORT_SYMBOL(nft_ctx_set_output);
@@ -403,6 +398,22 @@ void nft_ctx_set_optimize(struct nft_ctx *ctx, uint32_t flags)
ctx->optimize_flags = flags;
}
+EXPORT_SYMBOL(nft_ctx_input_get_flags);
+unsigned int nft_ctx_input_get_flags(struct nft_ctx *ctx)
+{
+ return ctx->input.flags;
+}
+
+EXPORT_SYMBOL(nft_ctx_input_set_flags);
+unsigned int nft_ctx_input_set_flags(struct nft_ctx *ctx, unsigned int flags)
+{
+ unsigned int old_flags;
+
+ old_flags = ctx->input.flags;
+ ctx->input.flags = flags;
+ return old_flags;
+}
+
EXPORT_SYMBOL(nft_ctx_output_get_flags);
unsigned int nft_ctx_output_get_flags(struct nft_ctx *ctx)
{
@@ -520,6 +531,14 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
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,
@@ -539,13 +558,6 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
if (err < 0 || nft->state->nerrs)
return -1;
- list_for_each_entry(cmd, cmds, list) {
- if (cmd->op != CMD_ADD)
- continue;
-
- nft_cmd_expand(cmd);
- }
-
return 0;
}
@@ -561,7 +573,7 @@ int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
nlbuf = xzalloc(strlen(buf) + 2);
sprintf(nlbuf, "%s\n", buf);
- if (nft_output_json(&nft->output))
+ if (nft_output_json(&nft->output) || nft_input_json(&nft->input))
rc = nft_parse_json_buffer(nft, nlbuf, &msgs, &cmds);
if (rc == -EINVAL)
rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds,
@@ -583,7 +595,7 @@ int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
goto err;
}
- if (nft_netlink(nft, &cmds, &msgs, nft->nf_sock) != 0)
+ if (nft_netlink(nft, &cmds, &msgs) != 0)
rc = -1;
err:
erec_print_list(&nft->output, &msgs, nft->debug_mask);
@@ -602,8 +614,10 @@ err:
nft_output_json(&nft->output) &&
nft_output_echo(&nft->output))
json_print_echo(nft);
- if (rc)
+
+ if (rc || nft->check)
nft_cache_release(&nft->cache);
+
return rc;
}
@@ -648,19 +662,52 @@ retry:
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);
+ erec = filename_is_useable(nft, filename);
+ if (erec) {
+ erec_print(&nft->output, erec, nft->debug_mask);
+ erec_destroy(erec);
+ return -1;
+ }
+
rc = load_cmdline_vars(nft, &msgs);
if (rc < 0)
goto err;
rc = -EINVAL;
- if (nft_output_json(&nft->output))
+ if (nft_output_json(&nft->output) || nft_input_json(&nft->input))
rc = nft_parse_json_filename(nft, filename, &msgs, &cmds);
if (rc == -EINVAL)
rc = nft_parse_bison_filename(nft, filename, &msgs, &cmds);
@@ -679,7 +726,7 @@ static int __nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename
goto err;
}
- if (nft_netlink(nft, &cmds, &msgs, nft->nf_sock) != 0)
+ if (nft_netlink(nft, &cmds, &msgs) != 0)
rc = -1;
err:
erec_print_list(&nft->output, &msgs, nft->debug_mask);
@@ -697,18 +744,19 @@ err:
list_for_each_entry_safe(indesc, next, &nft->vars_ctx.indesc_list, list) {
if (indesc->name)
- xfree(indesc->name);
+ free_const(indesc->name);
- xfree(indesc);
+ free(indesc);
}
}
- xfree(nft->vars_ctx.buf);
+ free_const(nft->vars_ctx.buf);
if (!rc &&
nft_output_json(&nft->output) &&
nft_output_echo(&nft->output))
json_print_echo(nft);
- if (rc)
+
+ if (rc || nft->check)
nft_cache_release(&nft->cache);
scope_release(nft->state->scopes[0]);
@@ -752,12 +800,12 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
if (nft->optimize_flags) {
ret = nft_run_optimized_file(nft, filename);
- xfree(nft->stdin_buf);
+ free_const(nft->stdin_buf);
return ret;
}
ret = __nft_run_cmd_from_filename(nft, filename);
- xfree(nft->stdin_buf);
+ free_const(nft->stdin_buf);
return ret;
}
diff --git a/src/libnftables.map b/src/libnftables.map
index a46a3ad5..9369f44f 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -33,3 +33,8 @@ LIBNFTABLES_3 {
nft_ctx_set_optimize;
nft_ctx_get_optimize;
} LIBNFTABLES_2;
+
+LIBNFTABLES_4 {
+ nft_ctx_input_get_flags;
+ nft_ctx_input_set_flags;
+} LIBNFTABLES_3;
diff --git a/src/main.c b/src/main.c
index 9bd25db8..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>
@@ -325,6 +325,7 @@ static bool nft_options_check(int argc, char * const argv[])
{
bool skip = false, nonoption = false;
int pos = 0, i;
+ size_t j;
for (i = 1; i < argc; i++) {
pos += strlen(argv[i - 1]) + 1;
@@ -333,23 +334,22 @@ static bool nft_options_check(int argc, char * const argv[])
} else if (skip) {
skip = false;
continue;
- } else if (argv[i][0] == '-') {
- if (nonoption) {
- nft_options_error(argc, argv, pos);
- return false;
- } else if (argv[i][1] == 'd' ||
- argv[i][1] == 'I' ||
- argv[i][1] == 'f' ||
- argv[i][1] == 'D' ||
- !strcmp(argv[i], "--debug") ||
- !strcmp(argv[i], "--includepath") ||
- !strcmp(argv[i], "--define") ||
- !strcmp(argv[i], "--file")) {
- skip = true;
- continue;
- }
} else if (argv[i][0] != '-') {
nonoption = true;
+ continue;
+ }
+ if (nonoption) {
+ nft_options_error(argc, argv, pos);
+ return false;
+ }
+ for (j = 0; j < NR_NFT_OPTIONS; j++) {
+ if (nft_options[j].arg &&
+ (argv[i][1] == (char)nft_options[j].val ||
+ (argv[i][1] == '-' &&
+ !strcmp(argv[i] + 2, nft_options[j].name)))) {
+ skip = true;
+ break;
+ }
}
}
@@ -361,11 +361,11 @@ int main(int argc, char * const *argv)
const struct option *options = get_options();
bool interactive = false, define = false;
const char *optstring = get_optstring();
- char *buf = NULL, *filename = NULL;
unsigned int output_flags = 0;
+ int i, val, rc = EXIT_SUCCESS;
unsigned int debug_mask;
+ char *filename = NULL;
unsigned int len;
- int i, val, rc;
/* nftables cannot be used with setuid in a safe way. */
if (getuid() != geteuid())
@@ -384,20 +384,20 @@ int main(int argc, char * const *argv)
switch (val) {
case OPT_HELP:
show_help(argv[0]);
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_VERSION:
printf("%s v%s (%s)\n",
PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME);
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_VERSION_LONG:
show_version();
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_DEFINE:
if (nft_ctx_add_var(nft, optarg)) {
fprintf(stderr,
"Failed to define variable '%s'\n",
optarg);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
define = true;
break;
@@ -405,9 +405,19 @@ int main(int argc, char * const *argv)
nft_ctx_set_dry_run(nft, true);
break;
case OPT_FILE:
+ if (interactive) {
+ fprintf(stderr,
+ "Error: -i/--interactive and -f/--file options cannot be combined\n");
+ goto out_fail;
+ }
filename = optarg;
break;
case OPT_INTERACTIVE:
+ if (filename) {
+ fprintf(stderr,
+ "Error: -i/--interactive and -f/--file options cannot be combined\n");
+ goto out_fail;
+ }
interactive = true;
break;
case OPT_INCLUDEPATH:
@@ -415,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:
@@ -450,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)
@@ -470,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:
@@ -492,18 +502,20 @@ int main(int argc, char * const *argv)
nft_ctx_set_optimize(nft, 0x1);
break;
case OPT_INVALID:
- exit(EXIT_FAILURE);
+ goto out_fail;
}
}
if (!filename && define) {
fprintf(stderr, "Error: -D/--define can only be used with -f/--filename\n");
- exit(EXIT_FAILURE);
+ goto out_fail;
}
nft_ctx_output_set_flags(nft, output_flags);
if (optind != argc) {
+ char *buf;
+
for (len = 0, i = optind; i < argc; i++)
len += strlen(argv[i]) + strlen(" ");
@@ -511,7 +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]);
@@ -519,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 dca71422..5e676be1 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -2,17 +2,16 @@
* Copyright (c) 2017 Elise Lennion <elise.lennion@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
-#include <stdint.h>
+#include <nft.h>
+
#include <expression.h>
#include <gmputil.h>
#include <list.h>
-static void expr_msort_value(const struct expr *expr, mpz_t value);
-
static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
{
unsigned int len = 0, ilen;
@@ -28,20 +27,17 @@ static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
mpz_import_data(value, data, BYTEORDER_HOST_ENDIAN, len);
}
-static void expr_msort_value(const struct expr *expr, mpz_t value)
+static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value)
{
switch (expr->etype) {
case EXPR_SET_ELEM:
- expr_msort_value(expr->key, value);
- break;
+ return expr_msort_value(expr->key, value);
case EXPR_BINOP:
case EXPR_MAPPING:
case EXPR_RANGE:
- expr_msort_value(expr->left, value);
- break;
+ return expr_msort_value(expr->left, value);
case EXPR_VALUE:
- mpz_set(value, expr->value);
- break;
+ return expr->value;
case EXPR_CONCAT:
concat_expr_msort_value(expr, value);
break;
@@ -52,20 +48,24 @@ static void expr_msort_value(const struct expr *expr, mpz_t value)
default:
BUG("Unknown expression %s\n", expr_name(expr));
}
+ return value;
}
static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
{
- mpz_t value1, value2;
+ mpz_srcptr value1;
+ mpz_srcptr value2;
+ mpz_t value1_tmp;
+ mpz_t value2_tmp;
int ret;
- mpz_init(value1);
- mpz_init(value2);
- expr_msort_value(e1, value1);
- expr_msort_value(e2, value2);
+ mpz_init(value1_tmp);
+ mpz_init(value2_tmp);
+ value1 = expr_msort_value(e1, value1_tmp);
+ value2 = expr_msort_value(e2, value2_tmp);
ret = mpz_cmp(value1, value2);
- mpz_clear(value1);
- mpz_clear(value2);
+ mpz_clear(value1_tmp);
+ mpz_clear(value2_tmp);
return ret;
}
@@ -78,7 +78,7 @@ void list_splice_sorted(struct list_head *list, struct list_head *head)
while (l != list) {
if (h == head ||
expr_msort_cmp(list_entry(l, typeof(struct expr), list),
- list_entry(h, typeof(struct expr), list)) < 0) {
+ list_entry(h, typeof(struct expr), list)) <= 0) {
l = l->next;
list_add_tail(l->prev, h);
continue;
diff --git a/src/meta.c b/src/meta.c
index 257bbc9f..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);
}
@@ -340,7 +325,7 @@ const struct datatype pkttype_type = {
void devgroup_table_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.devgroup = rt_symbol_table_init("/etc/iproute2/group");
+ ctx->output.tbl.devgroup = rt_symbol_table_init("group");
}
void devgroup_table_exit(struct nft_ctx *ctx)
@@ -361,17 +346,23 @@ static struct error_record *devgroup_type_parse(struct parse_ctx *ctx,
return symbolic_constant_parse(ctx, sym, ctx->tbl->devgroup, res);
}
+static void devgroup_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, "group",
+ octx->tbl.devgroup, &devgroup_type);
+}
+
const struct datatype devgroup_type = {
.type = TYPE_DEVGROUP,
.name = "devgroup",
.desc = "devgroup name",
+ .describe = devgroup_type_describe,
.byteorder = BYTEORDER_HOST_ENDIAN,
.size = 4 * BITS_PER_BYTE,
.basetype = &integer_type,
.print = devgroup_type_print,
.json = devgroup_type_json,
.parse = devgroup_type_parse,
- .flags = DTYPE_F_PREFIX,
};
const struct datatype ifname_type = {
@@ -385,21 +376,22 @@ const struct datatype ifname_type = {
static void date_type_print(const struct expr *expr, struct output_ctx *octx)
{
- uint64_t tstamp = mpz_get_uint64(expr->value);
- struct tm *tm, *cur_tm;
+ uint64_t tstamp64 = mpz_get_uint64(expr->value);
char timestr[21];
+ time_t tstamp;
+ struct tm tm;
/* Convert from nanoseconds to seconds */
- tstamp /= 1000000000L;
+ tstamp64 /= 1000000000L;
/* Obtain current tm, to add tm_gmtoff to the timestamp */
- cur_tm = localtime((time_t *) &tstamp);
+ tstamp = tstamp64;
+ if (localtime_r(&tstamp, &tm))
+ tstamp64 += tm.tm_gmtoff;
- if (cur_tm)
- tstamp += cur_tm->tm_gmtoff;
-
- if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
- strftime(timestr, sizeof(timestr) - 1, "%Y-%m-%d %T", tm))
+ tstamp = tstamp64;
+ if (gmtime_r(&tstamp, &tm) &&
+ strftime(timestr, sizeof(timestr) - 1, "%Y-%m-%d %T", &tm))
nft_print(octx, "\"%s\"", timestr);
else
nft_print(octx, "Error converting timestamp to printed time");
@@ -407,7 +399,8 @@ static void date_type_print(const struct expr *expr, struct output_ctx *octx)
static bool parse_iso_date(uint64_t *tstamp, const char *sym)
{
- struct tm tm, *cur_tm;
+ struct tm cur_tm;
+ struct tm tm;
time_t ts;
memset(&tm, 0, sizeof(struct tm));
@@ -429,14 +422,15 @@ success:
*/
ts = timegm(&tm);
- /* Obtain current tm as well (at the specified time), so that we can substract tm_gmtoff */
- cur_tm = localtime(&ts);
+ if (ts == (time_t) -1)
+ return false;
- if (ts == (time_t) -1 || cur_tm == NULL)
- return ts;
+ /* Obtain current tm as well (at the specified time), so that we can substract tm_gmtoff */
+ if (!localtime_r(&ts, &cur_tm))
+ return false;
/* Substract tm_gmtoff to get the current time */
- *tstamp = ts - cur_tm->tm_gmtoff;
+ *tstamp = ts - cur_tm.tm_gmtoff;
return true;
}
@@ -491,16 +485,21 @@ static void day_type_print(const struct expr *expr, struct output_ctx *octx)
static void hour_type_print(const struct expr *expr, struct output_ctx *octx)
{
uint32_t seconds = mpz_get_uint32(expr->value), minutes, hours;
- struct tm *cur_tm;
+ struct tm cur_tm;
time_t ts;
/* Obtain current tm, so that we can add tm_gmtoff */
ts = time(NULL);
- cur_tm = localtime(&ts);
+ if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm)) {
+ int32_t adj = seconds + cur_tm.tm_gmtoff;
- if (cur_tm)
- seconds = (seconds + cur_tm->tm_gmtoff) % SECONDS_PER_DAY;
+ if (adj < 0)
+ adj += SECONDS_PER_DAY;
+ else if (adj >= SECONDS_PER_DAY)
+ adj -= SECONDS_PER_DAY;
+ seconds = adj;
+ }
minutes = seconds / 60;
seconds %= 60;
hours = minutes / 60;
@@ -517,10 +516,12 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct error_record *er;
- struct tm tm, *cur_tm;
+ struct tm cur_tm_data;
+ struct tm *cur_tm;
uint32_t result;
uint64_t tmp;
char *endptr;
+ struct tm tm;
time_t ts;
memset(&tm, 0, sizeof(struct tm));
@@ -534,7 +535,10 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
/* Obtain current tm, so that we can substract tm_gmtoff */
ts = time(NULL);
- cur_tm = localtime(&ts);
+ if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm_data))
+ cur_tm = &cur_tm_data;
+ else
+ cur_tm = NULL;
endptr = strptime(sym->identifier, "%T", &tm);
if (endptr && *endptr == '\0')
@@ -698,6 +702,8 @@ const struct meta_template meta_templates[] = {
[NFT_META_SDIFNAME] = META_TEMPLATE("sdifname", &ifname_type,
IFNAMSIZ * BITS_PER_BYTE,
BYTEORDER_HOST_ENDIAN),
+ [NFT_META_BRI_BROUTE] = META_TEMPLATE("broute", &integer_type,
+ 1 , BYTEORDER_HOST_ENDIAN),
};
static bool meta_key_is_unqualified(enum nft_meta_keys key)
@@ -717,12 +723,16 @@ static bool meta_key_is_unqualified(enum nft_meta_keys key)
static void meta_expr_print(const struct expr *expr, struct output_ctx *octx)
{
- if (meta_key_is_unqualified(expr->meta.key))
- nft_print(octx, "%s",
- meta_templates[expr->meta.key].token);
+ const char *token = "unknown";
+ uint32_t key = expr->meta.key;
+
+ if (key < array_size(meta_templates))
+ token = meta_templates[key].token;
+
+ if (meta_key_is_unqualified(key))
+ nft_print(octx, "%s", token);
else
- nft_print(octx, "meta %s",
- meta_templates[expr->meta.key].token);
+ nft_print(octx, "meta %s", token);
}
static bool meta_expr_cmp(const struct expr *e1, const struct expr *e2)
@@ -734,6 +744,7 @@ static void meta_expr_clone(struct expr *new, const struct expr *expr)
{
new->meta.key = expr->meta.key;
new->meta.base = expr->meta.base;
+ new->meta.inner_desc = expr->meta.inner_desc;
}
/**
@@ -768,6 +779,11 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
break;
case NFT_META_NFPROTO:
protonum = mpz_get_uint8(right->value);
+ if (protonum == NFPROTO_IPV4 && h->desc == &proto_ip)
+ break;
+ else if (protonum == NFPROTO_IPV6 && h->desc == &proto_ip6)
+ break;
+
desc = proto_find_upper(h->desc, protonum);
if (desc == NULL) {
desc = &proto_unknown;
@@ -807,13 +823,19 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
}
#define NFTNL_UDATA_META_KEY 0
-#define NFTNL_UDATA_META_MAX 1
+#define NFTNL_UDATA_META_INNER_DESC 1
+#define NFTNL_UDATA_META_MAX 2
static int meta_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct expr *expr)
{
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_KEY, expr->meta.key);
+ if (expr->meta.inner_desc) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_INNER_DESC,
+ expr->meta.inner_desc->id);
+ }
+
return 0;
}
@@ -825,6 +847,7 @@ static int meta_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_META_KEY:
+ case NFTNL_UDATA_META_INNER_DESC:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -839,6 +862,8 @@ static int meta_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *meta_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_META_MAX + 1] = {};
+ const struct proto_desc *desc;
+ struct expr *expr;
uint32_t key;
int err;
@@ -852,7 +877,14 @@ static struct expr *meta_expr_parse_udata(const struct nftnl_udata *attr)
key = nftnl_udata_get_u32(ud[NFTNL_UDATA_META_KEY]);
- return meta_expr_alloc(&internal_location, key);
+ expr = meta_expr_alloc(&internal_location, key);
+
+ if (ud[NFTNL_UDATA_META_INNER_DESC]) {
+ desc = find_proto_desc(ud[NFTNL_UDATA_META_INNER_DESC]);
+ expr->meta.inner_desc = desc;
+ }
+
+ return expr;
}
const struct expr_ops meta_expr_ops = {
@@ -901,12 +933,16 @@ struct expr *meta_expr_alloc(const struct location *loc, enum nft_meta_keys key)
static void meta_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
+ const char *token = "unknown";
+ uint32_t key = stmt->meta.key;
+
+ if (key < array_size(meta_templates))
+ token = meta_templates[key].token;
+
if (meta_key_is_unqualified(stmt->meta.key))
- nft_print(octx, "%s set ",
- meta_templates[stmt->meta.key].token);
+ nft_print(octx, "%s set ", token);
else
- nft_print(octx, "meta %s set ",
- meta_templates[stmt->meta.key].token);
+ nft_print(octx, "meta %s set ", token);
expr_print(stmt->meta.expr, octx);
}
@@ -931,8 +967,11 @@ struct stmt *meta_stmt_alloc(const struct location *loc, enum nft_meta_keys key,
stmt = stmt_alloc(loc, &meta_stmt_ops);
stmt->meta.key = key;
- stmt->meta.tmpl = &meta_templates[key];
stmt->meta.expr = expr;
+
+ if (key < array_size(meta_templates))
+ stmt->meta.tmpl = &meta_templates[key];
+
return stmt;
}
@@ -960,11 +999,11 @@ struct error_record *meta_key_parse(const struct location *loc,
const char *str,
unsigned int *value)
{
- int ret, len, offset = 0;
const char *sep = "";
+ size_t offset = 0;
unsigned int i;
char buf[1024];
- size_t size;
+ size_t len;
for (i = 0; i < array_size(meta_templates); i++) {
if (!meta_templates[i].token || strcmp(meta_templates[i].token, str))
@@ -987,9 +1026,10 @@ struct error_record *meta_key_parse(const struct location *loc,
}
len = (int)sizeof(buf);
- size = sizeof(buf);
for (i = 0; i < array_size(meta_templates); i++) {
+ int ret;
+
if (!meta_templates[i].token)
continue;
@@ -997,8 +1037,8 @@ struct error_record *meta_key_parse(const struct location *loc,
sep = ", ";
ret = snprintf(buf+offset, len, "%s%s", sep, meta_templates[i].token);
- SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- assert(offset < (int)sizeof(buf));
+ SNPRINTF_BUFFER_SIZE(ret, &len, &offset);
+ assert(len > 0);
}
return error(loc, "syntax error, unexpected %s, known keys are %s", str, buf);
diff --git a/src/mini-gmp.c b/src/mini-gmp.c
index 04bed3f5..186dc3a4 100644
--- a/src/mini-gmp.c
+++ b/src/mini-gmp.c
@@ -41,12 +41,12 @@ see https://www.gnu.org/licenses/. */
mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c,
mpn/generic/submul_1.c. */
+#include <nft.h>
+
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include "mini-gmp.h"
diff --git a/src/misspell.c b/src/misspell.c
index 6536d755..f5354fa8 100644
--- a/src/misspell.c
+++ b/src/misspell.c
@@ -1,5 +1,13 @@
-#include <stdlib.h>
-#include <string.h>
+/*
+ * Copyright (c) 2018 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <limits.h>
#include <utils.h>
#include <misspell.h>
@@ -64,7 +72,7 @@ static unsigned int string_distance(const char *a, const char *b)
ret = DISTANCE(len_a, len_b);
- xfree(distance);
+ free(distance);
return ret;
}
diff --git a/src/mnl.c b/src/mnl.c
index e87b0338..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>
@@ -26,13 +28,12 @@
#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>
@@ -241,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;
@@ -259,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;
@@ -408,7 +416,7 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
.nl_ctx = ctx,
};
- mnl_set_sndbuffer(ctx->nft->nf_sock, ctx->batch);
+ mnl_set_sndbuffer(ctx);
mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
@@ -592,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;
@@ -602,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);
@@ -654,13 +666,21 @@ err_free:
}
struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
- const char *table, const char *chain)
+ const char *table, const char *chain,
+ uint64_t rule_handle,
+ bool dump, bool reset)
{
+ uint16_t nl_flags = dump ? NLM_F_DUMP : NLM_F_ACK;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_rule_list *nlr_list;
struct nftnl_rule *nlr = NULL;
struct nlmsghdr *nlh;
- int ret;
+ int msg_type, ret;
+
+ if (reset)
+ msg_type = NFT_MSG_GETRULE_RESET;
+ else
+ msg_type = NFT_MSG_GETRULE;
if (table) {
nlr = nftnl_rule_alloc();
@@ -670,14 +690,16 @@ struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
nftnl_rule_set_str(nlr, NFTNL_RULE_TABLE, table);
if (chain)
nftnl_rule_set_str(nlr, NFTNL_RULE_CHAIN, chain);
+ if (rule_handle)
+ nftnl_rule_set_u64(nlr, NFTNL_RULE_HANDLE, rule_handle);
}
nlr_list = nftnl_rule_list_alloc();
if (nlr_list == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family,
- NLM_F_DUMP, ctx->seqnum);
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type, family,
+ nl_flags, ctx->seqnum);
if (nlr) {
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
@@ -696,18 +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)
{
struct nftnl_udata_buf *udbuf;
- int priority, policy, i = 0;
struct nftnl_chain *nlc;
- unsigned int ifname_len;
- const char **dev_array;
- char ifname[IFNAMSIZ];
struct nlmsghdr *nlh;
- struct expr *expr;
- int dev_array_len;
+ int priority, policy;
nlc = nftnl_chain_alloc();
if (nlc == NULL)
@@ -720,46 +822,6 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS,
CHAIN_F_HW_OFFLOAD);
}
- if (cmd->chain->flags & CHAIN_F_BASECHAIN) {
- nftnl_chain_set_u32(nlc, NFTNL_CHAIN_HOOKNUM,
- cmd->chain->hook.num);
- mpz_export_data(&priority,
- cmd->chain->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- nftnl_chain_set_s32(nlc, NFTNL_CHAIN_PRIO, priority);
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_TYPE,
- cmd->chain->type.str);
- }
- if (cmd->chain->dev_expr) {
- dev_array = xmalloc(sizeof(char *) * 8);
- dev_array_len = 8;
- list_for_each_entry(expr, &cmd->chain->dev_expr->expressions, list) {
- ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
- memset(ifname, 0, sizeof(ifname));
- mpz_export_data(ifname, expr->value,
- BYTEORDER_HOST_ENDIAN,
- ifname_len);
- dev_array[i++] = xstrdup(ifname);
- if (i == dev_array_len) {
- dev_array_len *= 2;
- dev_array = xrealloc(dev_array,
- dev_array_len * sizeof(char *));
- }
- }
-
- dev_array[i] = NULL;
- if (i == 1)
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_DEV, dev_array[0]);
- else if (i > 1)
- nftnl_chain_set_data(nlc, NFTNL_CHAIN_DEVICES, dev_array,
- sizeof(char *) * dev_array_len);
-
- i = 0;
- while (dev_array[i] != NULL)
- xfree(dev_array[i++]);
-
- xfree(dev_array);
- }
if (cmd->chain->comment) {
udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udbuf)
@@ -794,12 +856,6 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS, cmd->chain->flags);
}
- if (cmd->chain && cmd->chain->flags & CHAIN_F_BASECHAIN) {
- nftnl_chain_unset(nlc, NFTNL_CHAIN_TYPE);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->type.loc);
- mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str);
- }
-
if (cmd->chain && cmd->chain->policy) {
mpz_export_data(&policy, cmd->chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
@@ -807,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);
@@ -847,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;
@@ -856,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);
@@ -872,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);
@@ -992,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;
@@ -1001,10 +1097,11 @@ 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);
@@ -1238,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;
@@ -1248,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);
@@ -1453,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;
@@ -1463,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);
@@ -1737,6 +1842,7 @@ int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd)
int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
const struct handle *h, const struct expr *init)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSETELEM;
struct nftnl_set *nls;
int err;
@@ -1753,7 +1859,10 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_DELSETELEM, 0,
+ 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);
@@ -1761,14 +1870,21 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
}
struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx,
- struct nftnl_set *nls_in)
+ struct nftnl_set *nls_in,
+ bool reset)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_set *nls_out;
struct nlmsghdr *nlh;
+ int msg_type;
int err;
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM,
+ if (reset)
+ msg_type = NFT_MSG_GETSETELEM_RESET;
+ else
+ msg_type = NFT_MSG_GETSETELEM;
+
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type,
nftnl_set_get_u32(nls_in, NFTNL_SET_FAMILY),
NLM_F_ACK, ctx->seqnum);
nftnl_set_elems_nlmsg_build_payload(nlh, nls_in);
@@ -1791,12 +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);
@@ -1867,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;
+ const struct expr *dev_expr = cmd->flowtable->dev_expr;
+ const struct nft_dev *dev_array;
+ struct nlattr *nest_dev;
+ int i, num_devs= 0;
- list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list)
- 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);
+ 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();
@@ -1918,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);
@@ -1951,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);
@@ -1960,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)
@@ -1971,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);
@@ -2000,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);
@@ -2071,11 +2175,11 @@ static struct basehook *basehook_alloc(void)
static void basehook_free(struct basehook *b)
{
list_del(&b->list);
- xfree(b->module_name);
- xfree(b->hookfn);
- xfree(b->chain);
- xfree(b->table);
- xfree(b);
+ free_const(b->module_name);
+ free_const(b->hookfn);
+ free_const(b->chain);
+ free_const(b->table);
+ free(b);
}
static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
@@ -2183,6 +2287,27 @@ static int dump_nf_attr_chain_cb(const struct nlattr *attr, void *data)
return MNL_CB_OK;
}
+static int dump_nf_attr_bpf_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFNLA_HOOK_BPF_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFNLA_HOOK_BPF_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ default:
+ return MNL_CB_OK;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
struct dump_nf_hook_data {
struct list_head *hook_list;
int family;
@@ -2217,16 +2342,23 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
struct nlattr *nested[NFNLA_HOOK_INFO_MAX + 1] = {};
uint32_t type;
- if (mnl_attr_parse_nested(tb[NFNLA_HOOK_CHAIN_INFO], dump_nf_chain_info_cb, nested) < 0)
+ if (mnl_attr_parse_nested(tb[NFNLA_HOOK_CHAIN_INFO],
+ dump_nf_chain_info_cb, nested) < 0) {
+ basehook_free(hook);
return -1;
+ }
type = ntohl(mnl_attr_get_u32(nested[NFNLA_HOOK_INFO_TYPE]));
if (type == NFNL_HOOK_TYPE_NFTABLES) {
struct nlattr *info[NFNLA_CHAIN_MAX + 1] = {};
const char *tablename, *chainname;
- if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC], dump_nf_attr_chain_cb, info) < 0)
+ if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC],
+ dump_nf_attr_chain_cb,
+ info) < 0) {
+ basehook_free(hook);
return -1;
+ }
tablename = mnl_attr_get_str(info[NFNLA_CHAIN_TABLE]);
chainname = mnl_attr_get_str(info[NFNLA_CHAIN_NAME]);
@@ -2235,6 +2367,23 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
hook->chain = xstrdup(chainname);
}
hook->chain_family = mnl_attr_get_u8(info[NFNLA_CHAIN_FAMILY]);
+ } else if (type == NFNL_HOOK_TYPE_BPF) {
+ struct nlattr *info[NFNLA_HOOK_BPF_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC],
+ dump_nf_attr_bpf_cb, info) < 0) {
+ basehook_free(hook);
+ return -1;
+ }
+
+ if (info[NFNLA_HOOK_BPF_ID]) {
+ char tmpbuf[16];
+
+ snprintf(tmpbuf, sizeof(tmpbuf), "id %u",
+ ntohl(mnl_attr_get_u32(info[NFNLA_HOOK_BPF_ID])));
+
+ hook->chain = xstrdup(tmpbuf);
+ }
}
}
if (tb[NFNLA_HOOK_HOOKNUM])
@@ -2356,6 +2505,8 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
if (hook->table && hook->chain)
fprintf(fp, " chain %s %s %s", family2str(hook->chain_family), hook->table, hook->chain);
+ else if (hook->hookfn && hook->chain)
+ fprintf(fp, " %s %s", hook->hookfn, hook->chain);
else if (hook->hookfn) {
fprintf(fp, " %s", hook->hookfn);
}
diff --git a/src/monitor.c b/src/monitor.c
index 4b55872b..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>
@@ -272,10 +272,13 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type,
chain_print_plain(c, &monh->ctx->nft->output);
break;
case NFT_MSG_DELCHAIN:
- nft_mon_print(monh, "chain %s %s %s",
- family2str(c->handle.family),
- c->handle.table.name,
- c->handle.chain.name);
+ if (c->dev_array_len > 0)
+ chain_print_plain(c, &monh->ctx->nft->output);
+ else
+ nft_mon_print(monh, "chain %s %s %s",
+ family2str(c->handle.family),
+ c->handle.table.name,
+ c->handle.chain.name);
break;
}
nft_mon_print(monh, "\n");
@@ -387,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;
}
@@ -434,6 +443,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
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)
diff --git a/src/netlink.c b/src/netlink.c
index 799cf9b8..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>
@@ -96,9 +96,10 @@ struct nftnl_expr *alloc_nft_expr(const char *name)
return nle;
}
-
-void __netlink_gen_data(const struct expr *expr,
- struct nft_data_linearize *data, bool expand);
+static void netlink_gen_key(const struct expr *expr,
+ struct nft_data_linearize *data);
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand);
struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
const struct expr *expr)
@@ -133,16 +134,23 @@ struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
case EXPR_SET_ELEM_CATCHALL:
break;
default:
- __netlink_gen_data(key, &nld, false);
- nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
if (set->set_flags & NFT_SET_INTERVAL &&
key->etype == EXPR_CONCAT && key->field_count > 1) {
+ key->flags |= EXPR_F_INTERVAL;
+ netlink_gen_key(key, &nld);
+ key->flags &= ~EXPR_F_INTERVAL;
+
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
+
key->flags |= EXPR_F_INTERVAL_END;
- __netlink_gen_data(key, &nld, false);
+ netlink_gen_key(key, &nld);
key->flags &= ~EXPR_F_INTERVAL_END;
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY_END,
&nld.value, nld.len);
+ } else {
+ netlink_gen_key(key, &nld);
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
}
break;
}
@@ -246,22 +254,38 @@ 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:
- i = end ? i->right : i->left;
+ 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 (end) {
+ if (flags & EXPR_F_INTERVAL_END) {
int count;
mpz_t v;
mpz_init_bitmask(v, i->len - i->prefix_len);
if (i->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(v, i->len / BITS_PER_BYTE);
+ byteorder_switch_expr_value(v, i);
mpz_add(v, i->prefix->value, v);
count = netlink_export_pad(data, v, i);
@@ -270,6 +294,16 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i,
}
return netlink_export_pad(data, i->prefix->value, i);
case EXPR_VALUE:
+ /* 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);
@@ -278,21 +312,57 @@ 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(const struct expr *expr,
- struct nft_data_linearize *nld)
+static void nft_data_memcpy(struct nft_data_linearize *nld,
+ const void *src, unsigned int len)
+{
+ 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;
- 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_key(expr->flags, i, data + offset);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static int __netlink_gen_concat_data(int end, const struct expr *i,
+ unsigned char *data)
+{
+ switch (i->etype) {
+ case EXPR_RANGE:
+ i = end ? i->right : i->left;
+ break;
+ case EXPR_PREFIX:
+ if (end) {
+ int count;
+ mpz_t v;
+
+ mpz_init_bitmask(v, i->len - i->prefix_len);
+ 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:
+ break;
+ default:
+ BUG("invalid expression type '%s' in set", expr_ops(i)->name);
+ }
+
+ return netlink_export_pad(data, i->value, i);
}
static void __netlink_gen_concat_expand(const struct expr *expr,
@@ -305,18 +375,31 @@ static void __netlink_gen_concat_expand(const struct expr *expr,
memset(data, 0, len);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(false, i, data + offset);
+ offset += __netlink_gen_concat_data(false, i, data + offset);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(true, i, data + offset);
+ offset += __netlink_gen_concat_data(true, i, data + offset);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static void __netlink_gen_concat(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = 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_data(expr->flags, i, data + offset);
+
+ nft_data_memcpy(nld, data, len);
}
static void netlink_gen_concat_data(const struct expr *expr,
- struct nft_data_linearize *nld,
- bool expand)
+ struct nft_data_linearize *nld, bool expand)
{
if (expand)
__netlink_gen_concat_expand(expr, nld);
@@ -381,30 +464,48 @@ 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, bool expand)
+static void netlink_gen_key(const struct expr *expr,
+ struct nft_data_linearize *data)
+{
+ switch (expr->etype) {
+ case EXPR_VALUE:
+ return netlink_gen_constant_data(expr, data);
+ case EXPR_CONCAT:
+ return netlink_gen_concat_key(expr, data);
+ case EXPR_RANGE:
+ return netlink_gen_range(expr, data);
+ case EXPR_PREFIX:
+ return netlink_gen_prefix(expr, data);
+ default:
+ BUG("invalid data expression type %s\n", expr_name(expr));
+ }
+}
+
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand)
{
switch (expr->etype) {
case EXPR_VALUE:
@@ -529,18 +630,20 @@ static int qsort_device_cmp(const void *a, const void *b)
struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
const struct nftnl_chain *nlc)
{
- const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {};
+ const struct nftnl_udata *ud[NFTNL_UDATA_CHAIN_MAX + 1] = {};
int priority, policy, len = 0, i;
const char * const *dev_array;
struct chain *chain;
const char *udata;
uint32_t ulen;
- chain = chain_alloc(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME));
+ chain = chain_alloc();
chain->handle.family =
nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
chain->handle.table.name =
xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE));
+ chain->handle.chain.name =
+ xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME));
chain->handle.handle.id =
nftnl_chain_get_u64(nlc, NFTNL_CHAIN_HANDLE);
if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_FLAGS))
@@ -707,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);
}
}
@@ -783,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)
@@ -800,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)
@@ -842,21 +951,21 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
const struct nftnl_udata *ud[NFTNL_UDATA_SET_MAX + 1] = {};
enum byteorder keybyteorder = BYTEORDER_INVALID;
enum byteorder databyteorder = BYTEORDER_INVALID;
- const struct datatype *keytype, *datatype = NULL;
- struct expr *typeof_expr_key, *typeof_expr_data;
struct setelem_parse_ctx set_parse_ctx;
+ const struct datatype *datatype = NULL;
+ const struct datatype *keytype = NULL;
+ const struct datatype *dtype2 = NULL;
+ const struct datatype *dtype = NULL;
+ struct expr *typeof_expr_data = NULL;
+ struct expr *typeof_expr_key = NULL;
const char *udata, *comment = NULL;
uint32_t flags, key, objtype = 0;
- const struct datatype *dtype;
uint32_t data_interval = 0;
bool automerge = false;
struct set *set;
uint32_t ulen;
uint32_t klen;
- typeof_expr_key = NULL;
- typeof_expr_data = NULL;
-
if (nftnl_set_is_set(nls, NFTNL_SET_USERDATA)) {
udata = nftnl_set_get_data(nls, NFTNL_SET_USERDATA, &ulen);
if (nftnl_udata_parse(udata, ulen, set_parse_udata_cb, ud) < 0) {
@@ -899,8 +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;
}
}
@@ -935,17 +1044,28 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
}
list_splice_tail(&set_parse_ctx.stmt_list, &set->stmt_list);
+ set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);
+
if (datatype) {
- dtype = set_datatype_alloc(datatype, databyteorder);
+ uint32_t dlen;
+
+ dtype2 = set_datatype_alloc(datatype, databyteorder);
klen = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE;
- if (set_udata_key_valid(typeof_expr_data, klen)) {
- datatype_free(datatype_get(dtype));
+ dlen = data_interval ? klen / 2 : klen;
+
+ if (set_udata_key_valid(typeof_expr_data, dlen)) {
+ typeof_expr_data->len = klen;
set->data = typeof_expr_data;
+ typeof_expr_data = NULL;
+ } else if (set->flags & NFT_SET_OBJECT) {
+ set->data = constant_expr_alloc(&netlink_location,
+ dtype2,
+ databyteorder, klen,
+ NULL);
} else {
- expr_free(typeof_expr_data);
set->data = constant_expr_alloc(&netlink_location,
- dtype,
+ dtype2,
databyteorder, klen,
NULL);
@@ -956,29 +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, klen)) {
- datatype_free(datatype_get(dtype));
set->key = typeof_expr_key;
+ typeof_expr_key = NULL;
set->key_typeof_valid = true;
} else {
- expr_free(typeof_expr_key);
set->key = constant_expr_alloc(&netlink_location, dtype,
keybyteorder, klen,
NULL);
}
- if (dtype != keytype)
- datatype_free(keytype);
-
- set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);
set->handle.handle.id = nftnl_set_get_u64(nls, NFTNL_SET_HANDLE);
set->objtype = objtype;
@@ -1005,6 +1117,13 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
}
}
+out:
+ expr_free(typeof_expr_data);
+ expr_free(typeof_expr_key);
+ datatype_free(datatype);
+ datatype_free(keytype);
+ datatype_free(dtype2);
+ datatype_free(dtype);
return set;
}
@@ -1109,7 +1228,7 @@ static struct expr *netlink_parse_interval_elem(const struct set *set,
return range_expr_to_prefix(range);
}
-static struct expr *concat_elem_expr(struct expr *key,
+static struct expr *concat_elem_expr(const struct set *set, struct expr *key,
const struct datatype *dtype,
struct expr *data, int *off)
{
@@ -1133,7 +1252,9 @@ static struct expr *concat_elem_expr(struct expr *key,
expr->byteorder = subtype->byteorder;
}
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ if (expr_basetype(expr)->type == TYPE_STRING ||
+ (!(set->flags & NFT_SET_INTERVAL) &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN))
mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
if (expr->dtype->basetype != NULL &&
@@ -1157,7 +1278,7 @@ static struct expr *netlink_parse_concat_elem_key(const struct set *set,
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- expr = concat_elem_expr(n, dtype, data, &off);
+ expr = concat_elem_expr(set, n, dtype, data, &off);
compound_expr_add(concat, expr);
if (set->key->etype == EXPR_CONCAT)
n = list_next_entry(n, list);
@@ -1180,7 +1301,7 @@ static struct expr *netlink_parse_concat_elem(const struct set *set,
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- expr = concat_elem_expr(NULL, dtype, data, &off);
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
list_add_tail(&expr->list, &expressions);
}
@@ -1192,7 +1313,7 @@ static struct expr *netlink_parse_concat_elem(const struct set *set,
while (off > 0) {
left = list_first_entry(&expressions, struct expr, list);
- expr = concat_elem_expr(NULL, dtype, data, &off);
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
list_del(&left->list);
range = range_expr_alloc(&data->location, left, expr);
@@ -1420,7 +1541,7 @@ static int list_setelements(struct nftnl_set *s, struct netlink_ctx *ctx)
}
int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
- struct set *set)
+ struct set *set, bool reset)
{
struct nftnl_set *nls;
int err;
@@ -1435,7 +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)
@@ -1463,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;
@@ -1482,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;
@@ -1677,6 +1798,55 @@ int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd,
return err;
}
+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)
@@ -1700,7 +1870,8 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
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]);
@@ -1995,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)) {
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 0b6cf107..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>
@@ -30,6 +29,16 @@
#include <cache.h>
#include <xt.h>
+struct dl_proto_ctx *dl_proto_ctx(struct rule_pp_ctx *ctx)
+{
+ return ctx->dl;
+}
+
+static struct dl_proto_ctx *dl_proto_ctx_outer(struct rule_pp_ctx *ctx)
+{
+ return &ctx->_dl[0];
+}
+
static int netlink_parse_expr(const struct nftnl_expr *nle,
struct netlink_parse_ctx *ctx);
@@ -72,8 +81,7 @@ static void netlink_set_register(struct netlink_parse_ctx *ctx,
return;
}
- if (ctx->registers[reg] != NULL)
- expr_free(ctx->registers[reg]);
+ expr_free(ctx->registers[reg]);
ctx->registers[reg] = expr;
}
@@ -479,7 +487,7 @@ static struct expr *netlink_parse_bitwise_bool(struct netlink_parse_ctx *ctx,
mpz_ior(m, m, o);
}
- if (left->len > 0 && mpz_scan0(m, 0) == left->len) {
+ if (left->len > 0 && mpz_scan0(m, 0) >= left->len) {
/* mask encompasses the entire value */
expr_free(mask);
} else {
@@ -527,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;
}
@@ -613,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;
@@ -620,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)
@@ -779,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);
}
@@ -832,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;
}
@@ -979,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)
@@ -1631,6 +1732,8 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
expr = netlink_parse_concat_key(ctx, loc, sreg, set->key);
if (expr == NULL)
return;
+ } else if (expr->dtype == &invalid_type) {
+ expr_set_type(expr, datatype_get(set->key->dtype), set->key->byteorder);
}
expr = set_elem_expr_alloc(&expr->location, expr);
@@ -1660,6 +1763,14 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_SREG_DATA)) {
sreg_data = netlink_parse_register(nle, NFTNL_EXPR_DYNSET_SREG_DATA);
expr_data = netlink_get_register(ctx, loc, sreg_data);
+
+ if (expr_data && expr_data->len < set->data->len) {
+ expr_free(expr_data);
+ expr_data = netlink_parse_concat_expr(ctx, loc, sreg_data, set->data->len);
+ if (expr_data == NULL)
+ netlink_error(ctx, loc,
+ "Could not parse dynset map data expressions");
+ }
}
if (expr_data != NULL) {
@@ -1772,6 +1883,7 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "bitwise", .parse = netlink_parse_bitwise },
{ .name = "byteorder", .parse = netlink_parse_byteorder },
{ .name = "payload", .parse = netlink_parse_payload },
+ { .name = "inner", .parse = netlink_parse_inner },
{ .name = "exthdr", .parse = netlink_parse_exthdr },
{ .name = "meta", .parse = netlink_parse_meta },
{ .name = "socket", .parse = netlink_parse_socket },
@@ -1780,6 +1892,7 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "ct", .parse = netlink_parse_ct },
{ .name = "connlimit", .parse = netlink_parse_connlimit },
{ .name = "counter", .parse = netlink_parse_counter },
+ { .name = "last", .parse = netlink_parse_last },
{ .name = "log", .parse = netlink_parse_log },
{ .name = "limit", .parse = netlink_parse_limit },
{ .name = "range", .parse = netlink_parse_range },
@@ -1868,6 +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,
@@ -1876,11 +2020,12 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
{
struct expr *left = payload, *right = expr->right, *tmp;
struct list_head list = LIST_HEAD_INIT(list);
- struct stmt *nstmt;
- struct expr *nexpr = NULL;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
enum proto_bases base = left->payload.base;
+ struct expr *nexpr = NULL;
+ struct stmt *nstmt;
- payload_expr_expand(&list, left, &ctx->pctx);
+ payload_expr_expand(&list, left, &dl->pctx);
list_for_each_entry(left, &list, list) {
tmp = constant_expr_splice(right, left->len);
@@ -1895,7 +2040,7 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
nexpr = relational_expr_alloc(&expr->location, expr->op,
left, tmp);
if (expr->op == OP_EQ)
- relational_expr_pctx_update(&ctx->pctx, nexpr);
+ relational_expr_pctx_update(&dl->pctx, nexpr);
nstmt = expr_stmt_alloc(&ctx->stmt->location, nexpr);
list_add_tail(&nstmt->list, &ctx->stmt->list);
@@ -1904,17 +2049,31 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
assert(left->payload.base);
assert(base == left->payload.base);
- if (payload_is_stacked(ctx->pctx.protocol[base].desc, nexpr))
+ if (expr->left->payload.inner_desc) {
+ if (expr->left->payload.inner_desc == expr->left->payload.desc) {
+ nexpr->left->payload.desc = expr->left->payload.desc;
+ nexpr->left->payload.tmpl = expr->left->payload.tmpl;
+ }
+ nexpr->left->payload.inner_desc = expr->left->payload.inner_desc;
+
+ if (meta_outer_may_dependency_kill(ctx, expr->left)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->left->payload.inner_desc->base);
+ }
+ }
+
+ if (payload_is_stacked(dl->pctx.protocol[base].desc, nexpr))
base--;
/* Remember the first payload protocol expression to
* kill it later on if made redundant by a higher layer
* payload expression.
*/
- payload_dependency_kill(&ctx->pdctx, nexpr->left,
- ctx->pctx.family);
+ payload_dependency_kill(&dl->pdctx, nexpr->left,
+ dl->pctx.family);
if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, nstmt, base);
+ payload_dependency_store(&dl->pdctx, nstmt, base);
}
list_del(&ctx->stmt->list);
stmt_free(ctx->stmt);
@@ -1923,6 +2082,7 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, const struct expr *value)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
uint8_t icmp_type;
@@ -1937,10 +2097,10 @@ static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, cons
/* icmp(v6) type is 8 bit, if value is smaller or larger, this is not
* a protocol dependency.
*/
- if (expr->len != 8 || value->len != 8 || rctx->pctx.th_dep.icmp.type)
+ if (expr->len != 8 || value->len != 8 || dl->pctx.th_dep.icmp.type)
return;
- desc = rctx->pctx.protocol[expr->payload.base].desc;
+ desc = dl->pctx.protocol[expr->payload.base].desc;
if (desc == NULL)
return;
@@ -1968,7 +2128,7 @@ static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, cons
expr->payload.desc = desc;
expr->payload.tmpl = tmpl;
- rctx->pctx.th_dep.icmp.type = icmp_type;
+ dl->pctx.th_dep.icmp.type = icmp_type;
return;
}
}
@@ -1977,6 +2137,8 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
struct expr *expr,
struct expr *payload)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
+
switch (expr->op) {
case OP_EQ:
case OP_NEQ:
@@ -2000,10 +2162,10 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
}
/* Fall through */
default:
- payload_expr_complete(payload, &ctx->pctx);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(expr->right, payload->dtype,
payload->byteorder);
- payload_dependency_kill(&ctx->pdctx, payload, ctx->pctx.family);
+ payload_dependency_kill(&dl->pdctx, payload, dl->pctx.family);
break;
}
}
@@ -2111,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;
@@ -2124,16 +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(&ctx->pdctx, base) &&
- meta_may_dependency_kill(&ctx->pdctx,
- ctx->pctx.family, expr))
- payload_dependency_release(&ctx->pdctx, base);
+ if (payload_dependency_exists(&dl->pdctx, base) &&
+ meta_may_dependency_kill(&dl->pdctx,
+ dl->pctx.family, expr))
+ payload_dependency_release(&dl->pdctx, base);
if (left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
+ payload_dependency_store(&dl->pdctx, ctx->stmt, base);
}
break;
default:
@@ -2264,13 +2427,14 @@ static void __binop_postprocess(struct rule_pp_ctx *ctx,
struct expr *mask,
struct expr **expr_binop)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct expr *binop = *expr_binop;
unsigned int shift;
assert(binop->etype == EXPR_BINOP);
if ((left->etype == EXPR_PAYLOAD &&
- payload_expr_trim(left, mask, &ctx->pctx, &shift)) ||
+ payload_expr_trim(left, mask, &dl->pctx, &shift)) ||
(left->etype == EXPR_EXTHDR &&
exthdr_find_template(left, mask, &shift))) {
struct expr *right = NULL;
@@ -2353,56 +2517,29 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) &&
right->dtype->basetype &&
- right->dtype->basetype->type == TYPE_BITMASK) {
- switch (right->etype) {
- case EXPR_VALUE:
- if (!mpz_cmp_ui(right->value, 0)) {
- /* Flag comparison: data & flags != 0
- *
- * Split the flags into a list of flag values and convert the
- * op to OP_EQ.
- */
- expr_free(right);
+ right->dtype->basetype->type == TYPE_BITMASK &&
+ right->etype == EXPR_VALUE &&
+ !mpz_cmp_ui(right->value, 0)) {
+ /* Flag comparison: data & flags != 0
+ *
+ * Split the flags into a list of flag values and convert the
+ * op to OP_EQ.
+ */
+ expr_free(right);
- expr->left = expr_get(binop->left);
- expr->right = binop_tree_to_list(NULL, binop->right);
- switch (expr->op) {
- case OP_NEQ:
- expr->op = OP_IMPLICIT;
- break;
- case OP_EQ:
- expr->op = OP_NEG;
- break;
- default:
- BUG("unknown operation type %d\n", expr->op);
- }
- expr_free(binop);
- } else if (binop->right->etype == EXPR_VALUE &&
- right->etype == EXPR_VALUE &&
- !mpz_cmp(right->value, binop->right->value)) {
- /* Skip flag / flag representation for:
- * data & flag == flag
- * data & flag != flag
- */
- ;
- } else {
- *exprp = flagcmp_expr_alloc(&expr->location, expr->op,
- expr_get(binop->left),
- binop_tree_to_list(NULL, binop->right),
- expr_get(right));
- expr_free(expr);
- }
+ expr->left = expr_get(binop->left);
+ expr->right = binop_tree_to_list(NULL, binop->right);
+ switch (expr->op) {
+ case OP_NEQ:
+ expr->op = OP_IMPLICIT;
break;
- case EXPR_BINOP:
- *exprp = flagcmp_expr_alloc(&expr->location, expr->op,
- expr_get(binop->left),
- binop_tree_to_list(NULL, binop->right),
- binop_tree_to_list(NULL, right));
- expr_free(expr);
+ case OP_EQ:
+ expr->op = OP_NEG;
break;
default:
- break;
+ BUG("unknown operation type %d\n", expr->op);
}
+ 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)) {
@@ -2455,6 +2592,42 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
}
}
+static bool payload_binop_postprocess(struct rule_pp_ctx *ctx,
+ struct expr **exprp)
+{
+ struct expr *expr = *exprp;
+
+ if (expr->op != OP_RSHIFT)
+ return false;
+
+ if (expr->left->etype == EXPR_UNARY) {
+ /*
+ * If the payload value was originally in a different byte-order
+ * from the payload expression, there will be a byte-order
+ * conversion to remove.
+ */
+ struct expr *left = expr_get(expr->left->arg);
+ expr_free(expr->left);
+ expr->left = left;
+ }
+
+ if (expr->left->etype != EXPR_BINOP || expr->left->op != OP_AND)
+ return false;
+
+ 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,
const struct expr *mask,
const struct expr *expr)
@@ -2520,8 +2693,54 @@ static struct expr *expr_postprocess_string(struct expr *expr)
return out;
}
+static void expr_postprocess_value(struct rule_pp_ctx *ctx, struct expr **exprp)
+{
+ bool interval = (ctx->set && ctx->set->flags & NFT_SET_INTERVAL);
+ struct expr *expr = *exprp;
+
+ // FIXME
+ if (expr->byteorder == BYTEORDER_HOST_ENDIAN && !interval)
+ mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+
+ if (expr_basetype(expr)->type == TYPE_STRING)
+ *exprp = expr_postprocess_string(expr);
+
+ expr = *exprp;
+ if (expr->dtype->basetype != NULL &&
+ expr->dtype->basetype->type == TYPE_BITMASK)
+ *exprp = bitmask_expr_to_binops(expr);
+}
+
+static void expr_postprocess_concat(struct rule_pp_ctx *ctx, struct expr **exprp)
+{
+ struct expr *i, *n, *expr = *exprp;
+ unsigned int type = expr->dtype->type, ntype = 0;
+ int off = expr->dtype->subtypes;
+ const struct datatype *dtype;
+ LIST_HEAD(tmp);
+
+ assert(expr->etype == EXPR_CONCAT);
+
+ ctx->flags |= RULE_PP_IN_CONCATENATION;
+ list_for_each_entry_safe(i, n, &expr->expressions, list) {
+ if (type) {
+ dtype = concat_subtype_lookup(type, --off);
+ expr_set_type(i, dtype, dtype->byteorder);
+ }
+ list_del(&i->list);
+ expr_postprocess(ctx, &i);
+ list_add_tail(&i->list, &tmp);
+
+ ntype = concat_subtype_add(ntype, i->dtype->type);
+ }
+ ctx->flags &= ~RULE_PP_IN_CONCATENATION;
+ list_splice(&tmp, &expr->expressions);
+ __datatype_set(expr, concat_type_alloc(ntype));
+}
+
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct expr *expr = *exprp, *i;
switch (expr->etype) {
@@ -2545,35 +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_HEAD(tmp);
- struct expr *n;
-
- ctx->flags |= RULE_PP_IN_CONCATENATION;
- list_for_each_entry_safe(i, n, &expr->expressions, list) {
- if (type) {
- dtype = concat_subtype_lookup(type, --off);
- expr_set_type(i, dtype, dtype->byteorder);
- }
- list_del(&i->list);
- expr_postprocess(ctx, &i);
- list_add_tail(&i->list, &tmp);
-
- ntype = concat_subtype_add(ntype, i->dtype->type);
- }
- ctx->flags &= ~RULE_PP_IN_CONCATENATION;
- list_splice(&tmp, &expr->expressions);
- datatype_set(expr, concat_type_alloc(ntype));
+ case EXPR_CONCAT:
+ expr_postprocess_concat(ctx, exprp);
break;
- }
case EXPR_UNARY:
expr_postprocess(ctx, &expr->arg);
expr_set_type(expr, expr->arg->dtype, !expr->arg->byteorder);
break;
case EXPR_BINOP:
+ if (payload_binop_postprocess(ctx, exprp))
+ break;
+
expr_postprocess(ctx, &expr->left);
switch (expr->op) {
case OP_LSHIFT:
@@ -2582,8 +2783,13 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
BYTEORDER_HOST_ENDIAN);
break;
case OP_AND:
- expr_set_type(expr->right, expr->left->dtype,
- expr->left->byteorder);
+ if (expr->right->len > expr->left->len) {
+ expr_set_type(expr->right, expr->left->dtype,
+ BYTEORDER_HOST_ENDIAN);
+ } else {
+ expr_set_type(expr->right, expr->left->dtype,
+ expr->left->byteorder);
+ }
/* Do not process OP_AND in ordinary rule context.
*
@@ -2603,19 +2809,62 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
}
break;
default:
- expr_set_type(expr->right, expr->left->dtype,
- expr->left->byteorder);
+ if (expr->right->len > expr->left->len) {
+ expr_set_type(expr->right, expr->left->dtype,
+ BYTEORDER_HOST_ENDIAN);
+ } else {
+ expr_set_type(expr->right, expr->left->dtype,
+ expr->left->byteorder);
+ }
}
expr_postprocess(ctx, &expr->right);
- expr_set_type(expr, expr->left->dtype,
- expr->left->byteorder);
+ switch (expr->op) {
+ case OP_LSHIFT:
+ case OP_RSHIFT:
+ expr_set_type(expr, &xinteger_type,
+ BYTEORDER_HOST_ENDIAN);
+ break;
+ default:
+ expr_set_type(expr, expr->left->dtype,
+ expr->left->byteorder);
+ }
+
break;
case EXPR_RELATIONAL:
switch (expr->left->etype) {
case EXPR_PAYLOAD:
payload_match_postprocess(ctx, expr, expr->left);
return;
+ case EXPR_CONCAT:
+ if (expr->right->etype == EXPR_SET_REF) {
+ assert(expr->left->dtype == &invalid_type);
+ assert(expr->right->dtype != &invalid_type);
+
+ datatype_set(expr->left, expr->right->dtype);
+ }
+ ctx->set = expr->right->set;
+ expr_postprocess(ctx, &expr->left);
+ ctx->set = NULL;
+ break;
+ case EXPR_UNARY:
+ if (lhs_is_meta_hour(expr->left->arg) &&
+ expr->right->etype == EXPR_RANGE) {
+ struct expr *range = expr->right;
+
+ /* Cross-day range needs to be reversed.
+ * Kernel handles time in UTC. Therefore,
+ * 03:00-14:00 AEDT (Sidney, Australia) time
+ * is a cross-day range.
+ */
+ if (mpz_cmp(range->left->value,
+ range->right->value) <= 0 &&
+ expr->op == OP_NEQ) {
+ range_expr_swap_values(range);
+ expr->op = OP_IMPLICIT;
+ }
+ }
+ /* fallthrough */
default:
expr_postprocess(ctx, &expr->left);
break;
@@ -2639,22 +2888,18 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
}
break;
case EXPR_PAYLOAD:
- payload_expr_complete(expr, &ctx->pctx);
- payload_dependency_kill(&ctx->pdctx, expr, ctx->pctx.family);
+ payload_expr_complete(expr, &dl->pctx);
+ if (expr->payload.inner_desc) {
+ if (meta_outer_may_dependency_kill(ctx, expr)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->payload.inner_desc->base);
+ }
+ }
+ payload_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_VALUE:
- // FIXME
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
-
- if (expr_basetype(expr)->type == TYPE_STRING)
- *exprp = expr_postprocess_string(expr);
-
- expr = *exprp;
- if (expr->dtype->basetype != NULL &&
- expr->dtype->basetype->type == TYPE_BITMASK)
- *exprp = bitmask_expr_to_binops(expr);
-
+ expr_postprocess_value(ctx, exprp);
break;
case EXPR_RANGE:
expr_postprocess(ctx, &expr->left);
@@ -2669,7 +2914,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
ctx->flags &= ~RULE_PP_IN_SET_ELEM;
break;
case EXPR_EXTHDR:
- exthdr_dependency_kill(&ctx->pdctx, expr, ctx->pctx.family);
+ exthdr_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_SET_REF:
case EXPR_META:
@@ -2686,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));
@@ -2695,34 +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:
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;
}
@@ -2731,26 +2977,26 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
*/
stmt->reject.verbose_print = 1;
- base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = dl->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = dl->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4: /* INET */
case __constant_htons(ETH_P_IP): /* BRIDGE, NETDEV */
stmt->reject.family = NFPROTO_IPV4;
- datatype_set(stmt->reject.expr, &icmp_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmp_code_type);
break;
case NFPROTO_IPV6: /* INET */
case __constant_htons(ETH_P_IPV6): /* BRIDGE, NETDEV */
stmt->reject.family = NFPROTO_IPV6;
- datatype_set(stmt->reject.expr, &icmpv6_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmpv6_code_type);
break;
default:
break;
}
- if (payload_dependency_exists(&rctx->pdctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(&rctx->pdctx,
+ if (payload_dependency_exists(&dl->pdctx, PROTO_BASE_NETWORK_HDR))
+ payload_dependency_release(&dl->pdctx,
PROTO_BASE_NETWORK_HDR);
break;
default:
@@ -2794,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);
@@ -2819,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)) {
+ if (payload_expr_trim(payload, mask, &dl->pctx, &shift)) {
binop_adjust(binop, mask, shift);
- payload_expr_complete(payload, &ctx->pctx);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(mask, payload->dtype,
payload->byteorder);
}
@@ -2993,9 +3242,10 @@ 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, &ctx->pctx);
+ payload_expr_complete(stmt->payload.expr, &dl->pctx);
if (!payload_is_known(stmt->payload.expr))
stmt_payload_binop_postprocess(ctx);
@@ -3041,19 +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 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:
@@ -3084,16 +3390,14 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
case STMT_NAT:
if (stmt->nat.addr != NULL)
expr_postprocess(&rctx, &stmt->nat.addr);
- if (stmt->nat.proto != NULL) {
- payload_dependency_reset(&rctx.pdctx);
+ if (stmt->nat.proto != NULL)
expr_postprocess(&rctx, &stmt->nat.proto);
- }
break;
case STMT_TPROXY:
if (stmt->tproxy.addr)
expr_postprocess(&rctx, &stmt->tproxy.addr);
if (stmt->tproxy.port) {
- payload_dependency_reset(&rctx.pdctx);
+ payload_dependency_reset(&dl->pdctx);
expr_postprocess(&rctx, &stmt->tproxy.port);
}
break;
@@ -3131,9 +3435,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
break;
}
- rctx.pdctx.prev = rctx.stmt;
+ dl->pdctx.prev = rctx.stmt;
- rule_maybe_reset_payload_deps(&rctx.pdctx, type);
+ rule_maybe_reset_payload_deps(&dl->pdctx, type);
}
}
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index c8bbcb74..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>
@@ -178,9 +179,8 @@ static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx, nle, &expr->location);
}
-static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
- const struct expr *expr,
- enum nft_registers dreg)
+static struct nftnl_expr *
+__netlink_gen_payload(const struct expr *expr, enum nft_registers dreg)
{
struct nftnl_expr *nle;
@@ -193,6 +193,72 @@ static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
+ return nle;
+}
+
+static struct nftnl_expr *
+__netlink_gen_meta(const struct expr *expr, enum nft_registers dreg)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("meta");
+ netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
+
+ return nle;
+}
+
+static struct nftnl_expr *netlink_gen_inner_expr(const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct expr *_expr = (struct expr *)expr;
+ struct nftnl_expr *nle;
+
+ switch (expr->etype) {
+ case EXPR_PAYLOAD:
+ if (expr->payload.base == NFT_PAYLOAD_INNER_HEADER + 1)
+ _expr->payload.base = NFT_PAYLOAD_TUN_HEADER + 1;
+
+ nle = __netlink_gen_payload(expr, dreg);
+ break;
+ case EXPR_META:
+ nle = __netlink_gen_meta(expr, dreg);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return nle;
+}
+
+static void netlink_gen_inner(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg,
+ const struct proto_desc *desc)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("inner");
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_HDRSIZE, desc->inner.hdrsize);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_FLAGS, desc->inner.flags);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_TYPE, desc->inner.type);
+ nftnl_expr_set(nle, NFTNL_EXPR_INNER_EXPR, netlink_gen_inner_expr(expr, dreg), 0);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+}
+
+static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct nftnl_expr *nle;
+
+ if (expr->payload.inner_desc) {
+ netlink_gen_inner(ctx, expr, dreg, expr->payload.inner_desc);
+ return;
+ }
+
+ nle = __netlink_gen_payload(expr, dreg);
nft_rule_add_expr(ctx, nle, &expr->location);
}
@@ -221,9 +287,12 @@ static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
{
struct nftnl_expr *nle;
- nle = alloc_nft_expr("meta");
- netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
+ if (expr->meta.inner_desc) {
+ netlink_gen_inner(ctx, expr, dreg, expr->meta.inner_desc);
+ return;
+ }
+
+ nle = __netlink_gen_meta(expr, dreg);
nft_rule_add_expr(ctx, nle, &expr->location);
}
@@ -391,7 +460,8 @@ static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx,
mpz_init(mask);
mpz_prefixmask(mask, expr->right->len, expr->right->prefix_len);
netlink_gen_raw_data(mask, expr->right->byteorder,
- expr->right->len / BITS_PER_BYTE, &nld);
+ div_round_up(expr->right->len, BITS_PER_BYTE),
+ &nld);
mpz_clear(mask);
zero.len = nld.len;
@@ -625,10 +695,10 @@ 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;
@@ -640,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) {
@@ -725,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)
@@ -738,7 +812,7 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx,
netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_SREG, dreg);
netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_DREG, dreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_LEN,
- expr->len / BITS_PER_BYTE);
+ div_round_up(expr->len, BITS_PER_BYTE));
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_SIZE,
byte_size);
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_OP,
@@ -933,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) {
@@ -944,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);
}
@@ -1502,13 +1589,13 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
int num_stmts = 0;
struct stmt *this;
- sreg_key = get_register(ctx, stmt->map.key);
- netlink_gen_expr(ctx, stmt->map.key, sreg_key);
+ sreg_key = get_register(ctx, stmt->map.key->key);
+ netlink_gen_expr(ctx, stmt->map.key->key, sreg_key);
sreg_data = get_register(ctx, stmt->map.data);
netlink_gen_expr(ctx, stmt->map.data, sreg_data);
- release_register(ctx, stmt->map.key);
+ release_register(ctx, stmt->map.key->key);
release_register(ctx, stmt->map.data);
nle = alloc_nft_expr("dynset");
@@ -1520,6 +1607,10 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
nft_rule_add_expr(ctx, nle, &stmt->location);
+ if (stmt->map.key->timeout > 0)
+ nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT,
+ stmt->map.key->timeout);
+
list_for_each_entry(this, &stmt->map.stmt_list, list)
num_stmts++;
@@ -1615,6 +1706,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
case STMT_COUNTER:
case STMT_LIMIT:
case STMT_QUOTA:
+ case STMT_LAST:
nle = netlink_gen_stmt_stateful(stmt);
nft_rule_add_expr(ctx, nle, &stmt->location);
break;
@@ -1656,9 +1748,9 @@ void netlink_linearize_fini(struct netlink_linearize_ctx *lctx)
for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) {
list_for_each_entry_safe(eloc, next, &lctx->expr_loc_htable[i], hlist)
- xfree(eloc);
+ free(eloc);
}
- xfree(lctx->expr_loc_htable);
+ free(lctx->expr_loc_htable);
}
void netlink_linearize_rule(struct netlink_ctx *ctx,
diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c
index 08e978de..20a1bfe7 100644
--- a/src/nfnl_osf.c
+++ b/src/nfnl_osf.c
@@ -19,12 +19,12 @@
* Based on iptables/utils/nfnl_osf.c.
*/
+#include <nft.h>
+
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
#include <time.h>
#include <netinet/ip.h>
diff --git a/src/nftutils.c b/src/nftutils.c
new file mode 100644
index 00000000..ca178aa0
--- /dev/null
+++ b/src/nftutils.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <nft.h>
+
+#include "nftutils.h"
+
+#include <netdb.h>
+
+/* Buffer size used for getprotobynumber_r() and similar. The manual comments
+ * that a buffer of 1024 should be sufficient "for most applications"(??), so
+ * let's double it. It still fits reasonably on the stack, so no need to
+ * choose a smaller one. */
+#define NETDB_BUFSIZE 2048
+
+bool nft_getprotobynumber(int proto, char *out_name, size_t name_len)
+{
+ const struct protoent *result;
+
+#if HAVE_DECL_GETPROTOBYNUMBER_R
+ struct protoent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getprotobynumber_r(proto,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct protoent **) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getprotobynumber(proto);
+#endif
+
+ if (!result)
+ return false;
+
+ if (strlen(result->p_name) >= name_len)
+ return false;
+ strcpy(out_name, result->p_name);
+ return true;
+}
+
+int nft_getprotobyname(const char *name)
+{
+ const struct protoent *result;
+
+#if HAVE_DECL_GETPROTOBYNAME_R
+ struct protoent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getprotobyname_r(name,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct protoent **) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getprotobyname(name);
+#endif
+
+ if (!result)
+ return -1;
+
+ if (result->p_proto < 0 || result->p_proto > UINT8_MAX)
+ return -1;
+ return (uint8_t) result->p_proto;
+}
+
+bool nft_getservbyport(int port, const char *proto, char *out_name, size_t name_len)
+{
+ const struct servent *result;
+
+#if HAVE_DECL_GETSERVBYPORT_R
+ struct servent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getservbyport_r(port,
+ proto,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct servent**) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getservbyport(port, proto);
+#endif
+
+ if (!result)
+ return false;
+
+ if (strlen(result->s_name) >= name_len)
+ return false;
+ strcpy(out_name, result->s_name);
+ return true;
+}
diff --git a/src/nftutils.h b/src/nftutils.h
new file mode 100644
index 00000000..7db56f42
--- /dev/null
+++ b/src/nftutils.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef NFTUTILS_H
+#define NFTUTILS_H
+
+#include <stddef.h>
+
+/* The maximum buffer size for (struct protoent).p_name. It is excessively large,
+ * while still reasonably fitting on the stack. Arbitrarily chosen. */
+#define NFT_PROTONAME_MAXSIZE 1024
+
+bool nft_getprotobynumber(int number, char *out_name, size_t name_len);
+int nft_getprotobyname(const char *name);
+
+/* The maximum buffer size for (struct servent).s_name. It is excessively large,
+ * while still reasonably fitting on the stack. Arbitrarily chosen. */
+#define NFT_SERVNAME_MAXSIZE 1024
+
+bool nft_getservbyport(int port, const char *proto, char *out_name, size_t name_len);
+
+#endif /* NFTUTILS_H */
diff --git a/src/numgen.c b/src/numgen.c
index ea2b2626..3029fa58 100644
--- a/src/numgen.c
+++ b/src/numgen.c
@@ -4,10 +4,12 @@
* Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <datatype.h>
diff --git a/src/optimize.c b/src/optimize.c
index 09013efc..b90dd995 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -11,8 +11,8 @@
* programme.
*/
-#define _GNU_SOURCE
-#include <string.h>
+#include <nft.h>
+
#include <errno.h>
#include <inttypes.h>
#include <nftables.h>
@@ -21,6 +21,7 @@
#include <statement.h>
#include <utils.h>
#include <erec.h>
+#include <linux/netfilter.h>
#define MAX_STMTS 32
@@ -46,6 +47,8 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
return false;
if (expr_a->payload.desc != expr_b->payload.desc)
return false;
+ if (expr_a->payload.inner_desc != expr_b->payload.inner_desc)
+ return false;
if (expr_a->payload.tmpl != expr_b->payload.tmpl)
return false;
break;
@@ -236,21 +239,58 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
if (stmt_a->nat.type != stmt_b->nat.type ||
stmt_a->nat.flags != stmt_b->nat.flags ||
stmt_a->nat.family != stmt_b->nat.family ||
- stmt_a->nat.type_flags != stmt_b->nat.type_flags ||
- (stmt_a->nat.addr &&
- stmt_a->nat.addr->etype != EXPR_SYMBOL &&
- stmt_a->nat.addr->etype != EXPR_RANGE) ||
- (stmt_b->nat.addr &&
- stmt_b->nat.addr->etype != EXPR_SYMBOL &&
- stmt_b->nat.addr->etype != EXPR_RANGE) ||
- (stmt_a->nat.proto &&
- stmt_a->nat.proto->etype != EXPR_SYMBOL &&
- stmt_a->nat.proto->etype != EXPR_RANGE) ||
- (stmt_b->nat.proto &&
- stmt_b->nat.proto->etype != EXPR_SYMBOL &&
- stmt_b->nat.proto->etype != EXPR_RANGE))
+ stmt_a->nat.type_flags != stmt_b->nat.type_flags)
return false;
+ switch (stmt_a->nat.type) {
+ case NFT_NAT_SNAT:
+ case NFT_NAT_DNAT:
+ if ((stmt_a->nat.addr &&
+ stmt_a->nat.addr->etype != EXPR_SYMBOL &&
+ stmt_a->nat.addr->etype != EXPR_RANGE) ||
+ (stmt_b->nat.addr &&
+ stmt_b->nat.addr->etype != EXPR_SYMBOL &&
+ stmt_b->nat.addr->etype != EXPR_RANGE) ||
+ (stmt_a->nat.proto &&
+ stmt_a->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_a->nat.proto->etype != EXPR_RANGE) ||
+ (stmt_b->nat.proto &&
+ stmt_b->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_b->nat.proto->etype != EXPR_RANGE))
+ return false;
+ break;
+ case NFT_NAT_MASQ:
+ break;
+ case NFT_NAT_REDIR:
+ if ((stmt_a->nat.proto &&
+ stmt_a->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_a->nat.proto->etype != EXPR_RANGE) ||
+ (stmt_b->nat.proto &&
+ stmt_b->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_b->nat.proto->etype != EXPR_RANGE))
+ return false;
+
+ /* it should be possible to infer implicit redirections
+ * such as:
+ *
+ * tcp dport 1234 redirect
+ * tcp dport 3456 redirect to :7890
+ * merge:
+ * redirect to tcp dport map { 1234 : 1234, 3456 : 7890 }
+ *
+ * currently not implemented.
+ */
+ if (fully_compare &&
+ stmt_a->nat.type == NFT_NAT_REDIR &&
+ stmt_b->nat.type == NFT_NAT_REDIR &&
+ (!!stmt_a->nat.proto ^ !!stmt_b->nat.proto))
+ return false;
+
+ break;
+ default:
+ assert(0);
+ }
+
return true;
default:
/* ... Merging anything else is yet unsupported. */
@@ -356,6 +396,7 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
clone->ops = &unsupported_stmt_ops;
break;
}
+ /* fall-through */
case STMT_VERDICT:
clone->expr = expr_get(stmt->expr);
break;
@@ -368,6 +409,13 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
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)
@@ -550,20 +598,19 @@ static void merge_stmts(const struct optimize_ctx *ctx,
}
}
-static void __merge_concat_stmts(const struct optimize_ctx *ctx, uint32_t i,
- const struct merge *merge, struct expr *set)
+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, *elem;
+ struct expr *concat, *next, *expr, *concat_clone, *clone;
LIST_HEAD(pending_list);
- LIST_HEAD(concat_list);
struct stmt *stmt_a;
uint32_t k;
concat = concat_expr_alloc(&internal_location);
- list_add(&concat->list, &concat_list);
+ list_add(&concat->list, concat_list);
for (k = 0; k < merge->num_stmts; k++) {
- list_for_each_entry_safe(concat, next, &concat_list, list) {
+ 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:
@@ -588,8 +635,17 @@ static void __merge_concat_stmts(const struct optimize_ctx *ctx, uint32_t i,
break;
}
}
- list_splice_init(&pending_list, &concat_list);
+ 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);
@@ -634,7 +690,8 @@ static void merge_concat_stmts(const struct optimize_ctx *ctx,
}
}
-static void build_verdict_map(struct expr *expr, struct stmt *verdict, struct expr *set)
+static void build_verdict_map(struct expr *expr, struct stmt *verdict,
+ struct expr *set, struct stmt *counter)
{
struct expr *item, *elem, *mapping;
@@ -642,6 +699,9 @@ static void build_verdict_map(struct expr *expr, struct stmt *verdict, struct ex
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);
@@ -650,6 +710,9 @@ static void build_verdict_map(struct expr *expr, struct stmt *verdict, struct ex
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);
@@ -661,6 +724,9 @@ static void build_verdict_map(struct expr *expr, struct stmt *verdict, struct ex
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);
@@ -689,6 +755,26 @@ static void remove_counter(const struct optimize_ctx *ctx, uint32_t from)
}
}
+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)
@@ -696,31 +782,33 @@ static void merge_stmts_vmap(const struct optimize_ctx *ctx,
struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
struct stmt *stmt_b, *verdict_a, *verdict_b, *stmt;
struct expr *expr_a, *expr_b, *expr, *left, *set;
+ struct stmt *counter;
uint32_t i;
int k;
k = stmt_verdict_find(ctx);
assert(k >= 0);
- verdict_a = ctx->stmt_matrix[from][k];
set = set_expr_alloc(&internal_location, NULL);
set->set_flags |= NFT_SET_ANONYMOUS;
expr_a = stmt_a->expr->right;
- build_verdict_map(expr_a, verdict_a, set);
+ verdict_a = ctx->stmt_matrix[from][k];
+ counter = zap_counter(ctx, from);
+ build_verdict_map(expr_a, verdict_a, set, counter);
+
for (i = from + 1; i <= to; i++) {
stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
expr_b = stmt_b->expr->right;
verdict_b = ctx->stmt_matrix[i][k];
-
- build_verdict_map(expr_b, verdict_b, set);
+ counter = zap_counter(ctx, i);
+ build_verdict_map(expr_b, verdict_b, set, counter);
}
left = expr_get(stmt_a->expr->left);
expr = map_expr_alloc(&internal_location, left, set);
stmt = verdict_stmt_alloc(&internal_location, expr);
- remove_counter(ctx, from);
list_add(&stmt->list, &stmt_a->list);
list_del(&stmt_a->list);
stmt_free(stmt_a);
@@ -728,14 +816,37 @@ static void merge_stmts_vmap(const struct optimize_ctx *ctx,
stmt_free(verdict_a);
}
+static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
+ uint32_t i, const struct merge *merge,
+ struct expr *set, struct stmt *verdict)
+{
+ struct expr *concat, *next, *elem, *mapping;
+ 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 expr *concat_a, *concat_b, *expr, *set;
- struct stmt *stmt, *stmt_a, *stmt_b, *verdict;
- uint32_t i, j;
+ struct stmt *stmt, *stmt_a, *verdict;
+ struct expr *concat_a, *expr, *set;
+ uint32_t i;
int k;
k = stmt_verdict_find(ctx);
@@ -753,21 +864,13 @@ static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
set->set_flags |= NFT_SET_ANONYMOUS;
for (i = from; i <= to; i++) {
- concat_b = concat_expr_alloc(&internal_location);
- for (j = 0; j < merge->num_stmts; j++) {
- stmt_b = ctx->stmt_matrix[i][merge->stmt[j]];
- expr = stmt_b->expr->right;
- compound_expr_add(concat_b, expr_get(expr));
- }
verdict = ctx->stmt_matrix[i][k];
- build_verdict_map(concat_b, verdict, set);
- expr_free(concat_b);
+ __merge_concat_stmts_vmap(ctx, i, merge, set, verdict);
}
expr = map_expr_alloc(&internal_location, concat_a, set);
stmt = verdict_stmt_alloc(&internal_location, expr);
- remove_counter(ctx, from);
list_add(&stmt->list, &orig_stmt->list);
list_del(&orig_stmt->list);
stmt_free(orig_stmt);
@@ -808,12 +911,35 @@ static bool stmt_verdict_cmp(const struct optimize_ctx *ctx,
return true;
}
-static int stmt_nat_find(const struct optimize_ctx *ctx)
+static int stmt_nat_type(const struct optimize_ctx *ctx, int from,
+ enum nft_nat_etypes *nat_type)
{
+ uint32_t j;
+
+ for (j = 0; j < ctx->num_stmts; j++) {
+ if (!ctx->stmt_matrix[from][j])
+ continue;
+
+ if (ctx->stmt_matrix[from][j]->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)
+ if (ctx->stmt[i]->ops->type != STMT_NAT ||
+ ctx->stmt[i]->nat.type != nat_type)
continue;
return i;
@@ -826,16 +952,24 @@ 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) {
- nat_expr = concat_expr_alloc(&internal_location);
- compound_expr_add(nat_expr, expr_get(nat_stmt->nat.addr));
- compound_expr_add(nat_expr, expr_get(nat_stmt->nat.proto));
+ if (nat_stmt->nat.addr) {
+ nat_expr = concat_expr_alloc(&internal_location);
+ compound_expr_add(nat_expr, expr_get(nat_stmt->nat.addr));
+ compound_expr_add(nat_expr, expr_get(nat_stmt->nat.proto));
+ } else {
+ nat_expr = expr_get(nat_stmt->nat.proto);
+ }
expr_free(nat_stmt->nat.proto);
nat_stmt->nat.proto = NULL;
} else {
nat_expr = expr_get(nat_stmt->nat.addr);
}
+ assert(nat_expr);
+
return nat_expr;
}
@@ -844,11 +978,11 @@ static void merge_nat(const struct optimize_ctx *ctx,
const struct merge *merge)
{
struct expr *expr, *set, *elem, *nat_expr, *mapping, *left;
+ int k, family = NFPROTO_UNSPEC;
struct stmt *stmt, *nat_stmt;
uint32_t i;
- int k;
- k = stmt_nat_find(ctx);
+ k = stmt_nat_find(ctx, from);
assert(k >= 0);
set = set_expr_alloc(&internal_location, NULL);
@@ -868,11 +1002,23 @@ static void merge_nat(const struct optimize_ctx *ctx,
stmt = ctx->stmt_matrix[from][merge->stmt[0]];
left = expr_get(stmt->expr->left);
+ if (left->etype == EXPR_PAYLOAD) {
+ if (left->payload.desc == &proto_ip)
+ family = NFPROTO_IPV4;
+ else if (left->payload.desc == &proto_ip6)
+ family = NFPROTO_IPV6;
+ }
expr = map_expr_alloc(&internal_location, left, set);
nat_stmt = ctx->stmt_matrix[from][k];
+ if (nat_stmt->nat.family == NFPROTO_UNSPEC)
+ nat_stmt->nat.family = family;
+
expr_free(nat_stmt->nat.addr);
- nat_stmt->nat.addr = expr;
+ if (nat_stmt->nat.type == NFT_NAT_REDIR)
+ nat_stmt->nat.proto = expr;
+ else
+ nat_stmt->nat.addr = expr;
remove_counter(ctx, from);
list_del(&stmt->list);
@@ -884,11 +1030,11 @@ static void merge_concat_nat(const struct optimize_ctx *ctx,
const struct merge *merge)
{
struct expr *expr, *set, *elem, *nat_expr, *mapping, *left, *concat;
+ int k, family = NFPROTO_UNSPEC;
struct stmt *stmt, *nat_stmt;
uint32_t i, j;
- int k;
- k = stmt_nat_find(ctx);
+ k = stmt_nat_find(ctx, from);
assert(k >= 0);
set = set_expr_alloc(&internal_location, NULL);
@@ -915,11 +1061,20 @@ static void merge_concat_nat(const struct optimize_ctx *ctx,
for (j = 0; j < merge->num_stmts; j++) {
stmt = ctx->stmt_matrix[from][merge->stmt[j]];
left = stmt->expr->left;
+ if (left->etype == EXPR_PAYLOAD) {
+ if (left->payload.desc == &proto_ip)
+ family = NFPROTO_IPV4;
+ else if (left->payload.desc == &proto_ip6)
+ family = NFPROTO_IPV6;
+ }
compound_expr_add(concat, expr_get(left));
}
expr = map_expr_alloc(&internal_location, concat, set);
nat_stmt = ctx->stmt_matrix[from][k];
+ if (nat_stmt->nat.family == NFPROTO_UNSPEC)
+ nat_stmt->nat.family = family;
+
expr_free(nat_stmt->nat.addr);
nat_stmt->nat.addr = expr;
@@ -964,22 +1119,36 @@ static void rule_optimize_print(struct output_ctx *octx,
fprintf(octx->error_fp, "%s\n", line);
}
-static enum stmt_types merge_stmt_type(const struct optimize_ctx *ctx)
+enum {
+ MERGE_BY_VERDICT,
+ MERGE_BY_NAT_MAP,
+ MERGE_BY_NAT,
+};
+
+static uint32_t merge_stmt_type(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to)
{
- uint32_t i;
+ const struct stmt *stmt;
+ uint32_t i, j;
- for (i = 0; i < ctx->num_stmts; i++) {
- switch (ctx->stmt[i]->ops->type) {
- case STMT_VERDICT:
- case STMT_NAT:
- return ctx->stmt[i]->ops->type;
- default:
- continue;
+ for (i = from; i <= to; i++) {
+ for (j = 0; j < ctx->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[i][j];
+ if (!stmt)
+ continue;
+ if (stmt->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;
+ }
}
}
- /* actually no verdict, this assumes rules have the same verdict. */
- return STMT_VERDICT;
+ /* merge by verdict, even if no verdict is specified. */
+ return MERGE_BY_VERDICT;
}
static void merge_rules(const struct optimize_ctx *ctx,
@@ -987,14 +1156,14 @@ static void merge_rules(const struct optimize_ctx *ctx,
const struct merge *merge,
struct output_ctx *octx)
{
- enum stmt_types stmt_type;
+ uint32_t merge_type;
bool same_verdict;
uint32_t i;
- stmt_type = merge_stmt_type(ctx);
+ merge_type = merge_stmt_type(ctx, from, to);
- switch (stmt_type) {
- case STMT_VERDICT:
+ switch (merge_type) {
+ case MERGE_BY_VERDICT:
same_verdict = stmt_verdict_cmp(ctx, from, to);
if (merge->num_stmts > 1) {
if (same_verdict)
@@ -1008,18 +1177,24 @@ static void merge_rules(const struct optimize_ctx *ctx,
merge_stmts_vmap(ctx, from, to, merge);
}
break;
- case STMT_NAT:
+ case MERGE_BY_NAT_MAP:
if (merge->num_stmts > 1)
merge_concat_nat(ctx, from, to, merge);
else
merge_nat(ctx, from, to, merge);
break;
+ case MERGE_BY_NAT:
+ if (merge->num_stmts > 1)
+ merge_concat_stmts(ctx, from, to, merge);
+ else
+ merge_stmts(ctx, from, to, merge);
+ break;
default:
assert(0);
}
if (ctx->rule[from]->comment) {
- xfree(ctx->rule[from]->comment);
+ free_const(ctx->rule[from]->comment);
ctx->rule[from]->comment = NULL;
}
@@ -1111,10 +1286,11 @@ static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
ctx->num_rules++;
}
- ctx->rule = xzalloc(sizeof(ctx->rule) * ctx->num_rules);
- ctx->stmt_matrix = xzalloc(sizeof(struct stmt *) * ctx->num_rules);
+ ctx->rule = xzalloc(sizeof(*ctx->rule) * ctx->num_rules);
+ ctx->stmt_matrix = xzalloc(sizeof(*ctx->stmt_matrix) * ctx->num_rules);
for (i = 0; i < ctx->num_rules; i++)
- ctx->stmt_matrix[i] = xzalloc(sizeof(struct stmt *) * MAX_STMTS);
+ ctx->stmt_matrix[i] = xzalloc_array(MAX_STMTS,
+ sizeof(**ctx->stmt_matrix));
merge = xzalloc(sizeof(*merge) * ctx->num_rules);
@@ -1171,16 +1347,16 @@ static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
}
ret = 0;
for (i = 0; i < ctx->num_rules; i++)
- xfree(ctx->stmt_matrix[i]);
+ free(ctx->stmt_matrix[i]);
- xfree(ctx->stmt_matrix);
- xfree(merge);
+ free(ctx->stmt_matrix);
+ free(merge);
err:
for (i = 0; i < ctx->num_stmts; i++)
stmt_free(ctx->stmt[i]);
- xfree(ctx->rule);
- xfree(ctx);
+ free(ctx->rule);
+ free(ctx);
return ret;
}
@@ -1214,7 +1390,7 @@ static int cmd_optimize(struct nft_ctx *nft, struct cmd *cmd)
int nft_optimize(struct nft_ctx *nft, struct list_head *cmds)
{
struct cmd *cmd;
- int ret;
+ int ret = 0;
list_for_each_entry(cmd, cmds, list) {
switch (cmd->op) {
diff --git a/src/osf.c b/src/osf.c
index cb58315d..a8f80b2b 100644
--- a/src/osf.c
+++ b/src/osf.c
@@ -1,7 +1,16 @@
+/*
+ * Copyright (c) 2018 Fernando Fernandez Mancera <ffmancera@riseup.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <utils.h>
-#include <string.h>
#include <osf.h>
#include <json.h>
diff --git a/src/owner.c b/src/owner.c
index 2d98a2e9..65eaad3e 100644
--- a/src/owner.c
+++ b/src/owner.c
@@ -1,6 +1,15 @@
+/*
+ * Copyright (c) 2021 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
#include <stdio.h>
#include <unistd.h>
-#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <inttypes.h>
@@ -66,7 +75,7 @@ static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode)
continue;
rl = readlink(procname, tmp, sizeof(tmp));
- if (rl <= 0 || rl > (ssize_t)sizeof(tmp))
+ if (rl <= 0 || rl >= (ssize_t)sizeof(tmp))
continue;
tmp[rl] = 0;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 760c23cf..53f45315 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -9,12 +9,14 @@
*/
%{
+#include <nft.h>
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <inttypes.h>
#include <syslog.h>
+#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/if_ether.h>
@@ -80,12 +82,11 @@ static int open_scope(struct parser_state *state, struct scope *scope)
static void close_scope(struct parser_state *state)
{
- if (state->scope_err) {
+ if (state->scope_err || state->scope == 0) {
state->scope_err = false;
return;
}
- assert(state->scope > 0);
state->scope--;
}
@@ -145,6 +146,51 @@ static bool already_set(const void *attr, const struct location *loc,
return true;
}
+static struct expr *ifname_expr_alloc(const struct location *location,
+ struct list_head *queue,
+ const char *name)
+{
+ size_t length = strlen(name);
+ struct expr *expr;
+
+ if (length == 0) {
+ free_const(name);
+ erec_queue(error(location, "empty interface name"), queue);
+ return NULL;
+ }
+
+ if (length >= IFNAMSIZ) {
+ free_const(name);
+ erec_queue(error(location, "interface name too long"), queue);
+ return NULL;
+ }
+
+ expr = constant_expr_alloc(location, &ifname_type, BYTEORDER_HOST_ENDIAN,
+ length * BITS_PER_BYTE, name);
+
+ free_const(name);
+
+ return expr;
+}
+
+static void timeout_state_free(struct timeout_state *s)
+{
+ free_const(s->timeout_str);
+ free(s);
+}
+
+static void timeout_states_free(struct list_head *list)
+{
+ struct timeout_state *ts, *next;
+
+ list_for_each_entry_safe(ts, next, list, head) {
+ list_del(&ts->head);
+ timeout_state_free(ts);
+ }
+
+ free(list);
+}
+
#define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N)
#define symbol_value(loc, str) \
@@ -202,6 +248,7 @@ int nft_lex(void *, void *, void *);
uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */
uint8_t field;
} tcp_kind_field;
+ struct timeout_state *timeout_state;
}
%token TOKEN_EOF 0 "end of file"
@@ -291,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"
@@ -328,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"
@@ -393,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"
@@ -439,6 +489,14 @@ int nft_lex(void *, void *, void *);
%token DCCP "dccp"
+%token VXLAN "vxlan"
+%token VNI "vni"
+
+%token GRE "gre"
+%token GRETAP "gretap"
+
+%token GENEVE "geneve"
+
%token SCTP "sctp"
%token CHUNK "chunk"
%token DATA "data"
@@ -544,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"
@@ -626,13 +687,15 @@ int nft_lex(void *, void *, void *);
%token IN "in"
%token OUT "out"
+%token XT "xt"
+
%type <limit_rate> limit_rate_pkts
%type <limit_rate> limit_rate_bytes
%type <string> identifier type_identifier string comment_spec
-%destructor { xfree($$); } identifier type_identifier string comment_spec
+%destructor { free_const($$); } identifier type_identifier string comment_spec
-%type <val> time_spec quota_used
+%type <val> time_spec time_spec_or_num_s quota_used
%type <expr> data_type_expr data_type_atom_expr
%destructor { expr_free($$); } data_type_expr data_type_atom_expr
@@ -640,8 +703,8 @@ int nft_lex(void *, void *, void *);
%type <cmd> line
%destructor { cmd_free($$); } line
-%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
-%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
+%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
+%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
%type <handle> table_spec tableid_spec table_or_id_spec
%destructor { handle_free(&$$); } table_spec tableid_spec table_or_id_spec
@@ -664,11 +727,13 @@ int nft_lex(void *, void *, void *);
%type <val> family_spec family_spec_explicit
%type <val32> int_num chain_policy
%type <prio_spec> extended_prio_spec prio_spec
+%destructor { expr_free($$.expr); } extended_prio_spec prio_spec
+
%type <string> extended_prio_name quota_unit basehook_device_name
-%destructor { xfree($$); } extended_prio_name quota_unit basehook_device_name
+%destructor { free_const($$); } extended_prio_name quota_unit basehook_device_name
%type <expr> dev_spec
-%destructor { xfree($$); } dev_spec
+%destructor { free($$); } dev_spec
%type <table> table_block_alloc table_block
%destructor { close_scope(state); table_free($$); } table_block_alloc
@@ -677,6 +742,8 @@ int nft_lex(void *, void *, void *);
%type <rule> rule rule_alloc
%destructor { rule_free($$); } rule
+%type <val> table_flags table_flag
+
%type <val> set_flag_list set_flag
%type <val> set_policy_spec
@@ -686,7 +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
+%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
@@ -695,11 +762,14 @@ int nft_lex(void *, void *, void *);
%destructor { obj_free($$); } obj_block_alloc
%type <list> stmt_list stateful_stmt_list set_elem_stmt_list
-%destructor { stmt_list_free($$); xfree($$); } stmt_list stateful_stmt_list set_elem_stmt_list
+%destructor { stmt_list_free($$); free($$); } stmt_list stateful_stmt_list set_elem_stmt_list
%type <stmt> stmt match_stmt verdict_stmt set_elem_stmt
%destructor { stmt_free($$); } stmt match_stmt verdict_stmt set_elem_stmt
-%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt
-%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt
+%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt last_stmt
+%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt last_stmt
+%type <stmt> 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
@@ -744,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 typeof_data_expr
-%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr
+%type <expr> primary_expr shift_expr and_expr 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
@@ -815,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
@@ -884,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
@@ -897,9 +969,19 @@ int nft_lex(void *, void *, void *);
%type <val> tcpopt_field_maxseg tcpopt_field_mptcp tcpopt_field_sack tcpopt_field_tsopt tcpopt_field_window
%type <tcp_kind_field> tcp_hdr_option_kind_and_field
+%type <expr> inner_eth_expr inner_inet_expr inner_expr
+%destructor { expr_free($$); } inner_eth_expr inner_inet_expr inner_expr
+
+%type <expr> vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%destructor { expr_free($$); } vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%type <val> vxlan_hdr_field geneve_hdr_field gre_hdr_field
+
%type <stmt> optstrip_stmt
%destructor { stmt_free($$); } optstrip_stmt
+%type <stmt> xt_stmt
+%destructor { stmt_free($$); } xt_stmt
+
%type <expr> boolean_expr
%destructor { expr_free($$); } boolean_expr
%type <val8> boolean_keys
@@ -908,10 +990,13 @@ int nft_lex(void *, void *, void *);
%destructor { expr_free($$); } exthdr_exists_expr
%type <val> exthdr_key
-%type <val> ct_l4protoname ct_obj_type ct_cmd_type
+%type <val> ct_l4protoname ct_obj_type ct_cmd_type ct_obj_type_map
+
+%type <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
@@ -946,7 +1031,9 @@ close_scope_at : { scanner_pop_start_cond(nft->scanner, PARSER_SC_AT); };
close_scope_comp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_COMP); };
close_scope_ct : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CT); };
close_scope_counter : { scanner_pop_start_cond(nft->scanner, PARSER_SC_COUNTER); };
+close_scope_last : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LAST); };
close_scope_dccp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DCCP); };
+close_scope_destroy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_DESTROY); };
close_scope_dst : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DST); };
close_scope_dup : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_DUP); };
close_scope_esp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_ESP); };
@@ -955,6 +1042,7 @@ close_scope_export : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT
close_scope_fib : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); };
close_scope_frag : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); };
close_scope_fwd : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); };
+close_scope_gre : { scanner_pop_start_cond(nft->scanner, PARSER_SC_GRE); };
close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); };
close_scope_hbh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HBH); };
close_scope_ip : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP); };
@@ -991,14 +1079,15 @@ close_scope_udplite : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPL
close_scope_log : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_LOG); }
close_scope_synproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_SYNPROXY); }
+close_scope_xt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); }
common_block : INCLUDE QUOTED_STRING stmt_separator
{
if (scanner_include_file(nft, scanner, $2, &@$) < 0) {
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
| DEFINE identifier '=' initializer_expr stmt_separator
{
@@ -1008,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
{
@@ -1029,10 +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);
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
| error stmt_separator
{
@@ -1080,6 +1169,7 @@ base_cmd : /* empty */ add_cmd { $$ = $1; }
| EXPORT export_cmd close_scope_export { $$ = $2; }
| MONITOR monitor_cmd close_scope_monitor { $$ = $2; }
| DESCRIBE describe_cmd { $$ = $2; }
+ | DESTROY destroy_cmd close_scope_destroy { $$ = $2; }
;
add_cmd : TABLE table_spec
@@ -1328,6 +1418,14 @@ delete_cmd : TABLE table_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, NULL);
}
+ | CHAIN chain_spec chain_block_alloc
+ '{' chain_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ close_scope(state);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, $5);
+ }
| RULE ruleid_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &$2, &@$, NULL);
@@ -1387,6 +1485,74 @@ delete_cmd : TABLE table_or_id_spec
}
;
+destroy_cmd : TABLE table_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | CHAIN chain_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | SET set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | FLOWTABLE flowtable_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtableid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
+ | COUNTER obj_or_id_spec close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
+ | QUOTA obj_or_id_spec close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
+ | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_DESTROY, $2, &$3, &@$, $4);
+ if ($2 == NFT_OBJECT_CT_TIMEOUT)
+ init_list_head(&$4->ct_timeout.timeout_list);
+ }
+ | LIMIT obj_or_id_spec close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ }
+ | SECMARK obj_or_id_spec close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ }
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ }
+ ;
+
+
get_cmd : ELEMENT set_spec set_block_expr
{
$$ = cmd_alloc(CMD_GET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
@@ -1555,8 +1721,13 @@ 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 close_scope_counter
@@ -1571,10 +1742,53 @@ reset_cmd : COUNTERS ruleset_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$3, &@$, NULL);
}
+ | 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
@@ -1693,31 +1907,38 @@ table_block_alloc : /* empty */
}
;
-table_options : FLAGS STRING
+table_options : FLAGS table_flags
{
- if (strcmp($2, "dormant") == 0) {
- $<table>0->flags |= TABLE_F_DORMANT;
- xfree($2);
- } else if (strcmp($2, "owner") == 0) {
- $<table>0->flags |= TABLE_F_OWNER;
- xfree($2);
- } else {
- erec_queue(error(&@2, "unknown table option %s", $2),
- state->msgs);
- xfree($2);
- YYERROR;
- }
+ $<table>0->flags |= $2;
}
| comment_spec
{
if (already_set($<table>0->comment, &@$, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<table>0->comment = $1;
}
;
+table_flags : table_flag
+ | table_flags COMMA table_flag
+ {
+ $$ = $1 | $3;
+ }
+ ;
+table_flag : STRING
+ {
+ $$ = parse_table_flag($1);
+ free_const($1);
+ if ($$ == 0) {
+ erec_queue(error(&@1, "unknown table option %s", $1),
+ state->msgs);
+ YYERROR;
+ }
+ }
+ ;
+
table_block : /* empty */ { $$ = $<table>-1; }
| table_block common_block
| table_block stmt_separator
@@ -1786,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 '}' close_scope_ct stmt_separator
+ | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_HELPER;
@@ -1795,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 '}' close_scope_ct stmt_separator
+ | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_TIMEOUT;
@@ -1804,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 '}' close_scope_ct stmt_separator
+ | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_EXPECT;
@@ -1850,7 +2071,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
chain_block_alloc : /* empty */
{
- $$ = chain_alloc(NULL);
+ $$ = chain_alloc();
if (open_scope(state, &$$->scope) < 0) {
erec_queue(error(&@$, "too many levels of nesting"),
state->msgs);
@@ -1870,10 +2091,19 @@ chain_block : /* empty */ { $$ = $<chain>-1; }
list_add_tail(&$2->list, &$1->rules);
$$ = $1;
}
+ | chain_block DEVICES '=' flowtable_expr stmt_separator
+ {
+ if ($$->dev_expr) {
+ list_splice_init(&$4->expressions, &$$->dev_expr->expressions);
+ expr_free($4);
+ break;
+ }
+ $$->dev_expr = $4;
+ }
| chain_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -1889,7 +2119,7 @@ subchain_block : /* empty */ { $$ = $<chain>-1; }
}
;
-typeof_data_expr : primary_expr
+typeof_verdict_expr : primary_expr
{
struct expr *e = $1;
@@ -1921,6 +2151,17 @@ typeof_data_expr : primary_expr
}
;
+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) {
@@ -1946,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 close_scope_type
+ | set_block typeof_key_expr stmt_separator
{
- $1->key = $3;
- $$ = $1;
- }
- | set_block TYPEOF typeof_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
@@ -1987,6 +2231,10 @@ set_block : /* empty */ { $$ = $<set>-1; }
}
| set_block ELEMENTS '=' set_block_expr
{
+ if (already_set($1->init, &@2, state)) {
+ expr_free($4);
+ YYERROR;
+ }
$1->init = $4;
$$ = $1;
}
@@ -1999,7 +2247,7 @@ set_block : /* empty */ { $$ = $<set>-1; }
| set_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -2026,10 +2274,14 @@ set_flag : CONSTANT { $$ = NFT_SET_CONSTANT; }
map_block_alloc : /* empty */
{
- $$ = set_alloc(NULL);
+ $$ = set_alloc(&internal_location);
}
;
+ct_obj_type_map : TIMEOUT { $$ = NFT_OBJECT_CT_TIMEOUT; }
+ | EXPECTATION { $$ = NFT_OBJECT_CT_EXPECT; }
+ ;
+
map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; }
| QUOTA close_scope_quota { $$ = NFT_OBJECT_QUOTA; }
| LIMIT close_scope_limit { $$ = NFT_OBJECT_LIMIT; }
@@ -2037,6 +2289,14 @@ map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; }
| SYNPROXY close_scope_synproxy { $$ = NFT_OBJECT_SYNPROXY; }
;
+map_block_obj_typeof : map_block_obj_type
+ | CT ct_obj_type_map close_scope_ct { $$ = $2; }
+ ;
+
+map_block_data_interval : INTERVAL { $$ = EXPR_F_INTERVAL; }
+ | { $$ = 0; }
+ ;
+
map_block : /* empty */ { $$ = $<set>-1; }
| map_block common_block
| map_block stmt_separator
@@ -2045,23 +2305,24 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->timeout = $3;
$$ = $1;
}
- | map_block TYPE
- data_type_expr COLON data_type_expr
- stmt_separator close_scope_type
+ | map_block GC_INTERVAL time_spec stmt_separator
{
- $1->key = $3;
- $1->data = $5;
-
- $1->flags |= NFT_SET_MAP;
+ $1->gc_int = $3;
$$ = $1;
}
| map_block TYPE
- data_type_expr COLON INTERVAL data_type_expr
+ data_type_expr COLON map_block_data_interval data_type_expr
stmt_separator close_scope_type
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ expr_free($6);
+ YYERROR;
+ }
+
$1->key = $3;
$1->data = $6;
- $1->data->flags |= EXPR_F_INTERVAL;
+ $1->data->flags |= $5;
$1->flags |= NFT_SET_MAP;
$$ = $1;
@@ -2070,29 +2331,43 @@ map_block : /* empty */ { $$ = $<set>-1; }
typeof_expr COLON typeof_data_expr
stmt_separator
{
- $1->key = $3;
- datatype_set($1->key, $3->dtype);
- $1->data = $5;
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ expr_free($5);
+ YYERROR;
+ }
- $1->flags |= NFT_SET_MAP;
- $$ = $1;
- }
- | map_block TYPEOF
- typeof_expr COLON INTERVAL typeof_expr
- stmt_separator
- {
$1->key = $3;
- datatype_set($1->key, $3->dtype);
- $1->data = $6;
- $1->data->flags |= EXPR_F_INTERVAL;
- $1->flags |= NFT_SET_MAP;
+ if ($5->etype == EXPR_CT && $5->ct.key == NFT_CT_HELPER) {
+ $1->objtype = NFT_OBJECT_CT_HELPER;
+ $1->flags |= NFT_SET_OBJECT;
+ expr_free($5);
+ } else {
+ $1->data = $5;
+ $1->flags |= NFT_SET_MAP;
+ }
+
$$ = $1;
}
| map_block TYPE
data_type_expr COLON map_block_obj_type
stmt_separator close_scope_type
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ YYERROR;
+ }
+
+ $1->key = $3;
+ $1->objtype = $5;
+ $1->flags |= NFT_SET_OBJECT;
+ $$ = $1;
+ }
+ | map_block TYPEOF
+ typeof_expr COLON map_block_obj_typeof
+ stmt_separator
+ {
$1->key = $3;
$1->objtype = $5;
$1->flags |= NFT_SET_OBJECT;
@@ -2117,7 +2392,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
| map_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -2142,7 +2417,7 @@ set_policy_spec : PERFORMANCE { $$ = NFT_SET_POL_PERFORMANCE; }
flowtable_block_alloc : /* empty */
{
- $$ = flowtable_alloc(NULL);
+ $$ = flowtable_alloc(&internal_location);
}
;
@@ -2156,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;
}
@@ -2204,17 +2479,21 @@ flowtable_list_expr : flowtable_expr_member
flowtable_expr_member : QUOTED_STRING
{
- $$ = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen($1) * BITS_PER_BYTE, $1);
- xfree($1);
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
}
| STRING
{
- $$ = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen($1) * BITS_PER_BYTE, $1);
- xfree($1);
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
}
| variable_expr
{
@@ -2229,12 +2508,12 @@ data_type_atom_expr : type_identifier
if (dtype == NULL) {
erec_queue(error(&@1, "unknown datatype %s", $1),
state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
$$ = constant_expr_alloc(&@1, dtype, dtype->byteorder,
dtype->size, NULL);
- xfree($1);
+ free_const($1);
}
| TIME
{
@@ -2257,7 +2536,7 @@ data_type_expr : data_type_atom_expr
obj_block_alloc : /* empty */
{
- $$ = obj_alloc(NULL);
+ $$ = obj_alloc(&internal_location);
}
;
@@ -2271,7 +2550,7 @@ counter_block : /* empty */ { $$ = $<obj>-1; }
| counter_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2288,7 +2567,7 @@ quota_block : /* empty */ { $$ = $<obj>-1; }
| quota_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2305,7 +2584,7 @@ ct_helper_block : /* empty */ { $$ = $<obj>-1; }
| ct_helper_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2316,6 +2595,7 @@ ct_timeout_block : /*empty */
{
$$ = $<obj>-1;
init_list_head(&$$->ct_timeout.timeout_list);
+ $$->type = NFT_OBJECT_CT_TIMEOUT;
}
| ct_timeout_block common_block
| ct_timeout_block stmt_separator
@@ -2326,7 +2606,7 @@ ct_timeout_block : /*empty */
| ct_timeout_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2343,7 +2623,7 @@ ct_expect_block : /*empty */ { $$ = $<obj>-1; }
| ct_expect_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2360,7 +2640,7 @@ limit_block : /* empty */ { $$ = $<obj>-1; }
| limit_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2377,7 +2657,7 @@ secmark_block : /* empty */ { $$ = $<obj>-1; }
| secmark_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2394,7 +2674,7 @@ synproxy_block : /* empty */ { $$ = $<obj>-1; }
| synproxy_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2415,12 +2695,15 @@ hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
if (chain_type == NULL) {
erec_queue(error(&@3, "unknown chain type"),
state->msgs);
- xfree($3);
+ free_const($3);
+ free_const($5);
+ expr_free($6);
+ expr_free($7.expr);
YYERROR;
}
$<chain>0->type.loc = @3;
$<chain>0->type.str = xstrdup(chain_type);
- xfree($3);
+ free_const($3);
$<chain>0->loc = @$;
$<chain>0->hook.loc = @5;
@@ -2428,10 +2711,12 @@ hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
if ($<chain>0->hook.name == NULL) {
erec_queue(error(&@5, "unknown chain hook"),
state->msgs);
- xfree($5);
+ free_const($5);
+ expr_free($6);
+ expr_free($7.expr);
YYERROR;
}
- xfree($5);
+ free_const($5);
$<chain>0->dev_expr = $6;
$<chain>0->priority = $7;
@@ -2478,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
@@ -2491,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
@@ -2504,7 +2789,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen(str) * BITS_PER_BYTE,
str);
- xfree($1);
+ free_const($1);
$$ = spec;
}
;
@@ -2515,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);
@@ -2576,6 +2860,7 @@ chain_policy : ACCEPT { $$ = NF_ACCEPT; }
;
identifier : STRING
+ | LAST { $$ = xstrdup("last"); }
;
string : STRING
@@ -2589,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;
@@ -2598,6 +2883,11 @@ time_spec : STRING
}
;
+/* compatibility kludge to allow either 60, 60s, 1m, ... */
+time_spec_or_num_s : NUM
+ | time_spec { $$ = $1 / 1000u; }
+ ;
+
family_spec : /* empty */ { $$ = NFPROTO_IPV4; }
| family_spec_explicit
;
@@ -2785,7 +3075,7 @@ comment_spec : COMMENT string
erec_queue(error(&@2, "comment too long, %d characters maximum allowed",
NFTNL_UDATA_COMMENT_MAXLEN),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
$$ = $2;
@@ -2822,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);
}
;
@@ -2852,10 +3142,65 @@ stateful_stmt_list : stateful_stmt
}
;
+objref_stmt_counter : COUNTER NAME stmt_expr close_scope_counter
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_COUNTER;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_limit : LIMIT NAME stmt_expr close_scope_limit
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_LIMIT;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_quota : QUOTA NAME stmt_expr close_scope_quota
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_QUOTA;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_synproxy : SYNPROXY NAME stmt_expr close_scope_synproxy
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_SYNPROXY;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_ct : CT TIMEOUT SET stmt_expr close_scope_ct
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
+ $$->objref.expr = $4;
+
+ }
+ | CT EXPECTATION SET stmt_expr close_scope_ct
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_EXPECT;
+ $$->objref.expr = $4;
+ }
+ ;
+
+objref_stmt : objref_stmt_counter
+ | objref_stmt_limit
+ | objref_stmt_quota
+ | objref_stmt_synproxy
+ | objref_stmt_ct
+ ;
+
stateful_stmt : counter_stmt close_scope_counter
| limit_stmt
| quota_stmt
| connlimit_stmt
+ | last_stmt close_scope_last
;
stmt : verdict_stmt
@@ -2879,6 +3224,19 @@ stmt : verdict_stmt
| synproxy_stmt close_scope_synproxy
| chain_stmt
| optstrip_stmt
+ | xt_stmt close_scope_xt
+ | objref_stmt
+ ;
+
+xt_stmt : XT STRING string
+ {
+ $$ = NULL;
+ free_const($2);
+ free_const($3);
+ erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"),
+ state->msgs);
+ YYERROR;
+ }
;
chain_stmt_type : JUMP { $$ = NFT_JUMP; }
@@ -2957,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
@@ -2974,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
;
@@ -3016,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;
@@ -3090,7 +3460,7 @@ log_arg : PREFIX string
state->msgs);
}
expr_free(expr);
- xfree($2);
+ free_const($2);
YYERROR;
}
item = variable_expr_alloc(&@$, scope, sym);
@@ -3120,7 +3490,7 @@ log_arg : PREFIX string
}
}
- xfree($2);
+ free_const($2);
$<stmt>0->log.prefix = expr;
$<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
@@ -3173,10 +3543,10 @@ level_type : string
else {
erec_queue(error(&@1, "invalid log level"),
state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
@@ -3242,12 +3612,6 @@ limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope
$$->limit.type = NFT_LIMIT_PKT_BYTES;
$$->limit.flags = $3;
}
- | LIMIT NAME stmt_expr close_scope_limit
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_LIMIT;
- $$->objref.expr = $3;
- }
;
quota_mode : OVER { $$ = NFT_QUOTA_F_INV; }
@@ -3266,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;
@@ -3281,7 +3645,7 @@ quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
uint64_t rate;
erec = data_unit_parse(&@$, $4, &rate);
- xfree($4);
+ free_const($4);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3291,12 +3655,6 @@ quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
$$->quota.used = $5;
$$->quota.flags = $2;
}
- | QUOTA NAME stmt_expr close_scope_quota
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_QUOTA;
- $$->objref.expr = $3;
- }
;
limit_mode : OVER { $$ = NFT_LIMIT_F_INV; }
@@ -3325,7 +3683,7 @@ limit_rate_bytes : NUM STRING
uint64_t rate, unit;
erec = rate_parse(&@$, $2, &rate, &unit);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3347,7 +3705,7 @@ limit_bytes : NUM BYTES { $$ = $1; }
uint64_t rate;
erec = data_unit_parse(&@$, $2, &rate);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3376,7 +3734,7 @@ reject_with_expr : STRING
{
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state), $1);
- xfree($1);
+ free_const($1);
}
| integer_expr { $$ = $1; }
;
@@ -3391,40 +3749,40 @@ reject_opts : /* empty */
$<stmt>0->reject.family = NFPROTO_IPV4;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmp_code_type);
}
| WITH ICMP reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV4;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmp_code_type);
}
| WITH ICMP6 TYPE reject_with_expr close_scope_type close_scope_icmp
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type);
}
| WITH ICMP6 reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type);
}
| WITH ICMPX TYPE reject_with_expr close_scope_type
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type);
}
| WITH ICMPX reject_with_expr
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type);
}
| WITH TCP close_scope_tcp RESET close_scope_reset
{
@@ -3435,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
@@ -3487,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
@@ -4040,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);
}
;
@@ -4055,7 +4407,7 @@ symbol_expr : variable_expr
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state),
$1);
- xfree($1);
+ free_const($1);
}
;
@@ -4068,7 +4420,7 @@ set_ref_symbol_expr : AT identifier close_scope_at
$$ = symbol_expr_alloc(&@$, SYMBOL_SET,
current_scope(state),
$2);
- xfree($2);
+ free_const($2);
}
;
@@ -4165,10 +4517,10 @@ osf_ttl : /* empty */
else {
erec_queue(error(&@2, "invalid ttl option"),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
;
@@ -4296,6 +4648,12 @@ meter_key_expr_alloc : concat_expr
set_elem_expr : set_elem_expr_alloc
| set_elem_expr_alloc set_elem_expr_options
+ | set_elem_expr_alloc set_elem_expr_options set_elem_stmt_list
+ {
+ $$ = $1;
+ list_splice_tail($3, &$$->stmt_list);
+ free($3);
+ }
;
set_elem_key_expr : set_lhs_expr { $$ = $1; }
@@ -4306,7 +4664,7 @@ set_elem_expr_alloc : set_elem_key_expr set_elem_stmt_list
{
$$ = set_elem_expr_alloc(&@1, $1);
list_splice_tail($2, &$$->stmt_list);
- xfree($2);
+ free($2);
}
| set_elem_key_expr
{
@@ -4332,7 +4690,7 @@ set_elem_option : TIMEOUT time_spec
| comment_spec
{
if (already_set($<expr>0->comment, &@1, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<expr>0->comment = $1;
@@ -4408,6 +4766,32 @@ set_elem_stmt : COUNTER close_scope_counter
$$->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;
+ }
;
set_elem_expr_option : TIMEOUT time_spec
@@ -4421,7 +4805,7 @@ set_elem_expr_option : TIMEOUT time_spec
| comment_spec
{
if (already_set($<expr>0->comment, &@1, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<expr>0->comment = $1;
@@ -4473,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;
@@ -4502,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);
}
;
@@ -4522,27 +4906,34 @@ ct_obj_type : HELPER { $$ = NFT_OBJECT_CT_HELPER; }
;
ct_cmd_type : HELPERS { $$ = CMD_OBJ_CT_HELPERS; }
- | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUT; }
- | EXPECTATION { $$ = CMD_OBJ_CT_EXPECT; }
+ | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUTS; }
+ | EXPECTATION { $$ = CMD_OBJ_CT_EXPECTATIONS; }
;
ct_l4protoname : TCP close_scope_tcp { $$ = IPPROTO_TCP; }
| UDP close_scope_udp { $$ = IPPROTO_UDP; }
;
-ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type
+ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type
{
struct ct_helper *ct;
int ret;
ct = &$<obj>0->ct_helper;
+ if (ct->l4proto) {
+ erec_queue(error(&@2, "You can only specify this once. This statement is already set for %s.", ct->name), state->msgs);
+ free_const($2);
+ YYERROR;
+ }
+
ret = snprintf(ct->name, sizeof(ct->name), "%s", $2);
if (ret <= 0 || ret >= (int)sizeof(ct->name)) {
erec_queue(error(&@2, "invalid name '%s', max length is %u\n", $2, (int)sizeof(ct->name)), state->msgs);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
ct->l4proto = $4;
}
@@ -4556,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;
@@ -4575,7 +4965,7 @@ timeout_state : STRING COLON NUM
ts->timeout_value = $3;
ts->location = @1;
init_list_head(&ts->head);
- $$ = &ts->head;
+ $$ = ts;
}
;
@@ -4593,7 +4983,7 @@ ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator
ct = &$<obj>0->ct_timeout;
list_splice_tail($4, &ct->timeout_list);
- xfree($4);
+ free($4);
}
| L3PROTOCOL family_spec_explicit stmt_separator
{
@@ -4791,9 +5181,11 @@ keyword_expr : ETHER close_scope_eth { $$ = symbol_value(&@$, "ether"); }
| SNAT close_scope_nat { $$ = symbol_value(&@$, "snat"); }
| ECN { $$ = symbol_value(&@$, "ecn"); }
| RESET close_scope_reset { $$ = symbol_value(&@$, "reset"); }
+ | DESTROY close_scope_destroy { $$ = symbol_value(&@$, "destroy"); }
| ORIGINAL { $$ = symbol_value(&@$, "original"); }
| REPLY { $$ = symbol_value(&@$, "reply"); }
| LABEL { $$ = symbol_value(&@$, "label"); }
+ | LAST close_scope_last { $$ = symbol_value(&@$, "last"); }
;
primary_rhs_expr : symbol_expr { $$ = $1; }
@@ -4856,6 +5248,13 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
+ | GRE close_scope_gre
+ {
+ uint8_t data = IPPROTO_GRE;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
| COMP close_scope_comp
{
uint8_t data = IPPROTO_COMP;
@@ -4929,7 +5328,7 @@ chain_expr : variable_expr
BYTEORDER_HOST_ENDIAN,
strlen($1) * BITS_PER_BYTE,
$1);
- xfree($1);
+ free_const($1);
}
;
@@ -4947,7 +5346,7 @@ meta_expr : META meta_key close_scope_meta
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -5024,9 +5423,10 @@ meta_stmt : META meta_key SET stmt_expr close_scope_meta
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
+ expr_free($4);
YYERROR;
}
@@ -5265,19 +5665,6 @@ ct_stmt : CT ct_key SET stmt_expr close_scope_ct
break;
}
}
- | CT TIMEOUT SET stmt_expr close_scope_ct
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
- $$->objref.expr = $4;
-
- }
- | CT EXPECTATION SET stmt_expr close_scope_ct
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_CT_EXPECT;
- $$->objref.expr = $4;
- }
| CT ct_dir ct_key_dir_optional SET stmt_expr close_scope_ct
{
$$ = ct_stmt_alloc(&@$, $3, $2, $5);
@@ -5311,9 +5698,31 @@ payload_expr : payload_raw_expr
| dccp_hdr_expr
| sctp_hdr_expr
| th_hdr_expr
+ | vxlan_hdr_expr
+ | geneve_hdr_expr
+ | gre_hdr_expr
+ | gretap_hdr_expr
;
-payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM close_scope_at
+payload_raw_len : NUM
+ {
+ if ($1 > NFT_MAX_EXPR_LEN_BITS) {
+ erec_queue(error(&@1, "raw payload length %u exceeds upper limit of %u",
+ $1, NFT_MAX_EXPR_LEN_BITS),
+ state->msgs);
+ YYERROR;
+ }
+
+ if ($1 == 0) {
+ erec_queue(error(&@1, "raw payload length cannot be 0"), state->msgs);
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+payload_raw_expr : AT payload_base_spec COMMA NUM COMMA payload_raw_len close_scope_at
{
$$ = payload_expr_alloc(&@$, NULL, 0);
payload_init_raw($$, $2, $4, $6);
@@ -5331,10 +5740,10 @@ payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; }
$$ = PROTO_BASE_INNER_HDR;
} else {
erec_queue(error(&@1, "unknown raw payload base"), state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
@@ -5482,6 +5891,8 @@ icmp6_hdr_field : TYPE close_scope_type { $$ = ICMP6HDR_TYPE; }
| ID { $$ = ICMP6HDR_ID; }
| SEQUENCE { $$ = ICMP6HDR_SEQ; }
| MAXDELAY { $$ = ICMP6HDR_MAXDELAY; }
+ | TADDR { $$ = ICMP6HDR_TADDR; }
+ | DADDR { $$ = ICMP6HDR_DADDR; }
;
auth_hdr_expr : AH auth_hdr_field close_scope_ah
@@ -5554,14 +5965,106 @@ tcp_hdr_expr : TCP tcp_hdr_field
| TCP OPTION tcp_hdr_option_kind_and_field
{
$$ = tcpopt_expr_alloc(&@$, $3.kind, $3.field);
+ if ($$ == NULL) {
+ erec_queue(error(&@1, "Could not find a tcp option template"), state->msgs);
+ YYERROR;
+ }
}
- | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA NUM
+ | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA payload_raw_len
{
$$ = tcpopt_expr_alloc(&@$, $5, 0);
tcpopt_init_raw($$, $5, $7, $9, 0);
}
;
+inner_inet_expr : ip_hdr_expr
+ | icmp_hdr_expr
+ | igmp_hdr_expr
+ | ip6_hdr_expr
+ | icmp6_hdr_expr
+ | auth_hdr_expr
+ | esp_hdr_expr
+ | comp_hdr_expr
+ | udp_hdr_expr
+ | udplite_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
+ | dccp_hdr_expr
+ | sctp_hdr_expr
+ | th_hdr_expr
+ ;
+
+inner_eth_expr : eth_hdr_expr
+ | vlan_hdr_expr
+ | arp_hdr_expr
+ ;
+
+inner_expr : inner_eth_expr
+ | inner_inet_expr
+ ;
+
+vxlan_hdr_expr : VXLAN vxlan_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_vxlan, $2);
+ expr->payload.inner_desc = &proto_vxlan;
+ $$ = expr;
+ }
+ | VXLAN inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_vxlan;
+ }
+ ;
+
+vxlan_hdr_field : VNI { $$ = VXLANHDR_VNI; }
+ | FLAGS { $$ = VXLANHDR_FLAGS; }
+ ;
+
+geneve_hdr_expr : GENEVE geneve_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_geneve, $2);
+ expr->payload.inner_desc = &proto_geneve;
+ $$ = expr;
+ }
+ | GENEVE inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_geneve;
+ }
+ ;
+
+geneve_hdr_field : VNI { $$ = GNVHDR_VNI; }
+ | TYPE { $$ = GNVHDR_TYPE; }
+ ;
+
+gre_hdr_expr : GRE gre_hdr_field close_scope_gre
+ {
+ $$ = payload_expr_alloc(&@$, &proto_gre, $2);
+ }
+ | GRE close_scope_gre inner_inet_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gre;
+ }
+ ;
+
+gre_hdr_field : HDRVERSION { $$ = GREHDR_VERSION; }
+ | FLAGS { $$ = GREHDR_FLAGS; }
+ | PROTOCOL { $$ = GREHDR_PROTOCOL; }
+ ;
+
+gretap_hdr_expr : GRETAP close_scope_gre inner_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gretap;
+ }
+ ;
+
optstrip_stmt : RESET TCP OPTION tcp_hdr_option_type close_scope_tcp
{
$$ = optstrip_stmt_alloc(&@$, tcpopt_expr_alloc(&@$,
@@ -5661,6 +6164,15 @@ dccp_hdr_expr : DCCP dccp_hdr_field close_scope_dccp
{
$$ = payload_expr_alloc(&@$, &proto_dccp, $2);
}
+ | DCCP OPTION NUM close_scope_dccp
+ {
+ if ($3 > DCCPOPT_TYPE_MAX) {
+ erec_queue(error(&@1, "value too large"),
+ state->msgs);
+ YYERROR;
+ }
+ $$ = dccpopt_expr_alloc(&@$, $3);
+ }
;
dccp_hdr_field : SPORT { $$ = DCCPHDR_SPORT; }
@@ -5909,7 +6421,7 @@ exthdr_exists_expr : EXTHDR exthdr_key
desc = exthdr_find_proto($2);
/* Assume that NEXTHDR template is always
- * the fist one in list of templates.
+ * the first one in list of templates.
*/
$$ = exthdr_expr_alloc(&@$, desc, 1);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
diff --git a/src/parser_json.c b/src/parser_json.c
index 76c268f8..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>
@@ -533,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;
@@ -546,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;
@@ -564,11 +592,45 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
json_error(ctx, "Invalid payload base '%s'.", base);
return NULL;
}
+
+ if (len <= 0 || len > (int)NFT_MAX_EXPR_LEN_BITS) {
+ json_error(ctx, "Payload length must be between 0 and %lu, got %d",
+ NFT_MAX_EXPR_LEN_BITS, len);
+ return NULL;
+ }
+
expr = payload_expr_alloc(int_loc, NULL, 0);
payload_init_raw(expr, val, offset, len);
expr->byteorder = BYTEORDER_BIG_ENDIAN;
expr->payload.is_raw = true;
return expr;
+ } else if (!json_unpack(root, "{s:s, s:s, s:s}",
+ "tunnel", &tunnel, "protocol", &protocol, "field", &field)) {
+ const struct proto_desc *proto = proto_lookup_byname(protocol);
+ const struct proto_desc *inner_proto = inner_proto_lookup_byname(tunnel);
+
+ if (!inner_proto) {
+ json_error(ctx, "Unknown payload tunnel protocol '%s'.",
+ tunnel);
+ return NULL;
+ }
+ if (!proto) {
+ json_error(ctx, "Unknown payload protocol '%s'.",
+ protocol);
+ return NULL;
+ }
+ if (json_parse_payload_field(proto, field, &val)) {
+ json_error(ctx, "Unknown %s field '%s'.",
+ protocol, field);
+ return NULL;
+ }
+ expr = payload_expr_alloc(int_loc, proto, val);
+ expr->payload.inner_desc = inner_proto;
+
+ if (proto == &proto_th)
+ expr->payload.is_raw = true;
+
+ return expr;
} else if (!json_unpack(root, "{s:s, s:s}",
"protocol", &protocol, "field", &field)) {
const struct proto_desc *proto = proto_lookup_byname(protocol);
@@ -602,12 +664,18 @@ static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx,
struct expr *expr;
if (!json_unpack(root, "{s:i, s:i, s:i}",
- "base", &kind, "offset", &offset, "len", &len)) {
+ "base", &kind, "offset", &offset, "len", &len)) {
uint32_t flag = 0;
if (kind < 0 || kind > 255)
return NULL;
+ if (len < 0 || len > (int)NFT_MAX_EXPR_LEN_BITS) {
+ json_error(ctx, "option length must be between 0 and %lu, got %d",
+ NFT_MAX_EXPR_LEN_BITS, len);
+ return NULL;
+ }
+
expr = tcpopt_expr_alloc(int_loc, kind,
TCPOPT_COMMON_KIND);
@@ -673,7 +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;
@@ -689,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);
+ IPOPT_FIELD_TYPE);
expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
return expr;
@@ -748,6 +816,22 @@ static struct expr *json_parse_sctp_chunk_expr(struct json_ctx *ctx,
return sctp_chunk_expr_alloc(int_loc, desc->type, fieldval);
}
+static struct expr *json_parse_dccp_option_expr(struct json_ctx *ctx,
+ const char *type, json_t *root)
+{
+ int opt_type;
+
+ if (json_unpack_err(ctx, root, "{s:i}", "type", &opt_type))
+ return NULL;
+
+ if (opt_type < DCCPOPT_TYPE_MIN || opt_type > DCCPOPT_TYPE_MAX) {
+ json_error(ctx, "Unknown dccp option type '%d'.", opt_type);
+ return NULL;
+ }
+
+ return dccpopt_expr_alloc(int_loc, opt_type);
+}
+
static const struct exthdr_desc *exthdr_lookup_byname(const char *name)
{
const struct exthdr_desc *exthdr_tbl[] = {
@@ -1076,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;
}
@@ -1120,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;
@@ -1454,6 +1550,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
{ "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
{ "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
{ "sctp chunk", json_parse_sctp_chunk_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
+ { "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 },
@@ -1652,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);
@@ -1678,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;
@@ -1687,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;
@@ -1706,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)
{
@@ -1719,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);
@@ -1779,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;
@@ -1929,7 +2052,7 @@ static struct stmt *json_parse_flow_offload_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_notrack_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
return notrack_stmt_alloc(int_loc);
}
@@ -1967,7 +2090,7 @@ static struct stmt *json_parse_dup_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
struct stmt *stmt;
@@ -2039,7 +2162,7 @@ static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root)
}
static int json_parse_nat_type_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+ json_t *root, int *flags)
{
const struct {
const char *flag;
@@ -2154,7 +2277,6 @@ 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);
@@ -2169,7 +2291,7 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx,
}
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;
@@ -2205,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;
@@ -2221,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;
}
}
@@ -2248,8 +2370,8 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
}
static void json_parse_set_stmt_list(struct json_ctx *ctx,
- struct list_head *stmt_list,
- json_t *stmt_json)
+ struct list_head *stmt_list,
+ json_t *stmt_json)
{
struct list_head *head;
struct stmt *tmp;
@@ -2271,7 +2393,7 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx,
}
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;
@@ -2318,6 +2440,63 @@ static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
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;
+}
+
static int json_parse_log_flag(struct json_ctx *ctx,
json_t *root, int *flags)
{
@@ -2554,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);
@@ -2589,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))
@@ -2704,7 +2883,14 @@ static struct stmt *json_parse_optstrip_stmt(struct json_ctx *ctx,
{
struct expr *expr = json_parse_expr(ctx, value);
- return expr ? optstrip_stmt_alloc(int_loc, expr) : NULL;
+ if (!expr ||
+ expr->etype != EXPR_EXTHDR ||
+ expr->exthdr.op != NFT_EXTHDR_OP_TCPOPT) {
+ json_error(ctx, "Illegal TCP optstrip argument");
+ return NULL;
+ }
+
+ return optstrip_stmt_alloc(int_loc, expr);
}
static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
@@ -2723,6 +2909,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "counter", json_parse_counter_stmt },
{ "mangle", json_parse_mangle_stmt },
{ "quota", json_parse_quota_stmt },
+ { "last", json_parse_last_stmt },
{ "limit", json_parse_limit_stmt },
{ "flow", json_parse_flow_offload_stmt },
{ "fwd", json_parse_fwd_stmt },
@@ -2734,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 },
@@ -2764,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);
@@ -2773,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)) {
@@ -2800,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)
@@ -2822,23 +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, *name;
- struct chain *chain;
+ const char *family = "", *policy = "", *type, *hookstr, *comment = NULL;
+ struct chain *chain = NULL;
+ json_t *devs = NULL;
int prio;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
"table", &h.table.name))
return NULL;
- if (op != CMD_DELETE &&
- json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name)) {
- return NULL;
+ if (op != CMD_DELETE) {
+ if (json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name))
+ return NULL;
+
+ json_unpack(root, "{s:s}", "comment", &comment);
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.chain.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -2853,14 +3138,22 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
if (h.chain.name)
h.chain.name = xstrdup(h.chain.name);
+ if (comment) {
+ chain = chain_alloc();
+ handle_merge(&chain->handle, &h);
+ chain->comment = xstrdup(comment);
+ }
+
if (op == CMD_DELETE ||
op == CMD_LIST ||
op == CMD_FLUSH ||
json_unpack(root, "{s:s, s:s, s:i}",
"type", &type, "hook", &hookstr, "prio", &prio))
- return cmd_alloc(op, obj, &h, int_loc, NULL);
+ return cmd_alloc(op, obj, &h, int_loc, chain);
+
+ if (!chain)
+ chain = chain_alloc();
- chain = chain_alloc(NULL);
chain->flags |= CHAIN_F_BASECHAIN;
chain->type.str = xstrdup(type);
chain->priority.expr = constant_expr_alloc(int_loc, &integer_type,
@@ -2874,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)) {
@@ -2923,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;
@@ -2934,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)) {
@@ -2982,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;
}
@@ -3005,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;
@@ -3019,7 +3316,7 @@ 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;
@@ -3030,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.");
@@ -3047,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.");
@@ -3070,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;
@@ -3131,6 +3430,7 @@ 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);
@@ -3175,41 +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 = 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 flowtable dev at index %zu.",
- index);
- expr_free(expr);
- return NULL;
- }
- tmp = constant_expr_alloc(int_loc, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(dev) * BITS_PER_BYTE, dev);
- compound_expr_add(expr, tmp);
- }
- return expr;
-}
-
static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
json_t *root, enum cmd_ops op,
enum cmd_obj cmd_obj)
@@ -3228,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.");
@@ -3243,17 +3508,17 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
if (h.flowtable.name)
h.flowtable.name = xstrdup(h.flowtable.name);
- if (op == CMD_DELETE || op == CMD_LIST)
+ if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY)
return cmd_alloc(op, cmd_obj, &h, int_loc, NULL);
- if (json_unpack_err(ctx, root, "{s:s, s:I}",
+ if (json_unpack_err(ctx, root, "{s:s, s:i}",
"hook", &hook,
"prio", &prio)) {
handle_free(&h);
return NULL;
}
- json_unpack(root, "{s:o}", &devs);
+ json_unpack(root, "{s:o}", "dev", &devs);
hookstr = chain_hookname_lookup(hook);
if (!hookstr) {
@@ -3270,7 +3535,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
sizeof(int) * BITS_PER_BYTE, &prio);
if (devs) {
- flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs);
+ flowtable->dev_expr = json_parse_devs(ctx, devs);
if (!flowtable->dev_expr) {
json_error(ctx, "Invalid flowtable dev.");
flowtable_free(flowtable);
@@ -3287,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)) {
@@ -3319,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;
@@ -3332,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)) {
@@ -3348,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));
@@ -3442,7 +3707,7 @@ 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)) {
@@ -3471,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;
@@ -3487,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")) {
@@ -3505,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)) {
@@ -3553,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;
@@ -3750,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)
{
@@ -3763,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;
@@ -3861,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 }
diff --git a/src/payload.c b/src/payload.c
index 101bfbda..44aa834c 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -10,11 +10,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
@@ -47,6 +46,10 @@ static void payload_expr_print(const struct expr *expr, struct output_ctx *octx)
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
+ if (expr->payload.inner_desc &&
+ expr->payload.inner_desc != expr->payload.desc)
+ nft_print(octx, "%s ", expr->payload.inner_desc->name);
+
desc = expr->payload.desc;
tmpl = expr->payload.tmpl;
if (payload_is_known(expr))
@@ -67,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;
@@ -114,11 +118,10 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
assert(desc->base <= PROTO_BASE_MAX);
if (desc->base == base->base) {
- assert(base->length > 0);
-
if (!left->payload.is_raw) {
if (desc->base == PROTO_BASE_LL_HDR &&
ctx->stacked_ll_count < PROTO_CTX_NUM_PROTOS) {
+ assert(base->length > 0);
ctx->stacked_ll[ctx->stacked_ll_count] = base;
ctx->stacked_ll_count++;
}
@@ -132,7 +135,8 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
#define NFTNL_UDATA_SET_KEY_PAYLOAD_BASE 2
#define NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET 3
#define NFTNL_UDATA_SET_KEY_PAYLOAD_LEN 4
-#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 5
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC 5
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 6
static unsigned int expr_payload_type(const struct proto_desc *desc,
const struct proto_hdr_template *tmpl)
@@ -162,10 +166,15 @@ static int payload_expr_build_udata(struct nftnl_udata_buf *udbuf,
if (expr->dtype->type == TYPE_INTEGER)
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_LEN, expr->len);
+ if (expr->payload.inner_desc) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC,
+ expr->payload.inner_desc->id);
+ }
+
return 0;
}
-static const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud)
+const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud)
{
return proto_find_desc(nftnl_udata_get_u32(ud));
}
@@ -182,6 +191,7 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data)
case NFTNL_UDATA_SET_KEY_PAYLOAD_BASE:
case NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET:
case NFTNL_UDATA_SET_KEY_PAYLOAD_LEN:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -238,11 +248,15 @@ static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr)
expr->payload.offset = offset;
expr->payload.is_raw = true;
expr->len = len;
- dtype = dtype_clone(&xinteger_type);
+ dtype = datatype_clone(&xinteger_type);
dtype->size = len;
dtype->byteorder = BYTEORDER_BIG_ENDIAN;
- dtype->refcnt = 1;
- expr->dtype = dtype;
+ __datatype_set(expr, dtype);
+ }
+
+ if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]) {
+ desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]);
+ expr->payload.inner_desc = desc;
}
return expr;
@@ -391,9 +405,11 @@ static int payload_add_dependency(struct eval_ctx *ctx,
{
const struct proto_hdr_template *tmpl;
struct expr *dep, *left, *right;
+ struct proto_ctx *pctx;
struct stmt *stmt;
- int protocol = proto_find_num(desc, upper);
+ int protocol;
+ protocol = proto_find_num(desc, upper);
if (protocol < 0)
return expr_error(ctx->msgs, expr,
"conflicting protocols specified: %s vs. %s",
@@ -410,20 +426,28 @@ static int payload_add_dependency(struct eval_ctx *ctx,
constant_data_ptr(protocol, tmpl->len));
dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+
stmt = expr_stmt_alloc(&dep->location, dep);
- if (stmt_evaluate(ctx, stmt) < 0) {
- return expr_error(ctx->msgs, expr,
- "dependency statement is invalid");
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
+
+ if (ctx->inner_desc) {
+ if (tmpl->meta_key)
+ left->meta.inner_desc = ctx->inner_desc;
+ else
+ left->payload.inner_desc = ctx->inner_desc;
}
- relational_expr_pctx_update(&ctx->pctx, dep);
+
+ pctx = eval_proto_ctx(ctx);
+ relational_expr_pctx_update(pctx, dep);
*res = stmt;
return 0;
}
static const struct proto_desc *
-payload_get_get_ll_hdr(const struct eval_ctx *ctx)
+payload_get_get_ll_hdr(const struct proto_ctx *pctx)
{
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_INET:
return &proto_inet;
case NFPROTO_BRIDGE:
@@ -440,9 +464,11 @@ payload_get_get_ll_hdr(const struct eval_ctx *ctx)
static const struct proto_desc *
payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+
switch (expr->payload.base) {
case PROTO_BASE_LL_HDR:
- return payload_get_get_ll_hdr(ctx);
+ return payload_get_get_ll_hdr(pctx);
case PROTO_BASE_TRANSPORT_HDR:
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_icmp6 ||
@@ -450,13 +476,21 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
const struct proto_desc *desc, *desc_upper;
struct stmt *nstmt;
- desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_LL_HDR].desc;
if (!desc) {
- desc = payload_get_get_ll_hdr(ctx);
+ desc = payload_get_get_ll_hdr(pctx);
if (!desc)
break;
}
+ /* this tunnel protocol does not encapsulate an inner
+ * link layer, use proto_netdev which relies on
+ * NFT_META_PROTOCOL for dependencies.
+ */
+ if (expr->payload.inner_desc &&
+ !(expr->payload.inner_desc->inner.flags & NFT_INNER_LL))
+ desc = &proto_netdev;
+
desc_upper = &proto_ip6;
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_igmp)
@@ -502,11 +536,14 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res)
{
- const struct hook_proto_desc *h = &hook_proto_desc[ctx->pctx.family];
+ const struct hook_proto_desc *h;
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
struct stmt *stmt;
uint16_t type;
+ pctx = eval_proto_ctx(ctx);
+ h = &hook_proto_desc[pctx->family];
if (expr->payload.base < h->base) {
if (expr->payload.base < h->base - 1)
return expr_error(ctx->msgs, expr,
@@ -519,15 +556,15 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
"for this family");
stmt = meta_stmt_meta_iiftype(&expr->location, type);
- if (stmt_evaluate(ctx, stmt) < 0) {
- return expr_error(ctx->msgs, expr,
- "dependency statement is invalid");
- }
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
+
*res = stmt;
+
return 0;
}
- desc = ctx->pctx.protocol[expr->payload.base - 1].desc;
+ desc = pctx->protocol[expr->payload.base - 1].desc;
/* Special case for mixed IPv4/IPv6 and bridge tables */
if (desc == NULL)
desc = payload_gen_special_dependency(ctx, expr);
@@ -538,7 +575,7 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
"no %s protocol specified",
proto_base_names[expr->payload.base - 1]);
- if (ctx->pctx.family == NFPROTO_BRIDGE && desc == &proto_eth) {
+ if (pctx->family == NFPROTO_BRIDGE && desc == &proto_eth) {
/* prefer netdev proto, which adds dependencies based
* on skb->protocol.
*
@@ -563,11 +600,13 @@ int exthdr_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
enum proto_bases pb, struct stmt **res)
{
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
- desc = ctx->pctx.protocol[pb].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[pb].desc;
if (desc == NULL) {
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_NETDEV:
case NFPROTO_BRIDGE:
case NFPROTO_INET:
@@ -739,26 +778,58 @@ static uint8_t icmp_dep_to_type(enum icmp_hdr_field_type t)
case PROTO_ICMP6_MTU: return ICMP6_PACKET_TOO_BIG;
case PROTO_ICMP6_MGMQ: return MLD_LISTENER_QUERY;
case PROTO_ICMP6_PPTR: return ICMP6_PARAM_PROB;
+ case PROTO_ICMP6_REDIRECT: return ND_REDIRECT;
+ case PROTO_ICMP6_ADDRESS: return ND_NEIGHBOR_SOLICIT;
}
BUG("Missing icmp type mapping");
}
+static bool 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);
- uint8_t icmp_type;
+ enum icmp_hdr_field_type icmp_dep;
- icmp_type = expr->payload.tmpl->icmp_dep;
- if (icmp_type == PROTO_ICMP_ANY)
+ icmp_dep = expr->payload.tmpl->icmp_dep;
+ if (icmp_dep == PROTO_ICMP_ANY)
return false;
if (dep->left->payload.desc != expr->payload.desc)
return false;
- icmp_type = icmp_dep_to_type(expr->payload.tmpl->icmp_dep);
+ if (expr->payload.tmpl->icmp_dep == PROTO_ICMP_ECHO ||
+ expr->payload.tmpl->icmp_dep == PROTO_ICMP6_ECHO ||
+ expr->payload.tmpl->icmp_dep == PROTO_ICMP6_ADDRESS)
+ return false;
- return ctx->icmp_type == icmp_type;
+ return ctx->icmp_type == icmp_dep_to_type(icmp_dep);
}
static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct expr *expr)
@@ -943,11 +1014,13 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
continue;
if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
- ctx->th_dep.icmp.type != icmp_dep_to_type(tmpl->icmp_dep))
+ !icmp_dep_type_match(tmpl->icmp_dep,
+ ctx->th_dep.icmp.type))
continue;
expr->dtype = tmpl->dtype;
expr->payload.desc = desc;
+ expr->byteorder = tmpl->byteorder;
expr->payload.tmpl = tmpl;
return;
}
@@ -1077,7 +1150,8 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
continue;
if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
- ctx->th_dep.icmp.type != icmp_dep_to_type(tmpl->icmp_dep))
+ !icmp_dep_type_match(tmpl->icmp_dep,
+ ctx->th_dep.icmp.type))
continue;
if (tmpl->len <= expr->len) {
@@ -1100,6 +1174,10 @@ raw:
new = payload_expr_alloc(&expr->location, NULL, 0);
payload_init_raw(new, expr->payload.base, payload_offset,
expr->len);
+
+ if (expr->payload.inner_desc)
+ new->dtype = &integer_type;
+
list_add_tail(&new->list, list);
}
@@ -1121,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;
@@ -1177,6 +1258,8 @@ struct expr *payload_expr_join(const struct expr *e1, const struct expr *e2)
expr->payload.base = e1->payload.base;
expr->payload.offset = e1->payload.offset;
expr->len = e1->len + e2->len;
+ expr->payload.inner_desc = e1->payload.inner_desc;
+
return expr;
}
@@ -1225,9 +1308,42 @@ __payload_gen_icmp_echo_dependency(struct eval_ctx *ctx, const struct expr *expr
return expr_stmt_alloc(&dep->location, dep);
}
+static struct stmt *
+__payload_gen_icmp6_addr_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ const struct proto_desc *desc)
+{
+ static const uint8_t icmp_addr_types[] = {
+ MLD_LISTENER_QUERY,
+ MLD_LISTENER_REPORT,
+ MLD_LISTENER_REDUCTION,
+ ND_NEIGHBOR_SOLICIT,
+ ND_NEIGHBOR_ADVERT,
+ ND_REDIRECT
+ };
+ struct expr *left, *right, *dep, *set;
+ size_t i;
+
+ left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
+
+ set = set_expr_alloc(&expr->location, NULL);
+
+ for (i = 0; i < array_size(icmp_addr_types); ++i) {
+ right = constant_expr_alloc(&expr->location, &icmp6_type_type,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE,
+ constant_data_ptr(icmp_addr_types[i],
+ BITS_PER_BYTE));
+ right = set_elem_expr_alloc(&expr->location, right);
+ compound_expr_add(set, right);
+ }
+
+ dep = relational_expr_alloc(&expr->location, OP_IMPLICIT, left, set);
+ return expr_stmt_alloc(&dep->location, dep);
+}
+
int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
struct stmt *stmt = NULL;
@@ -1244,11 +1360,11 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
break;
case PROTO_ICMP_ECHO:
/* do not test ICMP_ECHOREPLY here: its 0 */
- if (ctx->pctx.th_dep.icmp.type == ICMP_ECHO)
+ if (pctx->th_dep.icmp.type == ICMP_ECHO)
goto done;
type = ICMP_ECHO;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
@@ -1259,21 +1375,21 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
case PROTO_ICMP_MTU:
case PROTO_ICMP_ADDRESS:
type = icmp_dep_to_type(tmpl->icmp_dep);
- if (ctx->pctx.th_dep.icmp.type == type)
+ if (pctx->th_dep.icmp.type == type)
goto done;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
&icmp_type_type,
desc, type);
break;
case PROTO_ICMP6_ECHO:
- if (ctx->pctx.th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
- ctx->pctx.th_dep.icmp.type == ICMP6_ECHO_REPLY)
+ if (pctx->th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
+ pctx->th_dep.icmp.type == ICMP6_ECHO_REPLY)
goto done;
type = ICMP6_ECHO_REQUEST;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
@@ -1282,13 +1398,23 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
&icmp6_type_type,
desc);
break;
+ case PROTO_ICMP6_ADDRESS:
+ if (icmp_dep_type_match(PROTO_ICMP6_ADDRESS,
+ pctx->th_dep.icmp.type))
+ goto done;
+ type = ND_NEIGHBOR_SOLICIT;
+ if (pctx->th_dep.icmp.type)
+ goto bad_proto;
+ stmt = __payload_gen_icmp6_addr_dependency(ctx, expr, desc);
+ break;
+ case PROTO_ICMP6_REDIRECT:
case PROTO_ICMP6_MTU:
case PROTO_ICMP6_MGMQ:
case PROTO_ICMP6_PPTR:
type = icmp_dep_to_type(tmpl->icmp_dep);
- if (ctx->pctx.th_dep.icmp.type == type)
+ if (pctx->th_dep.icmp.type == type)
goto done;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
&icmp6_type_type,
@@ -1299,16 +1425,54 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
BUG("Unhandled icmp dependency code");
}
- ctx->pctx.th_dep.icmp.type = type;
+ pctx->th_dep.icmp.type = type;
- if (stmt_evaluate(ctx, stmt) < 0)
- return expr_error(ctx->msgs, expr,
- "icmp dependency statement is invalid");
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
done:
*res = stmt;
return 0;
bad_proto:
return expr_error(ctx->msgs, expr, "incompatible icmp match: rule has %d, need %u",
- ctx->pctx.th_dep.icmp.type, type);
+ pctx->th_dep.icmp.type, type);
+}
+
+int payload_gen_inner_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ struct stmt **res)
+{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ const struct proto_hdr_template *tmpl;
+ const struct proto_desc *desc, *inner_desc;
+ struct expr *left, *right, *dep;
+ struct stmt *stmt = NULL;
+ int protocol;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ inner_desc = expr->payload.inner_desc;
+ desc = pctx->protocol[inner_desc->base - 1].desc;
+ if (desc == NULL)
+ desc = &proto_ip;
+
+ tmpl = &inner_desc->templates[0];
+ assert(tmpl);
+
+ protocol = proto_find_num(desc, inner_desc);
+ if (protocol < 0)
+ return expr_error(ctx->msgs, expr,
+ "conflicting protocols specified: %s vs. %s",
+ desc->name, inner_desc->name);
+
+ left = meta_expr_alloc(&expr->location, tmpl->meta_key);
+
+ right = constant_expr_alloc(&expr->location, tmpl->dtype,
+ tmpl->dtype->byteorder, tmpl->len,
+ constant_data_ptr(protocol, tmpl->len));
+
+ dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+ stmt = expr_stmt_alloc(&dep->location, dep);
+
+ *res = stmt;
+ return 0;
}
diff --git a/src/print.c b/src/print.c
index d1b25e8b..8aefa961 100644
--- a/src/print.c
+++ b/src/print.c
@@ -2,11 +2,12 @@
* Copyright (c) 2017 Phil Sutter <phil@nwl.cc>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <stdarg.h>
#include <nftables.h>
#include <utils.h>
diff --git a/src/proto.c b/src/proto.c
index c8b3361b..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>
@@ -88,6 +87,30 @@ int proto_find_num(const struct proto_desc *base,
return -1;
}
+static const struct proto_desc *inner_protocols[] = {
+ &proto_vxlan,
+ &proto_geneve,
+ &proto_gre,
+ &proto_gretap,
+};
+
+const struct proto_desc *proto_find_inner(uint32_t type, uint32_t hdrsize,
+ uint32_t flags)
+{
+ const struct proto_desc *desc;
+ unsigned int i;
+
+ for (i = 0; i < array_size(inner_protocols); i++) {
+ desc = inner_protocols[i];
+ if (desc->inner.type == type &&
+ desc->inner.hdrsize == hdrsize &&
+ desc->inner.flags == flags)
+ return inner_protocols[i];
+ }
+
+ return &proto_unknown;
+}
+
static const struct dev_proto_desc dev_proto_desc[] = {
DEV_PROTO_DESC(ARPHRD_ETHER, &proto_eth),
};
@@ -160,7 +183,9 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
pr_debug(" %s", ctx->stacked_ll[i]->name);
}
- pr_debug("update %s protocol context:\n", proto_base_names[base]);
+ pr_debug("update %s protocol context%s:\n",
+ proto_base_names[base], ctx->inner ? " (inner)" : "");
+
for (i = PROTO_BASE_LL_HDR; i <= PROTO_BASE_MAX; i++) {
pr_debug(" %-20s: %s",
proto_base_names[i],
@@ -181,7 +206,7 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
* @debug_mask: display debugging information
*/
void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
- unsigned int debug_mask)
+ unsigned int debug_mask, bool inner)
{
const struct hook_proto_desc *h = &hook_proto_desc[family];
@@ -189,6 +214,7 @@ void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
ctx->family = family;
ctx->protocol[h->base].desc = h->desc;
ctx->debug_mask = debug_mask;
+ ctx->inner = inner;
proto_ctx_debug(ctx, h->base, debug_mask);
}
@@ -228,6 +254,8 @@ void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
ctx->protocol[base].protos[i].location = *loc;
}
break;
+ case PROTO_BASE_INNER_HDR:
+ break;
default:
BUG("unknown protocol base %d", base);
}
@@ -276,6 +304,8 @@ const struct proto_desc *proto_ctx_find_conflict(struct proto_ctx *ctx,
#define HDR_FIELD(__name, __struct, __member) \
HDR_TEMPLATE(__name, &integer_type, __struct, __member)
+#define HDR_HEX_FIELD(__name, __struct, __member) \
+ HDR_TEMPLATE(__name, &xinteger_type, __struct, __member)
#define HDR_BITFIELD(__name, __dtype, __offset, __len) \
PROTO_HDR_TEMPLATE(__name, __dtype, BYTEORDER_BIG_ENDIAN, \
__offset, __len)
@@ -408,10 +438,10 @@ const struct datatype icmp_type_type = {
.sym_tbl = &icmp_type_tbl,
};
-#define ICMP46HDR_FIELD(__token, __struct, __member, __dep) \
+#define ICMP46HDR_FIELD(__token, __dtype, __struct, __member, __dep) \
{ \
.token = (__token), \
- .dtype = &integer_type, \
+ .dtype = &__dtype, \
.byteorder = BYTEORDER_BIG_ENDIAN, \
.offset = offsetof(__struct, __member) * 8, \
.len = field_sizeof(__struct, __member) * 8, \
@@ -419,7 +449,7 @@ const struct datatype icmp_type_type = {
}
#define ICMPHDR_FIELD(__token, __member, __dep) \
- ICMP46HDR_FIELD(__token, struct icmphdr, __member, __dep)
+ ICMP46HDR_FIELD(__token, integer_type, struct icmphdr, __member, __dep)
#define ICMPHDR_TYPE(__name, __type, __member) \
HDR_TYPE(__name, __type, struct icmphdr, __member)
@@ -513,6 +543,10 @@ const struct proto_desc proto_udp = {
[UDPHDR_LENGTH] = UDPHDR_FIELD("length", len),
[UDPHDR_CHECKSUM] = UDPHDR_FIELD("checksum", check),
},
+ .protocols = {
+ PROTO_LINK(0, &proto_vxlan),
+ PROTO_LINK(0, &proto_geneve),
+ },
};
const struct proto_desc proto_udplite = {
@@ -742,6 +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) \
@@ -765,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),
@@ -774,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),
@@ -841,7 +913,7 @@ const struct datatype icmp6_type_type = {
};
#define ICMP6HDR_FIELD(__token, __member, __dep) \
- ICMP46HDR_FIELD(__token, struct icmp6_hdr, __member, __dep)
+ ICMP46HDR_FIELD(__token, integer_type, struct icmp6_hdr, __member, __dep)
#define ICMP6HDR_TYPE(__name, __type, __member) \
HDR_TYPE(__name, __type, struct icmp6_hdr, __member)
@@ -861,6 +933,12 @@ const struct proto_desc proto_icmp6 = {
[ICMP6HDR_ID] = ICMP6HDR_FIELD("id", icmp6_id, PROTO_ICMP6_ECHO),
[ICMP6HDR_SEQ] = ICMP6HDR_FIELD("sequence", icmp6_seq, PROTO_ICMP6_ECHO),
[ICMP6HDR_MAXDELAY] = ICMP6HDR_FIELD("max-delay", icmp6_maxdelay, PROTO_ICMP6_MGMQ),
+ [ICMP6HDR_TADDR] = ICMP46HDR_FIELD("taddr", ip6addr_type,
+ struct nd_neighbor_solicit, nd_ns_target,
+ PROTO_ICMP6_ADDRESS),
+ [ICMP6HDR_DADDR] = ICMP46HDR_FIELD("daddr", ip6addr_type,
+ struct nd_redirect, nd_rd_dst,
+ PROTO_ICMP6_REDIRECT),
},
};
@@ -891,6 +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),
@@ -956,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),
@@ -1136,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 = {
@@ -1153,7 +1286,7 @@ const struct proto_desc proto_netdev = {
},
};
-static const struct proto_desc *proto_definitions[PROTO_DESC_MAX + 1] = {
+static const struct proto_desc *const proto_definitions[PROTO_DESC_MAX + 1] = {
[PROTO_DESC_AH] = &proto_ah,
[PROTO_DESC_ESP] = &proto_esp,
[PROTO_DESC_COMP] = &proto_comp,
@@ -1171,6 +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/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 d1ee6c2e..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>
@@ -27,6 +26,7 @@
#include <cache.h>
#include <owner.h>
#include <intervals.h>
+#include "nftutils.h"
#include <libnftnl/common.h>
#include <libnftnl/ruleset.h>
@@ -76,7 +76,7 @@ static uint32_t udp_dflt_timeout[] = {
[NFTNL_CTTIMEOUT_UDP_REPLIED] = 120,
};
-struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
+struct timeout_protocol timeout_protocol[UINT8_MAX + 1] = {
[IPPROTO_TCP] = {
.array_size = NFTNL_CTTIMEOUT_TCP_MAX,
.state_to_name = tcp_state_to_name,
@@ -104,11 +104,11 @@ int timeout_str2num(uint16_t l4proto, struct timeout_state *ts)
void handle_free(struct handle *h)
{
- xfree(h->table.name);
- xfree(h->chain.name);
- xfree(h->set.name);
- xfree(h->flowtable.name);
- xfree(h->obj.name);
+ free_const(h->table.name);
+ free_const(h->chain.name);
+ free_const(h->set.name);
+ free_const(h->flowtable.name);
+ free_const(h->obj.name);
}
void handle_merge(struct handle *dst, const struct handle *src)
@@ -146,11 +146,12 @@ struct set *set_alloc(const struct location *loc)
{
struct set *set;
+ assert(loc);
+
set = xzalloc(sizeof(*set));
set->refcnt = 1;
set->handle.set_id = ++set_id;
- if (loc != NULL)
- set->location = *loc;
+ set->location = *loc;
init_list_head(&set->stmt_list);
@@ -161,7 +162,7 @@ struct set *set_clone(const struct set *set)
{
struct set *new_set;
- new_set = set_alloc(NULL);
+ new_set = set_alloc(&internal_location);
handle_merge(&new_set->handle, &set->handle);
new_set->flags = set->flags;
new_set->gc_int = set->gc_int;
@@ -190,16 +191,16 @@ void set_free(struct set *set)
if (--set->refcnt > 0)
return;
- if (set->init != NULL)
- expr_free(set->init);
+
+ expr_free(set->init);
if (set->comment)
- xfree(set->comment);
+ free_const(set->comment);
handle_free(&set->handle);
list_for_each_entry_safe(stmt, next, &set->stmt_list, list)
stmt_free(stmt);
expr_free(set->key);
expr_free(set->data);
- xfree(set);
+ free(set);
}
struct set *set_lookup_fuzzy(const char *set_name,
@@ -453,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);
@@ -476,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)
@@ -554,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)
@@ -594,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);
}
}
@@ -695,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;
@@ -727,16 +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.str);
+ free_const(chain->type.str);
expr_free(chain->dev_expr);
for (i = 0; i < chain->dev_array_len; i++)
- xfree(chain->dev_array[i]);
- xfree(chain->dev_array);
+ free_const(chain->dev_array[i]);
+ free(chain->dev_array);
expr_free(chain->priority.expr);
expr_free(chain->policy);
- xfree(chain->comment);
- xfree(chain);
+ free_const(chain->comment);
+
+ /* MUST be released after all expressions, they could
+ * hold refcounts.
+ */
+ scope_release(&chain->scope);
+ free(chain);
}
struct chain *chain_binding_lookup(const struct table *table,
@@ -864,7 +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" },
@@ -873,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" },
@@ -927,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;
@@ -936,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;
}
}
@@ -969,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) {
@@ -987,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;
}
@@ -1091,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.str, chain->hook.name,
+ nft_print(octx, " { type %s hook %s ",
+ chain->type.str, chain->hook.name);
+
+ if (chain->dev_array_len > 0) {
+ int i;
+
+ nft_print(octx, "devices = { ");
+ for (i = 0; i < chain->dev_array_len; i++) {
+ nft_print(octx, "%s", chain->dev_array[i]);
+ if (i + 1 != chain->dev_array_len)
+ nft_print(octx, ", ");
+ }
+ nft_print(octx, " } ");
+ }
+ nft_print(octx, "priority %s; policy %s; }",
prio2str(octx, priobuf, sizeof(priobuf),
chain->handle.family, chain->hook.num,
chain->priority.expr),
@@ -1107,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);
@@ -1133,7 +1158,7 @@ void table_free(struct table *table)
if (--table->refcnt > 0)
return;
if (table->comment)
- xfree(table->comment);
+ free_const(table->comment);
list_for_each_entry_safe(chain, next, &table->chains, list)
chain_free(chain);
list_for_each_entry_safe(chain, next, &table->chain_bindings, cache.list)
@@ -1163,7 +1188,7 @@ void table_free(struct table *table)
cache_free(&table->set_cache);
cache_free(&table->obj_cache);
cache_free(&table->ft_cache);
- xfree(table);
+ free(table);
}
struct table *table_get(struct table *table)
@@ -1190,6 +1215,7 @@ struct table *table_lookup_fuzzy(const struct handle *h,
static const char *table_flags_name[TABLE_FLAGS_MAX] = {
"dormant",
"owner",
+ "persist",
};
const char *table_flag_name(uint32_t flag)
@@ -1200,6 +1226,17 @@ const char *table_flag_name(uint32_t flag)
return table_flags_name[flag];
}
+unsigned int parse_table_flag(const char *name)
+{
+ int i;
+
+ for (i = 0; i < TABLE_FLAGS_MAX; i++) {
+ if (!strcmp(name, table_flags_name[i]))
+ return 1 << i;
+ }
+ return 0;
+}
+
static void table_print_flags(const struct table *table, const char **delim,
struct output_ctx *octx)
{
@@ -1233,6 +1270,11 @@ static void table_print(const struct table *table, struct output_ctx *octx)
const char *delim = "";
const char *family = family2str(table->handle.family);
+ if (table->has_xt_stmts)
+ fprintf(octx->error_fp,
+ "# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
+ family, table->handle.table.name);
+
nft_print(octx, "table %s %s {", family, table->handle.table.name);
if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER)
nft_print(octx, " #");
@@ -1278,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;
@@ -1293,181 +1337,6 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
return cmd;
}
-void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
-{
- if (cmd->num_attrs >= cmd->attr_array_len) {
- cmd->attr_array_len *= 2;
- cmd->attr = xrealloc(cmd->attr, sizeof(struct nlerr_loc) * cmd->attr_array_len);
- }
-
- cmd->attr[cmd->num_attrs].offset = offset;
- cmd->attr[cmd->num_attrs].location = loc;
- cmd->num_attrs++;
-}
-
-void nft_cmd_expand(struct cmd *cmd)
-{
- struct list_head new_cmds;
- struct flowtable *ft;
- struct table *table;
- struct chain *chain;
- struct rule *rule;
- struct set *set;
- struct obj *obj;
- struct cmd *new;
- struct handle h;
-
- init_list_head(&new_cmds);
-
- switch (cmd->obj) {
- case CMD_OBJ_TABLE:
- table = cmd->table;
- if (!table)
- return;
-
- list_for_each_entry(chain, &table->chains, list) {
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &chain->handle);
- h.chain_id = chain->handle.chain_id;
- new = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h,
- &chain->location, chain_get(chain));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(obj, &table->objs, list) {
- handle_merge(&obj->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &obj->handle);
- new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h,
- &obj->location, obj_get(obj));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(set, &table->sets, list) {
- handle_merge(&set->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &set->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h,
- &set->location, set_get(set));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(ft, &table->flowtables, list) {
- handle_merge(&ft->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &ft->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h,
- &ft->location, flowtable_get(ft));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(chain, &table->chains, list) {
- list_for_each_entry(rule, &chain->rules, list) {
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &rule->handle);
- if (chain->flags & CHAIN_F_BINDING) {
- rule->handle.chain_id =
- chain->handle.chain_id;
- rule->handle.chain.location =
- chain->location;
- }
- new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h,
- &rule->location,
- rule_get(rule));
- list_add_tail(&new->list, &new_cmds);
- }
- }
- list_splice(&new_cmds, &cmd->list);
- break;
- case CMD_OBJ_SET:
- case CMD_OBJ_MAP:
- set = cmd->set;
- if (!set->init)
- break;
-
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &set->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h,
- &set->location, set_get(set));
- list_add(&new->list, &cmd->list);
- break;
- default:
- break;
- }
-}
-
-bool nft_cmd_collapse(struct list_head *cmds)
-{
- struct cmd *cmd, *next, *elems = NULL;
- struct expr *expr, *enext;
- bool collapse = false;
-
- list_for_each_entry_safe(cmd, next, cmds, list) {
- if (cmd->op != CMD_ADD &&
- cmd->op != CMD_CREATE) {
- elems = NULL;
- continue;
- }
-
- if (cmd->obj != CMD_OBJ_ELEMENTS) {
- elems = NULL;
- continue;
- }
-
- if (!elems) {
- elems = cmd;
- continue;
- }
-
- if (cmd->op != elems->op) {
- elems = cmd;
- continue;
- }
-
- if (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);
- }
- }
-}
-
struct markup *markup_alloc(uint32_t format)
{
struct markup *markup;
@@ -1480,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)
@@ -1498,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)
@@ -1513,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;
@@ -1551,9 +1422,9 @@ void cmd_free(struct cmd *cmd)
BUG("invalid command object type %u\n", cmd->obj);
}
}
- xfree(cmd->attr);
- xfree(cmd->arg);
- xfree(cmd);
+ free(cmd->attr);
+ free_const(cmd->arg);
+ free(cmd);
}
#include <netlink.h>
@@ -1605,7 +1476,13 @@ static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
return -1;
}
- return mnl_nft_set_add(ctx, cmd, flags);
+ if (mnl_nft_set_add(ctx, cmd, flags) < 0)
+ return -1;
+
+ if (set_is_anonymous(set->flags))
+ return __do_add_elements(ctx, cmd, set, set->init, flags);
+
+ return 0;
}
static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
@@ -1723,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;
@@ -1732,11 +1608,6 @@ static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct print_fmt_options opts = {
- .tab = "\t",
- .nl = "\n",
- .stmt_separator = "\n",
- };
struct table *table;
struct set *set;
@@ -1754,13 +1625,12 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
!set_is_literal(set->flags))
continue;
if (cmd->obj == CMD_OBJ_METERS &&
- !set_is_meter(set->flags))
+ !set_is_meter_compat(set->flags))
continue;
if (cmd->obj == CMD_OBJ_MAPS &&
!map_is_literal(set->flags))
continue;
- set_print_declaration(set, &opts, &ctx->nft->output);
- nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl);
+ set_print(set, &ctx->nft->output);
}
nft_print(&ctx->nft->output, "}\n");
@@ -1772,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;
@@ -1790,18 +1661,18 @@ void obj_free(struct obj *obj)
{
if (--obj->refcnt > 0)
return;
- xfree(obj->comment);
+ free_const(obj->comment);
handle_free(&obj->handle);
if (obj->type == NFT_OBJECT_CT_TIMEOUT) {
struct timeout_state *ts, *next;
list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) {
list_del(&ts->head);
- xfree(ts->timeout_str);
- xfree(ts);
+ free_const(ts->timeout_str);
+ free(ts);
}
}
- xfree(obj);
+ free(obj);
}
struct obj *obj_lookup_fuzzy(const char *obj_name,
@@ -1826,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);
}
@@ -1844,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;
}
}
@@ -2070,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]);
@@ -2088,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]);
@@ -2180,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;
@@ -2206,10 +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);
+ free(flowtable);
}
static void flowtable_print_declaration(const struct flowtable *flowtable,
@@ -2355,15 +2230,10 @@ static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd)
table->handle.family != family)
continue;
- cmd->handle.family = table->handle.family;
- cmd->handle.table.name = table->handle.table.name;
-
- if (do_list_table(ctx, cmd, table) < 0)
+ if (do_list_table(ctx, table) < 0)
return -1;
}
- cmd->handle.table.name = NULL;
-
return 0;
}
@@ -2387,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,
@@ -2447,11 +2322,13 @@ static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
struct table *table)
{
- struct set *set;
+ struct set *set = cmd->set;
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return -1;
+ if (!set) {
+ set = set_cache_find(table, cmd->handle.set.name);
+ if (set == NULL)
+ return -1;
+ }
__do_list_set(ctx, cmd, set);
@@ -2484,7 +2361,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
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:
@@ -2494,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);
@@ -2513,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:
@@ -2538,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;
@@ -2556,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);
@@ -2572,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);
}
@@ -2601,6 +2482,23 @@ 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);
}
@@ -2718,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);
@@ -2864,10 +2763,8 @@ static void stmt_reduce(const struct rule *rule)
/* Must not merge across other statements */
if (stmt->ops->type != STMT_EXPRESSION) {
- if (idx < 2)
- continue;
-
- payload_do_merge(sa, idx);
+ if (idx >= 2)
+ payload_do_merge(sa, idx);
idx = 0;
continue;
}
@@ -2881,7 +2778,6 @@ static void stmt_reduce(const struct rule *rule)
switch (stmt->expr->op) {
case OP_EQ:
case OP_IMPLICIT:
- case OP_NEQ:
break;
default:
continue;
@@ -2892,7 +2788,8 @@ static void stmt_reduce(const struct rule *rule)
switch (stmt->expr->op) {
case OP_EQ:
case OP_IMPLICIT:
- if (stmt->expr->left->meta.key == NFT_META_PROTOCOL) {
+ if (stmt->expr->left->meta.key == NFT_META_PROTOCOL &&
+ !stmt->expr->left->meta.inner_desc) {
uint16_t protocol;
protocol = mpz_get_uint16(stmt->expr->right->value);
diff --git a/src/scanner.l b/src/scanner.l
index 1371cd04..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>
@@ -118,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)?
@@ -201,10 +204,12 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%s SCANSTATE_CT
%s SCANSTATE_COUNTER
%s SCANSTATE_ETH
+%s SCANSTATE_GRE
%s SCANSTATE_ICMP
%s SCANSTATE_IGMP
%s SCANSTATE_IP
%s SCANSTATE_IP6
+%s SCANSTATE_LAST
%s SCANSTATE_LIMIT
%s SCANSTATE_META
%s SCANSTATE_POLICY
@@ -214,6 +219,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%s SCANSTATE_TCP
%s SCANSTATE_TYPE
%s SCANSTATE_VLAN
+%s SCANSTATE_XT
+%s SCANSTATE_CMD_DESTROY
%s SCANSTATE_CMD_EXPORT
%s SCANSTATE_CMD_IMPORT
%s SCANSTATE_CMD_LIST
@@ -357,6 +364,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"import" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_IMPORT); return IMPORT; }
"export" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_EXPORT); return EXPORT; }
"monitor" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_MONITOR); return MONITOR; }
+"destroy" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_DESTROY); return DESTROY; }
+
"position" { return POSITION; }
"index" { return INDEX; }
@@ -397,9 +406,15 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT>"packets" { return PACKETS; }
<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"bytes" { return BYTES; }
+"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
+<SCANSTATE_LAST>{
+ "never" { return NEVER; }
+}
+
<SCANSTATE_CMD_LIST,SCANSTATE_CMD_RESET>{
"counters" { return COUNTERS; }
"quotas" { return QUOTAS; }
+ "rules" { return RULES; }
}
"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; }
@@ -431,10 +446,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
<SCANSTATE_QUOTA>{
- "used" { return USED; }
"until" { return UNTIL; }
}
+<SCANSTATE_QUOTA,SCANSTATE_LAST>"used" { return USED; }
+
"hour" { return HOUR; }
"day" { return DAY; }
@@ -491,7 +507,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; }
-<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF>{
+<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF,SCANSTATE_GRE>{
"version" { return HDRVERSION; }
}
<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP>{
@@ -508,7 +524,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_EXPR_OSF,SCANSTATE_IP>{
"ttl" { return TTL; }
}
-<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE>"protocol" { return PROTOCOL; }
+<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE,SCANSTATE_GRE>"protocol" { return PROTOCOL; }
<SCANSTATE_EXPR_MH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE,SCANSTATE_ICMP,SCANSTATE_IGMP,SCANSTATE_IP,SCANSTATE_SCTP,SCANSTATE_TCP>{
"checksum" { return CHECKSUM; }
}
@@ -573,6 +589,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"param-problem" { return PPTR; }
"max-delay" { return MAXDELAY; }
"mtu" { return MTU; }
+ "taddr" { return TADDR; }
+ "daddr" { return DADDR; }
}
<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_ICMP,SCANSTATE_TCP>{
"sequence" { return SEQUENCE; }
@@ -619,6 +637,17 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_CT,SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{
"dport" { return DPORT; }
}
+<SCANSTATE_EXPR_DCCP>{
+ "option" { return OPTION; }
+}
+
+"vxlan" { return VXLAN; }
+"vni" { return VNI; }
+
+"geneve" { return GENEVE; }
+
+"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; }
+"gretap" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; }
"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; }
@@ -799,6 +828,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"secmark" { scanner_push_start_cond(yyscanner, SCANSTATE_SECMARK); return SECMARK; }
+"xt" { scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; }
+
{addrstring} {
yylval->string = xstrdup(yytext);
return STRING;
@@ -815,9 +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;
@@ -825,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;
@@ -857,6 +901,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
{tab}+
{space}+
+{comment_line} {
+ reset_pos(yyget_extra(yyscanner), yylloc);
+ }
{comment}
<<EOF>> {
@@ -928,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;
@@ -942,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:
@@ -1008,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--) {
@@ -1020,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;
}
@@ -1057,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)
@@ -1152,8 +1261,8 @@ void *scanner_init(struct parser_state *state)
static void input_descriptor_destroy(const struct input_descriptor *indesc)
{
if (indesc->name)
- xfree(indesc->name);
- xfree(indesc);
+ free_const(indesc->name);
+ free_const(indesc);
}
static void input_descriptor_list_destroy(struct parser_state *state)
@@ -1175,7 +1284,7 @@ void scanner_destroy(struct nft_ctx *nft)
struct parser_state *state = yyget_extra(nft->scanner);
input_descriptor_list_destroy(state);
- xfree(state->startcond_active);
+ free(state->startcond_active);
yylex_destroy(nft->scanner);
}
diff --git a/src/sctp_chunk.c b/src/sctp_chunk.c
index 6e73e72f..24a07e20 100644
--- a/src/sctp_chunk.c
+++ b/src/sctp_chunk.c
@@ -6,10 +6,11 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <exthdr.h>
#include <sctp_chunk.h>
-#include <string.h>
#define PHT(__token, __offset, __len) \
PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \
diff --git a/src/segtree.c b/src/segtree.c
index 0e3d111f..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>
@@ -126,9 +126,8 @@ static struct expr *get_set_interval_find(const struct set *cache_set,
case EXPR_VALUE:
if (expr_basetype(i->key)->type != TYPE_STRING)
break;
- /* string type, check if its a range (wildcard), so
- * fall through.
- */
+ /* string type, check if its a range (wildcard). */
+ /* fall-through */
case EXPR_PREFIX:
case EXPR_RANGE:
range_expr_value_low(val, i);
@@ -633,6 +632,6 @@ out:
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 eb075153..8a149e63 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -4,10 +4,12 @@
* Copyright (c) 2018 Máté Eckl <ecklm94@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <socket.h>
diff --git a/src/statement.c b/src/statement.c
index 327d00f9..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>
@@ -52,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)
@@ -184,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 = {
@@ -249,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",
@@ -455,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);
@@ -819,6 +847,7 @@ static const struct stmt_ops map_stmt_ops = {
.name = "map",
.print = map_stmt_print,
.destroy = map_stmt_destroy,
+ .json = map_stmt_json,
};
struct stmt *map_stmt_alloc(const struct location *loc)
@@ -960,7 +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, ":");
@@ -997,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 c3e07d78..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>
@@ -119,6 +118,13 @@ static const struct exthdr_desc tcpopt_mptcp = {
[TCPOPT_MPTCP_SUBTYPE] = PHT("subtype", 16, 4),
},
};
+
+static const struct exthdr_desc tcpopt_fallback = {
+ .templates = {
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
+ },
+};
#undef PHT
const struct exthdr_desc *tcpopt_protocols[] = {
@@ -134,6 +140,17 @@ const struct exthdr_desc *tcpopt_protocols[] = {
[TCPOPT_KIND_FASTOPEN] = &tcpopt_fastopen,
};
+static void tcpopt_assign_tmpl(struct expr *expr,
+ const struct proto_hdr_template *tmpl,
+ const struct exthdr_desc *desc)
+{
+ expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+
+ expr->exthdr.desc = desc;
+ expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
+}
+
/**
* tcpopt_expr_alloc - allocate tcp option extension expression
*
@@ -183,32 +200,38 @@ struct expr *tcpopt_expr_alloc(const struct location *loc,
desc = tcpopt_protocols[kind];
if (!desc) {
- if (field != TCPOPT_COMMON_KIND || kind > 255)
+ if (kind > 255)
return NULL;
+ desc = &tcpopt_fallback;
+
+ switch (field) {
+ case TCPOPT_COMMON_KIND:
+ case TCPOPT_COMMON_LENGTH:
+ tmpl = &desc->templates[field];
+ break;
+ default:
+ tmpl = &tcpopt_unknown_template;
+ break;
+ }
+
expr = expr_alloc(loc, EXPR_EXTHDR, &integer_type,
BYTEORDER_BIG_ENDIAN, 8);
- desc = tcpopt_protocols[TCPOPT_NOP];
- tmpl = &desc->templates[field];
- expr->exthdr.desc = desc;
- expr->exthdr.tmpl = tmpl;
- expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
expr->exthdr.raw_type = kind;
+ tcpopt_assign_tmpl(expr, tmpl, desc);
return expr;
}
tmpl = &desc->templates[field];
- if (!tmpl)
+ if (!tmpl || !tmpl->dtype)
return NULL;
expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
BYTEORDER_BIG_ENDIAN, tmpl->len);
- expr->exthdr.desc = desc;
- expr->exthdr.tmpl = tmpl;
- expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+
expr->exthdr.raw_type = desc->type;
- expr->exthdr.offset = tmpl->offset;
+ tcpopt_assign_tmpl(expr, tmpl, desc);
return expr;
}
diff --git a/src/utils.c b/src/utils.c
index 925841c5..2aa1eb4e 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -8,12 +8,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
-#include <string.h>
#include <nftables.h>
#include <utils.h>
@@ -24,11 +24,6 @@ void __noreturn __memory_allocation_error(const char *filename, uint32_t line)
exit(NFT_EXIT_NOMEM);
}
-void xfree(const void *ptr)
-{
- free((void *)ptr);
-}
-
void *xmalloc(size_t size)
{
void *ptr;
@@ -100,3 +95,8 @@ void xstrunescape(const char *in, char *out)
}
out[k++] = '\0';
}
+
+int round_pow_2(unsigned int n)
+{
+ return 1UL << fls(n - 1);
+}
diff --git a/src/xfrm.c b/src/xfrm.c
index 80f0ea03..b32b2a1d 100644
--- a/src/xfrm.c
+++ b/src/xfrm.c
@@ -1,11 +1,15 @@
/*
* XFRM (ipsec) expression
*
+ * Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de>
+ *
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <erec.h>
#include <expression.h>
@@ -13,7 +17,6 @@
#include <datatype.h>
#include <gmputil.h>
#include <utils.h>
-#include <string.h>
#include <netinet/ip.h>
#include <linux/netfilter.h>
diff --git a/src/xt.c b/src/xt.c
index 789de992..f7bee216 100644
--- a/src/xt.c
+++ b/src/xt.c
@@ -2,14 +2,14 @@
* Copyright (c) 2013-2015 Pablo Neira Ayuso <pablo@netfilter.org>
* Copyright (c) 2015 Arturo Borrero Gonzalez <arturo@debian.org>
*
- * This program is free software; you can redistribute it and/or modifyi
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
-#include <stdlib.h>
+#include <nft.h>
+
#include <time.h>
-#include <string.h>
#include <net/if.h>
#include <getopt.h>
#include <ctype.h> /* for isspace */
@@ -28,85 +28,108 @@
#ifdef HAVE_LIBXTABLES
#include <xtables.h>
+
+static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af);
#endif
void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx)
{
+ static const char *typename[NFT_XT_MAX] = {
+ [NFT_XT_MATCH] = "match",
+ [NFT_XT_TARGET] = "target",
+ [NFT_XT_WATCHER] = "watcher",
+ };
+ int rc = 0;
#ifdef HAVE_LIBXTABLES
struct xt_xlate *xl = xt_xlate_alloc(10240);
+ struct xtables_target *tg;
+ struct xt_entry_target *t;
+ struct xtables_match *mt;
+ struct xt_entry_match *m;
+ size_t size;
+ void *entry;
+
+ xtables_set_nfproto(stmt->xt.family);
+ entry = xt_entry_alloc(&stmt->xt, stmt->xt.family);
switch (stmt->xt.type) {
case NFT_XT_MATCH:
- if (stmt->xt.match->xlate) {
+ mt = xtables_find_match(stmt->xt.name, XTF_TRY_LOAD, NULL);
+ if (!mt) {
+ fprintf(octx->error_fp,
+ "# Warning: XT match %s not found\n",
+ stmt->xt.name);
+ break;
+ }
+ size = XT_ALIGN(sizeof(*m)) + stmt->xt.infolen;
+
+ m = xzalloc(size);
+ memcpy(&m->data, stmt->xt.info, stmt->xt.infolen);
+
+ m->u.match_size = size;
+ m->u.user.revision = stmt->xt.rev;
+
+ if (mt->xlate) {
struct xt_xlate_mt_params params = {
- .ip = stmt->xt.entry,
- .match = stmt->xt.match->m,
- .numeric = 0,
+ .ip = entry,
+ .match = m,
+ .numeric = 1,
};
- stmt->xt.match->xlate(xl, &params);
- nft_print(octx, "%s", xt_xlate_get(xl));
- } else if (stmt->xt.match->print) {
- printf("#");
- stmt->xt.match->print(&stmt->xt.entry,
- stmt->xt.match->m, 0);
+ rc = mt->xlate(xl, &params);
}
+ free(m);
break;
case NFT_XT_WATCHER:
case NFT_XT_TARGET:
- if (stmt->xt.target->xlate) {
+ tg = xtables_find_target(stmt->xt.name, XTF_TRY_LOAD);
+ if (!tg) {
+ fprintf(octx->error_fp,
+ "# Warning: XT target %s not found\n",
+ stmt->xt.name);
+ break;
+ }
+ size = XT_ALIGN(sizeof(*t)) + stmt->xt.infolen;
+
+ t = xzalloc(size);
+ memcpy(&t->data, stmt->xt.info, stmt->xt.infolen);
+
+ t->u.target_size = size;
+ t->u.user.revision = stmt->xt.rev;
+
+ strcpy(t->u.user.name, tg->name);
+
+ if (tg->xlate) {
struct xt_xlate_tg_params params = {
- .ip = stmt->xt.entry,
- .target = stmt->xt.target->t,
- .numeric = 0,
+ .ip = entry,
+ .target = t,
+ .numeric = 1,
};
- stmt->xt.target->xlate(xl, &params);
- nft_print(octx, "%s", xt_xlate_get(xl));
- } else if (stmt->xt.target->print) {
- printf("#");
- stmt->xt.target->print(NULL, stmt->xt.target->t, 0);
+ rc = tg->xlate(xl, &params);
}
- break;
- default:
+ free(t);
break;
}
+ if (rc == 1)
+ nft_print(octx, "%s", xt_xlate_get(xl));
xt_xlate_free(xl);
-#else
- nft_print(octx, "# xt_%s", stmt->xt.name);
+ free(entry);
#endif
+ if (!rc)
+ nft_print(octx, "xt %s \"%s\"",
+ typename[stmt->xt.type], stmt->xt.name);
}
void xt_stmt_destroy(struct stmt *stmt)
{
-#ifdef HAVE_LIBXTABLES
- switch (stmt->xt.type) {
- case NFT_XT_MATCH:
- if (!stmt->xt.match)
- break;
- if (stmt->xt.match->m)
- xfree(stmt->xt.match->m);
- xfree(stmt->xt.match);
- break;
- case NFT_XT_WATCHER:
- case NFT_XT_TARGET:
- if (!stmt->xt.target)
- break;
- if (stmt->xt.target->t)
- xfree(stmt->xt.target->t);
- xfree(stmt->xt.target);
- break;
- default:
- break;
- }
-#endif
- xfree(stmt->xt.entry);
- xfree(stmt->xt.name);
+ free_const(stmt->xt.name);
+ free(stmt->xt.info);
}
#ifdef HAVE_LIBXTABLES
-static void *xt_entry_alloc(struct xt_stmt *xt, uint32_t af)
+static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af)
{
union nft_entry {
struct ipt_entry ipt;
@@ -173,24 +196,6 @@ static uint32_t xt_proto(const struct proto_ctx *pctx)
return 0;
}
-
-static struct xtables_target *xt_target_clone(struct xtables_target *t)
-{
- struct xtables_target *clone;
-
- clone = xzalloc(sizeof(struct xtables_target));
- memcpy(clone, t, sizeof(struct xtables_target));
- return clone;
-}
-
-static struct xtables_match *xt_match_clone(struct xtables_match *m)
-{
- struct xtables_match *clone;
-
- clone = xzalloc(sizeof(struct xtables_match));
- memcpy(clone, m, sizeof(struct xtables_match));
- return clone;
-}
#endif
/*
@@ -201,43 +206,23 @@ void netlink_parse_match(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- struct stmt *stmt;
- const char *name;
-#ifdef HAVE_LIBXTABLES
- struct xtables_match *mt;
const char *mtinfo;
- struct xt_entry_match *m;
+ struct stmt *stmt;
uint32_t mt_len;
- xtables_set_nfproto(ctx->table->handle.family);
-
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME);
-
- mt = xtables_find_match(name, XTF_TRY_LOAD, NULL);
- if (!mt) {
- fprintf(stderr, "XT match %s not found\n", name);
- return;
- }
mtinfo = nftnl_expr_get(nle, NFTNL_EXPR_MT_INFO, &mt_len);
- m = xzalloc(sizeof(struct xt_entry_match) + mt_len);
- memcpy(&m->data, mtinfo, mt_len);
-
- m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
- m->u.user.revision = nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV);
-
stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
+ stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME));
stmt->xt.type = NFT_XT_MATCH;
- stmt->xt.match = xt_match_clone(mt);
- stmt->xt.match->m = m;
-#else
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME);
+ stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV);
+ stmt->xt.family = ctx->table->handle.family;
- stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
- stmt->xt.type = NFT_XT_MATCH;
-#endif
+ stmt->xt.infolen = mt_len;
+ stmt->xt.info = xmalloc(mt_len);
+ memcpy(stmt->xt.info, mtinfo, mt_len);
+
+ ctx->table->has_xt_stmts = true;
rule_stmt_append(ctx->rule, stmt);
}
@@ -245,44 +230,23 @@ void netlink_parse_target(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- struct stmt *stmt;
- const char *name;
-#ifdef HAVE_LIBXTABLES
- struct xtables_target *tg;
const void *tginfo;
- struct xt_entry_target *t;
- size_t size;
+ struct stmt *stmt;
uint32_t tg_len;
- xtables_set_nfproto(ctx->table->handle.family);
-
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME);
- tg = xtables_find_target(name, XTF_TRY_LOAD);
- if (!tg) {
- fprintf(stderr, "XT target %s not found\n", name);
- return;
- }
tginfo = nftnl_expr_get(nle, NFTNL_EXPR_TG_INFO, &tg_len);
- size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
- t = xzalloc(size);
- memcpy(&t->data, tginfo, tg_len);
- t->u.target_size = size;
- t->u.user.revision = nftnl_expr_get_u32(nle, NFTNL_EXPR_TG_REV);
- strcpy(t->u.user.name, tg->name);
-
stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
+ stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME));
stmt->xt.type = NFT_XT_TARGET;
- stmt->xt.target = xt_target_clone(tg);
- stmt->xt.target->t = t;
-#else
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME);
+ stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_TG_REV);
+ stmt->xt.family = ctx->table->handle.family;
- stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
- stmt->xt.type = NFT_XT_TARGET;
-#endif
+ stmt->xt.infolen = tg_len;
+ stmt->xt.info = xmalloc(tg_len);
+ memcpy(stmt->xt.info, tginfo, tg_len);
+
+ ctx->table->has_xt_stmts = true;
rule_stmt_append(ctx->rule, stmt);
}
@@ -305,11 +269,12 @@ static bool is_watcher(uint32_t family, struct stmt *stmt)
void stmt_xt_postprocess(struct rule_pp_ctx *rctx, struct stmt *stmt,
struct rule *rule)
{
- if (is_watcher(rctx->pctx.family, stmt))
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
+
+ if (is_watcher(dl->pctx.family, stmt))
stmt->xt.type = NFT_XT_WATCHER;
- stmt->xt.proto = xt_proto(&rctx->pctx);
- stmt->xt.entry = xt_entry_alloc(&stmt->xt, rctx->pctx.family);
+ stmt->xt.proto = xt_proto(&dl->pctx);
}
static int nft_xt_compatible_revision(const char *name, uint8_t rev, int opt)
@@ -383,7 +348,7 @@ err:
}
static struct option original_opts[] = {
- { NULL },
+ { },
};
static struct xtables_globals xt_nft_globals = {
@@ -395,7 +360,18 @@ static struct xtables_globals xt_nft_globals = {
void xt_init(void)
{
- /* Default to IPv4, but this changes in runtime */
- xtables_init_all(&xt_nft_globals, NFPROTO_IPV4);
+ static bool init_once;
+
+ if (!init_once) {
+ /* libxtables is full of global variables and cannot be used
+ * concurrently by multiple threads. Hence, it's fine that the
+ * "init_once" guard is not thread-safe either.
+ * Don't link against xtables if you want thread safety.
+ */
+ init_once = true;
+
+ /* Default to IPv4, but this changes in runtime */
+ xtables_init_all(&xt_nft_globals, NFPROTO_IPV4);
+ }
}
#endif