All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/6] nft events reporting
@ 2014-02-26 16:09 Arturo Borrero Gonzalez
  2014-02-26 16:09 ` [RFC PATCH v2 1/6] rule: allow to print sets in plain format Arturo Borrero Gonzalez
                   ` (6 more replies)
  0 siblings, 7 replies; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 16:09 UTC (permalink / raw)
  To: netfilter-devel; +Cc: pablo

This series implements basic event reporting in the nftables CLI tool.

The first patches are some neccesary code factorization changes.
The last patch is the event reporting itself.

Its quite simple, the syntax is:
 % nft monitor [added|deleted] [tables|chains|sets|rules] [xml|json]

I've discarted using 'new|delete' keywords because 'new' collides with
the 'state new'ct option. 

Currently, 3 possible output formats:
 * A basic XML, provided by libnftnl.
 * A basic JSON, provided by libnftnl.
 * nft default-like syntax.

About this last format:

Rules are hard to print exactly as the user typed because sets.
The approach followed in the patch is:
 * keep a userspace cache of tables/anonymous sets.
 * since there are no event notifications for set_elements, query kernel
 for set_elements in the event callback.
 * since there are no event notification for deleted anon-sets, and sets names
 are reusable, scan each deleted rule to know which sets delete from the cache.
 * no need to do any caching if we are not monitoring new rule
 events in the nft default format.

So, the format with this series is as follow:

 % nft monitor -nnn
delete table ip6 filter
add table ip6 filter
add chain ip6 filter input { type filter hook input priority 0; }
add chain ip6 filter forward { type filter hook forward priority 0; }
add chain ip6 filter output { type filter hook output priority 0; }
add set ip6 filter set1 {type ipv6_address}
[...]
add rule ip filter input tcp sport { 1024-2048} tcp dport { 443, 80} counter packets 0 bytes 0 accept
delete rule ip filter input handle 94

 % nft monitor xml
<event><type>del</type><nftables>xml_object</nftables></event>

 % nft monitor json
{event:{type:"add",{"nftables":[json_object]}}}

Changes in this v2:
 * Address comments from Pablo and Patrick: Add the caching stuff and the XML/JSON format wrappers.

Please comment.
---

Arturo Borrero Gonzalez (6):
      rule: allow to print sets in plain format
      netlink: add netlink_delinearize_set() func
      rule: generalize chain_print()
      netlink: add netlink_delinearize_chain() func
      netlink: add netlink_delinearize_table() func
      src: add events reporting


 include/mnl.h     |    3 
 include/netlink.h |    8 +
 include/rule.h    |    7 +
 src/evaluate.c    |    1 
 src/mnl.c         |   45 +++-
 src/netlink.c     |  605 +++++++++++++++++++++++++++++++++++++++++++++++------
 src/parser.y      |   75 ++++++-
 src/rule.c        |  102 ++++++++-
 src/scanner.l     |    5 
 9 files changed, 757 insertions(+), 94 deletions(-)

-- 
Arturo Borrero Gonzalez

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 1/6] rule: allow to print sets in plain format
  2014-02-26 16:09 [RFC PATCH v2 0/6] nft events reporting Arturo Borrero Gonzalez
@ 2014-02-26 16:09 ` Arturo Borrero Gonzalez
  2014-02-26 16:44   ` Pablo Neira Ayuso
  2014-02-26 16:09 ` [RFC PATCH v2 2/6] netlink: add netlink_delinearize_set() func Arturo Borrero Gonzalez
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 16:09 UTC (permalink / raw)
  To: netfilter-devel; +Cc: pablo

Allow to print sets with or without format.

This is useful in situations where we want to print more or less the same
the user typed (IOW, in one single line, and with family/table info).

While at it, make family2str() function public, so it can be used in
other places.

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
---
 include/rule.h |    3 +++
 src/rule.c     |   62 +++++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 55 insertions(+), 10 deletions(-)

diff --git a/include/rule.h b/include/rule.h
index e06444e..b263593 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -125,6 +125,8 @@ extern void chain_add_hash(struct chain *chain, struct table *table);
 extern struct chain *chain_lookup(const struct table *table,
 				  const struct handle *h);
 
+extern const char *family2str(unsigned int family);
+
 /**
  * struct rule - nftables rule
  *
@@ -193,6 +195,7 @@ extern void set_free(struct set *set);
 extern void set_add_hash(struct set *set, struct table *table);
 extern struct set *set_lookup(const struct table *table, const char *name);
 extern void set_print(const struct set *set);
+extern void set_print_plain(const struct set *s);
 
 /**
  * enum cmd_ops - command operations
diff --git a/src/rule.c b/src/rule.c
index ab96e62..c774664 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -90,21 +90,37 @@ struct set *set_lookup(const struct table *table, const char *name)
 	return NULL;
 }
 
-void set_print(const struct set *set)
+struct print_fmt_options {
+	const char	*tab;
+	const char	*nl;
+	const char	*table;
+	const char	*family;
+	const char	*stmt_separator;
+};
+
+static void do_set_print(const struct set *set, struct print_fmt_options *opts)
 {
 	const char *delim = "";
 	const char *type;
 
 	type = set->flags & SET_F_MAP ? "map" : "set";
-	printf("\t%s %s {\n", type, set->handle.set);
+	printf("%s%s", opts->tab, type);
+
+	if (opts->family != NULL)
+		printf(" %s", opts->family);
+
+	if (opts->table != NULL)
+		printf(" %s", opts->table);
+
+	printf(" %s { %s", set->handle.set, opts->nl);
 
-	printf("\t\ttype %s", set->keytype->name);
+	printf("%s%stype %s", opts->tab, opts->tab, set->keytype->name);
 	if (set->flags & SET_F_MAP)
 		printf(" : %s", set->datatype->name);
-	printf("\n");
+	printf("%s", opts->stmt_separator);
 
 	if (set->flags & (SET_F_CONSTANT | SET_F_INTERVAL)) {
-		printf("\t\tflags ");
+		printf("%s%sflags ", opts->tab, opts->tab);
 		if (set->flags & SET_F_CONSTANT) {
 			printf("%sconstant", delim);
 			delim = ",";
@@ -113,15 +129,41 @@ void set_print(const struct set *set)
 			printf("%sinterval", delim);
 			delim = ",";
 		}
-		printf("\n");
+		printf("%s", opts->nl);
 	}
 
 	if (set->init != NULL && set->init->size > 0) {
-		printf("\t\telements = ");
+		printf("%s%selements = ", opts->tab, opts->tab);
 		expr_print(set->init);
-		printf("\n");
+		printf("%s", opts->nl);
 	}
-	printf("\t}\n");
+	printf("%s}%s", opts->tab, opts->nl);
+}
+
+void set_print(const struct set *s)
+{
+	struct print_fmt_options opts;
+
+	opts.tab = "\t";
+	opts.nl = "\n";
+	opts.table = NULL;
+	opts.family = NULL;
+	opts.stmt_separator = "\n";
+
+	do_set_print(s, &opts);
+}
+
+void set_print_plain(const struct set *s)
+{
+	struct print_fmt_options opts;
+
+	opts.tab = "";
+	opts.nl = "";
+	opts.table = s->handle.table;
+	opts.family = family2str(s->handle.family);
+	opts.stmt_separator = ";";
+
+	do_set_print(s, &opts);
 }
 
 struct rule *rule_alloc(const struct location *loc, const struct handle *h)
@@ -279,7 +321,7 @@ struct chain *chain_lookup(const struct table *table, const struct handle *h)
 	return NULL;
 }
 
-static const char *family2str(unsigned int family)
+const char *family2str(unsigned int family)
 {
 	switch (family) {
 		case NFPROTO_IPV4:


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 2/6] netlink: add netlink_delinearize_set() func
  2014-02-26 16:09 [RFC PATCH v2 0/6] nft events reporting Arturo Borrero Gonzalez
  2014-02-26 16:09 ` [RFC PATCH v2 1/6] rule: allow to print sets in plain format Arturo Borrero Gonzalez
@ 2014-02-26 16:09 ` Arturo Borrero Gonzalez
  2014-02-26 16:10 ` [RFC PATCH v2 3/6] rule: generalize chain_print() Arturo Borrero Gonzalez
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 16:09 UTC (permalink / raw)
  To: netfilter-devel; +Cc: pablo

Let's factorize this code, so it can be reused.

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
---
 src/netlink.c |   70 +++++++++++++++++++++++++++------------------------------
 1 file changed, 33 insertions(+), 37 deletions(-)

diff --git a/src/netlink.c b/src/netlink.c
index 2871888..74372bf 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -751,6 +751,37 @@ void netlink_dump_set(struct nft_set *nls)
 #endif
 }
 
+static struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
+					   struct nft_set *nls)
+{
+	struct set *set;
+	uint32_t data_len, keytype, datatype;
+
+	set = set_alloc(&netlink_location);
+	set->handle.family = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY);
+	set->handle.table  =
+		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_TABLE));
+	set->handle.set    =
+		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_NAME));
+
+	keytype = nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_TYPE);
+	set->keytype = dtype_map_from_kernel(keytype);
+	set->keylen        =
+		nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_LEN) * BITS_PER_BYTE;
+
+	set->flags         = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FLAGS);
+
+	datatype = nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_LEN);
+	set->datatype = dtype_map_from_kernel(datatype);
+	if (nft_set_attr_is_set(nls, NFT_SET_ATTR_DATA_LEN)) {
+		data_len = nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_LEN);
+		set->datalen = data_len * BITS_PER_BYTE;
+	}
+	list_add_tail(&set->list, &ctx->list);
+
+	return set;
+}
+
 int netlink_add_set(struct netlink_ctx *ctx, const struct handle *h,
 		    struct set *set)
 {
@@ -804,7 +835,6 @@ static int list_set_cb(struct nft_set *nls, void *arg)
 	struct netlink_ctx *ctx = arg;
 	const struct datatype *keytype, *datatype;
 	uint32_t flags, key, data;
-	struct set *set;
 
 	netlink_dump_set(nls);
 	key = nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_TYPE);
@@ -827,23 +857,7 @@ static int list_set_cb(struct nft_set *nls, void *arg)
 	} else
 		datatype = NULL;
 
-	set = set_alloc(&netlink_location);
-	set->handle.family = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY);
-	set->handle.table  =
-		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_TABLE));
-	set->handle.set    =
-		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_NAME));
-	set->keytype       = keytype;
-	set->keylen        =
-		nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_LEN) * BITS_PER_BYTE;
-	set->flags         = flags;
-	set->datatype      = datatype;
-	if (nft_set_attr_is_set(nls, NFT_SET_ATTR_DATA_LEN)) {
-		set->datalen =
-			nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_LEN) * BITS_PER_BYTE;
-	}
-	list_add_tail(&set->list, &ctx->list);
-
+	netlink_delinearize_set(ctx, nls);
 	return 0;
 }
 
@@ -867,7 +881,6 @@ int netlink_get_set(struct netlink_ctx *ctx, const struct handle *h,
 		    const struct location *loc)
 {
 	struct nft_set *nls;
-	struct set *set;
 	int err;
 
 	nls = alloc_nft_set(h);
@@ -878,24 +891,7 @@ int netlink_get_set(struct netlink_ctx *ctx, const struct handle *h,
 					"Could not receive set from kernel: %s",
 					strerror(errno));
 
-	set = set_alloc(&netlink_location);
-	set->handle.family = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY);
-	set->handle.table  =
-		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_TABLE));
-	set->handle.set    =
-		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_NAME));
-	set->keytype       =
-		 dtype_map_from_kernel(nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_TYPE));
-	set->keylen        =
-		nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_LEN) * BITS_PER_BYTE;
-	set->flags         = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FLAGS);
-	set->datatype      =
-		dtype_map_from_kernel(nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_TYPE));
-	if (nft_set_attr_is_set(nls, NFT_SET_ATTR_DATA_LEN)) {
-		set->datalen =
-			nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_LEN) * BITS_PER_BYTE;
-	}
-	list_add_tail(&set->list, &ctx->list);
+	netlink_delinearize_set(ctx, nls);
 	nft_set_free(nls);
 
 	return err;


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 3/6] rule: generalize chain_print()
  2014-02-26 16:09 [RFC PATCH v2 0/6] nft events reporting Arturo Borrero Gonzalez
  2014-02-26 16:09 ` [RFC PATCH v2 1/6] rule: allow to print sets in plain format Arturo Borrero Gonzalez
  2014-02-26 16:09 ` [RFC PATCH v2 2/6] netlink: add netlink_delinearize_set() func Arturo Borrero Gonzalez
