diff options
Diffstat (limited to 'src/cache.c')
-rw-r--r-- | src/cache.c | 222 |
1 files changed, 196 insertions, 26 deletions
diff --git a/src/cache.c b/src/cache.c index fd8df884..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,6 +18,7 @@ #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) { @@ -123,9 +126,9 @@ void nft_cache_filter_fini(struct nft_cache_filter *filter) struct nft_filter_obj *obj, *next; list_for_each_entry_safe(obj, next, &filter->obj[i].head, list) - xfree(obj); + free(obj); } - xfree(filter); + free(filter); } static void cache_filter_add(struct nft_cache_filter *filter, @@ -200,8 +203,12 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, { switch (cmd->obj) { case CMD_OBJ_TABLE: - if (filter && cmd->handle.table.name) { + if (filter) filter->list.family = cmd->handle.family; + if (!cmd->handle.table.name) { + flags |= NFT_CACHE_TABLE; + break; + } else if (filter) { filter->list.table = cmd->handle.table.name; } flags |= NFT_CACHE_FULL; @@ -211,6 +218,10 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, filter->list.family = cmd->handle.family; filter->list.table = cmd->handle.table.name; filter->list.chain = cmd->handle.chain.name; + /* implicit terse listing to fetch content of anonymous + * sets only when chain name is specified. + */ + flags |= NFT_CACHE_TERSE; } flags |= NFT_CACHE_FULL; break; @@ -223,8 +234,6 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, } if (filter->list.table && filter->list.set) flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM; - else if (nft_output_terse(&nft->output)) - flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE; else flags |= NFT_CACHE_FULL; break; @@ -234,6 +243,8 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, case CMD_OBJ_SETS: case CMD_OBJ_MAPS: flags |= NFT_CACHE_TABLE | NFT_CACHE_SET; + if (!nft_output_terse(&nft->output)) + flags |= NFT_CACHE_SETELEM; break; case CMD_OBJ_FLOWTABLE: if (filter && @@ -248,27 +259,164 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE; break; case CMD_OBJ_RULESET: - if (nft_output_terse(&nft->output)) - flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE; - else - flags |= NFT_CACHE_FULL; - break; default: flags |= NFT_CACHE_FULL; break; } flags |= NFT_CACHE_REFRESH; + if (nft_output_terse(&nft->output)) + flags |= NFT_CACHE_TERSE; + + return flags; +} + +static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags, + struct nft_cache_filter *filter) +{ + switch (cmd->obj) { + case CMD_OBJ_RULES: + case CMD_OBJ_RULE: + if (filter) { + if (cmd->handle.table.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + } + if (cmd->handle.chain.name) + filter->list.chain = cmd->handle.chain.name; + } + flags |= NFT_CACHE_SET | NFT_CACHE_FLOWTABLE | + NFT_CACHE_OBJECT | NFT_CACHE_CHAIN; + break; + case CMD_OBJ_ELEMENTS: + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + flags |= NFT_CACHE_SET; + break; + default: + flags |= NFT_CACHE_TABLE; + break; + } + return flags; } -unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds, - struct nft_cache_filter *filter) +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)); @@ -284,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 | @@ -296,7 +445,7 @@ 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: flags |= evaluate_cache_list(nft, cmd, flags, filter); @@ -318,8 +467,9 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds, break; } } + *pflags = flags; - return flags; + return 0; } void table_cache_add(struct table *table, struct nft_cache *cache) @@ -483,31 +633,36 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data) table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN); - if (h->family != family || - strcmp(table, h->table.name) != 0 || + if ((h->family != NFPROTO_UNSPEC && h->family != family) || + (h->table.name && strcmp(table, h->table.name) != 0) || (h->chain.name && strcmp(chain, h->chain.name) != 0)) return 0; netlink_dump_rule(nlr, ctx); rule = netlink_delinearize_rule(ctx, nlr); + assert(rule); list_add_tail(&rule->list, &ctx->list); return 0; } -static int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h, - const struct nft_cache_filter *filter) +int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h, + const struct nft_cache_filter *filter, + bool dump, bool reset) { struct nftnl_rule_list *rule_cache; const char *table = NULL; const char *chain = NULL; + uint64_t rule_handle = 0; if (filter) { table = filter->list.table; chain = filter->list.chain; + rule_handle = filter->list.rule_handle; } - rule_cache = mnl_nft_rule_dump(ctx, h->family, table, chain); + rule_cache = mnl_nft_rule_dump(ctx, h->family, + table, chain, rule_handle, dump, reset); if (rule_cache == NULL) { if (errno == EINTR) return -1; @@ -839,7 +994,7 @@ static int rule_init_cache(struct netlink_ctx *ctx, struct table *table, struct chain *chain; int ret; - ret = rule_cache_dump(ctx, &table->handle, filter); + ret = rule_cache_dump(ctx, &table->handle, filter, true, false); list_for_each_entry_safe(rule, nrule, &ctx->list, list) { chain = chain_cache_find(table, rule->handle.chain.name); @@ -847,12 +1002,21 @@ static int rule_init_cache(struct netlink_ctx *ctx, struct table *table, chain = chain_binding_lookup(table, rule->handle.chain.name); if (!chain) - return -1; + 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, @@ -863,8 +1027,10 @@ static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table, int ret = 0; list_for_each_entry(chain, &table->chain_bindings, cache.list) { - filter.list.table = table->handle.table.name; - filter.list.chain = chain->handle.chain.name; + filter.list = (typeof(filter.list)) { + .table = table->handle.table.name, + .chain = chain->handle.chain.name, + }; ret = rule_init_cache(ctx, table, &filter); } @@ -917,7 +1083,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags, continue; ret = netlink_list_setelems(ctx, &set->handle, - set); + set, false); if (ret < 0) goto cache_fails; } @@ -930,7 +1096,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags, continue; ret = netlink_list_setelems(ctx, &set->handle, - set); + set, false); if (ret < 0) goto cache_fails; } @@ -1064,7 +1230,11 @@ replay: goto replay; } + erec_queue(error(&netlink_location, "cache initialization failed: %s", + strerror(errno)), + msgs); nft_cache_release(cache); + return -1; } @@ -1109,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) |