diff mbox series

[iproute2] ematch: support JSON output

Message ID 20240314002415.26518-1-stephen@networkplumber.org (mailing list archive)
State Changes Requested
Delegated to: David Ahern
Headers show
Series [iproute2] ematch: support JSON output | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Stephen Hemminger March 14, 2024, 12:24 a.m. UTC
The ematch in filter was missing support JSON output
and therefore would generate bogus output.

Note: support for JSON output with ipt would be difficult
to implement since xtables API doesn't have a JSON API,
and anyway ipt is legacy. Therefore attempts to JSON
output for ematch ipt generates an error.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 tc/em_canid.c | 31 +++++++++-------
 tc/em_cmp.c   | 30 ++++++++++------
 tc/em_ipset.c |  9 +++--
 tc/em_ipt.c   |  6 ++++
 tc/em_meta.c  | 99 ++++++++++++++++++++++++++++-----------------------
 tc/em_nbyte.c | 12 ++++---
 tc/em_u32.c   | 10 +++---
 tc/m_ematch.c | 47 +++++++++++++++---------
 8 files changed, 149 insertions(+), 95 deletions(-)

Comments

David Ahern March 15, 2024, 3:12 p.m. UTC | #1
On 3/13/24 6:24 PM, Stephen Hemminger wrote:
> diff --git a/tc/em_canid.c b/tc/em_canid.c
> index 228547529134..815ed8c7bce0 100644
> --- a/tc/em_canid.c
> +++ b/tc/em_canid.c
> @@ -154,24 +154,29 @@ static int canid_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
>  
>  		if (pcfltr->can_id & CAN_EFF_FLAG) {
>  			if (pcfltr->can_mask == (CAN_EFF_FLAG | CAN_EFF_MASK))
> -				fprintf(fd, "eff 0x%"PRIX32,
> -						pcfltr->can_id & CAN_EFF_MASK);
> -			else
> -				fprintf(fd, "eff 0x%"PRIX32":0x%"PRIX32,
> -						pcfltr->can_id & CAN_EFF_MASK,
> -						pcfltr->can_mask & CAN_EFF_MASK);
> +				print_0xhex(PRINT_ANY, "eff", "eff 0x%"PRIX32,
> +					    pcfltr->can_id & CAN_EFF_MASK);
> +			else {
> +				print_0xhex(PRINT_ANY, "eff", "eff 0x%"PRIX32,
> +					    pcfltr->can_id & CAN_EFF_MASK);
> +				print_0xhex(PRINT_ANY, "mask", ":0x%"PRIX32,
> +					    pcfltr->can_mask & CAN_EFF_MASK);
> +			}

if the else branch has {}, the first one should as well.

>  		} else {
> +			

unneeded extra newline

>  			if (pcfltr->can_mask == (CAN_EFF_FLAG | CAN_SFF_MASK))
> -				fprintf(fd, "sff 0x%"PRIX32,
> -						pcfltr->can_id & CAN_SFF_MASK);
> -			else
> -				fprintf(fd, "sff 0x%"PRIX32":0x%"PRIX32,
> -						pcfltr->can_id & CAN_SFF_MASK,
> -						pcfltr->can_mask & CAN_SFF_MASK);
> +				print_0xhex(PRINT_ANY, "sff", "sff 0x%"PRIX32,
> +					    pcfltr->can_id & CAN_SFF_MASK);
> +			else {
> +				print_0xhex(PRINT_ANY, "sff", "sff 0x%"PRIX32,
> +					    pcfltr->can_id & CAN_SFF_MASK);
> +				print_0xhex(PRINT_ANY, "mask", ":0x%"PRIX32,
> +					    pcfltr->can_mask & CAN_SFF_MASK);
> +			}
>  		}
>  
>  		if ((i + 1) < rules_count)
> -			fprintf(fd, " ");
> +			print_string(PRINT_FP, NULL, " ", NULL);
>  	}
>  
>  	return 0;
> diff --git a/tc/em_cmp.c b/tc/em_cmp.c
> index dfd123df1e10..9e2d14077c6c 100644
> --- a/tc/em_cmp.c
> +++ b/tc/em_cmp.c
> @@ -138,6 +138,8 @@ static int cmp_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
>  			  int data_len)
>  {
>  	struct tcf_em_cmp *cmp = data;
> +	const char *align = NULL;
> +	const char *op = NULL;
>  
>  	if (data_len < sizeof(*cmp)) {
>  		fprintf(stderr, "CMP header size mismatch\n");
> @@ -145,28 +147,36 @@ static int cmp_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
>  	}
>  
>  	if (cmp->align == TCF_EM_ALIGN_U8)
> -		fprintf(fd, "u8 ");
> +		align = "u8";
>  	else if (cmp->align == TCF_EM_ALIGN_U16)
> -		fprintf(fd, "u16 ");
> +		align = "u16";
>  	else if (cmp->align == TCF_EM_ALIGN_U32)
> -		fprintf(fd, "u32 ");
> +		align = "u32";
>  
> -	fprintf(fd, "at %d layer %d ", cmp->off, cmp->layer);
> +	print_uint(PRINT_JSON, "align", "%u ", cmp->align);
> +	if (align)
> +		print_string(PRINT_FP, NULL, "%s ", align);
> +
> +	print_uint(PRINT_ANY, "offset", "at %u ", cmp->off);
> +	print_uint(PRINT_ANY, "layer", "layer %u ", cmp->layer);
>  
>  	if (cmp->mask)
> -		fprintf(fd, "mask 0x%x ", cmp->mask);
> +		print_0xhex(PRINT_ANY, "mask", "mask 0x%x ", cmp->mask);
>  
>  	if (cmp->flags & TCF_EM_CMP_TRANS)
> -		fprintf(fd, "trans ");
> +		print_null(PRINT_ANY, "trans", "trans ", NULL);
>  
>  	if (cmp->opnd == TCF_EM_OPND_EQ)
> -		fprintf(fd, "eq ");
> +		op = "eq";
>  	else if (cmp->opnd == TCF_EM_OPND_LT)
> -		fprintf(fd, "lt ");
> +		op = "lt";
>  	else if (cmp->opnd == TCF_EM_OPND_GT)
> -		fprintf(fd, "gt ");
> +		op = "gt";
> +
> +	if (op)
> +		print_string(PRINT_ANY, "opnd", "%s ", op);

seems like a change in output which tends to break the tdc suite. Please
cc Jamal on tc patches.

>  
> -	fprintf(fd, "%d", cmp->val);
> +	print_uint(PRINT_ANY, "val", "%u", cmp->val);
>  
>  	return 0;
>  }