@ 2014-02-26 16:10 ` Arturo Borrero Gonzalez
  2014-02-26 16:10 ` [RFC PATCH v2 4/6] netlink: add netlink_delinearize_chain() func Arturo Borrero Gonzalez
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 16:10 UTC (permalink / raw)
  To: netfilter-devel; +Cc: pablo

Lest generalize the chain_print() function, so we can print a plain chain
as the user typed in the basic CLI.

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
---
 include/rule.h |    1 +
 src/rule.c     |   14 ++++++++++++++
 2 files changed, 15 insertions(+)

diff --git a/include/rule.h b/include/rule.h
index b263593..9791cea 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -126,6 +126,7 @@ extern struct chain *chain_lookup(const struct table *table,
 				  const struct handle *h);
 
 extern const char *family2str(unsigned int family);
+extern void chain_print_plain(const struct chain *chain);
 
 /**
  * struct rule - nftables rule
diff --git a/src/rule.c b/src/rule.c
index c774664..d8a065a 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -397,6 +397,20 @@ static void chain_print(const struct chain *chain)
 	printf("\t}\n");
 }
 
+void chain_print_plain(const struct chain *chain)
+{
+	printf("chain %s %s %s", family2str(chain->handle.family),
+	       chain->handle.table, chain->handle.chain);
+
+	if (chain->flags & CHAIN_F_BASECHAIN) {
+		printf(" { type %s hook %s priority %u; }", chain->type,
+		       hooknum2str(chain->handle.family, chain->hooknum),
+		       chain->priority);
+	}
+
+	printf("\n");
+}
+
 struct table *table_alloc(void)
 {
 	struct table *table;


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 4/6] netlink: add netlink_delinearize_chain() func
  2014-02-26 16:09 [RFC PATCH v2 0/6] nft events reporting Arturo Borrero Gonzalez
                   ` (2 preceding siblings ...)
  2014-02-26 16:10 ` [RFC PATCH v2 3/6] rule: generalize chain_print() Arturo Borrero Gonzalez
@ 2014-02-26 16:10 ` Arturo Borrero Gonzalez
  2014-02-26 16:49   ` Pablo Neira Ayuso
  2014-02-26 16:10 ` [RFC PATCH v2 5/6] netlink: add netlink_delinearize_table() func Arturo Borrero Gonzalez
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 16:10 UTC (permalink / raw)
  To: netfilter-devel; +Cc: pablo

Let's make this code reusable.

Also, this patch fixes a hidden bug: the table in the chain's handle was being
set to the chain name.

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
---
 src/netlink.c |   48 ++++++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 26 deletions(-)

diff --git a/src/netlink.c b/src/netlink.c
index 74372bf..d2a7804 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -496,25 +496,16 @@ int netlink_delete_chain(struct netlink_ctx *ctx, const struct handle *h,
 	return err;
 }
 
-static int list_chain_cb(struct nft_chain *nlc, void *arg)
+static struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
+					       struct nft_chain *nlc)
 {
-	struct netlink_ctx *ctx = arg;
-	const struct handle *h = ctx->data;
 	struct chain *chain;
 
-	if ((h->family != nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY)) ||
-	    strcmp(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE), h->table) != 0)
-		return 0;
-
-	if (h->chain &&
-	    strcmp(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME), h->chain) != 0)
-		return 0;
-
 	chain = chain_alloc(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
 	chain->handle.family =
 		nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY);
 	chain->handle.table  =
-		xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
+		xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE));
 	chain->handle.handle =
 		nft_chain_attr_get_u64(nlc, NFT_CHAIN_ATTR_HANDLE);
 
@@ -531,6 +522,24 @@ static int list_chain_cb(struct nft_chain *nlc, void *arg)
 	}
 	list_add_tail(&chain->list, &ctx->list);
 
+	return chain;
+}
+
+static int list_chain_cb(struct nft_chain *nlc, void *arg)
+{
+	struct netlink_ctx *ctx = arg;
+	const struct handle *h = ctx->data;
+	const char *table = nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE);
+	const char *name = nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME);
+
+	if ((h->family != nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY)) ||
+	    strcmp(table, h->table) != 0)
+		return 0;
+
+	if (h->chain && strcmp(name, h->chain) != 0)
+		return 0;
+
+	netlink_delinearize_chain(ctx, nlc);
 	return 0;
 }
 
@@ -570,25 +579,12 @@ int netlink_get_chain(struct netlink_ctx *ctx, const struct handle *h,
 		      const struct location *loc)
 {
 	struct nft_chain *nlc;
-	struct chain *chain;
 	int err;
 
 	nlc = alloc_nft_chain(h);
 	err = mnl_nft_chain_get(nf_sock, nlc, 0);
 
-	chain = chain_alloc(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
-	chain->handle.family = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY);
-	chain->handle.table  = xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE));
-	chain->handle.handle = nft_chain_attr_get_u64(nlc, NFT_CHAIN_ATTR_HANDLE);
-	if (nft_chain_attr_is_set(nlc, NFT_CHAIN_ATTR_TYPE) &&
-	    nft_chain_attr_is_set(nlc, NFT_CHAIN_ATTR_HOOKNUM) &&
-	    nft_chain_attr_is_set(nlc, NFT_CHAIN_ATTR_PRIO)) {
-		chain->hooknum       = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_HOOKNUM);
-		chain->priority      = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_PRIO);
-		chain->type          = xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TYPE));
-	}
-	list_add_tail(&chain->list, &ctx->list);
-
+	netlink_delinearize_chain(ctx, nlc);
 	nft_chain_free(nlc);
 
 	if (err < 0)


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 5/6] netlink: add netlink_delinearize_table() func
  2014-02-26 16:09 [RFC PATCH v2 0/6] nft events reporting Arturo Borrero Gonzalez
                   ` (3 preceding siblings ...)
  2014-02-26 16:10 ` [RFC PATCH v2 4/6] netlink: add netlink_delinearize_chain() func Arturo Borrero Gonzalez
@ 2014-02-26 16:10 ` Arturo Borrero Gonzalez
  2014-02-26 16:10 ` [RFC PATCH v2 6/6] src: add events reporting Arturo Borrero Gonzalez
  2014-02-27 14:09 ` [RFC PATCH v2 0/6] nft " Patrick McHardy
  6 siblings, 0 replies; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 16:10 UTC (permalink / raw)
  To: netfilter-devel; +Cc: pablo

This code is suitable to be reusable.

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
---
 src/netlink.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/netlink.c b/src/netlink.c
index d2a7804..43af5d1 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -652,9 +652,9 @@ void netlink_dump_table(struct nft_table *nlt)
 #endif
 }
 
-static int list_table_cb(struct nft_table *nlt, void *arg)
+static struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
+					       struct nft_table *nlt)
 {
-	struct netlink_ctx *ctx = arg;
 	struct table *table;
 
 	netlink_dump_table(nlt);
@@ -665,6 +665,15 @@ static int list_table_cb(struct nft_table *nlt, void *arg)
 		xstrdup(nft_table_attr_get_str(nlt, NFT_TABLE_ATTR_NAME));
 	list_add_tail(&table->list, &ctx->list);
 
+	return table;
+}
+
+static int list_table_cb(struct nft_table *nlt, void *arg)
+{
+	struct netlink_ctx *ctx = arg;
+
+	netlink_delinearize_table(ctx, nlt);
+
 	return 0;
 }
 


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 6/6] src: add events reporting
  2014-02-26 16:09 [RFC PATCH v2 0/6] nft events reporting Arturo Borrero Gonzalez
                   ` (4 preceding siblings ...)
  2014-02-26 16:10 ` [RFC PATCH v2 5/6] netlink: add netlink_delinearize_table() func Arturo Borrero Gonzalez
@ 2014-02-26 16:10 ` Arturo Borrero Gonzalez
  2014-02-26 17:17   ` Arturo Borrero Gonzalez
  2014-02-26 17:19   ` Pablo Neira Ayuso
  2014-02-27 14:09 ` [RFC PATCH v2 0/6] nft " Patrick McHardy
  6 siblings, 2 replies; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 16:10 UTC (permalink / raw)
  To: netfilter-devel; +Cc: pablo

This patch adds a basic events reporting option to nft.

The syntax is:
 % nft monitor [added|deleted] [tables|chains|rules|sets] [xml|json]

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
---
 include/mnl.h     |    3 
 include/netlink.h |    8 +
 include/rule.h    |    3 
 src/evaluate.c    |    1 
 src/mnl.c         |   45 ++++-
 src/netlink.c     |  474 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/parser.y      |   75 ++++++++
 src/rule.c        |   26 +++
 src/scanner.l     |    5 +
 9 files changed, 621 insertions(+), 19 deletions(-)

diff --git a/include/mnl.h b/include/mnl.h
index f4de27d..ece7ee7 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -67,4 +67,7 @@ int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls);
 
 struct nft_ruleset *mnl_nft_ruleset_dump(struct mnl_socket *nf_sock,
 					 uint32_t family);
+int mnl_nft_event_listener(struct mnl_socket *nf_sock,
+			   int (*cb)(const struct nlmsghdr *nlh, void *data),
+			   void *cb_data);
 #endif /* _NFTABLES_MNL_H_ */
diff --git a/include/netlink.h b/include/netlink.h
index 4e3f8aa..627b25c 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -142,4 +142,12 @@ extern int netlink_io_error(struct netlink_ctx *ctx,
 extern struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx,
 						const struct handle *h,
 						const struct location *loc);
+struct netlink_mon_handler {
+	uint32_t		selector;
+	uint32_t		format;
+	struct netlink_ctx	*ctx;
+	const struct location	*loc;
+};
+
+extern int netlink_monitor(struct netlink_mon_handler *monhandler);
 #endif /* NFTABLES_NETLINK_H */
diff --git a/include/rule.h b/include/rule.h
index 9791cea..52e2aaa 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -210,6 +210,7 @@ extern void set_print_plain(const struct set *s);
  * @CMD_FLUSH:		flush container
  * @CMD_RENAME:		rename object
  * @CMD_EXPORT:		export the ruleset in a given format
+ * @CMD_MONITOR:	event listener
  */
 enum cmd_ops {
 	CMD_INVALID,
@@ -221,6 +222,7 @@ enum cmd_ops {
 	CMD_FLUSH,
 	CMD_RENAME,
 	CMD_EXPORT,
+	CMD_MONITOR,
 };
 
 /**
@@ -276,6 +278,7 @@ struct cmd {
 	};
 	const void		*arg;
 	uint32_t		format;
+	uint32_t		mon_selector;
 };
 
 extern struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
diff --git a/src/evaluate.c b/src/evaluate.c
index f10d0d9..914ea23 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1410,6 +1410,7 @@ int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
 	case CMD_FLUSH:
 	case CMD_RENAME:
 	case CMD_EXPORT:
+	case CMD_MONITOR:
 		return 0;
 	default:
 		BUG("invalid command operation %u\n", cmd->op);
diff --git a/src/mnl.c b/src/mnl.c
index e825fb0..7e34b31 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -34,24 +34,16 @@ uint32_t mnl_seqnum_alloc(void)
 }
 
 static int
-mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len,
-	 int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data)
+mnl_talk_recv(struct mnl_socket *nf_sock, int seqnum, uint32_t portid,
+	      int (*cb)(const struct nlmsghdr *nlh, void *data),
+	      void *cb_data)
 {
-	char buf[MNL_SOCKET_BUFFER_SIZE];
-	uint32_t portid = mnl_socket_get_portid(nf_sock);
 	int ret;
-
-#ifdef DEBUG
-	if (debug_level & DEBUG_MNL)
-		mnl_nlmsg_fprintf(stdout, data, len, sizeof(struct nfgenmsg));
-#endif
-
-	if (mnl_socket_sendto(nf_sock, data, len) < 0)
-		return -1;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
 
 	ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf));
 	while (ret > 0) {
-		ret = mnl_cb_run(buf, ret, seq, portid, cb, cb_data);
+		ret = mnl_cb_run(buf, ret, seqnum, portid, cb, cb_data);
 		if (ret <= 0)
 			goto out;
 
@@ -64,6 +56,23 @@ out:
 	return ret;
 }
 
+static int
+mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len,
+	 int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data)
+{
+	uint32_t portid = mnl_socket_get_portid(nf_sock);
+
+#ifdef DEBUG
+	if (debug_level & DEBUG_MNL)
+		mnl_nlmsg_fprintf(stdout, data, len, sizeof(struct nfgenmsg));
+#endif
+
+	if (mnl_socket_sendto(nf_sock, data, len) < 0)
+		return -1;
+
+	return mnl_talk_recv(nf_sock, seq, portid, cb, cb_data);
+}
+
 /*
  * Batching
  */
@@ -805,3 +814,13 @@ out:
 	nft_ruleset_free(rs);
 	return NULL;
 }
