From: Ana Rey <ana@xxxxxxxxx> This adds userspace support to accounting objects and expression. Example of use in nft: # nft add counter ip filter http-traffic # nft add counter ip filter https-traffic # nft add rule ip filter output tcp dport 80 counter name http-traffic # nft add rule ip filter output tcp dport 443 counter name https-traffic # nft delete counter ip filter https-traffic Generate Some traffic: # nft list table ip test table ip filter { counter http-traffic { pkts 779 bytes 99495} counter https-traffic { pkts 189 bytes 37824} chain output { type filter hook output priority 0; tcp dport http counter http-traffic tcp dport https counter https-traffic } } The kernel support is added in the commit: "netfilter: named counter: add support to counters in nftables" The libnftnl support is added in the commit: "src: Add accounters support" Signed-off-by: Ana Rey Botello <ana@xxxxxxxxx> --- include/linux/netfilter/nf_tables.h | 32 +++++ include/mnl.h | 8 ++ include/netlink.h | 22 ++++ include/rule.h | 47 +++++++ include/statement.h | 1 + src/evaluate.c | 13 +- src/mnl.c | 119 ++++++++++++++++++ src/netlink.c | 235 +++++++++++++++++++++++++++++++++++ src/netlink_delinearize.c | 3 + src/netlink_linearize.c | 4 + src/parser_bison.y | 60 ++++++++- src/rule.c | 139 +++++++++++++++++++++ src/scanner.l | 1 + src/statement.c | 8 +- 14 files changed, 685 insertions(+), 7 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 832bc46..b2d8028 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -53,6 +53,10 @@ enum nft_verdicts { * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes) * @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes) * @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes) + * @NFT_MSG_NEWCOUNTER: create a new counter (enum nft_counter_attributes) + * @NFT_MSG_GETCOUNTER: get a counter (enum nft_counter_attributes) + * @NFT_MSG_GETCOUNTER_ZERO: get a reset counter (enum nft_counter_attributes) + * @NFT_MSG_DELCOUNTER: delete a counter (enum nft_counter_attributes) */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, @@ -72,6 +76,10 @@ enum nf_tables_msg_types { NFT_MSG_DELSETELEM, NFT_MSG_NEWGEN, NFT_MSG_GETGEN, + NFT_MSG_NEWCOUNTER, + NFT_MSG_GETCOUNTER, + NFT_MSG_GETCOUNTER_ZERO, + NFT_MSG_DELCOUNTER, NFT_MSG_MAX, }; @@ -695,16 +703,40 @@ enum nft_limit_attributes { * * @NFTA_COUNTER_BYTES: number of bytes (NLA_U64) * @NFTA_COUNTER_PACKETS: number of packets (NLA_U64) + * @NFTA_COUNTER_NAME: name of the counter (NLA_STRING) */ enum nft_counter_attributes { NFTA_COUNTER_UNSPEC, NFTA_COUNTER_BYTES, NFTA_COUNTER_PACKETS, + NFTA_COUNTER_NAME, __NFTA_COUNTER_MAX }; #define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1) /** + * enum nft_named_counter_attributes - nf_tables named counter netlink attributes + * + * @NFTA_NAMED_CTR_NAME: named counter name (NLA_STRING) + * @NFTA_NAMED_CTR_TABLE: table name (NLA_STRING) + * @NFTA_NAMED_CTR_USE: number of references to this named counter (NLA_U32) + * @NFTA_NAMED_CTR_ID: uniquely identifies a named counter in a transaction (NLA_U32) + * @NFTA_NAMED_CTR_BYTES: number of bytes (NLA_U64) + * @NFTA_NAMED_CTR_PACKETS: number of packets (NLA_U64) + */ +enum nft_named_counter_attributes { + NFTA_NAMED_CTR_UNSPEC, + NFTA_NAMED_CTR_NAME, + NFTA_NAMED_CTR_TABLE, + NFTA_NAMED_CTR_USE, + NFTA_NAMED_CTR_ID, + NFTA_NAMED_CTR_BYTES, + NFTA_NAMED_CTR_PACKETS, + __NFTA_NAMED_CTR_MAX +}; +#define NFTA_NAMED_CTR_MAX (__NFTA_NAMED_CTR_MAX - 1) + +/** * enum nft_log_attributes - nf_tables log expression netlink attributes * * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32) diff --git a/include/mnl.h b/include/mnl.h index a0dfa1b..b447d62 100644 --- a/include/mnl.h +++ b/include/mnl.h @@ -34,6 +34,14 @@ int mnl_nft_rule_delete(struct mnl_socket *nf_sock, struct nft_rule *r, struct nft_rule_list *mnl_nft_rule_dump(struct mnl_socket *nf_sock, int family); +int mnl_nft_counter_batch_add(struct nft_counter *nlc, + unsigned int flags, uint32_t seq); +int mnl_nft_counter_batch_del(struct nft_counter *nlc, + unsigned int flags, uint32_t seq); +int mnl_nft_counter_get(struct mnl_socket *nf_sock, struct nft_counter *nlc); +struct nft_counter_list *mnl_nft_counter_dump(struct mnl_socket *nf_sock, + int family, const char *table); + int mnl_nft_chain_add(struct mnl_socket *nf_sock, struct nft_chain *nlc, unsigned int flags); int mnl_nft_chain_batch_add(struct nft_chain *nlc, diff --git a/include/netlink.h b/include/netlink.h index 4f79470..962aa4a 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -21,6 +21,7 @@ extern const struct location netlink_location; * @msgs: message queue * @list: list of parsed rules/chains/tables * @set: current set + * @counter: current counter * @data: pointer to pass data to callback * @seqnum: sequence number */ @@ -28,6 +29,7 @@ struct netlink_ctx { struct list_head *msgs; struct list_head list; struct set *set; + struct counter *counter; const void *data; uint32_t seqnum; bool batch_supported; @@ -38,6 +40,7 @@ extern struct nft_chain *alloc_nft_chain(const struct handle *h); extern struct nft_rule *alloc_nft_rule(const struct handle *h); extern struct nft_rule_expr *alloc_nft_expr(const char *name); extern struct nft_set *alloc_nft_set(const struct handle *h); +extern struct nft_counter *alloc_nft_counter(const struct handle *h); struct nft_data_linearize { uint32_t len; @@ -84,6 +87,24 @@ extern int netlink_del_rule_batch(struct netlink_ctx *ctx, const struct handle *h, const struct location *loc); +extern int netlink_add_counter(struct netlink_ctx *ctx, const struct handle *h, + struct counter *counter); +extern int netlink_rename_counter(struct netlink_ctx *ctx, + const struct handle *h, + const struct location *loc, const char *name); +extern int netlink_delete_counter(struct netlink_ctx *ctx, + const struct handle *h, + const struct location *loc); +extern int netlink_list_counters(struct netlink_ctx *ctx, + const struct handle *h, + const struct location *loc); +extern int netlink_get_counter(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc); +extern int netlink_flush_counter(struct netlink_ctx *ctx, + const struct handle *h, + const struct location *loc); + + extern int netlink_add_chain(struct netlink_ctx *ctx, const struct handle *h, const struct location *loc, const struct chain *chain, bool excl); @@ -135,6 +156,7 @@ extern void netlink_dump_chain(struct nft_chain *nlc); extern void netlink_dump_rule(struct nft_rule *nlr); extern void netlink_dump_expr(struct nft_rule_expr *nle); extern void netlink_dump_set(struct nft_set *nls); +extern void netlink_dump_counter(struct nft_counter *nlc); extern int netlink_batch_send(struct list_head *err_list); diff --git a/include/rule.h b/include/rule.h index 491411e..23c5581 100644 --- a/include/rule.h +++ b/include/rule.h @@ -12,6 +12,7 @@ * @table: table name * @chain: chain name (chains and rules only) * @set: set name (sets only) + * @counter: counter name (counters only) * @handle: rule handle (rules only) * @position: rule position (rules only) * @set_id: set ID (sets only) @@ -22,6 +23,7 @@ struct handle { const char *table; const char *chain; const char *set; + const char *counter; uint64_t handle; uint64_t position; uint32_t set_id; @@ -71,6 +73,7 @@ extern struct symbol *symbol_lookup(const struct scope *scope, * @location: location the table was defined at * @chains: chains contained in the table * @sets: sets contained in the table + * @counters: counters contained in the table */ struct table { struct list_head list; @@ -79,6 +82,7 @@ struct table { struct scope scope; struct list_head chains; struct list_head sets; + struct list_head counters; }; extern struct table *table_alloc(void); @@ -211,6 +215,41 @@ extern void set_print(const struct set *set); extern void set_print_plain(const struct set *s); /** + * struct counter - nftables counter + * + * @list: table counter list node + * @handle: counter handle + * @location: location the counter was defined/declared at + * @refcnt: reference count + * @flags: bitmask of counter flags + * @bytes: Total bytes + * @packets: Total packets + + */ +struct counter { + struct list_head list; + struct handle handle; + struct location location; + unsigned int refcnt; + uint32_t flags; + uint64_t bytes; + uint64_t packets; +}; + +extern struct counter *counter_alloc(const struct location *loc); +extern struct counter *counter_get(struct counter *counter); +extern void counter_free(struct counter *counter); +extern struct counter *counter_clone(const struct counter *counter); +extern void counter_add_hash(struct counter *counter, struct table *table); +extern struct counter *counter_lookup(const struct table *table, + const char *name); +extern struct counter *counter_lookup_global(uint32_t family, const char *table, + const char *name); +extern void counter_print(const struct counter *counter); + + + +/** * enum cmd_ops - command operations * * @CMD_INVALID: invalid @@ -253,6 +292,8 @@ enum cmd_ops { * @CMD_OBJ_EXPR: expression * @CMD_OBJ_MONITOR: monitor * @CMD_OBJ_EXPORT: export + * @CMD_OBJ_COUNTER: counter + * @CMD_OBJ_COUNTERS: counters */ enum cmd_obj { CMD_OBJ_INVALID, @@ -266,6 +307,8 @@ enum cmd_obj { CMD_OBJ_EXPR, CMD_OBJ_MONITOR, CMD_OBJ_EXPORT, + CMD_OBJ_COUNTER, + CMD_OBJ_COUNTERS, }; struct export { @@ -282,6 +325,7 @@ enum { CMD_MONITOR_OBJ_RULES, CMD_MONITOR_OBJ_SETS, CMD_MONITOR_OBJ_ELEMS, + CMD_MONITOR_OBJ_COUNTERS, CMD_MONITOR_OBJ_MAX }; @@ -320,6 +364,7 @@ struct cmd { void *data; struct expr *expr; struct set *set; + struct counter *counter; struct rule *rule; struct chain *chain; struct table *table; @@ -345,6 +390,7 @@ extern void cmd_free(struct cmd *cmd); * @table: current table * @rule: current rule * @set: current set + * @counter: current counter * @stmt: current statement * @ectx: expression context * @pctx: payload context @@ -355,6 +401,7 @@ struct eval_ctx { struct table *table; struct rule *rule; struct set *set; + struct counter *counter; struct stmt *stmt; struct expr_ctx ectx; struct proto_ctx pctx; diff --git a/include/statement.h b/include/statement.h index d143121..5c65633 100644 --- a/include/statement.h +++ b/include/statement.h @@ -13,6 +13,7 @@ extern struct stmt *verdict_stmt_alloc(const struct location *loc, struct counter_stmt { uint64_t packets; uint64_t bytes; + const char *name; }; extern struct stmt *counter_stmt_alloc(const struct location *loc); diff --git a/src/evaluate.c b/src/evaluate.c index d24d4cc..e7d3e80 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -1824,6 +1824,7 @@ static int table_evaluate(struct eval_ctx *ctx, struct table *table) if (set_evaluate(ctx, set) < 0) return -1; } + list_for_each_entry(chain, &table->chains, list) { handle_merge(&chain->handle, &table->handle); if (chain_evaluate(ctx, chain) < 0) @@ -1844,6 +1845,8 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) case CMD_OBJ_RULE: handle_merge(&cmd->rule->handle, &cmd->handle); return rule_evaluate(ctx, cmd->rule); + case CMD_OBJ_COUNTER: + return 0; case CMD_OBJ_CHAIN: if (cmd->data == NULL) return 0; @@ -1863,6 +1866,7 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd) case CMD_OBJ_SETELEM: return setelem_evaluate(ctx, &cmd->expr); case CMD_OBJ_SET: + case CMD_OBJ_COUNTER: case CMD_OBJ_RULE: case CMD_OBJ_CHAIN: case CMD_OBJ_TABLE: @@ -1892,13 +1896,16 @@ static uint32_t monitor_flags[CMD_MONITOR_EVENT_MAX][CMD_MONITOR_OBJ_MAX] = { (1 << NFT_MSG_DELSET), [CMD_MONITOR_OBJ_ELEMS] = (1 << NFT_MSG_NEWSETELEM) | (1 << NFT_MSG_DELSETELEM), + [CMD_MONITOR_OBJ_COUNTERS] = (1 << NFT_MSG_NEWCOUNTER) | + (1 << NFT_MSG_DELCOUNTER), }, [CMD_MONITOR_EVENT_NEW] = { [CMD_MONITOR_OBJ_ANY] = (1 << NFT_MSG_NEWTABLE) | (1 << NFT_MSG_NEWCHAIN) | (1 << NFT_MSG_NEWRULE) | (1 << NFT_MSG_NEWSET) | - (1 << NFT_MSG_NEWSETELEM), + (1 << NFT_MSG_NEWSETELEM)| + (1 << NFT_MSG_NEWCOUNTER), [CMD_MONITOR_OBJ_TABLES] = (1 << NFT_MSG_NEWTABLE), [CMD_MONITOR_OBJ_CHAINS] = (1 << NFT_MSG_NEWCHAIN), [CMD_MONITOR_OBJ_RULES] = (1 << NFT_MSG_NEWRULE), @@ -1910,12 +1917,14 @@ static uint32_t monitor_flags[CMD_MONITOR_EVENT_MAX][CMD_MONITOR_OBJ_MAX] = { (1 << NFT_MSG_DELCHAIN) | (1 << NFT_MSG_DELRULE) | (1 << NFT_MSG_DELSET) | - (1 << NFT_MSG_DELSETELEM), + (1 << NFT_MSG_DELSETELEM)| + (1 << NFT_MSG_DELCOUNTER), [CMD_MONITOR_OBJ_TABLES] = (1 << NFT_MSG_DELTABLE), [CMD_MONITOR_OBJ_CHAINS] = (1 << NFT_MSG_DELCHAIN), [CMD_MONITOR_OBJ_RULES] = (1 << NFT_MSG_DELRULE), [CMD_MONITOR_OBJ_SETS] = (1 << NFT_MSG_DELSET), [CMD_MONITOR_OBJ_ELEMS] = (1 << NFT_MSG_DELSETELEM), + [CMD_MONITOR_OBJ_COUNTERS] = (1 << NFT_MSG_DELCOUNTER), }, }; diff --git a/src/mnl.c b/src/mnl.c index f48ead5..47b91b2 100644 --- a/src/mnl.c +++ b/src/mnl.c @@ -16,6 +16,7 @@ #include <libnftnl/rule.h> #include <libnftnl/expr.h> #include <libnftnl/set.h> +#include <libnftnl/counter.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nf_tables.h> @@ -711,6 +712,124 @@ int mnl_nft_table_get(struct mnl_socket *nf_sock, struct nft_table *nlt, } /* + * Named counter + */ +int mnl_nft_counter_batch_add(struct nft_counter *nls, unsigned int flags, + uint32_t seqnum) +{ + struct nlmsghdr *nlh; + + nlh = nft_counter_nlmsg_build_hdr(nft_nlmsg_batch_current(), + NFT_MSG_NEWCOUNTER, + nft_counter_attr_get_u32(nls, NFT_COUNTER_ATTR_FAMILY), + NLM_F_CREATE | flags, seqnum); + nft_counter_nlmsg_build_payload(nlh, nls); + nft_batch_continue(); + + return 0; +} + +int mnl_nft_counter_batch_del(struct nft_counter *nls, unsigned int flags, + uint32_t seqnum) +{ + struct nlmsghdr *nlh; + + nlh = nft_counter_nlmsg_build_hdr(nft_nlmsg_batch_current(), + NFT_MSG_DELCOUNTER, + nft_counter_attr_get_u32(nls, NFT_COUNTER_ATTR_FAMILY), + flags, seqnum); + nft_counter_nlmsg_build_payload(nlh, nls); + nft_batch_continue(); + + return 0; +} + +static int counter_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_counter_list *ctr_list = data; + struct nft_counter *c; + + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + c = nft_counter_alloc(); + if (c == NULL) + memory_allocation_error(); + + if (nft_counter_nlmsg_parse(nlh, c) < 0) + goto err_free; + + nft_counter_list_add_tail(c, ctr_list); + return MNL_CB_OK; + +err_free: + nft_counter_free(c); + return MNL_CB_OK; +} + +static int counter_get_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_counter *a = data; + + nft_counter_nlmsg_parse(nlh, a); + + return MNL_CB_OK; +} + +int mnl_nft_counter_get(struct mnl_socket *nf_sock, struct nft_counter *counter) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + nlh = nft_counter_nlmsg_build_hdr(buf, NFT_MSG_GETCOUNTER, + nft_counter_attr_get_u32(counter, + NFT_COUNTER_ATTR_FAMILY), + NLM_F_ACK, seq); + nft_counter_nlmsg_build_payload(nlh, counter); + + return nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, counter_get_cb, + counter); +} + +struct nft_counter_list *mnl_nft_counter_dump(struct mnl_socket *nf_sock, + int family, + const char *table) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_counter *counter; + struct nft_counter_list *ctr_list; + int ret; + + counter = nft_counter_alloc(); + if (counter == NULL) + memory_allocation_error(); + + nlh = nft_counter_nlmsg_build_hdr(buf, NFT_MSG_GETCOUNTER, family, + NLM_F_DUMP|NLM_F_ACK, seq); + if (table != NULL) + nft_counter_attr_set_str(counter, NFT_COUNTER_ATTR_TABLE, + table); + nft_counter_nlmsg_build_payload(nlh, counter); + nft_counter_free(counter); + + ctr_list = nft_counter_list_alloc(); + if (ctr_list == NULL) + memory_allocation_error(); + + ret = nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, counter_cb, ctr_list); + + if (ret < 0) + goto err; + + return ctr_list; +err: + nft_counter_list_free(ctr_list); + return NULL; +} + + +/* * Set */ static int set_add_cb(const struct nlmsghdr *nlh, void *data) diff --git a/src/netlink.c b/src/netlink.c index 84d9d27..6cd775b 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -21,6 +21,7 @@ #include <libnftnl/chain.h> #include <libnftnl/expr.h> #include <libnftnl/set.h> +#include <libnftnl/counter.h> #include <libnftnl/common.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nf_tables.h> @@ -1434,6 +1435,176 @@ out: return err; } +static struct counter *netlink_delinearize_counter(struct netlink_ctx *ctx, + struct nft_counter *nla) +{ + struct counter *counter; + + counter = counter_alloc(&netlink_location); + + if (counter == NULL) + return NULL; + + counter->handle.family = + nft_counter_attr_get_u32(nla, NFT_COUNTER_ATTR_FAMILY); + counter->handle.counter = + xstrdup(nft_counter_attr_get_str(nla, NFT_COUNTER_ATTR_NAME)); + counter->handle.table = + xstrdup(nft_counter_attr_get_str(nla, NFT_COUNTER_ATTR_TABLE)); + counter->packets = + nft_counter_attr_get_u64(nla, NFT_COUNTER_ATTR_PKTS); + counter->bytes = nft_counter_attr_get_u64(nla, NFT_COUNTER_ATTR_BYTES); + + return counter; +} + +static int list_counter_cb(struct nft_counter *c, void *arg) +{ + struct netlink_ctx *ctx = arg; + struct counter *counter; + + netlink_dump_counter(c); + counter = netlink_delinearize_counter(ctx, c); + if (counter == NULL) + return -1; + + list_add_tail(&counter->list, &ctx->list); + + return 0; +} + +int netlink_list_counters(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc) +{ + struct nft_counter_list *ctr_list; + int err; + + ctr_list = mnl_nft_counter_dump(nf_sock, h->family, h->table); + if (ctr_list == NULL) { + if (errno == EINTR) + return -1; + + return netlink_io_error(ctx, loc, + "Could not receive counters from kernel: %s", + strerror(errno)); + } + + err = nft_counter_list_foreach(ctr_list, list_counter_cb, ctx); + nft_counter_list_free(ctr_list); + + return err; +} + +int netlink_get_counter(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc) +{ + struct nft_counter *nla; + struct counter *counter; + int err; + + nla = alloc_nft_counter(h); + netlink_dump_counter(nla); + err = mnl_nft_counter_get(nf_sock, nla); + if (err < 0) { + nft_counter_free(nla); + return netlink_io_error(ctx, loc, + "Could not receive counter from kernel: %s", + strerror(errno)); + } + + counter = netlink_delinearize_counter(ctx, nla); + if (counter == NULL) { + nft_counter_free(nla); + return -1; + } + + list_add_tail(&counter->list, &ctx->list); + + return err; +} + +void netlink_dump_counter(struct nft_counter *nla) +{ +#ifdef DEBUG + char buf[4096]; + + if (!(debug_level & DEBUG_NETLINK)) + return; + + nft_counter_snprintf(buf, sizeof(buf), nla, 0, 0); + fprintf(stdout, "%s\n", buf); +#endif +} + +struct nft_counter *alloc_nft_counter(const struct handle *h) +{ + struct nft_counter *nla; + + nla = nft_counter_alloc(); + if (nla == NULL) + memory_allocation_error(); + + nft_counter_attr_set_u32(nla, NFT_COUNTER_ATTR_FAMILY, h->family); + nft_counter_attr_set_str(nla, NFT_COUNTER_ATTR_TABLE, h->table); + if (h->counter != NULL) { + nft_counter_attr_set_str(nla, + NFT_COUNTER_ATTR_NAME, h->counter); + } + + return nla; +} + +static int netlink_add_counter_batch(struct netlink_ctx *ctx, + const struct handle *h, + struct counter *counter) +{ + struct nft_counter *nla; + int err; + + nla = alloc_nft_counter(h); + + netlink_dump_counter(nla); + + err = mnl_nft_counter_batch_add(nla, NLM_F_EXCL, ctx->seqnum); + if (err < 0) { + netlink_io_error(ctx, &counter->location, + "Could not add counter: %s", + strerror(errno)); + } + nft_counter_free(nla); + + return err; +} + +int netlink_add_counter(struct netlink_ctx *ctx, const struct handle *h, + struct counter *counter) +{ + return netlink_add_counter_batch(ctx, h, counter); +} + +static int netlink_del_counter_batch(struct netlink_ctx *ctx, + const struct handle *h, + const struct location *loc) +{ + struct nft_counter *nla; + int err; + + nla = alloc_nft_counter(h); + err = mnl_nft_counter_batch_del(nla, 0, ctx->seqnum); + nft_counter_free(nla); + + if (err < 0) + netlink_io_error(ctx, loc, "Could not delete counter: %s", + strerror(errno)); + return err; +} + +int netlink_delete_counter(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc) +{ + return netlink_del_counter_batch(ctx, h, loc); +} + int netlink_batch_send(struct list_head *err_list) { return mnl_batch_talk(nf_sock, err_list); @@ -1515,6 +1686,19 @@ static struct nft_set *netlink_set_alloc(const struct nlmsghdr *nlh) return nls; } +static struct nft_counter *netlink_counter_alloc(const struct nlmsghdr *nlh) +{ + struct nft_counter *nla = nft_counter_alloc(); + + if (nla == NULL) + memory_allocation_error(); + + if (nft_counter_nlmsg_parse(nlh, nla) < 0) + netlink_abi_error(); + + return nla; +} + static struct nft_set *netlink_setelem_alloc(const struct nlmsghdr *nlh) { struct nft_set *nls; @@ -1549,12 +1733,14 @@ static uint32_t netlink_msg2nftnl_of(uint32_t msg) case NFT_MSG_NEWSET: case NFT_MSG_NEWSETELEM: case NFT_MSG_NEWRULE: + case NFT_MSG_NEWCOUNTER: 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: + case NFT_MSG_DELCOUNTER: return NFT_OF_EVENT_DEL; } @@ -1806,6 +1992,51 @@ out: return MNL_CB_OK; } +static int netlink_events_counter_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct counter *counter; + uint32_t family; + struct nft_counter *nla = netlink_counter_alloc(nlh); + + switch (monh->format) { + case NFT_OUTPUT_DEFAULT: + switch (type) { + case NFT_MSG_NEWCOUNTER: + printf("add "); + counter = netlink_delinearize_counter(monh->ctx, nla); + if (counter == NULL) + return MNL_CB_ERROR; + counter_print(counter); + counter_free(counter); + printf("\n"); + break; + case NFT_MSG_DELCOUNTER: + family = nft_counter_attr_get_u32(nla, + NFT_COUNTER_ATTR_FAMILY); + printf("delete counter %s %s %s\n", + family2str(family), + nft_counter_attr_get_str(nla, + NFT_COUNTER_ATTR_TABLE), + nft_counter_attr_get_str(nla, + NFT_COUNTER_ATTR_NAME)); + break; + } + break; + case NFT_OUTPUT_XML: + case NFT_OUTPUT_JSON: + nft_counter_fprintf(stdout, nla, monh->format, + netlink_msg2nftnl_of(type)); + fprintf(stdout, "\n"); + break; + } + + nft_counter_free(nla); + + return MNL_CB_OK; + +} + static void rule_map_decompose_cb(struct set *s, void *data) { if (s->flags & NFT_SET_INTERVAL) @@ -2038,6 +2269,10 @@ static int netlink_events_cb(const struct nlmsghdr *nlh, void *data) case NFT_MSG_DELSETELEM: /* nft {add|delete} element */ ret = netlink_events_setelem_cb(nlh, type, monh); break; + case NFT_MSG_NEWCOUNTER: + case NFT_MSG_DELCOUNTER: /* nft {add|delete} counter */ + ret = netlink_events_counter_cb(nlh, type, monh); + break; case NFT_MSG_NEWRULE: case NFT_MSG_DELRULE: ret = netlink_events_rule_cb(nlh, type, monh); diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 387bb67..5ccfa30 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -445,6 +445,7 @@ static void netlink_parse_ct(struct netlink_parse_ctx *ctx, netlink_parse_ct_stmt(ctx, loc, nle); } + static void netlink_parse_counter(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nft_rule_expr *nle) @@ -456,6 +457,8 @@ static void netlink_parse_counter(struct netlink_parse_ctx *ctx, nft_rule_expr_get_u64(nle, NFT_EXPR_CTR_PACKETS); stmt->counter.bytes = nft_rule_expr_get_u64(nle, NFT_EXPR_CTR_BYTES); + stmt->counter.name = + nft_rule_expr_get_str(nle, NFT_EXPR_CTR_NAME); list_add_tail(&stmt->list, &ctx->rule->stmts); } diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 9bef67b..3a098eb 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -553,6 +553,10 @@ static void netlink_gen_counter_stmt(struct netlink_linearize_ctx *ctx, nft_rule_expr_set_u64(nle, NFT_EXPR_CTR_BYTES, stmt->counter.bytes); } + if (stmt->counter.name) { + nft_rule_expr_set_str(nle, NFT_EXPR_CTR_NAME, + stmt->counter.name); + } nft_rule_add_expr(ctx->nlr, nle); } diff --git a/src/parser_bison.y b/src/parser_bison.y index fd2407c..833e848 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -133,6 +133,7 @@ static void location_update(struct location *loc, struct location *rhs, int n) struct expr *expr; struct set *set; const struct datatype *datatype; + struct counter *counter; } %token TOKEN_EOF 0 "end of file" @@ -341,6 +342,7 @@ static void location_update(struct location *loc, struct location *rhs, int n) %token COUNTER "counter" %token PACKETS "packets" %token BYTES "bytes" +%token NAME "name" %token LOG "log" %token PREFIX "prefix" @@ -405,8 +407,8 @@ static void location_update(struct location *loc, struct location *rhs, int n) %type <cmd> base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd %destructor { cmd_free($$); } base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd -%type <handle> table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec -%destructor { handle_free(&$$); } table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec +%type <handle> table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec counter_spec counter_identifier +%destructor { handle_free(&$$); } table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec counter_spec counter_identifier %type <handle> set_spec set_identifier %destructor { handle_free(&$$); } set_spec set_identifier %type <val> handle_spec family_spec family_spec_explicit position_spec @@ -428,6 +430,9 @@ static void location_update(struct location *loc, struct location *rhs, int n) %type <set> map_block_alloc map_block %destructor { set_free($$); } map_block_alloc +%type <counter> counter_block_alloc +%destructor { counter_free($$); } counter_block_alloc + %type <list> stmt_list %destructor { stmt_list_free($$); xfree($$); } stmt_list %type <stmt> stmt match_stmt verdict_stmt @@ -680,6 +685,10 @@ add_cmd : TABLE table_spec handle_merge(&$3->handle, &$2); $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, &@$, $5); } + | COUNTER counter_spec + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, NULL); + } | MAP set_spec map_block_alloc '{' map_block '}' { @@ -740,6 +749,10 @@ delete_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL); } + | COUNTER counter_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL); + } | MAP set_spec { $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL); @@ -770,6 +783,10 @@ list_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, &@$, NULL); } + | COUNTER counter_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &$2, &@$, NULL); + } | RULESET ruleset_spec { $$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL); @@ -788,6 +805,10 @@ flush_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_SET, &$2, &@$, NULL); } + | COUNTER counter_spec + { + $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_COUNTER, &$2, &@$, NULL); + } | RULESET ruleset_spec { $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_RULESET, &$2, &@$, NULL); @@ -877,6 +898,16 @@ table_block : /* empty */ { $$ = $<table>-1; } list_add_tail(&$4->list, &$1->sets); $$ = $1; } + | table_block COUNTER counter_identifier + counter_block_alloc + stmt_seperator + { + $4->location = @3; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->counters); + $$ = $1; + } | table_block MAP set_identifier map_block_alloc '{' map_block '}' stmt_seperator @@ -907,6 +938,12 @@ chain_block : /* empty */ { $$ = $<chain>-1; } } ; +counter_block_alloc : /* empty */ + { + $$ = counter_alloc(NULL); + } + ; + set_block_alloc : /* empty */ { $$ = set_alloc(NULL); @@ -1112,6 +1149,13 @@ set_spec : table_spec identifier } ; +counter_spec : table_spec identifier + { + $$ = $1; + $$.counter = $2; + } + ; + set_identifier : identifier { memset(&$$, 0, sizeof($$)); @@ -1119,6 +1163,13 @@ set_identifier : identifier } ; +counter_identifier : identifier + { + memset(&$$, 0, sizeof($$)); + $$.counter = $1; + } + ; + handle_spec : /* empty */ { $$ = 0; @@ -1258,7 +1309,6 @@ verdict_map_list_member_expr: opt_newline map_lhs_expr COLON verdict_expr opt_ne } ; - counter_stmt : counter_stmt_alloc | counter_stmt_alloc counter_args @@ -1283,6 +1333,10 @@ counter_arg : PACKETS NUM { $<stmt>0->counter.bytes = $2; } + | NAME STRING + { + $<stmt>0->counter.name = $2; + } ; log_stmt : log_stmt_alloc diff --git a/src/rule.c b/src/rule.c index feafe26..53538ae 100644 --- a/src/rule.c +++ b/src/rule.c @@ -32,6 +32,7 @@ void handle_free(struct handle *h) xfree(h->table); xfree(h->chain); xfree(h->set); + xfree(h->counter); xfree(h->comment); } @@ -45,6 +46,8 @@ void handle_merge(struct handle *dst, const struct handle *src) dst->chain = xstrdup(src->chain); if (dst->set == NULL && src->set != NULL) dst->set = xstrdup(src->set); + if (dst->counter == NULL && src->counter != NULL) + dst->counter = xstrdup(src->counter); if (dst->handle == 0) dst->handle = src->handle; if (dst->position == 0) @@ -212,6 +215,70 @@ void set_print_plain(const struct set *s) do_set_print(s, &opts); } +struct counter *counter_alloc(const struct location *loc) +{ + struct counter *counter; + + counter = xzalloc(sizeof(*counter)); + counter->refcnt = 1; + + if (loc != NULL) + counter->location = *loc; + + return counter; +} + +struct counter *counter_get(struct counter *counter) +{ + counter->refcnt++; + + return counter; +} + +void counter_free(struct counter *counter) +{ + if (--counter->refcnt > 0) + return; + handle_free(&counter->handle); + xfree(counter); +} + +struct counter *counter_lookup(const struct table *table, const char *name) +{ + struct counter *counter; + + list_for_each_entry(counter, &table->counters, list) { + if (!strcmp(counter->handle.counter, name)) + return counter; + } + + return NULL; +} + +struct counter *counter_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 counter_lookup(t, name); +} + +void counter_print(const struct counter *counter) +{ + printf("\tcounter %s { ", counter->handle.counter); + printf("packets %"PRIu64" bytes %"PRIu64"", counter->packets, + counter->bytes); + printf("}\n"); +} + struct rule *rule_alloc(const struct location *loc, const struct handle *h) { struct rule *rule; @@ -467,6 +534,7 @@ struct table *table_alloc(void) table = xzalloc(sizeof(*table)); init_list_head(&table->chains); init_list_head(&table->sets); + init_list_head(&table->counters); init_list_head(&table->scope.symbols); return table; } @@ -504,6 +572,7 @@ struct table *table_lookup(const struct handle *h) static void table_print(const struct table *table) { struct chain *chain; + struct counter *counter; struct set *set; const char *delim = ""; const char *family = family2str(table->handle.family); @@ -516,11 +585,21 @@ static void table_print(const struct table *table) set_print(set); delim = "\n"; } + + if (!list_empty(&table->sets)) + printf("\n"); + list_for_each_entry(counter, &table->counters, list) { + counter_print(counter); + } + if (!list_empty(&table->chains)) + printf("\n"); + list_for_each_entry(chain, &table->chains, list) { printf("%s", delim); chain_print(chain); delim = "\n"; } + printf("}\n"); } @@ -602,6 +681,9 @@ void cmd_free(struct cmd *cmd) case CMD_OBJ_EXPORT: export_free(cmd->export); break; + case CMD_OBJ_COUNTER: + counter_free(cmd->counter); + break; default: BUG("invalid command object type %u\n", cmd->obj); } @@ -625,6 +707,15 @@ static int do_add_chain(struct netlink_ctx *ctx, const struct handle *h, return 0; } +static int do_add_counter(struct netlink_ctx *ctx, const struct handle *h, + struct counter *counter) +{ + if (netlink_add_counter(ctx, h, counter) < 0) + return -1; + + return 0; +} + static int do_add_setelems(struct netlink_ctx *ctx, const struct handle *h, const struct expr *expr) { @@ -654,6 +745,7 @@ static int do_add_table(struct netlink_ctx *ctx, const struct handle *h, { struct chain *chain; struct set *set; + struct counter *counter; if (netlink_add_table(ctx, h, loc, table, excl) < 0) return -1; @@ -663,6 +755,11 @@ static int do_add_table(struct netlink_ctx *ctx, const struct handle *h, if (do_add_set(ctx, &set->handle, set) < 0) return -1; } + list_for_each_entry(counter, &table->counters, list) { + handle_merge(&counter->handle, &table->handle); + if (do_add_counter(ctx, &counter->handle, counter) < 0) + return -1; + } list_for_each_entry(chain, &table->chains, list) { if (do_add_chain(ctx, &chain->handle, &chain->location, chain, excl) < 0) @@ -688,6 +785,8 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl) return do_add_set(ctx, &cmd->handle, cmd->set); case CMD_OBJ_SETELEM: return do_add_setelems(ctx, &cmd->handle, cmd->expr); + case CMD_OBJ_COUNTER: + return do_add_counter(ctx, &cmd->handle, cmd->counter); default: BUG("invalid command object type %u\n", cmd->obj); } @@ -720,6 +819,9 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd) return netlink_delete_set(ctx, &cmd->handle, &cmd->location); case CMD_OBJ_SETELEM: return netlink_delete_setelems(ctx, &cmd->handle, cmd->expr); + case CMD_OBJ_COUNTER: + return netlink_delete_counter(ctx, &cmd->handle, + &cmd->location); default: BUG("invalid command object type %u\n", cmd->obj); } @@ -741,6 +843,21 @@ static int do_list_sets(struct netlink_ctx *ctx, const struct location *loc, return 0; } +static int do_list_counters(struct netlink_ctx *ctx, const struct location *loc, + struct table *table) +{ + struct counter *counter, *ncounter; + + if (netlink_list_counters(ctx, &table->handle, loc) < 0) + return -1; + + list_for_each_entry_safe(counter, ncounter, &ctx->list, list) { + list_move_tail(&counter->list, &table->counters); + } + + return 0; +} + static int do_command_export(struct netlink_ctx *ctx, struct cmd *cmd) { struct nft_ruleset *rs = netlink_dump_ruleset(ctx, &cmd->handle, @@ -760,6 +877,7 @@ static void table_cleanup(struct table *table) { struct chain *chain, *nchain; struct set *set, *nset; + struct counter *counter, *ncounter; list_for_each_entry_safe(chain, nchain, &table->chains, list) { list_del(&chain->list); @@ -770,6 +888,10 @@ static void table_cleanup(struct table *table) list_del(&set->list); set_free(set); } + list_for_each_entry_safe(counter, ncounter, &table->counters, list) { + list_del(&counter->list); + counter_free(counter); + } } static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd, @@ -780,6 +902,8 @@ static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd, if (do_list_sets(ctx, &cmd->location, table) < 0) goto err; + if (do_list_counters(ctx, &cmd->location, table) < 0) + goto err; if (netlink_list_chains(ctx, &cmd->handle, &cmd->location) < 0) goto err; list_splice_tail_init(&ctx->list, &table->chains); @@ -835,6 +959,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) { struct table *table = NULL; struct set *set; + struct counter *counter; /* No need to allocate the table object when listing all tables */ if (cmd->handle.table != NULL) { @@ -887,6 +1012,20 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) set_print(set); } return 0; + case CMD_OBJ_COUNTERS: + if (netlink_list_counters(ctx, &cmd->handle, &cmd->location) < 0) + goto err; + list_for_each_entry(counter, &ctx->list, list) { + counter_print(counter); + } + return 0; + case CMD_OBJ_COUNTER: + if (netlink_get_counter(ctx, &cmd->handle, &cmd->location) < 0) + goto err; + list_for_each_entry(counter, &ctx->list, list) { + counter_print(counter); + } + return 0; case CMD_OBJ_RULESET: return do_list_ruleset(ctx, cmd); default: diff --git a/src/scanner.l b/src/scanner.l index 73c4f8b..1a191f1 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -279,6 +279,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "counter" { return COUNTER; } "packets" { return PACKETS; } "bytes" { return BYTES; } +"name" { return NAME; } "log" { return LOG; } "prefix" { return PREFIX; } diff --git a/src/statement.c b/src/statement.c index d72c6e9..990aa0e 100644 --- a/src/statement.c +++ b/src/statement.c @@ -105,8 +105,12 @@ struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr) static void counter_stmt_print(const struct stmt *stmt) { - printf("counter packets %" PRIu64 " bytes %" PRIu64, - stmt->counter.packets, stmt->counter.bytes); + printf("counter "); + if (stmt->counter.name) + printf("%s", stmt->counter.name); + else + printf("packets %" PRIu64 " bytes %" PRIu64, + stmt->counter.packets, stmt->counter.bytes); } static const struct stmt_ops counter_stmt_ops = { -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html