The function is used to return the given table's chains, so fetching chain cache is enough. This requires a bunch of manual rule cache updates in different places. To still support the fake cache logic from xtables-restore, make fetch_rule_cache() do nothing in case have_cache is set. Accidental double rule cache updates for the same chain need to be prevented. This is complicated by the fact that chain's rule list is managed by libnftnl. Hence the same logic as used for table list, namely checking list pointer value, can't be used. Instead, simply fetch rules only if the given chain's rule list is empty. If it isn't, rules have been fetched before; if it is, a second rule fetch won't hurt. Signed-off-by: Phil Sutter <phil@xxxxxx> --- iptables/nft.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/iptables/nft.c b/iptables/nft.c index 7c974af8b4141..729b88d990f9f 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -1264,6 +1264,7 @@ err: static struct nftnl_chain * nft_chain_find(struct nft_handle *h, const char *table, const char *chain); +static int fetch_rule_cache(struct nft_handle *h, struct nftnl_chain *c); int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, @@ -1275,6 +1276,14 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table, nft_xt_builtin_init(h, table); + /* Since ebtables user-defined chain policies are implemented as last + * rule in nftables, rule cache is required here to treat them right. */ + if (h->family == NFPROTO_BRIDGE) { + c = nft_chain_find(h, table, chain); + if (c && !nft_chain_builtin(c)) + fetch_rule_cache(h, c); + } + nft_fn = nft_rule_append; r = nft_rule_new(h, chain, table, data); @@ -1550,6 +1559,9 @@ static int nft_rule_list_update(struct nftnl_chain *c, void *data) struct nftnl_rule *rule; int ret; + if (nftnl_rule_lookup_byindex(c, 0)) + return 0; + rule = nftnl_rule_alloc(); if (!rule) return -1; @@ -1579,6 +1591,9 @@ static int fetch_rule_cache(struct nft_handle *h, struct nftnl_chain *c) { int i; + if (h->have_cache) + return 0; + if (c) return nft_rule_list_update(c, h); @@ -1670,7 +1685,8 @@ struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h, if (!t) return NULL; - nft_build_cache(h); + if (!h->have_cache && !h->cache->table[t->type].chains) + fetch_chain_cache(h); return h->cache->table[t->type].chains; } @@ -1761,6 +1777,7 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format) c = nftnl_chain_list_iter_next(iter); while (c) { + fetch_rule_cache(h, c); ret = nft_chain_save_rules(h, c, format); if (ret != 0) break; @@ -1949,6 +1966,10 @@ static int __nft_chain_user_del(struct nftnl_chain *c, void *data) fprintf(stdout, "Deleting chain `%s'\n", nftnl_chain_get_str(c, NFTNL_CHAIN_NAME)); + /* This triggers required policy rule deletion. */ + if (h->family == NFPROTO_BRIDGE) + fetch_rule_cache(h, c); + /* XXX This triggers a fast lookup from the kernel. */ nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE); ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c); @@ -2238,6 +2259,8 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen struct nftnl_rule_iter *iter; bool found = false; + fetch_rule_cache(h, c); + if (rulenum >= 0) /* Delete by rule number case */ return nftnl_rule_lookup_byindex(c, rulenum); @@ -3063,6 +3086,8 @@ int ebt_set_user_chain_policy(struct nft_handle *h, const char *table, else return 0; + fetch_rule_cache(h, c); + nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, pval); return 1; } @@ -3402,6 +3427,8 @@ static int nft_is_chain_compatible(struct nftnl_chain *c, void *data) enum nf_inet_hooks hook; int prio; + fetch_rule_cache(h, c); + if (nftnl_rule_foreach(c, nft_is_rule_compatible, NULL)) return -1; -- 2.23.0