+
+/*
+ * events
+ */
+int mnl_nft_event_listener(struct mnl_socket *nf_sock,
+			   int (*cb)(const struct nlmsghdr *nlh, void *data),
+			   void *cb_data)
+{
+	return mnl_talk_recv(nf_sock, 0, 0, cb, cb_data);
+}
diff --git a/src/netlink.c b/src/netlink.c
index 43af5d1..c7532eb 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -20,6 +20,7 @@
 #include <libnftnl/chain.h>
 #include <libnftnl/expr.h>
 #include <libnftnl/set.h>
+#include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nf_tables.h>
 #include <linux/netfilter.h>
 
@@ -32,6 +33,7 @@
 #include <erec.h>
 
 static struct mnl_socket *nf_sock;
+static struct mnl_socket *nf_mon_sock;
 
 const struct input_descriptor indesc_netlink = {
 	.name	= "netlink",
@@ -42,12 +44,18 @@ const struct location netlink_location = {
 	.indesc	= &indesc_netlink,
 };
 
-static void __init netlink_open_sock(void)
+static struct mnl_socket *nfsock_open(void)
 {
-	nf_sock = mnl_socket_open(NETLINK_NETFILTER);
-	if (nf_sock == NULL)
+	struct mnl_socket *s = mnl_socket_open(NETLINK_NETFILTER);
+	if (s == NULL)
 		memory_allocation_error();
 
+	return s;
+}
+
+static void __init netlink_open_sock(void)
+{
+	nf_sock = nfsock_open();
 	fcntl(mnl_socket_get_fd(nf_sock), F_SETFL, O_NONBLOCK);
 	mnl_batch_init();
 }
@@ -55,6 +63,13 @@ static void __init netlink_open_sock(void)
 static void __exit netlink_close_sock(void)
 {
 	mnl_socket_close(nf_sock);
+	if (nf_mon_sock)
+		mnl_socket_close(nf_mon_sock);
+}
+
+static void netlink_open_mon_sock(void)
+{
+	nf_mon_sock = nfsock_open();
 }
 
 int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,
@@ -1047,3 +1062,456 @@ struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx,
 
 	return rs;
 }
+
+static void netlink_events_printf_wrapper(const char *content,
+					  uint32_t format, int type)
+{
+	const char *type_str = "unknown";
+
+	switch (type) {
+	case NFT_MSG_NEWTABLE:
+	case NFT_MSG_NEWCHAIN:
+	case NFT_MSG_NEWSET:
+	case NFT_MSG_NEWRULE:
+		type_str = "add";
+		break;
+	case NFT_MSG_DELTABLE:
+	case NFT_MSG_DELCHAIN:
+	case NFT_MSG_DELSET:
+	case NFT_MSG_DELRULE:
+		type_str = "del";
+		break;
+	}
+
+	switch (format) {
+	case NFT_OUTPUT_XML:
+		printf("<event><type>%s</type>"
+		       "<nftables>%s</nftables></event>\n",
+		       type_str, content);
+		break;
+	case NFT_OUTPUT_JSON:
+		printf("{event:{type:\"%s\",{\"nftables\":[\"%s\"]}}}\n",
+		       type_str, content);
+		break;
+	default:
+		BUG("Unknown monitor output format.\n");
+	}
+}
+
+static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
+				   struct netlink_mon_handler *monh)
+{
+	struct nft_table *nlt;
+	uint32_t family;
+	char buf[4096];
+
+	nlt = nft_table_alloc();
+	if (nlt == NULL)
+		memory_allocation_error();
+
+	if (nft_table_nlmsg_parse(nlh, nlt) < 0) {
+		netlink_io_error(monh->ctx, monh->loc,
+				"Could not parse table: %s",
+				strerror(errno));
+		goto err;
+	}
+
+	if (monh->format == NFT_OUTPUT_DEFAULT) {
+		if (type == NFT_MSG_NEWTABLE)
+			printf("add table ");
+		else
+			printf("delete table ");
+
+		family = nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY);
+
+		printf("%s %s\n", family2str(family),
+		       nft_table_attr_get_str(nlt, NFT_TABLE_ATTR_NAME));
+	} else {
+		nft_table_snprintf(buf, sizeof(buf), nlt, monh->format, 0);
+		netlink_events_printf_wrapper(buf, monh->format, type);
+	}
+
+err:
+	nft_table_free(nlt);
+	return MNL_CB_OK;
+}
+
+static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type,
+				   struct netlink_mon_handler *monh)
+{
+	struct nft_chain *nlc;
+	struct chain *c;
+	uint32_t family;
+	char buf[4096];
+
+	nlc = nft_chain_alloc();
+	if (nlc == NULL)
+		memory_allocation_error();
+
+	if (nft_chain_nlmsg_parse(nlh, nlc) < 0) {
+		netlink_io_error(monh->ctx, monh->loc,
+				 "Could not parse chain: %s",
+				 strerror(errno));
+		goto out;
+	}
+
+	if (monh->format == NFT_OUTPUT_DEFAULT) {
+		if (type == NFT_MSG_NEWCHAIN) {
+			printf("add ");
+			c = netlink_delinearize_chain(monh->ctx, nlc);
+			chain_print_plain(c);
+			chain_free(c);
+		} else {
+			family = nft_chain_attr_get_u32(nlc,
+							NFT_CHAIN_ATTR_FAMILY);
+			printf("delete chain %s %s %s\n", family2str(family),
+			       nft_chain_attr_get_str(nlc,
+						      NFT_CHAIN_ATTR_TABLE),
+			       nft_chain_attr_get_str(nlc,
+						      NFT_CHAIN_ATTR_NAME));
+		}
+	} else {
+		nft_chain_snprintf(buf, sizeof(buf), nlc, monh->format, 0);
+		netlink_events_printf_wrapper(buf, monh->format, type);
+	}
+
+out:
+	nft_chain_free(nlc);
+	return MNL_CB_OK;
+}
+
+static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type,
+				 struct netlink_mon_handler *monh)
+{
+	struct nft_set *nls;
+	struct set *set;
+	uint32_t family, flags;
+	char buf[4096];
+
+	nls = nft_set_alloc();
+	if (nls == NULL)
+		memory_allocation_error();
+
+	if (nft_set_nlmsg_parse(nlh, nls) < 0) {
+		netlink_io_error(monh->ctx, monh->loc,
+				 "Could not parse set: %s",
+				 strerror(errno));
+		goto out;
+	}
+
+	flags = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FLAGS);
+	if (flags & SET_F_ANONYMOUS)
+		goto out;
+
+	if (monh->format == NFT_OUTPUT_DEFAULT) {
+		if (type == NFT_MSG_NEWSET) {
+			printf("add ");
+			set = netlink_delinearize_set(monh->ctx, nls);
+			set_print_plain(set);
+			set_free(set);
+		} else {
+			family = nft_set_attr_get_u32(nls,
+						      NFT_SET_ATTR_FAMILY);
+			printf("delete set %s %s %s",
+			       family2str(family),
+			       nft_set_attr_get_str(nls, NFT_SET_ATTR_TABLE),
+			       nft_set_attr_get_str(nls, NFT_SET_ATTR_NAME));
+		}
+
+		printf("\n");
+
+	} else {
+		nft_set_snprintf(buf, sizeof(buf), nls, monh->format, 0);
+		netlink_events_printf_wrapper(buf, monh->format, type);
+	}
+
+out:
+	nft_set_free(nls);
+	return MNL_CB_OK;
+}
+
+static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
+				  struct netlink_mon_handler *monh)
+{
+	struct nft_rule *nlr;
+	struct rule *r;
+	char buf[4096];
+	uint32_t fam;
+	const char *family;
+	const char *table;
+	const char *chain;
+	uint64_t handle;
+
+	nlr = nft_rule_alloc();
+	if (nlr == NULL)
+		memory_allocation_error();
+
+	if (nft_rule_nlmsg_parse(nlh, nlr) < 0) {
+		netlink_io_error(monh->ctx, monh->loc,
+				 "Could not parse rule: %s",
+				 strerror(errno));
+		goto out;
+	}
+
+	if (monh->format == NFT_OUTPUT_DEFAULT) {
+		fam = nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY);
+		family = family2str(fam);
+		table = nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_TABLE);
+		chain = nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_CHAIN);
+		handle = nft_rule_attr_get_u64(nlr, NFT_RULE_ATTR_HANDLE);
+
+		if (type == NFT_MSG_NEWRULE) {
+			r = netlink_delinearize_rule(monh->ctx, nlr);
+			printf("add rule %s %s %s", family, table, chain);
+			rule_print(r);
+			rule_free(r);
+			goto out;
+		}
+
+		printf("delete rule %s %s %s handle %u\n",
+		       family, table, chain, (unsigned int)handle);
+	} else {
+		nft_rule_snprintf(buf, sizeof(buf), nlr, monh->format, 0);
+		netlink_events_printf_wrapper(buf, monh->format, type);
+	}
+
+out:
+	nft_rule_free(nlr);
+	return MNL_CB_OK;
+}
+
+static void netlink_events_cache_addtable(struct netlink_mon_handler *monh,
+					  const struct nlmsghdr *nlh)
+{
+	struct nft_table *nlt;
+	struct table *t;
+
+	nlt = nft_table_alloc();
+	if (nlt == NULL)
+		memory_allocation_error();
+
+	if (nft_table_nlmsg_parse(nlh, nlt) < 0) {
+		netlink_io_error(monh->ctx, monh->loc,
+				"Could not parse table: %s",
+				strerror(errno));
+		goto out;
+	}
+
+	t = netlink_delinearize_table(monh->ctx, nlt);
+	table_add_hash(t);
+
+out:
+	nft_table_free(nlt);
+}
+
+static void netlink_events_cache_deltable(struct netlink_mon_handler *monh,
+					  const struct nlmsghdr *nlh)
+{
+	struct nft_table *nlt;
+	struct table *t;
+	struct handle h;
+
+	nlt = nft_table_alloc();
+	if (nlt == NULL)
+		memory_allocation_error();
+
+	if (nft_table_nlmsg_parse(nlh, nlt) < 0) {
+		netlink_io_error(monh->ctx, monh->loc,
+				"Could not parse table: %s",
+				strerror(errno));
+		goto out;
+	}
+
+	h.family = nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY);
+	h.table = nft_table_attr_get_str(nlt, NFT_TABLE_ATTR_NAME);
+
+	t = table_lookup(&h);
+	if (t == NULL)
+		goto out;
+
+	list_del(&t->list);
+	table_free(t);
+
+out:
+	nft_table_free(nlt);
+}
+
+static void netlink_events_cache_addset(struct netlink_mon_handler *monh,
+					const struct nlmsghdr *nlh)
+{
+	struct nft_set *nls;
+	struct netlink_ctx set_tmpctx;
+	LIST_HEAD(msgs);
+	struct table *t;
+	struct set *s;
+	uint32_t flags;
+
+	nls = nft_set_alloc();
+	if (nls == NULL)
+		memory_allocation_error();
+
+	if (nft_set_nlmsg_parse(nlh, nls) < 0) {
+		netlink_io_error(monh->ctx, monh->loc,
+				"Could not parse set: %s",
+				strerror(errno));
+		goto out;
+	}
+
+	flags = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FLAGS);
+	if (!(flags & SET_F_ANONYMOUS))
+		goto out;
+
+	memset(&set_tmpctx, 0, sizeof(set_tmpctx));
+	init_list_head(&set_tmpctx.list);
+	init_list_head(&msgs);
+	set_tmpctx.msgs = &msgs;
+	s = netlink_delinearize_set(&set_tmpctx, nls);
+
+	/* XXXX */
+	sleep(1);
+
+	/* only way, by now, to know set_elems (no set_elem events) */
+	if (netlink_get_setelems(&set_tmpctx, &s->handle, monh->loc, s) < 0)
+		goto out;
+
+	set_print(s);
+
+	t = table_lookup(&s->handle);
+	if (t == NULL)
+		goto out;
+
+	set_add_hash(s, t);
+out:
+	nft_set_free(nls);
+}
+
+static void netlink_events_cache_delsets(struct netlink_mon_handler *monh,
+					 const struct nlmsghdr *nlh)
+{
+	struct nft_rule *nlr;
+	struct handle th;
+	struct nft_rule_expr_iter *nlrei;
+	struct nft_rule_expr *nlre;
+	const char *expr_name, *set_name;
+	struct table *t;
+	struct set *s;
+
+	nlr = nft_rule_alloc();
+	if (nlr == NULL)
+		memory_allocation_error();
+
+	if (nft_rule_nlmsg_parse(nlh, nlr) < 0) {
+		netlink_io_error(monh->ctx, monh->loc,
+				 "Could not parse rule: %s",
+				 strerror(errno));
+		goto out;
+	}
+
+	th.table = nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_TABLE);
+	th.family = nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY);
+	t = table_lookup(&th);
+	if (t == NULL)
+		goto out;
+
+
+	nlrei = nft_rule_expr_iter_create(nlr);
+	if (nlrei == NULL)
+		memory_allocation_error();
+
+	nlre = nft_rule_expr_iter_next(nlrei);
+	while (nlre != NULL) {
+		expr_name = nft_rule_expr_get_str(nlre,
+						  NFT_RULE_EXPR_ATTR_NAME);
+		if (strcmp(expr_name, "lookup") != 0)
+			goto next;
+
+		set_name = nft_rule_expr_get_str(nlre, NFT_EXPR_LOOKUP_SET);
+		s = set_lookup(t, set_name);
+		if (s == NULL)
+			goto next;
+
+		list_del(&s->list);
+		set_free(s);
+next:
+		nlre = nft_rule_expr_iter_next(nlrei);
+	}
+	nft_rule_expr_iter_destroy(nlrei);
+
+out:
+	nft_rule_free(nlr);
+}
+
+static void netlink_events_cache_update(struct netlink_mon_handler *monh,
+					const struct nlmsghdr *nlh, int type)
+{
+	/* cache only needed when monitoring new rules in def. output format */
+	if (!(monh->selector & (1 << NFT_MSG_NEWRULE)) &&
+	    (monh->format != NFT_OUTPUT_DEFAULT))
+		return;
+
+	switch (type) {
+	case NFT_MSG_NEWTABLE:
+		netlink_events_cache_addtable(monh, nlh);
+		break;
+	case NFT_MSG_DELTABLE:
+		netlink_events_cache_deltable(monh, nlh);
+		break;
+	case NFT_MSG_NEWSET:
+		netlink_events_cache_addset(monh, nlh);
+		break;
+	case NFT_MSG_DELRULE:
+		/* there are no notification for anon-set deletion */
+		netlink_events_cache_delsets(monh, nlh);
+		break;
+	}
+}
+
+static int netlink_events_cb(const struct nlmsghdr *nlh, void *data)
+{
+	int ret = MNL_CB_OK;
+	int type = nlh->nlmsg_type & 0xFF;
+	struct netlink_mon_handler *monh = (struct netlink_mon_handler *)data;
+
+	netlink_events_cache_update(monh, nlh, type);
+
+	if (!(monh->selector & (1 << type)))
+		return ret;
+
+	switch (type) {
+	case NFT_MSG_NEWTABLE:
+	case NFT_MSG_DELTABLE:
+		ret = netlink_events_table_cb(nlh, type, monh);
+		break;
+	case NFT_MSG_NEWCHAIN:
+	case NFT_MSG_DELCHAIN:
+		ret = netlink_events_chain_cb(nlh, type, monh);
+		break;
+	case NFT_MSG_NEWSET:
+	case NFT_MSG_DELSET:
+		ret = netlink_events_set_cb(nlh, type, monh);
+		break;
+	case NFT_MSG_NEWRULE:
+	case NFT_MSG_DELRULE:
+		ret = netlink_events_rule_cb(nlh, type, monh);
+		break;
+	default:
+		BUG("Unknow event received from kernel.\n");
+		break;
+	}
+
+	return ret;
+}
+
+int netlink_monitor(struct netlink_mon_handler *monhandler)
+{
+	netlink_open_mon_sock();
+
+	if (mnl_socket_bind(nf_mon_sock, (1 << (NFNLGRP_NFTABLES-1)),
+			    MNL_SOCKET_AUTOPID) < 0)
+		return netlink_io_error(monhandler->ctx, monhandler->loc,
+					"Could not bind to netlink socket %s",
+					strerror(errno));
+
+	return mnl_nft_event_listener(nf_mon_sock, netlink_events_cb,
+				      monhandler);
+}
diff --git a/src/parser.y b/src/parser.y
index b3acc74..a154696 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -163,12 +163,16 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token TABLE			"table"
 %token TABLES			"tables"
 %token CHAIN			"chain"
