diff options
Diffstat (limited to 'src/cache.c')
-rw-r--r-- | src/cache.c | 638 |
1 files changed, 552 insertions, 86 deletions
diff --git a/src/cache.c b/src/cache.c index 42e6b65c..c000e32c 100644 --- a/src/cache.c +++ b/src/cache.c @@ -6,6 +6,8 @@ * later) as published by the Free Software Foundation. */ +#include <nft.h> + #include <expression.h> #include <statement.h> #include <rule.h> @@ -16,10 +18,27 @@ #include <mnl.h> #include <libnftnl/chain.h> #include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags) { + struct set *set; + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (!cmd->table) + break; + + flags |= NFT_CACHE_TABLE | + NFT_CACHE_CHAIN | + NFT_CACHE_SET | + NFT_CACHE_OBJECT | + NFT_CACHE_FLOWTABLE; + list_for_each_entry(set, &cmd->table->sets, list) { + if (set->automerge) + flags |= NFT_CACHE_SETELEM_MAYBE; + } + break; case CMD_OBJ_CHAIN: case CMD_OBJ_SET: case CMD_OBJ_COUNTER: @@ -86,13 +105,74 @@ static unsigned int evaluate_cache_get(struct cmd *cmd, unsigned int flags) return flags; } -static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags) +struct nft_cache_filter *nft_cache_filter_init(void) +{ + struct nft_cache_filter *filter; + int i; + + filter = xzalloc(sizeof(struct nft_cache_filter)); + memset(&filter->list, 0, sizeof(filter->list)); + for (i = 0; i < NFT_CACHE_HSIZE; i++) + init_list_head(&filter->obj[i].head); + + return filter; +} + +void nft_cache_filter_fini(struct nft_cache_filter *filter) +{ + int i; + + for (i = 0; i < NFT_CACHE_HSIZE; i++) { + struct nft_filter_obj *obj, *next; + + list_for_each_entry_safe(obj, next, &filter->obj[i].head, list) + free(obj); + } + free(filter); +} + +static void cache_filter_add(struct nft_cache_filter *filter, + const struct cmd *cmd) +{ + struct nft_filter_obj *obj; + uint32_t hash; + + obj = xmalloc(sizeof(struct nft_filter_obj)); + obj->family = cmd->handle.family; + obj->table = cmd->handle.table.name; + obj->set = cmd->handle.set.name; + + hash = djb_hash(cmd->handle.set.name) % NFT_CACHE_HSIZE; + list_add_tail(&obj->list, &filter->obj[hash].head); +} + +static bool cache_filter_find(const struct nft_cache_filter *filter, + const struct handle *handle) +{ + struct nft_filter_obj *obj; + uint32_t hash; + + hash = djb_hash(handle->set.name) % NFT_CACHE_HSIZE; + + list_for_each_entry(obj, &filter->obj[hash].head, list) { + if (obj->family == handle->family && + !strcmp(obj->table, handle->table.name) && + !strcmp(obj->set, handle->set.name)) + return true; + } + + return false; +} + +static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags, + struct nft_cache_filter *filter) { switch (cmd->obj) { case CMD_OBJ_SET: case CMD_OBJ_MAP: case CMD_OBJ_METER: flags |= NFT_CACHE_SET; + cache_filter_add(filter, cmd); break; case CMD_OBJ_RULESET: flags |= NFT_CACHE_FLUSHED; @@ -117,12 +197,229 @@ static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags) return flags; } -unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds) +static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, + unsigned int flags, + struct nft_cache_filter *filter) +{ + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (filter) + filter->list.family = cmd->handle.family; + if (!cmd->handle.table.name) { + flags |= NFT_CACHE_TABLE; + break; + } else if (filter) { + filter->list.table = cmd->handle.table.name; + } + flags |= NFT_CACHE_FULL; + break; + case CMD_OBJ_CHAIN: + if (filter && cmd->handle.chain.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + filter->list.chain = cmd->handle.chain.name; + /* implicit terse listing to fetch content of anonymous + * sets only when chain name is specified. + */ + flags |= NFT_CACHE_TERSE; + } + flags |= NFT_CACHE_FULL; + break; + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + if (filter && cmd->handle.table.name && cmd->handle.set.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + filter->list.set = cmd->handle.set.name; + } + if (filter->list.table && filter->list.set) + flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM; + else + flags |= NFT_CACHE_FULL; + break; + case CMD_OBJ_CHAINS: + flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN; + break; + case CMD_OBJ_SETS: + case CMD_OBJ_MAPS: + flags |= NFT_CACHE_TABLE | NFT_CACHE_SET; + if (!nft_output_terse(&nft->output)) + flags |= NFT_CACHE_SETELEM; + break; + case CMD_OBJ_FLOWTABLE: + if (filter && + cmd->handle.table.name && + cmd->handle.flowtable.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + filter->list.ft = cmd->handle.flowtable.name; + } + /* fall through */ + case CMD_OBJ_FLOWTABLES: + flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE; + break; + case CMD_OBJ_RULESET: + default: + flags |= NFT_CACHE_FULL; + break; + } + flags |= NFT_CACHE_REFRESH; + + if (nft_output_terse(&nft->output)) + flags |= NFT_CACHE_TERSE; + + return flags; +} + +static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags, + struct nft_cache_filter *filter) +{ + switch (cmd->obj) { + case CMD_OBJ_RULES: + case CMD_OBJ_RULE: + if (filter) { + if (cmd->handle.table.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + } + if (cmd->handle.chain.name) + filter->list.chain = cmd->handle.chain.name; + } + flags |= NFT_CACHE_SET | NFT_CACHE_FLOWTABLE | + NFT_CACHE_OBJECT | NFT_CACHE_CHAIN; + break; + case CMD_OBJ_ELEMENTS: + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + flags |= NFT_CACHE_SET; + break; + default: + flags |= NFT_CACHE_TABLE; + break; + } + + return flags; +} + +static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs) +{ + const struct handle *h = &cmd->handle; + const struct location *loc; + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + break; + case CMD_OBJ_RULE: + case CMD_OBJ_RULES: + case CMD_OBJ_CHAIN: + case CMD_OBJ_CHAINS: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + if (h->chain.name && + strlen(h->chain.name) > NFT_NAME_MAXLEN) { + loc = &h->chain.location; + goto err_name_too_long; + } + break; + case CMD_OBJ_ELEMENTS: + case CMD_OBJ_SET: + case CMD_OBJ_SETS: + case CMD_OBJ_MAP: + case CMD_OBJ_MAPS: + case CMD_OBJ_METER: + case CMD_OBJ_METERS: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + if (h->set.name && + strlen(h->set.name) > NFT_NAME_MAXLEN) { + loc = &h->set.location; + goto err_name_too_long; + } + break; + case CMD_OBJ_FLOWTABLE: + case CMD_OBJ_FLOWTABLES: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + if (h->flowtable.name && + strlen(h->flowtable.name) > NFT_NAME_MAXLEN) { + loc = &h->flowtable.location; + goto err_name_too_long; + } + break; + case CMD_OBJ_INVALID: + case CMD_OBJ_EXPR: + case CMD_OBJ_RULESET: + case CMD_OBJ_MARKUP: + case CMD_OBJ_MONITOR: + case CMD_OBJ_SETELEMS: + case CMD_OBJ_HOOKS: + break; + case CMD_OBJ_COUNTER: + case CMD_OBJ_COUNTERS: + case CMD_OBJ_QUOTA: + case CMD_OBJ_QUOTAS: + case CMD_OBJ_LIMIT: + case CMD_OBJ_LIMITS: + case CMD_OBJ_SECMARK: + case CMD_OBJ_SECMARKS: + case CMD_OBJ_SYNPROXY: + case CMD_OBJ_SYNPROXYS: + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_HELPERS: + case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_CT_TIMEOUTS: + case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_CT_EXPECTATIONS: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + if (h->obj.name && + strlen(h->obj.name) > NFT_NAME_MAXLEN) { + loc = &h->obj.location; + goto err_name_too_long; + } + break; + } + + return 0; + +err_name_too_long: + erec_queue(error(loc, "name too long, %d characters maximum allowed", + NFT_NAME_MAXLEN), + msgs); + return -1; +} + +int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds, + struct list_head *msgs, struct nft_cache_filter *filter, + unsigned int *pflags) { unsigned int flags = NFT_CACHE_EMPTY; struct cmd *cmd; list_for_each_entry(cmd, cmds, list) { + if (nft_handle_validate(cmd, msgs) < 0) + return -1; + + if (filter->list.table && cmd->op != CMD_LIST) + memset(&filter->list, 0, sizeof(filter->list)); + switch (cmd->op) { case CMD_ADD: case CMD_INSERT: @@ -135,6 +432,7 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds) flags = NFT_CACHE_FULL; break; case CMD_DELETE: + case CMD_DESTROY: flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN | NFT_CACHE_SET | @@ -147,30 +445,31 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds) flags = evaluate_cache_get(cmd, flags); break; case CMD_RESET: - flags |= NFT_CACHE_TABLE; + flags |= evaluate_cache_reset(cmd, flags, filter); break; case CMD_LIST: - case CMD_EXPORT: - flags |= NFT_CACHE_FULL | NFT_CACHE_REFRESH; + flags |= evaluate_cache_list(nft, cmd, flags, filter); break; case CMD_MONITOR: flags |= NFT_CACHE_FULL; break; case CMD_FLUSH: - flags = evaluate_cache_flush(cmd, flags); + flags = evaluate_cache_flush(cmd, flags, filter); break; case CMD_RENAME: flags = evaluate_cache_rename(cmd, flags); break; case CMD_DESCRIBE: case CMD_IMPORT: + case CMD_EXPORT: break; default: break; } } + *pflags = flags; - return flags; + return 0; } void table_cache_add(struct table *table, struct nft_cache *cache) @@ -218,13 +517,13 @@ static int chain_cache_cb(struct nftnl_chain *nlc, void *arg) struct chain *chain; table_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE); - chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME); family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY); - if (strcmp(table_name, ctx->table->handle.table.name) || - family != ctx->table->handle.family) + if (family != ctx->table->handle.family || + strcmp(table_name, ctx->table->handle.table.name)) return 0; + chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME); hash = djb_hash(chain_name) % NFT_CACHE_HSIZE; chain = netlink_delinearize_chain(ctx->nlctx, nlc); @@ -252,12 +551,22 @@ static int chain_cache_init(struct netlink_ctx *ctx, struct table *table, return 0; } -static struct nftnl_chain_list *chain_cache_dump(struct netlink_ctx *ctx, - int *err) +static struct nftnl_chain_list * +chain_cache_dump(struct netlink_ctx *ctx, + const struct nft_cache_filter *filter, int *err) { struct nftnl_chain_list *chain_list; + const char *table = NULL; + const char *chain = NULL; + int family = NFPROTO_UNSPEC; + + if (filter && filter->list.table && filter->list.chain) { + family = filter->list.family; + table = filter->list.table; + chain = filter->list.chain; + } - chain_list = mnl_nft_chain_dump(ctx, AF_UNSPEC); + chain_list = mnl_nft_chain_dump(ctx, family, table, chain); if (chain_list == NULL) { if (errno == EINTR) { *err = -1; @@ -270,6 +579,21 @@ static struct nftnl_chain_list *chain_cache_dump(struct netlink_ctx *ctx, return chain_list; } +void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table, + const char *chain) +{ + struct nftnl_chain_list *chain_list; + + chain_list = mnl_nft_chain_dump(ctx, table->handle.family, + table->handle.table.name, chain); + if (!chain_list) + return; + + chain_cache_init(ctx, table, chain_list); + + nftnl_chain_list_free(chain_list); +} + void chain_cache_add(struct chain *chain, struct table *table) { uint32_t hash; @@ -297,6 +621,61 @@ struct chain *chain_cache_find(const struct table *table, const char *name) return NULL; } +static int list_rule_cb(struct nftnl_rule *nlr, void *data) +{ + struct netlink_ctx *ctx = data; + const struct handle *h = ctx->data; + const char *table, *chain; + struct rule *rule; + uint32_t family; + + family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); + table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); + chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN); + + if ((h->family != NFPROTO_UNSPEC && h->family != family) || + (h->table.name && strcmp(table, h->table.name) != 0) || + (h->chain.name && strcmp(chain, h->chain.name) != 0)) + return 0; + + netlink_dump_rule(nlr, ctx); + rule = netlink_delinearize_rule(ctx, nlr); + assert(rule); + list_add_tail(&rule->list, &ctx->list); + + return 0; +} + +int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h, + const struct nft_cache_filter *filter, + bool dump, bool reset) +{ + struct nftnl_rule_list *rule_cache; + const char *table = NULL; + const char *chain = NULL; + uint64_t rule_handle = 0; + + if (filter) { + table = filter->list.table; + chain = filter->list.chain; + rule_handle = filter->list.rule_handle; + } + + rule_cache = mnl_nft_rule_dump(ctx, h->family, + table, chain, rule_handle, dump, reset); + if (rule_cache == NULL) { + if (errno == EINTR) + return -1; + + return 0; + } + + ctx->data = h; + nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx); + nftnl_rule_list_free(rule_cache); + return 0; +} + struct set_cache_dump_ctx { struct netlink_ctx *nlctx; struct table *table; @@ -305,10 +684,19 @@ struct set_cache_dump_ctx { static int set_cache_cb(struct nftnl_set *nls, void *arg) { struct set_cache_dump_ctx *ctx = arg; + const char *set_table; const char *set_name; + uint32_t set_family; struct set *set; uint32_t hash; + set_table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + set_family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + + if (set_family != ctx->table->handle.family || + strcmp(set_table, ctx->table->handle.table.name)) + return 0; + set = netlink_delinearize_set(ctx->nlctx, nls); if (!set) return -1; @@ -317,6 +705,8 @@ static int set_cache_cb(struct nftnl_set *nls, void *arg) hash = djb_hash(set_name) % NFT_CACHE_HSIZE; cache_add(&set->cache, &ctx->table->set_cache, hash); + nftnl_set_list_del(nls); + nftnl_set_free(nls); return 0; } @@ -327,19 +717,28 @@ static int set_cache_init(struct netlink_ctx *ctx, struct table *table, .nlctx = ctx, .table = table, }; + nftnl_set_list_foreach(set_list, set_cache_cb, &dump_ctx); return 0; } -static struct nftnl_set_list *set_cache_dump(struct netlink_ctx *ctx, - const struct table *table, - int *err) +static struct nftnl_set_list * +set_cache_dump(struct netlink_ctx *ctx, + const struct nft_cache_filter *filter, int *err) { struct nftnl_set_list *set_list; + int family = NFPROTO_UNSPEC; + const char *table = NULL; + const char *set = NULL; + + if (filter) { + family = filter->list.family; + table = filter->list.table; + set = filter->list.set; + } - set_list = mnl_nft_set_dump(ctx, table->handle.family, - table->handle.table.name); + set_list = mnl_nft_set_dump(ctx, family, table, set); if (!set_list) { if (errno == EINTR) { *err = -1; @@ -474,10 +873,19 @@ struct ft_cache_dump_ctx { static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg) { struct ft_cache_dump_ctx *ctx = arg; - const char *ft_name; struct flowtable *ft; + const char *ft_table; + const char *ft_name; + uint32_t ft_family; uint32_t hash; + ft_family = nftnl_flowtable_get_u32(nlf, NFTNL_FLOWTABLE_FAMILY); + ft_table = nftnl_flowtable_get_str(nlf, NFTNL_FLOWTABLE_TABLE); + + if (ft_family != ctx->table->handle.family || + strcmp(ft_table, ctx->table->handle.table.name)) + return 0; + ft = netlink_delinearize_flowtable(ctx->nlctx, nlf); if (!ft) return -1; @@ -486,6 +894,8 @@ static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg) hash = djb_hash(ft_name) % NFT_CACHE_HSIZE; cache_add(&ft->cache, &ctx->table->ft_cache, hash); + nftnl_flowtable_list_del(nlf); + nftnl_flowtable_free(nlf); return 0; } @@ -501,13 +911,21 @@ static int ft_cache_init(struct netlink_ctx *ctx, struct table *table, return 0; } -static struct nftnl_flowtable_list *ft_cache_dump(struct netlink_ctx *ctx, - const struct table *table) +static struct nftnl_flowtable_list * +ft_cache_dump(struct netlink_ctx *ctx, const struct nft_cache_filter *filter) { struct nftnl_flowtable_list *ft_list; + int family = NFPROTO_UNSPEC; + const char *table = NULL; + const char *ft = NULL; + + if (filter) { + family = filter->list.family; + table = filter->list.table; + ft = filter->list.ft; + } - ft_list = mnl_nft_flowtable_dump(ctx, table->handle.family, - table->handle.table.name); + ft_list = mnl_nft_flowtable_dump(ctx, family, table, ft); if (!ft_list) { if (errno == EINTR) return NULL; @@ -551,12 +969,13 @@ struct flowtable *ft_cache_find(const struct table *table, const char *name) } static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h, - struct nft_cache *cache) + struct nft_cache *cache, + const struct nft_cache_filter *filter) { struct table *table, *next; int ret; - ret = netlink_list_tables(ctx, h); + ret = netlink_list_tables(ctx, h, filter); if (ret < 0) return -1; @@ -568,83 +987,129 @@ static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h, return 0; } -static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags) +static int rule_init_cache(struct netlink_ctx *ctx, struct table *table, + const struct nft_cache_filter *filter) +{ + struct rule *rule, *nrule; + struct chain *chain; + int ret; + + ret = rule_cache_dump(ctx, &table->handle, filter, true, false); + + list_for_each_entry_safe(rule, nrule, &ctx->list, list) { + chain = chain_cache_find(table, rule->handle.chain.name); + if (!chain) + chain = chain_binding_lookup(table, + rule->handle.chain.name); + if (!chain) + goto err_ctx_list; + + list_move_tail(&rule->list, &chain->rules); + } + + return ret; + +err_ctx_list: + list_for_each_entry_safe(rule, nrule, &ctx->list, list) { + list_del(&rule->list); + rule_free(rule); + } + errno = EINTR; + + return -1; +} + +static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table, + const char *chain_name) +{ + struct nft_cache_filter filter; + struct chain *chain; + int ret = 0; + + list_for_each_entry(chain, &table->chain_bindings, cache.list) { + filter.list = (typeof(filter.list)) { + .table = table->handle.table.name, + .chain = chain->handle.chain.name, + }; + ret = rule_init_cache(ctx, table, &filter); + } + + return ret; +} + +static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags, + const struct nft_cache_filter *filter) { struct nftnl_flowtable_list *ft_list = NULL; struct nftnl_chain_list *chain_list = NULL; struct nftnl_set_list *set_list = NULL; struct nftnl_obj_list *obj_list; - struct 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); + chain_list = chain_cache_dump(ctx, filter, &ret); if (!chain_list) return -1; } + if (flags & NFT_CACHE_SET_BIT) { + set_list = set_cache_dump(ctx, filter, &ret); + if (!set_list) { + ret = -1; + goto cache_fails; + } + } + if (flags & NFT_CACHE_FLOWTABLE_BIT) { + ft_list = ft_cache_dump(ctx, filter); + if (!ft_list) { + ret = -1; + goto cache_fails; + } + } list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { if (flags & NFT_CACHE_SET_BIT) { - set_list = set_cache_dump(ctx, table, &ret); - if (!set_list) { - ret = -1; - goto cache_fails; - } ret = set_cache_init(ctx, table, set_list); - - nftnl_set_list_free(set_list); - - if (ret < 0) { - ret = -1; + if (ret < 0) goto cache_fails; - } } if (flags & NFT_CACHE_SETELEM_BIT) { list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (cache_filter_find(filter, &set->handle)) + continue; + if (!set_is_anonymous(set->flags) && + flags & NFT_CACHE_TERSE) + continue; + ret = netlink_list_setelems(ctx, &set->handle, - set); - if (ret < 0) { - ret = -1; + set, false); + if (ret < 0) goto cache_fails; - } } } else if (flags & NFT_CACHE_SETELEM_MAYBE) { list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (cache_filter_find(filter, &set->handle)) + continue; + if (!set_is_non_concat_range(set)) continue; ret = netlink_list_setelems(ctx, &set->handle, - set); - if (ret < 0) { - ret = -1; + set, false); + if (ret < 0) goto cache_fails; - } } } if (flags & NFT_CACHE_CHAIN_BIT) { ret = chain_cache_init(ctx, table, chain_list); - if (ret < 0) { - ret = -1; + if (ret < 0) goto cache_fails; - } } if (flags & NFT_CACHE_FLOWTABLE_BIT) { - ft_list = ft_cache_dump(ctx, table); - if (!ft_list) { - ret = -1; - goto cache_fails; - } ret = ft_cache_init(ctx, table, ft_list); - - nftnl_flowtable_list_free(ft_list); - - if (ret < 0) { - ret = -1; + if (ret < 0) goto cache_fails; - } } if (flags & NFT_CACHE_OBJECT_BIT) { obj_list = obj_cache_dump(ctx, table); @@ -656,41 +1121,37 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags) nftnl_obj_list_free(obj_list); - if (ret < 0) { - ret = -1; + if (ret < 0) goto cache_fails; - } } 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.chain.name); - if (!chain) - chain = chain_binding_lookup(table, - rule->handle.chain.name); - if (!chain) { - ret = -1; - goto cache_fails; - } - - list_move_tail(&rule->list, &chain->rules); - } - if (ret < 0) { - ret = -1; + ret = rule_init_cache(ctx, table, filter); + if (ret < 0) goto cache_fails; + + if (filter && filter->list.table && filter->list.chain) { + ret = implicit_chain_cache(ctx, table, filter->list.chain); + if (ret < 0) + goto cache_fails; } } } cache_fails: + if (set_list) + nftnl_set_list_free(set_list); + if (ft_list) + nftnl_flowtable_list_free(ft_list); + if (flags & NFT_CACHE_CHAIN_BIT) nftnl_chain_list_free(chain_list); return ret; } -static int nft_cache_init(struct netlink_ctx *ctx, unsigned int flags) +static int nft_cache_init(struct netlink_ctx *ctx, unsigned int flags, + const struct nft_cache_filter *filter) { struct handle handle = { .family = NFPROTO_UNSPEC, @@ -701,10 +1162,10 @@ static int nft_cache_init(struct netlink_ctx *ctx, unsigned int flags) return 0; /* assume NFT_CACHE_TABLE is always set. */ - ret = cache_init_tables(ctx, &handle, &ctx->nft->cache); + ret = cache_init_tables(ctx, &handle, &ctx->nft->cache, filter); if (ret < 0) return ret; - ret = cache_init_objects(ctx, flags); + ret = cache_init_objects(ctx, flags, filter); if (ret < 0) return ret; @@ -732,7 +1193,8 @@ bool nft_cache_needs_update(struct nft_cache *cache) } int nft_cache_update(struct nft_ctx *nft, unsigned int flags, - struct list_head *msgs) + struct list_head *msgs, + const struct nft_cache_filter *filter) { struct netlink_ctx ctx = { .list = LIST_HEAD_INIT(ctx.list), @@ -761,14 +1223,18 @@ replay: goto skip; } - ret = nft_cache_init(&ctx, flags); + ret = nft_cache_init(&ctx, flags, filter); if (ret < 0) { if (errno == EINTR) { nft_cache_release(cache); goto replay; } + erec_queue(error(&netlink_location, "cache initialization failed: %s", + strerror(errno)), + msgs); nft_cache_release(cache); + return -1; } @@ -813,7 +1279,7 @@ void cache_init(struct cache *cache) void cache_free(struct cache *cache) { - xfree(cache->ht); + free(cache->ht); } void cache_add(struct cache_item *item, struct cache *cache, uint32_t hash) |