diff options
Diffstat (limited to 'src/rule.c')
-rw-r--r-- | src/rule.c | 1018 |
1 files changed, 405 insertions, 613 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,195 +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 nftnl_chain_list *chain_list = NULL; - struct rule *rule, *nrule; - struct table *table; - struct chain *chain; - struct set *set; - int ret = 0; - - if (flags & NFT_CACHE_CHAIN_BIT) { - chain_list = chain_cache_dump(ctx, &ret); - if (!chain_list) - return 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 = chain_cache_init(ctx, table, chain_list); - if (ret < 0) - return -1; - } - 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_cache_find(table, &rule->handle); - if (!chain) - chain = chain_binding_lookup(table, - rule->handle.chain.name); - list_move_tail(&rule->list, &chain->rules); - } - if (ret < 0) - return -1; - } - } - - if (flags & NFT_CACHE_CHAIN_BIT) - nftnl_chain_list_free(chain_list); - - return 0; -} - -static int cache_init(struct netlink_ctx *ctx, unsigned int flags) -{ - struct handle handle = { - .family = NFPROTO_UNSPEC, - }; - int ret; - - if (flags == NFT_CACHE_EMPTY) - return 0; - - /* assume NFT_CACHE_TABLE is always set. */ - ret = cache_init_tables(ctx, &handle, &ctx->nft->cache); - if (ret < 0) - return ret; - ret = cache_init_objects(ctx, flags); - if (ret < 0) - return ret; - - return 0; -} - -static bool cache_is_complete(struct nft_cache *cache, unsigned int flags) -{ - return (cache->flags & flags) == flags; -} - -static bool cache_needs_refresh(struct nft_cache *cache) -{ - return cache->flags & NFT_CACHE_REFRESH; -} - -static bool cache_is_updated(struct nft_cache *cache, uint16_t genid) -{ - return genid && genid == cache->genid; -} - -bool cache_needs_update(struct nft_cache *cache) -{ - return cache->flags & NFT_CACHE_UPDATE; -} - -int cache_update(struct nft_ctx *nft, unsigned int flags, struct list_head *msgs) -{ - struct netlink_ctx ctx = { - .list = LIST_HEAD_INIT(ctx.list), - .nft = nft, - .msgs = msgs, - }; - struct nft_cache *cache = &nft->cache; - uint32_t genid, genid_stop, oldflags; - int ret; -replay: - ctx.seqnum = cache->seqnum++; - genid = mnl_genid_get(&ctx); - if (!cache_needs_refresh(cache) && - cache_is_complete(cache, flags) && - cache_is_updated(cache, genid)) - return 0; - - if (cache->genid) - cache_release(cache); - - if (flags & NFT_CACHE_FLUSHED) { - oldflags = flags; - flags = NFT_CACHE_EMPTY; - if (oldflags & NFT_CACHE_UPDATE) - flags |= NFT_CACHE_UPDATE; - goto skip; - } - - ret = cache_init(&ctx, flags); - if (ret < 0) { - cache_release(cache); - if (errno == EINTR) { - nft->nf_sock = nft_mnl_socket_reopen(nft->nf_sock); - goto replay; - } - return -1; - } - - genid_stop = mnl_genid_get(&ctx); - if (genid != genid_stop) { - cache_release(cache); - goto replay; - } -skip: - cache->genid = genid; - cache->flags = flags; - return 0; -} - -static void __cache_flush(struct list_head *table_list) -{ - struct table *table, *next; - - list_for_each_entry_safe(table, next, table_list, list) { - list_del(&table->list); - table_free(table); - } -} - -void cache_release(struct nft_cache *cache) -{ - __cache_flush(&cache->list); - cache->genid = 0; - cache->flags = NFT_CACHE_EMPTY; -} - /* internal ID to uniquely identify a set in the batch */ static uint32_t set_id; @@ -333,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; } @@ -345,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; @@ -357,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; } @@ -369,33 +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) - xfree(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, @@ -408,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; @@ -427,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 { @@ -500,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; @@ -570,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); } @@ -648,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); @@ -671,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) @@ -749,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) @@ -789,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); } } @@ -871,6 +679,7 @@ static const char * const chain_hookname_str_array[] = { "postrouting", "output", "ingress", + "egress", NULL, }; @@ -889,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; @@ -921,27 +729,20 @@ void chain_free(struct chain *chain) list_for_each_entry_safe(rule, next, &chain->rules, list) rule_free(rule); handle_free(&chain->handle); - scope_release(&chain->scope); - xfree(chain->type); + free_const(chain->type.str); expr_free(chain->dev_expr); for (i = 0; i < chain->dev_array_len; i++) - xfree(chain->dev_array[i]); - xfree(chain->dev_array); + free_const(chain->dev_array[i]); + free(chain->dev_array); expr_free(chain->priority.expr); expr_free(chain->policy); - xfree(chain->comment); - xfree(chain); -} + free_const(chain->comment); -struct chain *chain_lookup(const struct table *table, const struct handle *h) -{ - struct chain *chain; - - list_for_each_entry(chain, &table->chains, list) { - if (!strcmp(chain->handle.chain.name, h->chain.name)) - return chain; - } - return NULL; + /* MUST be released after all expressions, they could + * hold refcounts. + */ + scope_release(&chain->scope); + free(chain); } struct chain *chain_binding_lookup(const struct table *table, @@ -949,7 +750,7 @@ struct chain *chain_binding_lookup(const struct table *table, { struct chain *chain; - list_for_each_entry(chain, &table->chain_bindings, list) { + list_for_each_entry(chain, &table->chain_bindings, cache.list) { if (!strcmp(chain->handle.chain.name, chain_name)) return chain; } @@ -964,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; @@ -1033,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; } @@ -1041,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: @@ -1066,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" }, @@ -1075,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" }, @@ -1129,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; @@ -1138,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; } } @@ -1171,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) { @@ -1189,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; } @@ -1227,7 +1036,7 @@ static void chain_print_declaration(const struct chain *chain, nft_print(octx, "\n\t\tcomment \"%s\"", chain->comment); nft_print(octx, "\n"); if (chain->flags & CHAIN_F_BASECHAIN) { - nft_print(octx, "\t\ttype %s hook %s", chain->type, + 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]); @@ -1260,13 +1069,19 @@ static void chain_print_declaration(const struct chain *chain, void chain_rules_print(const struct chain *chain, struct output_ctx *octx, const char *indent) { + unsigned int flags = octx->flags; struct rule *rule; + if (chain->flags & CHAIN_F_BINDING) + octx->flags &= ~NFT_CTX_OUTPUT_HANDLE; + list_for_each_entry(rule, &chain->rules, list) { nft_print(octx, "\t\t%s", indent ? : ""); rule_print(rule, octx); nft_print(octx, "\n"); } + + octx->flags = flags; } static void chain_print(const struct chain *chain, struct output_ctx *octx) @@ -1287,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), @@ -1301,9 +1129,9 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx) struct table *table_alloc(void) { struct table *table; - int i; table = xzalloc(sizeof(*table)); + table->location = internal_location; init_list_head(&table->chains); init_list_head(&table->sets); init_list_head(&table->objs); @@ -1312,10 +1140,10 @@ struct table *table_alloc(void) init_list_head(&table->scope.symbols); table->refcnt = 1; - table->chain_htable = - xmalloc(sizeof(struct list_head) * NFT_CACHE_HSIZE); - for (i = 0; i < NFT_CACHE_HSIZE; i++) - init_list_head(&table->chain_htable[i]); + cache_init(&table->chain_cache); + cache_init(&table->set_cache); + cache_init(&table->obj_cache); + cache_init(&table->ft_cache); return table; } @@ -1330,21 +1158,37 @@ 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, list) + list_for_each_entry_safe(chain, next, &table->chain_bindings, cache.list) + chain_free(chain); + /* this is implicitly releasing chains in the hashtable cache */ + list_for_each_entry_safe(chain, next, &table->chain_cache.list, cache.list) chain_free(chain); list_for_each_entry_safe(set, nset, &table->sets, list) set_free(set); + /* this is implicitly releasing sets in the hashtable cache */ + list_for_each_entry_safe(set, nset, &table->set_cache.list, cache.list) + set_free(set); list_for_each_entry_safe(ft, nft, &table->flowtables, list) flowtable_free(ft); + /* this is implicitly releasing flowtables in the hashtable cache */ + list_for_each_entry_safe(ft, nft, &table->ft_cache.list, cache.list) + flowtable_free(ft); list_for_each_entry_safe(obj, nobj, &table->objs, list) obj_free(obj); + /* this is implicitly releasing objs in the hashtable cache */ + list_for_each_entry_safe(obj, nobj, &table->obj_cache.list, cache.list) + obj_free(obj); + handle_free(&table->handle); scope_release(&table->scope); - xfree(table->chain_htable); - 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) @@ -1353,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) { @@ -1379,39 +1205,48 @@ 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", }; -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]; +} + +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) @@ -1423,33 +1258,43 @@ static void table_print(const struct table *table, struct output_ctx *octx) const char *delim = ""; const char *family = family2str(table->handle.family); + if (table->has_xt_stmts) + fprintf(octx->error_fp, + "# Warning: table %s %s is managed by iptables-nft, do not touch!\n", + family, table->handle.table.name); + nft_print(octx, "table %s %s {", family, table->handle.table.name); + if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER) + nft_print(octx, " #"); if (nft_output_handle(octx)) - nft_print(octx, " # handle %" PRIu64, table->handle.handle.id); + nft_print(octx, " handle %" PRIu64, table->handle.handle.id); + if (table->flags & TABLE_F_OWNER) + nft_print(octx, " progname %s", get_progname(table->owner)); + nft_print(octx, "\n"); - table_print_options(table, &delim, octx); + table_print_flags(table, &delim, octx); if (table->comment) nft_print(octx, "\tcomment \"%s\"\n", table->comment); - list_for_each_entry(obj, &table->objs, list) { + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { nft_print(octx, "%s", delim); obj_print(obj, octx); delim = "\n"; } - list_for_each_entry(set, &table->sets, list) { + list_for_each_entry(set, &table->set_cache.list, cache.list) { if (set_is_anonymous(set->flags)) continue; nft_print(octx, "%s", delim); set_print(set, octx); delim = "\n"; } - list_for_each_entry(flowtable, &table->flowtables, list) { + list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) { nft_print(octx, "%s", delim); flowtable_print(flowtable, octx); delim = "\n"; } - list_for_each_entry(chain, &table->chains, list) { + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { nft_print(octx, "%s", delim); chain_print(chain, octx); delim = "\n"; @@ -1463,6 +1308,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; @@ -1470,105 +1317,12 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj, cmd->handle = *h; cmd->location = *loc; cmd->data = data; - return cmd; -} - -void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc) -{ - if (cmd->num_attrs > NFT_NLATTR_LOC_MAX) - return; - - 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); + 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); - switch (cmd->obj) { - case CMD_OBJ_TABLE: - table = cmd->table; - if (!table) - return; - - list_for_each_entry(chain, &table->chains, list) { - memset(&h, 0, sizeof(h)); - handle_merge(&h, &chain->handle); - h.chain_id = chain->handle.chain_id; - new = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h, - &chain->location, chain_get(chain)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(obj, &table->objs, list) { - handle_merge(&obj->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &obj->handle); - new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h, - &obj->location, obj_get(obj)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(set, &table->sets, list) { - handle_merge(&set->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &set->handle); - new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h, - &set->location, set_get(set)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(ft, &table->flowtables, list) { - handle_merge(&ft->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &ft->handle); - new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h, - &ft->location, flowtable_get(ft)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(chain, &table->chains, list) { - list_for_each_entry(rule, &chain->rules, list) { - memset(&h, 0, sizeof(h)); - handle_merge(&h, &rule->handle); - if (chain->flags & CHAIN_F_BINDING) { - rule->handle.chain_id = - chain->handle.chain_id; - rule->handle.chain.location = - chain->location; - } - new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h, - &rule->location, - rule_get(rule)); - list_add_tail(&new->list, &new_cmds); - } - } - list_splice(&new_cmds, &cmd->list); - break; - case CMD_OBJ_SET: - case CMD_OBJ_MAP: - set = cmd->set; - memset(&h, 0, sizeof(h)); - handle_merge(&h, &set->handle); - newset = set_clone(set); - newset->handle.set_id = set->handle.set_id; - newset->init = set->init; - set->init = NULL; - new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h, - &set->location, newset); - list_add(&new->list, &cmd->list); - break; - default: - break; - } + return cmd; } struct markup *markup_alloc(uint32_t format) @@ -1583,7 +1337,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) @@ -1601,8 +1355,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) @@ -1616,6 +1370,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; @@ -1654,65 +1410,66 @@ void cmd_free(struct cmd *cmd) BUG("invalid command object type %u\n", cmd->obj); } } - xfree(cmd->arg); - xfree(cmd); + free(cmd->attr); + free_const(cmd->arg); + free(cmd); } #include <netlink.h> #include <mnl.h> -static int __do_add_setelems(struct netlink_ctx *ctx, struct set *set, - struct expr *expr, uint32_t flags) +static int __do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd, + struct set *set, struct expr *expr, uint32_t flags) { expr->set_flags |= set->flags; - if (mnl_nft_setelem_add(ctx, set, expr, flags) < 0) + if (mnl_nft_setelem_add(ctx, cmd, set, expr, flags) < 0) return -1; - if (!set_is_anonymous(set->flags) && - set->init != NULL && set->init != expr && - set->flags & NFT_SET_INTERVAL && - set->desc.field_count <= 1) { - interval_map_decompose(expr); - list_splice_tail_init(&expr->expressions, &set->init->expressions); - set->init->size += expr->size; - expr->size = 0; - } - return 0; } -static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, +static int do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t flags) { struct expr *init = cmd->expr; struct set *set = cmd->elem.set; if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, init, true, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, init, true) < 0) return -1; - return __do_add_setelems(ctx, set, init, flags); + return __do_add_elements(ctx, cmd, set, init, flags); +} + +static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, + uint32_t flags) +{ + struct set *set = cmd->set; + + return __do_add_elements(ctx, cmd, set, set->init, flags); } static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd, - uint32_t flags, bool add) + uint32_t flags) { struct set *set = cmd->set; if (set->init != NULL) { + /* Update set->init->size (NFTNL_SET_DESC_SIZE) before adding + * the set to the kernel. Calling this from do_add_setelems() + * comes too late which might result in spurious ENFILE errors. + */ if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, set->init, true, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, set->init, true) < 0) return -1; } - if (add && mnl_nft_set_add(ctx, cmd, flags) < 0) + + if (mnl_nft_set_add(ctx, cmd, flags) < 0) return -1; - if (set->init != NULL) { - return __do_add_setelems(ctx, set, set->init, flags); - } + + if (set_is_anonymous(set->flags)) + return __do_add_elements(ctx, cmd, set, set->init, flags); + return 0; } @@ -1731,11 +1488,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: @@ -1786,12 +1543,10 @@ static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd) struct set *set = cmd->elem.set; if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, expr, false, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, expr, false) < 0) return -1; - if (mnl_nft_setelem_del(ctx, cmd) < 0) + if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0) return -1; return 0; @@ -1833,8 +1588,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; @@ -1842,15 +1596,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; @@ -1859,18 +1608,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"); @@ -1882,9 +1630,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; @@ -1900,27 +1649,18 @@ void obj_free(struct obj *obj) { if (--obj->refcnt > 0) return; - xfree(obj->comment); + free_const(obj->comment); handle_free(&obj->handle); - xfree(obj); -} + if (obj->type == NFT_OBJECT_CT_TIMEOUT) { + struct timeout_state *ts, *next; -void obj_add_hash(struct obj *obj, struct table *table) -{ - list_add_tail(&obj->list, &table->objs); -} - -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, @@ -1933,12 +1673,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; @@ -1949,10 +1685,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); } @@ -1967,11 +1703,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; } } @@ -2015,13 +1754,12 @@ static void obj_print_data(const struct obj *obj, 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); - 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); + 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; @@ -2194,7 +1932,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]); @@ -2212,7 +1950,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]); @@ -2270,7 +2008,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; @@ -2279,14 +2017,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))) @@ -2304,9 +2042,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; @@ -2330,15 +2069,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, @@ -2378,6 +2112,10 @@ static void flowtable_print_declaration(const struct flowtable *flowtable, nft_print(octx, " }%s", opts->stmt_separator); } + if (flowtable->flags & NFT_FLOWTABLE_HW_OFFLOAD) + nft_print(octx, "%s%sflags offload%s", opts->tab, opts->tab, + opts->stmt_separator); + if (flowtable->flags & NFT_FLOWTABLE_COUNTER) nft_print(octx, "%s%scounter%s", opts->tab, opts->tab, opts->stmt_separator); @@ -2402,17 +2140,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) @@ -2423,12 +2150,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; @@ -2442,8 +2165,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", @@ -2466,7 +2189,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; @@ -2475,7 +2198,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); } @@ -2490,20 +2213,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; } @@ -2511,7 +2229,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; @@ -2527,9 +2245,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, @@ -2539,13 +2262,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"); @@ -2557,14 +2276,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"); } @@ -2591,17 +2310,30 @@ static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, struct table *table) { - struct set *set; + struct set *set = cmd->set; - set = set_lookup(table, cmd->handle.set.name); - if (set == NULL) - return -1; + if (!set) { + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return -1; + } __do_list_set(ctx, cmd, set); return 0; } +static int do_list_hooks(struct netlink_ctx *ctx, struct cmd *cmd) +{ + const char *devname = cmd->handle.obj.name; + int hooknum = -1; + + if (cmd->handle.chain.name) + hooknum = cmd->handle.chain_id; + + return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, hooknum, devname); +} + static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) { struct table *table = NULL; @@ -2610,13 +2342,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: @@ -2626,6 +2359,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); @@ -2645,8 +2380,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: @@ -2661,6 +2398,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); } @@ -2668,7 +2407,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; @@ -2686,7 +2425,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); @@ -2702,7 +2441,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); } @@ -2731,15 +2470,36 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_QUOTA: type = NFT_OBJECT_QUOTA; break; + case CMD_OBJ_RULES: + ret = netlink_reset_rules(ctx, cmd, true); + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); + case CMD_OBJ_RULE: + return netlink_reset_rules(ctx, cmd, false); + case CMD_OBJ_ELEMENTS: + return do_get_setelems(ctx, cmd, true); + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + ret = netlink_list_setelems(ctx, &cmd->handle, cmd->set, true); + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); default: BUG("invalid command object type %u\n", cmd->obj); } ret = netlink_reset_objs(ctx, cmd, type, dump); list_for_each_entry_safe(obj, next, &ctx->list, list) { - table = table_lookup(&obj->handle, &ctx->nft->cache); - if (!obj_lookup(table, obj->handle.obj.name, obj->type)) - list_move(&obj->list, &table->objs); + table = table_cache_find(&ctx->nft->cache.table_cache, + obj->handle.table.name, + obj->handle.family); + if (!obj_cache_find(table, obj->handle.obj.name, obj->type)) { + list_del(&obj->list); + obj_cache_add(obj, table); + } } if (ret < 0) return ret; @@ -2767,12 +2527,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: @@ -2842,6 +2604,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); @@ -2957,49 +2720,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) @@ -3008,6 +2800,6 @@ do_merge: struct error_record *rule_postprocess(struct rule *rule) { - payload_try_merge(rule); + stmt_reduce(rule); return NULL; } |