+%token CHAINS			"chains"
 %token RULE			"rule"
+%token RULES			"rules"
 %token SETS			"sets"
 %token SET			"set"
 %token ELEMENT			"element"
 %token MAP			"map"
 %token HANDLE			"handle"
+%token ADDED			"added"
+%token DELETED			"deleted"
 
 %token INET			"inet"
 
@@ -181,6 +185,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token RENAME			"rename"
 %token DESCRIBE			"describe"
 %token EXPORT			"export"
+%token MONITOR			"monitor"
 
 %token ACCEPT			"accept"
 %token DROP			"drop"
@@ -361,8 +366,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %type <cmd>			line
 %destructor { cmd_free($$); }	line
 
-%type <cmd>			base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd
-%destructor { cmd_free($$); }	base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd
+%type <cmd>			base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd
+%destructor { cmd_free($$); }	base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd
 
 %type <handle>			table_spec tables_spec chain_spec chain_identifier ruleid_spec
 %destructor { handle_free(&$$); } table_spec tables_spec chain_spec chain_identifier ruleid_spec
@@ -485,7 +490,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %destructor { expr_free($$); }	ct_expr
 %type <val>			ct_key
 
-%type <val>			export_format
+%type <val>			export_format	output_format	mon_selector
 
 %%
 
@@ -585,6 +590,7 @@ base_cmd		:	/* empty */	add_cmd		{ $$ = $1; }
 			|	FLUSH		flush_cmd	{ $$ = $2; }
 			|	RENAME		rename_cmd	{ $$ = $2; }
 			|	EXPORT		export_cmd	{ $$ = $2; }
+			|	MONITOR		monitor_cmd	{ $$ = $2; }
 			|	DESCRIBE	primary_expr
 			{
 				expr_describe($2);
@@ -752,6 +758,69 @@ export_cmd		:	export_format
 			}
 			;
 
+monitor_cmd		:	mon_selector	output_format
+			{
+				struct handle h = { .family = NFPROTO_UNSPEC };
+				$$ = cmd_alloc(CMD_MONITOR, CMD_OBJ_RULESET, &h, &@$, NULL);
+				$$->mon_selector = $1;
+				$$->format = $2;
+			}
+			;
+
+mon_selector		:	/* empty */
+			{
+				$$ |= (1 << NFT_MSG_NEWRULE);	$$ |= (1 << NFT_MSG_DELRULE);
+				$$ |= (1 << NFT_MSG_NEWSET);	$$ |= (1 << NFT_MSG_DELSET);
+				$$ |= (1 << NFT_MSG_NEWCHAIN);	$$ |= (1 << NFT_MSG_DELCHAIN);
+				$$ |= (1 << NFT_MSG_NEWTABLE);	$$ |= (1 << NFT_MSG_DELTABLE);
+			}
+			|	ADDED
+			{
+				$$ |= (1 << NFT_MSG_NEWRULE);
+				$$ |= (1 << NFT_MSG_NEWSET);
+				$$ |= (1 << NFT_MSG_NEWCHAIN);
+				$$ |= (1 << NFT_MSG_NEWTABLE);
+			}
+			|	DELETED
+			{
+				$$ |= (1 << NFT_MSG_DELRULE);
+				$$ |= (1 << NFT_MSG_DELSET);
+				$$ |= (1 << NFT_MSG_DELCHAIN);
+				$$ |= (1 << NFT_MSG_DELTABLE);
+			}
+			|	TABLES
+			{
+				$$ |= (1 << NFT_MSG_NEWTABLE);	$$ |= (1 << NFT_MSG_DELTABLE);
+			}
+			|	ADDED 	TABLES 	{	$$ |= (1 << NFT_MSG_NEWTABLE); }
+			|	DELETED	TABLES	{	$$ |= (1 << NFT_MSG_DELTABLE); }
+			|	CHAIN
+			{
+				$$ |= (1 << NFT_MSG_NEWCHAIN);	$$ |= (1 << NFT_MSG_DELCHAIN);
+			}
+			|	ADDED	CHAINS	{	$$ |= (1 << NFT_MSG_NEWCHAIN); }
+			|	DELETED	CHAINS	{	$$ |= (1 << NFT_MSG_DELCHAIN); }
+			|	SET
+			{
+				$$ |= (1 << NFT_MSG_NEWSET);	$$ |= (1 << NFT_MSG_DELSET);
+			}
+			|	ADDED	SETS	{	$$ |= (1 << NFT_MSG_NEWSET); }
+			|	DELETED SETS	{	$$ |= (1 << NFT_MSG_DELSET); }
+			|	RULE
+			{
+				$$ |= (1 << NFT_MSG_NEWRULE);	$$ |= (1 << NFT_MSG_DELRULE);
+			}
+			|	ADDED 	RULES	{	$$ |= (1 << NFT_MSG_NEWRULE); }
+			|	DELETED RULES	{	$$ |= (1 << NFT_MSG_DELRULE); }
+			;
+
+output_format		:	/* empty */
+			{
+				$$ = NFT_OUTPUT_DEFAULT;
+			}
+			|	export_format
+			;
+
 table_block_alloc	:	/* empty */
 			{
 				$$ = table_alloc();
diff --git a/src/rule.c b/src/rule.c
index d8a065a..72fc306 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -799,6 +799,30 @@ static int do_command_rename(struct netlink_ctx *ctx, struct cmd *cmd)
 	return 0;
 }
 
+static int do_command_monitor(struct netlink_ctx *ctx, struct cmd *cmd)
+{
+	struct table *t, *nt;
+	struct netlink_mon_handler monhandler;
+
+	/* cache only needed if monitoring new rules in def. output format */
+	if ((cmd->mon_selector & (1 << NFT_MSG_NEWRULE)) &&
+	    (cmd->format == NFT_OUTPUT_DEFAULT)) {
+		if (netlink_list_tables(ctx, &cmd->handle, &cmd->location) < 0)
+			return -1;
+
+		list_for_each_entry_safe(t, nt, &ctx->list, list) {
+			table_add_hash(t);
+		}
+	}
+
+	monhandler.selector = cmd->mon_selector;
+	monhandler.format = cmd->format;
+	monhandler.ctx = ctx;
+	monhandler.loc = &cmd->location;
+
+	return netlink_monitor(&monhandler);
+}
+
 int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
 {
 	switch (cmd->op) {
@@ -818,6 +842,8 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
 		return do_command_rename(ctx, cmd);
 	case CMD_EXPORT:
 		return do_command_export(ctx, cmd);
+	case CMD_MONITOR:
+		return do_command_monitor(ctx, cmd);
 	default:
 		BUG("invalid command object type %u\n", cmd->obj);
 	}
diff --git a/src/scanner.l b/src/scanner.l
index 45c6476..59aee8a 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -232,12 +232,16 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "table"			{ return TABLE; }
 "tables"		{ return TABLES; }
 "chain"			{ return CHAIN; }
+"chains"		{ return CHAINS; }
 "rule"			{ return RULE; }
+"rules"			{ return RULES; }
 "sets"			{ return SETS; }
 "set"			{ return SET; }
 "element"		{ return ELEMENT; }
 "map"			{ return MAP; }
 "handle"		{ return HANDLE; }
+"added"			{ return ADDED; }
+"deleted"		{ return DELETED; }
 
 "accept"		{ return ACCEPT; }
 "drop"			{ return DROP; }
@@ -256,6 +260,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "flush"			{ return FLUSH; }
 "rename"		{ return RENAME; }
 "export"		{ return EXPORT; }
+"monitor"		{ return MONITOR; }
 
 "position"		{ return POSITION; }
 


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v2 1/6] rule: allow to print sets in plain format
  2014-02-26 16:09 ` [RFC PATCH v2 1/6] rule: allow to print sets in plain format Arturo Borrero Gonzalez
@ 2014-02-26 16:44   ` Pablo Neira Ayuso
  0 siblings, 0 replies; 14+ messages in thread
From: Pablo Neira Ayuso @ 2014-02-26 16:44 UTC (permalink / raw)
  To: Arturo Borrero Gonzalez; +Cc: netfilter-devel

Hi Arturo,