> @@ -436,53 +445,51 @@ static inline int print_value(FILE *fd, int type, struct rtattr *rta)
>  	}
>  
>  	switch (type) {
> -		case TCF_META_TYPE_INT:
> -			if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
> -				fprintf(stderr, "meta int type value TLV " \
> -				    "size mismatch.\n");
> -				return -1;
> -			}
> -			fprintf(fd, "%d", rta_getattr_u32(rta));
> -			break;
> +	case TCF_META_TYPE_INT:
> +		if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
> +			fprintf(stderr,
> +				"meta int type value TLV size mismatch.\n");
> +			return -1;
> +		}
> +		print_uint(PRINT_ANY, "value", "%u", rta_getattr_u32(rta));
> +		break;
>  
> -		case TCF_META_TYPE_VAR:
> -			print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta));
> -			break;
> +	case TCF_META_TYPE_VAR:
> +		print_binary(RTA_DATA(rta), RTA_PAYLOAD(rta));
> +		break;
>  	}

whitespace cleanup should be a separate patch.

in general this is a large patch. It would be better as a series

pw-bot: cr
diff mbox series

Patch

diff --git a/tc/em_canid.c b/tc/em_canid.c
index 228547529134..815ed8c7bce0 100644
--- a/tc/em_canid.c
+++ b/tc/em_canid.c
@@ -154,24 +154,29 @@  static int canid_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 
 		if (pcfltr->can_id & CAN_EFF_FLAG) {
 			if (pcfltr->can_mask == (CAN_EFF_FLAG | CAN_EFF_MASK))
-				fprintf(fd, "eff 0x%"PRIX32,
-						pcfltr->can_id & CAN_EFF_MASK);
-			else
-				fprintf(fd, "eff 0x%"PRIX32":0x%"PRIX32,
-						pcfltr->can_id & CAN_EFF_MASK,
-						pcfltr->can_mask & CAN_EFF_MASK);
+				print_0xhex(PRINT_ANY, "eff", "eff 0x%"PRIX32,
+					    pcfltr->can_id & CAN_EFF_MASK);
+			else {
+				print_0xhex(PRINT_ANY, "eff", "eff 0x%"PRIX32,
+					    pcfltr->can_id & CAN_EFF_MASK);
+				print_0xhex(PRINT_ANY, "mask", ":0x%"PRIX32,
+					    pcfltr->can_mask & CAN_EFF_MASK);
+			}
 		} else {
+			
 			if (pcfltr->can_mask == (CAN_EFF_FLAG | CAN_SFF_MASK))
-				fprintf(fd, "sff 0x%"PRIX32,
-						pcfltr->can_id & CAN_SFF_MASK);
-			else
-				fprintf(fd, "sff 0x%"PRIX32":0x%"PRIX32,
-						pcfltr->can_id & CAN_SFF_MASK,
-						pcfltr->can_mask & CAN_SFF_MASK);
+				print_0xhex(PRINT_ANY, "sff", "sff 0x%"PRIX32,
+					    pcfltr->can_id & CAN_SFF_MASK);
+			else {
+				print_0xhex(PRINT_ANY, "sff", "sff 0x%"PRIX32,
+					    pcfltr->can_id & CAN_SFF_MASK);
+				print_0xhex(PRINT_ANY, "mask", ":0x%"PRIX32,
+					    pcfltr->can_mask & CAN_SFF_MASK);
+			}
 		}
 
 		if ((i + 1) < rules_count)
