From 01e5c6f0ed0315046537612f5a80e506d37a7f8e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 17 Jun 2019 12:03:08 +0200 Subject: src: add cache level flags The score approach based on command type is confusing. This patch introduces cache level flags, each flag specifies what kind of object type is needed. These flags are set on/off depending on the list of commands coming in this batch. cache_is_complete() now checks if the cache contains the objects that are needed through these new flags. Signed-off-by: Pablo Neira Ayuso --- include/Makefile.am | 1 + include/cache.h | 35 +++++++++++++++++ include/nftables.h | 2 +- include/rule.h | 3 +- src/cache.c | 86 ++++++++++++++++++++++-------------------- src/evaluate.c | 3 +- src/libnftables.c | 6 +-- src/rule.c | 106 +++++++++++++++++++++++----------------------------- 8 files changed, 135 insertions(+), 107 deletions(-) create mode 100644 include/cache.h diff --git a/include/Makefile.am b/include/Makefile.am index b1f4fcf2..2d77a768 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,6 +2,7 @@ SUBDIRS = linux \ nftables noinst_HEADERS = cli.h \ + cache.h \ datatype.h \ expression.h \ fib.h \ diff --git a/include/cache.h b/include/cache.h new file mode 100644 index 00000000..d3502a8a --- /dev/null +++ b/include/cache.h @@ -0,0 +1,35 @@ +#ifndef _NFT_CACHE_H_ +#define _NFT_CACHE_H_ + +enum cache_level_bits { + NFT_CACHE_TABLE_BIT = (1 << 0), + NFT_CACHE_CHAIN_BIT = (1 << 1), + NFT_CACHE_SET_BIT = (1 << 2), + NFT_CACHE_FLOWTABLE_BIT = (1 << 3), + NFT_CACHE_OBJECT_BIT = (1 << 4), + NFT_CACHE_SETELEM_BIT = (1 << 5), + NFT_CACHE_RULE_BIT = (1 << 6), + __NFT_CACHE_MAX_BIT = (1 << 7), +}; + +enum cache_level_flags { + NFT_CACHE_EMPTY = 0, + NFT_CACHE_TABLE = NFT_CACHE_TABLE_BIT, + NFT_CACHE_CHAIN = NFT_CACHE_TABLE_BIT | + NFT_CACHE_CHAIN_BIT, + NFT_CACHE_SET = NFT_CACHE_TABLE_BIT | + NFT_CACHE_SET_BIT, + NFT_CACHE_FLOWTABLE = NFT_CACHE_TABLE_BIT | + NFT_CACHE_FLOWTABLE_BIT, + NFT_CACHE_OBJECT = NFT_CACHE_TABLE_BIT | + NFT_CACHE_OBJECT_BIT, + NFT_CACHE_SETELEM = NFT_CACHE_TABLE_BIT | + NFT_CACHE_SET_BIT | + NFT_CACHE_SETELEM_BIT, + NFT_CACHE_RULE = NFT_CACHE_TABLE_BIT | + NFT_CACHE_CHAIN_BIT | + NFT_CACHE_RULE_BIT, + NFT_CACHE_FULL = __NFT_CACHE_MAX_BIT - 1, +}; + +#endif /* _NFT_CACHE_H_ */ diff --git a/include/nftables.h b/include/nftables.h index b7c78572..ed446e2d 100644 --- a/include/nftables.h +++ b/include/nftables.h @@ -81,7 +81,7 @@ struct nft_cache { uint32_t genid; struct list_head list; uint32_t seqnum; - uint32_t cmd; + uint32_t flags; }; struct mnl_socket; diff --git a/include/rule.h b/include/rule.h index 299485ff..aefb24d9 100644 --- a/include/rule.h +++ b/include/rule.h @@ -462,7 +462,6 @@ enum cmd_ops { CMD_EXPORT, CMD_MONITOR, CMD_DESCRIBE, - __CMD_FLUSH_RULESET, }; /** @@ -636,7 +635,7 @@ extern struct error_record *rule_postprocess(struct rule *rule); struct netlink_ctx; extern int do_command(struct netlink_ctx *ctx, struct cmd *cmd); -extern int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds); +extern unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds); extern int cache_update(struct nft_ctx *ctx, enum cmd_ops cmd, struct list_head *msgs); extern void cache_flush(struct nft_ctx *ctx, struct list_head *msgs); diff --git a/src/cache.c b/src/cache.c index d7153f6f..d371c548 100644 --- a/src/cache.c +++ b/src/cache.c @@ -11,112 +11,116 @@ #include #include #include +#include -static unsigned int evaluate_cache_add(struct cmd *cmd) +static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags) { - unsigned int completeness = CMD_INVALID; - switch (cmd->obj) { case CMD_OBJ_SETELEM: - case CMD_OBJ_SET: - case CMD_OBJ_CHAIN: - case CMD_OBJ_FLOWTABLE: - completeness = cmd->op; + flags |= NFT_CACHE_SETELEM; break; case CMD_OBJ_RULE: - if (cmd->handle.index.id) - completeness = CMD_LIST; + if (cmd->handle.index.id || + cmd->handle.position.id) + flags |= NFT_CACHE_RULE; break; default: break; } - return completeness; + return flags; } -static unsigned int evaluate_cache_del(struct cmd *cmd) +static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags) { - unsigned int completeness = CMD_INVALID; - switch (cmd->obj) { case CMD_OBJ_SETELEM: - completeness = cmd->op; + flags |= NFT_CACHE_SETELEM; break; default: break; } - return completeness; + return flags; } -static unsigned int evaluate_cache_flush(struct cmd *cmd) +static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags) { - unsigned int completeness = CMD_INVALID; - switch (cmd->obj) { - case CMD_OBJ_RULESET: - completeness = __CMD_FLUSH_RULESET; - break; case CMD_OBJ_SET: case CMD_OBJ_MAP: case CMD_OBJ_METER: - completeness = cmd->op; + flags |= NFT_CACHE_SET; break; + case CMD_OBJ_RULESET: default: + flags = NFT_CACHE_EMPTY; break; } - return completeness; + return flags; } -static unsigned int evaluate_cache_rename(struct cmd *cmd) +static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags) { - unsigned int completeness = CMD_INVALID; - switch (cmd->obj) { case CMD_OBJ_CHAIN: - completeness = cmd->op; + flags |= NFT_CACHE_CHAIN; break; default: break; } - return completeness; + return flags; } -int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds) +unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds) { - unsigned int echo_completeness = CMD_INVALID; - unsigned int completeness = CMD_INVALID; + unsigned int flags = NFT_CACHE_EMPTY; struct cmd *cmd; list_for_each_entry(cmd, cmds, list) { switch (cmd->op) { case CMD_ADD: case CMD_INSERT: - case CMD_REPLACE: - if (nft_output_echo(&nft->output)) - echo_completeness = cmd->op; - + if (nft_output_echo(&nft->output)) { + flags = NFT_CACHE_FULL; + break; + } + + flags |= NFT_CACHE_TABLE | + NFT_CACHE_CHAIN | + NFT_CACHE_SET | + NFT_CACHE_FLOWTABLE | + NFT_CACHE_OBJECT; /* Fall through */ case CMD_CREATE: - completeness = evaluate_cache_add(cmd); + flags = evaluate_cache_add(cmd, flags); + break; + case CMD_REPLACE: + flags = NFT_CACHE_FULL; break; case CMD_DELETE: - completeness = evaluate_cache_del(cmd); + flags |= NFT_CACHE_TABLE | + NFT_CACHE_CHAIN | + NFT_CACHE_SET | + NFT_CACHE_FLOWTABLE | + NFT_CACHE_OBJECT; + + flags = evaluate_cache_del(cmd, flags); break; case CMD_GET: case CMD_LIST: case CMD_RESET: case CMD_EXPORT: case CMD_MONITOR: - completeness = cmd->op; + flags |= NFT_CACHE_FULL; break; case CMD_FLUSH: - completeness = evaluate_cache_flush(cmd); + flags = evaluate_cache_flush(cmd, flags); break; case CMD_RENAME: - completeness = evaluate_cache_rename(cmd); + flags = evaluate_cache_rename(cmd, flags); break; case CMD_DESCRIBE: case CMD_IMPORT: @@ -126,5 +130,5 @@ int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds) } } - return max(completeness, echo_completeness); + return flags; } diff --git a/src/evaluate.c b/src/evaluate.c index 73a4be33..511f9f14 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -3294,7 +3295,7 @@ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule, } /* add rules to cache only if it is complete enough to contain them */ - if (!cache_is_complete(&ctx->nft->cache, CMD_LIST)) + if (!cache_is_complete(&ctx->nft->cache, NFT_CACHE_RULE)) return 0; return rule_cache_update(ctx, op); diff --git a/src/libnftables.c b/src/libnftables.c index abd133be..dccb8ab4 100644 --- a/src/libnftables.c +++ b/src/libnftables.c @@ -381,11 +381,11 @@ static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename, static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs, struct list_head *cmds) { - unsigned int completeness; + unsigned int flags; struct cmd *cmd; - completeness = cache_evaluate(nft, cmds); - if (cache_update(nft, completeness, msgs) < 0) + flags = cache_evaluate(nft, cmds); + if (cache_update(nft, flags, msgs) < 0) return -1; list_for_each_entry(cmd, cmds, list) { diff --git a/src/rule.c b/src/rule.c index ed199889..f60374ab 100644 --- a/src/rule.c +++ b/src/rule.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -147,97 +148,85 @@ static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h, return 0; } -static int cache_init_objects(struct netlink_ctx *ctx, enum cmd_ops cmd) +static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags) { + struct rule *rule, *nrule; struct table *table; struct chain *chain; - struct rule *rule, *nrule; struct set *set; int ret; list_for_each_entry(table, &ctx->nft->cache.list, list) { - ret = netlink_list_sets(ctx, &table->handle); - list_splice_tail_init(&ctx->list, &table->sets); - - if (ret < 0) - return -1; - - list_for_each_entry(set, &table->sets, list) { - ret = netlink_list_setelems(ctx, &set->handle, set); + 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; } - - ret = netlink_list_chains(ctx, &table->handle); - if (ret < 0) - return -1; - list_splice_tail_init(&ctx->list, &table->chains); - - ret = netlink_list_flowtables(ctx, &table->handle); - if (ret < 0) - return -1; - list_splice_tail_init(&ctx->list, &table->flowtables); - - if (cmd != CMD_RESET) { + 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); } - /* Skip caching other objects to speed up things: We only need - * a full cache when listing the existing ruleset. - */ - if (cmd != CMD_LIST) - continue; - - 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 (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; } - - if (ret < 0) - return -1; } return 0; } -static int cache_init(struct netlink_ctx *ctx, enum cmd_ops cmd) +static int cache_init(struct netlink_ctx *ctx, unsigned int flags) { struct handle handle = { .family = NFPROTO_UNSPEC, }; int ret; - if (cmd == __CMD_FLUSH_RULESET) + 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, cmd); + ret = cache_init_objects(ctx, flags); if (ret < 0) return ret; return 0; } -/* Return a "score" of how complete local cache will be if - * cache_init_objects() ran for given cmd. Higher value - * means more complete. */ -static int cache_completeness(enum cmd_ops cmd) +bool cache_is_complete(struct nft_cache *cache, unsigned int flags) { - if (cmd == CMD_LIST) - return 3; - if (cmd != CMD_RESET) - return 2; - return 1; -} - -bool cache_is_complete(struct nft_cache *cache, enum cmd_ops cmd) -{ - return cache_completeness(cache->cmd) >= cache_completeness(cmd); + return (cache->flags & flags) == flags; } static bool cache_is_updated(struct nft_cache *cache, uint16_t genid) @@ -245,7 +234,7 @@ static bool cache_is_updated(struct nft_cache *cache, uint16_t genid) return genid && genid == cache->genid; } -int cache_update(struct nft_ctx *nft, enum cmd_ops cmd, struct list_head *msgs) +int cache_update(struct nft_ctx *nft, unsigned int flags, struct list_head *msgs) { struct netlink_ctx ctx = { .list = LIST_HEAD_INIT(ctx.list), @@ -259,14 +248,14 @@ int cache_update(struct nft_ctx *nft, enum cmd_ops cmd, struct list_head *msgs) replay: ctx.seqnum = cache->seqnum++; genid = mnl_genid_get(&ctx); - if (cache_is_complete(cache, cmd) && + if (cache_is_complete(cache, flags) && cache_is_updated(cache, genid)) return 0; if (cache->genid) cache_release(cache); - ret = cache_init(&ctx, cmd); + ret = cache_init(&ctx, flags); if (ret < 0) { cache_release(cache); if (errno == EINTR) { @@ -283,7 +272,7 @@ replay: } cache->genid = genid; - cache->cmd = cmd; + cache->flags = flags; return 0; } @@ -308,15 +297,14 @@ void cache_flush(struct nft_ctx *nft, struct list_head *msgs) __cache_flush(&cache->list); cache->genid = mnl_genid_get(&ctx); - cache->cmd = CMD_LIST; + cache->flags = NFT_CACHE_FULL; } void cache_release(struct nft_cache *cache) { __cache_flush(&cache->list); cache->genid = 0; - cache->cmd = CMD_INVALID; - + cache->flags = NFT_CACHE_EMPTY; } /* internal ID to uniquely identify a set in the batch */ -- cgit v1.2.3