summaryrefslogtreecommitdiffstats
path: root/src/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/json.c')
-rw-r--r--src/json.c516
1 files changed, 362 insertions, 154 deletions
diff --git a/src/json.c b/src/json.c
index 86028959..37530171 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1,11 +1,21 @@
-#define _GNU_SOURCE
-#include <string.h>
+/*
+ * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nft.h>
+
+#include <stdio.h>
#include <expression.h>
#include <list.h>
#include <netlink.h>
#include <rule.h>
#include <rt.h>
+#include "nftutils.h"
#include <netdb.h>
#include <netinet/icmp6.h>
@@ -42,7 +52,8 @@ static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx)
if (ops->json)
return ops->json(expr, octx);
- printf("warning: expr ops %s have no json callback\n", expr_name(expr));
+ fprintf(stderr, "warning: expr ops %s have no json callback\n",
+ expr_name(expr));
fp = octx->output_fp;
octx->output_fp = fmemopen(buf, 1024, "w");
@@ -59,33 +70,75 @@ static json_t *set_dtype_json(const struct expr *key)
{
char *namedup = xstrdup(key->dtype->name), *tok;
json_t *root = NULL;
+ char *tok_safe;
- tok = strtok(namedup, " .");
+ tok = strtok_r(namedup, " .", &tok_safe);
while (tok) {
- json_t *jtok = json_string(xstrdup(tok));
+ json_t *jtok = json_string(tok);
if (!root)
root = jtok;
else if (json_is_string(root))
root = json_pack("[o, o]", root, jtok);
else
json_array_append_new(root, jtok);
- tok = strtok(NULL, " .");
+ tok = strtok_r(NULL, " .", &tok_safe);
}
- xfree(namedup);
+ free(namedup);
return root;
}
-static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
+static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ char buf[1024];
+ FILE *fp;
+
+ if (stmt->ops->json)
+ return stmt->ops->json(stmt, octx);
+
+ fprintf(stderr, "warning: stmt ops %s have no json callback\n",
+ stmt->ops->name);
+
+ fp = octx->output_fp;
+ octx->output_fp = fmemopen(buf, 1024, "w");
+
+ stmt->ops->print(stmt, octx);
+
+ fclose(octx->output_fp);
+ octx->output_fp = fp;
+
+ return json_pack("s", buf);
+}
+
+static json_t *set_stmt_list_json(const struct list_head *stmt_list,
+ struct output_ctx *octx)
{
+ unsigned int flags = octx->flags;
json_t *root, *tmp;
- const char *type, *datatype_ext = NULL;
+ struct stmt *i;
+
+ root = json_array();
+ octx->flags |= NFT_CTX_OUTPUT_STATELESS;
+
+ list_for_each_entry(i, stmt_list, list) {
+ tmp = stmt_print_json(i, octx);
+ json_array_append_new(root, tmp);
+ }
+ octx->flags = flags;
+
+ return root;
+}
+
+static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
+{
+ json_t *root, *tmp, *datatype_ext = NULL;
+ const char *type;
if (set_is_datamap(set->flags)) {
type = "map";
- datatype_ext = set->data->dtype->name;
+ datatype_ext = set_dtype_json(set->data);
} else if (set_is_objmap(set->flags)) {
type = "map";
- datatype_ext = obj_type_name(set->objtype);
+ datatype_ext = json_string(obj_type_name(set->objtype));
} else if (set_is_meter(set->flags)) {
type = "meter";
} else {
@@ -98,8 +151,11 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
"table", set->handle.table.name,
"type", set_dtype_json(set->key),
"handle", set->handle.handle.id);
+
+ if (set->comment)
+ json_object_set_new(root, "comment", json_string(set->comment));
if (datatype_ext)
- json_object_set_new(root, "map", json_string(datatype_ext));
+ json_object_set_new(root, "map", datatype_ext);
if (!(set->flags & (NFT_SET_CONSTANT))) {
if (set->policy != NFT_SET_POL_PERFORMANCE) {
@@ -119,6 +175,8 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_array_append_new(tmp, json_pack("s", "interval"));
if (set->flags & NFT_SET_TIMEOUT)
json_array_append_new(tmp, json_pack("s", "timeout"));
+ if (set->flags & NFT_SET_EVAL)
+ json_array_append_new(tmp, json_pack("s", "dynamic"));
if (json_array_size(tmp) > 0) {
json_object_set_new(root, "flags", tmp);
@@ -136,8 +194,10 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
tmp = json_pack("i", set->gc_int / 1000);
json_object_set_new(root, "gc-interval", tmp);
}
+ if (set->automerge)
+ json_object_set_new(root, "auto-merge", json_true());
- if (set->init && set->init->size > 0) {
+ if (!nft_output_terse(octx) && set->init && set->init->size > 0) {
json_t *array = json_array();
const struct expr *i;
@@ -147,6 +207,11 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_object_set_new(root, "elem", array);
}
+ if (!list_empty(&set->stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&set->stmt_list, octx));
+ }
+
return json_pack("{s:o}", type, root);
}
@@ -163,34 +228,6 @@ static json_t *element_print_json(struct output_ctx *octx,
"elem", root);
}
-static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx)
-{
- char buf[1024];
- FILE *fp;
-
- /* XXX: Can't be supported at this point:
- * xt_stmt_xlate() ignores output_fp.
- */
- if (stmt->ops->type == STMT_XT)
- return json_pack("{s:n}", "xt");
-
- if (stmt->ops->json)
- return stmt->ops->json(stmt, octx);
-
- printf("warning: stmt ops %s have no json callback\n",
- stmt->ops->name);
-
- fp = octx->output_fp;
- octx->output_fp = fmemopen(buf, 1024, "w");
-
- stmt->ops->print(stmt, octx);
-
- fclose(octx->output_fp);
- octx->output_fp = fp;
-
- return json_pack("s", buf);
-}
-
static json_t *rule_print_json(struct output_ctx *octx,
const struct rule *rule)
{
@@ -222,9 +259,8 @@ static json_t *rule_print_json(struct output_ctx *octx,
static json_t *chain_print_json(const struct chain *chain)
{
- int priority, policy, n = 0;
- struct expr *dev, *expr;
- json_t *root, *tmp;
+ json_t *root, *tmp, *devs = NULL;
+ int priority, policy, i;
root = json_pack("{s:s, s:s, s:s, s:I}",
"family", family2str(chain->handle.family),
@@ -232,28 +268,33 @@ static json_t *chain_print_json(const struct chain *chain)
"name", chain->handle.chain.name,
"handle", chain->handle.handle.id);
+ if (chain->comment)
+ json_object_set_new(root, "comment", json_string(chain->comment));
+
if (chain->flags & CHAIN_F_BASECHAIN) {
mpz_export_data(&priority, chain->priority.expr->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
mpz_export_data(&policy, chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
tmp = json_pack("{s:s, s:s, s:i, s:s}",
- "type", chain->type,
+ "type", chain->type.str,
"hook", hooknum2str(chain->handle.family,
- chain->hooknum),
+ chain->hook.num),
"prio", priority,
"policy", chain_policy2str(policy));
- if (chain->dev_expr) {
- list_for_each_entry(expr, &chain->dev_expr->expressions, list) {
- dev = expr;
- n++;
- }
- }
- if (n == 1) {
- json_object_set_new(tmp, "dev",
- json_string(dev->identifier));
+ for (i = 0; i < chain->dev_array_len; i++) {
+ const char *dev = chain->dev_array[i];
+ if (!devs)
+ devs = json_string(dev);
+ else if (json_is_string(devs))
+ devs = json_pack("[o, s]", devs, dev);
+ else
+ json_array_append_new(devs, json_string(dev));
}
+ if (devs)
+ json_object_set_new(root, "dev", devs);
+
json_object_update(root, tmp);
json_decref(tmp);
}
@@ -263,10 +304,10 @@ static json_t *chain_print_json(const struct chain *chain)
static json_t *proto_name_json(uint8_t proto)
{
- const struct protoent *p = getprotobynumber(proto);
+ char name[NFT_PROTONAME_MAXSIZE];
- if (p)
- return json_string(p->p_name);
+ if (nft_getprotobynumber(proto, name, sizeof(name)))
+ return json_string(name);
return json_integer(proto);
}
@@ -300,6 +341,12 @@ static json_t *obj_print_json(const struct obj *obj)
"table", obj->handle.table.name,
"handle", obj->handle.handle.id);
+ if (obj->comment) {
+ tmp = json_pack("{s:s}", "comment", obj->comment);
+ json_object_update(root, tmp);
+ json_decref(tmp);
+ }
+
switch (obj->type) {
case NFT_OBJECT_COUNTER:
tmp = json_pack("{s:I, s:I}",
@@ -415,7 +462,7 @@ static json_t *flowtable_print_json(const struct flowtable *ftable)
"name", ftable->handle.flowtable.name,
"table", ftable->handle.table.name,
"handle", ftable->handle.handle.id,
- "hook", hooknum2str(NFPROTO_NETDEV, ftable->hooknum),
+ "hook", hooknum2str(NFPROTO_NETDEV, ftable->hook.num),
"prio", priority);
for (i = 0; i < ftable->dev_array_len; i++) {
@@ -441,7 +488,7 @@ static json_t *table_flags_json(const struct table *table)
while (flags) {
if (flags & 0x1) {
- tmp = json_string(table_flags_name[i]);
+ tmp = json_string(table_flag_name(i));
json_array_append_new(root, tmp);
}
flags >>= 1;
@@ -452,7 +499,7 @@ static json_t *table_flags_json(const struct table *table)
json_decref(root);
return NULL;
case 1:
- json_unpack(root, "[o]", &tmp);
+ json_unpack(root, "[O]", &tmp);
json_decref(root);
root = tmp;
break;
@@ -473,14 +520,44 @@ static json_t *table_print_json(const struct table *table)
if (tmp)
json_object_set_new(root, "flags", tmp);
+ if (table->comment)
+ json_object_set_new(root, "comment", json_string(table->comment));
+
return json_pack("{s:o}", "table", root);
}
+json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx)
+{
+ json_t *left;
+
+ left = json_pack("{s:[o, o]}", expr_op_symbols[OP_AND],
+ expr_print_json(expr->flagcmp.expr, octx),
+ expr_print_json(expr->flagcmp.mask, octx));
+
+ return json_pack("{s:{s:s, s:o, s:o}}", "match",
+ "op", expr_op_symbols[expr->op] ? : "in",
+ "left", left,
+ "right", expr_print_json(expr->flagcmp.value, octx));
+}
+
+static json_t *
+__binop_expr_json(int op, const struct expr *expr, struct output_ctx *octx)
+{
+ json_t *a = json_array();
+
+ if (expr->etype == EXPR_BINOP && expr->op == op) {
+ json_array_extend(a, __binop_expr_json(op, expr->left, octx));
+ json_array_extend(a, __binop_expr_json(op, expr->right, octx));
+ } else {
+ json_array_append_new(a, expr_print_json(expr, octx));
+ }
+ return a;
+}
+
json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx)
{
- return json_pack("{s:[o, o]}", expr_op_symbols[expr->op],
- expr_print_json(expr->left, octx),
- expr_print_json(expr->right, octx));
+ return json_pack("{s:o}", expr_op_symbols[expr->op],
+ __binop_expr_json(expr->op, expr, octx));
}
json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -516,15 +593,23 @@ json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx)
{
json_t *root;
- if (payload_is_known(expr))
- root = json_pack("{s:s, s:s}",
- "protocol", expr->payload.desc->name,
- "field", expr->payload.tmpl->token);
- else
+ if (payload_is_known(expr)) {
+ if (expr->payload.inner_desc) {
+ root = json_pack("{s:s, s:s, s:s}",
+ "tunnel", expr->payload.inner_desc->name,
+ "protocol", expr->payload.desc->name,
+ "field", expr->payload.tmpl->token);
+ } else {
+ root = json_pack("{s:s, s:s}",
+ "protocol", expr->payload.desc->name,
+ "field", expr->payload.tmpl->token);
+ }
+ } else {
root = json_pack("{s:s, s:i, s:i}",
"base", proto_base_tokens[expr->payload.base],
"offset", expr->payload.offset,
"len", expr->len);
+ }
return json_pack("{s:o}", "payload", root);
}
@@ -580,13 +665,15 @@ json_t *set_ref_expr_json(const struct expr *expr, struct output_ctx *octx)
json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
{
json_t *root = expr_print_json(expr->key, octx);
+ struct stmt *stmt;
json_t *tmp;
if (!root)
return NULL;
/* these element attributes require formal set elem syntax */
- if (expr->timeout || expr->expiration || expr->comment) {
+ if (expr->timeout || expr->expiration || expr->comment ||
+ !list_empty(&expr->stmt_list)) {
root = json_pack("{s:o}", "val", root);
if (expr->timeout) {
@@ -601,6 +688,14 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
tmp = json_string(expr->comment);
json_object_set_new(root, "comment", tmp);
}
+ list_for_each_entry(stmt, &expr->stmt_list, list) {
+ tmp = stmt_print_json(stmt, octx);
+ /* XXX: detect and complain about clashes? */
+ json_object_update_missing(root, tmp);
+ json_decref(tmp);
+ /* TODO: only one statement per element. */
+ break;
+ }
return json_pack("{s:o}", "elem", root);
}
@@ -656,47 +751,51 @@ json_t *map_expr_json(const struct expr *expr, struct output_ctx *octx)
json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx)
{
const char *desc = expr->exthdr.desc ?
- expr->exthdr.desc->name :
- "unknown-exthdr";
+ expr->exthdr.desc->name : NULL;
const char *field = expr->exthdr.tmpl->token;
json_t *root;
bool is_exists = expr->exthdr.flags & NFT_EXTHDR_F_PRESENT;
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
+ static const char *offstrs[] = { "", "1", "2", "3" };
unsigned int offset = expr->exthdr.offset / 64;
+ const char *offstr = "";
- if (offset) {
- const char *offstrs[] = { "0", "1", "2", "3" };
- const char *offstr = "";
-
+ if (desc) {
if (offset < 4)
offstr = offstrs[offset];
root = json_pack("{s:s+}", "name", desc, offstr);
+
+ if (!is_exists)
+ json_object_set_new(root, "field", json_string(field));
} else {
- root = json_pack("{s:s}", "name", desc);
+ root = json_pack("{s:i, s:i, s:i}",
+ "base", expr->exthdr.raw_type,
+ "offset", expr->exthdr.offset,
+ "len", expr->len);
}
- if (!is_exists)
- json_object_set_new(root, "field", json_string(field));
-
return json_pack("{s:o}", "tcp option", root);
}
- if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
- root = json_pack("{s:s}", "name", desc);
-
- if (!is_exists)
- json_object_set_new(root, "field", json_string(field));
- return json_pack("{s:o}", "ip option", root);
+ if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) {
+ root = json_pack("{s:i}", "type", expr->exthdr.raw_type);
+ return json_pack("{s:o}", "dccp option", root);
}
- root = json_pack("{s:s}",
- "name", desc);
+ root = json_pack("{s:s}", "name", desc);
if (!is_exists)
json_object_set_new(root, "field", json_string(field));
- return json_pack("{s:o}", "exthdr", root);
+ switch (expr->exthdr.op) {
+ case NFT_EXTHDR_OP_IPV4:
+ return json_pack("{s:o}", "ip option", root);
+ case NFT_EXTHDR_OP_SCTP:
+ return json_pack("{s:o}", "sctp chunk", root);
+ default:
+ return json_pack("{s:o}", "exthdr", root);
+ }
}
json_t *verdict_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -862,6 +961,11 @@ static json_t *symbolic_constant_json(const struct symbol_table *tbl,
return json_string(s->identifier);
}
+json_t *set_elem_catchall_expr_json(const struct expr *expr, struct output_ctx *octx)
+{
+ return json_string("*");
+}
+
static json_t *datatype_json(const struct expr *expr, struct output_ctx *octx)
{
const struct datatype *dtype = expr->dtype;
@@ -1009,12 +1113,11 @@ json_t *boolean_type_json(const struct expr *expr, struct output_ctx *octx)
json_t *inet_protocol_type_json(const struct expr *expr,
struct output_ctx *octx)
{
- struct protoent *p;
-
if (!nft_output_numeric_proto(octx)) {
- p = getprotobynumber(mpz_get_uint8(expr->value));
- if (p != NULL)
- return json_string(p->p_name);
+ char name[NFT_PROTONAME_MAXSIZE];
+
+ if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name)))
+ return json_string(name);
}
return integer_type_json(expr, octx);
}
@@ -1022,13 +1125,13 @@ json_t *inet_protocol_type_json(const struct expr *expr,
json_t *inet_service_type_json(const struct expr *expr, struct output_ctx *octx)
{
uint16_t port = mpz_get_be16(expr->value);
- const struct servent *s = NULL;
+ char name[NFT_SERVNAME_MAXSIZE];
if (!nft_output_service(octx) ||
- (s = getservbyport(port, NULL)) == NULL)
+ !nft_getservbyport(port, NULL, name, sizeof(name)))
return json_integer(ntohs(port));
- return json_string(s->s_name);
+ return json_string(name);
}
json_t *mark_type_json(const struct expr *expr, struct output_ctx *octx)
@@ -1089,6 +1192,13 @@ json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return expr_print_json(stmt->expr, octx);
}
+json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ return json_pack("{s:{s:s, s:s+}}", "flow",
+ "op", "add", "flowtable",
+ "@", stmt->flow.table_name);
+}
+
json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
return json_pack("{s: {s:o, s:o}}", "mangle",
@@ -1153,19 +1263,17 @@ json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
burst_unit = get_rate(stmt->limit.burst, &burst);
}
- root = json_pack("{s:I, s:s}",
+ root = json_pack("{s:I, s:I, s:s}",
"rate", rate,
+ "burst", burst,
"per", get_unit(stmt->limit.unit));
if (inv)
json_object_set_new(root, "inv", json_boolean(inv));
if (rate_unit)
json_object_set_new(root, "rate_unit", json_string(rate_unit));
- if (burst && burst != 5) {
- json_object_set_new(root, "burst", json_integer(burst));
- if (burst_unit)
- json_object_set_new(root, "burst_unit",
- json_string(burst_unit));
- }
+ if (burst_unit)
+ json_object_set_new(root, "burst_unit",
+ json_string(burst_unit));
return json_pack("{s:o}", "limit", root);
}
@@ -1224,9 +1332,12 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root = json_object(), *flags;
- if (stmt->log.flags & STMT_LOG_PREFIX)
- json_object_set_new(root, "prefix",
- json_string(stmt->log.prefix));
+ if (stmt->log.flags & STMT_LOG_PREFIX) {
+ char prefix[NF_LOG_PREFIXLEN] = {};
+
+ expr_to_string(stmt->log.prefix, prefix);
+ json_object_set_new(root, "prefix", json_string(prefix));
+ }
if (stmt->log.flags & STMT_LOG_GROUP)
json_object_set_new(root, "group",
json_integer(stmt->log.group));
@@ -1276,7 +1387,7 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return json_pack("{s:o}", "log", root);
}
-static json_t *nat_flags_json(int flags)
+static json_t *nat_flags_json(uint32_t flags)
{
json_t *array = json_array();
@@ -1286,9 +1397,33 @@ static json_t *nat_flags_json(int flags)
json_array_append_new(array, json_string("fully-random"));
if (flags & NF_NAT_RANGE_PERSISTENT)
json_array_append_new(array, json_string("persistent"));
+ if (flags & NF_NAT_RANGE_NETMAP)
+ json_array_append_new(array, json_string("netmap"));
+ return array;
+}
+
+static json_t *nat_type_flags_json(uint32_t type_flags)
+{
+ json_t *array = json_array();
+
+ if (type_flags & STMT_NAT_F_PREFIX)
+ json_array_append_new(array, json_string("prefix"));
+
return array;
}
+static void nat_stmt_add_array(json_t *root, const char *name, json_t *array)
+{
+ if (json_array_size(array) > 1) {
+ json_object_set_new(root, name, array);
+ } else {
+ if (json_array_size(array))
+ json_object_set(root, name,
+ json_array_get(array, 0));
+ json_decref(array);
+ }
+}
+
json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root = json_object();
@@ -1310,13 +1445,12 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_object_set_new(root, "port",
expr_print_json(stmt->nat.proto, octx));
- if (json_array_size(array) > 1) {
- json_object_set_new(root, "flags", array);
- } else {
- if (json_array_size(array))
- json_object_set(root, "flags",
- json_array_get(array, 0));
- json_decref(array);
+ nat_stmt_add_array(root, "flags", array);
+
+ if (stmt->nat.type_flags) {
+ array = nat_type_flags_json(stmt->nat.type_flags);
+
+ nat_stmt_add_array(root, "type_flags", array);
}
if (!json_object_size(root)) {
@@ -1337,24 +1471,16 @@ json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
type = "tcp reset";
break;
case NFT_REJECT_ICMPX_UNREACH:
- if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)
- break;
type = "icmpx";
jexpr = expr_print_json(stmt->reject.expr, octx);
break;
case NFT_REJECT_ICMP_UNREACH:
switch (stmt->reject.family) {
case NFPROTO_IPV4:
- if (!stmt->reject.verbose_print &&
- stmt->reject.icmp_code == ICMP_PORT_UNREACH)
- break;
type = "icmp";
jexpr = expr_print_json(stmt->reject.expr, octx);
break;
case NFPROTO_IPV6:
- if (!stmt->reject.verbose_print &&
- stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
- break;
type = "icmpv6";
jexpr = expr_print_json(stmt->reject.expr, octx);
break;
@@ -1383,12 +1509,49 @@ json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
"bytes", stmt->counter.bytes);
}
+json_t *last_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ if (nft_output_stateless(octx) || stmt->last.set == 0)
+ return json_pack("{s:n}", "last");
+
+ return json_pack("{s:{s:I}}", "last", "used", stmt->last.used);
+}
+
json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- return json_pack("{s:{s:s, s:o, s:s+}}", "set",
+ json_t *root;
+
+ root = json_pack("{s:s, s:o, s:s+}",
"op", set_stmt_op_names[stmt->set.op],
"elem", expr_print_json(stmt->set.key, octx),
"set", "@", stmt->set.set->set->handle.set.name);
+
+ if (!list_empty(&stmt->set.stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&stmt->set.stmt_list,
+ octx));
+ }
+
+ return json_pack("{s:o}", "set", root);
+}
+
+json_t *map_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ json_t *root;
+
+ root = json_pack("{s:s, s:o, s:o, s:s+}",
+ "op", set_stmt_op_names[stmt->map.op],
+ "elem", expr_print_json(stmt->map.key, octx),
+ "data", expr_print_json(stmt->map.data, octx),
+ "map", "@", stmt->map.set->set->handle.set.name);
+
+ if (!list_empty(&stmt->map.stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&stmt->map.stmt_list,
+ octx));
+ }
+
+ return json_pack("{s:o}", "map", root);
}
json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1522,10 +1685,29 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return json_pack("{s:o}", "synproxy", root);
}
+json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ return json_pack("{s:o}", "reset",
+ expr_print_json(stmt->optstrip.expr, octx));
+}
+
+json_t *xt_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ static const char *xt_typename[NFT_XT_MAX] = {
+ [NFT_XT_MATCH] = "match",
+ [NFT_XT_TARGET] = "target",
+ [NFT_XT_WATCHER] = "watcher",
+ };
+
+ return json_pack("{s:{s:s, s:s}}", "xt",
+ "type", xt_typename[stmt->xt.type],
+ "name", stmt->xt.name);
+}
+
static json_t *table_print_json_full(struct netlink_ctx *ctx,
struct table *table)
{
- json_t *root = json_array(), *tmp;
+ json_t *root = json_array(), *rules = json_array(), *tmp;
struct flowtable *flowtable;
struct chain *chain;
struct rule *rule;
@@ -1535,45 +1717,52 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
tmp = table_print_json(table);
json_array_append_new(root, tmp);
- list_for_each_entry(obj, &table->objs, list) {
+ /* both maps and rules may refer to chains, list them first */
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
+ tmp = chain_print_json(chain);
+ json_array_append_new(root, tmp);
+ }
+ list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
tmp = obj_print_json(obj);
json_array_append_new(root, tmp);
}
- list_for_each_entry(set, &table->sets, list) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
if (set_is_anonymous(set->flags))
continue;
tmp = set_print_json(&ctx->nft->output, set);
json_array_append_new(root, tmp);
}
- list_for_each_entry(flowtable, &table->flowtables, list) {
+ list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) {
tmp = flowtable_print_json(flowtable);
json_array_append_new(root, tmp);
}
- list_for_each_entry(chain, &table->chains, list) {
- tmp = chain_print_json(chain);
- json_array_append_new(root, tmp);
-
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
list_for_each_entry(rule, &chain->rules, list) {
tmp = rule_print_json(&ctx->nft->output, rule);
- json_array_append_new(root, tmp);
+ json_array_append_new(rules, tmp);
}
}
+ json_array_extend(root, rules);
+ json_decref(rules);
+
return root;
}
static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
{
unsigned int family = cmd->handle.family;
- json_t *root = json_array();
+ json_t *root = json_array(), *tmp;
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (family != NFPROTO_UNSPEC &&
table->handle.family != family)
continue;
- json_array_extend(root, table_print_json_full(ctx, table));
+ tmp = table_print_json_full(ctx, table);
+ json_array_extend(root, tmp);
+ json_decref(tmp);
}
return root;
@@ -1585,7 +1774,7 @@ static json_t *do_list_tables_json(struct netlink_ctx *ctx, struct cmd *cmd)
json_t *root = json_array();
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (family != NFPROTO_UNSPEC &&
table->handle.family != family)
continue;
@@ -1609,7 +1798,7 @@ static json_t *do_list_chain_json(struct netlink_ctx *ctx,
struct chain *chain;
struct rule *rule;
- list_for_each_entry(chain, &table->chains, list) {
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
if (chain->handle.family != cmd->handle.family ||
strcmp(cmd->handle.chain.name, chain->handle.chain.name))
continue;
@@ -1632,12 +1821,12 @@ static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd)
struct table *table;
struct chain *chain;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
- list_for_each_entry(chain, &table->chains, list) {
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
json_t *tmp = chain_print_json(chain);
json_array_append_new(root, tmp);
@@ -1650,10 +1839,13 @@ static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd)
static json_t *do_list_set_json(struct netlink_ctx *ctx,
struct cmd *cmd, struct table *table)
{
- struct set *set = set_lookup(table, cmd->handle.set.name);
+ struct set *set = cmd->set;
- if (set == NULL)
- return json_null();
+ if (!set) {
+ set = set_cache_find(table, cmd->handle.set.name);
+ if (set == NULL)
+ return json_null();
+ }
return json_pack("[o]", set_print_json(&ctx->nft->output, set));
}
@@ -1665,12 +1857,12 @@ static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd)
struct table *table;
struct set *set;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
- list_for_each_entry(set, &table->sets, list) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
if (cmd->obj == CMD_OBJ_SETS &&
!set_is_literal(set->flags))
continue;
@@ -1694,7 +1886,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx,
struct table *table;
struct obj *obj;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
@@ -1703,7 +1895,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx,
strcmp(cmd->handle.table.name, table->handle.table.name))
continue;
- list_for_each_entry(obj, &table->objs, list) {
+ list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
if (obj->type != type ||
(cmd->handle.obj.name &&
strcmp(cmd->handle.obj.name, obj->handle.obj.name)))
@@ -1722,8 +1914,8 @@ static json_t *do_list_flowtable_json(struct netlink_ctx *ctx,
json_t *root = json_array();
struct flowtable *ft;
- ft = flowtable_lookup(table, cmd->handle.flowtable.name);
- if (ft == NULL)
+ ft = ft_cache_find(table, cmd->handle.flowtable.name);
+ if (!ft)
return json_null();
json_array_append_new(root, flowtable_print_json(ft));
@@ -1737,12 +1929,12 @@ static json_t *do_list_flowtables_json(struct netlink_ctx *ctx, struct cmd *cmd)
struct flowtable *flowtable;
struct table *table;
- list_for_each_entry(table, &ctx->nft->cache.list, list) {
+ list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (cmd->handle.family != NFPROTO_UNSPEC &&
cmd->handle.family != table->handle.family)
continue;
- list_for_each_entry(flowtable, &table->flowtables, list) {
+ list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) {
tmp = flowtable_print_json(flowtable);
json_array_append_new(root, tmp);
}
@@ -1765,7 +1957,9 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
json_t *root;
if (cmd->handle.table.name)
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name,
+ cmd->handle.family);
switch (cmd->obj) {
case CMD_OBJ_TABLE:
@@ -1787,6 +1981,7 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SET:
root = do_list_set_json(ctx, cmd, table);
break;
+ case CMD_OBJ_RULES:
case CMD_OBJ_RULESET:
root = do_list_ruleset_json(ctx, cmd);
break;
@@ -1852,9 +2047,15 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
static void monitor_print_json(struct netlink_mon_handler *monh,
const char *cmd, json_t *obj)
{
+ struct nft_ctx *nft = monh->ctx->nft;
+
obj = json_pack("{s:o}", cmd, obj);
- json_dumpf(obj, monh->ctx->nft->output.output_fp, 0);
- json_decref(obj);
+ if (nft_output_echo(&nft->output) && !nft->json_root) {
+ json_array_append_new(nft->json_echo, obj);
+ } else {
+ json_dumpf(obj, nft->output.output_fp, 0);
+ json_decref(obj);
+ }
}
void monitor_print_table_json(struct netlink_mon_handler *monh,
@@ -1898,3 +2099,10 @@ void monitor_print_rule_json(struct netlink_mon_handler *monh,
monitor_print_json(monh, cmd, rule_print_json(octx, r));
}
+
+void json_alloc_echo(struct nft_ctx *nft)
+{
+ nft->json_echo = json_array();
+ if (!nft->json_echo)
+ memory_allocation_error();
+}