-			fprintf(fd, " ");
+			print_string(PRINT_FP, NULL, " ", NULL);
 	}
 
 	return 0;
diff --git a/tc/em_cmp.c b/tc/em_cmp.c
index dfd123df1e10..9e2d14077c6c 100644
--- a/tc/em_cmp.c
+++ b/tc/em_cmp.c
@@ -138,6 +138,8 @@  static int cmp_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 			  int data_len)
 {
 	struct tcf_em_cmp *cmp = data;
+	const char *align = NULL;
+	const char *op = NULL;
 
 	if (data_len < sizeof(*cmp)) {
 		fprintf(stderr, "CMP header size mismatch\n");
@@ -145,28 +147,36 @@  static int cmp_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 	}
 
 	if (cmp->align == TCF_EM_ALIGN_U8)
-		fprintf(fd, "u8 ");
+		align = "u8";
 	else if (cmp->align == TCF_EM_ALIGN_U16)
-		fprintf(fd, "u16 ");
+		align = "u16";
 	else if (cmp->align == TCF_EM_ALIGN_U32)
-		fprintf(fd, "u32 ");
+		align = "u32";
 
-	fprintf(fd, "at %d layer %d ", cmp->off, cmp->layer);
+	print_uint(PRINT_JSON, "align", "%u ", cmp->align);
+	if (align)
+		print_string(PRINT_FP, NULL, "%s ", align);
+
+	print_uint(PRINT_ANY, "offset", "at %u ", cmp->off);
+	print_uint(PRINT_ANY, "layer", "layer %u ", cmp->layer);
 
 	if (cmp->mask)
-		fprintf(fd, "mask 0x%x ", cmp->mask);
+		print_0xhex(PRINT_ANY, "mask", "mask 0x%x ", cmp->mask);
 
 	if (cmp->flags & TCF_EM_CMP_TRANS)
-		fprintf(fd, "trans ");
+		print_null(PRINT_ANY, "trans", "trans ", NULL);
 
 	if (cmp->opnd == TCF_EM_OPND_EQ)
-		fprintf(fd, "eq ");
+		op = "eq";
 	else if (cmp->opnd == TCF_EM_OPND_LT)
-		fprintf(fd, "lt ");
+		op = "lt";
 	else if (cmp->opnd == TCF_EM_OPND_GT)
-		fprintf(fd, "gt ");
+		op = "gt";
+
+	if (op)
+		print_string(PRINT_ANY, "opnd", "%s ", op);
 
-	fprintf(fd, "%d", cmp->val);
+	print_uint(PRINT_ANY, "val", "%u", cmp->val);
 
 	return 0;
 }
