@@ -202,26 +202,51 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
return NULL;
}
+static int
+nft_cache_add_base_chain(struct nft_handle *h, const struct builtin_table *t,
+ struct nft_chain *nc)
+{
+ uint32_t hooknum = nftnl_chain_get_u32(nc->nftnl, NFTNL_CHAIN_HOOKNUM);
+ const char *name = nftnl_chain_get_str(nc->nftnl, NFTNL_CHAIN_NAME);
+ const char *type = nftnl_chain_get_str(nc->nftnl, NFTNL_CHAIN_TYPE);
+ uint32_t prio = nftnl_chain_get_u32(nc->nftnl, NFTNL_CHAIN_PRIO);
+ const struct builtin_chain *bc = NULL;
+ int i;
+
+ for (i = 0; i < NF_IP_NUMHOOKS && t->chains[i].name != NULL; i++) {
+ if (hooknum == t->chains[i].hook) {
+ bc = &t->chains[i];
+ break;
+ }
+ }
+
+ if (!bc ||
+ prio != bc->prio ||
+ strcmp(name, bc->name) ||
+ strcmp(type, bc->type))
+ return -EINVAL;
+
+ if (h->cache->table[t->type].base_chains[hooknum])
+ return -EEXIST;
+
+ h->cache->table[t->type].base_chains[hooknum] = nc;
+ return 0;
+}
+
int nft_cache_add_chain(struct nft_handle *h, const struct builtin_table *t,
struct nftnl_chain *c)
{
const char *cname = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
struct nft_chain *nc = nft_chain_alloc(c);
+ int ret;
if (nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM)) {
- uint32_t hooknum = nftnl_chain_get_u32(c, NFTNL_CHAIN_HOOKNUM);
-
- if (hooknum >= NF_INET_NUMHOOKS) {
+ ret = nft_cache_add_base_chain(h, t, nc);
+ if (ret) {
+ h->cache->table[t->type].tainted = true;
nft_chain_free(nc);
- return -EINVAL;
+ return ret;
}
-
- if (h->cache->table[t->type].base_chains[hooknum]) {
- nft_chain_free(nc);
- return -EEXIST;
- }
-
- h->cache->table[t->type].base_chains[hooknum] = nc;
} else {
list_add_tail(&nc->head,
&h->cache->table[t->type].chains->list);
@@ -3513,38 +3513,8 @@ static int nft_is_rule_compatible(struct nftnl_rule *rule, void *data)
static int nft_is_chain_compatible(struct nft_chain *nc, void *data)
{
struct nftnl_chain *c = nc->nftnl;
- const struct builtin_table *table;
- const struct builtin_chain *chain;
- const char *tname, *cname, *type;
- struct nft_handle *h = data;
- enum nf_inet_hooks hook;
- int prio;
-
- if (nftnl_rule_foreach(c, nft_is_rule_compatible, NULL))
- return -1;
-
- if (!nft_chain_builtin(c))
- return 0;
-
- tname = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
- table = nft_table_builtin_find(h, tname);
- if (!table)
- return -1;
-
- cname = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
- chain = nft_chain_builtin_find(table, cname);
- if (!chain)
- return -1;
- type = nftnl_chain_get_str(c, NFTNL_CHAIN_TYPE);
- prio = nftnl_chain_get_u32(c, NFTNL_CHAIN_PRIO);
- hook = nftnl_chain_get_u32(c, NFTNL_CHAIN_HOOKNUM);
- if (strcmp(type, chain->type) ||
- prio != chain->prio ||
- hook != chain->hook)
- return -1;
-
- return 0;
+ return nftnl_rule_foreach(c, nft_is_rule_compatible, NULL);
}
bool nft_is_table_compatible(struct nft_handle *h,
@@ -3559,13 +3529,24 @@ bool nft_is_table_compatible(struct nft_handle *h,
return !nft_chain_foreach(h, table, nft_is_chain_compatible, h);
}
+bool nft_is_table_tainted(struct nft_handle *h, const char *table)
+{
+ const struct builtin_table *t = nft_table_builtin_find(h, table);
+
+ return t ? h->cache->table[t->type].tainted : false;
+}
+
void nft_assert_table_compatible(struct nft_handle *h,
const char *table, const char *chain)
{
const char *pfx = "", *sfx = "";
- if (nft_is_table_compatible(h, table, chain))
+ if (nft_is_table_compatible(h, table, chain)) {
+ if (nft_is_table_tainted(h, table))
+ printf("# Table `%s' contains incompatible base-chains, use 'nft' tool to list them.\n",
+ table);
return;
+ }
if (chain) {
pfx = "chain `";
@@ -45,6 +45,7 @@ struct nft_cache {
struct nftnl_set_list *sets;
bool exists;
bool sorted;
+ bool tainted;
} table[NFT_TABLE_MAX];
};
@@ -262,6 +263,7 @@ void nft_rule_to_arpt_entry(struct nftnl_rule *r, struct arpt_entry *fw);
bool nft_is_table_compatible(struct nft_handle *h,
const char *table, const char *chain);
+bool nft_is_table_tainted(struct nft_handle *h, const char *table);
void nft_assert_table_compatible(struct nft_handle *h,
const char *table, const char *chain);
@@ -13,6 +13,10 @@ set -e
nft -f - <<EOF
table ip filter {
+ chain a {
+ type filter hook input priority filter
+ }
+
chain INPUT {
type filter hook input priority filter
counter packets 218 bytes 91375 accept
@@ -24,4 +28,10 @@ table ip filter {
}
EOF
-$XT_MULTI iptables -L
+EXPECT="# Table \`filter' contains incompatible base-chains, use 'nft' tool to list them.
+-P INPUT ACCEPT
+-P FORWARD ACCEPT
+-P OUTPUT ACCEPT
+-A INPUT -j ACCEPT"
+
+diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -S)
@@ -78,6 +78,9 @@ __do_output(struct nft_handle *h, const char *tablename, void *data)
printf("# Table `%s' is incompatible, use 'nft' tool.\n",
tablename);
return 0;
+ } else if (nft_is_table_tainted(h, tablename)) {
+ printf("# Table `%s' contains incompatible base-chains, use 'nft' tool to list them.\n",
+ tablename);
}
now = time(NULL);
With introduction of dedicated base-chain slots, a selection process was established as no longer all base-chains ended in the same chain list for later searching/checking but only the first one found for each hook matching criteria is kept and the rest discarded. A side-effect of the above is that table compatibility checking started to omit consecutive base-chains, making iptables-nft less restrictive as long as the expected base-chains were returned first from kernel when populating the cache. Make behaviour consistent and warn users about the possibly disturbing chains found by: * Run all base-chain checks from nft_is_chain_compatible() before allowing a base-chain to occupy its slot. * If an unfit base-chain was found (and discarded), flag the table's cache as tainted and warn about it if the remaining ruleset is otherwise compatible. Since base-chains that remain in cache would pass nft_is_chain_compatible() checking, remove that and reduce it to rule inspection. Signed-off-by: Phil Sutter <phil@nwl.cc> --- iptables/nft-cache.c | 47 ++++++++++++++----- iptables/nft.c | 45 +++++------------- iptables/nft.h | 2 + .../shell/testcases/chain/0004extra-base_0 | 12 ++++- iptables/xtables-save.c | 3 ++ 5 files changed, 65 insertions(+), 44 deletions(-)