Extend nft_chain_list_get() to accept an optional chain name callers are interested in. If chain cache is not present yet, add a suitable payload to NFT_MSG_GETCHAIN request to retrieve only that single chain. The table's chain list will then contain only that chain. Chain cache is considered present (i.e., have_chain_cache set to true) only if fetch_chain_cache() was called without a specific chain name. Therefore a full cache update could happen after fetching a specific chain which means cache update preparation has to drop each table's chain list to avoid duplicates. With fetch_chain_cache() potentially ignoring all but a single table, make fetch_rule_cache() aware of potentially uninitialized chain lists. Rules are simply not fetched for those then. Signed-off-by: Phil Sutter <phil@xxxxxx> --- iptables/nft.c | 64 +++++++++++++++++++++++++++----------- iptables/nft.h | 3 +- iptables/xtables-restore.c | 2 +- iptables/xtables-save.c | 2 +- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 82c892ad96f34..46c7be372a10f 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -750,7 +750,7 @@ nft_chain_builtin_find(const struct builtin_table *t, const char *chain) static void nft_chain_builtin_init(struct nft_handle *h, const struct builtin_table *table) { - struct nftnl_chain_list *list = nft_chain_list_get(h, table->name); + struct nftnl_chain_list *list = nft_chain_list_get(h, table->name, NULL); struct nftnl_chain *c; int i; @@ -1425,7 +1425,7 @@ static int fetch_table_cache(struct nft_handle *h) return 1; } -static int fetch_chain_cache(struct nft_handle *h) +static int fetch_chain_cache(struct nft_handle *h, const char *table, const char *chain) { char buf[16536]; struct nlmsghdr *nlh; @@ -1439,13 +1439,37 @@ static int fetch_chain_cache(struct nft_handle *h) if (!h->tables[i].name) continue; + /* When not fetching a single chain, kernel returns all chains + * of the given family which may belong to other tables than + * the expected one. Hence skip chain list initialization only + * if both chain and table are specified. */ + if (chain && table && strcmp(table, h->tables[i].name)) + continue; + + if (h->cache->table[type].chains) + nftnl_chain_list_free(h->cache->table[type].chains); + h->cache->table[type].chains = nftnl_chain_list_alloc(); if (!h->cache->table[type].chains) return -1; } - nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family, - NLM_F_DUMP, h->seq); + if (table && chain) { + struct nftnl_chain *c; + + nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family, + NLM_F_ACK, h->seq); + c = nftnl_chain_alloc(); + if (!c) + return -1; + nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table); + nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain); + nftnl_chain_nlmsg_build_payload(nlh, c); + nftnl_chain_free(c); + } else { + nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family, + NLM_F_DUMP, h->seq); + } ret = mnl_talk(h, nlh, nftnl_chain_list_cb, h); if (ret < 0 && errno == EINTR) @@ -1596,6 +1620,9 @@ static int fetch_rule_cache(struct nft_handle *h) if (!h->tables[i].name) continue; + if (!h->cache->table[type].chains) + continue; + if (nftnl_chain_list_foreach(h->cache->table[type].chains, nft_rule_list_update, h)) return -1; @@ -1609,7 +1636,7 @@ static void __nft_build_cache(struct nft_handle *h) retry: mnl_genid_get(h, &genid_start); - fetch_chain_cache(h); + fetch_chain_cache(h, NULL, NULL); fetch_rule_cache(h); h->have_chain_cache = true; h->have_rule_cache = true; @@ -1690,7 +1717,7 @@ static void nft_release_cache(struct nft_handle *h) } struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h, - const char *table) + const char *table, const char *chain) { const struct builtin_table *t; @@ -1699,8 +1726,9 @@ struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h, return NULL; if (!h->have_chain_cache) { - fetch_chain_cache(h); - h->have_chain_cache = true; + fetch_chain_cache(h, table, chain); + if (!chain) + h->have_chain_cache = true; } return h->cache->table[t->type].chains; @@ -1782,7 +1810,7 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format) struct nftnl_chain *c; int ret = 0; - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, NULL); if (!list) return 0; @@ -1845,7 +1873,7 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table, nft_fn = nft_rule_flush; - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, chain); if (list == NULL) { ret = 1; goto err; @@ -1910,7 +1938,7 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c); - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, NULL); if (list) nftnl_chain_list_add(c, list); @@ -1950,7 +1978,7 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c); - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, chain); if (list) nftnl_chain_list_add(c, list); @@ -2005,7 +2033,7 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain, nft_fn = nft_chain_user_del; - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, chain); if (list == NULL) return 0; @@ -2037,7 +2065,7 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain) { struct nftnl_chain_list *list; - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, chain); if (list == NULL) return NULL; @@ -2584,7 +2612,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, return 0; } - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, chain); if (!list) return 0; @@ -2687,7 +2715,7 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, return 0; } - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, chain); if (!list) return 0; @@ -3371,7 +3399,7 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain, struct nftnl_chain *c; int ret = 0; - list = nft_chain_list_get(h, table); + list = nft_chain_list_get(h, table, chain); if (list == NULL) goto err; @@ -3479,7 +3507,7 @@ bool nft_is_table_compatible(struct nft_handle *h, const char *tablename) { struct nftnl_chain_list *clist; - clist = nft_chain_list_get(h, tablename); + clist = nft_chain_list_get(h, tablename, NULL); if (clist == NULL) return false; diff --git a/iptables/nft.h b/iptables/nft.h index 718acdbf0c55d..4b1c191effbd6 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -98,7 +98,8 @@ struct nftnl_chain; int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters); struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h, - const char *table); + const char *table, + const char *chain); int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list); int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table); int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table, bool verbose); diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index d1486afedc480..835a21be7324f 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -62,7 +62,7 @@ static struct nftnl_chain_list *get_chain_list(struct nft_handle *h, { struct nftnl_chain_list *chain_list; - chain_list = nft_chain_list_get(h, table); + chain_list = nft_chain_list_get(h, table, NULL); if (chain_list == NULL) xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n"); diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 77b13f7ffbcdd..503ae401737c5 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -82,7 +82,7 @@ __do_output(struct nft_handle *h, const char *tablename, void *data) return 0; } - chain_list = nft_chain_list_get(h, tablename); + chain_list = nft_chain_list_get(h, tablename, NULL); if (!chain_list) return 0; -- 2.23.0