diff --git a/tc/em_ipset.c b/tc/em_ipset.c
index f97abaf3cfb7..ce2c8e75fc58 100644
--- a/tc/em_ipset.c
+++ b/tc/em_ipset.c
@@ -243,10 +243,15 @@  static int ipset_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 
 	if (get_set_byid(setname, set_info->index))
 		return -1;
-	fputs(setname, fd);
+
+	open_json_array(PRINT_ANY, setname);
+
 	for (i = 1; i <= set_info->dim; i++) {
-		fprintf(fd, "%s%s", i == 1 ? " " : ",", set_info->flags & (1 << i) ? "src" : "dst");
+		print_string(PRINT_FP, NULL, "%s", i == 1 ? " " : ",");
+		print_string(PRINT_ANY, NULL, "%s", 
+			     set_info->flags & (1 << i) ? "src" : "dst");
 	}
+	close_json_array(PRINT_JSON, NULL);
 
 	return 0;
 }
diff --git a/tc/em_ipt.c b/tc/em_ipt.c
index 69efefd8c5e3..6102b5513853 100644
--- a/tc/em_ipt.c
+++ b/tc/em_ipt.c
@@ -175,6 +175,12 @@  static int em_ipt_print_epot(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 	const char *mname;
 	__u8 nfproto;
 
+	/* xtables-legacy doesn't support JSON print so skip it */
+	if (is_json_context()) {
+		fprintf(stderr, "xtables-legacy json not supported\n");
+		return -1;
+	}
+	
 	if (parse_rtattr(tb, TCA_EM_IPT_MAX, data, data_len) < 0)
 		return -1;
 
diff --git a/tc/em_meta.c b/tc/em_meta.c
index 6a5654f3a28b..662596283d12 100644
--- a/tc/em_meta.c
+++ b/tc/em_meta.c
@@ -406,29 +406,38 @@  static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
 }
 #undef PARSE_ERR
 
-static inline void print_binary(FILE *fd, unsigned char *str, int len)
+static void print_binary(const unsigned char *str, int len)
 {
 	int i;
 
+	if (is_json_context()) {
+		open_json_array(PRINT_JSON, "data");
+
+		for (i = 0; i < len; i++)
+			print_0xhex(PRINT_JSON, NULL, NULL, str[i]);
+		close_json_array(PRINT_JSON, NULL);
+		return;
+	}
+	
 	for (i = 0; i < len; i++)
 		if (!isprint(str[i]))
 			goto binary;
 
 	for (i = 0; i < len; i++)
-		fprintf(fd, "%c", str[i]);
+		putchar(str[i]);
 	return;
 
 binary:
 	for (i = 0; i < len; i++)
-		fprintf(fd, "%02x ", str[i]);
+		printf("%02x ", str[i]);
 
-	fprintf(fd, "\"");
+	putchar ('"');
 	for (i = 0; i < len; i++)
-		fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.');
-	fprintf(fd, "\"");
+		putchar(isprint(str[i]) ? str[i] : '.');
+	putchar ('"');
 }
 
-static inline int print_value(FILE *fd, int type, struct rtattr *rta)
+static int print_value(int type, struct rtattr *rta)
 {
 	if (rta == NULL) {
 		fprintf(stderr, "Missing value TLV\n");
@@ -436,53 +445,51 @@  static inline int print_value(FILE *fd, int type, struct rtattr *rta)
 	}
 
 	switch (type) {
-		case TCF_META_TYPE_INT:
-			if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
-				fprintf(stderr, "meta int type value TLV " \
-				    "size mismatch.\n");
-				return -1;
-			}
-			fprintf(fd, "%d", rta_getattr_u32(rta));
-			break;
+	case TCF_META_TYPE_INT:
+		if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
+			fprintf(stderr,
+				"meta int type value TLV size mismatch.\n");
+			return -1;
+		}
+		print_uint(PRINT_ANY, "value", "%u", rta_getattr_u32(rta));
+		break;
 
-		case TCF_META_TYPE_VAR:
-			print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta));
-			break;
+	case TCF_META_TYPE_VAR:
+		print_binary(RTA_DATA(rta), RTA_PAYLOAD(rta));
+		break;
 	}
 
 	return 0;
 }
 
