From a09e8ae2a1e2ff589af839ad3460493fc04306d7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <phil@nwl.cc>
Date: Thu, 15 Nov 2018 14:53:02 +0100
Subject: [PATCH] xtables: Introduce per table chain caches
Being able to omit the previously obligatory table name check when
iterating over the chain cache might help restore performance with large
rulesets in xtables-save and -restore.
There is one subtle quirk in the code: flush_chain_cache() did free the
global chain cache if not called with a table name but didn't if a table
name was given even if it emptied the chain cache. In other places,
chain_cache being non-NULL prevented a cache update from happening, so
this patch establishes the same behaviour (for each individual chain
cache) since otherwise unexpected cache updates lead to weird problems.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
(cherry picked from commit c58ecf9f8bcb7619a27ef8ffaddf847a562475a5)
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
iptables/nft-shared.h | 3 +-
iptables/nft.c | 160 +++++++++++++++++--------------------
iptables/nft.h | 10 ++-
iptables/xtables-restore.c | 16 ++--
iptables/xtables-save.c | 12 +--
5 files changed, 95 insertions(+), 106 deletions(-)
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index e3ecdb4d23df3..9a61d8d2863e3 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -251,7 +251,8 @@ struct nftnl_chain_list;
struct nft_xt_restore_cb {
void (*table_new)(struct nft_handle *h, const char *table);
- struct nftnl_chain_list *(*chain_list)(struct nft_handle *h);
+ struct nftnl_chain_list *(*chain_list)(struct nft_handle *h,
+ const char *table);
void (*chain_del)(struct nftnl_chain_list *clist, const char *curtable,
const char *chain);
int (*chain_user_flush)(struct nft_handle *h,
diff --git a/iptables/nft.c b/iptables/nft.c
index 6863d851e44c2..36529048a0ca6 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -673,15 +673,17 @@ nft_chain_builtin_find(struct builtin_table *t, const char *chain)
static void nft_chain_builtin_init(struct nft_handle *h,
struct builtin_table *table)
{
- struct nftnl_chain_list *list = nft_chain_list_get(h);
+ struct nftnl_chain_list *list = nft_chain_list_get(h, table->name);
struct nftnl_chain *c;
int i;
+ if (!list)
+ return;
+
/* Initialize built-in chains if they don't exist yet */
for (i=0; i < NF_INET_NUMHOOKS && table->chains[i].name != NULL; i++) {
- c = nft_chain_list_find(list, table->name,
- table->chains[i].name);
+ c = nft_chain_list_find(list, table->chains[i].name);
if (c != NULL)
continue;
@@ -782,27 +784,33 @@ static void flush_rule_cache(struct nft_handle *h, const char *tablename)
static int __flush_chain_cache(struct nftnl_chain *c, void *data)
{
- const char *tablename = data;
-
- if (!strcmp(nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), tablename)) {
- nftnl_chain_list_del(c);
- nftnl_chain_free(c);
- }
+ nftnl_chain_list_del(c);
+ nftnl_chain_free(c);
return 0;
}
static void flush_chain_cache(struct nft_handle *h, const char *tablename)
{
- if (!h->chain_cache)
- return;
+ int i;
- if (tablename) {
- nftnl_chain_list_foreach(h->chain_cache, __flush_chain_cache,
- (void *)tablename);
- } else {
- nftnl_chain_list_free(h->chain_cache);
- h->chain_cache = NULL;
+ for (i = 0; i < NFT_TABLE_MAX; i++) {
+ if (h->tables[i].name == NULL)
+ continue;
+
+ if (tablename && strcmp(h->tables[i].name, tablename))
+ continue;
+
+ if (h->tables[i].chain_cache) {
+ if (tablename) {
+ nftnl_chain_list_foreach(h->tables[i].chain_cache,
+ __flush_chain_cache, NULL);
+ break;
+ } else {
+ nftnl_chain_list_free(h->tables[i].chain_cache);
+ h->tables[i].chain_cache = NULL;
+ }
+ }
}
}
@@ -1244,8 +1252,9 @@ nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
{
+ struct nft_handle *h = data;
+ struct builtin_table *t;
struct nftnl_chain *c;
- struct nftnl_chain_list *list = data;
c = nftnl_chain_alloc();
if (c == NULL)
@@ -1254,7 +1263,18 @@ static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
goto out;
- nftnl_chain_list_add_tail(c, list);
+ t = nft_table_builtin_find(h,
+ nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE));
+ if (!t)
+ goto out;
+
+ if (!t->chain_cache) {
+ t->chain_cache = nftnl_chain_list_alloc();
+ if (!t->chain_cache)
+ goto out;
+ }
+
+ nftnl_chain_list_add_tail(c, t->chain_cache);
return MNL_CB_OK;
out:
@@ -1263,35 +1283,34 @@ err:
return MNL_CB_OK;
}
-struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h)
+struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+ const char *table)
{
char buf[16536];
struct nlmsghdr *nlh;
- struct nftnl_chain_list *list;
+ struct builtin_table *t;
int ret;
- if (h->chain_cache)
- return h->chain_cache;
-retry:
- list = nftnl_chain_list_alloc();
- if (list == NULL) {
- errno = ENOMEM;
+ t = nft_table_builtin_find(h, table);
+ if (!t)
return NULL;
- }
+ if (t->chain_cache)
+ return t->chain_cache;
+retry:
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, list);
+ ret = mnl_talk(h, nlh, nftnl_chain_list_cb, h);
if (ret < 0 && errno == EINTR) {
assert(nft_restart(h) >= 0);
- nftnl_chain_list_free(list);
goto retry;
}
- h->chain_cache = list;
+ if (!t->chain_cache)
+ t->chain_cache = nftnl_chain_list_alloc();
- return list;
+ return t->chain_cache;
}
static const char *policy_name[NF_ACCEPT+1] = {
@@ -1299,8 +1318,7 @@ static const char *policy_name[NF_ACCEPT+1] = {
[NF_ACCEPT] = "ACCEPT",
};
-int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
- const char *table)
+int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
{
struct nftnl_chain_list_iter *iter;
struct nft_family_ops *ops;
@@ -1314,13 +1332,8 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
- const char *chain_table =
- nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *policy = NULL;
- if (strcmp(table, chain_table) != 0)
- goto next;
-
if (nft_chain_builtin(c)) {
uint32_t pol = NF_ACCEPT;
@@ -1331,7 +1344,7 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
if (ops->save_chain)
ops->save_chain(c, policy);
-next:
+
c = nftnl_chain_list_iter_next(iter);
}
@@ -1502,7 +1515,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);
+ list = nft_chain_list_get(h, table);
if (list == NULL) {
ret = 1;
goto err;
@@ -1516,21 +1529,16 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
- const char *table_name =
- nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
- if (strcmp(table, table_name) != 0)
- goto next;
-
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
if (verbose)
fprintf(stdout, "Flushing chain `%s'\n", chain_name);
- __nft_rule_flush(h, table_name, chain_name);
+ __nft_rule_flush(h, table, chain_name);
if (chain != NULL)
break;
@@ -1546,6 +1554,7 @@ err:
int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
{
+ struct nftnl_chain_list *list;
struct nftnl_chain *c;
int ret;
@@ -1564,9 +1573,9 @@ 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);
- nft_chain_list_get(h);
-
- nftnl_chain_list_add(c, h->chain_cache);
+ list = nft_chain_list_get(h, table);
+ if (list)
+ nftnl_chain_list_add(c, list);
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
@@ -1588,7 +1597,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);
+ list = nft_chain_list_get(h, table);
if (list == NULL)
goto err;
@@ -1598,8 +1607,6 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
- const char *table_name =
- nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
@@ -1607,9 +1614,6 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
if (nft_chain_builtin(c))
goto next;
- if (strcmp(table, table_name) != 0)
- goto next;
-
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
@@ -1644,8 +1648,7 @@ err:
}
struct nftnl_chain *
-nft_chain_list_find(struct nftnl_chain_list *list,
- const char *table, const char *chain)
+nft_chain_list_find(struct nftnl_chain_list *list, const char *chain)
{
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
@@ -1656,14 +1659,9 @@ nft_chain_list_find(struct nftnl_chain_list *list,
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
- const char *table_name =
- nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
- if (strcmp(table, table_name) != 0)
- goto next;
-
if (strcmp(chain, chain_name) != 0)
goto next;
@@ -1681,11 +1679,11 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
{
struct nftnl_chain_list *list;
- list = nft_chain_list_get(h);
+ list = nft_chain_list_get(h, table);
if (list == NULL)
return NULL;
- return nft_chain_list_find(list, table, chain);
+ return nft_chain_list_find(list, chain);
}
bool nft_chain_exists(struct nft_handle *h,
@@ -2297,7 +2295,9 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
return 1;
}
- list = nft_chain_list_get(h);
+ list = nft_chain_list_get(h, table);
+ if (!list)
+ goto err; /* XXX: return 0 instead? */
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
@@ -2308,8 +2308,6 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
- const char *chain_table =
- nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
uint32_t policy =
@@ -2326,8 +2324,6 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
basechain = true;
- if (strcmp(table, chain_table) != 0)
- goto next;
if (chain) {
if (strcmp(chain, chain_name) != 0)
goto next;
@@ -2442,7 +2438,9 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
return 0;
}
- list = nft_chain_list_get(h);
+ list = nft_chain_list_get(h, table);
+ if (!list)
+ goto err; /* XXX: correct? */
/* Dump policies and custom chains first */
if (!rulenum)
@@ -2460,13 +2458,9 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
c = nftnl_chain_list_iter_next(iter);
while (c != NULL) {
- const char *chain_table =
- nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
const char *chain_name =
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
- if (strcmp(table, chain_table) != 0)
- goto next;
if (chain && strcmp(chain, chain_name) != 0)
goto next;
@@ -3045,7 +3039,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);
+ list = nft_chain_list_get(h, table);
if (list == NULL)
goto err;
@@ -3057,11 +3051,6 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
while (c != NULL) {
const char *chain_name =
nftnl_chain_get(c, NFTNL_CHAIN_NAME);
- const char *chain_table =
- nftnl_chain_get(c, NFTNL_CHAIN_TABLE);
-
- if (strcmp(table, chain_table) != 0)
- goto next;
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
@@ -3202,7 +3191,7 @@ static int nft_are_chains_compatible(struct nft_handle *h, const char *tablename
struct nftnl_chain *chain;
int ret = 0;
- list = nft_chain_list_get(h);
+ list = nft_chain_list_get(h, tablename);
if (list == NULL)
return -1;
@@ -3212,12 +3201,7 @@ static int nft_are_chains_compatible(struct nft_handle *h, const char *tablename
chain = nftnl_chain_list_iter_next(iter);
while (chain != NULL) {
- const char *chain_table;
-
- chain_table = nftnl_chain_get_str(chain, NFTNL_CHAIN_TABLE);
-
- if (strcmp(chain_table, tablename) ||
- !nft_chain_builtin(chain))
+ if (!nft_chain_builtin(chain))
goto next;
ret = nft_is_chain_compatible(h, chain);
diff --git a/iptables/nft.h b/iptables/nft.h
index 052105fc6f3cd..6229221bd51f7 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -25,6 +25,7 @@ struct builtin_table {
const char *name;
struct builtin_chain chains[NF_INET_NUMHOOKS];
bool initialized;
+ struct nftnl_chain_list *chain_cache;
};
struct nft_handle {
@@ -38,7 +39,6 @@ struct nft_handle {
struct list_head err_list;
struct nft_family_ops *ops;
struct builtin_table *tables;
- struct nftnl_chain_list *chain_cache;
struct nftnl_rule_list *rule_cache;
bool restore;
int8_t config_done;
@@ -78,9 +78,11 @@ struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *t
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);
-struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list, const char *table, const char *chain);
-int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list, const char *table);
+struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+ const char *table);
+struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list,
+ 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);
int nft_chain_user_flush(struct nft_handle *h, struct nftnl_chain_list *list,
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
index f529774054215..a46a92955a01a 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -56,11 +56,12 @@ static void print_usage(const char *name, const char *version)
" [ --ipv6 ]\n", name);
}
-static struct nftnl_chain_list *get_chain_list(struct nft_handle *h)
+static struct nftnl_chain_list *get_chain_list(struct nft_handle *h,
+ const char *table)
{
struct nftnl_chain_list *chain_list;
- chain_list = nft_chain_list_get(h);
+ chain_list = nft_chain_list_get(h, table);
if (chain_list == NULL)
xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n");
@@ -72,7 +73,7 @@ static void chain_delete(struct nftnl_chain_list *clist, const char *curtable,
{
struct nftnl_chain *chain_obj;
- chain_obj = nft_chain_list_find(clist, curtable, chain);
+ chain_obj = nft_chain_list_find(clist, chain);
/* This chain has been found, delete from list. Later
* on, unvisited chains will be purged out.
*/
@@ -112,9 +113,6 @@ void xtables_restore_parse(struct nft_handle *h,
line = 0;
- if (cb->chain_list)
- chain_list = cb->chain_list(h);
-
/* Grab standard input. */
while (fgets(buffer, sizeof(buffer), p->in)) {
int ret = 0;
@@ -165,6 +163,9 @@ void xtables_restore_parse(struct nft_handle *h,
if (p->tablename && (strcmp(p->tablename, table) != 0))
continue;
+ if (cb->chain_list)
+ chain_list = cb->chain_list(h, table);
+
if (noflush == 0) {
DEBUGP("Cleaning all chains of table '%s'\n",
table);
@@ -197,8 +198,7 @@ void xtables_restore_parse(struct nft_handle *h,
if (cb->chain_del)
cb->chain_del(chain_list, curtable->name,
chain);
- } else if (nft_chain_list_find(chain_list,
- curtable->name, chain)) {
+ } else if (nft_chain_list_find(chain_list, chain)) {
chain_exists = true;
/* Apparently -n still flushes existing user
* defined chains that are redefined. Otherwise,
diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
index bed3ee0318995..d121d50e180ff 100644
--- a/iptables/xtables-save.c
+++ b/iptables/xtables-save.c
@@ -73,7 +73,9 @@ __do_output(struct nft_handle *h, const char *tablename, bool counters)
return 0;
}
- chain_list = nft_chain_list_get(h);
+ chain_list = nft_chain_list_get(h, tablename);
+ if (!chain_list)
+ return 0;
time_t now = time(NULL);
@@ -83,7 +85,7 @@ __do_output(struct nft_handle *h, const char *tablename, bool counters)
/* Dump out chain names first,
* thereby preventing dependency conflicts */
- nft_chain_save(h, chain_list, tablename);
+ nft_chain_save(h, chain_list);
nft_rule_save(h, tablename, counters ? 0 : FMT_NOCOUNTS);
now = time(NULL);
@@ -257,7 +259,7 @@ static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters
return 0;
}
- chain_list = nft_chain_list_get(h);
+ chain_list = nft_chain_list_get(h, tablename);
if (first) {
now = time(NULL);
@@ -272,7 +274,7 @@ static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters
/* Dump out chain names first,
* thereby preventing dependency conflicts */
- nft_chain_save(h, chain_list, tablename);
+ nft_chain_save(h, chain_list);
nft_rule_save(h, tablename, format);
printf("\n");
return 0;
@@ -399,7 +401,7 @@ int xtables_arp_save_main(int argc, char **argv)
}
printf("*filter\n");
- nft_chain_save(&h, nft_chain_list_get(&h), "filter");
+ nft_chain_save(&h, nft_chain_list_get(&h, "filter"));
nft_rule_save(&h, "filter", show_counters ? 0 : FMT_NOCOUNTS);
printf("\n");
nft_fini(&h);
--
2.21.0