nft event monitor
cover nft event monitor
The following series implements a basic nftables monitor via Netlink messages.
Most of the work in first patches is related to refactorization/generalization of code.
The final patch is the big one.
About the syntax, i'm proposing:
% nft monitor [added|deleted] [tables|chains|sets|elements|rules] [xml|json]
The straight-forward way of test this new feature is to simply run:
% nft monitor
Other examples:
* report new tables in XML format
% nft monitor added tables xml
* report deleted elements in standar nft syntax
% nft monitor deleted elements
* report all added/deleted rules in JSON format
% nft monitor rules json
Handling set/set_elems is one of the harders parts of event reporting. I've succesfully tested many cases (maps, named sets, anon-sets..), but I guess more tuning can be done in the future, with some additional use and testing by the community.
Please comment.
regards.
---
Arturo Borrero Gonzalez (8):
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
netlink: refactorize set_elem conversion from netlink
netlink: add socket error reporting helper function
src: add events reporting
doc/nftables.xml | 1 include/mnl.h | 3 include/netlink.h | 11 + include/nftables.h | 1 include/rule.h | 10 + src/evaluate.c | 1 src/mnl.c | 45 ++- src/netlink.c | 746 ++++++++++++++++++++++++++++++++++++++++++++++------ src/parser.y | 90 ++++++ src/rule.c | 163 +++++++++++ src/scanner.l | 5 11 files changed, 957 insertions(+), 119 deletions(-)
Signature · 1 line
Arturo Borrero Gonzalez
PATCH 1/8 rule: allow to print sets in plain format
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.
--- include/rule.h | 3 +++ src/rule.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 10 deletions(-)
include/rule.h +3 -0
@@ -127,6 +127,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 *
@@ -195,6 +197,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
src/rule.c +50 -10
93 lines not on this page · raw message
PATCH 2/8 netlink: add netlink_delinearize_set() func
In reply to Arturo Borrero GonzalezLet's factorize this code, so it can be reused.
--- src/netlink.c | 112 +++++++++++++++++++++++++++------------------------------ 1 file changed, 53 insertions(+), 59 deletions(-)
src/netlink.c +53 -59
@@ -755,6 +755,57 @@ 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; +const struct datatype *keytype, *datatype; +uint32_t flags, key, data, data_len; + + key =nft_set_attr_get_u32 (nls,NFT_SET_ATTR_KEY_TYPE ); + keytype =dtype_map_from_kernel (key); +if (keytype ==NULL ) { +netlink_io_error (ctx,NULL ,"Unknown data type in set key %u" , + key); +return NULL ; + } + + flags =nft_set_attr_get_u32 (nls,NFT_SET_ATTR_FLAGS ); +if (flags &NFT_SET_MAP ) { + data =nft_set_attr_get_u32 (nls,NFT_SET_ATTR_DATA_TYPE ); + datatype =dtype_map_from_kernel (data); +if (datatype ==NULL ) { +netlink_io_error (ctx,NULL , +"Unknown data type in set key %u" , + data); +return NULL ; + } + }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 =nft_set_attr_get_u32 (nls,NFT_SET_ATTR_FLAGS ); + + set->datatype = 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) {
@@ -806,48 +857,9 @@ int netlink_delete_set(struct netlink_ctx *ctx, const struct handle *h,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 ); - keytype =dtype_map_from_kernel (key); -if (keytype ==NULL ) { -netlink_io_error (ctx,NULL ,"Unknown data type in set key %u" , - key); -return -1 ; - } - - flags =nft_set_attr_get_u32 (nls,NFT_SET_ATTR_FLAGS ); -if (flags &NFT_SET_MAP ) { - data =nft_set_attr_get_u32 (nls,NFT_SET_ATTR_DATA_TYPE ); - datatype =dtype_map_from_kernel (data); -if (datatype ==NULL ) { -netlink_io_error (ctx,NULL ,"Unknown data type in set key %u" , - data); -return -1 ; - } - }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 ; }
@@ -871,7 +883,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);
@@ -882,24 +893,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;
PATCH 3/8 rule: generalize chain_print()
In reply to Arturo Borrero GonzalezLest generalize the chain_print() function, so we can print a plain chain as the user typed in the basic CLI.
--- include/rule.h | 1 + src/rule.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)
include/rule.h +1 -0
@@ -128,6 +128,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
src/rule.c +14 -0
@@ -401,6 +401,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;
PATCH 4/8 netlink: add netlink_delinearize_chain() func
In reply to Arturo Borrero GonzalezLet's make this code reusable.
--- src/netlink.c | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-)
src/netlink.c +21 -25
@@ -500,20 +500,11 @@ 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 );
@@ -535,6 +526,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 ; }
@@ -574,25 +583,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 )
PATCH 5/8 netlink: add netlink_delinearize_table() func
In reply to Arturo Borrero GonzalezThis code is suitable to be reusable.
--- src/netlink.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
src/netlink.c +11 -2
@@ -656,9 +656,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);
@@ -669,6 +669,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 ; }
PATCH 6/8 netlink: refactorize set_elem conversion from netlink
In reply to Arturo Borrero GonzalezLets refactorize set_elem handling.
--- src/netlink.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
src/netlink.c +8 -3
@@ -953,11 +953,10 @@ int netlink_delete_setelems(struct netlink_ctx *ctx, const struct handle *h,return err; } -static int list_setelem_cb (struct nft_set_elem *nlse,void *arg) +static int netlink_delinearize_setelem (struct nft_set_elem *nlse, +struct set *set) {struct nft_data_delinearize nld; -struct netlink_ctx *ctx = arg; -struct set *set = ctx->set;struct expr *expr, *data;uint32_t flags =0 ;
@@ -1001,6 +1000,12 @@ out:return 0 ; } +static int list_setelem_cb (struct nft_set_elem *nlse,void *arg) +{ +struct netlink_ctx *ctx = arg; +return netlink_delinearize_setelem (nlse, ctx->set); +} +extern void interval_map_decompose (struct expr *set); int netlink_get_setelems(struct netlink_ctx *ctx, const struct handle *h,
PATCH 7/8 netlink: add socket error reporting helper function
In reply to Arturo Borrero GonzalezThis patch adds a simple helper function to report errors while opening the Netlink socket.
To help users to diagnose problems, a new NFT_EXIT_NONL exit code is included, which is 3.
--- doc/nftables.xml | 1 + include/netlink.h | 1 + include/nftables.h | 1 + src/netlink.c | 10 +++++++++- 4 files changed, 12 insertions(+), 1 deletion(-)
doc/nftables.xml +1 -0
@@ -928,6 +928,7 @@ On success, nftables exits with a status of 0. Unspecified errors cause it to exit with a status of 1, memory allocation errors with a status of 2. + If unable to open Netlink socket, the return code is 3. </para> </refsect1>
include/netlink.h +1 -0
@@ -138,6 +138,7 @@ extern void netlink_dump_set(struct nft_set *nls);extern int netlink_batch_send (struct list_head *err_list);extern int netlink_io_error (struct netlink_ctx *ctx,const struct location *loc,const char *fmt, ...); +extern void netlink_open_error (void )__noreturn ; extern struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, const struct handle *h,
include/nftables.h +1 -0
@@ -39,6 +39,7 @@ enum nftables_exit_codes {NFT_EXIT_SUCCESS =0 ,NFT_EXIT_FAILURE =1 ,NFT_EXIT_NOMEM =2 , + NFT_EXIT_NONL = 3, };struct input_descriptor ;
src/netlink.c +9 -1
@@ -15,6 +15,7 @@#include <libmnl/libmnl.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <stdlib.h> #include <libnftnl/table.h> #include <libnftnl/chain.h>
@@ -46,7 +47,7 @@ static void __init netlink_open_sock(void) { nf_sock =mnl_socket_open (NETLINK_NETFILTER );if (nf_sock ==NULL ) -memory_allocation_error (); +netlink_open_error ();fcntl (mnl_socket_get_fd (nf_sock),F_SETFL ,O_NONBLOCK );mnl_batch_init ();
@@ -73,6 +74,13 @@ int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,return -1 ; } +void __noreturn netlink_open_error (void ) +{ +fprintf (stderr,"E: Unable to open Netlink socket: %s\n" , +strerror (errno)); +exit (NFT_EXIT_NONL ); +} +struct nft_table *alloc_nft_table (const struct handle *h) {struct nft_table *nlt;
PATCH 8/8 src: add events reporting
In reply to Arturo Borrero GonzalezThis patch adds a basic events reporting option to nft.
The syntax is:
% nft monitor [added|deleted] [tables|chains|rules|sets] [xml|json]
--- include/mnl.h | 3 include/netlink.h | 10 + include/rule.h | 6 + src/evaluate.c | 1 src/mnl.c | 45 +++- src/netlink.c | 554 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/parser.y | 90 ++++++++- src/rule.c | 89 +++++++++ src/scanner.l | 5 9 files changed, 784 insertions(+), 19 deletions(-)
include/mnl.h +3 -0
@@ -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_ */
include/netlink.h +10 -0
@@ -136,6 +136,7 @@ extern void netlink_dump_expr(struct nft_rule_expr *nle);extern void netlink_dump_set (struct nft_set *nls);extern int netlink_batch_send (struct list_head *err_list); +extern void netlink_abi_error (void )__noreturn ;extern int netlink_io_error (struct netlink_ctx *ctx,const struct location *loc,const char *fmt, ...);extern void netlink_open_error (void )__noreturn ;
@@ -143,4 +144,13 @@ extern void netlink_open_error(void) __noreturn;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 monitor_flags; +uint32_t format; +struct netlink_ctx *ctx; +const struct location *loc; +bool cache_needed; +}; + +extern int netlink_monitor (struct netlink_mon_handler *monhandler);#endif /* NFTABLES_NETLINK_H */
include/rule.h +6 -0
@@ -195,8 +195,11 @@ struct set {extern struct set *set_alloc (const struct location *loc);extern struct set *set_get (struct set *set);extern void set_free (struct set *set); +extern struct set *set_clone (const 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 struct set *set_lookup_global (uint32_t family,const char *table, +const char *name);extern void set_print (const struct set *set);extern void set_print_plain (const struct set *s);
@@ -212,6 +215,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 ,
@@ -223,6 +227,7 @@ enum cmd_ops {CMD_FLUSH ,CMD_RENAME ,CMD_EXPORT , + CMD_MONITOR, };/**
@@ -278,6 +283,7 @@ struct cmd { };const void *arg;uint32_t format; +uint32_t monitor_flags; }; extern struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
src/evaluate.c +1 -0
@@ -1428,6 +1428,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);
src/mnl.c +32 -13
65 lines not on this page · raw message
src/netlink.c +551 -3
584 lines not on this page · raw message
src/parser.y +87 -3
132 lines not on this page · raw message
src/rule.c +89 -0
113 lines not on this page · raw message
src/scanner.l +5 -0
@@ -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; } "comment" { return COMMENT; }
Pablo Neira Ayuso
maintainerRe: [nft PATCH 8/8] src: add events reporting
src/mnl.c uint32_t mnl_seqnum_alloc(void) · line 39
Quoted 13 lines · Arturo Borrero Gonzalez
src/mnl.c +3
-2@@ -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,uint32_t seqnum,uint32_t portid, +int (*cb)(const struct nlmsghdr *nlh,void *data), +void *cb_data)
Please, now that you're revisiting this, rename it to:
nft_mnl_recv()
and while at it rename mnl_talk() to nft_mnl_talk().
I would like to avoid a clash with some new function in libmnl in the future. That name selection I made was not very good idea indeed.
Thanks.
Quoted 153 lines
src/mnl.c +29
-11{ -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); +}src/netlink.c +52
-3@@ -21,6 +21,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> @@ -33,6 +34,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" ,@@ -43,12 +45,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 )netlink_open_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 (); }@@ -56,6 +64,20 @@ 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 (); +} + +void __noreturn netlink_abi_error (void ) +{ +fprintf (stderr,"E: Contact urgently your Linux kernel vendor. " +"Netlink ABI is broken: %s\n" ,strerror (errno)); +exit (NFT_EXIT_FAILURE ); } int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,@@ -1062,3 +1084,529 @@ 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 : +case NFT_MSG_NEWSETELEM : + type_str ="add" ; +break ; +case NFT_MSG_DELTABLE : +case NFT_MSG_DELCHAIN : +case NFT_MSG_DELSET : +case NFT_MSG_DELRULE : +case NFT_MSG_DELSETELEM : + type_str ="del" ; +break ; + } + +switch (format) { +case NFT_OUTPUT_XML : + printf("<event><type>%s</type>" + "<nftables>%s</nftables></event>\n",
src/netlink.c struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, · line 1123
Quoted 10 lines
src/netlink.c +10
-0+ 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" ); + } +}
This chunk, I'd like to see it in the library.
src/netlink.c struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, · line 1125
Quoted 1 line
src/netlink.c +1
-0+static struct nft_table *netlink_nlmsghdr2nltable (const struct nlmsghdr *nlh)
Perhaps netlink_table_alloc(const struct nlmsghdr *nlh) ?
src/netlink.c struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, · line 1137
Quoted 12 lines
src/netlink.c +12
-0+{ +struct nft_table *nlt =nft_table_alloc (); +if (nlt ==NULL ) +memory_allocation_error (); + +if (nft_table_nlmsg_parse (nlh, nlt) <0 ) +netlink_abi_error (); + +return nlt; +} + +static struct nft_chain *netlink_nlmsghdr2nlchain (const struct nlmsghdr *nlh)
netlink_chain_alloc ?
src/netlink.c struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, · line 1150
Quoted 13 lines
src/netlink.c +13
-0+{ +struct nft_chain *nlc =nft_chain_alloc (); +if (nlc ==NULL ) +memory_allocation_error (); + +if (nft_chain_nlmsg_parse (nlh, nlc) <0 ) +netlink_abi_error (); + +return nlc; +} + +static struct nft_set *netlink_nlmsghdr2nlset (const struct nlmsghdr *nlh) +{
netlink_set_alloc
src/netlink.c struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, · line 1161
Quoted 11 lines
src/netlink.c +11
-0+struct nft_set *nls =nft_set_alloc (); +if (nls ==NULL ) +memory_allocation_error (); + +if (nft_set_nlmsg_parse (nlh, nls) <0 ) +netlink_abi_error (); + +return nls; +} + +static struct nft_set *netlink_nlmsghdr2nlsetelem (const struct nlmsghdr *nlh)
netlink_setelem_alloc
Quoted 5 lines
src/netlink.c +5
-0+{ +struct nft_set *nls =nft_set_alloc (); +if (nls ==NULL ) +memory_allocation_error (); +
src/netlink.c struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, · line 1176
Quoted 10 lines
src/netlink.c +10
-0+if (nft_set_elems_nlmsg_parse (nlh, nls) <0 ) +netlink_abi_error (); + +return nls; +} + +static struct nft_set_elem *netlink_nlset_first_elem (struct nft_set *nls) +{ +struct nft_set_elems_iter *nlsei; +struct nft_set_elem *nlse;
I remember you asked to have this in the library, go ahead add some helper there.
Otherwise, rename this to netlink_set_get_first_elem?
Quoted 29 lines
src/netlink.c +29
-0+ +/* only first, useful for set_elem event reporting from kernel */ + nlsei =nft_set_elems_iter_create (nls); +if (nlsei ==NULL ) +memory_allocation_error (); + + nlse =nft_set_elems_iter_cur (nlsei); +if (nlse ==NULL ) +memory_allocation_error (); + +nft_set_elems_iter_destroy (nlsei); + +return nlse; +} + +static struct nft_rule *netlink_nlmsghdr2nlrule (const struct nlmsghdr *nlh) +{ +struct nft_rule *nlr =nft_rule_alloc (); +if (nlr ==NULL ) +memory_allocation_error (); + +if (nft_rule_nlmsg_parse (nlh, nlr) <0 ) +netlink_abi_error (); + +return nlr; +} + +static int netlink_events_table_cb (const struct nlmsghdr *nlh,int type, +struct netlink_mon_handler *monh)
src/netlink.c struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, · line 1215
Quoted 10 lines
src/netlink.c +10
-0+{ +uint32_t family; +char buf[4096 ]; +struct nft_table *nlt =netlink_nlmsghdr2nltable (nlh); + +if (monh->format ==NFT_OUTPUT_DEFAULT ) { +if (type ==NFT_MSG_NEWTABLE ) +printf ("add table " ); +else +printf ("delete table " );
You have to check nlh->nlmsg_flags & NLM_F_EXCL here to catch table updates.
Quoted 14 lines
src/netlink.c +14
-0+ + 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); + } + +nft_table_free (nlt); +return MNL_CB_OK ; +} +
src/netlink.c struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, · line 1239
Quoted 10 lines
src/netlink.c +10
-0+static int netlink_events_chain_cb (const struct nlmsghdr *nlh,int type, +struct netlink_mon_handler *monh) +{ +struct chain *c; +uint32_t family; +char buf[4096 ]; +struct nft_chain *nlc =netlink_nlmsghdr2nlchain (nlh); + +if (monh->format ==NFT_OUTPUT_DEFAULT ) { +if (type ==NFT_MSG_NEWCHAIN ) {
Same thing here, catch chain updates.
Quoted 644 lines
src/netlink.c +373
-0+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); + } + +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 set *set; +uint32_t family, flags; +char buf[4096 ]; +struct nft_set *nls =netlink_nlmsghdr2nlset (nlh); + + 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_setelem_cb (const struct nlmsghdr *nlh,int type, +struct netlink_mon_handler *monh) +{ +struct nft_set_elem *nlse; +struct set *dummyset; +struct set *set; +const char *setname, *table; +uint32_t family; +char buf[4096 ]; +struct nft_set *nls =netlink_nlmsghdr2nlsetelem (nlh); + + table =nft_set_attr_get_str (nls,NFT_SET_ATTR_TABLE ); + setname =nft_set_attr_get_str (nls,NFT_SET_ATTR_NAME ); + family =nft_set_attr_get_u32 (nls,NFT_SET_ATTR_FAMILY ); + + set =set_lookup_global (family, table, setname); +if (set ==NULL ) { +fprintf (stderr,"W: Received event for an unknown set." ); + goto out; + } + +if (monh->format ==NFT_OUTPUT_DEFAULT ) { +if (set->flags &SET_F_ANONYMOUS ) + goto out; + + nlse =netlink_nlset_first_elem (nls); + +/* we want to 'delinearize' the set_elem, but don't modify the +* original cached set. This path is only used by named sets, +* so use a dummy set. +*/ + dummyset =set_alloc (monh->loc); + dummyset->keytype = set->keytype; + dummyset->datatype = set->datatype; + dummyset->init =set_expr_alloc (monh->loc); + +if (netlink_delinearize_setelem (nlse, dummyset) <0 ) { +set_free (dummyset); + goto out; + } + +if (type ==NFT_MSG_NEWSETELEM ) +printf ("add " ); +else +printf ("delete " ); + +printf ("element %s %s %s " ,family2str (family), table, setname); +expr_print (dummyset->init); +printf ("\n" ); + +set_free (dummyset); + }else { +nft_set_snprintf (buf,sizeof (buf), nls, monh->format,0 ); +netlink_events_printf_wrapper (buf, monh->format, type); + } + +out: +/* nlse is freed here */ +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 rule *r; +char buf[4096 ]; +uint32_t fam; +const char *family; +const char *table; +const char *chain; +uint64_t handle; +struct nft_rule *nlr =netlink_nlmsghdr2nlrule (nlh); + +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); +printf ("\n" ); + +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 table *t; +struct nft_table *nlt =netlink_nlmsghdr2nltable (nlh); + + t =netlink_delinearize_table (monh->ctx, nlt); +table_add_hash (t); + +nft_table_free (nlt); +} + +static void netlink_events_cache_deltable (struct netlink_mon_handler *monh, +const struct nlmsghdr *nlh) +{ +struct table *t; +struct handle h; +struct nft_table *nlt =netlink_nlmsghdr2nltable (nlh); + + 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 set *s; +LIST_HEAD (msgs); +struct table *t; +struct netlink_ctx set_tmpctx; +struct nft_set *nls =netlink_nlmsghdr2nlset (nlh); + +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); + s->init =set_expr_alloc (monh->loc); + + t =table_lookup (&s->handle); +if (t ==NULL ) { +fprintf (stderr,"W: Unable to cache set: table not found.\n" ); + goto out; + } + +set_add_hash (s, t); +out: +nft_set_free (nls); +} + +static void netlink_events_cache_addsetelem (struct netlink_mon_handler *monh, +const struct nlmsghdr *nlh) +{ +struct set *set; +struct nft_set_elem *nlse; +const char *table, *setname; +struct nft_set *nls =netlink_nlmsghdr2nlsetelem (nlh); + + table =nft_set_attr_get_str (nls,NFT_SET_ATTR_TABLE ); + setname =nft_set_attr_get_str (nls,NFT_SET_ATTR_NAME ); + + set =set_lookup_global (nft_set_attr_get_u32 (nls,NFT_SET_ATTR_FAMILY ), + table, setname); +if (set ==NULL ) { +fprintf (stderr, +"W: Unable to cache set_elem. Set not found.\n" ); + goto out; + } + + nlse =netlink_nlset_first_elem (nls); + +if (netlink_delinearize_setelem (nlse, set) <0 ) +fprintf (stderr, +"W: Unable to cache set_elem. Delinearize failed.\n" ); +out: +/* nlse is freed inside nft_set_free() */ +nft_set_free (nls); +} + +static void netlink_events_cache_delsets (struct netlink_mon_handler *monh, +const struct nlmsghdr *nlh) +{ +struct set *s; +uint32_t family; +struct nft_rule_expr *nlre; +struct nft_rule_expr_iter *nlrei; +const char *expr_name, *set_name, *table; +struct nft_rule *nlr =netlink_nlmsghdr2nlrule (nlh); + + nlrei =nft_rule_expr_iter_create (nlr); +if (nlrei ==NULL ) +memory_allocation_error (); + + family =nft_rule_attr_get_u32 (nlr,NFT_RULE_ATTR_FAMILY ); + table =nft_rule_attr_get_str (nlr,NFT_RULE_ATTR_TABLE ); + + 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_global (family, table, 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); + +nft_rule_free (nlr); +} + +static void netlink_events_cache_update (struct netlink_mon_handler *monh, +const struct nlmsghdr *nlh,int type) +{ +if (!monh->cache_needed) +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_NEWSETELEM : +netlink_events_cache_addsetelem (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->monitor_flags & (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 :/* nft {add|delete} set */ + ret =netlink_events_set_cb (nlh, type, monh); +break ; +case NFT_MSG_NEWSETELEM : +case NFT_MSG_DELSETELEM :/* nft {add|delete} element */ + ret =netlink_events_setelem_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 netlink.\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); +}src/parser.y +87
-3@@ -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"@@ -362,8 +367,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@@ -491,7 +496,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 monitor_flags %%@@ -591,6 +596,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);@@ -758,6 +764,84 @@ export_cmd : export_format } ; +monitor_cmd : monitor_flags output_format + { + struct handle h = { .family = NFPROTO_UNSPEC }; + $$ = cmd_alloc(CMD_MONITOR, CMD_OBJ_RULESET, &h, &@$, NULL); + $$->monitor_flags = $1; + $$->format = $2; + } + ; + +monitor_flags : /* empty */ + { + $$ |= (1 << NFT_MSG_NEWRULE); + $$ |= (1 << NFT_MSG_DELRULE); + $$ |= (1 << NFT_MSG_NEWSET); + $$ |= (1 << NFT_MSG_DELSET); + $$ |= (1 << NFT_MSG_NEWSETELEM); + $$ |= (1 << NFT_MSG_DELSETELEM); + $$ |= (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_NEWSETELEM); + $$ |= (1 << NFT_MSG_NEWCHAIN); + $$ |= (1 << NFT_MSG_NEWTABLE); + } + | DELETED + { + $$ |= (1 << NFT_MSG_DELRULE); + $$ |= (1 << NFT_MSG_DELSET); + $$ |= (1 << NFT_MSG_DELSETELEM); + $$ |= (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); } + | SETS + { + $$ |= (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); } + | ELEMENTS + { + $$ |= (1 << NFT_MSG_NEWSETELEM); + $$ |= (1 << NFT_MSG_DELSETELEM); + } + | ADDED ELEMENTS { $$ |= (1 << NFT_MSG_NEWSETELEM); } + | DELETED ELEMENTS { $$ |= (1 << NFT_MSG_DELSETELEM); } + ; + +output_format : /* empty */ + { + $$ = NFT_OUTPUT_DEFAULT; + } + | export_format + ; + table_block_alloc : /* empty */ { $$ = table_alloc();src/rule.c +89
-0@@ -77,6 +77,22 @@ void set_free(struct set *set)xfree (set); } +struct set *set_clone (const struct set *set) +{ +struct set *newset =set_alloc (&set->location); + + newset->list = set->list; +handle_merge (&newset->handle, &set->handle); + newset->flags = set->flags; + newset->keytype = set->keytype; + newset->keylen = set->keylen; + newset->datatype = set->datatype; + newset->datalen = set->datalen; + newset->init =expr_clone (set->init); + +return newset; +} +void set_add_hash (struct set *set,struct table *table) {list_add_tail (&set->list, &table->sets);@@ -93,6 +109,22 @@ struct set *set_lookup(const struct table *table, const char *name)return NULL ; } +struct set *set_lookup_global (uint32_t family,const char *table, +const char *name) +{ +struct handle h; +struct table *t; + + h.family = family; + h.table = table; + + t =table_lookup (&h); +if (t ==NULL ) +return NULL ; + +return set_lookup (t, name); +} +struct print_fmt_options {const char *tab;const char *nl;@@ -808,6 +840,61 @@ 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 set *s, *ns; +struct netlink_ctx set_ctx; +LIST_HEAD (msgs); +struct handle set_handle; +struct netlink_mon_handler monhandler; + +/* cache only needed if monitoring: +* - new rules in default format +* - new elements +*/ +if (((cmd->monitor_flags & (1 <<NFT_MSG_NEWRULE )) && + (cmd->format ==NFT_OUTPUT_DEFAULT )) || + (cmd->monitor_flags & (1 <<NFT_MSG_NEWSETELEM ))) + monhandler.cache_needed = true; +else + monhandler.cache_needed = false; + +if (monhandler.cache_needed) { +memset (&set_ctx,0 ,sizeof (set_ctx)); +init_list_head (&msgs); + set_ctx.msgs = &msgs; + +if (netlink_list_tables (ctx, &cmd->handle, &cmd->location) <0 ) +return -1 ; + +list_for_each_entry_safe (t, nt, &ctx->list, list) { + set_handle.family = t->handle.family; + set_handle.table = t->handle.table; + +init_list_head (&set_ctx.list); + +if (netlink_list_sets (&set_ctx, &set_handle, + &cmd->location) <0 ) +return -1 ; + +list_for_each_entry_safe (s, ns, &set_ctx.list, list) { + s->init =set_expr_alloc (&cmd->location); +set_add_hash (s, t); + } + +table_add_hash (t); + } + } + + monhandler.monitor_flags = cmd->monitor_flags; + 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) {@@ -827,6 +914,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); }src/scanner.l +0
-0@@ -232,12 +232,16 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "table" { return TABLE; } "tables" { return TABLES; } "chain" { return CHAIN; }
src/scanner.l addrstring ({macaddr}|{ip4addr}|{ip6addr}) · line 244
Quoted 10 lines
src/scanner.l +4
-0+"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; }
I think Patrick asked to use "new" instead of "added" and "destroy" instead of "deleted" IIRC.
Quoted 12 lines
src/scanner.l +1
-0"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; } "comment" { return COMMENT; }
Pablo Neira Ayuso
maintainerQuoted 1 line · Arturo Borrero Gonzalez
The following series implements a basic nftables monitor via Netlink messages.
Nice series.
Quoted 4 lines
Most of the work in first patches is related to refactorization/generalization of code.
The final patch is the big one.
This time is OK with me, but next time you can probably split it in smaller chunks, so you add table events in one patch, then chain event in another, and so on. So 8/8 gets distributed is several patches. But I think it's also reasonable to have it in one patch for this time.
Please wait to see if Patrick and others have comments on it and then go revisit 8/8, thanks Arturo.
Quoted 10 lines · Pablo Neira Ayuso
Quoted 1 line · Arturo Borrero Gonzalez
The following series implements a basic nftables monitor via Netlink messages.
Nice series.
Quoted 4 lines
Most of the work in first patches is related to refactorization/generalization of code.
The final patch is the big one.
Quoted 7 lines
This time is OK with me, but next time you can probably split it in smaller chunks, so you add table events in one patch, then chain event in another, and so on. So 8/8 gets distributed is several patches. But I think it's also reasonable to have it in one patch for this time.
Please wait to see if Patrick and others have comments on it and then go revisit 8/8, thanks Arturo.
I'm fine with these patches. If you resend just 8/8 I can apply them.
Pablo Neira Ayuso
maintainerQuoted 21 lines · Arturo Borrero Gonzalez
The following series implements a basic nftables monitor via Netlink messages.
Most of the work in first patches is related to refactorization/generalization of code.
The final patch is the big one.
About the syntax, i'm proposing:
% nft monitor [added|deleted] [tables|chains|sets|elements|rules] [xml|json]The straight-forward way of test this new feature is to simply run:
% nft monitorOther examples:
* report new tables in XML format
% nft monitor added tables xml
* report deleted elements in standar nft syntax
% nft monitor deleted elements
* report all added/deleted rules in JSON format
% nft monitor rules json
Quoted 4 lines
Handling set/set_elems is one of the harders parts of event reporting. I've succesfully tested many cases (maps, named sets, anon-sets..), but I guess more tuning can be done in the future, with some additional use and testing by the community.
Series applied, thanks Arturo.