On Wed, Feb 26, 2014 at 05:09:49PM +0100, Arturo Borrero Gonzalez wrote:
> Allow to print sets with or without format.
> 
> This is useful in situations where we want to print more or less the same
> the user typed (IOW, in one single line, and with family/table info).
> 
> While at it, make family2str() function public, so it can be used in
> other places.
> 
> Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
> ---
>  include/rule.h |    3 +++
>  src/rule.c     |   62 +++++++++++++++++++++++++++++++++++++++++++++++---------
>  2 files changed, 55 insertions(+), 10 deletions(-)
> 
> diff --git a/include/rule.h b/include/rule.h
> index e06444e..b263593 100644
> --- a/include/rule.h
> +++ b/include/rule.h
> @@ -125,6 +125,8 @@ extern void chain_add_hash(struct chain *chain, struct table *table);
>  extern struct chain *chain_lookup(const struct table *table,
>  				  const struct handle *h);
>  
> +extern const char *family2str(unsigned int family);
> +
>  /**
>   * struct rule - nftables rule
>   *
> @@ -193,6 +195,7 @@ extern void set_free(struct set *set);
>  extern void set_add_hash(struct set *set, struct table *table);
>  extern struct set *set_lookup(const struct table *table, const char *name);
>  extern void set_print(const struct set *set);
> +extern void set_print_plain(const struct set *s);
>  
>  /**
>   * enum cmd_ops - command operations
> diff --git a/src/rule.c b/src/rule.c
> index ab96e62..c774664 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -90,21 +90,37 @@ struct set *set_lookup(const struct table *table, const char *name)
>  	return NULL;
>  }
>  
> -void set_print(const struct set *set)
> +struct print_fmt_options {
> +	const char	*tab;
> +	const char	*nl;
> +	const char	*table;
> +	const char	*family;
> +	const char	*stmt_separator;
> +};
> +
> +static void do_set_print(const struct set *set, struct print_fmt_options *opts)
>  {
>  	const char *delim = "";
>  	const char *type;
>  
>  	type = set->flags & SET_F_MAP ? "map" : "set";
> -	printf("\t%s %s {\n", type, set->handle.set);
> +	printf("%s%s", opts->tab, type);
> +
> +	if (opts->family != NULL)
> +		printf(" %s", opts->family);
> +
> +	if (opts->table != NULL)
> +		printf(" %s", opts->table);
> +
> +	printf(" %s { %s", set->handle.set, opts->nl);
>  
> -	printf("\t\ttype %s", set->keytype->name);
> +	printf("%s%stype %s", opts->tab, opts->tab, set->keytype->name);
>  	if (set->flags & SET_F_MAP)
>  		printf(" : %s", set->datatype->name);
> -	printf("\n");
> +	printf("%s", opts->stmt_separator);
>  
>  	if (set->flags & (SET_F_CONSTANT | SET_F_INTERVAL)) {
> -		printf("\t\tflags ");
> +		printf("%s%sflags ", opts->tab, opts->tab);
>  		if (set->flags & SET_F_CONSTANT) {
>  			printf("%sconstant", delim);
>  			delim = ",";
> @@ -113,15 +129,41 @@ void set_print(const struct set *set)
>  			printf("%sinterval", delim);
>  			delim = ",";
>  		}
> -		printf("\n");
> +		printf("%s", opts->nl);
>  	}
>  
>  	if (set->init != NULL && set->init->size > 0) {
> -		printf("\t\telements = ");
> +		printf("%s%selements = ", opts->tab, opts->tab);
>  		expr_print(set->init);
> -		printf("\n");
> +		printf("%s", opts->nl);
>  	}
> -	printf("\t}\n");
> +	printf("%s}%s", opts->tab, opts->nl);
> +}
> +
> +void set_print(const struct set *s)
> +{
> +	struct print_fmt_options opts;
> +
> +	opts.tab = "\t";
> +	opts.nl = "\n";
> +	opts.table = NULL;
> +	opts.family = NULL;
> +	opts.stmt_separator = "\n";

I think you can use C99 structure initialization:

struct print_fmt_options opts = {
        .tabs           = "\t",
        .nl             = "\n",
        .stmt_separator  = "\n",
};

Note that unspecified fields are already set to zero.

> +
> +	do_set_print(s, &opts);
> +}
> +
> +void set_print_plain(const struct set *s)
> +{
> +	struct print_fmt_options opts;
> +
> +	opts.tab = "";
> +	opts.nl = "";
> +	opts.table = s->handle.table;
> +	opts.family = family2str(s->handle.family);
> +	opts.stmt_separator = ";";

Same thing here.

> +	do_set_print(s, &opts);
>  }
>  
>  struct rule *rule_alloc(const struct location *loc, const struct handle *h)
> @@ -279,7 +321,7 @@ struct chain *chain_lookup(const struct table *table, const struct handle *h)
>  	return NULL;
>  }
>  
> -static const char *family2str(unsigned int family)
> +const char *family2str(unsigned int family)
>  {
>  	switch (family) {
>  		case NFPROTO_IPV4:
> 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v2 4/6] netlink: add netlink_delinearize_chain() func
  2014-02-26 16:10 ` [RFC PATCH v2 4/6] netlink: add netlink_delinearize_chain() func Arturo Borrero Gonzalez
@ 2014-02-26 16:49   ` Pablo Neira Ayuso
  0 siblings, 0 replies; 14+ messages in thread
From: Pablo Neira Ayuso @ 2014-02-26 16:49 UTC (permalink / raw)
  To: Arturo Borrero Gonzalez; +Cc: netfilter-devel

On Wed, Feb 26, 2014 at 05:10:05PM +0100, Arturo Borrero Gonzalez wrote:
> Let's make this code reusable.
> 
> Also, this patch fixes a hidden bug: the table in the chain's handle was being
> set to the chain name.
> 
> Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
> ---
>  src/netlink.c |   48 ++++++++++++++++++++++--------------------------
>  1 file changed, 22 insertions(+), 26 deletions(-)
> 
> diff --git a/src/netlink.c b/src/netlink.c
> index 74372bf..d2a7804 100644
> --- a/src/netlink.c
> +++ b/src/netlink.c
> @@ -496,25 +496,16 @@ int netlink_delete_chain(struct netlink_ctx *ctx, const struct handle *h,
>  	return err;
>  }
>  
> -static int list_chain_cb(struct nft_chain *nlc, void *arg)
> +static struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
> +					       struct nft_chain *nlc)
>  {
> -	struct netlink_ctx *ctx = arg;
> -	const struct handle *h = ctx->data;
>  	struct chain *chain;
>  
> -	if ((h->family != nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY)) ||
> -	    strcmp(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE), h->table) != 0)
> -		return 0;
> -
> -	if (h->chain &&
> -	    strcmp(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME), h->chain) != 0)
> -		return 0;
> -
>  	chain = chain_alloc(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
>  	chain->handle.family =
>  		nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY);
>  	chain->handle.table  =
> -		xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
> +		xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE));

Would be good to have a separated fix for this. This can come as the
first patch of the stack.

>  	chain->handle.handle =
>  		nft_chain_attr_get_u64(nlc, NFT_CHAIN_ATTR_HANDLE);
>  
> @@ -531,6 +522,24 @@ static int list_chain_cb(struct nft_chain *nlc, void *arg)
>  	}
>  	list_add_tail(&chain->list, &ctx->list);
>  
> +	return chain;
> +}
> +
> +static int list_chain_cb(struct nft_chain *nlc, void *arg)
> +{
> +	struct netlink_ctx *ctx = arg;
> +	const struct handle *h = ctx->data;
> +	const char *table = nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE);
> +	const char *name = nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME);
> +
> +	if ((h->family != nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY)) ||
> +	    strcmp(table, h->table) != 0)
> +		return 0;
> +
> +	if (h->chain && strcmp(name, h->chain) != 0)
> +		return 0;
> +
> +	netlink_delinearize_chain(ctx, nlc);
>  	return 0;
>  }
>  
> @@ -570,25 +579,12 @@ int netlink_get_chain(struct netlink_ctx *ctx, const struct handle *h,
>  		      const struct location *loc)
>  {
>  	struct nft_chain *nlc;
> -	struct chain *chain;
>  	int err;
>  
>  	nlc = alloc_nft_chain(h);
>  	err = mnl_nft_chain_get(nf_sock, nlc, 0);
>  
> -	chain = chain_alloc(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
> -	chain->handle.family = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY);
> -	chain->handle.table  = xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE));
> -	chain->handle.handle = nft_chain_attr_get_u64(nlc, NFT_CHAIN_ATTR_HANDLE);
> -	if (nft_chain_attr_is_set(nlc, NFT_CHAIN_ATTR_TYPE) &&
> -	    nft_chain_attr_is_set(nlc, NFT_CHAIN_ATTR_HOOKNUM) &&
> -	    nft_chain_attr_is_set(nlc, NFT_CHAIN_ATTR_PRIO)) {
> -		chain->hooknum       = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_HOOKNUM);
> -		chain->priority      = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_PRIO);
> -		chain->type          = xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TYPE));
> -	}
> -	list_add_tail(&chain->list, &ctx->list);
> -
> +	netlink_delinearize_chain(ctx, nlc);
>  	nft_chain_free(nlc);
>  
>  	if (err < 0)
> 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v2 6/6] src: add events reporting
  2014-02-26 16:10 ` [RFC PATCH v2 6/6] src: add events reporting Arturo Borrero Gonzalez
@ 2014-02-26 17:17   ` Arturo Borrero Gonzalez
  2014-02-26 17:27     ` Pablo Neira Ayuso
  2014-02-26 17:19   ` Pablo Neira Ayuso
  1 sibling, 1 reply; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 17:17 UTC (permalink / raw)
  To: Netfilter Development Mailing list; +Cc: Pablo Neira Ayuso

On 26 February 2014 17:10, Arturo Borrero Gonzalez
<arturo.borrero.glez@gmail.com> wrote:
>
> +
> +       /* XXXX */
> +       sleep(1);
> +
> +       /* only way, by now, to know set_elems (no set_elem events) */
> +       if (netlink_get_setelems(&set_tmpctx, &s->handle, monh->loc, s) < 0)
> +               goto out;
> +

About this:

it seems that I can't query the kernel just after the set is created,
because I get an empty set.

I need help at this point.

-- 
Arturo Borrero González
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v2 6/6] src: add events reporting
  2014-02-26 16:10 ` [RFC PATCH v2 6/6] src: add events reporting Arturo Borrero Gonzalez
  2014-02-26 17:17   ` Arturo Borrero Gonzalez
