summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/evaluate.c1
-rw-r--r--src/mnl.c10
-rw-r--r--src/netlink.c547
-rw-r--r--src/parser.y90
-rw-r--r--src/rule.c89
-rw-r--r--src/scanner.l5
6 files changed, 736 insertions, 6 deletions
diff --git a/src/evaluate.c b/src/evaluate.c
index dc4406cd..2330bbb2 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -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);
diff --git a/src/mnl.c b/src/mnl.c
index e825fb06..d19b5213 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -805,3 +805,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 nft_mnl_recv(nf_sock, 0, 0, cb, cb_data);
+}
diff --git a/src/netlink.c b/src/netlink.c
index 10951f96..22150f20 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -21,6 +21,8 @@
#include <libnftnl/chain.h>
#include <libnftnl/expr.h>
#include <libnftnl/set.h>
+#include <libnftnl/common.h>
+#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter.h>
@@ -33,6 +35,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 +46,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 +65,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 +1085,521 @@ struct nft_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx,
return rs;
}
+
+static struct nft_table *netlink_table_alloc(const struct nlmsghdr *nlh)
+{
+ 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_chain_alloc(const struct nlmsghdr *nlh)
+{
+ 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_set_alloc(const struct nlmsghdr *nlh)
+{
+ 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_setelem_alloc(const struct nlmsghdr *nlh)
+{
+ struct nft_set *nls = nft_set_alloc();
+ if (nls == NULL)
+ memory_allocation_error();
+
+ if (nft_set_elems_nlmsg_parse(nlh, nls) < 0)
+ netlink_abi_error();
+
+ return nls;
+}
+
+static struct nft_rule *netlink_rule_alloc(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 uint32_t netlink_msg2nftnl_of(uint32_t msg)
+{
+ switch (msg) {
+ case NFT_MSG_NEWTABLE:
+ case NFT_MSG_NEWCHAIN:
+ case NFT_MSG_NEWSET:
+ case NFT_MSG_NEWSETELEM:
+ case NFT_MSG_NEWRULE:
+ return NFT_OF_EVENT_NEW;
+ case NFT_MSG_DELTABLE:
+ case NFT_MSG_DELCHAIN:
+ case NFT_MSG_DELSET:
+ case NFT_MSG_DELSETELEM:
+ case NFT_MSG_DELRULE:
+ return NFT_OF_EVENT_DEL;
+ }
+
+ return 0;
+}
+
+static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
+ struct netlink_mon_handler *monh)
+{
+ uint32_t family;
+ struct nft_table *nlt = netlink_table_alloc(nlh);
+
+ if (monh->format == NFT_OUTPUT_DEFAULT) {
+ if (type == NFT_MSG_NEWTABLE) {
+ if (nlh->nlmsg_flags & NLM_F_EXCL)
+ printf("update table ");
+ else
+ 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_fprintf(stdout, nlt, monh->format,
+ netlink_msg2nftnl_of(type));
+ fprintf(stdout, "\n");
+ }
+
+ 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 chain *c;
+ uint32_t family;
+ struct nft_chain *nlc = netlink_chain_alloc(nlh);
+
+ if (monh->format == NFT_OUTPUT_DEFAULT) {
+ if (type == NFT_MSG_NEWCHAIN) {
+ if (nlh->nlmsg_flags & NLM_F_EXCL)
+ printf("update ");
+ else
+ 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_fprintf(stdout, nlc, monh->format,
+ netlink_msg2nftnl_of(type));
+ fprintf(stdout, "\n");
+ }
+
+ 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;
+ struct nft_set *nls = netlink_set_alloc(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_fprintf(stdout, nls, monh->format,
+ netlink_msg2nftnl_of(type));
+ fprintf(stdout, "\n");
+ }
+
+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 nft_set_elems_iter *nlsei;
+ struct set *dummyset;
+ struct set *set;
+ const char *setname, *table;
+ uint32_t family;
+ struct nft_set *nls = netlink_setelem_alloc(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;
+
+ /* 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);
+
+ nlsei = nft_set_elems_iter_create(nls);
+ if (nlsei == NULL)
+ memory_allocation_error();
+
+ nlse = nft_set_elems_iter_next(nlsei);
+ while (nlse != NULL) {
+ if (netlink_delinearize_setelem(nlse, dummyset) < 0) {
+ set_free(dummyset);
+ nft_set_elems_iter_destroy(nlsei);
+ goto out;
+ }
+ nlse = nft_set_elems_iter_next(nlsei);
+ }
+ nft_set_elems_iter_destroy(nlsei);
+
+ 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_fprintf(stdout, nls, monh->format,
+ netlink_msg2nftnl_of(type));
+ fprintf(stdout, "\n");
+ }
+
+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 rule *r;
+ uint32_t fam;
+ const char *family;
+ const char *table;
+ const char *chain;
+ uint64_t handle;
+ struct nft_rule *nlr = netlink_rule_alloc(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_fprintf(stdout, nlr, monh->format,
+ netlink_msg2nftnl_of(type));
+ fprintf(stdout, "\n");
+ }
+
+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_table_alloc(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_table_alloc(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_set_alloc(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;
+ struct nft_set_elems_iter *nlsei;
+ const char *table, *setname;
+ struct nft_set *nls = netlink_setelem_alloc(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;
+ }
+
+ nlsei = nft_set_elems_iter_create(nls);
+ if (nlsei == NULL)
+ memory_allocation_error();
+
+ nlse = nft_set_elems_iter_next(nlsei);
+ while (nlse != NULL) {
+ if (netlink_delinearize_setelem(nlse, set) < 0) {
+ fprintf(stderr,
+ "W: Unable to cache set_elem. "
+ "Delinearize failed.\n");
+ nft_set_elems_iter_destroy(nlsei);
+ goto out;
+ }
+ nlse = nft_set_elems_iter_next(nlsei);
+ }
+ nft_set_elems_iter_destroy(nlsei);
+
+out:
+ 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_rule_alloc(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);
+}
diff --git a/src/parser.y b/src/parser.y
index 010cf9c3..9c20737b 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 NEW "new"
+%token DESTROY "destroy"
%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"
@@ -364,8 +369,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
@@ -493,7 +498,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
%%
@@ -593,6 +598,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);
@@ -760,6 +766,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);
+ }
+ | NEW
+ {
+ $$ |= (1 << NFT_MSG_NEWRULE);
+ $$ |= (1 << NFT_MSG_NEWSET);
+ $$ |= (1 << NFT_MSG_NEWSETELEM);
+ $$ |= (1 << NFT_MSG_NEWCHAIN);
+ $$ |= (1 << NFT_MSG_NEWTABLE);
+ }
+ | DESTROY
+ {
+ $$ |= (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);
+ }
+ | NEW TABLES { $$ |= (1 << NFT_MSG_NEWTABLE); }
+ | DESTROY TABLES { $$ |= (1 << NFT_MSG_DELTABLE); }
+ | CHAIN
+ {
+ $$ |= (1 << NFT_MSG_NEWCHAIN); $$ |= (1 << NFT_MSG_DELCHAIN);
+ }
+ | NEW CHAINS { $$ |= (1 << NFT_MSG_NEWCHAIN); }
+ | DESTROY CHAINS { $$ |= (1 << NFT_MSG_DELCHAIN); }
+ | SETS
+ {
+ $$ |= (1 << NFT_MSG_NEWSET); $$ |= (1 << NFT_MSG_DELSET);
+ }
+ | NEW SETS { $$ |= (1 << NFT_MSG_NEWSET); }
+ | DESTROY SETS { $$ |= (1 << NFT_MSG_DELSET); }
+ | RULE
+ {
+ $$ |= (1 << NFT_MSG_NEWRULE); $$ |= (1 << NFT_MSG_DELRULE);
+ }
+ | NEW RULES { $$ |= (1 << NFT_MSG_NEWRULE); }
+ | DESTROY RULES { $$ |= (1 << NFT_MSG_DELRULE); }
+ | ELEMENTS
+ {
+ $$ |= (1 << NFT_MSG_NEWSETELEM);
+ $$ |= (1 << NFT_MSG_DELSETELEM);
+ }
+ | NEW ELEMENTS { $$ |= (1 << NFT_MSG_NEWSETELEM); }
+ | DESTROY ELEMENTS { $$ |= (1 << NFT_MSG_DELSETELEM); }
+ ;
+
+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 858149ed..43a3e117 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -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);
}
diff --git a/src/scanner.l b/src/scanner.l
index 69d6b8f8..801c0303 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; }
+"new" { return NEW; }
+"destroy" { return DESTROY; }
"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; }