-static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta)
+static int print_object(struct tcf_meta_val *obj, struct rtattr *rta)
 {
 	int id = TCF_META_ID(obj->kind);
 	int type = TCF_META_TYPE(obj->kind);
 	const struct meta_entry *entry;
 
 	if (id == TCF_META_ID_VALUE)
-		return print_value(fd, type, rta);
+		return print_value(type, rta);
 
 	entry = lookup_meta_entry_byid(id);
 
 	if (entry == NULL)
-		fprintf(fd, "[unknown meta id %d]", id);
+		print_int(PRINT_ANY, "id", "[unknown meta id %d]", id);
 	else
-		fprintf(fd, "%s", entry->kind);
+		print_string(PRINT_ANY, "id", "%s", entry->kind);
 
 	if (obj->shift)
-		fprintf(fd, " shift %d", obj->shift);
+		print_int(PRINT_ANY, "shift", " shift %d", obj->shift);
 
-	switch (type) {
-		case TCF_META_TYPE_INT:
-			if (rta) {
-				if (RTA_PAYLOAD(rta) < sizeof(__u32))
-					goto size_mismatch;
+	if (type == TCF_META_TYPE_INT && rta) {
+		__u32 mask;
+			
+		if (RTA_PAYLOAD(rta) < sizeof(__u32))
+			goto size_mismatch;
 
-				if (rta_getattr_u32(rta))
-					fprintf(fd, " mask 0x%08x",
-						rta_getattr_u32(rta));
-			}
-			break;
+		mask = rta_getattr_u32(rta);
+		if (mask)
+			print_0xhex(PRINT_ANY, "mask", " mask 0x%08x", mask);
 	}
 
 	return 0;
@@ -498,6 +505,7 @@  static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 {
 	struct rtattr *tb[TCA_EM_META_MAX+1];
 	struct tcf_meta_hdr *meta_hdr;
+	const char *op = NULL;
 
 	if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0)
 		return -1;
@@ -514,22 +522,25 @@  static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 
 	meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]);
 
-	if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0)
+	if (print_object(&meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0)
 		return -1;
 
 	switch (meta_hdr->left.op) {
-		case TCF_EM_OPND_EQ:
-			fprintf(fd, " eq ");
-			break;
-		case TCF_EM_OPND_LT:
-			fprintf(fd, " lt ");
-			break;
-		case TCF_EM_OPND_GT:
-			fprintf(fd, " gt ");
-			break;
+	case TCF_EM_OPND_EQ:
+		op = "eq";
+		break;
+	case TCF_EM_OPND_LT:
+		op = "lt";
+		break;
+	case TCF_EM_OPND_GT:
+		op = "gt";
+		break;
 	}
 
-	return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]);
+	if (op)
+		print_string(PRINT_ANY, "opnd", " %s ", op);
+
+	return print_object(&meta_hdr->right, tb[TCA_EM_META_RVALUE]);
 }
 
 struct ematch_util meta_ematch_util = {
diff --git a/tc/em_nbyte.c b/tc/em_nbyte.c
index 9f421fb423a6..cfcd1b413baa 100644
--- a/tc/em_nbyte.c
+++ b/tc/em_nbyte.c
@@ -116,13 +116,17 @@  static int nbyte_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 
 	needle = data + sizeof(*nb);
 
+	open_json_array(PRINT_JSON, "needle");
 	for (i = 0; i < nb->len; i++)
-		fprintf(fd, "%02x ", needle[i]);
+		print_0xhex(PRINT_ANY, NULL, "%02x ", needle[i]);
 
-	fprintf(fd, "\"");
+	close_json_array(PRINT_ANY, "\"");
 	for (i = 0; i < nb->len; i++)
-		fprintf(fd, "%c", isprint(needle[i]) ? needle[i] : '.');
-	fprintf(fd, "\" at %d layer %d", nb->off, nb->layer);
+		print_0xhex(PRINT_FP, NULL, "%c",
+			    isprint(needle[i]) ? needle[i] : '.');
+
+	print_uint(PRINT_ANY, "offset", "\" at %u ", nb->off);
+	print_uint(PRINT_ANY, "layer", "layer %u", nb->layer);
 
 	return 0;
 }
diff --git a/tc/em_u32.c b/tc/em_u32.c
index a83382ba4417..88feb0de8317 100644
--- a/tc/em_u32.c
+++ b/tc/em_u32.c
@@ -153,11 +153,11 @@  static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 		return -1;
 	}
 
