xtables-restore: add support for dormant tables

This patch adds support for dormant tables for xtables-restore.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 542b654..837bab3 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -53,9 +53,19 @@
 };
 #define NFTA_HOOK_MAX		(__NFTA_HOOK_MAX - 1)
 
+/**
+ * enum nft_table_flags - nf_tables table flags
+ *
+ * @NFT_TABLE_F_DORMANT: this table is not active
+ */
+enum nft_table_flags {
+	NFT_TABLE_F_DORMANT	= 0x1,
+};
+
 enum nft_table_attributes {
 	NFTA_TABLE_UNSPEC,
 	NFTA_TABLE_NAME,
+	NFTA_TABLE_FLAGS,
 	__NFTA_TABLE_MAX
 };
 #define NFTA_TABLE_MAX		(__NFTA_TABLE_MAX - 1)
diff --git a/iptables/nft.c b/iptables/nft.c
index 2430120..1f5ecb7 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -229,7 +229,9 @@
 	},
 };
 
-static int nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t)
+static int
+nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t,
+			bool dormant)
 {
 	char buf[MNL_SOCKET_BUFFER_SIZE];
 	struct nlmsghdr *nlh;
@@ -241,6 +243,10 @@
 		return -1;
 
 	nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)_t->name);
+	if (dormant) {
+		nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS,
+					NFT_TABLE_F_DORMANT);
+	}
 
 	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET,
 					NLM_F_ACK|NLM_F_EXCL, h->seq);
@@ -367,7 +373,7 @@
 		ret = -1;
 		goto out;
 	}
-	if (nft_table_builtin_add(h, t) < 0) {
+	if (nft_table_builtin_add(h, t, false) < 0) {
 		/* Built-in table already initialized, skip. */
 		if (errno == EEXIST)
 			goto out;
@@ -423,6 +429,49 @@
 	return mnl_talk(h, nlh, NULL, NULL);
 }
 
+int nft_table_set_dormant(struct nft_handle *h, const char *table)
+{
+	int ret = 0, i;
+	struct builtin_table *t;
+
+	t = nft_table_builtin_find(table);
+	if (t == NULL) {
+		ret = -1;
+		goto out;
+	}
+	/* Add this table as dormant */
+	if (nft_table_builtin_add(h, t, true) < 0) {
+		/* Built-in table already initialized, skip. */
+		if (errno == EEXIST)
+			goto out;
+	}
+	for (i=0; t->chains[i].name != NULL && i<NF_INET_NUMHOOKS; i++)
+		__nft_chain_builtin_init(h, t, t->chains[i].name, NF_ACCEPT);
+out:
+	return ret;
+}
+
+int nft_table_wake_dormant(struct nft_handle *h, const char *table)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	struct nft_table *t;
+
+	t = nft_table_alloc();
+	if (t == NULL)
+		return -1;
+
+	nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table);
+	nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS, 0);
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET,
+					NLM_F_ACK, h->seq);
+	nft_table_nlmsg_build_payload(nlh, t);
+	nft_table_free(t);
+
+	return mnl_talk(h, nlh, NULL, NULL);
+}
+
 static void nft_chain_print_debug(struct nft_chain *c, struct nlmsghdr *nlh)
 {
 #ifdef NLDEBUG
@@ -449,7 +498,7 @@
 	_t = nft_table_builtin_find(table);
 	/* if this built-in table does not exists, create it */
 	if (_t != NULL)
-		nft_table_builtin_add(h, _t);
+		nft_table_builtin_add(h, _t, false);
 
 	_c = nft_chain_builtin_find(_t, chain);
 	if (_c != NULL) {
diff --git a/iptables/nft.h b/iptables/nft.h
index aa458f8..aed2498 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -20,6 +20,8 @@
 int nft_table_add(struct nft_handle *h, const struct nft_table *t);
 int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, bool counters), bool counters);
 bool nft_table_find(struct nft_handle *h, const char *tablename);
+int nft_table_set_dormant(struct nft_handle *h, const char *table);
+int nft_table_wake_dormant(struct nft_handle *h, const char *table);
 
 /*
  * Operations with chains.
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
index 09922a0..30ea813 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -243,11 +243,16 @@
 				fputs(buffer, stdout);
 			continue;
 		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
-			/* FIXME commit/testing operation not supported */
 			if (!testing) {
+				if (nft_table_wake_dormant(&h, curtable) < 0) {
+					fprintf(stderr, "Failed to wake up "
+						"dormant table `%s'\n",
+						curtable);
+				}
 				DEBUGP("Calling commit\n");
 				ret = 1;
 			} else {
+				/* FIXME -t needs to be fixed */
 				DEBUGP("Not calling commit, testing\n");
 				ret = 1;
 			}
@@ -270,6 +275,7 @@
 			if (tablename && (strcmp(tablename, table) != 0))
 				continue;
 
+			nft_table_set_dormant(&h, table);
 			if (noflush == 0) {
 				DEBUGP("Cleaning all chains of table '%s'\n",
 					table);