Skip to contentSkip to conversation

ip multicast command JSON support

PATCH In review Stephen Hemminger 3 participants 11 messages Mar 7, 2018 – Mar 8, 2018

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover ip multicast command JSON support

From: Stephen Hemminger <[email protected]>

Update maddr and mroute to support JSON.
Fix bug in ipmroute that causes it print error on every unicast route.

Stephen Hemminger (3):
ipmaddr: json and color support
ipmroute: don't complain about unicast routes
ipmroute: convert to output JSON

 ip/ipmaddr.c  |  69 ++++++++++++++++++++------------
 ip/ipmroute.c | 124 +++++++++++++++++++++++++++++++++++++---------------------
 2 files changed, 123 insertions(+), 70 deletions(-)
Signature · 1 line
2.16.1
#1 lore raw

PATCH 1/3 ipmaddr: json and color support

From: Stephen Hemminger <[email protected]>

Support printing mulitcast addresses in json and color mode. Output format is unchanged for normal use.

---
 ip/ipmaddr.c | 69 +++++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 43 insertions(+), 26 deletions(-)
ip/ipmaddr.c +43 -26
index d7bf1f99f67e..a48499029e17 100644
@@ -28,6 +28,7 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
+#include "json_print.h"
 
 static struct {
 	char *dev;
@@ -193,50 +194,66 @@ static void read_igmp6(struct ma_info **result_p)
 
 static void print_maddr(FILE *fp, struct ma_info *list)
 {
-	fprintf(fp, "\t");
+	print_string(PRINT_FP, NULL, "\t", NULL);
 
+	open_json_object(NULL);
 	if (list->addr.family == AF_PACKET) {
 		SPRINT_BUF(b1);
-		fprintf(fp, "link  %s", ll_addr_n2a((unsigned char *)list->addr.data,
-						    list->addr.bytelen, 0,
-						    b1, sizeof(b1)));
+
+		print_string(PRINT_FP, NULL, "link  ", NULL);
+		print_color_string(PRINT_ANY, COLOR_MAC, "link", "%s",
+				   ll_addr_n2a((void *)list->addr.data, list->addr.bytelen,
+					       0, b1, sizeof(b1)));
 	} else {
-		switch (list->addr.family) {
-		case AF_INET:
-			fprintf(fp, "inet  ");
-			break;
-		case AF_INET6:
-			fprintf(fp, "inet6 ");
-			break;
-		default:
-			fprintf(fp, "family %d ", list->addr.family);
-			break;
-		}
-		fprintf(fp, "%s",
-			format_host(list->addr.family,
-				    -1, list->addr.data));
+		print_string(PRINT_ANY, "family", "%-5s ",
+			     family_name(list->addr.family));
+		print_color_string(PRINT_ANY, ifa_family_color(list->addr.family),
+				   "address", "%s",
+				   format_host(list->addr.family,
+					       -1, list->addr.data));
 	}
+
 	if (list->users != 1)
-		fprintf(fp, " users %d", list->users);
+		print_uint(PRINT_ANY, "users", " users %u", list->users);
+
 	if (list->features)
-		fprintf(fp, " %s", list->features);
-	fprintf(fp, "\n");
+		print_string(PRINT_ANY, "features", " %s", list->features);
+
+	print_string(PRINT_FP, NULL, "\n", NULL);
+	close_json_object();
 }
 
 static void print_mlist(FILE *fp, struct ma_info *list)
 {
 	int cur_index = 0;
 
+	new_json_obj(json);
 	for (; list; list = list->next) {
-		if (oneline) {
-			cur_index = list->index;
-			fprintf(fp, "%d:\t%s%s", cur_index, list->name, _SL_);
-		} else if (cur_index != list->index) {
+
+		if (list->index != cur_index || oneline) {
+			if (cur_index) {
+				close_json_array(PRINT_JSON, NULL);
+				close_json_object();
+			}
+			open_json_object(NULL);
+
+			print_uint(PRINT_ANY, "ifindex", "%d:", list->index);
+			print_color_string(PRINT_ANY, COLOR_IFNAME,
+					   "ifname", "\t%s", list->name);
+			print_string(PRINT_FP, NULL, "%s", _SL_);
 			cur_index = list->index;
-			fprintf(fp, "%d:\t%s\n", cur_index, list->name);
+
+			open_json_array(PRINT_JSON, "maddr");
 		}
+
 		print_maddr(fp, list);
 	}
+	if (cur_index) {
+		close_json_array(PRINT_JSON, NULL);
+		close_json_object();
+	}
+
+	delete_json_obj();
 }
 
 static int multiaddr_list(int argc, char **argv)
Signature · 1 line
2.16.1
#2 lore raw

PATCH 2/3 ipmroute: don't complain about unicast routes

In reply to Stephen Hemminger

From: Stephen Hemminger <[email protected]>

Every non-multicast route prints an error message.
Kernel doesn't filter out unicast routes, it is up to filter function to do this.

---
 ip/ipmroute.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
ip/ipmroute.c +3 -4
index aa5029b44f41..03ca0575e571 100644
@@ -75,15 +75,14 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 		return -1;
 	}
-	if (r->rtm_type != RTN_MULTICAST) {
-		fprintf(stderr, "Not a multicast route (type: %s)\n",
-			rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+	if (r->rtm_type != RTN_MULTICAST)
 		return 0;
-	}
 
 	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
 	table = rtm_get_table(r, tb);
 
+
 	if (filter.tb > 0 && filter.tb != table)
 		return 0;
 
Signature · 1 line
2.16.1
#3 lore raw

Re: [PATCH iproute2-next 2/3] ipmroute: don't complain about unicast routes

Hello!

Quoted 21 lines · Stephen Hemminger

From: Stephen Hemminger <[email protected]>

Every non-multicast route prints an error message.
Kernel doesn't filter out unicast routes, it is up to filter function to do this.

---
  ip/ipmroute.c | 7 +++----
  1 file changed, 3 insertions(+), 4 deletions(-)
ip/ipmroute.c +0 -2
index aa5029b44f41..03ca0575e571 100644
@@ -75,15 +75,14 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
  		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
  		return -1;
  	}
-	if (r->rtm_type != RTN_MULTICAST) {
-		fprintf(stderr, "Not a multicast route (type: %s)\n",

ip/ipmroute.c int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) · line 85

Quoted 10 lines
ip/ipmroute.c +3 -2
-			rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+	if (r->rtm_type != RTN_MULTICAST)
  		return 0;
-	}
 
  	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
  	table = rtm_get_table(r, tb);
 
+

Why?

ip/ipmroute.c int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) · line 88