-	fprintf(fd, "%08x/%08x at %s%d",
-	    (unsigned int) ntohl(u_key->val),
-	    (unsigned int) ntohl(u_key->mask),
-	    u_key->offmask ? "nexthdr+" : "",
-	    u_key->off);
+	print_0xhex(PRINT_ANY, "val", "%08x", ntohl(u_key->val));
+	print_0xhex(PRINT_ANY, "mask", "/%08x at ", ntohl(u_key->mask));
+	if (u_key->offmask)
+		print_null(PRINT_ANY, "nexthdr", "nexthdr+", NULL);
+	print_int(PRINT_ANY, "offset", "%d", u_key->off);
 
 	return 0;
 }
diff --git a/tc/m_ematch.c b/tc/m_ematch.c
index fefc78608d6f..81590ad18011 100644
--- a/tc/m_ematch.c
+++ b/tc/m_ematch.c
@@ -390,10 +390,18 @@  int parse_ematch(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
 	return 0;
 }
 
+static void print_ematch_indent(int prefix)
+{
+	int n;
+	
+	for (n = 0; n < prefix; n++)
+		print_string(PRINT_FP, NULL, "  ", NULL);
+}
+
 static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start,
 			    int prefix)
 {
-	int n, i = start;
+	int i = start;
 	struct tcf_ematch_hdr *hdr;
 	int dlen;
 	void *data;
@@ -411,7 +419,7 @@  static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start,
 		hdr = RTA_DATA(tb[i]);
 
 		if (hdr->flags & TCF_EM_INVERT)
-			fprintf(fd, "NOT ");
+			print_null(PRINT_ANY, "not", "NOT ", NULL);
 
 		if (hdr->kind == 0) {
 			__u32 ref;
@@ -420,40 +428,45 @@  static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start,
 				return -1;
 
 			ref = *(__u32 *) data;
-			fprintf(fd, "(\n");
-			for (n = 0; n <= prefix; n++)
-				fprintf(fd, "  ");
+			print_string(PRINT_FP, NULL, "(%s", _SL_);
+			print_ematch_indent(prefix);
+			open_json_object("match");
+			
 			if (print_ematch_seq(fd, tb, ref + 1, prefix + 1) < 0)
 				return -1;
-			for (n = 0; n < prefix; n++)
-				fprintf(fd, "  ");
-			fprintf(fd, ") ");
+
+			close_json_object();
+			print_ematch_indent(prefix);
+			print_string(PRINT_FP, NULL, ") ", NULL);
 
 		} else {
 			struct ematch_util *e;
 
 			e = get_ematch_kind_num(hdr->kind);
 			if (e == NULL)
-				fprintf(fd, "[unknown ematch %d]\n",
-				    hdr->kind);
+				fprintf(stderr, "[unknown ematch %d]\n",
+					hdr->kind);
 			else {
-				fprintf(fd, "%s(", e->kind);
+				print_string(PRINT_FP, NULL, "%s(", e->kind);
+				open_json_object(e->kind);
+
 				if (e->print_eopt(fd, hdr, data, dlen) < 0)
 					return -1;
-				fprintf(fd, ")\n");
+
+				close_json_object();
+				print_string(PRINT_FP, NULL, ")%s", _SL_);
 			}
 			if (hdr->flags & TCF_EM_REL_MASK)
-				for (n = 0; n < prefix; n++)
-					fprintf(fd, "  ");
+				print_ematch_indent(prefix);
 		}
 
 		switch (hdr->flags & TCF_EM_REL_MASK) {
 			case TCF_EM_REL_AND:
-				fprintf(fd, "AND ");
+				print_null(PRINT_ANY, "and", "AND ", NULL);
 				break;
 
 			case TCF_EM_REL_OR:
-				fprintf(fd, "OR ");
+				print_null(PRINT_ANY, "or",  "OR ", NULL);
 				break;
 
 			default:
@@ -480,7 +493,7 @@  static int print_ematch_list(FILE *fd, struct tcf_ematch_tree_hdr *hdr,
 		if (parse_rtattr_nested(tb, hdr->nmatches, rta) < 0)
 			goto errout;
 
-		fprintf(fd, "\n  ");
+		print_string(PRINT_FP, NULL, "%s  ", _SL_);
 		if (print_ematch_seq(fd, tb, 1, 1) < 0)
 			goto errout;
 	}