Although technically there already is support for JSON output via 'nft export json' command, it is hardly useable since it exports all the gory details of nftables VM. Also, libnftables has no control over what is exported since the content comes directly from libnftnl. Instead, implement JSON format support for regular 'nft list' commands. Signed-off-by: Phil Sutter <phil@xxxxxx> --- Changes since v1: - Fix numerical uid/gid output (use a JSON integer instead of string). Changes since v2: - Implement json_pack_assert() as a macro. This way triggered assert() prints a useful line number. - Fix for changes in struct handle. --- configure.ac | 14 +- include/datatype.h | 4 + include/expression.h | 3 + include/gmputil.h | 1 + include/json.h | 161 ++++ include/nftables.h | 1 + include/nftables/libnftables.h | 2 + include/statement.h | 3 + src/Makefile.am | 5 + src/ct.c | 4 + src/datatype.c | 8 + src/expression.c | 15 + src/exthdr.c | 2 + src/fib.c | 1 + src/hash.c | 1 + src/json.c | 1564 ++++++++++++++++++++++++++++++++ src/libnftables.c | 16 + src/main.c | 11 +- src/meta.c | 6 + src/numgen.c | 1 + src/payload.c | 3 + src/rt.c | 2 + src/rule.c | 4 + src/statement.c | 15 + 24 files changed, 1845 insertions(+), 2 deletions(-) create mode 100644 include/json.h create mode 100644 src/json.c diff --git a/configure.ac b/configure.ac index 249657793646a..65fe038a316fe 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,17 @@ AC_DEFINE([HAVE_LIBXTABLES], [1], [0]) AC_SUBST(with_libxtables) AM_CONDITIONAL([BUILD_XTABLES], [test "x$with_libxtables" == xyes]) +AC_ARG_WITH([json], [AS_HELP_STRING([--with-json], + [Enable JSON output support)])], + [], [with_json=no]) +AS_IF([test "x$with_json" != xno], [ +AC_CHECK_LIB([jansson], [json_object], , + AC_MSG_ERROR([No suitable version of libjansson found])) +AC_DEFINE([HAVE_LIBJANSSON], [1], [Define if you have libjansson]) +]) +AC_SUBST(with_json) +AM_CONDITIONAL([BUILD_JSON], [test "x$with_json" != xno]) + AC_CONFIG_FILES([ \ Makefile \ libnftables.pc \ @@ -130,4 +141,5 @@ nft configuration: use mini-gmp: ${with_mini_gmp} enable man page: ${enable_man_doc} enable pdf documentation: ${enable_pdf_doc} - libxtables support: ${with_libxtables}" + libxtables support: ${with_libxtables} + json output support: ${with_json}" diff --git a/include/datatype.h b/include/datatype.h index a1abe31f2b2bd..44ac075c2c534 100644 --- a/include/datatype.h +++ b/include/datatype.h @@ -1,6 +1,8 @@ #ifndef NFTABLES_DATATYPE_H #define NFTABLES_DATATYPE_H +#include <json.h> + /** * enum datatypes * @@ -149,6 +151,8 @@ struct datatype { const char *basefmt; void (*print)(const struct expr *expr, struct output_ctx *octx); + json_t *(*json)(const struct expr *expr, + struct output_ctx *octx); struct error_record *(*parse)(const struct expr *sym, struct expr **res); const struct symbol_table *sym_tbl; diff --git a/include/expression.h b/include/expression.h index 23d6bd23cff29..15af35e87a983 100644 --- a/include/expression.h +++ b/include/expression.h @@ -9,6 +9,7 @@ #include <datatype.h> #include <utils.h> #include <list.h> +#include <json.h> /** * enum expr_types @@ -154,6 +155,8 @@ struct expr_ops { enum byteorder byteorder); void (*print)(const struct expr *expr, struct output_ctx *octx); + json_t *(*json)(const struct expr *expr, + struct output_ctx *octx); bool (*cmp)(const struct expr *e1, const struct expr *e2); void (*pctx_update)(struct proto_ctx *ctx, diff --git a/include/gmputil.h b/include/gmputil.h index 084aa62266879..73959c170c917 100644 --- a/include/gmputil.h +++ b/include/gmputil.h @@ -13,6 +13,7 @@ extern int mpz_vfprintf(FILE *fp, const char *format, va_list args); #define gmp_vfprintf mpz_vfprintf #endif +#include <inttypes.h> #include <asm/byteorder.h> enum mpz_word_order { diff --git a/include/json.h b/include/json.h new file mode 100644 index 0000000000000..579bd5dfa98d3 --- /dev/null +++ b/include/json.h @@ -0,0 +1,161 @@ +#ifndef NFTABLES_JSON_H +#define NFTABLES_JSON_H + +struct chain; +struct cmd; +struct expr; +struct netlink_ctx; +struct rule; +struct set; +struct stmt; +struct symbol_table; +struct table; + +#ifdef HAVE_LIBJANSSON + +#include <jansson.h> + +json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *meta_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *concat_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *set_expr_json(const struct expr *expr, struct output_ctx *octx); +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 *prefix_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *list_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *unary_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *mapping_expr_json(const struct expr *expr, struct output_ctx *octx); +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); +json_t *verdict_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *rt_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *numgen_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *hash_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *constant_expr_json(const struct expr *expr, struct output_ctx *octx); + +json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx); +json_t *string_type_json(const struct expr *expr, struct output_ctx *octx); +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); +json_t *inet_service_type_json(const struct expr *expr, + struct output_ctx *octx); +json_t *mark_type_json(const struct expr *expr, struct output_ctx *octx); +json_t *devgroup_type_json(const struct expr *expr, struct output_ctx *octx); +json_t *ct_label_type_json(const struct expr *expr, struct output_ctx *octx); +json_t *time_type_json(const struct expr *expr, struct output_ctx *octx); +json_t *uid_type_json(const struct expr *expr, struct output_ctx *octx); +json_t *gid_type_json(const struct expr *expr, struct output_ctx *octx); + +json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *exthdr_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *ct_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *notrack_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *dup_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *meta_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx); +json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx); + +int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd); + +#else /* ! HAVE_LIBJANSSON */ + +typedef void json_t; + +#define JSON_PRINT_STUB(name, arg1_t, arg2_t) \ +static inline json_t *name##_json(arg1_t arg1, arg2_t arg2) { return NULL; } + +#define EXPR_PRINT_STUB(name) \ + JSON_PRINT_STUB(name, const struct expr *, struct output_ctx *) +#define STMT_PRINT_STUB(name) \ + JSON_PRINT_STUB(name##_stmt, const struct stmt *, struct output_ctx *) + +EXPR_PRINT_STUB(binop_expr) +EXPR_PRINT_STUB(relational_expr) +EXPR_PRINT_STUB(range_expr) +EXPR_PRINT_STUB(meta_expr) +EXPR_PRINT_STUB(payload_expr) +EXPR_PRINT_STUB(ct_expr) +EXPR_PRINT_STUB(concat_expr) +EXPR_PRINT_STUB(set_expr) +EXPR_PRINT_STUB(set_ref_expr) +EXPR_PRINT_STUB(set_elem_expr) +EXPR_PRINT_STUB(prefix_expr) +EXPR_PRINT_STUB(list_expr) +EXPR_PRINT_STUB(unary_expr) +EXPR_PRINT_STUB(mapping_expr) +EXPR_PRINT_STUB(map_expr) +EXPR_PRINT_STUB(exthdr_expr) +EXPR_PRINT_STUB(verdict_expr) +EXPR_PRINT_STUB(rt_expr) +EXPR_PRINT_STUB(numgen_expr) +EXPR_PRINT_STUB(hash_expr) +EXPR_PRINT_STUB(fib_expr) +EXPR_PRINT_STUB(constant_expr) + +EXPR_PRINT_STUB(integer_type) +EXPR_PRINT_STUB(string_type) +EXPR_PRINT_STUB(boolean_type) +EXPR_PRINT_STUB(inet_protocol_type) +EXPR_PRINT_STUB(inet_service_type) +EXPR_PRINT_STUB(mark_type) +EXPR_PRINT_STUB(devgroup_type) +EXPR_PRINT_STUB(ct_label_type) +EXPR_PRINT_STUB(time_type) +EXPR_PRINT_STUB(uid_type) +EXPR_PRINT_STUB(gid_type) + +STMT_PRINT_STUB(expr) +STMT_PRINT_STUB(payload) +STMT_PRINT_STUB(exthdr) +STMT_PRINT_STUB(quota) +STMT_PRINT_STUB(ct) +STMT_PRINT_STUB(limit) +STMT_PRINT_STUB(fwd) +STMT_PRINT_STUB(notrack) +STMT_PRINT_STUB(dup) +STMT_PRINT_STUB(meta) +STMT_PRINT_STUB(nat) +STMT_PRINT_STUB(reject) +STMT_PRINT_STUB(counter) +STMT_PRINT_STUB(set) +STMT_PRINT_STUB(log) +STMT_PRINT_STUB(objref) +STMT_PRINT_STUB(meter) +STMT_PRINT_STUB(queue) +STMT_PRINT_STUB(verdict) + +#undef STMT_PRINT_STUB +#undef EXPR_PRINT_STUB +#undef JSON_PRINT_STUB + +static inline json_t *symbolic_constant_json(const struct symbol_table *tbl, + const struct expr *expr) +{ + return NULL; +} + +static inline int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + return -1; +} + +#endif /* HAVE_LIBJANSSON */ + +#endif /* NFTABLES_JSON_H */ diff --git a/include/nftables.h b/include/nftables.h index 661c1e17392c8..f88d0530a963e 100644 --- a/include/nftables.h +++ b/include/nftables.h @@ -21,6 +21,7 @@ struct output_ctx { unsigned int ip2name; unsigned int handle; unsigned int echo; + unsigned int json; union { FILE *output_fp; struct cookie output_cookie; diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h index 652e0ca99182a..4bfdaf9a26885 100644 --- a/include/nftables/libnftables.h +++ b/include/nftables/libnftables.h @@ -55,6 +55,8 @@ bool nft_ctx_output_get_handle(struct nft_ctx *ctx); void nft_ctx_output_set_handle(struct nft_ctx *ctx, bool val); bool nft_ctx_output_get_echo(struct nft_ctx *ctx); void nft_ctx_output_set_echo(struct nft_ctx *ctx, bool val); +bool nft_ctx_output_get_json(struct nft_ctx *ctx); +void nft_ctx_output_set_json(struct nft_ctx *ctx, bool val); FILE *nft_ctx_set_output(struct nft_ctx *ctx, FILE *fp); int nft_ctx_buffer_output(struct nft_ctx *ctx); diff --git a/include/statement.h b/include/statement.h index 2c6d0dfa2dd50..de26549b32f4c 100644 --- a/include/statement.h +++ b/include/statement.h @@ -3,6 +3,7 @@ #include <list.h> #include <expression.h> +#include <json.h> extern struct stmt *expr_stmt_alloc(const struct location *loc, struct expr *expr); @@ -289,6 +290,8 @@ struct stmt_ops { void (*destroy)(struct stmt *stmt); void (*print)(const struct stmt *stmt, struct output_ctx *octx); + json_t *(*json)(const struct stmt *stmt, + struct output_ctx *octx); }; enum stmt_flags { diff --git a/src/Makefile.am b/src/Makefile.am index 92e6795f659aa..c5c3b0bca1f6b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,4 +88,9 @@ if BUILD_CLI nft_SOURCES += cli.c endif +if BUILD_JSON +libnftables_la_SOURCES += json.c +libnftables_la_LIBADD += ${JANSSON_LIBS} +endif + nft_LDADD = libnftables.la diff --git a/src/ct.c b/src/ct.c index a1a91f3ae7644..1d50382bab8b1 100644 --- a/src/ct.c +++ b/src/ct.c @@ -225,6 +225,7 @@ static const struct datatype ct_label_type = { .size = CT_LABEL_BIT_SIZE, .basetype = &bitmask_type, .print = ct_label_type_print, + .json = ct_label_type_json, .parse = ct_label_type_parse, }; @@ -360,6 +361,7 @@ static const struct expr_ops ct_expr_ops = { .type = EXPR_CT, .name = "ct", .print = ct_expr_print, + .json = ct_expr_json, .cmp = ct_expr_cmp, .clone = ct_expr_clone, .pctx_update = ct_expr_pctx_update, @@ -442,6 +444,7 @@ static const struct stmt_ops ct_stmt_ops = { .type = STMT_CT, .name = "ct", .print = ct_stmt_print, + .json = ct_stmt_json, }; struct stmt *ct_stmt_alloc(const struct location *loc, enum nft_ct_keys key, @@ -467,6 +470,7 @@ static const struct stmt_ops notrack_stmt_ops = { .type = STMT_NOTRACK, .name = "notrack", .print = notrack_stmt_print, + .json = notrack_stmt_json, }; struct stmt *notrack_stmt_alloc(const struct location *loc) diff --git a/src/datatype.c b/src/datatype.c index 8b4579daa8444..98b743feb1664 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -25,6 +25,7 @@ #include <gmputil.h> #include <erec.h> #include <netlink.h> +#include <json.h> #include <netinet/ip_icmp.h> @@ -357,6 +358,7 @@ const struct datatype integer_type = { .name = "integer", .desc = "integer", .print = integer_type_print, + .json = integer_type_json, .parse = integer_type_parse, }; @@ -386,6 +388,7 @@ const struct datatype string_type = { .desc = "string", .byteorder = BYTEORDER_HOST_ENDIAN, .print = string_type_print, + .json = string_type_json, .parse = string_type_parse, }; @@ -603,6 +606,7 @@ const struct datatype inet_protocol_type = { .size = BITS_PER_BYTE, .basetype = &integer_type, .print = inet_protocol_type_print, + .json = inet_protocol_type_json, .parse = inet_protocol_type_parse, }; @@ -658,6 +662,7 @@ const struct datatype inet_service_type = { .size = 2 * BITS_PER_BYTE, .basetype = &integer_type, .print = inet_service_type_print, + .json = inet_service_type_json, .parse = inet_service_type_parse, .sym_tbl = &inet_service_tbl, }; @@ -753,6 +758,7 @@ const struct datatype mark_type = { .basetype = &integer_type, .basefmt = "0x%.8Zx", .print = mark_type_print, + .json = mark_type_json, .parse = mark_type_parse, .flags = DTYPE_F_PREFIX, }; @@ -967,6 +973,7 @@ const struct datatype time_type = { .size = 8 * BITS_PER_BYTE, .basetype = &integer_type, .print = time_type_print, + .json = time_type_json, .parse = time_type_parse, }; @@ -1143,4 +1150,5 @@ const struct datatype boolean_type = { .size = 1, .basetype = &integer_type, .sym_tbl = &boolean_tbl, + .json = boolean_type_json, }; diff --git a/src/expression.c b/src/expression.c index 049c75a7787d1..a8c0cc7a5ae3b 100644 --- a/src/expression.c +++ b/src/expression.c @@ -23,6 +23,7 @@ #include <utils.h> #include <list.h> #include <erec.h> +#include <json.h> struct expr *expr_alloc(const struct location *loc, const struct expr_ops *ops, const struct datatype *dtype, enum byteorder byteorder, @@ -195,6 +196,7 @@ static const struct expr_ops verdict_expr_ops = { .type = EXPR_VERDICT, .name = "verdict", .print = verdict_expr_print, + .json = verdict_expr_json, .cmp = verdict_expr_cmp, .clone = verdict_expr_clone, .destroy = verdict_expr_destroy, @@ -319,6 +321,7 @@ static const struct expr_ops constant_expr_ops = { .type = EXPR_VALUE, .name = "value", .print = constant_expr_print, + .json = constant_expr_json, .cmp = constant_expr_cmp, .clone = constant_expr_clone, .destroy = constant_expr_destroy, @@ -464,6 +467,7 @@ static const struct expr_ops prefix_expr_ops = { .type = EXPR_PREFIX, .name = "prefix", .print = prefix_expr_print, + .json = prefix_expr_json, .set_type = prefix_expr_set_type, .clone = prefix_expr_clone, .destroy = prefix_expr_destroy, @@ -517,6 +521,7 @@ static const struct expr_ops unary_expr_ops = { .type = EXPR_UNARY, .name = "unary", .print = unary_expr_print, + .json = unary_expr_json, .clone = unary_expr_clone, .destroy = unary_expr_destroy, }; @@ -592,6 +597,7 @@ static const struct expr_ops binop_expr_ops = { .type = EXPR_BINOP, .name = "binop", .print = binop_expr_print, + .json = binop_expr_json, .clone = binop_expr_clone, .destroy = binop_expr_destroy, }; @@ -613,6 +619,7 @@ static const struct expr_ops relational_expr_ops = { .type = EXPR_RELATIONAL, .name = "relational", .print = binop_expr_print, + .json = relational_expr_json, .destroy = binop_expr_destroy, }; @@ -679,6 +686,7 @@ static const struct expr_ops range_expr_ops = { .type = EXPR_RANGE, .name = "range", .print = range_expr_print, + .json = range_expr_json, .clone = range_expr_clone, .destroy = range_expr_destroy, .set_type = range_expr_set_type, @@ -763,6 +771,7 @@ static const struct expr_ops concat_expr_ops = { .type = EXPR_CONCAT, .name = "concat", .print = concat_expr_print, + .json = concat_expr_json, .clone = compound_expr_clone, .destroy = concat_expr_destroy, }; @@ -781,6 +790,7 @@ static const struct expr_ops list_expr_ops = { .type = EXPR_LIST, .name = "list", .print = list_expr_print, + .json = list_expr_json, .clone = compound_expr_clone, .destroy = compound_expr_destroy, }; @@ -867,6 +877,7 @@ static const struct expr_ops set_expr_ops = { .type = EXPR_SET, .name = "set", .print = set_expr_print, + .json = set_expr_json, .set_type = set_expr_set_type, .clone = compound_expr_clone, .destroy = compound_expr_destroy, @@ -915,6 +926,7 @@ static const struct expr_ops mapping_expr_ops = { .type = EXPR_MAPPING, .name = "mapping", .print = mapping_expr_print, + .json = mapping_expr_json, .set_type = mapping_expr_set_type, .clone = mapping_expr_clone, .destroy = mapping_expr_destroy, @@ -959,6 +971,7 @@ static const struct expr_ops map_expr_ops = { .type = EXPR_MAP, .name = "map", .print = map_expr_print, + .json = map_expr_json, .clone = map_expr_clone, .destroy = map_expr_destroy, }; @@ -1000,6 +1013,7 @@ static const struct expr_ops set_ref_expr_ops = { .type = EXPR_SET_REF, .name = "set reference", .print = set_ref_expr_print, + .json = set_ref_expr_json, .clone = set_ref_expr_clone, .destroy = set_ref_expr_destroy, }; @@ -1055,6 +1069,7 @@ static const struct expr_ops set_elem_expr_ops = { .name = "set element", .clone = set_elem_expr_clone, .print = set_elem_expr_print, + .json = set_elem_expr_json, .destroy = set_elem_expr_destroy, }; diff --git a/src/exthdr.c b/src/exthdr.c index 06a82070a6bb2..cb0a58e8526a5 100644 --- a/src/exthdr.c +++ b/src/exthdr.c @@ -70,6 +70,7 @@ const struct expr_ops exthdr_expr_ops = { .type = EXPR_EXTHDR, .name = "exthdr", .print = exthdr_expr_print, + .json = exthdr_expr_json, .cmp = exthdr_expr_cmp, .clone = exthdr_expr_clone, }; @@ -107,6 +108,7 @@ static const struct stmt_ops exthdr_stmt_ops = { .type = STMT_EXTHDR, .name = "exthdr", .print = exthdr_stmt_print, + .json = exthdr_stmt_json, }; struct stmt *exthdr_stmt_alloc(const struct location *loc, diff --git a/src/fib.c b/src/fib.c index 069411f0131f2..9a19cc348a3df 100644 --- a/src/fib.c +++ b/src/fib.c @@ -105,6 +105,7 @@ static const struct expr_ops fib_expr_ops = { .type = EXPR_FIB, .name = "fib", .print = fib_expr_print, + .json = fib_expr_json, .cmp = fib_expr_cmp, .clone = fib_expr_clone, }; diff --git a/src/hash.c b/src/hash.c index e6999637566c3..a2d2314baa544 100644 --- a/src/hash.c +++ b/src/hash.c @@ -60,6 +60,7 @@ static const struct expr_ops hash_expr_ops = { .type = EXPR_HASH, .name = "hash", .print = hash_expr_print, + .json = hash_expr_json, .cmp = hash_expr_cmp, .clone = hash_expr_clone, }; diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000000000..e458eb3e4bdd8 --- /dev/null +++ b/src/json.c @@ -0,0 +1,1564 @@ +#define _GNU_SOURCE +#include <string.h> + +#include <expression.h> +#include <list.h> +#include <netlink.h> +#include <rule.h> +#include <rt.h> + +#include <netdb.h> +#include <netinet/icmp6.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_log.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter/nf_tables.h> +#include <pwd.h> +#include <grp.h> +#include <jansson.h> +#include <syslog.h> + +#ifdef DEBUG +#define __json_pack json_pack +#define json_pack(...) ({ \ + json_t *__out = __json_pack(__VA_ARGS__); \ + assert(__out); \ + __out; \ +}) +#endif + +static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx) +{ + char buf[1024]; + FILE *fp; + + if (expr->ops->json) + return expr->ops->json(expr, octx); + + printf("warning: expr ops %s have no json callback\n", + expr->ops->name); + + fp = octx->output_fp; + octx->output_fp = fmemopen(buf, 1024, "w"); + + expr->ops->print(expr, octx); + + fclose(octx->output_fp); + octx->output_fp = fp; + + return json_pack("s", buf); +} + +static json_t *set_dtype_json(const struct expr *key) +{ + char *namedup = xstrdup(key->dtype->name), *tok; + json_t *root = NULL; + + tok = strtok(namedup, " ."); + while (tok) { + json_t *jtok = json_string(xstrdup(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, " ."); + } + xfree(namedup); + return root; +} + +static json_t *set_print_json(struct output_ctx *octx, const struct set *set) +{ + json_t *root, *tmp; + const char *type, *datatype_ext = NULL; + + if (set->flags & NFT_SET_MAP) { + type = "map"; + datatype_ext = set->datatype->name; + } else if (set->flags & NFT_SET_OBJECT) { + type = "map"; + datatype_ext = obj_type_name(set->objtype); + } else if (set->flags & NFT_SET_EVAL) { + type = "meter"; + } else { + type = "set"; + } + + root = json_pack("{s:s, s:s, s:s, s:o}", + "family", family2str(set->handle.family), + "name", set->handle.set.name, + "table", set->handle.table.name, + "type", set_dtype_json(set->key)); + if (octx->handle) + json_object_set_new(root, "handle", + json_integer(set->handle.handle.id)); + if (datatype_ext) + json_object_set_new(root, "map", json_string(datatype_ext)); + + if (!(set->flags & (NFT_SET_CONSTANT))) { + if (set->policy != NFT_SET_POL_PERFORMANCE) { + tmp = json_pack("s", set_policy2str(set->policy)); + json_object_set_new(root, "policy", tmp); + } + if (set->desc.size) { + tmp = json_pack("i", set->desc.size); + json_object_set_new(root, "size", tmp); + } + } + + tmp = json_array(); + if (set->flags & NFT_SET_CONSTANT) + json_array_append_new(tmp, json_pack("s", "constant")); + if (set->flags & NFT_SET_INTERVAL) + 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 (json_array_size(tmp) > 0) { + json_object_set_new(root, "flags", tmp); + } else { + if (json_array_size(tmp)) + json_object_set(root, "flags", json_array_get(tmp, 0)); + json_decref(tmp); + } + + if (set->timeout) { + tmp = json_pack("i", set->timeout / 1000); + json_object_set_new(root, "timeout", tmp); + } + if (set->gc_int) { + tmp = json_pack("i", set->gc_int / 1000); + json_object_set_new(root, "gc-interval", tmp); + } + + if (set->init && set->init->size > 0) { + json_t *array = json_array(); + const struct expr *i; + + list_for_each_entry(i, &set->init->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + + json_object_set_new(root, "elem", array); + } + + return json_pack("{s:o}", type, root); +} + +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); + + 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) +{ + const struct stmt *stmt; + json_t *root, *tmp; + + root = json_pack("{s:s, s:s, s:s, s:I}", + "family", family2str(rule->handle.family), + "table", rule->handle.table.name, + "chain", rule->handle.chain.name, + "position", rule->handle.position.id); + if (octx->handle) + json_object_set_new(root, "handle", + json_integer(rule->handle.handle.id)); + if (rule->comment) + json_object_set_new(root, "comment", + json_string(rule->comment)); + + tmp = json_array(); + list_for_each_entry(stmt, &rule->stmts, list) + json_array_append_new(tmp, stmt_print_json(stmt, octx)); + + if (json_array_size(tmp)) + json_object_set_new(root, "expr", tmp); + else { + fprintf(stderr, "rule without statements?!\n"); + json_decref(tmp); + } + + return json_pack("{s:o}", "rule", root); +} + +static json_t *chain_print_json(const struct output_ctx *octx, + const struct chain *chain) +{ + json_t *root, *tmp; + + root = json_pack("{s:s, s:s, s:s}", + "family", family2str(chain->handle.family), + "table", chain->handle.table.name, + "name", chain->handle.chain.name); + if (octx->handle) + json_object_set_new(root, "handle", + json_integer(chain->handle.handle.id)); + + if (chain->flags & CHAIN_F_BASECHAIN) { + tmp = json_pack("{s:s, s:s, s:i, s:s}", + "type", chain->type, + "hook", hooknum2str(chain->handle.family, + chain->hooknum), + "prio", chain->priority, + "policy", chain_policy2str(chain->policy)); + if (chain->dev) + json_object_set_new(tmp, "dev", json_string(chain->dev)); + json_object_update(root, tmp); + json_decref(tmp); + } + + return json_pack("{s:o}", "chain", root); +} + +static json_t *proto_name_json(uint8_t proto) +{ + const struct protoent *p = getprotobynumber(proto); + + if (p) + return json_string(p->p_name); + return json_integer(proto); +} + +static json_t *obj_print_json(struct output_ctx *octx, const struct obj *obj) +{ + const char *type = obj_type_name(obj->type); + json_t *root, *tmp; + const char *unit; + uint64_t rate; + + root = json_pack("{s:s, s:s, s:s}", + "family", family2str(obj->handle.family), + "name", obj->handle.obj.name, + "table", obj->handle.table.name); + if (octx->handle) + json_object_set_new(root, "handle", + json_integer(obj->handle.handle.id)); + + switch (obj->type) { + case NFT_OBJECT_COUNTER: + tmp = json_pack("{s:I, s:I}", + "packets", obj->counter.packets, + "bytes", obj->counter.bytes); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_QUOTA: + tmp = json_pack("{s:I, s:I, s:b}", + "bytes", obj->quota.bytes, + "used", obj->quota.used, + "inv", obj->quota.flags & NFT_QUOTA_F_INV); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_CT_HELPER: + type = "ct helper"; + tmp = json_pack("{s:s, s:o, s:s}", + "helper", obj->ct_helper.name, "protocol", + proto_name_json(obj->ct_helper.l4proto), + "l3proto", family2str(obj->ct_helper.l3proto)); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_LIMIT: + tmp = json_pack("{s:b, s:s}", + "inv", obj->limit.flags & NFT_LIMIT_F_INV, + "per", get_unit(obj->limit.unit)); + switch (obj->limit.type) { + case NFT_LIMIT_PKTS: + json_object_set_new(tmp, "rate", + json_integer(obj->limit.rate)); + json_object_set_new(tmp, "burst", + json_integer(obj->limit.burst)); + break; + case NFT_LIMIT_PKT_BYTES: + unit = get_rate(obj->limit.rate, &rate); + json_object_set_new(tmp, "rate", json_integer(rate)); + json_object_set_new(tmp, "rate_unit", + json_string(unit)); + if (obj->limit.burst) { + unit = get_rate(obj->limit.burst, &rate); + json_object_set_new(tmp, "burst", + json_integer(rate)); + json_object_set_new(tmp, "burst_unit", + json_string(unit)); + } + break; + } + + json_object_update(root, tmp); + json_decref(tmp); + break; + } + + return json_pack("{s:o}", type, root); +} + +static json_t *flowtable_print_json(const struct flowtable *ftable) +{ + json_t *root, *devs = NULL; + int i; + + root = json_pack("{s:s, s:s, s:s, s:s, s:i}", + "family", family2str(ftable->handle.family), + "name", ftable->handle.flowtable, + "table", ftable->handle.table.name, + "hook", hooknum2str(NFPROTO_NETDEV, ftable->hooknum), + "prio", ftable->priority); + + for (i = 0; i < ftable->dev_array_len; i++) { + const char *dev = ftable->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); + + return json_pack("{s:o}", "flowtable", root); +} + +static json_t *table_flags_json(const struct table *table) +{ + uint32_t flags = table->flags; + json_t *root = json_array(), *tmp; + int i = 0; + + while (flags) { + if (flags & 0x1) { + tmp = json_string(table_flags_name[i]); + json_array_append_new(root, tmp); + } + flags >>= 1; + i++; + } + switch (json_array_size(root)) { + case 0: + json_decref(root); + return NULL; + case 1: + json_unpack(root, "[o]", &tmp); + json_decref(root); + root = tmp; + break; + } + return root; +} + +static json_t *table_print_json(const struct output_ctx *octx, + const struct table *table) +{ + json_t *root, *tmp; + + root = json_pack("{s:s, s:s}", + "family", family2str(table->handle.family), + "name", table->handle.table.name); + if (octx->handle) + json_object_set_new(root, "handle", + json_integer(table->handle.handle.id)); + + tmp = table_flags_json(table); + if (tmp) + json_object_set_new(root, "flags", tmp); + + return json_pack("{s:o}", "table", root); +} + +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)); +} + +json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *tmp; + + tmp = json_pack("{s:o, s:o}", + "left", expr_print_json(expr->left, octx), + "right", expr_print_json(expr->right, octx)); + /* XXX: check taken from binop_expr_print() + * if right is range, op is OP_EQ which in turn crashes nft if fed with it */ + if (expr_op_symbols[expr->op] && + (expr->op != OP_EQ || must_print_eq_op(expr))) + json_object_set_new(tmp, "op", + json_string(expr_op_symbols[expr->op])); + + return json_pack("{s:o}", "match", tmp); +} + +json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("{s:[o, o]}", "range", + expr_print_json(expr->left, octx), + expr_print_json(expr->right, octx)); +} + +json_t *meta_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("{s:s}", "meta", meta_templates[expr->meta.key].token); +} + +json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const struct proto_hdr_template *tmpl; + const struct proto_desc *desc; + json_t *root; + + desc = expr->payload.desc; + tmpl = expr->payload.tmpl; + if (payload_is_known(expr)) + root = json_pack("{s:s, s:s}", + "name", desc->name, + "field", tmpl->token); + else + root = json_pack("{s:s, s:s, s:i, s:i}", + "name", "raw", + "base", proto_base_tokens[expr->payload.base], + "offset", expr->payload.offset, + "len", expr->len); + + return json_pack("{s:o}", "payload", root); +} + +json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *dirstr = ct_dir2str(expr->ct.direction); + enum nft_ct_keys key = expr->ct.key; + const struct proto_desc *desc; + json_t *root; + + root = json_pack("{s:s}", "key", ct_templates[key].token); + + if (expr->ct.direction < 0) + goto out; + + if (dirstr) + json_object_set_new(root, "dir", json_string(dirstr)); + + switch (key) { + case NFT_CT_SRC: + case NFT_CT_DST: + desc = proto_find_upper(&proto_inet, expr->ct.nfproto); + if (desc) + json_object_set_new(root, "family", + json_string(desc->name)); + break; + default: + break; + } +out: + return json_pack("{s:o}", "ct", root); +} + +json_t *concat_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *array = json_array(); + const struct expr *i; + + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + + return json_pack("{s:o}", "concat", array); +} + +json_t *set_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *array = json_array(); + const struct expr *i; + + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + + return json_pack("{s:o}", "set", array); +} + +json_t *set_ref_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + if (expr->set->flags & NFT_SET_ANONYMOUS) { + return expr_print_json(expr->set->init, octx); + } else { + return json_pack("s+", "@", expr->set->handle.set.name); + } +} + +json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *root = expr_print_json(expr->key, octx); + + if (!root) + return NULL; + + /* these element attributes require formal set elem syntax */ + if (expr->timeout || expr->expiration || expr->comment) { + root = json_pack("{s:o}", "val", root); + + if (expr->timeout) + json_object_set_new(root, "elem_timeout", + json_integer(expr->timeout / 1000)); + if (expr->expiration) + json_object_set_new(root, "elem_expires", + json_integer(expr->expiration / 1000)); + if (expr->comment) + json_object_set_new(root, "elem_comment", + json_string(expr->comment)); + return json_pack("{s:o}", "elem", root); + } + + return root; +} + +json_t *prefix_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *root = expr_print_json(expr->prefix, octx); + + return json_pack("{s:{s:o, s:i}}", "prefix", + "addr", root, + "len", expr->prefix_len); +} + +json_t *list_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *array = json_array(); + const struct expr *i; + + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + + //return json_pack("{s:s, s:o}", "type", "list", "val", array); + return array; +} + +json_t *unary_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return expr_print_json(expr->arg, octx); +} + +json_t *mapping_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("[o, o]", + expr_print_json(expr->left, octx), + expr_print_json(expr->right, octx)); +} + +json_t *map_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("{s:{s:o, s:o}}", "map", + "left", expr_print_json(expr->map, octx), + "right", expr_print_json(expr->mappings, 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"; + 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) { + unsigned int offset = expr->exthdr.offset / 64; + + if (offset) { + const char *offstrs[] = { "0", "1", "2", "3" }; + const char *offstr = ""; + + if (offset < 4) + offstr = offstrs[offset]; + + root = json_pack("{s:s+}", "name", desc, offstr); + } else { + root = json_pack("{s:s}", "name", desc); + } + + if (!is_exists) + json_object_set_new(root, "field", json_string(field)); + + return json_pack("{s:o}", "tcp option", root); + } + + 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); +} + +json_t *verdict_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const struct { + int verdict; + const char *name; + bool chain; + } verdict_tbl[] = { + { NFT_CONTINUE, "continue", false }, + { NFT_BREAK, "break", false }, + { NFT_JUMP, "jump", true }, + { NFT_GOTO, "goto", true }, + { NFT_RETURN, "return", false }, + { NF_ACCEPT, "accept", false }, + { NF_DROP, "drop", false }, + { NF_QUEUE, "queue", false }, + }; + const char *name = NULL; + const char *chain = NULL; + unsigned int i; + + for (i = 0; i < array_size(verdict_tbl); i++) { + if (expr->verdict == verdict_tbl[i].verdict) { + name = verdict_tbl[i].name; + if (verdict_tbl[i].chain && expr->chain) + chain = expr->chain; + break; + } + } + if (!name) { + BUG("Unknown verdict %d.", expr->verdict); + return NULL; + } + return json_pack("{s:o}", name, chain ? json_string(chain) : json_null()); +} + +json_t *rt_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *key = rt_templates[expr->rt.key].token; + json_t *root = json_pack("{s:s}", "key", key); + const char *family = NULL; + + switch (expr->rt.key) { + case NFT_RT_NEXTHOP4: + family = "ip"; + break; + case NFT_RT_NEXTHOP6: + family = "ip6"; + break; + default: + break; + } + + if (family) + json_object_set_new(root, "family", json_string(family)); + + return json_pack("{s:o}", "rt", root); +} + +json_t *numgen_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *mode; + + switch (expr->numgen.type) { + case NFT_NG_INCREMENTAL: + mode = "inc"; + break; + case NFT_NG_RANDOM: + mode = "random"; + break; + default: + mode = "unknown"; + break; + } + + return json_pack("{s:{s:s, s:i, s:i}}", "numgen", + "mode", mode, + "mod", expr->numgen.mod, + "offset", expr->numgen.offset); +} + +json_t *hash_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *type; + json_t *root, *jexpr = NULL; + + switch (expr->hash.type) { + case NFT_HASH_SYM: + type = "symhash"; + break; + case NFT_HASH_JENKINS: + default: + type = "jhash"; + jexpr = expr_print_json(expr->hash.expr, octx); + break; + } + + root = json_pack("{s:i}", "mod", expr->hash.mod); + if (expr->hash.seed_set) + json_object_set_new(root, "seed", + json_integer(expr->hash.seed)); + if (expr->hash.offset) + json_object_set_new(root, "offset", + json_integer(expr->hash.offset)); + if (jexpr) + json_object_set_new(root, "expr", jexpr); + + return json_pack("{s:o}", type, root); +} + +json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *fib_flags[] = { "saddr", "daddr", "mark", "iif", "oif" }; + unsigned int flags = expr->fib.flags & ~NFTA_FIB_F_PRESENT; + json_t *root; + + root = json_pack("{s:s}", "result", fib_result_str(expr->fib.result)); + + if (flags) { + json_t *tmp = json_array(); + unsigned int i; + + for (i = 0; i < array_size(fib_flags); i++) { + if (flags & (1 << i)) { + json_array_append_new(tmp, json_string(fib_flags[i])); + flags &= ~(1 << i); + } + } + if (flags) + json_array_append_new(tmp, json_integer(flags)); + json_object_set_new(root, "flags", tmp); + } + return json_pack("{s:o}", "fib", root); +} + +static json_t *symbolic_constant_json(const struct symbol_table *tbl, + const struct expr *expr, + struct output_ctx *octx) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + const struct symbolic_constant *s; + uint64_t val = 0; + + /* Export the data in the correct byteorder for comparison */ + assert(expr->len / BITS_PER_BYTE <= sizeof(val)); + mpz_export_data(constant_data_ptr(val, expr->len), expr->value, + expr->byteorder, len); + + for (s = tbl->symbols; s->identifier != NULL; s++) { + if (val == s->value) + break; + } + if (!s->identifier) + return expr_basetype(expr)->json(expr, octx); + + if (octx->numeric > NFT_NUMERIC_ALL) + return json_integer(val); + else + return json_string(s->identifier); +} + +static json_t *datatype_json(const struct expr *expr, struct output_ctx *octx) +{ + const struct datatype *dtype = expr->dtype; + + do { + if (dtype->json) + return dtype->json(expr, octx); + if (dtype->sym_tbl) + return symbolic_constant_json(dtype->sym_tbl, + expr, octx); + if (dtype->print) { + struct output_ctx octx = { .numeric = 3 }; + char buf[1024]; + + octx.output_fp = fmemopen(buf, 1024, "w"); + dtype->print(expr, &octx); + fclose(octx.output_fp); + + if (buf[0] == '"') { + memmove(buf, buf + 1, strlen(buf)); + *strchrnul(buf, '"') = '\0'; + } + + return json_string(buf); + } + } while ((dtype = dtype->basetype)); + + BUG("datatype %s has no print method or symbol table\n", + expr->dtype->name); +} + +json_t *constant_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return datatype_json(expr, octx); +} + +json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx) +{ + char buf[1024] = "0x"; + + if (mpz_fits_ulong_p(expr->value)) + return json_integer(mpz_get_ui(expr->value)); + + mpz_get_str(buf + 2, 16, expr->value); + return json_string(buf); +} + +json_t *string_type_json(const struct expr *expr, struct output_ctx *octx) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + char data[len+1]; + + mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len); + data[len] = '\0'; + + return json_string(data); +} + +json_t *boolean_type_json(const struct expr *expr, struct output_ctx *octx) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + uint64_t val = 0; + + /* Export the data in the correct byteorder for comparison */ + assert(expr->len / BITS_PER_BYTE <= sizeof(val)); + mpz_export_data(constant_data_ptr(val, expr->len), expr->value, + expr->byteorder, len); + + return json_boolean((int)val); +} + +json_t *inet_protocol_type_json(const struct expr *expr, + struct output_ctx *octx) +{ + struct protoent *p; + + if (octx->numeric < NFT_NUMERIC_ALL) { + p = getprotobynumber(mpz_get_uint8(expr->value)); + if (p != NULL) + return json_string(p->p_name); + } + return integer_type_json(expr, octx); +} + +json_t *inet_service_type_json(const struct expr *expr, struct output_ctx *octx) +{ + if (octx->numeric >= NFT_NUMERIC_PORT) + return integer_type_json(expr, octx); + + return symbolic_constant_json(&inet_service_tbl, expr, octx); +} + +json_t *mark_type_json(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_json(mark_tbl, expr, octx); +} + +json_t *devgroup_type_json(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_json(devgroup_tbl, expr, octx); +} + +json_t *ct_label_type_json(const struct expr *expr, struct output_ctx *octx) +{ + unsigned long bit = mpz_scan1(expr->value, 0); + const char *labelstr = ct_label2str(bit); + + if (labelstr) + return json_string(labelstr); + + /* can happen when connlabel.conf is altered after rules were added */ + return json_integer(bit); +} + +json_t *time_type_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_integer(mpz_get_uint64(expr->value) / MSEC_PER_SEC); +} + +json_t *uid_type_json(const struct expr *expr, struct output_ctx *octx) +{ + uint32_t uid = mpz_get_uint32(expr->value); + + if (octx->numeric < NFT_NUMERIC_ALL) { + struct passwd *pw = getpwuid(uid); + + if (pw) + return json_string(pw->pw_name); + } + return json_integer(uid); +} + +json_t *gid_type_json(const struct expr *expr, struct output_ctx *octx) +{ + uint32_t gid = mpz_get_uint32(expr->value); + + if (octx->numeric < NFT_NUMERIC_ALL) { + struct group *gr = getgrgid(gid); + + if (gr) + return json_string(gr->gr_name); + } + return json_integer(gid); +} + +json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return expr_print_json(stmt->expr, octx); +} + +json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s: {s:o, s:o}}", "mangle", + "left", expr_print_json(stmt->payload.expr, octx), + "right", expr_print_json(stmt->payload.val, octx)); +} + +json_t *exthdr_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s: {s:o, s:o}}", "mangle", + "left", expr_print_json(stmt->exthdr.expr, octx), + "right", expr_print_json(stmt->exthdr.val, octx)); +} + +json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + const char *data_unit; + uint64_t bytes; + json_t *root; + + data_unit = get_rate(stmt->quota.bytes, &bytes); + root = json_pack("{s:I, s:s}", + "val", bytes, + "val_unit", data_unit); + + if (stmt->quota.flags & NFT_QUOTA_F_INV) + json_object_set_new(root, "inv", json_true()); + if (!octx->stateless && stmt->quota.used) { + data_unit = get_rate(stmt->quota.used, &bytes); + json_object_set_new(root, "used", json_integer((int)bytes)); + json_object_set_new(root, "used_unit", json_string(data_unit)); + } + + return json_pack("{s:o}", "quota", root); +} + +json_t *ct_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + struct expr expr = { + .ct = { + .key = stmt->ct.key, + .direction = stmt->ct.direction, + .nfproto = 0, + }, + }; + + return json_pack("{s:{s:o, s:o}}", "mangle", + "left", ct_expr_json(&expr, octx), + "right", expr_print_json(stmt->ct.expr, octx)); +} + +json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + const char *rate_unit = NULL, *burst_unit = NULL; + bool inv = stmt->limit.flags & NFT_LIMIT_F_INV; + uint64_t burst = stmt->limit.burst; + uint64_t rate = stmt->limit.rate; + json_t *root; + + if (stmt->limit.type == NFT_LIMIT_PKT_BYTES) { + rate_unit = get_rate(stmt->limit.rate, &rate); + burst_unit = get_rate(stmt->limit.burst, &burst); + } + + root = json_pack("{s:I, s:s}", + "rate", rate, + "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) { + json_object_set_new(root, "burst", json_integer(burst)); + if (burst_unit) + json_object_set_new(root, "burst_unit", + json_string(burst_unit)); + } + + return json_pack("{s:o}", "limit", root); +} + +json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root; + + root = expr_print_json(stmt->fwd.to, octx); + return json_pack("{s:o}", "fwd", root); +} + +json_t *notrack_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s:n}", "notrack"); +} + +json_t *dup_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root = json_object(); + + if (stmt->dup.to) { + root = json_pack("{s:o}", "addr", expr_print_json(stmt->dup.to, octx)); + if (stmt->dup.dev) + json_object_set_new(root, "dev", + expr_print_json(stmt->dup.dev, octx)); + } else { + root = json_null(); + } + return json_pack("{s:o}", "dup", root); +} + +json_t *meta_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root; + + root = json_pack("{s:s}", "meta", meta_templates[stmt->meta.key].token); + root = json_pack("{s:o, s:o}", + "left", root, + "right", expr_print_json(stmt->meta.expr, octx)); + + return json_pack("{s:o}", "mangle", root); +} + +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_GROUP) + json_object_set_new(root, "group", + json_integer(stmt->log.group)); + if (stmt->log.flags & STMT_LOG_SNAPLEN) + json_object_set_new(root, "snaplen", + json_integer(stmt->log.snaplen)); + if (stmt->log.flags & STMT_LOG_QTHRESHOLD) + json_object_set_new(root, "queue-threshold", + json_integer(stmt->log.qthreshold)); + if ((stmt->log.flags & STMT_LOG_LEVEL) && + stmt->log.level != LOG_WARNING) + json_object_set_new(root, "level", + json_string(log_level(stmt->log.level))); + + flags = json_array(); + + if ((stmt->log.logflags & NF_LOG_MASK) == NF_LOG_MASK) { + json_array_append_new(flags, json_string("all")); + } else { + if (stmt->log.logflags & NF_LOG_TCPSEQ) + json_array_append_new(flags, + json_string("tcp sequence")); + if (stmt->log.logflags & NF_LOG_TCPOPT) + json_array_append_new(flags, + json_string("tcp options")); + if (stmt->log.logflags & NF_LOG_IPOPT) + json_array_append_new(flags, json_string("ip options")); + if (stmt->log.logflags & NF_LOG_UID) + json_array_append_new(flags, json_string("skuid")); + if (stmt->log.logflags & NF_LOG_MACDECODE) + json_array_append_new(flags, json_string("ether")); + } + if (json_array_size(flags) > 1) { + json_object_set_new(root, "flags", flags); + } else { + if (json_array_size(flags)) + json_object_set(root, "flags", + json_array_get(flags, 0)); + json_decref(flags); + } + + if (!json_object_size(root)) { + json_decref(root); + root = json_null(); + } + + return json_pack("{s:o}", "log", root); +} + +static json_t *nat_flags_json(int flags) +{ + json_t *array = json_array(); + + if (flags & NF_NAT_RANGE_PROTO_RANDOM) + json_array_append_new(array, json_string("random")); + if (flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) + json_array_append_new(array, json_string("fully-random")); + if (flags & NF_NAT_RANGE_PERSISTENT) + json_array_append_new(array, json_string("persistent")); + return array; +} + +json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root = json_object(); + json_t *array = nat_flags_json(stmt->nat.flags); + + if (stmt->nat.addr) + json_object_set_new(root, "addr", + expr_print_json(stmt->nat.addr, octx)); + + if (stmt->nat.proto) + 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); + } + + if (!json_object_size(root)) { + json_decref(root); + root = json_null(); + } + + return json_pack("{s:o}", nat_etype2str(stmt->nat.type), root); +} + +json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root, *jexpr = NULL; + const char *type = NULL; + + switch (stmt->reject.type) { + case NFT_REJECT_TCP_RST: + 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.icmp_code == ICMP_PORT_UNREACH) + break; + type = "icmp"; + jexpr = expr_print_json(stmt->reject.expr, octx); + break; + case NFPROTO_IPV6: + if (stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT) + break; + type = "icmpv6"; + jexpr = expr_print_json(stmt->reject.expr, octx); + break; + } + } + + if (!type && !jexpr) + return json_pack("{s:n}", "reject"); + + root = json_object(); + if (type) + json_object_set_new(root, "type", json_string(type)); + if (jexpr) + json_object_set_new(root, "expr", jexpr); + + return json_pack("{s:o}", "reject", root); +} + +json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + if (octx->stateless) + return json_pack("{s:n}", "counter"); + + return json_pack("{s:{s:I, s:I}}", "counter", + "packets", stmt->counter.packets, + "bytes", stmt->counter.bytes); +} + +json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s:{s:s, s:o, s:s+}}", "set", + "op", set_stmt_op_names[stmt->set.op], + "elem", expr_print_json(stmt->set.key, octx), + "set", "@", stmt->set.set->set->handle.set.name); +} + +json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + const char *name; + + if (stmt->objref.type > NFT_OBJECT_MAX) + name = "unknown"; + else + name = objref_type_name(stmt->objref.type); + + return json_pack("{s:o}", name, expr_print_json(stmt->objref.expr, octx)); +} + +json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root, *tmp; + + octx->stateless++; + tmp = stmt_print_json(stmt->meter.stmt, octx); + octx->stateless--; + + root = json_pack("{s:o, s:o}", + "key", expr_print_json(stmt->meter.key, octx), + "stmt", tmp); + if (stmt->meter.set) { + tmp = json_string(stmt->meter.set->set->handle.set.name); + json_object_set_new(root, "name", tmp); + } + + return json_pack("{s:o}", "meter", root); +} + +json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root, *flags; + + root = json_object(); + + if (stmt->queue.queue) + json_object_set_new(root, "num", + expr_print_json(stmt->queue.queue, octx)); + + flags = json_array(); + if (stmt->queue.flags & NFT_QUEUE_FLAG_BYPASS) + json_array_append_new(flags, json_string("bypass")); + if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT) + json_array_append_new(flags, json_string("fanout")); + if (json_array_size(flags) > 1) { + json_object_set_new(root, "flags", flags); + } else { + if (json_array_size(flags)) + json_object_set(root, "flags", + json_array_get(flags, 0)); + json_decref(flags); + } + + if (!json_object_size(root)) { + json_decref(root); + root = json_null(); + } + + return json_pack("{s:o}", "queue", root); +} + +json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return expr_print_json(stmt->expr, octx); +} + +static json_t *table_print_json_full(struct netlink_ctx *ctx, + struct table *table) +{ + json_t *root = json_array(), *tmp; + struct flowtable *flowtable; + struct chain *chain; + struct rule *rule; + struct obj *obj; + struct set *set; + + tmp = table_print_json(ctx->octx, table); + json_array_append_new(root, tmp); + + list_for_each_entry(obj, &table->objs, list) { + tmp = obj_print_json(ctx->octx, obj); + json_array_append_new(root, tmp); + } + list_for_each_entry(set, &table->sets, list) { + if (set->flags & NFT_SET_ANONYMOUS) + continue; + tmp = set_print_json(ctx->octx, set); + json_array_append_new(root, tmp); + } + list_for_each_entry(flowtable, &table->flowtables, list) { + tmp = flowtable_print_json(flowtable); + json_array_append_new(root, tmp); + } + list_for_each_entry(chain, &table->chains, list) { + tmp = chain_print_json(ctx->octx, chain); + json_array_append_new(root, tmp); + + list_for_each_entry(rule, &chain->rules, list) { + tmp = rule_print_json(ctx->octx, rule); + json_array_append_new(root, tmp); + } + } + + 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(); + struct table *table; + + list_for_each_entry(table, &ctx->cache->list, list) { + if (family != NFPROTO_UNSPEC && + table->handle.family != family) + continue; + + json_array_extend(root, table_print_json_full(ctx, table)); + } + + return root; +} + +static json_t *do_list_tables_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + unsigned int family = cmd->handle.family; + json_t *root = json_array(); + struct table *table; + + list_for_each_entry(table, &ctx->cache->list, list) { + if (family != NFPROTO_UNSPEC && + table->handle.family != family) + continue; + + json_array_append_new(root, table_print_json(ctx->octx, table)); + } + + return root; +} + +static json_t *do_list_table_json(struct netlink_ctx *ctx, + struct cmd *cmd, struct table *table) +{ + return table_print_json_full(ctx, table); +} + +static json_t *do_list_chain_json(struct netlink_ctx *ctx, + struct cmd *cmd, struct table *table) +{ + json_t *root = json_array(); + struct chain *chain; + struct rule *rule; + + list_for_each_entry(chain, &table->chains, list) { + if (chain->handle.family != cmd->handle.family || + strcmp(cmd->handle.chain.name, chain->handle.chain.name)) + continue; + + json_array_append_new(root, chain_print_json(ctx->octx, chain)); + + list_for_each_entry(rule, &chain->rules, list) { + json_t *tmp = rule_print_json(ctx->octx, rule); + + json_array_append_new(root, tmp); + } + } + + return root; +} + +static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + json_t *root = json_array(); + struct table *table; + struct chain *chain; + + list_for_each_entry(table, &ctx->cache->list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + list_for_each_entry(chain, &table->chains, list) { + json_t *tmp = chain_print_json(ctx->octx, chain); + + json_array_append_new(root, tmp); + } + } + + return root; +} + +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); + + if (set == NULL) + return json_null(); + + return json_pack("[o]", set_print_json(ctx->octx, set)); +} + +static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct output_ctx *octx = ctx->octx; + json_t *root = json_array(); + struct table *table; + struct set *set; + + list_for_each_entry(table, &ctx->cache->list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + list_for_each_entry(set, &table->sets, list) { + if (cmd->obj == CMD_OBJ_SETS && + (set->flags & NFT_SET_ANONYMOUS || + set->flags & NFT_SET_MAP)) + continue; + if (cmd->obj == CMD_OBJ_METERS && + !(set->flags & NFT_SET_EVAL)) + continue; + if (cmd->obj == CMD_OBJ_MAPS && + !(set->flags & NFT_SET_MAP)) + continue; + json_array_append_new(root, set_print_json(octx, set)); + } + } + + return root; +} + +static json_t *do_list_obj_json(struct netlink_ctx *ctx, + struct cmd *cmd, uint32_t type) +{ + json_t *root = json_array(); + struct table *table; + struct obj *obj; + + list_for_each_entry(table, &ctx->cache->list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + if (cmd->handle.table.name && + strcmp(cmd->handle.table.name, table->handle.table.name)) + continue; + + list_for_each_entry(obj, &table->objs, list) { + if (obj->type != type || + (cmd->handle.obj.name && + strcmp(cmd->handle.obj.name, obj->handle.obj.name))) + continue; + + json_array_append_new(root, + obj_print_json(ctx->octx, obj)); + } + } + + return root; +} + +static json_t *do_list_flowtables_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + json_t *root = json_array(), *tmp; + struct flowtable *flowtable; + struct table *table; + + list_for_each_entry(table, &ctx->cache->list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + list_for_each_entry(flowtable, &table->flowtables, list) { + tmp = flowtable_print_json(flowtable); + json_array_append_new(root, tmp); + } + } + + return root; +} + +int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct table *table = NULL; + json_t *root; + + if (cmd->handle.table.name) + table = table_lookup(&cmd->handle, ctx->cache); + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (!cmd->handle.table.name) { + root = do_list_tables_json(ctx, cmd); + break; + } + root = do_list_table_json(ctx, cmd, table); + break; + case CMD_OBJ_CHAIN: + root = do_list_chain_json(ctx, cmd, table); + break; + case CMD_OBJ_CHAINS: + root = do_list_chains_json(ctx, cmd); + break; + case CMD_OBJ_SETS: + root = do_list_sets_json(ctx, cmd); + break; + case CMD_OBJ_SET: + root = do_list_set_json(ctx, cmd, table); + break; + case CMD_OBJ_RULESET: + root = do_list_ruleset_json(ctx, cmd); + break; + case CMD_OBJ_METERS: + root = do_list_sets_json(ctx, cmd); + break; + case CMD_OBJ_METER: + root = do_list_set_json(ctx, cmd, table); + break; + case CMD_OBJ_MAPS: + root = do_list_sets_json(ctx, cmd); + break; + case CMD_OBJ_MAP: + root = do_list_set_json(ctx, cmd, table); + break; + case CMD_OBJ_COUNTER: + case CMD_OBJ_COUNTERS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_COUNTER); + break; + case CMD_OBJ_QUOTA: + case CMD_OBJ_QUOTAS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_QUOTA); + break; + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_HELPERS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_CT_HELPER); + break; + case CMD_OBJ_LIMIT: + case CMD_OBJ_LIMITS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_LIMIT); + break; + case CMD_OBJ_FLOWTABLES: + root = do_list_flowtables_json(ctx, cmd); + break; + default: + BUG("invalid command object type %u\n", cmd->obj); + } + + if (json_is_array(root) && !json_array_size(root)) { + json_decref(root); + root = json_null(); + } + root = json_pack("{s:o}", "nftables", root); + json_dumpf(root, ctx->octx->output_fp, 0); + json_decref(root); + return 0; +} diff --git a/src/libnftables.c b/src/libnftables.c index ae61ce652941d..68e53f70cae8f 100644 --- a/src/libnftables.c +++ b/src/libnftables.c @@ -373,6 +373,22 @@ void nft_ctx_output_set_echo(struct nft_ctx *ctx, bool val) ctx->output.echo = val; } +bool nft_ctx_output_get_json(struct nft_ctx *ctx) +{ +#ifdef HAVE_LIBJANSSON + return ctx->output.json; +#else + return false; +#endif +} + +void nft_ctx_output_set_json(struct nft_ctx *ctx, bool val) +{ +#ifdef HAVE_LIBJANSSON + ctx->output.json = val; +#endif +} + static const struct input_descriptor indesc_cmdline = { .type = INDESC_BUFFER, .name = "<cmdline>", diff --git a/src/main.c b/src/main.c index d26ea01889a08..f36159744744a 100644 --- a/src/main.c +++ b/src/main.c @@ -31,6 +31,7 @@ enum opt_vals { OPT_FILE = 'f', OPT_INTERACTIVE = 'i', OPT_INCLUDEPATH = 'I', + OPT_JSON = 'j', OPT_NUMERIC = 'n', OPT_STATELESS = 's', OPT_IP2NAME = 'N', @@ -40,7 +41,7 @@ enum opt_vals { OPT_INVALID = '?', }; -#define OPTSTRING "hvcf:iI:vnsNae" +#define OPTSTRING "hvcf:iI:jvnsNae" static const struct option options[] = { { @@ -94,6 +95,10 @@ static const struct option options[] = { .name = "echo", .val = OPT_ECHO, }, + { + .name = "json", + .val = OPT_JSON, + }, { .name = NULL } @@ -112,6 +117,7 @@ static void show_help(const char *name) " -f, --file <filename> Read input from <filename>\n" " -i, --interactive Read input from interactive CLI\n" "\n" +" -j, --json Format output in JSON\n" " -n, --numeric When specified once, show network addresses numerically (default behaviour).\n" " Specify twice to also show Internet services (port numbers) numerically.\n" " Specify three times to also show protocols, user IDs, and group IDs numerically.\n" @@ -255,6 +261,9 @@ int main(int argc, char * const *argv) case OPT_ECHO: nft_ctx_output_set_echo(nft, true); break; + case OPT_JSON: + nft_ctx_output_set_json(nft, true); + break; case OPT_INVALID: exit(EXIT_FAILURE); } diff --git a/src/meta.c b/src/meta.c index fb94ed406d4a5..ff0cb122d7dfa 100644 --- a/src/meta.c +++ b/src/meta.c @@ -35,6 +35,7 @@ #include <utils.h> #include <erec.h> #include <iface.h> +#include <json.h> static struct symbol_table *realm_tbl; void realm_table_meta_init(void) @@ -251,6 +252,7 @@ const struct datatype uid_type = { .size = sizeof(uid_t) * BITS_PER_BYTE, .basetype = &integer_type, .print = uid_type_print, + .json = uid_type_json, .parse = uid_type_parse, }; @@ -303,6 +305,7 @@ const struct datatype gid_type = { .size = sizeof(gid_t) * BITS_PER_BYTE, .basetype = &integer_type, .print = gid_type_print, + .json = gid_type_json, .parse = gid_type_parse, }; @@ -366,6 +369,7 @@ const struct datatype devgroup_type = { .size = 4 * BITS_PER_BYTE, .basetype = &integer_type, .print = devgroup_type_print, + .json = devgroup_type_json, .parse = devgroup_type_parse, .flags = DTYPE_F_PREFIX, }; @@ -550,6 +554,7 @@ static const struct expr_ops meta_expr_ops = { .type = EXPR_META, .name = "meta", .print = meta_expr_print, + .json = meta_expr_json, .cmp = meta_expr_cmp, .clone = meta_expr_clone, .pctx_update = meta_expr_pctx_update, @@ -603,6 +608,7 @@ static const struct stmt_ops meta_stmt_ops = { .type = STMT_META, .name = "meta", .print = meta_stmt_print, + .json = meta_stmt_json, }; struct stmt *meta_stmt_alloc(const struct location *loc, enum nft_meta_keys key, diff --git a/src/numgen.c b/src/numgen.c index aa6da490d5d99..b7751b0727c2c 100644 --- a/src/numgen.c +++ b/src/numgen.c @@ -55,6 +55,7 @@ static const struct expr_ops numgen_expr_ops = { .type = EXPR_NUMGEN, .name = "numgen", .print = numgen_expr_print, + .json = numgen_expr_json, .cmp = numgen_expr_cmp, .clone = numgen_expr_clone, }; diff --git a/src/payload.c b/src/payload.c index 09665a0e81568..6517686cbfba5 100644 --- a/src/payload.c +++ b/src/payload.c @@ -25,6 +25,7 @@ #include <payload.h> #include <gmputil.h> #include <utils.h> +#include <json.h> bool payload_is_known(const struct expr *expr) { @@ -107,6 +108,7 @@ static const struct expr_ops payload_expr_ops = { .type = EXPR_PAYLOAD, .name = "payload", .print = payload_expr_print, + .json = payload_expr_json, .cmp = payload_expr_cmp, .clone = payload_expr_clone, .pctx_update = payload_expr_pctx_update, @@ -191,6 +193,7 @@ static const struct stmt_ops payload_stmt_ops = { .type = STMT_PAYLOAD, .name = "payload", .print = payload_stmt_print, + .json = payload_stmt_json, }; struct stmt *payload_stmt_alloc(const struct location *loc, diff --git a/src/rt.c b/src/rt.c index 2530b663bb249..caa4947d048a5 100644 --- a/src/rt.c +++ b/src/rt.c @@ -22,6 +22,7 @@ #include <datatype.h> #include <rt.h> #include <rule.h> +#include <json.h> static struct symbol_table *realm_tbl; void realm_table_rt_init(void) @@ -112,6 +113,7 @@ static const struct expr_ops rt_expr_ops = { .type = EXPR_RT, .name = "rt", .print = rt_expr_print, + .json = rt_expr_json, .cmp = rt_expr_cmp, .clone = rt_expr_clone, }; diff --git a/src/rule.c b/src/rule.c index 7f00a24cda73d..5515f001bfe39 100644 --- a/src/rule.c +++ b/src/rule.c @@ -21,6 +21,7 @@ #include <utils.h> #include <netdb.h> #include <netlink.h> +#include <json.h> #include <libnftnl/common.h> #include <libnftnl/ruleset.h> @@ -1811,6 +1812,9 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) { struct table *table = NULL; + if (ctx->octx->json) + return do_command_list_json(ctx, cmd); + if (cmd->handle.table.name != NULL) table = table_lookup(&cmd->handle, ctx->cache); diff --git a/src/statement.c b/src/statement.c index 8160e0adfce49..d2910018b47eb 100644 --- a/src/statement.c +++ b/src/statement.c @@ -24,6 +24,7 @@ #include <utils.h> #include <list.h> #include <xt.h> +#include <json.h> #include <netinet/in.h> #include <linux/netfilter/nf_nat.h> @@ -79,6 +80,7 @@ static const struct stmt_ops expr_stmt_ops = { .type = STMT_EXPRESSION, .name = "expression", .print = expr_stmt_print, + .json = expr_stmt_json, .destroy = expr_stmt_destroy, }; @@ -95,6 +97,7 @@ static const struct stmt_ops verdict_stmt_ops = { .type = STMT_VERDICT, .name = "verdict", .print = expr_stmt_print, + .json = verdict_stmt_json, .destroy = expr_stmt_destroy, }; @@ -137,6 +140,7 @@ static const struct stmt_ops meter_stmt_ops = { .type = STMT_METER, .name = "meter", .print = meter_stmt_print, + .json = meter_stmt_json, .destroy = meter_stmt_destroy, }; @@ -160,6 +164,7 @@ static const struct stmt_ops counter_stmt_ops = { .type = STMT_COUNTER, .name = "counter", .print = counter_stmt_print, + .json = counter_stmt_json, }; struct stmt *counter_stmt_alloc(const struct location *loc) @@ -204,6 +209,7 @@ static const struct stmt_ops objref_stmt_ops = { .type = STMT_OBJREF, .name = "objref", .print = objref_stmt_print, + .json = objref_stmt_json, }; struct stmt *objref_stmt_alloc(const struct location *loc) @@ -293,6 +299,7 @@ static const struct stmt_ops log_stmt_ops = { .type = STMT_LOG, .name = "log", .print = log_stmt_print, + .json = log_stmt_json, .destroy = log_stmt_destroy, }; @@ -376,6 +383,7 @@ static const struct stmt_ops limit_stmt_ops = { .type = STMT_LIMIT, .name = "limit", .print = limit_stmt_print, + .json = limit_stmt_json, }; struct stmt *limit_stmt_alloc(const struct location *loc) @@ -409,6 +417,7 @@ static const struct stmt_ops queue_stmt_ops = { .type = STMT_QUEUE, .name = "queue", .print = queue_stmt_print, + .json = queue_stmt_json, }; struct stmt *queue_stmt_alloc(const struct location *loc) @@ -436,6 +445,7 @@ static const struct stmt_ops quota_stmt_ops = { .type = STMT_QUOTA, .name = "quota", .print = quota_stmt_print, + .json = quota_stmt_json, }; struct stmt *quota_stmt_alloc(const struct location *loc) @@ -483,6 +493,7 @@ static const struct stmt_ops reject_stmt_ops = { .type = STMT_REJECT, .name = "reject", .print = reject_stmt_print, + .json = reject_stmt_json, }; struct stmt *reject_stmt_alloc(const struct location *loc) @@ -572,6 +583,7 @@ static const struct stmt_ops nat_stmt_ops = { .type = STMT_NAT, .name = "nat", .print = nat_stmt_print, + .json = nat_stmt_json, .destroy = nat_stmt_destroy, }; @@ -608,6 +620,7 @@ static const struct stmt_ops set_stmt_ops = { .type = STMT_SET, .name = "set", .print = set_stmt_print, + .json = set_stmt_json, .destroy = set_stmt_destroy, }; @@ -669,6 +682,7 @@ static const struct stmt_ops dup_stmt_ops = { .type = STMT_DUP, .name = "dup", .print = dup_stmt_print, + .json = dup_stmt_json, .destroy = dup_stmt_destroy, }; @@ -692,6 +706,7 @@ static const struct stmt_ops fwd_stmt_ops = { .type = STMT_FWD, .name = "fwd", .print = fwd_stmt_print, + .json = fwd_stmt_json, .destroy = fwd_stmt_destroy, }; -- 2.17.0 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html