@ 2014-02-26 17:19   ` Pablo Neira Ayuso
  1 sibling, 0 replies; 14+ messages in thread
From: Pablo Neira Ayuso @ 2014-02-26 17:19 UTC (permalink / raw)
  To: Arturo Borrero Gonzalez; +Cc: netfilter-devel

On Wed, Feb 26, 2014 at 05:10:15PM +0100, Arturo Borrero Gonzalez wrote:
> This patch adds a basic events reporting option to nft.
> 
> The syntax is:
>  % nft monitor [added|deleted] [tables|chains|rules|sets] [xml|json]
                  ^-----------^
In conntrack, we use "new" and "destroy", perhaps Patrick has a better
idea on how a nice syntax would be or he likes it already.

More comments below.

> Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
> ---
>  include/mnl.h     |    3 
>  include/netlink.h |    8 +
>  include/rule.h    |    3 
>  src/evaluate.c    |    1 
>  src/mnl.c         |   45 ++++-
>  src/netlink.c     |  474 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  src/parser.y      |   75 ++++++++
>  src/rule.c        |   26 +++
>  src/scanner.l     |    5 +
>  9 files changed, 621 insertions(+), 19 deletions(-)
> 
> diff --git a/include/mnl.h b/include/mnl.h
> index f4de27d..ece7ee7 100644
> --- a/include/mnl.h
> +++ b/include/mnl.h
> @@ -67,4 +67,7 @@ int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls);
>  
>  struct nft_ruleset *mnl_nft_ruleset_dump(struct mnl_socket *nf_sock,
>  					 uint32_t family);
> +int mnl_nft_event_listener(struct mnl_socket *nf_sock,
> +			   int (*cb)(const struct nlmsghdr *nlh, void *data),
> +			   void *cb_data);
>  #endif /* _NFTABLES_MNL_H_ */
> diff --git a/include/netlink.h b/include/netlink.h
> index 4e3f8aa..627b25c 100644
> --- a/include/netlink.h
> +++ b/include/netlink.h
> @@ -142,4 +142,12 @@ extern int netlink_io_error(struct netlink_ctx *ctx,
>  extern struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx,
>  						const struct handle *h,
>  						const struct location *loc);
> +struct netlink_mon_handler {
> +	uint32_t		selector;
> +	uint32_t		format;
> +	struct netlink_ctx	*ctx;
> +	const struct location	*loc;
> +};
> +
> +extern int netlink_monitor(struct netlink_mon_handler *monhandler);
>  #endif /* NFTABLES_NETLINK_H */
> diff --git a/include/rule.h b/include/rule.h
> index 9791cea..52e2aaa 100644
> --- a/include/rule.h
> +++ b/include/rule.h
> @@ -210,6 +210,7 @@ extern void set_print_plain(const struct set *s);
>   * @CMD_FLUSH:		flush container
>   * @CMD_RENAME:		rename object
>   * @CMD_EXPORT:		export the ruleset in a given format
> + * @CMD_MONITOR:	event listener
>   */
>  enum cmd_ops {
>  	CMD_INVALID,
> @@ -221,6 +222,7 @@ enum cmd_ops {
>  	CMD_FLUSH,
>  	CMD_RENAME,
>  	CMD_EXPORT,
> +	CMD_MONITOR,
>  };
>  
>  /**
> @@ -276,6 +278,7 @@ struct cmd {
>  	};
>  	const void		*arg;
>  	uint32_t		format;
> +	uint32_t		mon_selector;

Perhap call this monitor_flags ?

>  };
>  
>  extern struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
> diff --git a/src/evaluate.c b/src/evaluate.c
> index f10d0d9..914ea23 100644
> --- a/src/evaluate.c
> +++ b/src/evaluate.c
> @@ -1410,6 +1410,7 @@ int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
>  	case CMD_FLUSH:
>  	case CMD_RENAME:
>  	case CMD_EXPORT:
> +	case CMD_MONITOR:
>  		return 0;
>  	default:
>  		BUG("invalid command operation %u\n", cmd->op);
> diff --git a/src/mnl.c b/src/mnl.c
> index e825fb0..7e34b31 100644
> --- a/src/mnl.c
> +++ b/src/mnl.c
> @@ -34,24 +34,16 @@ uint32_t mnl_seqnum_alloc(void)
>  }
>  
>  static int
> -mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len,
> -	 int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data)
> +mnl_talk_recv(struct mnl_socket *nf_sock, int seqnum, uint32_t portid,

You can already obtain the portid via:

        mnl_socket_get_portid(nf_sock)

so you can save that parameter.

seqnum should be uint32_t

> +	      int (*cb)(const struct nlmsghdr *nlh, void *data),
> +	      void *cb_data)
>  {
> -	char buf[MNL_SOCKET_BUFFER_SIZE];
> -	uint32_t portid = mnl_socket_get_portid(nf_sock);
>  	int ret;
> -
> -#ifdef DEBUG
> -	if (debug_level & DEBUG_MNL)
> -		mnl_nlmsg_fprintf(stdout, data, len, sizeof(struct nfgenmsg));
> -#endif
> -
> -	if (mnl_socket_sendto(nf_sock, data, len) < 0)
> -		return -1;
> +	char buf[MNL_SOCKET_BUFFER_SIZE];
>  
>  	ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf));
>  	while (ret > 0) {
> -		ret = mnl_cb_run(buf, ret, seq, portid, cb, cb_data);
> +		ret = mnl_cb_run(buf, ret, seqnum, portid, cb, cb_data);
>  		if (ret <= 0)
>  			goto out;
>  
> @@ -64,6 +56,23 @@ out:
>  	return ret;
>  }
>  
> +static int
> +mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len,
> +	 int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data)
> +{
> +	uint32_t portid = mnl_socket_get_portid(nf_sock);
> +
> +#ifdef DEBUG
> +	if (debug_level & DEBUG_MNL)
> +		mnl_nlmsg_fprintf(stdout, data, len, sizeof(struct nfgenmsg));
> +#endif
> +
> +	if (mnl_socket_sendto(nf_sock, data, len) < 0)
> +		return -1;
> +
> +	return mnl_talk_recv(nf_sock, seq, portid, cb, cb_data);
> +}
> +
>  /*
>   * Batching
>   */
> @@ -805,3 +814,13 @@ out:
>  	nft_ruleset_free(rs);
>  	return NULL;
>  }
> +
> +/*
> + * events
> + */
> +int mnl_nft_event_listener(struct mnl_socket *nf_sock,
> +			   int (*cb)(const struct nlmsghdr *nlh, void *data),
> +			   void *cb_data)
> +{
> +	return mnl_talk_recv(nf_sock, 0, 0, cb, cb_data);
> +}
> diff --git a/src/netlink.c b/src/netlink.c
> index 43af5d1..c7532eb 100644
> --- a/src/netlink.c
> +++ b/src/netlink.c
> @@ -20,6 +20,7 @@
>  #include <libnftnl/chain.h>
>  #include <libnftnl/expr.h>
>  #include <libnftnl/set.h>
> +#include <linux/netfilter/nfnetlink.h>
>  #include <linux/netfilter/nf_tables.h>
>  #include <linux/netfilter.h>
>  
> @@ -32,6 +33,7 @@
>  #include <erec.h>
>  
>  static struct mnl_socket *nf_sock;
> +static struct mnl_socket *nf_mon_sock;
>  
>  const struct input_descriptor indesc_netlink = {
>  	.name	= "netlink",
> @@ -42,12 +44,18 @@ const struct location netlink_location = {
>  	.indesc	= &indesc_netlink,
>  };
>  
> -static void __init netlink_open_sock(void)
> +static struct mnl_socket *nfsock_open(void)
>  {
> -	nf_sock = mnl_socket_open(NETLINK_NETFILTER);
> -	if (nf_sock == NULL)
> +	struct mnl_socket *s = mnl_socket_open(NETLINK_NETFILTER);
> +	if (s == NULL)
>  		memory_allocation_error();

I think you can add:

void __noreturn netlink_open_error(void)
{
        fprintf(stderr, "Failed to open netlink: %s\n", strerror(errno));
        exit(NFT_EXIT_NONL);
}

You'll need to add NFT_EXIT_NONL.

That should help people to diagnose problems (missing modules / broken
built / no netlink support for some reason).

> +	return s;
> +}
> +
> +static void __init netlink_open_sock(void)
> +{
> +	nf_sock = nfsock_open();
>  	fcntl(mnl_socket_get_fd(nf_sock), F_SETFL, O_NONBLOCK);
>  	mnl_batch_init();
>  }
> @@ -55,6 +63,13 @@ static void __init netlink_open_sock(void)
>  static void __exit netlink_close_sock(void)
>  {
>  	mnl_socket_close(nf_sock);
> +	if (nf_mon_sock)
> +		mnl_socket_close(nf_mon_sock);
> +}
> +
> +static void netlink_open_mon_sock(void)
> +{
> +	nf_mon_sock = nfsock_open();
>  }
>  
>  int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,
> @@ -1047,3 +1062,456 @@ struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx,
>  
>  	return rs;
>  }
> +
> +static void netlink_events_printf_wrapper(const char *content,
> +					  uint32_t format, int type)
> +{
> +	const char *type_str = "unknown";
> +
> +	switch (type) {
> +	case NFT_MSG_NEWTABLE:
> +	case NFT_MSG_NEWCHAIN:
> +	case NFT_MSG_NEWSET:
> +	case NFT_MSG_NEWRULE:
> +		type_str = "add";
> +		break;
> +	case NFT_MSG_DELTABLE:
> +	case NFT_MSG_DELCHAIN:
> +	case NFT_MSG_DELSET:
> +	case NFT_MSG_DELRULE:
> +		type_str = "del";
> +		break;
> +	}
> +
> +	switch (format) {
> +	case NFT_OUTPUT_XML:
> +		printf("<event><type>%s</type>"
> +		       "<nftables>%s</nftables></event>\n",
> +		       type_str, content);
> +		break;
> +	case NFT_OUTPUT_JSON:
> +		printf("{event:{type:\"%s\",{\"nftables\":[\"%s\"]}}}\n",
> +		       type_str, content);
> +		break;
> +	default:
> +		BUG("Unknown monitor output format.\n");
> +	}
> +}
> +
> +static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
> +				   struct netlink_mon_handler *monh)
> +{
> +	struct nft_table *nlt;
> +	uint32_t family;
> +	char buf[4096];
> +
> +	nlt = nft_table_alloc();
> +	if (nlt == NULL)
> +		memory_allocation_error();
> +
> +	if (nft_table_nlmsg_parse(nlh, nlt) < 0) {
> +		netlink_io_error(monh->ctx, monh->loc,
> +				"Could not parse table: %s",
> +				strerror(errno));

I really think the only way to hit problems here is that someone
accidentally broke the ABI in the kernel, ie. someone changes
NFTA_TABLE_FLAGS from u32 to u64, which is illegal. So I would be more
agressive here, stop the process and spot an error message "Someone
broke the netlink ABI: Fix your kernel urgently".

BTW, I just noticed a bug in libnfntnl when looking into this:

int nft_table_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_table *t)
{
        struct nlattr *tb[NFTA_TABLE_MAX+1] = {};
        struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);

        mnl_attr_parse(nlh, sizeof(*nfg), nft_table_parse_attr_cb, tb);
        ...

We have to check if mnl_attr_parse() returns an error, which means
that it failed to validate and retrieve the attributes.

Same thing in all _nlmsg_parse() calls.

> +		goto err;
> +	}
> +
> +	if (monh->format == NFT_OUTPUT_DEFAULT) {
> +		if (type == NFT_MSG_NEWTABLE)
> +			printf("add table ");
> +		else
> +			printf("delete table ");
> +
> +		family = nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY);
> +
> +		printf("%s %s\n", family2str(family),
> +		       nft_table_attr_get_str(nlt, NFT_TABLE_ATTR_NAME));
> +	} else {
> +		nft_table_snprintf(buf, sizeof(buf), nlt, monh->format, 0);
> +		netlink_events_printf_wrapper(buf, monh->format, type);
> +	}
> +
> +err:
> +	nft_table_free(nlt);
> +	return MNL_CB_OK;
> +}
> +
> +static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type,
> +				   struct netlink_mon_handler *monh)
> +{
> +	struct nft_chain *nlc;
> +	struct chain *c;
> +	uint32_t family;
> +	char buf[4096];
> +
> +	nlc = nft_chain_alloc();
> +	if (nlc == NULL)
> +		memory_allocation_error();
> +
> +	if (nft_chain_nlmsg_parse(nlh, nlc) < 0) {
> +		netlink_io_error(monh->ctx, monh->loc,
> +				 "Could not parse chain: %s",
> +				 strerror(errno));
> +		goto out;
> +	}
> +
> +	if (monh->format == NFT_OUTPUT_DEFAULT) {
> +		if (type == NFT_MSG_NEWCHAIN) {
> +			printf("add ");
> +			c = netlink_delinearize_chain(monh->ctx, nlc);
> +			chain_print_plain(c);
> +			chain_free(c);
> +		} else {
> +			family = nft_chain_attr_get_u32(nlc,
> +							NFT_CHAIN_ATTR_FAMILY);
> +			printf("delete chain %s %s %s\n", family2str(family),
> +			       nft_chain_attr_get_str(nlc,
> +						      NFT_CHAIN_ATTR_TABLE),
> +			       nft_chain_attr_get_str(nlc,
> +						      NFT_CHAIN_ATTR_NAME));
> +		}
> +	} else {
> +		nft_chain_snprintf(buf, sizeof(buf), nlc, monh->format, 0);
> +		netlink_events_printf_wrapper(buf, monh->format, type);
> +	}
> +
> +out:
> +	nft_chain_free(nlc);
> +	return MNL_CB_OK;
> +}
> +
> +static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type,
> +				 struct netlink_mon_handler *monh)
> +{
> +	struct nft_set *nls;
> +	struct set *set;
> +	uint32_t family, flags;
> +	char buf[4096];
> +
> +	nls = nft_set_alloc();
> +	if (nls == NULL)
> +		memory_allocation_error();
> +
> +	if (nft_set_nlmsg_parse(nlh, nls) < 0) {
> +		netlink_io_error(monh->ctx, monh->loc,
> +				 "Could not parse set: %s",
> +				 strerror(errno));
> +		goto out;
> +	}
> +
> +	flags = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FLAGS);
> +	if (flags & SET_F_ANONYMOUS)
> +		goto out;
> +
> +	if (monh->format == NFT_OUTPUT_DEFAULT) {
> +		if (type == NFT_MSG_NEWSET) {
> +			printf("add ");
> +			set = netlink_delinearize_set(monh->ctx, nls);
> +			set_print_plain(set);
> +			set_free(set);
> +		} else {
> +			family = nft_set_attr_get_u32(nls,
> +						      NFT_SET_ATTR_FAMILY);
> +			printf("delete set %s %s %s",
> +			       family2str(family),
> +			       nft_set_attr_get_str(nls, NFT_SET_ATTR_TABLE),
> +			       nft_set_attr_get_str(nls, NFT_SET_ATTR_NAME));
> +		}
> +
> +		printf("\n");
> +
> +	} else {
> +		nft_set_snprintf(buf, sizeof(buf), nls, monh->format, 0);
> +		netlink_events_printf_wrapper(buf, monh->format, type);
> +	}
> +
> +out:
> +	nft_set_free(nls);
> +	return MNL_CB_OK;
> +}
> +
> +static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
> +				  struct netlink_mon_handler *monh)
> +{
> +	struct nft_rule *nlr;
> +	struct rule *r;
> +	char buf[4096];
> +	uint32_t fam;
> +	const char *family;
> +	const char *table;
> +	const char *chain;
> +	uint64_t handle;
> +
> +	nlr = nft_rule_alloc();
> +	if (nlr == NULL)
> +		memory_allocation_error();
> +
> +	if (nft_rule_nlmsg_parse(nlh, nlr) < 0) {
> +		netlink_io_error(monh->ctx, monh->loc,
> +				 "Could not parse rule: %s",
> +				 strerror(errno));
> +		goto out;
> +	}
> +
> +	if (monh->format == NFT_OUTPUT_DEFAULT) {
> +		fam = nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY);
> +		family = family2str(fam);
> +		table = nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_TABLE);
> +		chain = nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_CHAIN);
> +		handle = nft_rule_attr_get_u64(nlr, NFT_RULE_ATTR_HANDLE);
> +
> +		if (type == NFT_MSG_NEWRULE) {
> +			r = netlink_delinearize_rule(monh->ctx, nlr);
> +			printf("add rule %s %s %s", family, table, chain);
> +			rule_print(r);
> +			rule_free(r);
> +			goto out;
> +		}
> +
> +		printf("delete rule %s %s %s handle %u\n",
> +		       family, table, chain, (unsigned int)handle);
> +	} else {
> +		nft_rule_snprintf(buf, sizeof(buf), nlr, monh->format, 0);
> +		netlink_events_printf_wrapper(buf, monh->format, type);
> +	}
> +
> +out:
> +	nft_rule_free(nlr);
> +	return MNL_CB_OK;
> +}
> +
> +static void netlink_events_cache_addtable(struct netlink_mon_handler *monh,
> +					  const struct nlmsghdr *nlh)
> +{
> +	struct nft_table *nlt;
> +	struct table *t;
> +
> +	nlt = nft_table_alloc();
> +	if (nlt == NULL)
> +		memory_allocation_error();
> +
> +	if (nft_table_nlmsg_parse(nlh, nlt) < 0) {
> +		netlink_io_error(monh->ctx, monh->loc,
> +				"Could not parse table: %s",
> +				strerror(errno));
> +		goto out;
> +	}
> +
> +	t = netlink_delinearize_table(monh->ctx, nlt);
> +	table_add_hash(t);
> +
> +out:
> +	nft_table_free(nlt);
> +}
> +
> +static void netlink_events_cache_deltable(struct netlink_mon_handler *monh,
> +					  const struct nlmsghdr *nlh)
> +{
> +	struct nft_table *nlt;
> +	struct table *t;
> +	struct handle h;
> +
> +	nlt = nft_table_alloc();
> +	if (nlt == NULL)
> +		memory_allocation_error();
> +
> +	if (nft_table_nlmsg_parse(nlh, nlt) < 0) {
> +		netlink_io_error(monh->ctx, monh->loc,
> +				"Could not parse table: %s",
> +				strerror(errno));
> +		goto out;
> +	}
> +
> +	h.family = nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY);
> +	h.table = nft_table_attr_get_str(nlt, NFT_TABLE_ATTR_NAME);
> +
> +	t = table_lookup(&h);
> +	if (t == NULL)
> +		goto out;
> +
> +	list_del(&t->list);
> +	table_free(t);
> +
> +out:
> +	nft_table_free(nlt);
> +}
> +
> +static void netlink_events_cache_addset(struct netlink_mon_handler *monh,
> +					const struct nlmsghdr *nlh)
> +{
> +	struct nft_set *nls;
> +	struct netlink_ctx set_tmpctx;
> +	LIST_HEAD(msgs);
> +	struct table *t;
> +	struct set *s;
> +	uint32_t flags;
> +
> +	nls = nft_set_alloc();
> +	if (nls == NULL)
> +		memory_allocation_error();
> +
> +	if (nft_set_nlmsg_parse(nlh, nls) < 0) {
> +		netlink_io_error(monh->ctx, monh->loc,
> +				"Could not parse set: %s",
> +				strerror(errno));
> +		goto out;
> +	}
> +
> +	flags = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FLAGS);
> +	if (!(flags & SET_F_ANONYMOUS))
> +		goto out;
> +
> +	memset(&set_tmpctx, 0, sizeof(set_tmpctx));
> +	init_list_head(&set_tmpctx.list);
> +	init_list_head(&msgs);
> +	set_tmpctx.msgs = &msgs;
> +	s = netlink_delinearize_set(&set_tmpctx, nls);
> +
> +	/* XXXX */
> +	sleep(1);
> +
> +	/* only way, by now, to know set_elems (no set_elem events) */
> +	if (netlink_get_setelems(&set_tmpctx, &s->handle, monh->loc, s) < 0)
> +		goto out;
> +
> +	set_print(s);
> +
> +	t = table_lookup(&s->handle);
> +	if (t == NULL)
> +		goto out;
> +
> +	set_add_hash(s, t);
> +out:
> +	nft_set_free(nls);
> +}
> +
> +static void netlink_events_cache_delsets(struct netlink_mon_handler *monh,
> +					 const struct nlmsghdr *nlh)
> +{
> +	struct nft_rule *nlr;
> +	struct handle th;
> +	struct nft_rule_expr_iter *nlrei;
> +	struct nft_rule_expr *nlre;
> +	const char *expr_name, *set_name;
> +	struct table *t;
> +	struct set *s;
> +
> +	nlr = nft_rule_alloc();
> +	if (nlr == NULL)
> +		memory_allocation_error();
> +
> +	if (nft_rule_nlmsg_parse(nlh, nlr) < 0) {
> +		netlink_io_error(monh->ctx, monh->loc,
> +				 "Could not parse rule: %s",
> +				 strerror(errno));
> +		goto out;
> +	}
> +
> +	th.table = nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_TABLE);
> +	th.family = nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY);
> +	t = table_lookup(&th);
> +	if (t == NULL)
> +		goto out;
> +
> +

remove extra line here.

> +	nlrei = nft_rule_expr_iter_create(nlr);
> +	if (nlrei == NULL)
> +		memory_allocation_error();
> +
> +	nlre = nft_rule_expr_iter_next(nlrei);
> +	while (nlre != NULL) {
> +		expr_name = nft_rule_expr_get_str(nlre,
> +						  NFT_RULE_EXPR_ATTR_NAME);
> +		if (strcmp(expr_name, "lookup") != 0)
> +			goto next;
> +
> +		set_name = nft_rule_expr_get_str(nlre, NFT_EXPR_LOOKUP_SET);
> +		s = set_lookup(t, set_name);
> +		if (s == NULL)
> +			goto next;
> +
> +		list_del(&s->list);
> +		set_free(s);
> +next:
> +		nlre = nft_rule_expr_iter_next(nlrei);
> +	}
> +	nft_rule_expr_iter_destroy(nlrei);
> +
> +out:
> +	nft_rule_free(nlr);
> +}
> +
> +static void netlink_events_cache_update(struct netlink_mon_handler *monh,
> +					const struct nlmsghdr *nlh, int type)
> +{
> +	/* cache only needed when monitoring new rules in def. output format */
> +	if (!(monh->selector & (1 << NFT_MSG_NEWRULE)) &&
> +	    (monh->format != NFT_OUTPUT_DEFAULT))
> +		return;
> +
> +	switch (type) {
> +	case NFT_MSG_NEWTABLE:
> +		netlink_events_cache_addtable(monh, nlh);
> +		break;
> +	case NFT_MSG_DELTABLE:
> +		netlink_events_cache_deltable(monh, nlh);
> +		break;
> +	case NFT_MSG_NEWSET:
> +		netlink_events_cache_addset(monh, nlh);
> +		break;
> +	case NFT_MSG_DELRULE:
> +		/* there are no notification for anon-set deletion */
> +		netlink_events_cache_delsets(monh, nlh);
> +		break;
> +	}
> +}
> +
> +static int netlink_events_cb(const struct nlmsghdr *nlh, void *data)
> +{
> +	int ret = MNL_CB_OK;
> +	int type = nlh->nlmsg_type & 0xFF;
> +	struct netlink_mon_handler *monh = (struct netlink_mon_handler *)data;
> +
> +	netlink_events_cache_update(monh, nlh, type);
> +
> +	if (!(monh->selector & (1 << type)))
> +		return ret;
> +
> +	switch (type) {
> +	case NFT_MSG_NEWTABLE:
> +	case NFT_MSG_DELTABLE:
> +		ret = netlink_events_table_cb(nlh, type, monh);
> +		break;
> +	case NFT_MSG_NEWCHAIN:
> +	case NFT_MSG_DELCHAIN:
> +		ret = netlink_events_chain_cb(nlh, type, monh);
> +		break;
> +	case NFT_MSG_NEWSET:
> +	case NFT_MSG_DELSET:
> +		ret = netlink_events_set_cb(nlh, type, monh);
> +		break;
> +	case NFT_MSG_NEWRULE:
> +	case NFT_MSG_DELRULE:
> +		ret = netlink_events_rule_cb(nlh, type, monh);
> +		break;
> +	default:
> +		BUG("Unknow event received from kernel.\n");
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +int netlink_monitor(struct netlink_mon_handler *monhandler)
> +{
> +	netlink_open_mon_sock();
> +
> +	if (mnl_socket_bind(nf_mon_sock, (1 << (NFNLGRP_NFTABLES-1)),
> +			    MNL_SOCKET_AUTOPID) < 0)
> +		return netlink_io_error(monhandler->ctx, monhandler->loc,
> +					"Could not bind to netlink socket %s",
> +					strerror(errno));
> +
> +	return mnl_nft_event_listener(nf_mon_sock, netlink_events_cb,
> +				      monhandler);
> +}
> diff --git a/src/parser.y b/src/parser.y
> index b3acc74..a154696 100644
> --- a/src/parser.y
> +++ b/src/parser.y
> @@ -163,12 +163,16 @@ static void location_update(struct location *loc, struct location *rhs, int n)
>  %token TABLE			"table"
>  %token TABLES			"tables"
>  %token CHAIN			"chain"
> +%token CHAINS			"chains"
>  %token RULE			"rule"
> +%token RULES			"rules"
>  %token SETS			"sets"
>  %token SET			"set"
>  %token ELEMENT			"element"
>  %token MAP			"map"
>  %token HANDLE			"handle"
> +%token ADDED			"added"
> +%token DELETED			"deleted"
>  %token INET			"inet"
>  
> @@ -181,6 +185,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
>  %token RENAME			"rename"
>  %token DESCRIBE			"describe"
>  %token EXPORT			"export"
> +%token MONITOR			"monitor"
>  
>  %token ACCEPT			"accept"
>  %token DROP			"drop"
> @@ -361,8 +366,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
>  %type <cmd>			line
>  %destructor { cmd_free($$); }	line
>  
> -%type <cmd>			base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd
> -%destructor { cmd_free($$); }	base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd
> +%type <cmd>			base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd
> +%destructor { cmd_free($$); }	base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd
>  
>  %type <handle>			table_spec tables_spec chain_spec chain_identifier ruleid_spec
>  %destructor { handle_free(&$$); } table_spec tables_spec chain_spec chain_identifier ruleid_spec
> @@ -485,7 +490,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
>  %destructor { expr_free($$); }	ct_expr
>  %type <val>			ct_key
>  
> -%type <val>			export_format
> +%type <val>			export_format	output_format	mon_selector
>  
>  %%
>  
> @@ -585,6 +590,7 @@ base_cmd		:	/* empty */	add_cmd		{ $$ = $1; }
>  			|	FLUSH		flush_cmd	{ $$ = $2; }
>  			|	RENAME		rename_cmd	{ $$ = $2; }
>  			|	EXPORT		export_cmd	{ $$ = $2; }
> +			|	MONITOR		monitor_cmd	{ $$ = $2; }
>  			|	DESCRIBE	primary_expr
>  			{
>  				expr_describe($2);
> @@ -752,6 +758,69 @@ export_cmd		:	export_format
>  			}
>  			;
>  
> +monitor_cmd		:	mon_selector	output_format
> +			{
> +				struct handle h = { .family = NFPROTO_UNSPEC };
> +				$$ = cmd_alloc(CMD_MONITOR, CMD_OBJ_RULESET, &h, &@$, NULL);
> +				$$->mon_selector = $1;
> +				$$->format = $2;
> +			}
> +			;
> +
> +mon_selector		:	/* empty */
> +			{
> +				$$ |= (1 << NFT_MSG_NEWRULE);	$$ |= (1 << NFT_MSG_DELRULE);

Not sure if anyone commented on this, I prefer two lines here. I tend
to stop reading when I find semi-colon.

> +				$$ |= (1 << NFT_MSG_NEWSET);	$$ |= (1 << NFT_MSG_DELSET);
> +				$$ |= (1 << NFT_MSG_NEWCHAIN);	$$ |= (1 << NFT_MSG_DELCHAIN);
> +				$$ |= (1 << NFT_MSG_NEWTABLE);	$$ |= (1 << NFT_MSG_DELTABLE);
> +			}
> +			|	ADDED
> +			{
> +				$$ |= (1 << NFT_MSG_NEWRULE);
> +				$$ |= (1 << NFT_MSG_NEWSET);
> +				$$ |= (1 << NFT_MSG_NEWCHAIN);
> +				$$ |= (1 << NFT_MSG_NEWTABLE);
> +			}
> +			|	DELETED
> +			{
> +				$$ |= (1 << NFT_MSG_DELRULE);
> +				$$ |= (1 << NFT_MSG_DELSET);
> +				$$ |= (1 << NFT_MSG_DELCHAIN);
> +				$$ |= (1 << NFT_MSG_DELTABLE);
> +			}
> +			|	TABLES
> +			{
> +				$$ |= (1 << NFT_MSG_NEWTABLE);	$$ |= (1 << NFT_MSG_DELTABLE);
> +			}
> +			|	ADDED 	TABLES 	{	$$ |= (1 << NFT_MSG_NEWTABLE); }
> +			|	DELETED	TABLES	{	$$ |= (1 << NFT_MSG_DELTABLE); }
> +			|	CHAIN
> +			{
> +				$$ |= (1 << NFT_MSG_NEWCHAIN);	$$ |= (1 << NFT_MSG_DELCHAIN);
> +			}
> +			|	ADDED	CHAINS	{	$$ |= (1 << NFT_MSG_NEWCHAIN); }
> +			|	DELETED	CHAINS	{	$$ |= (1 << NFT_MSG_DELCHAIN); }
> +			|	SET
> +			{
> +				$$ |= (1 << NFT_MSG_NEWSET);	$$ |= (1 << NFT_MSG_DELSET);
> +			}
> +			|	ADDED	SETS	{	$$ |= (1 << NFT_MSG_NEWSET); }
> +			|	DELETED SETS	{	$$ |= (1 << NFT_MSG_DELSET); }
> +			|	RULE
> +			{
> +				$$ |= (1 << NFT_MSG_NEWRULE);	$$ |= (1 << NFT_MSG_DELRULE);
> +			}
> +			|	ADDED 	RULES	{	$$ |= (1 << NFT_MSG_NEWRULE); }
> +			|	DELETED RULES	{	$$ |= (1 << NFT_MSG_DELRULE); }
> +			;
> +
> +output_format		:	/* empty */
> +			{
> +				$$ = NFT_OUTPUT_DEFAULT;
> +			}
> +			|	export_format
> +			;
> +
>  table_block_alloc	:	/* empty */
>  			{
>  				$$ = table_alloc();
> diff --git a/src/rule.c b/src/rule.c
> index d8a065a..72fc306 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -799,6 +799,30 @@ static int do_command_rename(struct netlink_ctx *ctx, struct cmd *cmd)
>  	return 0;
>  }
>  
> +static int do_command_monitor(struct netlink_ctx *ctx, struct cmd *cmd)
> +{
> +	struct table *t, *nt;
> +	struct netlink_mon_handler monhandler;
> +
> +	/* cache only needed if monitoring new rules in def. output format */
> +	if ((cmd->mon_selector & (1 << NFT_MSG_NEWRULE)) &&
> +	    (cmd->format == NFT_OUTPUT_DEFAULT)) {
> +		if (netlink_list_tables(ctx, &cmd->handle, &cmd->location) < 0)
> +			return -1;
> +
> +		list_for_each_entry_safe(t, nt, &ctx->list, list) {
> +			table_add_hash(t);
> +		}
> +	}
> +
> +	monhandler.selector = cmd->mon_selector;
> +	monhandler.format = cmd->format;
> +	monhandler.ctx = ctx;
> +	monhandler.loc = &cmd->location;
> +
> +	return netlink_monitor(&monhandler);
> +}
> +
>  int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
>  {
>  	switch (cmd->op) {
> @@ -818,6 +842,8 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
>  		return do_command_rename(ctx, cmd);
>  	case CMD_EXPORT:
>  		return do_command_export(ctx, cmd);
> +	case CMD_MONITOR:
> +		return do_command_monitor(ctx, cmd);
>  	default:
>  		BUG("invalid command object type %u\n", cmd->obj);
>  	}
> diff --git a/src/scanner.l b/src/scanner.l
> index 45c6476..59aee8a 100644
> --- a/src/scanner.l
> +++ b/src/scanner.l
> @@ -232,12 +232,16 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
>  "table"			{ return TABLE; }
>  "tables"		{ return TABLES; }
>  "chain"			{ return CHAIN; }
> +"chains"		{ return CHAINS; }
>  "rule"			{ return RULE; }
> +"rules"			{ return RULES; }
>  "sets"			{ return SETS; }
>  "set"			{ return SET; }
>  "element"		{ return ELEMENT; }
>  "map"			{ return MAP; }
>  "handle"		{ return HANDLE; }
> +"added"			{ return ADDED; }
> +"deleted"		{ return DELETED; }
>  
>  "accept"		{ return ACCEPT; }
>  "drop"			{ return DROP; }
> @@ -256,6 +260,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
>  "flush"			{ return FLUSH; }
>  "rename"		{ return RENAME; }
>  "export"		{ return EXPORT; }
> +"monitor"		{ return MONITOR; }
>  
>  "position"		{ return POSITION; }
>  
> 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v2 6/6] src: add events reporting
  2014-02-26 17:17   ` Arturo Borrero Gonzalez
