add xtables-events

Add new program to listen to rule updates:

shell$ xtables-events
-A INPUT -m state --state ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-D INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-D INPUT -m state --state ESTABLISHED -j ACCEPT

You can use `-c' option to display counters.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index 2b1f3fa..2ed1696 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -31,7 +31,7 @@
 xtables_multi_SOURCES += xtables-save.c xtables-restore.c \
 			 xtables-standalone.c xtables.c nft.c \
 			 xtables-config-parser.y xtables-config-syntax.l \
-			 xtables-config.c
+			 xtables-config.c xtables-events.c
 xtables_multi_LDADD   += -lmnl -lnftables
 xtables_multi_CFLAGS  += -DENABLE_NFTABLES
 # yacc and lex generate dirty code
@@ -55,7 +55,8 @@
 v6_sbin_links  = ip6tables ip6tables-restore ip6tables-save
 endif
 if ENABLE_NFTABLES
-x_sbin_links  = xtables xtables-restore xtables-save xtables-config
+x_sbin_links  = xtables xtables-restore xtables-save xtables-config \
+		xtables-events
 endif
 
 iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man
diff --git a/iptables/nft.c b/iptables/nft.c
index cef1f32..097c28b 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -1385,13 +1385,22 @@
 	}
 }
 
-static void nft_rule_print_save(struct nft_rule *r, bool counters)
+void
+nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters)
 {
 	struct nft_rule_expr_iter *iter;
 	struct nft_rule_expr *expr;
+	const char *chain = nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
 
 	/* print chain name */
-	printf("-A %s ", nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN));
+	switch(type) {
+	case NFT_RULE_APPEND:
+		printf("-A %s ", chain);
+		break;
+	case NFT_RULE_DEL:
+		printf("-D %s ", chain);
+		break;
+	}
 
 	iter = nft_rule_expr_iter_create(r);
 	if (iter == NULL)
@@ -1614,7 +1623,7 @@
 		if (strcmp(table, rule_table) != 0)
 			goto next;
 
-		nft_rule_print_save(r, counters);
+		nft_rule_print_save(r, NFT_RULE_APPEND, counters);
 
 next:
 		r = nft_rule_list_iter_next(iter);
@@ -2714,7 +2723,7 @@
 			/* Delete by matching rule case */
 			DEBUGP("comparing with... ");
 #ifdef DEBUG_DEL
-			nft_rule_print_save(r, 0);
+			nft_rule_print_save(r, NFT_RULE_APPEND, 0);
 #endif
 
 			nft_rule_to_iptables_command_state(r, &this);
@@ -3378,7 +3387,7 @@
 list_save(const struct iptables_command_state *cs, struct nft_rule *r,
 	  unsigned int num, unsigned int format)
 {
-	nft_rule_print_save(r, !(format & FMT_NOCOUNTS));
+	nft_rule_print_save(r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS));
 }
 
 static int
diff --git a/iptables/nft.h b/iptables/nft.h
index 834fff0..3cffb77 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -56,6 +56,13 @@
 int nft_rule_save(struct nft_handle *h, const char *table, bool counters);
 int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table);
 
+enum nft_rule_print {
+	NFT_RULE_APPEND,
+	NFT_RULE_DEL,
+};
+
+void nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters);
+
 /*
  * global commit and abort
  */
diff --git a/iptables/xtables-events.c b/iptables/xtables-events.c
new file mode 100644
index 0000000..2600a25
--- /dev/null
+++ b/iptables/xtables-events.c
@@ -0,0 +1,208 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <getopt.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+#include <libnftables/rule.h>
+
+#include <include/xtables.h>
+#include "iptables.h" /* for xtables_globals */
+#include "xtables-multi.h"
+#include "nft.h"
+
+static int table_cb(const struct nlmsghdr *nlh, int type)
+{
+	struct nft_table *t;
+	char buf[4096];
+
+	t = nft_table_alloc();
+	if (t == NULL) {
+		perror("OOM");
+		goto err;
+	}
+
+	if (nft_table_nlmsg_parse(nlh, t) < 0) {
+		perror("nft_table_nlmsg_parse");
+		goto err_free;
+	}
+
+	nft_table_snprintf(buf, sizeof(buf), t, NFT_TABLE_O_DEFAULT, 0);
+	/* FIXME: define syntax to represent table events */
+	printf("# [table: %s]\t%s", type == NFT_MSG_NEWTABLE ? "NEW" : "DEL", buf);
+
+err_free:
+	nft_table_free(t);
+err:
+	return MNL_CB_OK;
+}
+
+static bool counters;
+
+static int rule_cb(const struct nlmsghdr *nlh, int type)
+{
+	struct nft_rule *r;
+
+	r = nft_rule_alloc();
+	if (r == NULL) {
+		perror("OOM");
+		goto err;
+	}
+
+	if (nft_rule_nlmsg_parse(nlh, r) < 0) {
+		perror("nft_rule_nlmsg_parse");
+		goto err_free;
+	}
+
+	switch(nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)) {
+	case AF_INET:
+		printf("-4 ");
+		break;
+	case AF_INET6:
+		printf("-6 ");
+		break;
+	default:
+		break;
+	}
+
+	nft_rule_print_save(r, type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND :
+							 NFT_RULE_DEL,
+			    counters);
+err_free:
+	nft_rule_free(r);
+err:
+	return MNL_CB_OK;
+}
+
+static int chain_cb(const struct nlmsghdr *nlh, int type)
+{
+	struct nft_chain *t;
+	char buf[4096];
+
+	t = nft_chain_alloc();
+	if (t == NULL) {
+		perror("OOM");
+		goto err;
+	}
+
+	if (nft_chain_nlmsg_parse(nlh, t) < 0) {
+		perror("nft_chain_nlmsg_parse");
+		goto err_free;
+	}
+
+	nft_chain_snprintf(buf, sizeof(buf), t, NFT_CHAIN_O_DEFAULT, 0);
+	/* FIXME: define syntax to represent chain events */
+	printf("# [chain: %s]\t%s", type == NFT_MSG_NEWCHAIN ? "NEW" : "DEL", buf);
+
+err_free:
+	nft_chain_free(t);
+err:
+	return MNL_CB_OK;
+}
+
+static int events_cb(const struct nlmsghdr *nlh, void *data)
+{
+	int ret = MNL_CB_OK;
+	int type = nlh->nlmsg_type & 0xFF;
+
+	switch(type) {
+	case NFT_MSG_NEWTABLE:
+	case NFT_MSG_DELTABLE:
+		ret = table_cb(nlh, type);
+		break;
+	case NFT_MSG_NEWCHAIN:
+	case NFT_MSG_DELCHAIN:
+		ret = chain_cb(nlh, type);
+		break;
+	case NFT_MSG_NEWRULE:
+	case NFT_MSG_DELRULE:
+		ret = rule_cb(nlh, type);
+		break;
+	}
+
+	return ret;
+}
+
+static const struct option options[] = {
+	{.name = "counters", .has_arg = false, .val = 'c'},
+	{NULL},
+};
+
+static void print_usage(const char *name, const char *version)
+{
+	fprintf(stderr, "Usage: %s [-c]\n"
+			"	   [ --counters ]\n", name);
+	exit(EXIT_FAILURE);
+}
+
+int xtables_events_main(int argc, char *argv[])
+{
+	struct mnl_socket *nl;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	int ret, c;
+
+	xtables_globals.program_name = "xtables-events";
+	/* XXX xtables_init_all does several things we don't want */
+	c = xtables_init_all(&xtables_globals, NFPROTO_IPV4);
+	if (c < 0) {
+		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+				xtables_globals.program_name,
+				xtables_globals.program_version);
+		exit(1);
+	}
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+	init_extensions();
+	init_extensions4();
+#endif
+
+	while ((c = getopt_long(argc, argv, "c", options, NULL)) != -1) {
+		switch (c) {
+	        case 'c':
+			counters = true;
+			break;
+		}
+	}
+
+	nl = mnl_socket_open(NETLINK_NETFILTER);
+	if (nl == NULL) {
+		perror("mnl_socket_open");
+		exit(EXIT_FAILURE);
+	}
+
+	if (mnl_socket_bind(nl, (1 << (NFNLGRP_NFTABLES-1)), MNL_SOCKET_AUTOPID) < 0) {
+		perror("mnl_socket_bind");
+		exit(EXIT_FAILURE);
+	}
+
+	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+	while (ret > 0) {
+		ret = mnl_cb_run(buf, ret, 0, 0, events_cb, NULL);
+		if (ret <= 0)
+			break;
+		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+	}
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+	mnl_socket_close(nl);
+
+	return EXIT_SUCCESS;
+}
diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c
index c174643..5732ba3 100644
--- a/iptables/xtables-multi.c
+++ b/iptables/xtables-multi.c
@@ -41,6 +41,7 @@
 	{"xtables-save",        xtables_save_main},
 	{"xtables-restore",     xtables_restore_main},
 	{"xtables-config",      xtables_config_main},
+	{"xtables-events",      xtables_events_main},
 #endif
 	{NULL},
 };
diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h
index be2b3ad..c609ea5 100644
--- a/iptables/xtables-multi.h
+++ b/iptables/xtables-multi.h
@@ -6,5 +6,6 @@
 extern int xtables_save_main(int, char **);
 extern int xtables_restore_main(int, char **);
 extern int xtables_config_main(int, char **);
+extern int xtables_events_main(int, char **);
 
 #endif /* _XTABLES_MULTI_H */