diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/monitor.c | 948 | ||||
-rw-r--r-- | src/netlink.c | 931 |
3 files changed, 961 insertions, 919 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 7fa72a8e..677ca396 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,6 +44,7 @@ libnftables_la_SOURCES = \ netlink.c \ netlink_linearize.c \ netlink_delinearize.c \ + monitor.c \ segtree.c \ rbtree.c \ gmputil.c \ diff --git a/src/monitor.c b/src/monitor.c new file mode 100644 index 00000000..f7fa6311 --- /dev/null +++ b/src/monitor.c @@ -0,0 +1,948 @@ +/* + * Copyright (c) 2015 Arturo Borrero Gonzalez <arturo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <libmnl/libmnl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <inttypes.h> + +#include <libnftnl/table.h> +#include <libnftnl/trace.h> +#include <libnftnl/chain.h> +#include <libnftnl/expr.h> +#include <libnftnl/object.h> +#include <libnftnl/set.h> +#include <libnftnl/flowtable.h> +#include <libnftnl/udata.h> +#include <libnftnl/ruleset.h> +#include <libnftnl/common.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter.h> + +#include <nftables.h> +#include <netlink.h> +#include <mnl.h> +#include <expression.h> +#include <statement.h> +#include <gmputil.h> +#include <utils.h> +#include <erec.h> +#include <iface.h> + +#define nft_mon_print(monh, ...) nft_print(monh->ctx->octx, __VA_ARGS__) + +static struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_table *nlt; + + nlt = nftnl_table_alloc(); + if (nlt == NULL) + memory_allocation_error(); + if (nftnl_table_nlmsg_parse(nlh, nlt) < 0) + netlink_abi_error(); + + return nlt; +} + +static struct nftnl_chain *netlink_chain_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_chain *nlc; + + nlc = nftnl_chain_alloc(); + if (nlc == NULL) + memory_allocation_error(); + if (nftnl_chain_nlmsg_parse(nlh, nlc) < 0) + netlink_abi_error(); + + return nlc; +} + +static struct nftnl_set *netlink_set_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_set *nls; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + if (nftnl_set_nlmsg_parse(nlh, nls) < 0) + netlink_abi_error(); + + return nls; +} + +static struct nftnl_set *netlink_setelem_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_set *nls; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + if (nftnl_set_elems_nlmsg_parse(nlh, nls) < 0) + netlink_abi_error(); + + return nls; +} + +static struct nftnl_rule *netlink_rule_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_rule *nlr; + + nlr = nftnl_rule_alloc(); + if (nlr == NULL) + memory_allocation_error(); + if (nftnl_rule_nlmsg_parse(nlh, nlr) < 0) + netlink_abi_error(); + + return nlr; +} + +static struct nftnl_obj *netlink_obj_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_obj *nlo; + + nlo = nftnl_obj_alloc(); + if (nlo == NULL) + memory_allocation_error(); + if (nftnl_obj_nlmsg_parse(nlh, nlo) < 0) + netlink_abi_error(); + + return nlo; +} + +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 NFTNL_OF_EVENT_NEW; + case NFT_MSG_DELTABLE: + case NFT_MSG_DELCHAIN: + case NFT_MSG_DELSET: + case NFT_MSG_DELSETELEM: + case NFT_MSG_DELRULE: + return NFTNL_OF_EVENT_DEL; + } + + return 0; +} + +static void nlr_for_each_set(struct nftnl_rule *nlr, + void (*cb)(struct set *s, void *data), + void *data, struct nft_cache *cache) +{ + struct nftnl_expr_iter *nlrei; + struct nftnl_expr *nlre; + const char *set_name, *table; + const char *name; + struct set *s; + uint32_t family; + + nlrei = nftnl_expr_iter_create(nlr); + if (nlrei == NULL) + memory_allocation_error(); + + family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); + table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); + + nlre = nftnl_expr_iter_next(nlrei); + while (nlre != NULL) { + name = nftnl_expr_get_str(nlre, NFTNL_EXPR_NAME); + if (strcmp(name, "lookup") != 0) + goto next; + + set_name = nftnl_expr_get_str(nlre, NFTNL_EXPR_LOOKUP_SET); + s = set_lookup_global(family, table, set_name, cache); + if (s == NULL) + goto next; + + cb(s, data); +next: + nlre = nftnl_expr_iter_next(nlrei); + } + nftnl_expr_iter_destroy(nlrei); +} + +static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_table *nlt; + uint32_t family; + + nlt = netlink_table_alloc(nlh); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + if (type == NFT_MSG_NEWTABLE) { + if (nlh->nlmsg_flags & NLM_F_EXCL) + nft_mon_print(monh, "update table "); + else + nft_mon_print(monh, "add table "); + } else { + nft_mon_print(monh, "delete table "); + } + + family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY); + + nft_mon_print(monh, "%s %s", family2str(family), + nftnl_table_get_str(nlt, NFTNL_TABLE_NAME)); + if (monh->ctx->octx->handle > 0) + nft_mon_print(monh, " # handle %" PRIu64 "", + nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE)); + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_table_fprintf(monh->ctx->octx->output_fp, nlt, + monh->format, netlink_msg2nftnl_of(type)); + nft_mon_print(monh, "\n"); + break; + } + + nftnl_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 nftnl_chain *nlc; + struct chain *c; + uint32_t family; + + nlc = netlink_chain_alloc(nlh); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + switch (type) { + case NFT_MSG_NEWCHAIN: + if (nlh->nlmsg_flags & NLM_F_EXCL) + nft_mon_print(monh, "update "); + else + nft_mon_print(monh, "add "); + + c = netlink_delinearize_chain(monh->ctx, nlc); + chain_print_plain(c, monh->ctx->octx); + chain_free(c); + break; + case NFT_MSG_DELCHAIN: + family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY); + nft_mon_print(monh, "delete chain %s %s %s\n", family2str(family), + nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE), + nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME)); + break; + } + break; + case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_chain_fprintf(monh->ctx->octx->output_fp, nlc, + monh->format, netlink_msg2nftnl_of(type)); + nft_mon_print(monh, "\n"); + break; + } + + nftnl_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 nftnl_set *nls; + struct set *set; + uint32_t family, flags; + + nls = netlink_set_alloc(nlh); + flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); + if (flags & NFT_SET_ANONYMOUS) + goto out; + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + switch (type) { + case NFT_MSG_NEWSET: + nft_mon_print(monh, "add "); + set = netlink_delinearize_set(monh->ctx, nls); + if (set == NULL) { + nftnl_set_free(nls); + return MNL_CB_ERROR; + } + set_print_plain(set, monh->ctx->octx); + set_free(set); + nft_mon_print(monh, "\n"); + break; + case NFT_MSG_DELSET: + family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + nft_mon_print(monh, "delete set %s %s %s\n", + family2str(family), + nftnl_set_get_str(nls, NFTNL_SET_TABLE), + nftnl_set_get_str(nls, NFTNL_SET_NAME)); + break; + } + break; + case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_set_fprintf(monh->ctx->octx->output_fp, nls, + monh->format, netlink_msg2nftnl_of(type)); + nft_mon_print(monh, "\n"); + break; + } +out: + nftnl_set_free(nls); + return MNL_CB_OK; +} + +/* returns true if the event should be ignored (i.e. null element) */ +static bool netlink_event_ignore_range_event(struct nftnl_set_elem *nlse) +{ + uint32_t flags = 0; + + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_FLAGS)) + flags = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_FLAGS); + if (!(flags & NFT_SET_ELEM_INTERVAL_END)) + return false; + + if (nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_KEY) != 0) + return false; + + return true; +} + +static bool set_elem_is_open_interval(struct expr *elem) +{ + switch (elem->ops->type) { + case EXPR_SET_ELEM: + return elem->elem_flags & SET_ELEM_F_INTERVAL_OPEN; + case EXPR_MAPPING: + return set_elem_is_open_interval(elem->left); + default: + return false; + } +} + +/* returns true if the we cached the range element */ +static bool netlink_event_range_cache(struct set *cached_set, + struct set *dummyset) +{ + struct expr *elem; + + /* not an interval ? */ + if (!(cached_set->flags & NFT_SET_INTERVAL)) + return false; + + /* if cache exists, dummyset must contain the other end of the range */ + if (cached_set->rg_cache) { + compound_expr_add(dummyset->init, cached_set->rg_cache); + cached_set->rg_cache = NULL; + goto out_decompose; + } + + /* don't cache half-open range elements */ + elem = list_entry(dummyset->init->expressions.prev, struct expr, list); + if (!set_elem_is_open_interval(elem)) { + cached_set->rg_cache = expr_clone(elem); + return true; + } + +out_decompose: + interval_map_decompose(dummyset->init); + return false; +} + +static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_set_elems_iter *nlsei; + struct nftnl_set_elem *nlse; + struct nftnl_set *nls; + struct set *dummyset; + struct set *set; + const char *setname, *table; + uint32_t family; + + nls = netlink_setelem_alloc(nlh); + table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + setname = nftnl_set_get_str(nls, NFTNL_SET_NAME); + family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + + set = set_lookup_global(family, table, setname, monh->cache); + if (set == NULL) { + fprintf(stderr, "W: Received event for an unknown set."); + goto out; + } + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + if (set->flags & NFT_SET_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->key = expr_clone(set->key); + dummyset->datatype = set->datatype; + dummyset->flags = set->flags; + dummyset->init = set_expr_alloc(monh->loc, set); + + nlsei = nftnl_set_elems_iter_create(nls); + if (nlsei == NULL) + memory_allocation_error(); + + nlse = nftnl_set_elems_iter_next(nlsei); + while (nlse != NULL) { + if (netlink_event_ignore_range_event(nlse)) { + set_free(dummyset); + nftnl_set_elems_iter_destroy(nlsei); + goto out; + } + if (netlink_delinearize_setelem(nlse, dummyset, + monh->cache) < 0) { + set_free(dummyset); + nftnl_set_elems_iter_destroy(nlsei); + goto out; + } + nlse = nftnl_set_elems_iter_next(nlsei); + } + nftnl_set_elems_iter_destroy(nlsei); + + if (netlink_event_range_cache(set, dummyset)) { + set_free(dummyset); + goto out; + } + + switch (type) { + case NFT_MSG_NEWSETELEM: + nft_mon_print(monh, "add "); + break; + case NFT_MSG_DELSETELEM: + nft_mon_print(monh, "delete "); + break; + default: + set_free(dummyset); + goto out; + } + nft_mon_print(monh, "element %s %s %s ", family2str(family), table, setname); + expr_print(dummyset->init, monh->ctx->octx); + nft_mon_print(monh, "\n"); + + set_free(dummyset); + break; + case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_set_fprintf(monh->ctx->octx->output_fp, nls, + monh->format, netlink_msg2nftnl_of(type)); + nft_mon_print(monh, "\n"); + break; + } +out: + nftnl_set_free(nls); + return MNL_CB_OK; +} + +static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_obj *nlo; + uint32_t family; + struct obj *obj; + + nlo = netlink_obj_alloc(nlh); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + switch (type) { + case NFT_MSG_NEWOBJ: + nft_mon_print(monh, "add "); + obj = netlink_delinearize_obj(monh->ctx, nlo); + if (obj == NULL) { + nftnl_obj_free(nlo); + return MNL_CB_ERROR; + } + obj_print_plain(obj, monh->ctx->octx); + obj_free(obj); + nft_mon_print(monh, "\n"); + break; + case NFT_MSG_DELOBJ: + family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY); + nft_mon_print(monh, "delete %s %s %s %s\n", + obj_type_name(nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE)), + family2str(family), + nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE), + nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME)); + break; + } + break; + case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_obj_fprintf(monh->ctx->octx->output_fp, nlo, + monh->format, netlink_msg2nftnl_of(type)); + nft_mon_print(monh, "\n"); + break; + } + + nftnl_obj_free(nlo); + return MNL_CB_OK; +} + +static void rule_map_decompose_cb(struct set *s, void *data) +{ + if (s->flags & NFT_SET_INTERVAL) + interval_map_decompose(s->init); +} + +static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_rule *nlr; + const char *family; + const char *table; + const char *chain; + struct rule *r; + uint64_t handle; + uint32_t fam; + + nlr = netlink_rule_alloc(nlh); + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + fam = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); + family = family2str(fam); + table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); + chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN); + handle = nftnl_rule_get_u64(nlr, NFTNL_RULE_HANDLE); + + switch (type) { + case NFT_MSG_NEWRULE: + r = netlink_delinearize_rule(monh->ctx, nlr); + nlr_for_each_set(nlr, rule_map_decompose_cb, NULL, + monh->cache); + + nft_mon_print(monh, "add rule %s %s %s ", family, table, chain); + rule_print(r, monh->ctx->octx); + nft_mon_print(monh, "\n"); + + rule_free(r); + break; + case NFT_MSG_DELRULE: + nft_mon_print(monh, "delete rule %s %s %s handle %u\n", + family, table, chain, (unsigned int)handle); + break; + } + break; + case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_rule_fprintf(monh->ctx->octx->output_fp, nlr, + monh->format, netlink_msg2nftnl_of(type)); + nft_mon_print(monh, "\n"); + break; + } + + nftnl_rule_free(nlr); + return MNL_CB_OK; +} + +static void netlink_events_cache_addtable(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_table *nlt; + struct table *t; + + nlt = netlink_table_alloc(nlh); + t = netlink_delinearize_table(monh->ctx, nlt); + nftnl_table_free(nlt); + + table_add_hash(t, monh->cache); +} + +static void netlink_events_cache_deltable(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_table *nlt; + struct table *t; + struct handle h; + + nlt = netlink_table_alloc(nlh); + h.family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY); + h.table = nftnl_table_get_str(nlt, NFTNL_TABLE_NAME); + + t = table_lookup(&h, monh->cache); + if (t == NULL) + goto out; + + list_del(&t->list); + table_free(t); +out: + nftnl_table_free(nlt); +} + +static void netlink_events_cache_addset(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct netlink_ctx set_tmpctx; + struct nftnl_set *nls; + struct table *t; + struct set *s; + LIST_HEAD(msgs); + + memset(&set_tmpctx, 0, sizeof(set_tmpctx)); + init_list_head(&set_tmpctx.list); + init_list_head(&msgs); + set_tmpctx.msgs = &msgs; + + nls = netlink_set_alloc(nlh); + s = netlink_delinearize_set(&set_tmpctx, nls); + if (s == NULL) + goto out; + s->init = set_expr_alloc(monh->loc, s); + + t = table_lookup(&s->handle, monh->cache); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache set: table not found.\n"); + set_free(s); + goto out; + } + + set_add_hash(s, t); +out: + nftnl_set_free(nls); +} + +static void netlink_events_cache_addsetelem(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_set_elems_iter *nlsei; + struct nftnl_set_elem *nlse; + struct nftnl_set *nls; + struct set *set; + const char *table, *setname; + uint32_t family; + + nls = netlink_setelem_alloc(nlh); + family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + setname = nftnl_set_get_str(nls, NFTNL_SET_NAME); + + set = set_lookup_global(family, table, setname, monh->cache); + if (set == NULL) { + fprintf(stderr, + "W: Unable to cache set_elem. Set not found.\n"); + goto out; + } + + nlsei = nftnl_set_elems_iter_create(nls); + if (nlsei == NULL) + memory_allocation_error(); + + nlse = nftnl_set_elems_iter_next(nlsei); + while (nlse != NULL) { + if (netlink_delinearize_setelem(nlse, set, monh->cache) < 0) { + fprintf(stderr, + "W: Unable to cache set_elem. " + "Delinearize failed.\n"); + nftnl_set_elems_iter_destroy(nlsei); + goto out; + } + nlse = nftnl_set_elems_iter_next(nlsei); + } + nftnl_set_elems_iter_destroy(nlsei); +out: + nftnl_set_free(nls); +} + +static void netlink_events_cache_delset_cb(struct set *s, + void *data) +{ + list_del(&s->list); + set_free(s); +} + +static void netlink_events_cache_delsets(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_rule *nlr = netlink_rule_alloc(nlh); + + nlr_for_each_set(nlr, netlink_events_cache_delset_cb, NULL, + monh->cache); + nftnl_rule_free(nlr); +} + +static void netlink_events_cache_addobj(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct netlink_ctx obj_tmpctx; + struct nftnl_obj *nlo; + struct table *t; + struct obj *obj; + LIST_HEAD(msgs); + + memset(&obj_tmpctx, 0, sizeof(obj_tmpctx)); + init_list_head(&obj_tmpctx.list); + init_list_head(&msgs); + obj_tmpctx.msgs = &msgs; + + nlo = netlink_obj_alloc(nlh); + obj = netlink_delinearize_obj(&obj_tmpctx, nlo); + if (obj == NULL) + goto out; + + t = table_lookup(&obj->handle, monh->cache); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache object: table not found.\n"); + obj_free(obj); + goto out; + } + + obj_add_hash(obj, t); +out: + nftnl_obj_free(nlo); +} + +static void netlink_events_cache_delobj(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_obj *nlo; + const char *name; + struct obj *obj; + struct handle h; + struct table *t; + uint32_t type; + + nlo = netlink_obj_alloc(nlh); + h.family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY); + h.table = nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE); + + name = nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME); + type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE); + h.handle.id = nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE); + + t = table_lookup(&h, monh->cache); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache object: table not found.\n"); + goto out; + } + + obj = obj_lookup(t, name, type); + if (obj == NULL) { + fprintf(stderr, "W: Unable to find object in cache\n"); + goto out; + } + + list_del(&obj->list); + obj_free(obj); +out: + nftnl_obj_free(nlo); +} + +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; + case NFT_MSG_NEWOBJ: + netlink_events_cache_addobj(monh, nlh); + break; + case NFT_MSG_DELOBJ: + netlink_events_cache_delobj(monh, nlh); + break; + } +} + +/* only those which could be useful listening to events */ +static const char *const nftnl_msg_types[NFT_MSG_MAX] = { + [NFT_MSG_NEWTABLE] = "NFT_MSG_NEWTABLE", + [NFT_MSG_DELTABLE] = "NFT_MSG_DELTABLE", + [NFT_MSG_NEWCHAIN] = "NFT_MSG_NEWCHAIN", + [NFT_MSG_DELCHAIN] = "NFT_MSG_DELCHAIN", + [NFT_MSG_NEWSET] = "NFT_MSG_NEWSET", + [NFT_MSG_DELSET] = "NFT_MSG_DELSET", + [NFT_MSG_NEWSETELEM] = "NFT_MSG_NEWSETELEM", + [NFT_MSG_DELSETELEM] = "NFT_MSG_DELSETELEM", + [NFT_MSG_NEWRULE] = "NFT_MSG_NEWRULE", + [NFT_MSG_DELRULE] = "NFT_MSG_DELRULE", + [NFT_MSG_TRACE] = "NFT_MSG_TRACE", + [NFT_MSG_NEWGEN] = "NFT_MSG_NEWGEN", + [NFT_MSG_NEWOBJ] = "NFT_MSG_NEWOBJ", + [NFT_MSG_DELOBJ] = "NFT_MSG_DELOBJ", +}; + +static const char *nftnl_msgtype2str(uint16_t type) +{ + if (type >= NFT_MSG_MAX || !nftnl_msg_types[type]) + return "unknown"; + + return nftnl_msg_types[type]; +} + +static void netlink_events_debug(uint16_t type, unsigned int debug_mask) +{ + if (!(debug_mask & NFT_DEBUG_NETLINK)) + return; + + printf("netlink event: %s\n", nftnl_msgtype2str(type)); +} + +static int netlink_events_newgen_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + const struct nlattr *attr; + char name[256] = ""; + int genid = -1, pid = -1; + + mnl_attr_for_each(attr, nlh, sizeof(struct nfgenmsg)) { + switch (mnl_attr_get_type(attr)) { + case NFTA_GEN_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + break; + genid = ntohl(mnl_attr_get_u32(attr)); + break; + case NFTA_GEN_PROC_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + break; + snprintf(name, sizeof(name), "%s", mnl_attr_get_str(attr)); + break; + case NFTA_GEN_PROC_PID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + break; + pid = ntohl(mnl_attr_get_u32(attr)); + break; + } + } + if (genid >= 0) { + nft_mon_print(monh, "# new generation %d", genid); + if (pid >= 0) { + nft_mon_print(monh, " by process %d", pid); + if (!monh->ctx->octx->numeric) + nft_mon_print(monh, " (%s)", name); + } + nft_mon_print(monh, "\n"); + } + + return MNL_CB_OK; +} + +static int netlink_events_cb(const struct nlmsghdr *nlh, void *data) +{ + int ret = MNL_CB_OK; + uint16_t type = NFNL_MSG_TYPE(nlh->nlmsg_type); + struct netlink_mon_handler *monh = (struct netlink_mon_handler *)data; + + netlink_events_debug(type, monh->debug_mask); + 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; + case NFT_MSG_TRACE: + ret = netlink_events_trace_cb(nlh, type, monh); + break; + case NFT_MSG_NEWOBJ: + case NFT_MSG_DELOBJ: + ret = netlink_events_obj_cb(nlh, type, monh); + break; + case NFT_MSG_NEWGEN: + ret = netlink_events_newgen_cb(nlh, type, monh); + break; + } + + return ret; +} + +int netlink_echo_callback(const struct nlmsghdr *nlh, void *data) +{ + struct netlink_ctx *ctx = data; + struct netlink_mon_handler echo_monh = { + .format = NFTNL_OUTPUT_DEFAULT, + .ctx = ctx, + .loc = &netlink_location, + .monitor_flags = 0xffffffff, + .cache_needed = true, + .cache = ctx->cache, + .debug_mask = ctx->debug_mask, + }; + + if (!echo_monh.ctx->octx->echo) + return MNL_CB_OK; + + return netlink_events_cb(nlh, &echo_monh); +} + +int netlink_monitor(struct netlink_mon_handler *monhandler, + struct mnl_socket *nf_sock) +{ + int group; + + if (monhandler->monitor_flags & (1 << NFT_MSG_TRACE)) { + group = NFNLGRP_NFTRACE; + if (mnl_socket_setsockopt(nf_sock, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(int)) < 0) + return netlink_io_error(monhandler->ctx, + monhandler->loc, + "Could not bind to netlink socket %s", + strerror(errno)); + } + if (monhandler->monitor_flags & ~(1 << NFT_MSG_TRACE)) { + group = NFNLGRP_NFTABLES; + if (mnl_socket_setsockopt(nf_sock, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(int)) < 0) + return netlink_io_error(monhandler->ctx, + monhandler->loc, + "Could not bind to netlink socket %s", + strerror(errno)); + } + + return mnl_nft_event_listener(nf_sock, monhandler->debug_mask, + monhandler->ctx->octx, netlink_events_cb, + monhandler); +} diff --git a/src/netlink.c b/src/netlink.c index 526ec9c1..46603976 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -669,8 +669,8 @@ int netlink_delete_chain_batch(struct netlink_ctx *ctx, const struct handle *h, return err; } -static struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx, - const struct nftnl_chain *nlc) +struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx, + const struct nftnl_chain *nlc) { struct chain *chain; @@ -808,8 +808,8 @@ int netlink_delete_table_batch(struct netlink_ctx *ctx, const struct handle *h, return err; } -static struct table *netlink_delinearize_table(struct netlink_ctx *ctx, - const struct nftnl_table *nlt) +struct table *netlink_delinearize_table(struct netlink_ctx *ctx, + const struct nftnl_table *nlt) { struct table *table; @@ -917,8 +917,8 @@ static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data) return 0; } -static struct set *netlink_delinearize_set(struct netlink_ctx *ctx, - const struct nftnl_set *nls) +struct set *netlink_delinearize_set(struct netlink_ctx *ctx, + const struct nftnl_set *nls) { const struct nftnl_udata *ud[UDATA_SET_MAX + 1] = {}; uint32_t flags, key, data, data_len, objtype = 0; @@ -1274,9 +1274,8 @@ static void set_elem_parse_udata(struct nftnl_set_elem *nlse, expr->elem_flags = nftnl_udata_get_u32(ud[UDATA_SET_ELEM_FLAGS]); } -static int netlink_delinearize_setelem(struct nftnl_set_elem *nlse, - const struct set *set, - struct nft_cache *cache) +int netlink_delinearize_setelem(struct nftnl_set_elem *nlse, + const struct set *set, struct nft_cache *cache) { struct nft_data_delinearize nld; struct expr *expr, *key, *data; @@ -1490,8 +1489,8 @@ int netlink_delete_obj(struct netlink_ctx *ctx, const struct handle *h, return err; } -static struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, - struct nftnl_obj *nlo) +struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, + struct nftnl_obj *nlo) { struct obj *obj; uint32_t type; @@ -1781,743 +1780,6 @@ struct nftnl_ruleset *netlink_dump_ruleset(struct netlink_ctx *ctx, return rs; } -static struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh) -{ - struct nftnl_table *nlt; - - nlt = nftnl_table_alloc(); - if (nlt == NULL) - memory_allocation_error(); - if (nftnl_table_nlmsg_parse(nlh, nlt) < 0) - netlink_abi_error(); - - return nlt; -} - -static struct nftnl_chain *netlink_chain_alloc(const struct nlmsghdr *nlh) -{ - struct nftnl_chain *nlc; - - nlc = nftnl_chain_alloc(); - if (nlc == NULL) - memory_allocation_error(); - if (nftnl_chain_nlmsg_parse(nlh, nlc) < 0) - netlink_abi_error(); - - return nlc; -} - -static struct nftnl_set *netlink_set_alloc(const struct nlmsghdr *nlh) -{ - struct nftnl_set *nls; - - nls = nftnl_set_alloc(); - if (nls == NULL) - memory_allocation_error(); - if (nftnl_set_nlmsg_parse(nlh, nls) < 0) - netlink_abi_error(); - - return nls; -} - -static struct nftnl_set *netlink_setelem_alloc(const struct nlmsghdr *nlh) -{ - struct nftnl_set *nls; - - nls = nftnl_set_alloc(); - if (nls == NULL) - memory_allocation_error(); - if (nftnl_set_elems_nlmsg_parse(nlh, nls) < 0) - netlink_abi_error(); - - return nls; -} - -static struct nftnl_rule *netlink_rule_alloc(const struct nlmsghdr *nlh) -{ - struct nftnl_rule *nlr; - - nlr = nftnl_rule_alloc(); - if (nlr == NULL) - memory_allocation_error(); - if (nftnl_rule_nlmsg_parse(nlh, nlr) < 0) - netlink_abi_error(); - - return nlr; -} - -static struct nftnl_obj *netlink_obj_alloc(const struct nlmsghdr *nlh) -{ - struct nftnl_obj *nlo; - - nlo = nftnl_obj_alloc(); - if (nlo == NULL) - memory_allocation_error(); - if (nftnl_obj_nlmsg_parse(nlh, nlo) < 0) - netlink_abi_error(); - - return nlo; -} - -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 NFTNL_OF_EVENT_NEW; - case NFT_MSG_DELTABLE: - case NFT_MSG_DELCHAIN: - case NFT_MSG_DELSET: - case NFT_MSG_DELSETELEM: - case NFT_MSG_DELRULE: - return NFTNL_OF_EVENT_DEL; - } - - return 0; -} - -static void nlr_for_each_set(struct nftnl_rule *nlr, - void (*cb)(struct set *s, void *data), - void *data, struct nft_cache *cache) -{ - struct nftnl_expr_iter *nlrei; - struct nftnl_expr *nlre; - const char *set_name, *table; - const char *name; - struct set *s; - uint32_t family; - - nlrei = nftnl_expr_iter_create(nlr); - if (nlrei == NULL) - memory_allocation_error(); - - family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); - table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); - - nlre = nftnl_expr_iter_next(nlrei); - while (nlre != NULL) { - name = nftnl_expr_get_str(nlre, NFTNL_EXPR_NAME); - if (strcmp(name, "lookup") != 0) - goto next; - - set_name = nftnl_expr_get_str(nlre, NFTNL_EXPR_LOOKUP_SET); - s = set_lookup_global(family, table, set_name, cache); - if (s == NULL) - goto next; - - cb(s, data); -next: - nlre = nftnl_expr_iter_next(nlrei); - } - nftnl_expr_iter_destroy(nlrei); -} - -static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type, - struct netlink_mon_handler *monh) -{ - struct nftnl_table *nlt; - uint32_t family; - - nlt = netlink_table_alloc(nlh); - - switch (monh->format) { - case NFTNL_OUTPUT_DEFAULT: - if (type == NFT_MSG_NEWTABLE) { - if (nlh->nlmsg_flags & NLM_F_EXCL) - nft_mon_print(monh, "update table "); - else - nft_mon_print(monh, "add table "); - } else { - nft_mon_print(monh, "delete table "); - } - - family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY); - - nft_mon_print(monh, "%s %s", family2str(family), - nftnl_table_get_str(nlt, NFTNL_TABLE_NAME)); - if (monh->ctx->octx->handle > 0) - nft_mon_print(monh, " # handle %" PRIu64 "", - nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE)); - nft_mon_print(monh, "\n"); - break; - case NFTNL_OUTPUT_XML: - case NFTNL_OUTPUT_JSON: - nftnl_table_fprintf(monh->ctx->octx->output_fp, nlt, - monh->format, netlink_msg2nftnl_of(type)); - nft_mon_print(monh, "\n"); - break; - } - - nftnl_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 nftnl_chain *nlc; - struct chain *c; - uint32_t family; - - nlc = netlink_chain_alloc(nlh); - - switch (monh->format) { - case NFTNL_OUTPUT_DEFAULT: - switch (type) { - case NFT_MSG_NEWCHAIN: - if (nlh->nlmsg_flags & NLM_F_EXCL) - nft_mon_print(monh, "update "); - else - nft_mon_print(monh, "add "); - - c = netlink_delinearize_chain(monh->ctx, nlc); - chain_print_plain(c, monh->ctx->octx); - chain_free(c); - break; - case NFT_MSG_DELCHAIN: - family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY); - nft_mon_print(monh, "delete chain %s %s %s\n", family2str(family), - nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE), - nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME)); - break; - } - break; - case NFTNL_OUTPUT_XML: - case NFTNL_OUTPUT_JSON: - nftnl_chain_fprintf(monh->ctx->octx->output_fp, nlc, - monh->format, netlink_msg2nftnl_of(type)); - nft_mon_print(monh, "\n"); - break; - } - - nftnl_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 nftnl_set *nls; - struct set *set; - uint32_t family, flags; - - nls = netlink_set_alloc(nlh); - flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); - if (flags & NFT_SET_ANONYMOUS) - goto out; - - switch (monh->format) { - case NFTNL_OUTPUT_DEFAULT: - switch (type) { - case NFT_MSG_NEWSET: - nft_mon_print(monh, "add "); - set = netlink_delinearize_set(monh->ctx, nls); - if (set == NULL) { - nftnl_set_free(nls); - return MNL_CB_ERROR; - } - set_print_plain(set, monh->ctx->octx); - set_free(set); - nft_mon_print(monh, "\n"); - break; - case NFT_MSG_DELSET: - family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); - nft_mon_print(monh, "delete set %s %s %s\n", - family2str(family), - nftnl_set_get_str(nls, NFTNL_SET_TABLE), - nftnl_set_get_str(nls, NFTNL_SET_NAME)); - break; - } - break; - case NFTNL_OUTPUT_XML: - case NFTNL_OUTPUT_JSON: - nftnl_set_fprintf(monh->ctx->octx->output_fp, nls, - monh->format, netlink_msg2nftnl_of(type)); - nft_mon_print(monh, "\n"); - break; - } -out: - nftnl_set_free(nls); - return MNL_CB_OK; -} - -/* returns true if the event should be ignored (i.e. null element) */ -static bool netlink_event_ignore_range_event(struct nftnl_set_elem *nlse) -{ - uint32_t flags = 0; - - if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_FLAGS)) - flags = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_FLAGS); - if (!(flags & NFT_SET_ELEM_INTERVAL_END)) - return false; - - if (nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_KEY) != 0) - return false; - - return true; -} - -static bool set_elem_is_open_interval(struct expr *elem) -{ - switch (elem->ops->type) { - case EXPR_SET_ELEM: - return elem->elem_flags & SET_ELEM_F_INTERVAL_OPEN; - case EXPR_MAPPING: - return set_elem_is_open_interval(elem->left); - default: - return false; - } -} - -/* returns true if the we cached the range element */ -static bool netlink_event_range_cache(struct set *cached_set, - struct set *dummyset) -{ - struct expr *elem; - - /* not an interval ? */ - if (!(cached_set->flags & NFT_SET_INTERVAL)) - return false; - - /* if cache exists, dummyset must contain the other end of the range */ - if (cached_set->rg_cache) { - compound_expr_add(dummyset->init, cached_set->rg_cache); - cached_set->rg_cache = NULL; - goto out_decompose; - } - - /* don't cache half-open range elements */ - elem = list_entry(dummyset->init->expressions.prev, struct expr, list); - if (!set_elem_is_open_interval(elem)) { - cached_set->rg_cache = expr_clone(elem); - return true; - } - -out_decompose: - interval_map_decompose(dummyset->init); - return false; -} - -static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, - struct netlink_mon_handler *monh) -{ - struct nftnl_set_elems_iter *nlsei; - struct nftnl_set_elem *nlse; - struct nftnl_set *nls; - struct set *dummyset; - struct set *set; - const char *setname, *table; - uint32_t family; - - nls = netlink_setelem_alloc(nlh); - table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); - setname = nftnl_set_get_str(nls, NFTNL_SET_NAME); - family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); - - set = set_lookup_global(family, table, setname, monh->cache); - if (set == NULL) { - fprintf(stderr, "W: Received event for an unknown set."); - goto out; - } - - switch (monh->format) { - case NFTNL_OUTPUT_DEFAULT: - if (set->flags & NFT_SET_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->key = expr_clone(set->key); - dummyset->datatype = set->datatype; - dummyset->flags = set->flags; - dummyset->init = set_expr_alloc(monh->loc, set); - - nlsei = nftnl_set_elems_iter_create(nls); - if (nlsei == NULL) - memory_allocation_error(); - - nlse = nftnl_set_elems_iter_next(nlsei); - while (nlse != NULL) { - if (netlink_event_ignore_range_event(nlse)) { - set_free(dummyset); - nftnl_set_elems_iter_destroy(nlsei); - goto out; - } - if (netlink_delinearize_setelem(nlse, dummyset, - monh->cache) < 0) { - set_free(dummyset); - nftnl_set_elems_iter_destroy(nlsei); - goto out; - } - nlse = nftnl_set_elems_iter_next(nlsei); - } - nftnl_set_elems_iter_destroy(nlsei); - - if (netlink_event_range_cache(set, dummyset)) { - set_free(dummyset); - goto out; - } - - switch (type) { - case NFT_MSG_NEWSETELEM: - nft_mon_print(monh, "add "); - break; - case NFT_MSG_DELSETELEM: - nft_mon_print(monh, "delete "); - break; - default: - set_free(dummyset); - goto out; - } - nft_mon_print(monh, "element %s %s %s ", family2str(family), table, setname); - expr_print(dummyset->init, monh->ctx->octx); - nft_mon_print(monh, "\n"); - - set_free(dummyset); - break; - case NFTNL_OUTPUT_XML: - case NFTNL_OUTPUT_JSON: - nftnl_set_fprintf(monh->ctx->octx->output_fp, nls, - monh->format, netlink_msg2nftnl_of(type)); - nft_mon_print(monh, "\n"); - break; - } -out: - nftnl_set_free(nls); - return MNL_CB_OK; -} - -static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type, - struct netlink_mon_handler *monh) -{ - struct nftnl_obj *nlo; - uint32_t family; - struct obj *obj; - - nlo = netlink_obj_alloc(nlh); - - switch (monh->format) { - case NFTNL_OUTPUT_DEFAULT: - switch (type) { - case NFT_MSG_NEWOBJ: - nft_mon_print(monh, "add "); - obj = netlink_delinearize_obj(monh->ctx, nlo); - if (obj == NULL) { - nftnl_obj_free(nlo); - return MNL_CB_ERROR; - } - obj_print_plain(obj, monh->ctx->octx); - obj_free(obj); - nft_mon_print(monh, "\n"); - break; - case NFT_MSG_DELOBJ: - family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY); - nft_mon_print(monh, "delete %s %s %s %s\n", - obj_type_name(nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE)), - family2str(family), - nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE), - nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME)); - break; - } - break; - case NFTNL_OUTPUT_XML: - case NFTNL_OUTPUT_JSON: - nftnl_obj_fprintf(monh->ctx->octx->output_fp, nlo, - monh->format, netlink_msg2nftnl_of(type)); - nft_mon_print(monh, "\n"); - break; - } - - nftnl_obj_free(nlo); - return MNL_CB_OK; -} - -static void rule_map_decompose_cb(struct set *s, void *data) -{ - if (s->flags & NFT_SET_INTERVAL) - interval_map_decompose(s->init); -} - -static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type, - struct netlink_mon_handler *monh) -{ - struct nftnl_rule *nlr; - const char *family; - const char *table; - const char *chain; - struct rule *r; - uint64_t handle; - uint32_t fam; - - nlr = netlink_rule_alloc(nlh); - switch (monh->format) { - case NFTNL_OUTPUT_DEFAULT: - fam = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); - family = family2str(fam); - table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); - chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN); - handle = nftnl_rule_get_u64(nlr, NFTNL_RULE_HANDLE); - - switch (type) { - case NFT_MSG_NEWRULE: - r = netlink_delinearize_rule(monh->ctx, nlr); - nlr_for_each_set(nlr, rule_map_decompose_cb, NULL, - monh->cache); - - nft_mon_print(monh, "add rule %s %s %s ", family, table, chain); - rule_print(r, monh->ctx->octx); - nft_mon_print(monh, "\n"); - - rule_free(r); - break; - case NFT_MSG_DELRULE: - nft_mon_print(monh, "delete rule %s %s %s handle %u\n", - family, table, chain, (unsigned int)handle); - break; - } - break; - case NFTNL_OUTPUT_XML: - case NFTNL_OUTPUT_JSON: - nftnl_rule_fprintf(monh->ctx->octx->output_fp, nlr, - monh->format, netlink_msg2nftnl_of(type)); - nft_mon_print(monh, "\n"); - break; - } - - nftnl_rule_free(nlr); - return MNL_CB_OK; -} - -static void netlink_events_cache_addtable(struct netlink_mon_handler *monh, - const struct nlmsghdr *nlh) -{ - struct nftnl_table *nlt; - struct table *t; - - nlt = netlink_table_alloc(nlh); - t = netlink_delinearize_table(monh->ctx, nlt); - nftnl_table_free(nlt); - - table_add_hash(t, monh->cache); -} - -static void netlink_events_cache_deltable(struct netlink_mon_handler *monh, - const struct nlmsghdr *nlh) -{ - struct nftnl_table *nlt; - struct table *t; - struct handle h; - - nlt = netlink_table_alloc(nlh); - h.family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY); - h.table = nftnl_table_get_str(nlt, NFTNL_TABLE_NAME); - - t = table_lookup(&h, monh->cache); - if (t == NULL) - goto out; - - list_del(&t->list); - table_free(t); -out: - nftnl_table_free(nlt); -} - -static void netlink_events_cache_addset(struct netlink_mon_handler *monh, - const struct nlmsghdr *nlh) -{ - struct netlink_ctx set_tmpctx; - struct nftnl_set *nls; - struct table *t; - struct set *s; - LIST_HEAD(msgs); - - memset(&set_tmpctx, 0, sizeof(set_tmpctx)); - init_list_head(&set_tmpctx.list); - init_list_head(&msgs); - set_tmpctx.msgs = &msgs; - - nls = netlink_set_alloc(nlh); - s = netlink_delinearize_set(&set_tmpctx, nls); - if (s == NULL) - goto out; - s->init = set_expr_alloc(monh->loc, s); - - t = table_lookup(&s->handle, monh->cache); - if (t == NULL) { - fprintf(stderr, "W: Unable to cache set: table not found.\n"); - set_free(s); - goto out; - } - - set_add_hash(s, t); -out: - nftnl_set_free(nls); -} - -static void netlink_events_cache_addsetelem(struct netlink_mon_handler *monh, - const struct nlmsghdr *nlh) -{ - struct nftnl_set_elems_iter *nlsei; - struct nftnl_set_elem *nlse; - struct nftnl_set *nls; - struct set *set; - const char *table, *setname; - uint32_t family; - - nls = netlink_setelem_alloc(nlh); - family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); - table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); - setname = nftnl_set_get_str(nls, NFTNL_SET_NAME); - - set = set_lookup_global(family, table, setname, monh->cache); - if (set == NULL) { - fprintf(stderr, - "W: Unable to cache set_elem. Set not found.\n"); - goto out; - } - - nlsei = nftnl_set_elems_iter_create(nls); - if (nlsei == NULL) - memory_allocation_error(); - - nlse = nftnl_set_elems_iter_next(nlsei); - while (nlse != NULL) { - if (netlink_delinearize_setelem(nlse, set, monh->cache) < 0) { - fprintf(stderr, - "W: Unable to cache set_elem. " - "Delinearize failed.\n"); - nftnl_set_elems_iter_destroy(nlsei); - goto out; - } - nlse = nftnl_set_elems_iter_next(nlsei); - } - nftnl_set_elems_iter_destroy(nlsei); -out: - nftnl_set_free(nls); -} - -static void netlink_events_cache_delset_cb(struct set *s, - void *data) -{ - list_del(&s->list); - set_free(s); -} - -static void netlink_events_cache_delsets(struct netlink_mon_handler *monh, - const struct nlmsghdr *nlh) -{ - struct nftnl_rule *nlr = netlink_rule_alloc(nlh); - - nlr_for_each_set(nlr, netlink_events_cache_delset_cb, NULL, - monh->cache); - nftnl_rule_free(nlr); -} - -static void netlink_events_cache_addobj(struct netlink_mon_handler *monh, - const struct nlmsghdr *nlh) -{ - struct netlink_ctx obj_tmpctx; - struct nftnl_obj *nlo; - struct table *t; - struct obj *obj; - LIST_HEAD(msgs); - - memset(&obj_tmpctx, 0, sizeof(obj_tmpctx)); - init_list_head(&obj_tmpctx.list); - init_list_head(&msgs); - obj_tmpctx.msgs = &msgs; - - nlo = netlink_obj_alloc(nlh); - obj = netlink_delinearize_obj(&obj_tmpctx, nlo); - if (obj == NULL) - goto out; - - t = table_lookup(&obj->handle, monh->cache); - if (t == NULL) { - fprintf(stderr, "W: Unable to cache object: table not found.\n"); - obj_free(obj); - goto out; - } - - obj_add_hash(obj, t); -out: - nftnl_obj_free(nlo); -} - -static void netlink_events_cache_delobj(struct netlink_mon_handler *monh, - const struct nlmsghdr *nlh) -{ - struct nftnl_obj *nlo; - const char *name; - struct obj *obj; - struct handle h; - struct table *t; - uint32_t type; - - nlo = netlink_obj_alloc(nlh); - h.family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY); - h.table = nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE); - - name = nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME); - type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE); - h.handle.id = nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE); - - t = table_lookup(&h, monh->cache); - if (t == NULL) { - fprintf(stderr, "W: Unable to cache object: table not found.\n"); - goto out; - } - - obj = obj_lookup(t, name, type); - if (obj == NULL) { - fprintf(stderr, "W: Unable to find object in cache\n"); - goto out; - } - - list_del(&obj->list); - obj_free(obj); -out: - nftnl_obj_free(nlo); -} - -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; - case NFT_MSG_NEWOBJ: - netlink_events_cache_addobj(monh, nlh); - break; - case NFT_MSG_DELOBJ: - netlink_events_cache_delobj(monh, nlh); - break; - } -} - static void trace_print_hdr(const struct nftnl_trace *nlt, struct output_ctx *octx) { @@ -2754,8 +2016,8 @@ static void trace_print_packet(const struct nftnl_trace *nlt, nft_print(octx, "\n"); } -static int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, - struct netlink_mon_handler *monh) +int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) { struct nftnl_trace *nlt; @@ -2799,175 +2061,6 @@ static int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, return MNL_CB_OK; } -/* only those which could be useful listening to events */ -static const char *const nftnl_msg_types[NFT_MSG_MAX] = { - [NFT_MSG_NEWTABLE] = "NFT_MSG_NEWTABLE", - [NFT_MSG_DELTABLE] = "NFT_MSG_DELTABLE", - [NFT_MSG_NEWCHAIN] = "NFT_MSG_NEWCHAIN", - [NFT_MSG_DELCHAIN] = "NFT_MSG_DELCHAIN", - [NFT_MSG_NEWSET] = "NFT_MSG_NEWSET", - [NFT_MSG_DELSET] = "NFT_MSG_DELSET", - [NFT_MSG_NEWSETELEM] = "NFT_MSG_NEWSETELEM", - [NFT_MSG_DELSETELEM] = "NFT_MSG_DELSETELEM", - [NFT_MSG_NEWRULE] = "NFT_MSG_NEWRULE", - [NFT_MSG_DELRULE] = "NFT_MSG_DELRULE", - [NFT_MSG_TRACE] = "NFT_MSG_TRACE", - [NFT_MSG_NEWGEN] = "NFT_MSG_NEWGEN", - [NFT_MSG_NEWOBJ] = "NFT_MSG_NEWOBJ", - [NFT_MSG_DELOBJ] = "NFT_MSG_DELOBJ", -}; - -static const char *nftnl_msgtype2str(uint16_t type) -{ - if (type >= NFT_MSG_MAX || !nftnl_msg_types[type]) - return "unknown"; - - return nftnl_msg_types[type]; -} - -static void netlink_events_debug(uint16_t type, unsigned int debug_mask) -{ - if (!(debug_mask & NFT_DEBUG_NETLINK)) - return; - - printf("netlink event: %s\n", nftnl_msgtype2str(type)); -} - -static int netlink_events_newgen_cb(const struct nlmsghdr *nlh, int type, - struct netlink_mon_handler *monh) -{ - const struct nlattr *attr; - char name[256] = ""; - int genid = -1, pid = -1; - - mnl_attr_for_each(attr, nlh, sizeof(struct nfgenmsg)) { - switch (mnl_attr_get_type(attr)) { - case NFTA_GEN_ID: - if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) - break; - genid = ntohl(mnl_attr_get_u32(attr)); - break; - case NFTA_GEN_PROC_NAME: - if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) - break; - snprintf(name, sizeof(name), "%s", mnl_attr_get_str(attr)); - break; - case NFTA_GEN_PROC_PID: - if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) - break; - pid = ntohl(mnl_attr_get_u32(attr)); - break; - } - } - if (genid >= 0) { - nft_mon_print(monh, "# new generation %d", genid); - if (pid >= 0) { - nft_mon_print(monh, " by process %d", pid); - if (!monh->ctx->octx->numeric) - nft_mon_print(monh, " (%s)", name); - } - nft_mon_print(monh, "\n"); - } - - return MNL_CB_OK; -} - -static int netlink_events_cb(const struct nlmsghdr *nlh, void *data) -{ - int ret = MNL_CB_OK; - uint16_t type = NFNL_MSG_TYPE(nlh->nlmsg_type); - struct netlink_mon_handler *monh = (struct netlink_mon_handler *)data; - - netlink_events_debug(type, monh->debug_mask); - 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; - case NFT_MSG_TRACE: - ret = netlink_events_trace_cb(nlh, type, monh); - break; - case NFT_MSG_NEWOBJ: - case NFT_MSG_DELOBJ: - ret = netlink_events_obj_cb(nlh, type, monh); - break; - case NFT_MSG_NEWGEN: - ret = netlink_events_newgen_cb(nlh, type, monh); - break; - } - - return ret; -} - -int netlink_echo_callback(const struct nlmsghdr *nlh, void *data) -{ - struct netlink_ctx *ctx = data; - struct netlink_mon_handler echo_monh = { - .format = NFTNL_OUTPUT_DEFAULT, - .ctx = ctx, - .loc = &netlink_location, - .monitor_flags = 0xffffffff, - .cache_needed = true, - .cache = ctx->cache, - .debug_mask = ctx->debug_mask, - }; - - if (!echo_monh.ctx->octx->echo) - return MNL_CB_OK; - - return netlink_events_cb(nlh, &echo_monh); -} - -int netlink_monitor(struct netlink_mon_handler *monhandler, - struct mnl_socket *nf_sock) -{ - int group; - - if (monhandler->monitor_flags & (1 << NFT_MSG_TRACE)) { - group = NFNLGRP_NFTRACE; - if (mnl_socket_setsockopt(nf_sock, NETLINK_ADD_MEMBERSHIP, - &group, sizeof(int)) < 0) - return netlink_io_error(monhandler->ctx, - monhandler->loc, - "Could not bind to netlink socket %s", - strerror(errno)); - } - if (monhandler->monitor_flags & ~(1 << NFT_MSG_TRACE)) { - group = NFNLGRP_NFTABLES; - if (mnl_socket_setsockopt(nf_sock, NETLINK_ADD_MEMBERSHIP, - &group, sizeof(int)) < 0) - return netlink_io_error(monhandler->ctx, - monhandler->loc, - "Could not bind to netlink socket %s", - strerror(errno)); - } - - return mnl_nft_event_listener(nf_sock, monhandler->debug_mask, - monhandler->ctx->octx, netlink_events_cb, - monhandler); -} - static int netlink_markup_setelems(const struct nftnl_parse_ctx *ctx) { const struct ruleset_parse *rp; |