summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac14
-rw-r--r--include/datatype.h4
-rw-r--r--include/expression.h3
-rw-r--r--include/gmputil.h1
-rw-r--r--include/json.h161
-rw-r--r--include/nftables.h1
-rw-r--r--include/nftables/libnftables.h2
-rw-r--r--include/statement.h3
-rw-r--r--src/Makefile.am5
-rw-r--r--src/ct.c4
-rw-r--r--src/datatype.c8
-rw-r--r--src/expression.c15
-rw-r--r--src/exthdr.c2
-rw-r--r--src/fib.c1
-rw-r--r--src/hash.c1
-rw-r--r--src/json.c1564
-rw-r--r--src/libnftables.c16
-rw-r--r--src/main.c11
-rw-r--r--src/meta.c6
-rw-r--r--src/numgen.c1
-rw-r--r--src/payload.c3
-rw-r--r--src/rt.c2
-rw-r--r--src/rule.c4
-rw-r--r--src/statement.c15
24 files changed, 1845 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index eb5649dd..36148aea 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 \
@@ -132,4 +143,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 56892ddc..b641f0ed 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 23d6bd23..15af35e8 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 084aa622..73959c17 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 00000000..579bd5df
--- /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 661c1e17..f88d0530 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 652e0ca9..4bfdaf9a 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 2c6d0dfa..de26549b 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 92e6795f..c5c3b0bc 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 a1a91f3a..1d50382b 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 d5a5091e..c77d228e 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,
};
@@ -987,6 +993,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,
};
@@ -1163,4 +1170,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 3c5ea0ff..53fb1811 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 06a82070..cb0a58e8 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 069411f0..9a19cc34 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 e6999637..a2d2314b 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 00000000..e458eb3e
--- /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 ae61ce65..68e53f70 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 d26ea018..f3615974 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[] = {
{
@@ -95,6 +96,10 @@ static const struct option options[] = {
.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 fb94ed40..ff0cb122 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 aa6da490..b7751b07 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 09665a0e..6517686c 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 2530b663..caa4947d 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 a3ed5179..3e8dea40 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>
@@ -1819,6 +1820,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 8160e0ad..d2910018 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,
};