summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/evaluate.c2
-rw-r--r--src/mnl.c59
-rw-r--r--src/netlink.c67
-rw-r--r--src/parser_bison.y30
-rw-r--r--src/rule.c140
-rw-r--r--src/scanner.l3
-rw-r--r--src/statement.c2
7 files changed, 300 insertions, 3 deletions
diff --git a/src/evaluate.c b/src/evaluate.c
index 557c61c8..b3630c30 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2845,6 +2845,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
return 0;
case CMD_OBJ_CHAINS:
case CMD_OBJ_SETS:
+ case CMD_OBJ_COUNTERS:
+ case CMD_OBJ_QUOTAS:
case CMD_OBJ_RULESET:
case CMD_OBJ_FLOWTABLES:
case CMD_OBJ_MAPS:
diff --git a/src/mnl.c b/src/mnl.c
index 257b630e..534d02f4 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/object.h>
#include <libnftnl/batch.h>
#include <linux/netfilter/nfnetlink.h>
@@ -795,6 +796,64 @@ err:
return NULL;
}
+static int obj_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nftnl_obj_list *nln_list = data;
+ struct nftnl_obj *n;
+
+ if (check_genid(nlh) < 0)
+ return MNL_CB_ERROR;
+
+ n = nftnl_obj_alloc();
+ if (n == NULL)
+ memory_allocation_error();
+
+ if (nftnl_obj_nlmsg_parse(nlh, n) < 0)
+ goto err_free;
+
+ nftnl_obj_list_add_tail(n, nln_list);
+ return MNL_CB_OK;
+
+err_free:
+ nftnl_obj_free(n);
+ return MNL_CB_OK;
+}
+
+
+struct nftnl_obj_list *
+mnl_nft_obj_dump(struct mnl_socket *nf_sock, int family, const char *table)
+{
+ struct nftnl_obj_list *nln_list;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nftnl_obj *n;
+ struct nlmsghdr *nlh;
+ int ret;
+
+ n = nftnl_obj_alloc();
+ if (n == NULL)
+ memory_allocation_error();
+
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETOBJ, family,
+ NLM_F_DUMP | NLM_F_ACK, seq);
+ if (table != NULL)
+ nftnl_obj_set(n, NFTNL_OBJ_TABLE, table);
+ nftnl_obj_nlmsg_build_payload(nlh, n);
+ nftnl_obj_free(n);
+
+ nln_list = nftnl_obj_list_alloc();
+ if (nln_list == NULL)
+ memory_allocation_error();
+
+ ret = nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, obj_cb, nln_list);
+ if (ret < 0)
+ goto err;
+
+ return nln_list;
+err:
+ nftnl_obj_list_free(nln_list);
+ return NULL;
+}
+
static int set_get_cb(const struct nlmsghdr *nlh, void *data)
{
struct nftnl_set *s = data;
diff --git a/src/netlink.c b/src/netlink.c
index e37d3bf1..bbf675f9 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1608,6 +1608,73 @@ out:
return err;
}
+static struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
+ struct nftnl_obj *nlo)
+{
+ struct obj *obj;
+ uint32_t type;
+
+ obj = obj_alloc(&netlink_location);
+ obj->handle.family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY);
+ obj->handle.table =
+ xstrdup(nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE));
+ obj->handle.obj =
+ xstrdup(nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME));
+
+ type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE);
+ switch (type) {
+ case NFT_OBJECT_COUNTER:
+ obj->counter.packets =
+ nftnl_obj_get_u64(nlo, NFTNL_OBJ_CTR_PKTS);
+ obj->counter.bytes =
+ nftnl_obj_get_u64(nlo, NFTNL_OBJ_CTR_BYTES);
+ break;
+ case NFT_OBJECT_QUOTA:
+ obj->quota.bytes =
+ nftnl_obj_get_u64(nlo, NFTNL_OBJ_QUOTA_BYTES);
+ obj->quota.used =
+ nftnl_obj_get_u64(nlo, NFTNL_OBJ_QUOTA_CONSUMED);
+ obj->quota.flags =
+ nftnl_obj_get_u32(nlo, NFTNL_OBJ_QUOTA_FLAGS);
+ }
+ obj->type = type;
+
+ return obj;
+}
+
+static int list_obj_cb(struct nftnl_obj *nls, void *arg)
+{
+ struct netlink_ctx *ctx = arg;
+ struct obj *obj;
+
+ obj = netlink_delinearize_obj(ctx, nls);
+ if (obj == NULL)
+ return -1;
+ list_add_tail(&obj->list, &ctx->list);
+ return 0;
+}
+
+int netlink_list_objs(struct netlink_ctx *ctx, const struct handle *h,
+ const struct location *loc)
+{
+ struct nftnl_obj_list *obj_cache;
+ int err;
+
+ obj_cache = mnl_nft_obj_dump(nf_sock, h->family, h->table);
+ if (obj_cache == NULL) {
+ if (errno == EINTR)
+ return -1;
+
+ return netlink_io_error(ctx, loc,
+ "Could not receive stateful object from kernel: %s",
+ strerror(errno));
+ }
+
+ err = nftnl_obj_list_foreach(obj_cache, list_obj_cb, ctx);
+ nftnl_obj_list_free(obj_cache);
+ return err;
+}
+
int netlink_batch_send(struct list_head *err_list)
{
return mnl_batch_talk(nf_sock, err_list);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index aea6e47d..2213f380 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -364,6 +364,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%token PACKETS "packets"
%token BYTES "bytes"
+%token COUNTERS "counters"
+%token QUOTAS "quotas"
+
%token LOG "log"
%token PREFIX "prefix"
%token GROUP "group"
@@ -441,8 +444,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <handle> table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
%destructor { handle_free(&$$); } table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
-%type <handle> set_spec set_identifier
-%destructor { handle_free(&$$); } set_spec set_identifier
+%type <handle> set_spec set_identifier obj_spec
+%destructor { handle_free(&$$); } set_spec set_identifier obj_spec
%type <val> family_spec family_spec_explicit chain_policy prio_spec
%type <string> dev_spec quota_unit
@@ -872,6 +875,22 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, &@$, NULL);
}
+ | COUNTERS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
+ }
+ | COUNTER obj_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
+ | QUOTAS ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$2, &@$, NULL);
+ }
+ | QUOTA obj_spec
+ {
+ $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
| RULESET ruleset_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);
@@ -1299,6 +1318,13 @@ set_identifier : identifier
}
;
+obj_spec : table_spec identifier
+ {
+ $$ = $1;
+ $$.obj = $2;
+ }
+ ;
+
handle_spec : HANDLE NUM
{
memset(&$$, 0, sizeof($$));
diff --git a/src/rule.c b/src/rule.c
index 988305b5..05f0eb87 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -93,6 +93,12 @@ static int cache_init_objects(struct netlink_ctx *ctx, enum cmd_ops cmd)
return -1;
list_splice_tail_init(&ctx->list, &table->chains);
+ /* Don't check for errors on listings, this would break nft with
+ * old kernels with no stateful object support.
+ */
+ netlink_list_objs(ctx, &table->handle, &internal_location);
+ list_splice_tail_init(&ctx->list, &table->objs);
+
/* Skip caching other objects to speed up things: We only need
* a full cache when listing the existing ruleset.
*/
@@ -688,6 +694,7 @@ struct table *table_alloc(void)
table = xzalloc(sizeof(*table));
init_list_head(&table->chains);
init_list_head(&table->sets);
+ init_list_head(&table->objs);
init_list_head(&table->scope.symbols);
table->refcnt = 1;
@@ -762,6 +769,7 @@ static void table_print_options(const struct table *table, const char **delim)
static void table_print(const struct table *table)
{
struct chain *chain;
+ struct obj *obj;
struct set *set;
const char *delim = "";
const char *family = family2str(table->handle.family);
@@ -769,6 +777,11 @@ static void table_print(const struct table *table)
printf("table %s %s {\n", family, table->handle.table);
table_print_options(table, &delim);
+ list_for_each_entry(obj, &table->objs, list) {
+ printf("%s", delim);
+ obj_print(obj);
+ delim = "\n";
+ }
list_for_each_entry(set, &table->sets, list) {
if (set->flags & NFT_SET_ANONYMOUS)
continue;
@@ -1098,6 +1111,129 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
return 0;
}
+struct obj *obj_alloc(const struct location *loc)
+{
+ struct obj *obj;
+
+ obj = xzalloc(sizeof(*obj));
+ if (loc != NULL)
+ obj->location = *loc;
+ return obj;
+}
+
+void obj_free(struct obj *obj)
+{
+ handle_free(&obj->handle);
+ xfree(obj);
+}
+
+void obj_add_hash(struct obj *obj, struct table *table)
+{
+ list_add_tail(&obj->list, &table->objs);
+}
+
+static void obj_print_data(const struct obj *obj,
+ struct print_fmt_options *opts)
+{
+ switch (obj->type) {
+ case NFT_OBJECT_COUNTER:
+ printf(" %s {%s%s%s", obj->handle.obj,
+ opts->nl, opts->tab, opts->tab);
+ printf("packets %"PRIu64" bytes %"PRIu64"",
+ obj->counter.packets, obj->counter.bytes);
+ break;
+ case NFT_OBJECT_QUOTA: {
+ const char *data_unit;
+ uint64_t bytes;
+
+ printf(" %s {%s%s%s", obj->handle.obj,
+ opts->nl, opts->tab, opts->tab);
+ data_unit = get_rate(obj->quota.bytes, &bytes);
+ printf("%s%"PRIu64" %s",
+ obj->quota.flags & NFT_QUOTA_F_INV ? "over " : "",
+ bytes, data_unit);
+ if (obj->quota.used) {
+ data_unit = get_rate(obj->quota.used, &bytes);
+ printf(" used %"PRIu64" %s", bytes, data_unit);
+ }
+ }
+ break;
+ default:
+ printf("unknown {%s", opts->nl);
+ break;
+ }
+}
+
+static const char *obj_type_name_array[] = {
+ [NFT_OBJECT_COUNTER] = "counter",
+ [NFT_OBJECT_QUOTA] = "quota",
+};
+
+const char *obj_type_name(enum stmt_types type)
+{
+ assert(type <= NFT_OBJECT_QUOTA && obj_type_name_array[type]);
+
+ return obj_type_name_array[type];
+}
+
+static void obj_print_declaration(const struct obj *obj,
+ struct print_fmt_options *opts)
+{
+ printf("%s%s", opts->tab, obj_type_name(obj->type));
+
+ if (opts->family != NULL)
+ printf(" %s", opts->family);
+
+ if (opts->table != NULL)
+ printf(" %s", opts->table);
+
+ obj_print_data(obj, opts);
+
+ printf("%s%s}%s", opts->nl, opts->tab, opts->nl);
+}
+
+void obj_print(const struct obj *obj)
+{
+ struct print_fmt_options opts = {
+ .tab = "\t",
+ .nl = "\n",
+ .stmt_separator = "\n",
+ };
+
+ obj_print_declaration(obj, &opts);
+}
+
+static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type)
+{
+ struct print_fmt_options opts = {
+ .tab = "\t",
+ .nl = "\n",
+ .stmt_separator = "\n",
+ };
+ struct table *table;
+ struct obj *obj;
+
+ list_for_each_entry(table, &table_list, list) {
+ if (cmd->handle.family != NFPROTO_UNSPEC &&
+ cmd->handle.family != table->handle.family)
+ continue;
+
+ printf("table %s %s {\n",
+ family2str(table->handle.family),
+ table->handle.table);
+
+ list_for_each_entry(obj, &table->objs, list) {
+ if (obj->type != type)
+ continue;
+
+ obj_print_declaration(obj, &opts);
+ }
+
+ printf("}\n");
+ }
+ return 0;
+}
+
static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd)
{
unsigned int family = cmd->handle.family;
@@ -1230,6 +1366,10 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
return do_list_sets(ctx, cmd);
case CMD_OBJ_MAP:
return do_list_set(ctx, cmd, table);
+ case CMD_OBJ_COUNTERS:
+ return do_list_obj(ctx, cmd, NFT_OBJECT_COUNTER);
+ case CMD_OBJ_QUOTAS:
+ return do_list_obj(ctx, cmd, NFT_OBJECT_QUOTA);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
diff --git a/src/scanner.l b/src/scanner.l
index 8aa4b08b..cb989320 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -293,6 +293,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"packets" { return PACKETS; }
"bytes" { return BYTES; }
+"counters" { return COUNTERS; }
+"quotas" { return QUOTAS; }
+
"log" { return LOG; }
"prefix" { return PREFIX; }
"group" { return GROUP; }
diff --git a/src/statement.c b/src/statement.c
index 4d3ca55a..fbd78aaf 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -255,7 +255,7 @@ static const char *data_unit[] = {
NULL
};
-static const char *get_rate(uint64_t byte_rate, uint64_t *rate)
+const char *get_rate(uint64_t byte_rate, uint64_t *rate)
{
uint64_t res, prev, rest;
int i;