@ 2014-02-26 17:27     ` Pablo Neira Ayuso
  2014-02-26 17:36       ` Arturo Borrero Gonzalez
  0 siblings, 1 reply; 14+ messages in thread
From: Pablo Neira Ayuso @ 2014-02-26 17:27 UTC (permalink / raw)
  To: Arturo Borrero Gonzalez; +Cc: Netfilter Development Mailing list

On Wed, Feb 26, 2014 at 06:17:36PM +0100, Arturo Borrero Gonzalez wrote:
> On 26 February 2014 17:10, Arturo Borrero Gonzalez
> <arturo.borrero.glez@gmail.com> wrote:
> >
> > +
> > +       /* XXXX */
> > +       sleep(1);
> > +
> > +       /* only way, by now, to know set_elems (no set_elem events) */
> > +       if (netlink_get_setelems(&set_tmpctx, &s->handle, monh->loc, s) < 0)
> > +               goto out;
> > +
> 
> About this:
> 
> it seems that I can't query the kernel just after the set is created,
> because I get an empty set.

I see. Since sets are first created then elements are added, I think
you have to make a kernel patch to add set element events.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v2 6/6] src: add events reporting
  2014-02-26 17:27     ` Pablo Neira Ayuso
@ 2014-02-26 17:36       ` Arturo Borrero Gonzalez
  0 siblings, 0 replies; 14+ messages in thread