Quoted 3 lines
ip/ipmroute.c +0 -0
  	if (filter.tb > 0 && filter.tb != table)
  		return 0;
 

MBR, Sergei

#4 lore raw

Re: [PATCH iproute2-next 2/3] ipmroute: don't complain about unicast routes

Quoted 38 lines · On Wed, 7 Mar 2018 11:43:33 +0300 Sergei Shtylyov…

Hello!

Quoted 21 lines · Stephen Hemminger

From: Stephen Hemminger <[email protected]>

Every non-multicast route prints an error message.
Kernel doesn't filter out unicast routes, it is up to filter function to do this.

---
  ip/ipmroute.c | 7 +++----
  1 file changed, 3 insertions(+), 4 deletions(-)
ip/ipmroute.c +0 -2
index aa5029b44f41..03ca0575e571 100644
@@ -75,15 +75,14 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
  		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
  		return -1;
  	}
-	if (r->rtm_type != RTN_MULTICAST) {
-		fprintf(stderr, "Not a multicast route (type: %s)\n",

ip/ipmroute.c int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) · line 85

Quoted 10 lines
ip/ipmroute.c +3 -2
-			rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+	if (r->rtm_type != RTN_MULTICAST)
  		return 0;
-	}
 
  	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
  	table = rtm_get_table(r, tb);
 
