When batching up multiple commands, one may run into a situation where the current command requires a cache update while the previous ones didn't and that causes objects added by previous commands to be removed from cache. If the current or any following command references any of these objects, the command is rejected. Resolve this by copying Florian's solution from iptables-nft: After droping the whole cache and populating it again with entries fetched from kernel, use the current list of commands to restore local entries again. Signed-off-by: Phil Sutter <phil@xxxxxx> --- Changes since v1: - Don't add anonymous sets to cache when restoring, as suggested by Eric Garver. --- src/rule.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/rule.c b/src/rule.c index 17bf5bbbe680c..d1a86ea9fadd6 100644 --- a/src/rule.c +++ b/src/rule.c @@ -220,6 +220,78 @@ static int cache_init(struct netlink_ctx *ctx, enum cmd_ops cmd) return 0; } +static void cache_add_set_cmd(struct nft_ctx *nft, struct cmd *cmd) +{ + struct table *table; + + table = table_lookup(&cmd->handle, &nft->cache); + if (table == NULL) + return; + + if (set_lookup(table, cmd->set->handle.set.name) == NULL) + set_add_hash(set_get(cmd->set), table); +} + +static void cache_add_chain_cmd(struct nft_ctx *nft, struct cmd *cmd) +{ + struct table *table; + struct chain *chain; + + table = table_lookup(&cmd->handle, &nft->cache); + if (table == NULL) + return; + + if (cmd->chain == NULL) { + if (chain_lookup(table, &cmd->handle) == NULL) { + chain = chain_alloc(NULL); + handle_merge(&chain->handle, &cmd->handle); + chain_add_hash(chain, table); + } + return; + } + if (chain_lookup(table, &cmd->chain->handle) == NULL) + chain_add_hash(chain_get(cmd->chain), table); +} + +static void cache_add_table_cmd(struct nft_ctx *nft, struct cmd *cmd) +{ + struct table *table; + + if (table_lookup(&cmd->handle, &nft->cache)) + return; + + if (cmd->table == NULL) { + table = table_alloc(); + handle_merge(&table->handle, &cmd->handle); + table_add_hash(table, &nft->cache); + } else { + table_add_hash(table_get(cmd->table), &nft->cache); + } +} + +static void cache_add_commands(struct nft_ctx *nft) +{ + struct cmd *cmd; + + list_for_each_entry(cmd, &nft->cmds, list) { + switch (cmd->obj) { + case CMD_OBJ_SET: + if (cmd->set->flags & NFT_SET_ANONYMOUS) + continue; + cache_add_set_cmd(nft, cmd); + break; + case CMD_OBJ_CHAIN: + cache_add_chain_cmd(nft, cmd); + break; + case CMD_OBJ_TABLE: + cache_add_table_cmd(nft, cmd); + break; + default: + break; + } + } +} + /* Return a "score" of how complete local cache will be if * cache_init_objects() ran for given cmd. Higher value * means more complete. */ @@ -270,6 +342,7 @@ replay: } cache->genid = genid; cache->cmd = cmd; + cache_add_commands(nft); return 0; } -- 2.21.0