automatic creation of built-in table and chains

In order to emulate the iptables behaviour, this patch changes
the current behaviour to:

1st) check if the table and chains are built-in.
2nd) If they don't exists, create them. If they exists, don't touch
     them.

The automatic creation happens in the -I and -P paths.

We should provide a new command to allow to delete (unregister)
built-in tables and chains. It would be similar to unloading
the iptable_X module that registers the custom table.

This is not done for other commands like -C or -D since they
will fail while trying to find the rule in the kernel if such
combination of chain and table does not exists.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
diff --git a/iptables/nft.c b/iptables/nft.c
index 91383bf..9eaa2ac 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -82,7 +82,7 @@
 	return 0;
 }
 
-static void nft_table_init_one(struct nft_handle *h, const char *name, int portid)
+static void nft_table_builtin_add(struct nft_handle *h, const char *table)
 {
 	char buf[MNL_SOCKET_BUFFER_SIZE];
 	struct nlmsghdr *nlh;
@@ -92,7 +92,7 @@
 	if (t == NULL)
 		return;
 
-	nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)name);
+	nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table);
 
 	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET,
 					NLM_F_ACK|NLM_F_EXCL, h->seq);
@@ -111,7 +111,7 @@
 #define SECURITY	3
 #define TABLES_MAX	4
 
-static struct default_table {
+static struct builtin_table {
 	const char *name;
 	uint32_t prio;
 } tables[TABLES_MAX] = {
@@ -134,15 +134,7 @@
 	/* nat already registered by nf_tables */
 };
 
-static void nft_table_init(struct nft_handle *h)
-{
-	int i;
-
-	for (i=0; i<TABLES_MAX; i++)
-		nft_table_init_one(h, tables[i].name, h->portid);
-}
-
-static struct default_chain {
+static struct builtin_chain {
 	const char *name;
 	uint32_t hook;
 } chains[TABLES_MAX][NF_IP_NUMHOOKS] = {
@@ -208,17 +200,15 @@
 	},
 };
 
-static void
-nft_chain_default_add(struct nft_handle *h, struct default_table *table,
-		      struct default_chain *chain, int policy)
+static struct nft_chain *
+nft_chain_builtin_alloc(struct builtin_table *table,
+			struct builtin_chain *chain, int policy)
 {
-	char buf[MNL_SOCKET_BUFFER_SIZE];
-	struct nlmsghdr *nlh;
 	struct nft_chain *c;
 
 	c = nft_chain_alloc();
 	if (c == NULL)
-		return;
+		return NULL;
 
 	nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table->name);
 	nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain->name);
@@ -226,6 +216,21 @@
 	nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_PRIO, table->prio);
 	nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy);
 
+	return c;
+}
+
+static void
+nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table,
+		      struct builtin_chain *chain, int policy)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	struct nft_chain *c;
+
+	c = nft_chain_builtin_alloc(table, chain, policy);
+	if (c == NULL)
+		return;
+
 	nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET,
 					NLM_F_ACK|NLM_F_EXCL, h->seq);
 	nft_chain_nlmsg_build_payload(nlh, c);
@@ -233,23 +238,72 @@
 
 	if (mnl_talk(h, nlh, NULL, NULL) < 0) {
 		if (errno != EEXIST)
-			perror("mnl_talk:nft_chain_default_add");
+			perror("mnl_talk:nft_chain_builtin_add");
 	}
 }
 
-static void nft_chain_init(struct nft_handle *h)
+/* find if built-in table already exists */
+static struct builtin_table *nft_table_builtin_find(const char *table)
+{
+	int i;
+	bool found = false;
+
+	for (i=0; i<TABLES_MAX; i++) {
+		if (strcmp(tables[i].name, table) != 0)
+			continue;
+
+		found = true;
+		break;
+	}
+
+	return found ? &tables[i] : NULL;
+}
+
+/* find if built-in chain already exists */
+static struct builtin_chain *
+nft_chain_builtin_find(const char *table, const char *chain)
 {
 	int i, j;
+	bool found = false;
 
 	for (i=0; i<TABLES_MAX; i++) {
 		for (j=0; j<NF_IP_NUMHOOKS; j++) {
-			if (chains[i][j].name == NULL)
-				break;
+			if (strcmp(tables[i].name, table) != 0 ||
+			    strcmp(chains[i][j].name, chain) != 0)
+				continue;
 
-			nft_chain_default_add(h, &tables[i], &chains[i][j],
-					      NF_ACCEPT);
+			found = true;
+			goto out;
 		}
 	}
+out:
+	return found ? &chains[i][j] : NULL;
+}
+
+static int
+nft_chain_builtin_init(struct nft_handle *h, const char *table,
+		       const char *chain, int policy)
+{
+	int ret = 0;
+	struct builtin_table *t;
+	struct builtin_chain *c;
+
+	t = nft_table_builtin_find(table);
+	if (t == NULL) {
+		ret = -1;
+		goto out;
+	}
+	nft_table_builtin_add(h, table);
+
+	c = nft_chain_builtin_find(table, chain);
+	if (c == NULL) {
+		ret = -1;
+		goto out;
+	}
+	/* If it already exists, it does not modify it */
+	nft_chain_builtin_add(h, t, c, policy);
+out:
+	return ret;
 }
 
 int nft_init(struct nft_handle *h)
@@ -266,14 +320,6 @@
 	}
 	h->portid = mnl_socket_get_portid(h->nl);
 
-/*
- * In case we want to inconditionally register all tables / chain.
- * This is not flexible and performance consuming.
- *
- *	nft_table_init(h);
- *	nft_chain_init(h);
- */
-
 	return 0;
 }
 
@@ -326,14 +372,32 @@
 	struct nlmsghdr *nlh;
 	struct nft_chain *c;
 	int ret;
+	struct builtin_table *_t;
+	struct builtin_chain *_c;
 
-	c = nft_chain_alloc();
-	if (c == NULL)
-		return -1;
+	_t = nft_table_builtin_find(table);
+	/* if this built-in table does not exists, create it */
+	if (_t != NULL)
+		nft_table_builtin_add(h, table);
 
-	nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table);
-	nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain);
-	nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy);
+	_c = nft_chain_builtin_find(table, chain);
+	if (_c != NULL) {
+		/* This is a built-in chain */
+		c = nft_chain_builtin_alloc(_t, _c, policy);
+		if (c == NULL)
+			return -1;
+
+	} else {
+		/* This is a custom chain */
+		c = nft_chain_alloc();
+		if (c == NULL)
+			return -1;
+
+		nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table);
+		nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain);
+		nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy);
+	}
+
 	if (counters) {
 		nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_BYTES,
 					counters->bcnt);
@@ -546,6 +610,9 @@
 	uint32_t op;
 	int flags = append ? NLM_F_APPEND : 0;
 
+	/* If built-in chain does not exists, create it */
+	nft_chain_builtin_init(h, table, chain, NF_ACCEPT);
+
 	nft_fn = nft_rule_add;
 
 	r = nft_rule_alloc();