+  

Why?

Quoted 5 lines

ip/ipmroute.c int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) · line 88

Quoted 3 lines
ip/ipmroute.c +0 -0
  	if (filter.tb > 0 && filter.tb != table)
  		return 0;
 

MBR, Sergei

The kernel dumps all routes in response to the RTM_GETROUTE and therefore all routes show up in print_mroute. On my system (which has no mcast), I get this with standard 4.14 kernel and ip commands.

$ ip mroute
Not a multicast route (type: unicast)
Not a multicast route (type: unicast)
Not a multicast route (type: unicast)
Not a multicast route (type: unicast)
Not a multicast route (type: unicast)
Not a multicast route (type: broadcast)
Not a multicast route (type: local)
Not a multicast route (type: local)
Not a multicast route (type: broadcast)
Not a multicast route (type: broadcast)
Not a multicast route (type: local)
#5 lore raw

Re: [PATCH iproute2-next 2/3] ipmroute: don't complain about unicast routes

Quoted 42 lines · Stephen Hemminger
Quoted 42 lines

Hello!

Quoted 21 lines · Stephen Hemminger

From: Stephen Hemminger <[email protected]>

Every non-multicast route prints an error message.
Kernel doesn't filter out unicast routes, it is up to filter function to do this.

---
  ip/ipmroute.c | 7 +++----
  1 file changed, 3 insertions(+), 4 deletions(-)
ip/ipmroute.c +0 -2
index aa5029b44f41..03ca0575e571 100644
@@ -75,15 +75,14 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
  		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
  		return -1;
  	}
-	if (r->rtm_type != RTN_MULTICAST) {
-		fprintf(stderr, "Not a multicast route (type: %s)\n",

ip/ipmroute.c int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) · line 85

Quoted 10 lines
ip/ipmroute.c +3 -2
-			rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+	if (r->rtm_type != RTN_MULTICAST)
  		return 0;
-	}
 
  	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
  	table = rtm_get_table(r, tb);
 
+  

Why?

Quoted 3 lines
ip/ipmroute.c +0 -0
  	if (filter.tb > 0 && filter.tb != table)
  		return 0;
 
Quoted 5 lines
Quoted 1 line

MBR, Sergei

The kernel dumps all routes in response to the RTM_GETROUTE and therefore all routes show up in print_mroute. On my system (which has no mcast), I get this with standard 4.14 kernel and ip commands.

I was merely asking about a double new line... :-)

MBR, Sergei

#6 lore raw

Re: [PATCH iproute2-next 2/3] ipmroute: don't complain about unicast routes

In reply to Stephen Hemminger
Quoted 24 lines · On Tue, 6 Mar 2018 17:03:54 -0800 Stephen Hemminger…

From: Stephen Hemminger <[email protected]>

Every non-multicast route prints an error message.
Kernel doesn't filter out unicast routes, it is up to filter function to do this.

---
 ip/ipmroute.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
ip/ipmroute.c +2 -3
index aa5029b44f41..03ca0575e571 100644
@@ -75,15 +75,14 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 		return -1;
 	}
-	if (r->rtm_type != RTN_MULTICAST) {
-		fprintf(stderr, "Not a multicast route (type: %s)\n",
-			rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+	if (r->rtm_type != RTN_MULTICAST)

ip/ipmroute.c int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) · line 88

Quoted 10 lines
ip/ipmroute.c +1 -1
 		return 0;
-	}
 
 	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
 	table = rtm_get_table(r, tb);
 
+
 	if (filter.tb > 0 && filter.tb != table)
 		return 0;
 

Actually, this is a recent kernel regression. Should be fixed there

#7 lore raw

