diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 119 | ||||
-rw-r--r-- | src/cache.c | 47 | ||||
-rw-r--r-- | src/cli.c | 68 | ||||
-rw-r--r-- | src/cmd.c | 39 | ||||
-rw-r--r-- | src/ct.c | 17 | ||||
-rw-r--r-- | src/datatype.c | 356 | ||||
-rw-r--r-- | src/dccpopt.c | 277 | ||||
-rw-r--r-- | src/erec.c | 12 | ||||
-rw-r--r-- | src/evaluate.c | 892 | ||||
-rw-r--r-- | src/expression.c | 55 | ||||
-rw-r--r-- | src/exthdr.c | 20 | ||||
-rw-r--r-- | src/fib.c | 3 | ||||
-rw-r--r-- | src/gmputil.c | 27 | ||||
-rw-r--r-- | src/hash.c | 2 | ||||
-rw-r--r-- | src/iface.c | 4 | ||||
-rw-r--r-- | src/intervals.c | 13 | ||||
-rw-r--r-- | src/ipopt.c | 3 | ||||
-rw-r--r-- | src/json.c | 180 | ||||
-rw-r--r-- | src/libnftables.c | 125 | ||||
-rw-r--r-- | src/libnftables.map | 5 | ||||
-rw-r--r-- | src/main.c | 76 | ||||
-rw-r--r-- | src/mergesort.c | 36 | ||||
-rw-r--r-- | src/meta.c | 117 | ||||
-rw-r--r-- | src/mini-gmp.c | 4 | ||||
-rw-r--r-- | src/misspell.c | 6 | ||||
-rw-r--r-- | src/mnl.c | 42 | ||||
-rw-r--r-- | src/monitor.c | 15 | ||||
-rw-r--r-- | src/netlink.c | 120 | ||||
-rw-r--r-- | src/netlink_delinearize.c | 186 | ||||
-rw-r--r-- | src/netlink_linearize.c | 34 | ||||
-rw-r--r-- | src/nfnl_osf.c | 4 | ||||
-rw-r--r-- | src/nftutils.c | 100 | ||||
-rw-r--r-- | src/nftutils.h | 20 | ||||
-rw-r--r-- | src/numgen.c | 2 | ||||
-rw-r--r-- | src/optimize.c | 17 | ||||
-rw-r--r-- | src/osf.c | 3 | ||||
-rw-r--r-- | src/owner.c | 3 | ||||
-rw-r--r-- | src/parser_bison.y | 607 | ||||
-rw-r--r-- | src/parser_json.c | 420 | ||||
-rw-r--r-- | src/payload.c | 123 | ||||
-rw-r--r-- | src/print.c | 2 | ||||
-rw-r--r-- | src/proto.c | 26 | ||||
-rw-r--r-- | src/rt.c | 15 | ||||
-rw-r--r-- | src/rule.c | 219 | ||||
-rw-r--r-- | src/scanner.l | 84 | ||||
-rw-r--r-- | src/sctp_chunk.c | 3 | ||||
-rw-r--r-- | src/segtree.c | 13 | ||||
-rw-r--r-- | src/socket.c | 2 | ||||
-rw-r--r-- | src/statement.c | 17 | ||||
-rw-r--r-- | src/tcpopt.c | 51 | ||||
-rw-r--r-- | src/utils.c | 9 | ||||
-rw-r--r-- | src/xfrm.c | 3 | ||||
-rw-r--r-- | src/xt.c | 31 |
53 files changed, 3200 insertions, 1474 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 95adee7f..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,15 @@ 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; } @@ -280,6 +288,11 @@ static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags, 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; @@ -368,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; @@ -1012,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); } @@ -1066,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; } @@ -1079,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; } @@ -1262,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) @@ -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 */ @@ -6,6 +6,8 @@ * later) as published by the Free Software Foundation. */ +#include <nft.h> + #include <erec.h> #include <mnl.h> #include <cmd.h> @@ -13,9 +15,7 @@ #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) { @@ -41,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; @@ -57,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; @@ -86,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); @@ -116,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), @@ -147,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, @@ -176,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); @@ -205,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); @@ -311,6 +311,12 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd, 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)); } @@ -401,21 +407,6 @@ void nft_cmd_expand(struct cmd *cmd) nft_cmd_expand_chain(chain, &new_cmds); list_splice(&new_cmds, &cmd->list); break; - default: - break; - } -} - -void nft_cmd_post_expand(struct cmd *cmd) -{ - struct list_head new_cmds; - struct set *set; - struct cmd *new; - struct handle h; - - init_list_head(&new_cmds); - - switch (cmd->obj) { case CMD_OBJ_SET: case CMD_OBJ_MAP: set = cmd->set; @@ -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, @@ -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 da802a18..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> @@ -29,6 +29,7 @@ #include <netlink.h> #include <json.h> #include <misspell.h> +#include "nftutils.h" #include <netinet/ip_icmp.h> @@ -63,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, @@ -72,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, @@ -599,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; } @@ -658,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; - err = getaddrinfo(sym->identifier, NULL, &hints, &ai); - if (err != 0) - return error(&sym->location, "Could not resolve hostname: %s", - gai_strerror(err)); + if (nft_input_no_dns(ctx->input)) { + if (inet_pton(AF_INET6, sym->identifier, &addr) != 1) + return error(&sym->location, "Invalid IPv6 address"); + } else { + struct addrinfo *ai, hints = { .ai_family = AF_INET6, + .ai_socktype = SOCK_DGRAM}; + int err; - 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_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; } @@ -697,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; } } @@ -711,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); } } @@ -727,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; @@ -740,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, @@ -768,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; } @@ -808,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); } @@ -832,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; @@ -854,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; } @@ -876,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; } @@ -885,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) @@ -911,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, @@ -922,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 = { @@ -940,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 = { @@ -963,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 = { @@ -984,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", @@ -991,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); @@ -1015,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 { @@ -1176,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; @@ -1199,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; } @@ -1229,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) @@ -1263,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; @@ -1275,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; @@ -1431,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; @@ -1472,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) @@ -1481,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; +} @@ -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 50f1496c..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> @@ -75,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; @@ -82,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); @@ -90,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; @@ -100,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; @@ -119,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); } @@ -168,12 +201,21 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr, assert(basetype == TYPE_INTEGER); - op = byteorder_conversion_op(i, byteorder); - unary = unary_expr_alloc(&i->location, op, i); - if (expr_evaluate(ctx, &unary) < 0) - return -1; + 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); + list_replace(&i->list, &unary->list); + } + } } return 0; @@ -214,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)); } @@ -230,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); @@ -247,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, @@ -266,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); @@ -277,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; @@ -398,7 +443,7 @@ 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; } @@ -414,8 +459,8 @@ static int expr_evaluate_integer(struct eval_ctx *ctx, struct expr **exprp) 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; } @@ -452,10 +497,22 @@ 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; @@ -477,7 +534,7 @@ 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"); @@ -504,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; @@ -525,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); @@ -542,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, @@ -549,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; @@ -558,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) @@ -570,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: { @@ -622,6 +702,7 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp) 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; @@ -679,9 +760,8 @@ 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; @@ -695,56 +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; + assert(base == PROTO_BASE_LL_HDR); + pctx = eval_proto_ctx(ctx); - if (payload->payload.base == PROTO_BASE_LL_HDR) { - 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); - } + if (proto_is_dummy(desc)) { + if (ctx->inner_desc) { + proto_ctx_update(pctx, PROTO_BASE_LL_HDR, &payload->location, &proto_eth); } else { - unsigned int i; + err = meta_iiftype_gen_dependency(ctx, payload, &nstmt); + if (err < 0) + return err; - /* 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; - } + desc = payload->payload.desc; + rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); + } + } else { + unsigned int i; + + /* payload desc stored in the L2 header stack? No conflict. */ + for (i = 0; i < pctx->stacked_ll_count; i++) { + if (pctx->stacked_ll[i] == payload->payload.desc) + return 0; } } - assert(base <= PROTO_BASE_MAX); /* This payload and the existing context don't match, conflict. */ if (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 < pctx->stacked_ll_count; i++) - payload->payload.offset += 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); @@ -786,12 +862,13 @@ 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); + assert(pctx->stacked_ll_count); payload->payload.offset += pctx->stacked_ll[0]->length; rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); return 1; @@ -842,8 +919,8 @@ 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; @@ -852,7 +929,8 @@ check_icmp: return 0; } return expr_error(ctx->msgs, payload, - "conflicting protocols specified: %s vs. %s", + "conflicting %s protocols specified: %s vs. %s", + proto_base_names[base], pctx->protocol[base].desc->name, payload->payload.desc->name); } @@ -865,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) @@ -876,8 +955,14 @@ 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; @@ -1128,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; } @@ -1139,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; @@ -1191,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; @@ -1226,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; @@ -1293,9 +1377,14 @@ 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 = mpz_get_uint32(right->value); - unsigned int max_shift_len; + 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 @@ -1327,8 +1416,8 @@ 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; - int byteorder; if (ctx->stmt_len > left->len) { max_len = ctx->stmt_len; @@ -1369,6 +1458,13 @@ 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; @@ -1414,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) @@ -1496,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) @@ -1528,15 +1666,19 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr) if (key && expressions) { if (list_is_last(&key->list, expressions)) runaway = true; - - key = list_next_entry(key, list); + 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) @@ -1561,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); } @@ -1704,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, @@ -1800,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 && list_empty(&i->stmt_list)) { - 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; @@ -1868,19 +2002,34 @@ static void __mapping_expr_expand(struct expr *i) } } -static void mapping_expr_expand(struct expr *init) +static int mapping_expr_expand(struct eval_ctx *ctx) { struct expr *i; - list_for_each_entry(i, &init->expressions, list) + 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 *map = *expr, *mappings; struct expr_ctx ectx = ctx->ectx; - const struct datatype *dtype; struct expr *key, *data; if (map->map->etype == EXPR_CT && @@ -1901,13 +2050,13 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr) } expr_set_context(&ctx->ectx, NULL, 0); - ctx->stmt_len = 0; if (expr_evaluate(ctx, &map->map) < 0) return -1; if (expr_is_constant(map->map)) 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; @@ -1922,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); @@ -1954,8 +2116,8 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr) if (ctx->set->data->flags & EXPR_F_INTERVAL) { ctx->set->data->len *= 2; - if (set_is_anonymous(ctx->set->flags)) - mapping_expr_expand(ctx->set->init); + if (mapping_expr_expand(ctx)) + return -1; } ctx->set->key->len = ctx->ectx.len; @@ -1974,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", @@ -2038,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; @@ -2069,6 +2231,11 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr) 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; } @@ -2303,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; @@ -2312,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; @@ -2329,6 +2496,35 @@ 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; @@ -2365,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; } } @@ -2408,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: @@ -2428,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) { @@ -2455,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 && @@ -2634,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) { @@ -2659,7 +2901,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **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: @@ -2777,11 +3019,7 @@ static int __stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt, dtype->desc, (*expr)->dtype->desc, (*expr)->len); - if ((dtype->type == TYPE_MARK && - !datatype_equal(datatype_basetype(dtype), datatype_basetype((*expr)->dtype))) || - (dtype->type != TYPE_MARK && - (*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", @@ -2806,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; } @@ -2860,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: @@ -2885,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) @@ -2923,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)) { @@ -2942,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++; @@ -3017,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) @@ -3036,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; @@ -3061,7 +3342,11 @@ static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt) stmt->meta.tmpl->len, stmt->meta.tmpl->byteorder, &stmt->meta.expr); - ctx->stmt_len = 0; + if (ret < 0) + return ret; + + if (stmt->meta.expr->etype == EXPR_RANGE) + return stmt_error_range(ctx, stmt, stmt->meta.expr); return ret; } @@ -3077,8 +3362,6 @@ static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt) stmt->ct.tmpl->len, stmt->ct.tmpl->byteorder, &stmt->ct.expr); - ctx->stmt_len = 0; - if (ret < 0) return -1; @@ -3086,6 +3369,9 @@ static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt) 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; } @@ -3170,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; } @@ -3412,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; @@ -3421,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); @@ -3593,28 +3889,6 @@ static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt, return 0; } -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_evaluate_l3proto(struct eval_ctx *ctx, struct stmt *stmt, uint8_t family) { @@ -3695,8 +3969,10 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt) { struct proto_ctx *pctx = eval_proto_ctx(ctx); struct expr *one, *two, *data, *tmp; - const struct datatype *dtype; - int addr_type, err; + const struct datatype *dtype = NULL; + const struct datatype *dtype2; + int addr_type; + int err; if (stmt->nat.family == NFPROTO_INET) expr_family_infer(pctx, stmt->nat.addr, &stmt->nat.family); @@ -3716,18 +3992,23 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt) 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) @@ -3735,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; } @@ -3790,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; @@ -3875,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, &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; @@ -3927,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; @@ -3947,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) @@ -3993,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: @@ -4011,6 +4307,10 @@ 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; } @@ -4032,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: @@ -4050,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: @@ -4083,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); @@ -4105,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, @@ -4199,6 +4515,10 @@ 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 (!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, @@ -4263,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; @@ -4296,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", @@ -4344,6 +4667,8 @@ 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: @@ -4424,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; @@ -4468,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) @@ -4482,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); @@ -4504,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; @@ -4512,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, @@ -4535,8 +4901,6 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set) 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); @@ -4558,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 " @@ -4602,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; } @@ -4649,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; @@ -4748,7 +5110,7 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft) return table_not_found(ctx); if (!ft_cache_find(table, ft->handle.flowtable.name)) { - if (!ft->hook.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); @@ -4953,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); } @@ -5034,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; @@ -5091,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); @@ -5290,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); @@ -5336,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); @@ -5378,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, @@ -5431,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, @@ -5476,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); } @@ -5549,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)); @@ -5717,7 +6061,7 @@ static const char * const cmd_op_name[] = { 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]; diff --git a/src/expression.c b/src/expression.c index 9b53f43b..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> @@ -95,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) @@ -141,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) { @@ -315,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 = { @@ -995,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; @@ -1013,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; @@ -1336,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) @@ -1352,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", @@ -1379,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) @@ -1498,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; @@ -1532,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(enum expr_types 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. */ @@ -8,13 +8,14 @@ * 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); } @@ -8,6 +8,8 @@ * 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 3647778c..428acaae 100644 --- a/src/iface.c +++ b/src/iface.c @@ -6,11 +6,11 @@ * 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 d79c52c5..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; @@ -459,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); 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> @@ -6,15 +6,16 @@ * later) as published by the Free Software Foundation. */ -#define _GNU_SOURCE +#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> @@ -41,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; @@ -69,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) @@ -79,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; } @@ -128,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 { @@ -153,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) { @@ -173,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); @@ -190,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(); @@ -253,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), @@ -277,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); } @@ -297,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); } @@ -492,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; @@ -533,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) @@ -573,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); } @@ -751,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)); @@ -1080,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); } @@ -1093,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) @@ -1477,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; @@ -1495,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; @@ -1658,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); @@ -1673,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; } @@ -1691,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) { @@ -1699,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; @@ -1778,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)); } diff --git a/src/libnftables.c b/src/libnftables.c index de16d203..0dee1bac 100644 --- a/src/libnftables.c +++ b/src/libnftables.c @@ -5,6 +5,9 @@ * 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> @@ -13,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 = { @@ -153,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); @@ -181,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); @@ -219,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; } @@ -344,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); @@ -354,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); @@ -409,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) { @@ -527,7 +532,8 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs, collapsed = true; list_for_each_entry(cmd, cmds, list) { - if (cmd->op != CMD_ADD) + if (cmd->op != CMD_ADD && + cmd->op != CMD_CREATE) continue; nft_cmd_expand(cmd); @@ -552,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_post_expand(cmd); - } - return 0; } @@ -574,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, @@ -596,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); @@ -615,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; } @@ -661,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); @@ -692,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); @@ -710,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]); @@ -765,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; @@ -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; @@ -408,7 +408,7 @@ int main(int argc, char * const *argv) if (interactive) { fprintf(stderr, "Error: -i/--interactive and -f/--file options cannot be combined\n"); - exit(EXIT_FAILURE); + goto out_fail; } filename = optarg; break; @@ -416,7 +416,7 @@ int main(int argc, char * const *argv) if (filename) { fprintf(stderr, "Error: -i/--interactive and -f/--file options cannot be combined\n"); - exit(EXIT_FAILURE); + goto out_fail; } interactive = true; break; @@ -425,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: @@ -460,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) @@ -480,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: @@ -502,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(" "); @@ -521,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]); @@ -529,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 a3a9d605..5e676be1 100644 --- a/src/mergesort.c +++ b/src/mergesort.c @@ -6,13 +6,12 @@ * 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; @@ -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); - - if (cur_tm) - tstamp += cur_tm->tm_gmtoff; + tstamp = tstamp64; + if (localtime_r(&tstamp, &tm)) + tstamp64 += 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) + /* 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') @@ -965,8 +969,8 @@ struct stmt *meta_stmt_alloc(const struct location *loc, enum nft_meta_keys key, stmt->meta.key = key; stmt->meta.expr = expr; - if (key < array_size(meta_templates)) - stmt->meta.tmpl = &meta_templates[key]; + if (key < array_size(meta_templates)) + stmt->meta.tmpl = &meta_templates[key]; return stmt; } @@ -995,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)) @@ -1022,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; @@ -1032,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 8992c75e..f5354fa8 100644 --- a/src/misspell.c +++ b/src/misspell.c @@ -6,8 +6,8 @@ * later) as published by the Free Software Foundation. */ -#include <stdlib.h> -#include <string.h> +#include <nft.h> + #include <limits.h> #include <utils.h> #include <misspell.h> @@ -72,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; } @@ -8,6 +8,8 @@ * 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> @@ -27,13 +29,11 @@ #include <mnl.h> #include <cmd.h> -#include <string.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> @@ -242,7 +242,7 @@ 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(struct netlink_ctx *ctx) @@ -776,9 +776,9 @@ static void nft_dev_array_free(const struct nft_dev *dev_array) int i = 0; while (dev_array[i].ifname != NULL) - xfree(dev_array[i++].ifname); + free_const(dev_array[i++].ifname); - xfree(dev_array); + free_const(dev_array); } static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd) @@ -1870,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); @@ -1900,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); @@ -2161,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) diff --git a/src/monitor.c b/src/monitor.c index 3a189691..2fc16d67 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -6,13 +6,13 @@ * 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> @@ -390,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; } @@ -437,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 3352ad0a..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> @@ -254,6 +254,11 @@ static int netlink_export_pad(unsigned char *data, const mpz_t v, return netlink_padded_len(i->len) / BITS_PER_BYTE; } +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) { @@ -268,7 +273,7 @@ static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i, if (expr_basetype(expr)->type == TYPE_INTEGER && expr->byteorder == BYTEORDER_HOST_ENDIAN) - mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + byteorder_switch_expr_value(expr->value, expr); i = expr; break; @@ -280,7 +285,7 @@ static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i, 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); @@ -298,7 +303,7 @@ static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i, expr = (struct expr *)i; if (expr_basetype(expr)->type == TYPE_INTEGER && expr->byteorder == BYTEORDER_HOST_ENDIAN) - mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + byteorder_switch_expr_value(expr->value, expr); break; default: BUG("invalid expression type '%s' in set", expr_ops(i)->name); @@ -307,6 +312,16 @@ static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i, return netlink_export_pad(data, i->value, i); } +static void nft_data_memcpy(struct nft_data_linearize *nld, + const void *src, unsigned int len) +{ + if (len > sizeof(nld->value)) + BUG("nld buffer overflow: want to copy %u, max %u\n", len, (unsigned int)sizeof(nld->value)); + + memcpy(nld->value, src, len); + nld->len = len; +} + static void netlink_gen_concat_key(const struct expr *expr, struct nft_data_linearize *nld) { @@ -319,8 +334,7 @@ static void netlink_gen_concat_key(const struct expr *expr, list_for_each_entry(i, &expr->expressions, list) 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, @@ -366,8 +380,7 @@ static void __netlink_gen_concat_expand(const struct expr *expr, list_for_each_entry(i, &expr->expressions, list) 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, @@ -382,8 +395,7 @@ static void __netlink_gen_concat(const struct expr *expr, list_for_each_entry(i, &expr->expressions, list) offset += __netlink_gen_concat_data(expr->flags, i, data + offset); - memcpy(nld->value, data, len); - nld->len = len; + nft_data_memcpy(nld, data, len); } static void netlink_gen_concat_data(const struct expr *expr, @@ -452,26 +464,27 @@ 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); } static void netlink_gen_key(const struct expr *expr, @@ -617,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)) @@ -795,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); } } @@ -871,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) @@ -888,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) @@ -930,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) { @@ -987,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; } } @@ -1023,22 +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) { uint32_t dlen; - dtype = set_datatype_alloc(datatype, databyteorder); + dtype2 = set_datatype_alloc(datatype, databyteorder); klen = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE; dlen = data_interval ? klen / 2 : klen; if (set_udata_key_valid(typeof_expr_data, dlen)) { typeof_expr_data->len = klen; - datatype_free(datatype_get(dtype)); 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); @@ -1049,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; @@ -1098,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; } @@ -1515,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; @@ -1530,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) @@ -1558,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; @@ -1577,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; diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 9241f466..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> @@ -82,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; } @@ -1734,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); @@ -2517,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)) { @@ -2720,6 +2693,51 @@ 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); @@ -2746,30 +2764,9 @@ 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); @@ -2846,8 +2843,28 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp) 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; @@ -2882,18 +2899,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp) 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); @@ -2942,7 +2948,7 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx) switch (dl->pctx.family) { case NFPROTO_IPV4: stmt->reject.family = dl->pctx.family; - datatype_set(stmt->reject.expr, &icmp_code_type); + datatype_set(stmt->reject.expr, &reject_icmp_code_type); if (stmt->reject.type == NFT_REJECT_TCP_RST && payload_dependency_exists(&dl->pdctx, PROTO_BASE_TRANSPORT_HDR)) @@ -2951,7 +2957,7 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx) break; case NFPROTO_IPV6: stmt->reject.family = dl->pctx.family; - datatype_set(stmt->reject.expr, &icmpv6_code_type); + datatype_set(stmt->reject.expr, &reject_icmpv6_code_type); if (stmt->reject.type == NFT_REJECT_TCP_RST && payload_dependency_exists(&dl->pdctx, PROTO_BASE_TRANSPORT_HDR)) @@ -2962,7 +2968,7 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx) 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; } @@ -2978,12 +2984,12 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx) 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; diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 11cf48a3..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> @@ -459,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; @@ -693,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; @@ -708,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) { @@ -793,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) @@ -806,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, @@ -1583,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"); @@ -1742,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 256514d1..3029fa58 100644 --- a/src/numgen.c +++ b/src/numgen.c @@ -8,6 +8,8 @@ * 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 7ca57ce7..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> @@ -396,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; @@ -1193,7 +1194,7 @@ static void merge_rules(const struct optimize_ctx *ctx, } if (ctx->rule[from]->comment) { - xfree(ctx->rule[from]->comment); + free_const(ctx->rule[from]->comment); ctx->rule[from]->comment = NULL; } @@ -1346,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; } @@ -6,10 +6,11 @@ * 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 c34b0c15..65eaad3e 100644 --- a/src/owner.c +++ b/src/owner.c @@ -6,9 +6,10 @@ * 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> diff --git a/src/parser_bison.y b/src/parser_bison.y index 90a2b9c3..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" @@ -330,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" @@ -395,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" @@ -645,9 +693,9 @@ int nft_lex(void *, void *, void *); %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 @@ -679,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 @@ -692,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 @@ -701,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 @@ -710,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 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 @@ -759,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 @@ -830,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 @@ -899,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 @@ -933,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 <list> timeout_states timeout_state -%destructor { xfree($$); } timeout_states timeout_state +%type <timeout_state> timeout_state +%destructor { timeout_state_free($$); } timeout_state + +%type <list> timeout_states +%destructor { timeout_states_free($$); } timeout_states %type <val> xfrm_state_key xfrm_state_proto_key xfrm_dir xfrm_spnum %type <expr> xfrm_expr @@ -1024,10 +1084,10 @@ 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 { @@ -1037,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 { @@ -1058,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 { @@ -1363,6 +1423,7 @@ delete_cmd : TABLE table_or_id_spec { $5->location = @5; handle_merge(&$3->handle, &$2); + close_scope(state); $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, $5); } | RULE ruleid_spec @@ -1716,6 +1777,18 @@ reset_cmd : COUNTERS ruleset_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 @@ -1834,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 @@ -1927,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; @@ -1936,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; @@ -1945,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; @@ -1991,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); @@ -2023,7 +2103,7 @@ chain_block : /* empty */ { $$ = $<chain>-1; } | chain_block comment_spec stmt_separator { if (already_set($1->comment, &@2, state)) { - xfree($2); + free_const($2); YYERROR; } $1->comment = $2; @@ -2039,7 +2119,7 @@ subchain_block : /* empty */ { $$ = $<chain>-1; } } ; -typeof_data_expr : primary_expr +typeof_verdict_expr : primary_expr { struct expr *e = $1; @@ -2071,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) { @@ -2096,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 - { - $1->key = $3; - $$ = $1; - } - | set_block TYPEOF typeof_expr stmt_separator + | set_block typeof_key_expr stmt_separator { - $1->key = $3; - datatype_set($1->key, $3->dtype); + if (already_set($1->key, &@2, state)) { + expr_free($2); + YYERROR; + } + + $1->key = $2; $$ = $1; } | set_block FLAGS set_flag_list stmt_separator @@ -2137,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; } @@ -2149,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; @@ -2176,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; } @@ -2187,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 @@ -2195,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; @@ -2220,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; @@ -2267,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; @@ -2292,7 +2417,7 @@ set_policy_spec : PERFORMANCE { $$ = NFT_SET_POL_PERFORMANCE; } flowtable_block_alloc : /* empty */ { - $$ = flowtable_alloc(NULL); + $$ = flowtable_alloc(&internal_location); } ; @@ -2306,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; } @@ -2354,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 { @@ -2379,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 { @@ -2407,7 +2536,7 @@ data_type_expr : data_type_atom_expr obj_block_alloc : /* empty */ { - $$ = obj_alloc(NULL); + $$ = obj_alloc(&internal_location); } ; @@ -2421,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; @@ -2438,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; @@ -2455,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; @@ -2466,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 @@ -2476,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; @@ -2493,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; @@ -2510,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; @@ -2527,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; @@ -2544,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; @@ -2565,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; @@ -2578,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; @@ -2628,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 @@ -2641,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 @@ -2654,7 +2789,7 @@ extended_prio_spec : int_num BYTEORDER_HOST_ENDIAN, strlen(str) * BITS_PER_BYTE, str); - xfree($1); + free_const($1); $$ = spec; } ; @@ -2665,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); @@ -2740,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; @@ -2749,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 ; @@ -2936,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; @@ -2973,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); } ; @@ -3003,6 +3142,60 @@ 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 @@ -3032,13 +3225,14 @@ stmt : verdict_stmt | chain_stmt | optstrip_stmt | xt_stmt close_scope_xt + | objref_stmt ; xt_stmt : XT STRING string { $$ = NULL; - xfree($2); - xfree($3); + free_const($2); + free_const($3); erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"), state->msgs); YYERROR; @@ -3121,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 @@ -3138,10 +3326,12 @@ 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; } ; @@ -3196,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; @@ -3270,7 +3460,7 @@ log_arg : PREFIX string state->msgs); } expr_free(expr); - xfree($2); + free_const($2); YYERROR; } item = variable_expr_alloc(&@$, scope, sym); @@ -3300,7 +3490,7 @@ log_arg : PREFIX string } } - xfree($2); + free_const($2); $<stmt>0->log.prefix = expr; $<stmt>0->log.flags |= STMT_LOG_PREFIX; } @@ -3353,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); } ; @@ -3422,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; } @@ -3446,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; @@ -3461,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; @@ -3471,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; } @@ -3505,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; @@ -3527,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; @@ -3556,7 +3734,7 @@ reject_with_expr : STRING { $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE, current_scope(state), $1); - xfree($1); + free_const($1); } | integer_expr { $$ = $1; } ; @@ -3571,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 { @@ -3615,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 @@ -3667,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 @@ -4220,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); } ; @@ -4235,7 +4407,7 @@ symbol_expr : variable_expr $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE, current_scope(state), $1); - xfree($1); + free_const($1); } ; @@ -4248,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); } ; @@ -4345,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); } ; @@ -4476,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; } @@ -4486,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 { @@ -4512,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; @@ -4594,7 +4772,7 @@ set_elem_stmt : COUNTER close_scope_counter uint64_t rate; erec = data_unit_parse(&@$, $4, &rate); - xfree($4); + free_const($4); if (erec != NULL) { erec_queue(erec, state->msgs); YYERROR; @@ -4627,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; @@ -4679,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; @@ -4708,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); } ; @@ -4728,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; } @@ -4762,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; @@ -4781,7 +4965,7 @@ timeout_state : STRING COLON NUM ts->timeout_value = $3; ts->location = @1; init_list_head(&ts->head); - $$ = &ts->head; + $$ = ts; } ; @@ -4799,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 { @@ -5144,7 +5328,7 @@ chain_expr : variable_expr BYTEORDER_HOST_ENDIAN, strlen($1) * BITS_PER_BYTE, $1); - xfree($1); + free_const($1); } ; @@ -5162,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; @@ -5239,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; } @@ -5480,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); @@ -5532,7 +5704,25 @@ payload_expr : payload_raw_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); @@ -5550,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); } ; @@ -5701,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 @@ -5773,8 +5965,12 @@ 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); @@ -5968,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; } diff --git a/src/parser_json.c b/src/parser_json.c index ad31b4e0..8b7efaf2 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -6,10 +6,9 @@ * later) as published by the Free Software Foundation. */ -#define _GNU_SOURCE +#include <nft.h> + #include <errno.h> -#include <stdint.h> /* needed by gmputil.h */ -#include <string.h> #include <syslog.h> #include <erec.h> @@ -541,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; @@ -554,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; @@ -572,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); @@ -616,6 +670,12 @@ static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx, 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); @@ -756,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[] = { @@ -1128,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; @@ -1462,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 }, @@ -1660,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); @@ -1714,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) { @@ -2228,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; } } @@ -2325,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) { @@ -2596,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)) @@ -2711,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) @@ -2730,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 }, @@ -2741,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 }, @@ -2785,6 +2966,45 @@ 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) { @@ -2793,6 +3013,7 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, .table.location = *int_loc, }; struct table *table = NULL; + enum table_flags flags = 0; if (json_unpack_err(ctx, root, "{s:s}", "family", &family)) @@ -2803,6 +3024,9 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, 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)) { @@ -2816,10 +3040,12 @@ 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) { + if (comment || flags) { table = table_alloc(); handle_merge(&table->handle, &h); - table->comment = xstrdup(comment); + if (comment) + table->comment = xstrdup(comment); + table->flags = flags; } if (op == CMD_ADD) @@ -2844,14 +3070,49 @@ 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, *comment = NULL; + 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}", @@ -2878,7 +3139,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, h.chain.name = xstrdup(h.chain.name); if (comment) { - chain = chain_alloc(NULL); + chain = chain_alloc(); handle_merge(&chain->handle, &h); chain->comment = xstrdup(comment); } @@ -2891,7 +3152,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, return cmd_alloc(op, obj, &h, int_loc, chain); if (!chain) - chain = chain_alloc(NULL); + chain = chain_alloc(); chain->flags |= CHAIN_F_BASECHAIN; chain->type.str = xstrdup(type); @@ -2906,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)) { @@ -3014,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; } @@ -3037,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; @@ -3051,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; @@ -3082,12 +3347,13 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, 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."); @@ -3103,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; @@ -3164,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); @@ -3208,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) @@ -3279,7 +3511,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, 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); @@ -3303,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); @@ -3320,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)) { @@ -3352,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; @@ -3475,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)) { @@ -3504,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; @@ -3520,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")) { @@ -3538,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)) { @@ -3586,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; @@ -3831,6 +4066,9 @@ static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx, { "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; diff --git a/src/payload.c b/src/payload.c index f67b5407..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> @@ -119,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++; } @@ -250,11 +248,10 @@ 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]) { @@ -429,11 +426,10 @@ 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) @@ -560,11 +556,11 @@ 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; } @@ -782,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) @@ -986,7 +1014,8 @@ 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; @@ -1121,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) { @@ -1278,6 +1308,38 @@ __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) { @@ -1336,6 +1398,16 @@ 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: @@ -1355,9 +1427,8 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr, 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; diff --git a/src/print.c b/src/print.c index 4896e13c..8aefa961 100644 --- a/src/print.c +++ b/src/print.c @@ -6,6 +6,8 @@ * 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 edf99e84..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> @@ -305,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) @@ -437,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, \ @@ -448,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) @@ -845,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), @@ -912,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) @@ -932,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), }, }; @@ -1279,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, @@ -1298,6 +1305,7 @@ static const struct proto_desc *proto_definitions[PROTO_DESC_MAX + 1] = { [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, }; @@ -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[] = { @@ -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; } @@ -1120,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); @@ -1146,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) @@ -1176,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) @@ -1203,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) @@ -1213,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) { @@ -1296,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; @@ -1323,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) @@ -1341,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) @@ -1356,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; @@ -1394,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> @@ -1448,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) @@ -1574,11 +1608,6 @@ static int do_list_table(struct netlink_ctx *ctx, struct table *table) 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; @@ -1596,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"); @@ -1614,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; @@ -1632,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, @@ -1668,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); } @@ -1686,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; } } @@ -2022,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; @@ -2048,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, @@ -2289,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); @@ -2357,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: @@ -2382,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; @@ -2400,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); @@ -2416,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); } @@ -2453,6 +2490,15 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd) 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); } @@ -2717,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; } @@ -2734,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; diff --git a/src/scanner.l b/src/scanner.l index 15ca3d46..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> @@ -586,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; } @@ -632,6 +637,9 @@ 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; } @@ -967,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; @@ -981,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: @@ -1047,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--) { @@ -1059,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; } @@ -1096,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) @@ -1191,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) @@ -1214,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 356557b4..8a149e63 100644 --- a/src/socket.c +++ b/src/socket.c @@ -8,6 +8,8 @@ * 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 72455522..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 = { @@ -268,6 +267,7 @@ 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) @@ -485,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); @@ -849,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) @@ -990,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, ":"); 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 a5815018..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; @@ -8,6 +8,8 @@ * later) as published by the Free Software Foundation. */ +#include <nft.h> + #include <nftables.h> #include <erec.h> #include <expression.h> @@ -15,7 +17,6 @@ #include <datatype.h> #include <gmputil.h> #include <utils.h> -#include <string.h> #include <netinet/ip.h> #include <linux/netfilter.h> @@ -7,9 +7,9 @@ * 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 */ @@ -78,7 +78,7 @@ void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx) rc = mt->xlate(xl, ¶ms); } - xfree(m); + free(m); break; case NFT_XT_WATCHER: case NFT_XT_TARGET: @@ -108,14 +108,14 @@ void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx) rc = tg->xlate(xl, ¶ms); } - xfree(t); + free(t); break; } if (rc == 1) nft_print(octx, "%s", xt_xlate_get(xl)); xt_xlate_free(xl); - xfree(entry); + free(entry); #endif if (!rc) nft_print(octx, "xt %s \"%s\"", @@ -124,8 +124,8 @@ void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx) void xt_stmt_destroy(struct stmt *stmt) { - xfree(stmt->xt.name); - xfree(stmt->xt.info); + free_const(stmt->xt.name); + free(stmt->xt.info); } #ifdef HAVE_LIBXTABLES @@ -348,7 +348,7 @@ err: } static struct option original_opts[] = { - { NULL }, + { }, }; static struct xtables_globals xt_nft_globals = { @@ -360,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 |