diff options
Diffstat (limited to 'src/json.c')
-rw-r--r-- | src/json.c | 513 |
1 files changed, 362 insertions, 151 deletions
@@ -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> @@ -32,6 +42,15 @@ }) #endif +static int json_array_extend_new(json_t *array, json_t *other_array) +{ + int ret; + + ret = json_array_extend(array, other_array); + json_decref(other_array); + return ret; +} + static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx) { const struct expr_ops *ops; @@ -42,7 +61,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 +79,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 +160,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 +184,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 +203,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 +216,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 +237,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 +268,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 +277,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->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 +313,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 +350,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}", @@ -441,7 +497,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 +508,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 +529,46 @@ 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_new(a, + __binop_expr_json(op, expr->left, octx)); + json_array_extend_new(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 +604,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 +676,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 +699,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 +762,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 +972,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 +1124,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 +1136,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 +1203,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 +1274,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); } @@ -1279,7 +1398,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(); @@ -1289,9 +1408,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(); @@ -1313,13 +1456,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)) { @@ -1340,24 +1482,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; @@ -1386,12 +1520,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) @@ -1525,10 +1696,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; @@ -1538,47 +1728,49 @@ 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_new(root, 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(), *tmp; + 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; - tmp = table_print_json_full(ctx, table); - json_array_extend(root, tmp); - json_decref(tmp); + json_array_extend_new(root, table_print_json_full(ctx, table)); } return root; @@ -1590,7 +1782,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; @@ -1614,7 +1806,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; @@ -1637,12 +1829,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); @@ -1655,10 +1847,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)); } @@ -1670,12 +1865,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; @@ -1699,7 +1894,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; @@ -1708,7 +1903,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))) @@ -1727,8 +1922,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)); @@ -1742,12 +1937,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); } @@ -1770,7 +1965,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: @@ -1792,6 +1989,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; @@ -1857,9 +2055,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, @@ -1903,3 +2107,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(); +} |