Re: [PATCH iproute2-next 2/3] ipmroute: don't complain about unicast routes

Quoted 33 lines · Stephen Hemminger
Quoted 30 lines · On Tue, 6 Mar 2018 17:03:54 -0800 Stephen Hemminger…

From: Stephen Hemminger <[email protected]>

Every non-multicast route prints an error message.
Kernel doesn't filter out unicast routes, it is up to filter function to do this.

---
 ip/ipmroute.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
ip/ipmroute.c +2 -4
index aa5029b44f41..03ca0575e571 100644
@@ -75,15 +75,14 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 		return -1;
 	}
-	if (r->rtm_type != RTN_MULTICAST) {
-		fprintf(stderr, "Not a multicast route (type: %s)\n",
-			rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+	if (r->rtm_type != RTN_MULTICAST)
 		return 0;
-	}
 
 	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
 	table = rtm_get_table(r, tb);
 
Quoted 7 lines

ip/ipmroute.c int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) · line 88

Quoted 4 lines
ip/ipmroute.c +1 -0
+
 	if (filter.tb > 0 && filter.tb != table)
 		return 0;
 

Actually, this is a recent kernel regression. Should be fixed there

from Yuval's recent multicast convergence changes?

#8 lore raw

Re: [PATCH iproute2-next 2/3] ipmroute: don't complain about unicast routes

In reply to Stephen Hemminger
Quoted 7 lines · On Tue, 6 Mar 2018 17:03:54 -0800 Stephen Hemminger…

From: Stephen Hemminger <[email protected]>

Every non-multicast route prints an error message.
Kernel doesn't filter out unicast routes, it is up to filter function to do this.

I found the issue (in kernel) but not sure how to deal with it. If kernel is built without multicast routing configured !CONFIG_IP_MROUTE then the netlink request to return multicast routes will return all routes!

This is because in the kernel the way route dump works is that each address family registers a callback to dump routes for a specific address family. If that address family is not registered then the fall back is to address family PF_UNSPEC which has a handler that dumps all routes.

Unfortunately, changing that behavior in kernel will certainly break some user. And there is no direct way to determine multicast routing is enabled in ip mroute code.

Maybe just change the message in ip mroute to do:

