@@ -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)
@@ -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,
@@ -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);
@@ -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;
@@ -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);
@@ -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),
},
};
@@ -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)
@@ -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);
@@ -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);
}
@@ -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);
}
@@ -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
@@ -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:
@@ -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; }
@@ -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 = {