path: root/iptables/nft-cache.c
Commit message (Collapse)AuthorAgeFilesLines
* nft: cache: Check consistency with NFT_CL_FAKE, tooPhil Sutter2020-08-211-2/+2
| | | | | | | | Athough this cache level fetches table names only, it shouldn't skip the consistency check. Fixes: f42bfb344af82 ("nft: cache: Re-establish cache consistency check") Signed-off-by: Phil Sutter <>
* nft: Eliminate table list from cachePhil Sutter2020-07-311-55/+18
| | | | | | | | | | | | | | The full list of tables in kernel is not relevant, only those used by iptables-nft and for those, knowing if they exist or not is sufficient. For holding that information, the already existing 'table' array in nft_cache suits well. Consequently, nft_table_find() merely checks if the new 'exists' boolean is true or not and nft_for_each_table() iterates over the builtin_table array in nft_handle, additionally checking the boolean in cache for whether to skip the entry or not. Signed-off-by: Phil Sutter <>
* iptables: replace libnftnl table list by linux listPablo Neira Ayuso2020-07-271-23/+49
| | | | | | | | This patch removes the libnftnl table list by linux list. This comes with an extra memory allocation to store the nft_table object. Probably, there is no need to cache the entire nftnl_table in the near future. Signed-off-by: Pablo Neira Ayuso <>
* nft: cache: Drop duplicate chain checkPhil Sutter2020-07-241-6/+1
| | | | | | | | | When fetching chains from kernel, checking for duplicate chain names is not needed: Nftables doesn't support them in the first place. This is merely a leftover from when multiple cache fetches could happen and so a bit of sanity checking was in order. Signed-off-by: Phil Sutter <>
* nft: cache: Re-establish cache consistency checkPhil Sutter2020-05-111-0/+11
| | | | | | | | | | | | | Restore code ensuring __nft_build_cache() returns a consistent cache in which all ruleset elements belong to the same generation. This check was removed by commit 200bc39965149 ("nft: cache: Fix iptables-save segfault under stress") as it could lead to segfaults if a partial cache fetch was done while cache's chain list was traversed. With the new cache fetch logic, __nft_build_cache() is never called while holding references to cache entries. Signed-off-by: Phil Sutter <>
* nft: cache: Fetch cache for specific chainsPhil Sutter2020-05-111-8/+73
| | | | | | | | | | | | | | | | | Iterate over command list and collect chains to cache. Insert them into a sorted list to pass to __nft_build_cache(). If a command is interested in all chains (e.g., --list), cmd->chain remains unset. To record this case reliably, use a boolean ('all_chains'). Otherwise, it is hard to distinguish between first call to nft_cache_level_set() and previous command with NULL cmd->chain value. When caching only specific chains, manually add builtin ones for the given table as well - otherwise nft_xt_builtin_init() will act as if they don't exist and possibly override non-default chain policies. Signed-off-by: Phil Sutter <>
* nft-cache: Introduce __fetch_chain_cache()Phil Sutter2020-05-111-20/+30
| | | | | | | Extract the inner part of fetch_chain_cache() into a dedicated function, preparing for individual chain caching. Signed-off-by: Phil Sutter <>
* nft-cache: Fetch cache per tablePhil Sutter2020-05-111-11/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | | Restore per-table operation of cache routines as initially implemented in commit e2883c5531e6e ("nft-cache: Support partial cache per table"). As before, this doesn't limit fetching of tables (their number is supposed to be low) but instead limits fetching of sets, chains and rules to the specified table. For this to behave correctly when restoring without flushing over multiple tables, cache must be freed fully after each commit - otherwise the previous table's cache level is reused for the current one. The exception being fake cache, used for flushing restore: NFT_CL_FAKE is set just once at program startup, so it must stay set otherwise consecutive tables cause pointless cache fetching. The sole use-case requiring a multi-table cache, iptables-save, is indicated by req->table being NULL. Therefore, req->table assignment is a bit sloppy: All calls to nft_cache_level_set() are assumed to set the same table value, collision detection exists merely to catch programming mistakes. Make nft_fini() call nft_release_cache() instead of flush_chain_cache(), the former does a full cache deinit including cache_req contents. Signed-off-by: Phil Sutter <>
* nft: cache: Introduce struct nft_cache_reqPhil Sutter2020-05-111-15/+17
| | | | | | | | | | This embedded struct collects cache requirement info gathered from parsed nft_cmds and is interpreted by __nft_build_cache(). While being at it, remove unused parameters passed to the latter function, nft_handle pointer is sufficient. Signed-off-by: Phil Sutter <>
* nft: cache: Improve fake cache integrationPhil Sutter2020-05-111-13/+3
| | | | | | | | With NFT_CL_FAKE being highest cache level while at the same time __nft_build_cache() treating it equal to NFT_CL_TABLES, no special handling for fake cache is required anymore. Signed-off-by: Phil Sutter <>
* nft: cache: Simplify rule and set fetchersPhil Sutter2020-05-111-15/+5
| | | | | | | | | | | | Since no incremental cache fetching happens anymore, code fetching rules for chains or elements for sets may safely assume that whatever is in cache also didn't get populated with rules or elements before. Therefore no (optional) chain name needs to be passed on to fetch_rule_cache() and fetch_set_cache() doesn't have to select for which sets in a table to call set_fetch_elem_cb(). Signed-off-by: Phil Sutter <>
* nft: remove cache build callsPablo Neira Ayuso2020-05-111-20/+0
| | | | | | | | | The cache requirements are now calculated once from the parsing phase. There is no need to call __nft_build_cache() from several spots in the codepath anymore. Signed-off-by: Pablo Neira Ayuso <> Signed-off-by: Phil Sutter <>
* nft: calculate cache requirements from list of commandsPablo Neira Ayuso2020-05-111-39/+29
| | | | | | | | | | | | | | | | | | | | This patch uses the new list of commands to calculate the cache requirements, the rationale after this updates is the following: #1 Parsing, that builds the list of commands and it also calculates cache level requirements. #2 Cache building. #3 Translate commands to jobs #4 Translate jobs to netlink This patch removes the pre-parsing code in xtables-restore.c to calculate the cache. After this patch, cache is calculated only once, there is no need to cancel and refetch for an in-transit transaction. Signed-off-by: Pablo Neira Ayuso <> Signed-off-by: Phil Sutter <>
* nft: cache: Fetch sets per tablePhil Sutter2020-05-111-11/+15
| | | | | | | Kernel accepts a table name when dumping sets, so make use of that in case a table was passed to fetch_set_cache() but no set name. Signed-off-by: Phil Sutter <>
* nft: cache: Init per table set list along with chain listPhil Sutter2020-05-111-15/+4
| | | | | | | This simplifies code a bit and also aligns set and chain lists handling in cache. Signed-off-by: Phil Sutter <>
* nft: cache: Eliminate init_chain_cache()Phil Sutter2020-05-111-12/+4
| | | | | | | The function is always called immediately after fetch_table_cache(), so merge it into the latter. Signed-off-by: Phil Sutter <>
* nft: cache: Fix iptables-save segfault under stressPhil Sutter2020-03-161-14/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If kernel ruleset is constantly changing, code called by nft_is_table_compatible() may crash: For each item in table's chain list, nft_is_chain_compatible() is called. This in turn calls nft_build_cache() to fetch chain's rules. Though if kernel genid has changed meanwhile, cache is flushed and rebuilt from scratch, thereby freeing table's chain list - the foreach loop in nft_is_table_compatible() then operates on freed memory. A simple reproducer (may need a few calls): | RULESET='*filter | :INPUT ACCEPT [10517:1483527] | :FORWARD ACCEPT [0:0] | :OUTPUT ACCEPT [1714:105671] | COMMIT | ' | | for ((i = 0; i < 100; i++)); do | iptables-nft-restore <<< "$RULESET" & | done & | iptables-nft-save To fix the problem, basically revert commit ab1cd3b510fa5 ("nft: ensure cache consistency") so that __nft_build_cache() no longer flushes the cache. Instead just record kernel's genid when fetching for the first time. If kernel rule set changes until the changes are committed, the commit simply fails and local cache is being rebuilt. Signed-off-by: Phil Sutter <>
* nft: cache: Fix for unused variable warningsPhil Sutter2020-03-161-3/+1
| | | | | | | Loop index variable was left in place after removing the loops. Fixes: 39ec645093baa ("nft: cache: Simplify chain list allocation") Signed-off-by: Phil Sutter <>
* nft: cache: Review flush_cache()Phil Sutter2020-03-061-9/+11
| | | | | | | | | While fixing for iptables-nft-restore under stress, I managed to hit NULL-pointer deref in flush_cache(). Given that nftnl_*_list_free() functions are not NULL-pointer tolerant, better make sure such are not passed by accident. Signed-off-by: Phil Sutter <>
* nft: cache: Simplify chain list allocationPhil Sutter2020-03-061-27/+19
| | | | | | | | | | Allocate chain lists right after fetching table cache, regardless of whether partial cache is fetched or not. Chain list pointers reside in struct nft_cache's table array and hence are present irrespective of actual tables in kernel. Given the small number of tables, there wasn't much overhead avoided by the conditional in fetch_chain_cache(). Signed-off-by: Phil Sutter <>
* nft: cache: Make nft_rebuild_cache() respect fake cachePhil Sutter2020-03-061-3/+8
| | | | | | | | | | | | If transaction needed a refresh in nft_action(), restore with flush would fetch a full cache instead of merely refreshing table list contained in "fake" cache. To fix this, nft_rebuild_cache() must distinguish between fake cache and full rule cache. Therefore introduce NFT_CL_FAKE to be distinguished from NFT_CL_RULES. Signed-off-by: Phil Sutter <>
* nft: cache: Fix nft_release_cache() under stressPhil Sutter2020-03-061-2/+8
| | | | | | | | | | | | | | | | | iptables-nft-restore calls nft_action(h, NFT_COMPAT_COMMIT) for each COMMIT line in input. When restoring a dump containing multiple large tables, chances are nft_rebuild_cache() has to run multiple times. If the above happens, consecutive table contents are added to __cache[1] which nft_rebuild_cache() then frees, so next commit attempt accesses invalid memory. Fix this by making nft_release_cache() (called after each successful commit) return things into pre-rebuild state again, but keeping the fresh cache copy. Fixes: f6ad231d698c7 ("nft: keep original cache in case of ERESTART") Signed-off-by: Phil Sutter <>
* nft: Introduce NFT_CL_SETS cache levelPhil Sutter2019-11-251-9/+196
| | | | | | | | | | In order to support anonymous sets, introduce an intermediate cache level between NFT_CL_CHAINS and NFT_CL_RULES. Actually chains are not needed to fetch sets, but given that sets are only needed for rules, put it late to not slow down fetching chains. Signed-off-by: Phil Sutter <> Acked-by: Pablo Neira Ayuso <>
* nft: Optimize flushing all chains of a tablePhil Sutter2019-10-171-3/+19
| | | | | | | | | | | | | | | | | | | | | | | | Leverage nftables' support for flushing all chains of a table by omitting NFTNL_RULE_CHAIN attribute in NFT_MSG_DELRULE payload. The only caveat is with verbose output, as that still requires to have a list of (existing) chains to iterate over. Apart from that, implementing this shortcut is pretty straightforward: Don't retrieve a chain list and just call __nft_rule_flush() directly which doesn't set above attribute if chain name pointer is NULL. A bigger deal is keeping rule cache consistent: Instead of just clearing rule list for each flushed chain, flush_rule_cache() is updated to iterate over all cached chains of the given table, clearing their rule lists if not called for a specific chain. While being at it, sort local variable declarations in nft_rule_flush() from longest to shortest and drop the loop-local 'chain_name' variable (but instead use 'chain' function parameter which is not used at that point). Signed-off-by: Phil Sutter <> Acked-by: Pablo Neira Ayuso <>
* nft-cache: Support partial rule cache per chainPhil Sutter2019-10-171-20/+56
| | | | | | | | | | | | | | | | | Accept an additional chain name pointer in __nft_build_cache() and pass it along to fetch only that specific chain and its rules. Enhance nft_build_cache() to take an optional nftnl_chain pointer to fetch rules for. Enhance nft_chain_list_get() to take an optional chain name. If cache level doesn't include chains already, it will fetch only the specified chain from kernel (if existing) and add that to table's chain list which is returned. This keeps operations for all chains of a table or a specific one within the same code path in nft.c. Signed-off-by: Phil Sutter <> Acked-by: Pablo Neira Ayuso <>
* nft-cache: Support partial cache per tablePhil Sutter2019-10-171-25/+57
| | | | | | | | | | | | Accept a builtin_table pointer in __nft_build_cache() and pass it along when fetching chains and rules to operate on that table only (unless the pointer is NULL). Make use of it in nft_chain_list_get() since that accepts a table name and performs a builtin table lookup internally already. Signed-off-by: Phil Sutter <> Acked-by: Pablo Neira Ayuso <>
* nft-cache: Cover for multiple fetcher invocationPhil Sutter2019-10-171-1/+18
| | | | | | | | | | | | | | | | | | | | Preparing for partial caches, it is necessary to make sure these functions don't cause harm if called repeatedly. * Use h->cache->tables pointer as indicator for existing table cache, return immediately from fetch_table_cache() if non-NULL. * Initialize table's chain list only if non-NULL. * Search for chain in table's chain list before adding it. * Don't fetch rules for a chain if it has any rules already. With rule list being embedded in struct nftnl_chain, this is the best way left to check if rules have been fetched already or not. It will fail for empty chains, but causes no harm in that case, either. Signed-off-by: Phil Sutter <> Acked-by: Pablo Neira Ayuso <>
* nft-cache: Fetch only chains in nft_chain_list_get()Phil Sutter2019-10-171-1/+1
| | | | | | | | | | The function is used to return the given table's chains, so fetching chain cache is enough. Add calls to nft_build_cache() in places where a rule cache is required. Signed-off-by: Phil Sutter <> Acked-by: Pablo Neira Ayuso <>
* nft-cache: Introduce cache levelsPhil Sutter2019-10-171-15/+39
| | | | | | | | | | | | | Replace the simple have_cache boolean by a cache level indicator defining how complete the cache is. Since have_cache indicated full cache (including rules), make code depending on it check for cache level NFT_CL_RULES. Core cache fetching routine __nft_build_cache() accepts a new level via parameter and raises cache completeness to that level. Signed-off-by: Phil Sutter <> Acked-by: Pablo Neira Ayuso <>
* nft: Extract cache routines into nft-cache.cPhil Sutter2019-10-101-0/+376
The amount of code dealing with caching only is considerable and hence deserves an own source file. Signed-off-by: Phil Sutter <> Acked-by: Pablo Neira Ayuso <>