ip/ipmroute.c +2 -3
index aa5029b44f41..31b9bfe95596 100644
@@ -76,9 +76,8 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
                return -1;
        }
        if (r->rtm_type != RTN_MULTICAST) {
-               fprintf(stderr, "Not a multicast route (type: %s)\n",
-                       rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
-               return 0;
+               fprintf(stderr, "Multicast routing does not appear to be enabled\n");
+               return -1;
        }
 
        parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
#9 lore raw

Re: [PATCH iproute2-next 2/3] ipmroute: don't complain about unicast routes

Quoted 15 lines · Stephen Hemminger
Quoted 7 lines · On Tue, 6 Mar 2018 17:03:54 -0800 Stephen Hemminger…

From: Stephen Hemminger <[email protected]>

Every non-multicast route prints an error message.
Kernel doesn't filter out unicast routes, it is up to filter function to do this.

I found the issue (in kernel) but not sure how to deal with it. If kernel is built without multicast routing configured !CONFIG_IP_MROUTE then the netlink request to return multicast routes will return all routes!

Quoted 5 lines

This is because in the kernel the way route dump works is that each address family registers a callback to dump routes for a specific address family. If that address family is not registered then the fall back is to address family PF_UNSPEC which has a handler that dumps all routes.

If I ask the kernel for IP{6}MR address family and the kernel returns a dump of all address families, that seems like a kernel bug to me. From a short review it seems to be that way for a long time. I guess iproute2 has no choice but to adapt to the kernel design.

Are you going to resubmit this series?

Quoted 24 lines

Unfortunately, changing that behavior in kernel will certainly break some user. And there is no direct way to determine multicast routing is enabled in ip mroute code.

Maybe just change the message in ip mroute to do:

ip/ipmroute.c +2 -3
index aa5029b44f41..31b9bfe95596 100644
@@ -76,9 +76,8 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
                return -1;
        }
        if (r->rtm_type != RTN_MULTICAST) {
-               fprintf(stderr, "Not a multicast route (type: %s)\n",
-                       rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
-               return 0;
+               fprintf(stderr, "Multicast routing does not appear to be enabled\n");
+               return -1;
        }
 
        parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
#10 lore raw

PATCH 3/3 ipmroute: convert to output JSON

In reply to Stephen Hemminger

From: Stephen Hemminger <[email protected]>

Should be no change for non-json case except putting color on address if desired.

---
 ip/ipmroute.c | 117 ++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 77 insertions(+), 40 deletions(-)
ip/ipmroute.c +77 -40
index 03ca0575e571..8258a4ed0a0b 100644
@@ -29,6 +29,7 @@
 #include <rt_names.h>
 #include "utils.h"
 #include "ip_common.h"
+#include "json_print.h"
 
 static void usage(void) __attribute__((noreturn));
 
@@ -53,13 +54,12 @@ struct rtfilter {
 
 int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *tb[RTA_MAX+1];
-	char obuf[256];
-
+	const char *src, *dst;
 	SPRINT_BUF(b1);
+	SPRINT_BUF(b2);
 	__u32 table;
 	int iif = 0;
 	int family;
@@ -102,30 +102,44 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 
 	family = get_real_family(r->rtm_type, r->rtm_family);
 
+	open_json_object(NULL);
 	if (n->nlmsg_type == RTM_DELROUTE)
-		fprintf(fp, "Deleted ");
+		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 
 	if (tb[RTA_SRC])
-		len = snprintf(obuf, sizeof(obuf),
-			       "(%s, ", rt_addr_n2a_rta(family, tb[RTA_SRC]));
+		src = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_SRC]),
+				    RTA_DATA(tb[RTA_SRC]), b1, sizeof(b1));
 	else
-		len = sprintf(obuf, "(unknown, ");
+		src = "unknown";
+
 	if (tb[RTA_DST])
-		snprintf(obuf + len, sizeof(obuf) - len,
-			 "%s)", rt_addr_n2a_rta(family, tb[RTA_DST]));
+		dst = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_DST]),
+				    RTA_DATA(tb[RTA_DST]), b2, sizeof(b2));
 	else
-		snprintf(obuf + len, sizeof(obuf) - len, "unknown) ");
+		dst = "unknown";
+
+	if (is_json_context()) {
+		print_string(PRINT_JSON, "src", NULL, src);
+		print_string(PRINT_JSON, "dst", NULL, dst);
+	} else {
+		char obuf[256];
+
+		snprintf(obuf, sizeof(obuf), "(%s,%s)", src, dst);
+		print_string(PRINT_FP, NULL,
+			     "%-32s Iif: ", obuf);
+	}
 
-	fprintf(fp, "%-32s Iif: ", obuf);
 	if (iif)
-		fprintf(fp, "%-10s ", ll_index_to_name(iif));
+		print_color_string(PRINT_ANY, COLOR_IFNAME,
+				   "iif", "%-10s ", ll_index_to_name(iif));
 	else
-		fprintf(fp, "unresolved ");
+		print_string(PRINT_ANY,"iif", "%s ", "unresolved");
 
 	if (tb[RTA_MULTIPATH]) {
 		struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
 		int first = 1;
 
+		open_json_array(PRINT_JSON, "multipath");
 		len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
 
 		for (;;) {
@@ -134,47 +148,67 @@ int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 			if (nh->rtnh_len > len)
 				break;
 
+			open_json_object(NULL);
 			if (first) {
-				fprintf(fp, "Oifs: ");
+				print_string(PRINT_FP, NULL, "Oifs: ", NULL);
 				first = 0;
 			}
-			fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex));
+
+			print_color_string(PRINT_ANY, COLOR_IFNAME,
+					   "oif", "%s", ll_index_to_name(nh->rtnh_ifindex));
+
 			if (nh->rtnh_hops > 1)
