diff options
Diffstat (limited to 'src/rule.c')
-rw-r--r-- | src/rule.c | 1106 |
1 files changed, 504 insertions, 602 deletions
@@ -8,11 +8,10 @@ * Development of this code funded by Astaro AG (http://www.astaro.com/) */ +#include <nft.h> + #include <stddef.h> -#include <stdlib.h> #include <stdio.h> -#include <stdint.h> -#include <string.h> #include <inttypes.h> #include <errno.h> @@ -25,6 +24,9 @@ #include <misspell.h> #include <json.h> #include <cache.h> +#include <owner.h> +#include <intervals.h> +#include "nftutils.h" #include <libnftnl/common.h> #include <libnftnl/ruleset.h> @@ -71,10 +73,10 @@ static uint32_t tcp_dflt_timeout[] = { static uint32_t udp_dflt_timeout[] = { [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30, - [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180, + [NFTNL_CTTIMEOUT_UDP_REPLIED] = 120, }; -struct timeout_protocol timeout_protocol[IPPROTO_MAX] = { +struct timeout_protocol timeout_protocol[UINT8_MAX + 1] = { [IPPROTO_TCP] = { .array_size = NFTNL_CTTIMEOUT_TCP_MAX, .state_to_name = tcp_state_to_name, @@ -102,11 +104,11 @@ int timeout_str2num(uint16_t l4proto, struct timeout_state *ts) void handle_free(struct handle *h) { - xfree(h->table.name); - xfree(h->chain.name); - xfree(h->set.name); - xfree(h->flowtable.name); - xfree(h->obj.name); + free_const(h->table.name); + free_const(h->chain.name); + free_const(h->set.name); + free_const(h->flowtable.name); + free_const(h->obj.name); } void handle_merge(struct handle *dst, const struct handle *src) @@ -137,176 +139,6 @@ void handle_merge(struct handle *dst, const struct handle *src) dst->index = src->index; } -static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h, - struct nft_cache *cache) -{ - int ret; - - ret = netlink_list_tables(ctx, h); - if (ret < 0) - return -1; - - list_splice_tail_init(&ctx->list, &cache->list); - return 0; -} - -static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags) -{ - struct rule *rule, *nrule; - struct table *table; - struct chain *chain; - struct set *set; - int ret; - - list_for_each_entry(table, &ctx->nft->cache.list, list) { - if (flags & NFT_CACHE_SET_BIT) { - ret = netlink_list_sets(ctx, &table->handle); - list_splice_tail_init(&ctx->list, &table->sets); - if (ret < 0) - return -1; - } - if (flags & NFT_CACHE_SETELEM_BIT) { - list_for_each_entry(set, &table->sets, list) { - ret = netlink_list_setelems(ctx, &set->handle, - set); - if (ret < 0) - return -1; - } - } - if (flags & NFT_CACHE_CHAIN_BIT) { - ret = netlink_list_chains(ctx, &table->handle); - if (ret < 0) - return -1; - list_splice_tail_init(&ctx->list, &table->chains); - } - if (flags & NFT_CACHE_FLOWTABLE_BIT) { - ret = netlink_list_flowtables(ctx, &table->handle); - if (ret < 0) - return -1; - list_splice_tail_init(&ctx->list, &table->flowtables); - } - if (flags & NFT_CACHE_OBJECT_BIT) { - ret = netlink_list_objs(ctx, &table->handle); - if (ret < 0) - return -1; - list_splice_tail_init(&ctx->list, &table->objs); - } - - if (flags & NFT_CACHE_RULE_BIT) { - ret = netlink_list_rules(ctx, &table->handle); - list_for_each_entry_safe(rule, nrule, &ctx->list, list) { - chain = chain_lookup(table, &rule->handle); - list_move_tail(&rule->list, &chain->rules); - } - if (ret < 0) - return -1; - } - } - return 0; -} - -static int cache_init(struct netlink_ctx *ctx, unsigned int flags) -{ - struct handle handle = { - .family = NFPROTO_UNSPEC, - }; - int ret; - - if (flags == NFT_CACHE_EMPTY) - return 0; - - /* assume NFT_CACHE_TABLE is always set. */ - ret = cache_init_tables(ctx, &handle, &ctx->nft->cache); - if (ret < 0) - return ret; - ret = cache_init_objects(ctx, flags); - if (ret < 0) - return ret; - - return 0; -} - -static bool cache_is_complete(struct nft_cache *cache, unsigned int flags) -{ - return (cache->flags & flags) == flags; -} - -static bool cache_is_updated(struct nft_cache *cache, uint16_t genid) -{ - return genid && genid == cache->genid; -} - -bool cache_needs_update(struct nft_cache *cache) -{ - return cache->flags & NFT_CACHE_UPDATE; -} - -int cache_update(struct nft_ctx *nft, unsigned int flags, struct list_head *msgs) -{ - struct netlink_ctx ctx = { - .list = LIST_HEAD_INIT(ctx.list), - .nft = nft, - .msgs = msgs, - }; - struct nft_cache *cache = &nft->cache; - uint32_t genid, genid_stop, oldflags; - int ret; -replay: - ctx.seqnum = cache->seqnum++; - genid = mnl_genid_get(&ctx); - if (cache_is_complete(cache, flags) && - cache_is_updated(cache, genid)) - return 0; - - if (cache->genid) - cache_release(cache); - - if (flags & NFT_CACHE_FLUSHED) { - oldflags = flags; - flags = NFT_CACHE_EMPTY; - if (oldflags & NFT_CACHE_UPDATE) - flags |= NFT_CACHE_UPDATE; - goto skip; - } - - ret = cache_init(&ctx, flags); - if (ret < 0) { - cache_release(cache); - if (errno == EINTR) { - nft->nf_sock = nft_mnl_socket_reopen(nft->nf_sock); - goto replay; - } - return -1; - } - - genid_stop = mnl_genid_get(&ctx); - if (genid != genid_stop) { - cache_release(cache); - goto replay; - } -skip: - cache->genid = genid; - cache->flags = flags; - return 0; -} - -static void __cache_flush(struct list_head *table_list) -{ - struct table *table, *next; - - list_for_each_entry_safe(table, next, table_list, list) { - list_del(&table->list); - table_free(table); - } -} - -void cache_release(struct nft_cache *cache) -{ - __cache_flush(&cache->list); - cache->genid = 0; - cache->flags = NFT_CACHE_EMPTY; -} - /* internal ID to uniquely identify a set in the batch */ static uint32_t set_id; @@ -314,11 +146,15 @@ struct set *set_alloc(const struct location *loc) { struct set *set; + assert(loc); + set = xzalloc(sizeof(*set)); set->refcnt = 1; set->handle.set_id = ++set_id; - if (loc != NULL) - set->location = *loc; + set->location = *loc; + + init_list_head(&set->stmt_list); + return set; } @@ -326,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; @@ -338,6 +174,7 @@ struct set *set_clone(const struct set *set) new_set->policy = set->policy; new_set->automerge = set->automerge; new_set->desc = set->desc; + init_list_head(&new_set->stmt_list); return new_set; } @@ -350,31 +187,20 @@ struct set *set_get(struct set *set) void set_free(struct set *set) { + struct stmt *stmt, *next; + if (--set->refcnt > 0) return; - if (set->init != NULL) - expr_free(set->init); + + expr_free(set->init); + if (set->comment) + free_const(set->comment); handle_free(&set->handle); - stmt_free(set->stmt); + list_for_each_entry_safe(stmt, next, &set->stmt_list, list) + stmt_free(stmt); expr_free(set->key); expr_free(set->data); - xfree(set); -} - -void set_add_hash(struct set *set, struct table *table) -{ - list_add_tail(&set->list, &table->sets); -} - -struct set *set_lookup(const struct table *table, const char *name) -{ - struct set *set; - - list_for_each_entry(set, &table->sets, list) { - if (!strcmp(set->handle.set.name, name)) - return set; - } - return NULL; + free(set); } struct set *set_lookup_fuzzy(const char *set_name, @@ -387,14 +213,10 @@ struct set *set_lookup_fuzzy(const char *set_name, string_misspell_init(&st); - list_for_each_entry(table, &cache->list, list) { - list_for_each_entry(set, &table->sets, list) { + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + list_for_each_entry(set, &table->set_cache.list, cache.list) { if (set_is_anonymous(set->flags)) continue; - if (!strcmp(set->handle.set.name, set_name)) { - *t = table; - return set; - } if (string_misspell_update(set->handle.set.name, set_name, set, &st)) *t = table; @@ -406,17 +228,13 @@ struct set *set_lookup_fuzzy(const char *set_name, struct set *set_lookup_global(uint32_t family, const char *table, const char *name, struct nft_cache *cache) { - struct handle h; struct table *t; - h.family = family; - h.table.name = table; - - t = table_lookup(&h, cache); + t = table_cache_find(&cache->table_cache, table, family); if (t == NULL) return NULL; - return set_lookup(t, name); + return set_cache_find(t, name); } struct print_fmt_options { @@ -479,6 +297,7 @@ static void set_print_declaration(const struct set *set, struct output_ctx *octx) { const char *delim = ""; + struct stmt *stmt; const char *type; uint32_t flags; @@ -549,11 +368,19 @@ static void set_print_declaration(const struct set *set, nft_print(octx, "%s", opts->stmt_separator); } - if (set->stmt) { + if (!list_empty(&set->stmt_list)) { + unsigned int flags = octx->flags; + nft_print(octx, "%s%s", opts->tab, opts->tab); + octx->flags |= NFT_CTX_OUTPUT_STATELESS; - stmt_print(set->stmt, octx); - octx->flags &= ~NFT_CTX_OUTPUT_STATELESS; + list_for_each_entry(stmt, &set->stmt_list, list) { + stmt_print(stmt, octx); + if (!list_is_last(&stmt->list, &set->stmt_list)) + nft_print(octx, " "); + } + octx->flags = flags; + nft_print(octx, "%s", opts->stmt_separator); } @@ -571,6 +398,13 @@ static void set_print_declaration(const struct set *set, time_print(set->gc_int, octx); nft_print(octx, "%s", opts->stmt_separator); } + + if (set->comment) { + nft_print(octx, "%s%scomment \"%s\"%s", + opts->tab, opts->tab, + set->comment, + opts->stmt_separator); + } } static void do_set_print(const struct set *set, struct print_fmt_options *opts, @@ -620,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); @@ -643,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) @@ -721,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) @@ -761,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); } } @@ -843,6 +679,7 @@ static const char * const chain_hookname_str_array[] = { "postrouting", "output", "ingress", + "egress", NULL, }; @@ -858,16 +695,19 @@ const char *chain_hookname_lookup(const char *name) return NULL; } -struct chain *chain_alloc(const char *name) +/* internal ID to uniquely identify a set in the batch */ +static uint32_t chain_id; + +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; @@ -889,28 +729,29 @@ void chain_free(struct chain *chain) list_for_each_entry_safe(rule, next, &chain->rules, list) rule_free(rule); handle_free(&chain->handle); - scope_release(&chain->scope); - xfree(chain->type); + free_const(chain->type.str); expr_free(chain->dev_expr); for (i = 0; i < chain->dev_array_len; i++) - xfree(chain->dev_array[i]); - xfree(chain->dev_array); + free_const(chain->dev_array[i]); + free(chain->dev_array); expr_free(chain->priority.expr); expr_free(chain->policy); - xfree(chain); -} + free_const(chain->comment); -void chain_add_hash(struct chain *chain, struct table *table) -{ - list_add_tail(&chain->list, &table->chains); + /* MUST be released after all expressions, they could + * hold refcounts. + */ + scope_release(&chain->scope); + free(chain); } -struct chain *chain_lookup(const struct table *table, const struct handle *h) +struct chain *chain_binding_lookup(const struct table *table, + const char *chain_name) { struct chain *chain; - list_for_each_entry(chain, &table->chains, list) { - if (!strcmp(chain->handle.chain.name, h->chain.name)) + list_for_each_entry(chain, &table->chain_bindings, cache.list) { + if (!strcmp(chain->handle.chain.name, chain_name)) return chain; } return NULL; @@ -924,14 +765,13 @@ struct chain *chain_lookup_fuzzy(const struct handle *h, struct table *table; struct chain *chain; + if (!h->chain.name) + return NULL; + string_misspell_init(&st); - list_for_each_entry(table, &cache->list, list) { - list_for_each_entry(chain, &table->chains, list) { - if (!strcmp(chain->handle.chain.name, h->chain.name)) { - *t = table; - return chain; - } + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { if (string_misspell_update(chain->handle.chain.name, h->chain.name, chain, &st)) *t = table; @@ -979,6 +819,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum) return "postrouting"; case NF_INET_LOCAL_OUT: return "output"; + case NF_INET_INGRESS: + return "ingress"; default: break; }; @@ -991,6 +833,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum) return "forward"; case NF_ARP_OUT: return "output"; + case __NF_ARP_INGRESS: + return "ingress"; default: break; } @@ -999,6 +843,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum) switch (hooknum) { case NF_NETDEV_INGRESS: return "ingress"; + case NF_NETDEV_EGRESS: + return "egress"; } break; default: @@ -1024,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" }, @@ -1033,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" }, @@ -1087,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; @@ -1096,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; } } @@ -1129,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) { @@ -1147,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; } @@ -1175,12 +1026,17 @@ static void chain_print_declaration(const struct chain *chain, char priobuf[STD_PRIO_BUFSIZE]; int policy, i; + if (chain->flags & CHAIN_F_BINDING) + return; + nft_print(octx, "\tchain %s {", chain->handle.chain.name); if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id); + if (chain->comment) + nft_print(octx, "\n\t\tcomment \"%s\"", chain->comment); nft_print(octx, "\n"); if (chain->flags & CHAIN_F_BASECHAIN) { - nft_print(octx, "\t\ttype %s hook %s", chain->type, + nft_print(octx, "\t\ttype %s hook %s", chain->type.str, hooknum2str(chain->handle.family, chain->hook.num)); if (chain->dev_array_len == 1) { nft_print(octx, " device \"%s\"", chain->dev_array[0]); @@ -1210,17 +1066,28 @@ static void chain_print_declaration(const struct chain *chain, } } -static void chain_print(const struct chain *chain, struct output_ctx *octx) +void chain_rules_print(const struct chain *chain, struct output_ctx *octx, + const char *indent) { + unsigned int flags = octx->flags; struct rule *rule; - chain_print_declaration(chain, octx); + if (chain->flags & CHAIN_F_BINDING) + octx->flags &= ~NFT_CTX_OUTPUT_HANDLE; list_for_each_entry(rule, &chain->rules, list) { - nft_print(octx, "\t\t"); + nft_print(octx, "\t\t%s", indent ? : ""); rule_print(rule, octx); nft_print(octx, "\n"); } + + octx->flags = flags; +} + +static void chain_print(const struct chain *chain, struct output_ctx *octx) +{ + chain_print_declaration(chain, octx); + chain_rules_print(chain, octx, NULL); nft_print(octx, "\t}\n"); } @@ -1235,8 +1102,21 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx) if (chain->flags & CHAIN_F_BASECHAIN) { mpz_export_data(&policy, chain->policy->value, BYTEORDER_HOST_ENDIAN, sizeof(int)); - nft_print(octx, " { type %s hook %s priority %s; policy %s; }", - chain->type, chain->hook.name, + nft_print(octx, " { type %s hook %s ", + chain->type.str, chain->hook.name); + + if (chain->dev_array_len > 0) { + int i; + + nft_print(octx, "devices = { "); + for (i = 0; i < chain->dev_array_len; i++) { + nft_print(octx, "%s", chain->dev_array[i]); + if (i + 1 != chain->dev_array_len) + nft_print(octx, ", "); + } + nft_print(octx, " } "); + } + nft_print(octx, "priority %s; policy %s; }", prio2str(octx, priobuf, sizeof(priobuf), chain->handle.family, chain->hook.num, chain->priority.expr), @@ -1251,13 +1131,20 @@ 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); init_list_head(&table->flowtables); + init_list_head(&table->chain_bindings); init_list_head(&table->scope.symbols); table->refcnt = 1; + cache_init(&table->chain_cache); + cache_init(&table->set_cache); + cache_init(&table->obj_cache); + cache_init(&table->ft_cache); + return table; } @@ -1270,17 +1157,38 @@ void table_free(struct table *table) if (--table->refcnt > 0) return; + if (table->comment) + free_const(table->comment); list_for_each_entry_safe(chain, next, &table->chains, list) chain_free(chain); + list_for_each_entry_safe(chain, next, &table->chain_bindings, cache.list) + chain_free(chain); + /* this is implicitly releasing chains in the hashtable cache */ + list_for_each_entry_safe(chain, next, &table->chain_cache.list, cache.list) + chain_free(chain); list_for_each_entry_safe(set, nset, &table->sets, list) set_free(set); + /* this is implicitly releasing sets in the hashtable cache */ + list_for_each_entry_safe(set, nset, &table->set_cache.list, cache.list) + set_free(set); list_for_each_entry_safe(ft, nft, &table->flowtables, list) flowtable_free(ft); + /* this is implicitly releasing flowtables in the hashtable cache */ + list_for_each_entry_safe(ft, nft, &table->ft_cache.list, cache.list) + flowtable_free(ft); list_for_each_entry_safe(obj, nobj, &table->objs, list) obj_free(obj); + /* this is implicitly releasing objs in the hashtable cache */ + list_for_each_entry_safe(obj, nobj, &table->obj_cache.list, cache.list) + obj_free(obj); + handle_free(&table->handle); scope_release(&table->scope); - xfree(table); + cache_free(&table->chain_cache); + cache_free(&table->set_cache); + cache_free(&table->obj_cache); + cache_free(&table->ft_cache); + free(table); } struct table *table_get(struct table *table) @@ -1289,24 +1197,6 @@ struct table *table_get(struct table *table) return table; } -void table_add_hash(struct table *table, struct nft_cache *cache) -{ - list_add_tail(&table->list, &cache->list); -} - -struct table *table_lookup(const struct handle *h, - const struct nft_cache *cache) -{ - struct table *table; - - list_for_each_entry(table, &cache->list, list) { - if (table->handle.family == h->family && - !strcmp(table->handle.table.name, h->table.name)) - return table; - } - return NULL; -} - struct table *table_lookup_fuzzy(const struct handle *h, const struct nft_cache *cache) { @@ -1315,39 +1205,60 @@ struct table *table_lookup_fuzzy(const struct handle *h, string_misspell_init(&st); - list_for_each_entry(table, &cache->list, list) { - if (!strcmp(table->handle.table.name, h->table.name)) - return table; - + list_for_each_entry(table, &cache->table_cache.list, cache.list) { string_misspell_update(table->handle.table.name, h->table.name, table, &st); } return st.obj; } -const char *table_flags_name[TABLE_FLAGS_MAX] = { +static const char *table_flags_name[TABLE_FLAGS_MAX] = { "dormant", + "owner", + "persist", }; -static void table_print_options(const struct table *table, const char **delim, - struct output_ctx *octx) +const char *table_flag_name(uint32_t flag) +{ + if (flag >= TABLE_FLAGS_MAX) + return "unknown"; + + return table_flags_name[flag]; +} + +unsigned int parse_table_flag(const char *name) +{ + int i; + + for (i = 0; i < TABLE_FLAGS_MAX; i++) { + if (!strcmp(name, table_flags_name[i])) + return 1 << i; + } + return 0; +} + +static void table_print_flags(const struct table *table, const char **delim, + struct output_ctx *octx) { uint32_t flags = table->flags; + bool comma = false; int i; - if (flags) { - nft_print(octx, "\tflags "); + if (!table->flags) + return; - for (i = 0; i < TABLE_FLAGS_MAX; i++) { - if (flags & 0x1) - nft_print(octx, "%s", table_flags_name[i]); - flags >>= 1; - if (flags) + nft_print(octx, "\tflags "); + for (i = 0; i < TABLE_FLAGS_MAX; i++) { + if (flags & (1 << i)) { + if (comma) nft_print(octx, ","); + + nft_print(octx, "%s", table_flag_name(i)); + comma = true; } - nft_print(octx, "\n"); - *delim = "\n"; } + nft_print(octx, "\n"); + *delim = "\n"; } static void table_print(const struct table *table, struct output_ctx *octx) @@ -1359,30 +1270,43 @@ static void table_print(const struct table *table, struct output_ctx *octx) const char *delim = ""; const char *family = family2str(table->handle.family); + if (table->has_xt_stmts) + fprintf(octx->error_fp, + "# Warning: table %s %s is managed by iptables-nft, do not touch!\n", + family, table->handle.table.name); + nft_print(octx, "table %s %s {", family, table->handle.table.name); + if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER) + nft_print(octx, " #"); if (nft_output_handle(octx)) - nft_print(octx, " # handle %" PRIu64, table->handle.handle.id); + nft_print(octx, " handle %" PRIu64, table->handle.handle.id); + if (table->flags & TABLE_F_OWNER) + nft_print(octx, " progname %s", get_progname(table->owner)); + nft_print(octx, "\n"); - table_print_options(table, &delim, octx); + table_print_flags(table, &delim, octx); - list_for_each_entry(obj, &table->objs, list) { + if (table->comment) + nft_print(octx, "\tcomment \"%s\"\n", table->comment); + + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { nft_print(octx, "%s", delim); obj_print(obj, octx); delim = "\n"; } - list_for_each_entry(set, &table->sets, list) { + list_for_each_entry(set, &table->set_cache.list, cache.list) { if (set_is_anonymous(set->flags)) continue; nft_print(octx, "%s", delim); set_print(set, octx); delim = "\n"; } - list_for_each_entry(flowtable, &table->flowtables, list) { + list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) { nft_print(octx, "%s", delim); flowtable_print(flowtable, octx); delim = "\n"; } - list_for_each_entry(chain, &table->chains, list) { + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { nft_print(octx, "%s", delim); chain_print(chain, octx); delim = "\n"; @@ -1396,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; @@ -1403,95 +1329,12 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj, cmd->handle = *h; cmd->location = *loc; cmd->data = data; - return cmd; -} + cmd->attr = xzalloc_array(NFT_NLATTR_LOC_MAX, + sizeof(struct nlerr_loc)); + cmd->attr_array_len = NFT_NLATTR_LOC_MAX; + init_list_head(&cmd->collapse_list); -void cmd_add_loc(struct cmd *cmd, uint16_t offset, struct location *loc) -{ - assert(cmd->num_attrs < NFT_NLATTR_LOC_MAX); - cmd->attr[cmd->num_attrs].offset = offset; - cmd->attr[cmd->num_attrs].location = loc; - cmd->num_attrs++; -} - -void nft_cmd_expand(struct cmd *cmd) -{ - struct list_head new_cmds; - struct set *set, *newset; - struct flowtable *ft; - struct table *table; - struct chain *chain; - struct rule *rule; - struct obj *obj; - struct cmd *new; - struct handle h; - - init_list_head(&new_cmds); - - switch (cmd->obj) { - case CMD_OBJ_TABLE: - table = cmd->table; - if (!table) - return; - - list_for_each_entry(chain, &table->chains, list) { - memset(&h, 0, sizeof(h)); - handle_merge(&h, &chain->handle); - new = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h, - &chain->location, chain_get(chain)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(obj, &table->objs, list) { - handle_merge(&obj->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &obj->handle); - new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h, - &obj->location, obj_get(obj)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(set, &table->sets, list) { - handle_merge(&set->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &set->handle); - new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h, - &set->location, set_get(set)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(ft, &table->flowtables, list) { - handle_merge(&ft->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &ft->handle); - new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h, - &ft->location, flowtable_get(ft)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(chain, &table->chains, list) { - list_for_each_entry(rule, &chain->rules, list) { - memset(&h, 0, sizeof(h)); - handle_merge(&h, &rule->handle); - new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h, - &rule->location, - rule_get(rule)); - list_add_tail(&new->list, &new_cmds); - } - } - list_splice(&new_cmds, &cmd->list); - break; - case CMD_OBJ_SET: - set = cmd->set; - memset(&h, 0, sizeof(h)); - handle_merge(&h, &set->handle); - newset = set_clone(set); - newset->handle.set_id = set->handle.set_id; - newset->init = set->init; - set->init = NULL; - new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h, - &set->location, newset); - list_add(&new->list, &cmd->list); - break; - default: - break; - } + return cmd; } struct markup *markup_alloc(uint32_t format) @@ -1506,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) @@ -1524,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) @@ -1535,8 +1378,12 @@ void cmd_free(struct cmd *cmd) switch (cmd->obj) { case CMD_OBJ_ELEMENTS: expr_free(cmd->expr); + if (cmd->elem.set) + set_free(cmd->elem.set); break; case CMD_OBJ_SET: + case CMD_OBJ_MAP: + case CMD_OBJ_METER: case CMD_OBJ_SETELEMS: set_free(cmd->set); break; @@ -1575,70 +1422,66 @@ void cmd_free(struct cmd *cmd) BUG("invalid command object type %u\n", cmd->obj); } } - xfree(cmd->arg); - xfree(cmd); + free(cmd->attr); + free_const(cmd->arg); + free(cmd); } #include <netlink.h> #include <mnl.h> -static int __do_add_setelems(struct netlink_ctx *ctx, struct set *set, - struct expr *expr, uint32_t flags) +static int __do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd, + struct set *set, struct expr *expr, uint32_t flags) { expr->set_flags |= set->flags; - if (mnl_nft_setelem_add(ctx, set, expr, flags) < 0) + if (mnl_nft_setelem_add(ctx, cmd, set, expr, flags) < 0) return -1; - if (!set_is_anonymous(set->flags) && - set->init != NULL && set->init != expr && - set->flags & NFT_SET_INTERVAL && - set->desc.field_count <= 1) { - interval_map_decompose(expr); - list_splice_tail_init(&expr->expressions, &set->init->expressions); - set->init->size += expr->size; - expr->size = 0; - } - return 0; } -static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, +static int do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t flags) { - struct handle *h = &cmd->handle; struct expr *init = cmd->expr; - struct table *table; - struct set *set; - - table = table_lookup(h, &ctx->nft->cache); - set = set_lookup(table, h->set.name); + struct set *set = cmd->elem.set; if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, init, true, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, init, true) < 0) return -1; - return __do_add_setelems(ctx, set, init, flags); + return __do_add_elements(ctx, cmd, set, init, flags); +} + +static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, + uint32_t flags) +{ + struct set *set = cmd->set; + + return __do_add_elements(ctx, cmd, set, set->init, flags); } static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd, - uint32_t flags, bool add) + uint32_t flags) { struct set *set = cmd->set; if (set->init != NULL) { + /* Update set->init->size (NFTNL_SET_DESC_SIZE) before adding + * the set to the kernel. Calling this from do_add_setelems() + * comes too late which might result in spurious ENFILE errors. + */ if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, set->init, true, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, set->init, true) < 0) return -1; } - if (add && mnl_nft_set_add(ctx, cmd, flags) < 0) + + if (mnl_nft_set_add(ctx, cmd, flags) < 0) return -1; - if (set->init != NULL) { - return __do_add_setelems(ctx, set, set->init, flags); - } + + if (set_is_anonymous(set->flags)) + return __do_add_elements(ctx, cmd, set, set->init, flags); + return 0; } @@ -1657,11 +1500,11 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl) case CMD_OBJ_RULE: return mnl_nft_rule_add(ctx, cmd, flags | NLM_F_APPEND); case CMD_OBJ_SET: - return do_add_set(ctx, cmd, flags, true); + return do_add_set(ctx, cmd, flags); case CMD_OBJ_SETELEMS: - return do_add_set(ctx, cmd, flags, false); - case CMD_OBJ_ELEMENTS: return do_add_setelems(ctx, cmd, flags); + case CMD_OBJ_ELEMENTS: + return do_add_elements(ctx, cmd, flags); case CMD_OBJ_COUNTER: case CMD_OBJ_QUOTA: case CMD_OBJ_CT_HELPER: @@ -1708,21 +1551,14 @@ static int do_command_insert(struct netlink_ctx *ctx, struct cmd *cmd) static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd) { - struct handle *h = &cmd->handle; - struct expr *expr = cmd->expr; - struct table *table; - struct set *set; - - table = table_lookup(h, &ctx->nft->cache); - set = set_lookup(table, h->set.name); + struct expr *expr = cmd->elem.expr; + struct set *set = cmd->elem.set; if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, expr, false, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, expr, false) < 0) return -1; - if (mnl_nft_setelem_del(ctx, cmd) < 0) + if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0) return -1; return 0; @@ -1764,8 +1600,7 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd) } } -static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd, - struct table *table) +static int do_list_table(struct netlink_ctx *ctx, struct table *table) { table_print(table, &ctx->nft->output); return 0; @@ -1773,15 +1608,10 @@ static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd, static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd) { - struct print_fmt_options opts = { - .tab = "\t", - .nl = "\n", - .stmt_separator = "\n", - }; struct table *table; struct set *set; - list_for_each_entry(table, &ctx->nft->cache.list, list) { + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { if (cmd->handle.family != NFPROTO_UNSPEC && cmd->handle.family != table->handle.family) continue; @@ -1790,18 +1620,17 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd) family2str(table->handle.family), table->handle.table.name); - list_for_each_entry(set, &table->sets, list) { + list_for_each_entry(set, &table->set_cache.list, cache.list) { if (cmd->obj == CMD_OBJ_SETS && !set_is_literal(set->flags)) continue; if (cmd->obj == CMD_OBJ_METERS && - !set_is_meter(set->flags)) + !set_is_meter_compat(set->flags)) continue; if (cmd->obj == CMD_OBJ_MAPS && !map_is_literal(set->flags)) continue; - set_print_declaration(set, &opts, &ctx->nft->output); - nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl); + set_print(set, &ctx->nft->output); } nft_print(&ctx->nft->output, "}\n"); @@ -1813,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; @@ -1831,26 +1661,18 @@ void obj_free(struct obj *obj) { if (--obj->refcnt > 0) return; + free_const(obj->comment); handle_free(&obj->handle); - xfree(obj); -} - -void obj_add_hash(struct obj *obj, struct table *table) -{ - list_add_tail(&obj->list, &table->objs); -} + if (obj->type == NFT_OBJECT_CT_TIMEOUT) { + struct timeout_state *ts, *next; -struct obj *obj_lookup(const struct table *table, const char *name, - uint32_t type) -{ - struct obj *obj; - - list_for_each_entry(obj, &table->objs, list) { - if (!strcmp(obj->handle.obj.name, name) && - obj->type == type) - return obj; + list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) { + list_del(&ts->head); + free_const(ts->timeout_str); + free(ts); + } } - return NULL; + free(obj); } struct obj *obj_lookup_fuzzy(const char *obj_name, @@ -1863,12 +1685,8 @@ struct obj *obj_lookup_fuzzy(const char *obj_name, string_misspell_init(&st); - list_for_each_entry(table, &cache->list, list) { - list_for_each_entry(obj, &table->objs, list) { - if (!strcmp(obj->handle.obj.name, obj_name)) { - *t = table; - return obj; - } + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { if (string_misspell_update(obj->handle.obj.name, obj_name, obj, &st)) *t = table; @@ -1879,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); } @@ -1897,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; } } @@ -1924,6 +1745,16 @@ static const char *synproxy_timestamp_to_str(const uint32_t flags) return ""; } +static void obj_print_comment(const struct obj *obj, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + if (obj->comment) + nft_print(octx, "%s%s%scomment \"%s\"", + opts->nl, opts->tab, opts->tab, + obj->comment); +} + static void obj_print_data(const struct obj *obj, struct print_fmt_options *opts, struct output_ctx *octx) @@ -1933,13 +1764,14 @@ static void obj_print_data(const struct obj *obj, nft_print(octx, " %s {", obj->handle.obj.name); if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); - nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); - if (nft_output_stateless(octx)) { - nft_print(octx, "packets 0 bytes 0"); - break; - } - nft_print(octx, "packets %" PRIu64 " bytes %" PRIu64 "%s", - obj->counter.packets, obj->counter.bytes, opts->nl); + + obj_print_comment(obj, opts, octx); + if (nft_output_stateless(octx)) + nft_print(octx, "%s", opts->nl); + else + nft_print(octx, "%s%s%spackets %" PRIu64 " bytes %" PRIu64 "%s", + opts->nl, opts->tab, opts->tab, + obj->counter.packets, obj->counter.bytes, opts->nl); break; case NFT_OBJECT_QUOTA: { const char *data_unit; @@ -1948,6 +1780,8 @@ static void obj_print_data(const struct obj *obj, nft_print(octx, " %s {", obj->handle.obj.name); if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); data_unit = get_rate(obj->quota.bytes, &bytes); nft_print(octx, "%s%" PRIu64 " %s", @@ -1965,6 +1799,8 @@ static void obj_print_data(const struct obj *obj, nft_print(octx, " %s {", obj->handle.obj.name); if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); nft_print(octx, "\"%s\"%s", obj->secmark.ctx, opts->nl); break; @@ -1972,6 +1808,8 @@ static void obj_print_data(const struct obj *obj, nft_print(octx, " %s {", obj->handle.obj.name); if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); nft_print(octx, "%s", opts->nl); nft_print(octx, "%s%stype \"%s\" protocol ", opts->tab, opts->tab, obj->ct_helper.name); @@ -1986,6 +1824,8 @@ static void obj_print_data(const struct obj *obj, nft_print(octx, " %s {", obj->handle.obj.name); if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); nft_print(octx, "%s", opts->nl); nft_print(octx, "%s%sprotocol ", opts->tab, opts->tab); print_proto_name_proto(obj->ct_timeout.l4proto, octx); @@ -2001,6 +1841,8 @@ static void obj_print_data(const struct obj *obj, nft_print(octx, " %s {", obj->handle.obj.name); if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); nft_print(octx, "%s", opts->nl); nft_print(octx, "%s%sprotocol ", opts->tab, opts->tab); print_proto_name_proto(obj->ct_expect.l4proto, octx); @@ -2029,6 +1871,8 @@ static void obj_print_data(const struct obj *obj, nft_print(octx, " %s {", obj->handle.obj.name); if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); switch (obj->limit.type) { case NFT_LIMIT_PKTS: @@ -2066,6 +1910,8 @@ static void obj_print_data(const struct obj *obj, if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + obj_print_comment(obj, opts, octx); + if (flags & NF_SYNPROXY_OPT_MSS) { nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); nft_print(octx, "mss %u", obj->synproxy.mss); @@ -2098,7 +1944,7 @@ static const char * const obj_type_name_array[] = { [NFT_OBJECT_CT_EXPECT] = "ct expectation", }; -const char *obj_type_name(enum stmt_types type) +const char *obj_type_name(unsigned int type) { assert(type <= NFT_OBJECT_MAX && obj_type_name_array[type]); @@ -2116,7 +1962,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = { [NFT_OBJECT_CT_EXPECT] = CMD_OBJ_CT_EXPECT, }; -uint32_t obj_type_to_cmd(uint32_t type) +enum cmd_obj obj_type_to_cmd(uint32_t type) { assert(type <= NFT_OBJECT_MAX && obj_type_cmd_array[type]); @@ -2174,7 +2020,7 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type) struct table *table; struct obj *obj; - list_for_each_entry(table, &ctx->nft->cache.list, list) { + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { if (cmd->handle.family != NFPROTO_UNSPEC && cmd->handle.family != table->handle.family) continue; @@ -2183,14 +2029,14 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type) strcmp(cmd->handle.table.name, table->handle.table.name)) continue; - if (list_empty(&table->objs)) + if (list_empty(&table->obj_cache.list)) continue; nft_print(&ctx->nft->output, "table %s %s {\n", family2str(table->handle.family), table->handle.table.name); - list_for_each_entry(obj, &table->objs, list) { + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { if (obj->type != type || (cmd->handle.obj.name != NULL && strcmp(cmd->handle.obj.name, obj->handle.obj.name))) @@ -2208,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; @@ -2234,15 +2081,10 @@ void flowtable_free(struct flowtable *flowtable) if (flowtable->dev_array != NULL) { for (i = 0; i < flowtable->dev_array_len; i++) - xfree(flowtable->dev_array[i]); - xfree(flowtable->dev_array); + free_const(flowtable->dev_array[i]); + free(flowtable->dev_array); } - xfree(flowtable); -} - -void flowtable_add_hash(struct flowtable *flowtable, struct table *table) -{ - list_add_tail(&flowtable->list, &table->flowtables); + free(flowtable); } static void flowtable_print_declaration(const struct flowtable *flowtable, @@ -2272,13 +2114,19 @@ static void flowtable_print_declaration(const struct flowtable *flowtable, flowtable->hook.num, flowtable->priority.expr), opts->stmt_separator); - nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab); - for (i = 0; i < flowtable->dev_array_len; i++) { - nft_print(octx, "%s", flowtable->dev_array[i]); - if (i + 1 != flowtable->dev_array_len) - nft_print(octx, ", "); + if (flowtable->dev_array_len > 0) { + nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab); + for (i = 0; i < flowtable->dev_array_len; i++) { + nft_print(octx, "%s", flowtable->dev_array[i]); + if (i + 1 != flowtable->dev_array_len) + nft_print(octx, ", "); + } + nft_print(octx, " }%s", opts->stmt_separator); } - nft_print(octx, " }%s", opts->stmt_separator); + + if (flowtable->flags & NFT_FLOWTABLE_HW_OFFLOAD) + nft_print(octx, "%s%sflags offload%s", opts->tab, opts->tab, + opts->stmt_separator); if (flowtable->flags & NFT_FLOWTABLE_COUNTER) nft_print(octx, "%s%scounter%s", opts->tab, opts->tab, @@ -2304,17 +2152,6 @@ void flowtable_print(const struct flowtable *s, struct output_ctx *octx) do_flowtable_print(s, &opts, octx); } -struct flowtable *flowtable_lookup(const struct table *table, const char *name) -{ - struct flowtable *ft; - - list_for_each_entry(ft, &table->flowtables, list) { - if (!strcmp(ft->handle.flowtable.name, name)) - return ft; - } - return NULL; -} - struct flowtable *flowtable_lookup_fuzzy(const char *ft_name, const struct nft_cache *cache, const struct table **t) @@ -2325,12 +2162,8 @@ struct flowtable *flowtable_lookup_fuzzy(const char *ft_name, string_misspell_init(&st); - list_for_each_entry(table, &cache->list, list) { - list_for_each_entry(ft, &table->flowtables, list) { - if (!strcmp(ft->handle.flowtable.name, ft_name)) { - *t = table; - return ft; - } + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + list_for_each_entry(ft, &table->ft_cache.list, cache.list) { if (string_misspell_update(ft->handle.flowtable.name, ft_name, ft, &st)) *t = table; @@ -2344,8 +2177,8 @@ static int do_list_flowtable(struct netlink_ctx *ctx, struct cmd *cmd, { struct flowtable *ft; - ft = flowtable_lookup(table, cmd->handle.flowtable.name); - if (ft == NULL) + ft = ft_cache_find(table, cmd->handle.flowtable.name); + if (!ft) return -1; nft_print(&ctx->nft->output, "table %s %s {\n", @@ -2368,7 +2201,7 @@ static int do_list_flowtables(struct netlink_ctx *ctx, struct cmd *cmd) struct flowtable *flowtable; struct table *table; - list_for_each_entry(table, &ctx->nft->cache.list, list) { + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { if (cmd->handle.family != NFPROTO_UNSPEC && cmd->handle.family != table->handle.family) continue; @@ -2377,7 +2210,7 @@ static int do_list_flowtables(struct netlink_ctx *ctx, struct cmd *cmd) family2str(table->handle.family), table->handle.table.name); - list_for_each_entry(flowtable, &table->flowtables, list) { + list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) { flowtable_print_declaration(flowtable, &opts, &ctx->nft->output); nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl); } @@ -2392,20 +2225,15 @@ static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd) unsigned int family = cmd->handle.family; struct table *table; - list_for_each_entry(table, &ctx->nft->cache.list, list) { + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { if (family != NFPROTO_UNSPEC && table->handle.family != family) continue; - cmd->handle.family = table->handle.family; - cmd->handle.table.name = table->handle.table.name; - - if (do_list_table(ctx, cmd, table) < 0) + if (do_list_table(ctx, table) < 0) return -1; } - cmd->handle.table.name = NULL; - return 0; } @@ -2413,7 +2241,7 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd) { struct table *table; - list_for_each_entry(table, &ctx->nft->cache.list, list) { + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { if (cmd->handle.family != NFPROTO_UNSPEC && cmd->handle.family != table->handle.family) continue; @@ -2429,9 +2257,14 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd) static void table_print_declaration(struct table *table, struct output_ctx *octx) { - nft_print(octx, "table %s %s {\n", - family2str(table->handle.family), - table->handle.table.name); + const char *family = family2str(table->handle.family); + + if (table->has_xt_stmts) + fprintf(octx->error_fp, + "# Warning: table %s %s is managed by iptables-nft, do not touch!\n", + family, table->handle.table.name); + + nft_print(octx, "table %s %s {\n", family, table->handle.table.name); } static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd, @@ -2441,13 +2274,9 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd, table_print_declaration(table, &ctx->nft->output); - list_for_each_entry(chain, &table->chains, list) { - if (chain->handle.family != cmd->handle.family || - strcmp(cmd->handle.chain.name, chain->handle.chain.name) != 0) - continue; - + chain = chain_cache_find(table, cmd->handle.chain.name); + if (chain) chain_print(chain, &ctx->nft->output); - } nft_print(&ctx->nft->output, "}\n"); @@ -2459,14 +2288,14 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd) struct table *table; struct chain *chain; - list_for_each_entry(table, &ctx->nft->cache.list, list) { + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { if (cmd->handle.family != NFPROTO_UNSPEC && cmd->handle.family != table->handle.family) continue; table_print_declaration(table, &ctx->nft->output); - list_for_each_entry(chain, &table->chains, list) { + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { chain_print_declaration(chain, &ctx->nft->output); nft_print(&ctx->nft->output, "\t}\n"); } @@ -2477,9 +2306,15 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd) } static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, - struct table *table, struct set *set) + struct set *set) { + struct table *table = table_alloc(); + + table->handle.table.name = xstrdup(cmd->handle.table.name); + table->handle.family = cmd->handle.family; table_print_declaration(table, &ctx->nft->output); + table_free(table); + set_print(set, &ctx->nft->output); nft_print(&ctx->nft->output, "}\n"); } @@ -2487,17 +2322,30 @@ static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, struct table *table) { - struct set *set; + struct set *set = cmd->set; - set = set_lookup(table, cmd->handle.set.name); - if (set == NULL) - return -1; + if (!set) { + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return -1; + } - __do_list_set(ctx, cmd, table, set); + __do_list_set(ctx, cmd, set); return 0; } +static int do_list_hooks(struct netlink_ctx *ctx, struct cmd *cmd) +{ + const char *devname = cmd->handle.obj.name; + int hooknum = -1; + + if (cmd->handle.chain.name) + hooknum = cmd->handle.chain_id; + + return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, hooknum, devname); +} + static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) { struct table *table = NULL; @@ -2506,13 +2354,14 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) return do_command_list_json(ctx, cmd); if (cmd->handle.table.name != NULL) - table = table_lookup(&cmd->handle, &ctx->nft->cache); - + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); switch (cmd->obj) { case CMD_OBJ_TABLE: if (!cmd->handle.table.name) return do_list_tables(ctx, cmd); - return do_list_table(ctx, cmd, table); + return do_list_table(ctx, table); case CMD_OBJ_CHAIN: return do_list_chain(ctx, cmd, table); case CMD_OBJ_CHAINS: @@ -2522,6 +2371,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_SET: return do_list_set(ctx, cmd, table); case CMD_OBJ_RULESET: + case CMD_OBJ_RULES: + case CMD_OBJ_RULE: return do_list_ruleset(ctx, cmd); case CMD_OBJ_METERS: return do_list_sets(ctx, cmd); @@ -2541,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: @@ -2557,6 +2410,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) return do_list_flowtable(ctx, cmd, table); case CMD_OBJ_FLOWTABLES: return do_list_flowtables(ctx, cmd); + case CMD_OBJ_HOOKS: + return do_list_hooks(ctx, cmd); default: BUG("invalid command object type %u\n", cmd->obj); } @@ -2564,14 +2419,13 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) return 0; } -static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, - struct table *table) +static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, bool reset) { struct set *set, *new_set; struct expr *init; int err; - set = set_lookup(table, cmd->handle.set.name); + set = cmd->elem.set; /* Create a list of elements based of what we got from command line. */ if (set_is_non_concat_range(set)) @@ -2583,9 +2437,9 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, /* Fetch from kernel the elements that have been requested .*/ err = netlink_get_setelem(ctx, &cmd->handle, &cmd->location, - table, new_set, init); + cmd->elem.set, new_set, init, reset); if (err >= 0) - __do_list_set(ctx, cmd, table, new_set); + __do_list_set(ctx, cmd, new_set); if (set_is_non_concat_range(set)) expr_free(init); @@ -2597,14 +2451,9 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd) { - struct table *table = NULL; - - if (cmd->handle.table.name != NULL) - table = table_lookup(&cmd->handle, &ctx->nft->cache); - switch (cmd->obj) { case CMD_OBJ_ELEMENTS: - return do_get_setelems(ctx, cmd, table); + return do_get_setelems(ctx, cmd, false); default: BUG("invalid command object type %u\n", cmd->obj); } @@ -2633,20 +2482,41 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_QUOTA: type = NFT_OBJECT_QUOTA; break; + case CMD_OBJ_RULES: + ret = netlink_reset_rules(ctx, cmd, true); + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); + case CMD_OBJ_RULE: + return netlink_reset_rules(ctx, cmd, false); + case CMD_OBJ_ELEMENTS: + return do_get_setelems(ctx, cmd, true); + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + ret = netlink_list_setelems(ctx, &cmd->handle, cmd->set, true); + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); default: BUG("invalid command object type %u\n", cmd->obj); } ret = netlink_reset_objs(ctx, cmd, type, dump); list_for_each_entry_safe(obj, next, &ctx->list, list) { - table = table_lookup(&obj->handle, &ctx->nft->cache); - if (!obj_lookup(table, obj->handle.obj.name, obj->type)) - list_move(&obj->list, &table->objs); + table = table_cache_find(&ctx->nft->cache.table_cache, + obj->handle.table.name, + obj->handle.family); + if (!obj_cache_find(table, obj->handle.obj.name, obj->type)) { + list_del(&obj->list); + obj_cache_add(obj, table); + } } if (ret < 0) return ret; - return do_list_obj(ctx, cmd, type); + return do_command_list(ctx, cmd); } static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd) @@ -2669,12 +2539,14 @@ static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd) static int do_command_rename(struct netlink_ctx *ctx, struct cmd *cmd) { - struct table *table = table_lookup(&cmd->handle, &ctx->nft->cache); + struct table *table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); const struct chain *chain; switch (cmd->obj) { case CMD_OBJ_CHAIN: - chain = chain_lookup(table, &cmd->handle); + chain = chain_cache_find(table, cmd->handle.chain.name); return mnl_nft_chain_rename(ctx, cmd, chain); default: @@ -2744,6 +2616,7 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_REPLACE: return do_command_replace(ctx, cmd); case CMD_DELETE: + case CMD_DESTROY: return do_command_delete(ctx, cmd); case CMD_GET: return do_command_get(ctx, cmd); @@ -2859,49 +2732,78 @@ static void payload_do_merge(struct stmt *sa[], unsigned int n) } /** - * payload_try_merge - try to merge consecutive payload match statements + * stmt_reduce - reduce statements in rule * * @rule: nftables rule * + * This function aims to: + * + * - remove redundant statement, e.g. remove 'meta protocol ip' if family is ip + * - merge consecutive payload match statements + * * Locate sequences of payload match statements referring to adjacent * header locations and merge those using only equality relations. * * As a side-effect, payload match statements are ordered in ascending * order according to the location of the payload. */ -static void payload_try_merge(const struct rule *rule) +static void stmt_reduce(const struct rule *rule) { + struct stmt *stmt, *dstmt = NULL, *next; struct stmt *sa[rule->num_stmts]; - struct stmt *stmt, *next; unsigned int idx = 0; list_for_each_entry_safe(stmt, next, &rule->stmts, list) { + /* delete this redundant statement */ + if (dstmt) { + list_del(&dstmt->list); + stmt_free(dstmt); + dstmt = NULL; + } + /* Must not merge across other statements */ - if (stmt->ops->type != STMT_EXPRESSION) - goto do_merge; + if (stmt->ops->type != STMT_EXPRESSION) { + if (idx >= 2) + payload_do_merge(sa, idx); + idx = 0; + continue; + } if (stmt->expr->etype != EXPR_RELATIONAL) continue; - if (stmt->expr->left->etype != EXPR_PAYLOAD) - continue; if (stmt->expr->right->etype != EXPR_VALUE) continue; - switch (stmt->expr->op) { - case OP_EQ: - case OP_IMPLICIT: - case OP_NEQ: - break; - default: - continue; - } - sa[idx++] = stmt; - continue; -do_merge: - if (idx < 2) - continue; - payload_do_merge(sa, idx); - idx = 0; + if (stmt->expr->left->etype == EXPR_PAYLOAD) { + switch (stmt->expr->op) { + case OP_EQ: + case OP_IMPLICIT: + break; + default: + continue; + } + + sa[idx++] = stmt; + } else if (stmt->expr->left->etype == EXPR_META) { + switch (stmt->expr->op) { + case OP_EQ: + case OP_IMPLICIT: + if (stmt->expr->left->meta.key == NFT_META_PROTOCOL && + !stmt->expr->left->meta.inner_desc) { + uint16_t protocol; + + protocol = mpz_get_uint16(stmt->expr->right->value); + if ((rule->handle.family == NFPROTO_IPV4 && + protocol == ETH_P_IP) || + (rule->handle.family == NFPROTO_IPV6 && + protocol == ETH_P_IPV6)) + dstmt = stmt; + } + break; + default: + break; + } + } } if (idx > 1) @@ -2910,6 +2812,6 @@ do_merge: struct error_record *rule_postprocess(struct rule *rule) { - payload_try_merge(rule); + stmt_reduce(rule); return NULL; } |