From: Arturo Borrero Gonzalez @ 2014-02-26 17:36 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: Netfilter Development Mailing list

On 26 February 2014 18:27, Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> On Wed, Feb 26, 2014 at 06:17:36PM +0100, Arturo Borrero Gonzalez wrote:
>> On 26 February 2014 17:10, Arturo Borrero Gonzalez
>> <arturo.borrero.glez@gmail.com> wrote:
>> >
>> > +
>> > +       /* XXXX */
>> > +       sleep(1);
>> > +
>> > +       /* only way, by now, to know set_elems (no set_elem events) */
>> > +       if (netlink_get_setelems(&set_tmpctx, &s->handle, monh->loc, s) < 0)
>> > +               goto out;
>> > +
>>
>> About this:
>>
>> it seems that I can't query the kernel just after the set is created,
>> because I get an empty set.
>
> I see. Since sets are first created then elements are added, I think
> you have to make a kernel patch to add set element events.

Ok,

I am working on that already, the patch is half done.

So this monitor stuff goes to my tasks queue.

regards.
-- 
Arturo Borrero González
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v2 0/6] nft events reporting
  2014-02-26 16:09 [RFC PATCH v2 0/6] nft events reporting Arturo Borrero Gonzalez
                   ` (5 preceding siblings ...)
  2014-02-26 16:10 ` [RFC PATCH v2 6/6] src: add events reporting Arturo Borrero Gonzalez
@ 2014-02-27 14:09 ` Patrick McHardy
  6 siblings, 0 replies; 14+ messages in thread
From: Patrick McHardy @ 2014-02-27 14:09 UTC (permalink / raw)
  To: Arturo Borrero Gonzalez; +Cc: netfilter-devel, pablo

On Wed, Feb 26, 2014 at 05:09:44PM +0100, Arturo Borrero Gonzalez wrote:
> This series implements basic event reporting in the nftables CLI tool.
> 
> The first patches are some neccesary code factorization changes.
> The last patch is the event reporting itself.
> 
> Its quite simple, the syntax is:
>  % nft monitor [added|deleted] [tables|chains|sets|rules] [xml|json]
> 
> I've discarted using 'new|delete' keywords because 'new' collides with
> the 'state new'ct option. 

I have to strong preference, only tending to think that new/delete ic a bit
nicer. If you want to keep them, you can do handle this similar how to we
deal with f.i. "ip protocol tcp". If "new" is occuring somewhere outside
of the monitor rule, you simply convert it to a symbol expression.

> About this last format:
> 
> Rules are hard to print exactly as the user typed because sets.
> The approach followed in the patch is:
>  * keep a userspace cache of tables/anonymous sets.
>  * since there are no event notifications for set_elements, query kernel
>  for set_elements in the event callback.
>  * since there are no event notification for deleted anon-sets, and sets names
>  are reusable, scan each deleted rule to know which sets delete from the cache.
>  * no need to do any caching if we are not monitoring new rule
>  events in the nft default format.

We could add notifications for set elements and anonymous set deletions
if that makes things easier. Is my assumption correct that you only print
the set members for anonymous sets and for non-anonmyous sets simply print
... @set?

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2014-02-27 14:09 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-26 16:09 [RFC PATCH v2 0/6] nft events reporting Arturo Borrero Gonzalez
2014-02-26 16:09 ` [RFC PATCH v2 1/6] rule: allow to print sets in plain format Arturo Borrero Gonzalez
2014-02-26 16:44   ` Pablo Neira Ayuso
2014-02-26 16:09 ` [RFC PATCH v2 2/6] netlink: add netlink_delinearize_set() func Arturo Borrero Gonzalez
2014-02-26 16:10 ` [RFC PATCH v2 3/6] rule: generalize chain_print() Arturo Borrero Gonzalez
2014-02-26 16:10 ` [RFC PATCH v2 4/6] netlink: add netlink_delinearize_chain() func Arturo Borrero Gonzalez
2014-02-26 16:49   ` Pablo Neira Ayuso
2014-02-26 16:10 ` [RFC PATCH v2 5/6] netlink: add netlink_delinearize_table() func Arturo Borrero Gonzalez
2014-02-26 16:10 ` [RFC PATCH v2 6/6] src: add events reporting Arturo Borrero Gonzalez
2014-02-26 17:17   ` Arturo Borrero Gonzalez
2014-02-26 17:27     ` Pablo Neira Ayuso
2014-02-26 17:36       ` Arturo Borrero Gonzalez
2014-02-26 17:19   ` Pablo Neira Ayuso
2014-02-27 14:09 ` [RFC PATCH v2 0/6] nft " Patrick McHardy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.