-				fprintf(fp, "(ttl %d) ", nh->rtnh_hops);
+				print_uint(PRINT_ANY,
+					   "ttl", "(ttl %u) ", nh->rtnh_hops);
 			else
-				fprintf(fp, " ");
+				print_string(PRINT_FP, NULL, " ", NULL);
+
+			close_json_object();
 			len -= NLMSG_ALIGN(nh->rtnh_len);
 			nh = RTNH_NEXT(nh);
 		}
+		close_json_array(PRINT_JSON, NULL);
 	}
-	fprintf(fp, " State: %s",
-		r->rtm_flags & RTNH_F_UNRESOLVED ? "unresolved" : "resolved");
+
+	print_string(PRINT_ANY, "state", " State: %s",
+		     (r->rtm_flags & RTNH_F_UNRESOLVED) ? "unresolved" : "resolved");
+
 	if (r->rtm_flags & RTNH_F_OFFLOAD)
-		fprintf(fp, " offload");
-	if (show_stats && tb[RTA_MFC_STATS]) {
-		struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]);
-
-		fprintf(fp, "%s  %"PRIu64" packets, %"PRIu64" bytes", _SL_,
-			(uint64_t)mfcs->mfcs_packets,
-			(uint64_t)mfcs->mfcs_bytes);
-		if (mfcs->mfcs_wrong_if)
-			fprintf(fp, ", %"PRIu64" arrived on wrong iif.",
-				(uint64_t)mfcs->mfcs_wrong_if);
-	}
-	if (show_stats && tb[RTA_EXPIRES]) {
-		struct timeval tv;
+		print_null(PRINT_ANY, "offload", " offload", NULL);
+
+	if (show_stats) {
+		if ( tb[RTA_MFC_STATS]) {
+			struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]);
 
-		__jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES]));
-		fprintf(fp, ", Age %4i.%.2i", (int)tv.tv_sec,
-			(int)tv.tv_usec/10000);
+			print_string(PRINT_FP, NULL, "%s", _SL_);
+			print_uint(PRINT_ANY, "packets", "  %"PRIu64" packets,",
+				   mfcs->mfcs_packets);
+			print_uint(PRINT_ANY, "bytes", " %"PRIu64" bytes", mfcs->mfcs_bytes);
+
+			if (mfcs->mfcs_wrong_if)
+				print_uint(PRINT_ANY, "wrong_if",
+					   ", %"PRIu64" arrived on wrong iif.",
+					   mfcs->mfcs_wrong_if);
+		}
+
+		if (tb[RTA_EXPIRES]) {
+			struct timeval tv;
+			double age;
+
+			__jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES]));
+			age = tv.tv_sec;
+			age += tv.tv_usec / 1000000.;
+			print_float(PRINT_ANY, "expires",
+				    ", Age %.2f", age);
+		}
 	}
 
 	if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb)
-		fprintf(fp, " Table: %s",
-			rtnl_rttable_n2a(table, b1, sizeof(b1)));
+		print_string(PRINT_ANY, "table", " Table: %s",
+			     rtnl_rttable_n2a(table, b1, sizeof(b1)));
 
-	fprintf(fp, "\n");
-	fflush(fp);
+	print_string(PRINT_FP, NULL, "\n", NULL);
+	close_json_object();
 	return 0;
 }
 
@@ -255,12 +289,15 @@ static int mroute_list(int argc, char **argv)
 		return 1;
 	}
 
+	new_json_obj(json);
 	if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) {
+		delete_json_obj();
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
+	delete_json_obj();
 
-	exit(0);
+	return 0;
 }
 
 int do_multiroute(int argc, char **argv)
Signature · 1 line
2.16.1