A command line like 'nft list table t1 \; list table t2' would return an error for non-existent table t1: The filter setup for the second command limited cache population to table t2, so t1 was not found in cache. Try to sort this by passing a temporary filter object to evaluate_cache routines and merging its content to the original object. The logic goes like this: - The first command is easy, just copy filter->list values. - Folllowing commands may only remove filters, not add ones. Therefore only unset filter values, if either the current command does not set a filter (i.e., "needs all data") or filters for a different object (in the case above, drop table filter for t1 instead of changing it to t2). - If the family value varies between commands, unset all filters. The kernel can't dump e.g. all tables named "t" in all families, so a command like 'list table ip t ; list table ip6 t' breaks the algorihm. - Treat NFT_CACHE_TERSE flag like a filter (which it is), i.e. allow later commands to unset it ("needs set elements") but not set it. Fixes: 3f1d3912c3a6b ("cache: filter out tables that are not requested") Signed-off-by: Phil Sutter --- This patch is a mess and probably pretty fragile, too. I at least encountered way more "special" cases while testing it than anticipated. A proper fix would be to perform cache population for each command individually and thus support per-command filters - basically refactoring the cache population step in nft_evaluate(). What do you think? Go for a proper solution or not? Apply this one "for now" or not? --- src/cache.c | 47 +++++++++- .../testcases/listing/cache_filter_merge | 88 +++++++++++++++++++ 2 files changed, 132 insertions(+), 3 deletions(-) create mode 100755 tests/shell/testcases/listing/cache_filter_merge diff --git a/src/cache.c b/src/cache.c index 88a83b7406a6b..f6fff4d6d5fae 100644 --- a/src/cache.c +++ b/src/cache.c @@ -483,13 +483,15 @@ static void reset_filter(struct nft_cache_filter *filter) } int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds, - struct list_head *msgs, struct nft_cache_filter *filter, + struct list_head *msgs, struct nft_cache_filter *all_filter, unsigned int *pflags) { unsigned int flags, batch_flags = NFT_CACHE_EMPTY; + struct nft_cache_filter _filter, *filter = &_filter; + bool first_cmd = true; struct cmd *cmd; - assert(filter); + assert(all_filter); list_for_each_entry(cmd, cmds, list) { if (nft_handle_validate(cmd, msgs) < 0) @@ -536,7 +538,46 @@ int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds, default: break; } - batch_flags |= flags; + + if (first_cmd) { + batch_flags = flags; + all_filter->list = filter->list; + first_cmd = false; + } else { + /* do not let later commands set TERSE if not set */ + batch_flags |= flags & ~NFT_CACHE_TERSE; + /* unset TERSE if later commands need it off */ + if (!(flags & NFT_CACHE_TERSE)) + batch_flags &= ~NFT_CACHE_TERSE; + if (all_filter->list.family != filter->list.family) { + /* no filtering possible if family value varies */ + memset(&all_filter->list, 0, + sizeof(all_filter->list)); + all_filter->list.family = 0; + } + if (!all_filter->list.table || + !filter->list.table || + strcmp(all_filter->list.table, filter->list.table)) + all_filter->list.table = NULL; + if (!all_filter->list.chain || + !filter->list.chain || + strcmp(all_filter->list.chain, filter->list.chain)) + all_filter->list.chain = NULL; + if (!all_filter->list.obj || + !filter->list.obj || + strcmp(all_filter->list.obj, filter->list.obj)) + all_filter->list.obj = NULL; + if (!all_filter->list.set || + !filter->list.set || + strcmp(all_filter->list.set, filter->list.set)) + all_filter->list.set = NULL; + if (!all_filter->list.ft || + !filter->list.ft || + strcmp(all_filter->list.ft, filter->list.ft)) + all_filter->list.ft = NULL; + if (all_filter->list.obj_type != filter->list.obj_type) + all_filter->list.obj_type = 0; + } } *pflags = batch_flags; diff --git a/tests/shell/testcases/listing/cache_filter_merge b/tests/shell/testcases/listing/cache_filter_merge new file mode 100755 index 0000000000000..82132d8e5dbbd --- /dev/null +++ b/tests/shell/testcases/listing/cache_filter_merge @@ -0,0 +1,88 @@ +#!/bin/bash + +set -e + +$NFT -f - < ' unsupported +commands+=("list chains") +for fam in ip ip6; do + commands+=( + "list chains $fam" + ) +done +for fam in ip ip6; do + commands+=( + "list table $fam t" + "list flowtable $fam t ft" + "list set $fam t s" + "list counter $fam t cnt" + "list quota $fam t qt" + "list chain $fam t c" + ) +done + +declare -a outputs +for cmd in "${commands[@]}"; do + outputs+=("$($NFT "$cmd")") +done + +for ((i = 0; i < ${#commands[*]} - 1; i++)); do + for ((j = $i + 1; j < ${#commands[*]}; j++)); do + diff -u --label expect --label "${commands[$i]}; ${commands[$j]}" \ + <(echo "${outputs[$i]}"; echo "${outputs[$j]}") \ + <($NFT "${commands[$i]}; ${commands[$j]}") + done +done -- 2.51.0