[RFC] iproute2: add json and color support

Subsystems: the rest

2 messages, 2 authors, 2018-01-30 · open the first message on its own page

[RFC] iproute2: add json and color support

From: Stephen Hemminger <stephen@networkplumber.org>
Date: 2018-01-29 20:29:17

This patch makes ip route command have json and color output in
a similar fashion of ip link and address.

The printing is split into functions and duplicate code
removed.

Probably incomplete, and has formatting glitches.

---
 include/utils.h |   4 +
 ip/iproute.c    | 764 ++++++++++++++++++++++++++++++++++----------------------
 2 files changed, 469 insertions(+), 299 deletions(-)
diff --git a/include/utils.h b/include/utils.h
index 0394268e1276..e35ea32c1d3b 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -155,6 +155,10 @@ int af_byte_len(int af);
 
 const char *format_host_r(int af, int len, const void *addr,
 			       char *buf, int buflen);
+#define format_host_rta_r(af, rta, buf, buflen)	\
+	format_host_r(af, RTA_PAYLOAD(rta), RTA_DATA(rta), \
+		      buf, buflen)
+
 const char *format_host(int af, int lne, const void *addr);
 #define format_host_rta(af, rta) \
 	format_host(af, RTA_PAYLOAD(rta), RTA_DATA(rta))
diff --git a/ip/iproute.c b/ip/iproute.c
index bf886fda9d76..b07d1ac7b7c9 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -340,12 +340,346 @@ static void print_rtax_features(FILE *fp, unsigned int features)
 	unsigned int of = features;
 
 	if (features & RTAX_FEATURE_ECN) {
-		fprintf(fp, "ecn ");
+		print_null(PRINT_ANY, "ecn", "ecn ", NULL);
 		features &= ~RTAX_FEATURE_ECN;
 	}
 
 	if (features)
-		fprintf(fp, "0x%x ", of);
+		print_0xhex(PRINT_ANY,
+			    "features", "0x%x ", of);
+}
+
+static void print_rt_flags(FILE *fp, unsigned int flags)
+{
+	open_json_array(PRINT_JSON,
+			is_json_context() ?  "flags" : "");
+
+	if (flags & RTNH_F_DEAD)
+		print_null(PRINT_ANY, "dead", "dead ", NULL);
+	if (flags & RTNH_F_ONLINK)
+		print_null(PRINT_ANY, "onlink", "onlink ", NULL);
+	if (flags & RTNH_F_PERVASIVE)
+		print_null(PRINT_ANY, "pervasive", "pervasive ", NULL);
+	if (flags & RTNH_F_OFFLOAD)
+		print_null(PRINT_ANY, "offload", "offload ", NULL);
+	if (flags & RTM_F_NOTIFY)
+		print_null(PRINT_ANY, "notify", "notify ", NULL);
+	if (flags & RTNH_F_LINKDOWN)
+		print_null(PRINT_ANY, "linkdown", "linkdown ", NULL);
+	if (flags & RTNH_F_UNRESOLVED)
+		print_null(PRINT_ANY, "unresolved", "unresolved ", NULL);
+
+	close_json_array(PRINT_JSON, NULL);
+}
+
+static void print_rt_metrics(FILE *fp, struct rtattr *rtm)
+{
+	struct rtattr *mxrta[RTAX_MAX+1];
+	unsigned int mxlock = 0;
+	int i;
+
+	open_json_array(PRINT_JSON, "metrics");
+
+	parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(rtm),
+		     RTA_PAYLOAD(rtm));
+
+	if (mxrta[RTAX_LOCK])
+		mxlock = rta_getattr_u32(mxrta[RTAX_LOCK]);
+
+	for (i = 2; i <= RTAX_MAX; i++) {
+		__u32 val = 0U;
+
+		if (mxrta[i] == NULL && !(mxlock & (1 << i)))
+			continue;
+
+		if (mxrta[i] != NULL && i != RTAX_CC_ALGO)
+			val = rta_getattr_u32(mxrta[i]);
+
+		if (i == RTAX_HOPLIMIT && (int)val == -1)
+			continue;
+
+		if (!is_json_context()) {
+			if (i < sizeof(mx_names)/sizeof(char *) && mx_names[i])
+				fprintf(fp, "%s ", mx_names[i]);
+			else
+				fprintf(fp, "metric %d ", i);
+
+			if (mxlock & (1<<i))
+				fprintf(fp, "lock ");
+		}
+
+		switch (i) {
+		case RTAX_FEATURES:
+			print_rtax_features(fp, val);
+			break;
+		default:
+			fprintf(fp, "%u ", val);
+			break;
+
+		case RTAX_RTT:
+		case RTAX_RTTVAR:
+		case RTAX_RTO_MIN:
+			if (i == RTAX_RTT)
+				val /= 8;
+			else if (i == RTAX_RTTVAR)
+				val /= 4;
+
+			if (is_json_context())
+				print_uint(PRINT_JSON, mx_names[i],
+					   NULL, val);
+			else {
+				if (val >= 1000)
+					fprintf(fp, "%gs ", val/1e3);
+				else
+					fprintf(fp, "%ums ", val);
+			}
+			break;
+		case RTAX_CC_ALGO:
+			print_string(PRINT_ANY, "congestion",
+				     "%s ", rta_getattr_str(mxrta[i]));
+			break;
+		}
+	}
+
+	close_json_array(PRINT_JSON, NULL);
+}
+
+static void print_rt_if(FILE *fp, struct rtattr *rif,
+			const char *tag, const char *prefix)
+{
+	const char *ifname = ll_index_to_name(rta_getattr_u32(rif));
+
+	if (is_json_context())
+		print_string(PRINT_JSON, tag, NULL, ifname);
+	else {
+		fprintf(fp, "%s ", prefix);
+		color_fprintf(fp, COLOR_IFNAME, "%s", ifname);
+		fprintf(fp, " ");
+	}
+}
+
+static void print_ipv4_flags(FILE *fp,
+			     const struct rtmsg *r)
+{
+	__u32 flags = r->rtm_flags & ~0xFFFF;
+
+	if (!is_json_context())
+		fprintf(fp, "%s", _SL_);
+
+	open_json_array(PRINT_ANY,
+			is_json_context() ? "cache"
+			: "    cache <");
+
+#define PRTFL(fl, flname)						\
+	if (flags & RTCF_##fl) {					\
+		flags &= ~RTCF_##fl;					\
+		print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", flname); \
+	}								\
+
+	PRTFL(LOCAL, "local");
+	PRTFL(REJECT, "reject");
+	PRTFL(MULTICAST, "mc");
+	PRTFL(BROADCAST, "brd");
+	PRTFL(DNAT, "dst-nat");
+	PRTFL(SNAT, "src-nat");
+	PRTFL(MASQ, "masq");
+	PRTFL(DIRECTDST, "dst-direct");
+	PRTFL(DIRECTSRC, "src-direct");
+	PRTFL(REDIRECTED, "redirected");
+	PRTFL(DOREDIRECT, "redirect");
+	PRTFL(FAST, "fastroute");
+	PRTFL(NOTIFY, "notify");
+	PRTFL(TPROXY, "proxy");
+#undef PRTFL
+
+	if (flags)
+		print_hex(PRINT_ANY, NULL, "%x", flags);
+	close_json_array(PRINT_ANY, "> ");
+}
+
+static void print_cacheinfo(FILE *fp,
+			    const struct rta_cacheinfo *ci)
+{
+	static int hz;
+
+	if (!hz)
+		hz = get_user_hz();
+
+	if (ci->rta_expires != 0)
+		print_uint(PRINT_ANY, "expires",
+			   "expires %usec ", ci->rta_expires/hz);
+	if (ci->rta_error != 0)
+		print_uint(PRINT_ANY, "error",
+			   "error %u ", ci->rta_error);
+
+	if (show_stats) {
+		if (ci->rta_clntref)
+			print_uint(PRINT_ANY, "users",
+				   "users %u ", ci->rta_clntref);
+		if (ci->rta_used != 0)
+			print_uint(PRINT_ANY, "used",
+				   "used %u ", ci->rta_used);
+		if (ci->rta_lastuse != 0)
+			print_uint(PRINT_ANY, "age",
+				   "age %usec ", ci->rta_lastuse/hz);
+	}
+	if (ci->rta_id)
+		print_0xhex(PRINT_ANY, "ipid",
+			    "ipid 0x%04x ", ci->rta_id);
+	if (ci->rta_ts || ci->rta_tsage) {
+		print_0xhex(PRINT_ANY, "ts",
+			    "ts 0x%x", ci->rta_ts);
+		print_uint(PRINT_ANY, "tsage",
+			   "tsage %usec ",ci->rta_tsage);
+	}
+}
+
+static void print_rta_flow(FILE *fp,
+			   const struct rtattr *rta)
+{
+	__u32 to = rta_getattr_u32(rta);
+	__u32 from = to >> 16;
+	SPRINT_BUF(b1);
+
+	to &= 0xFFFF;
+	if (is_json_context()) {
+		open_json_object("flow");
+
+		if (from)
+			print_string(PRINT_JSON, "from", NULL,
+				     rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+		print_string(PRINT_JSON, "to", NULL,
+			     rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+		close_json_object();
+	} else {
+		fprintf(fp, "realm%s ", from ? "s" : "");
+
+		if (from)
+			print_string(PRINT_FP, NULL, "%s/",
+					     rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+		print_string(PRINT_FP, NULL, "%s ",
+			     rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+	}
+}
+
+static void print_rta_newdst(FILE *fp, const struct rtmsg *r,
+			     const struct rtattr *rta)
+{
+	const char *newdst = format_host_rta(r->rtm_family, rta);
+
+	if (is_json_context())
+		print_string(PRINT_JSON, "to", NULL, newdst);
+	else {
+		fprintf(fp, "as to ");
+		print_color_string(PRINT_FP,
+				   ifa_family_color(r->rtm_family),
+				   NULL, "%s ", newdst);
+	}
+}
+
+static void print_rta_gateway(FILE *fp, const struct rtmsg *r,
+			     const struct rtattr *rta)
+{
+	const char *gateway = format_host_rta(r->rtm_family, rta);
+
+	if (is_json_context())
+		print_string(PRINT_JSON, "gateway", NULL, gateway);
+	else {
+		fprintf(fp, "via ");
+		print_color_string(PRINT_FP,
+				   ifa_family_color(r->rtm_family),
+				   NULL, "%s ", gateway);
+	}
+}
+
+static void print_rta_via(FILE *fp, const struct rtattr *rta)
+{
+	size_t len = RTA_PAYLOAD(rta) - 2;
+	const struct rtvia *via = RTA_DATA(rta);
+
+	if (is_json_context()) {
+		open_json_object("via");
+		print_string(PRINT_JSON, "family", NULL,
+			     family_name(via->rtvia_family));
+		print_string(PRINT_JSON, "host", NULL,
+			     format_host(via->rtvia_family, len,
+					 via->rtvia_addr));
+		close_json_object();
+	} else {
+		print_string(PRINT_FP, NULL, "via %s ",
+			     family_name(via->rtvia_family));
+		print_color_string(PRINT_FP,
+				   ifa_family_color(via->rtvia_family),
+				   NULL, "%s ",
+				   format_host(via->rtvia_family,
+					       len, via->rtvia_addr));
+	}
+}
+
+static void print_multipath(FILE *fp, const struct rtmsg *r,
+			    struct rtattr *rta)
+{
+	const struct rtnexthop *nh = RTA_DATA(rta);
+	int len = RTA_PAYLOAD(rta);
+	int first = 0;
+
+	while (len > sizeof(*nh)) {
+		struct rtattr *tb[RTA_MAX + 1];
+
+		if (nh->rtnh_len > len)
+			break;
+
+		if (!is_json_context()) {
+			if ((r->rtm_flags & RTM_F_CLONED) &&
+			    r->rtm_type == RTN_MULTICAST) {
+				if (first) {
+					fprintf(fp, "Oifs: ");
+					first = 0;
+				} else {
+					fprintf(fp, " ");
+				}
+			} else
+				fprintf(fp, "%s\tnexthop ", _SL_);
+		}
+
+		if (nh->rtnh_len < sizeof(*nh))
+			continue;
+
+		parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh),
+			     nh->rtnh_len - sizeof(*nh));
+
+		if (tb[RTA_ENCAP])
+			lwt_print_encap(fp, tb[RTA_ENCAP_TYPE], tb[RTA_ENCAP]);
+
+		if (tb[RTA_NEWDST])
+			print_rta_newdst(fp, r, tb[RTA_NEWDST]);
+
+		if (tb[RTA_GATEWAY])
+			print_rta_gateway(fp, r, tb[RTA_GATEWAY]);
+
+		if (tb[RTA_VIA])
+			print_rta_via(fp, tb[RTA_VIA]);
+
+		if (tb[RTA_FLOW])
+			print_rta_flow(fp, tb[RTA_FLOW]);
+
+		if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) {
+			fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex));
+			if (nh->rtnh_hops != 1)
+				fprintf(fp, "(ttl>%d)", nh->rtnh_hops);
+			fprintf(fp, " ");
+		} else {
+			fprintf(fp, "dev %s ", ll_index_to_name(nh->rtnh_ifindex));
+			if (r->rtm_family != AF_MPLS)
+				fprintf(fp, "weight %d ",
+					nh->rtnh_hops+1);
+		}
+
+		print_rt_flags(fp, nh->rtnh_flags);
+
+		len -= NLMSG_ALIGN(nh->rtnh_len);
+		nh = RTNH_NEXT(nh);
+	}
 }
 
 int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
@@ -354,12 +688,11 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *tb[RTA_MAX+1];
-	int host_len, family;
+	int family, color, host_len;
 	__u32 table;
 	int ret;
 
 	SPRINT_BUF(b1);
-	static int hz;
 
 	if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
 		fprintf(stderr, "Not a route: %08x %08x %08x\n",
@@ -401,376 +734,202 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 	}
 
 	if (n->nlmsg_type == RTM_DELROUTE)
-		fprintf(fp, "Deleted ");
+		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+
 	if ((r->rtm_type != RTN_UNICAST || show_details > 0) &&
 	    (!filter.typemask || (filter.typemask & (1 << r->rtm_type))))
-		fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+		print_string(PRINT_ANY, NULL, "%s ",
+			     rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
 
+	color = COLOR_NONE;
 	if (tb[RTA_DST]) {
 		family = get_real_family(r->rtm_type, r->rtm_family);
+		color = ifa_family_color(family);
+
 		if (r->rtm_dst_len != host_len) {
-			fprintf(fp, "%s/%u ",
-				rt_addr_n2a_rta(family, tb[RTA_DST]),
-			        r->rtm_dst_len);
+			snprintf(b1, sizeof(b1),
+				 "%s/%u", rt_addr_n2a_rta(family, tb[RTA_DST]),
+				 r->rtm_dst_len);
 		} else {
-			fprintf(fp, "%s ",
-				format_host_rta(family, tb[RTA_DST]));
+			format_host_rta_r(family, tb[RTA_DST],
+					  b1, sizeof(b1));
+
 		}
 	} else if (r->rtm_dst_len) {
-		fprintf(fp, "0/%d ", r->rtm_dst_len);
+		snprintf(b1, sizeof(b1), "0/%d ", r->rtm_dst_len);
 	} else {
-		fprintf(fp, "default ");
+		strncpy(b1, "default", sizeof(b1));
 	}
+	print_color_string(PRINT_ANY, color,
+			   "dst", "%s ", b1);
+
 	if (tb[RTA_SRC]) {
 		family = get_real_family(r->rtm_type, r->rtm_family);
+		color = ifa_family_color(family);
+
 		if (r->rtm_src_len != host_len) {
-			fprintf(fp, "from %s/%u ",
+			snprintf(b1, sizeof(b1),
+				"%s/%u",
 				rt_addr_n2a_rta(family, tb[RTA_SRC]),
 			        r->rtm_src_len);
 		} else {
-			fprintf(fp, "from %s ",
-				format_host_rta(family, tb[RTA_SRC]));
+			format_host_rta_r(family, tb[RTA_SRC],
+					  b1, sizeof(b1));
 		}
+		print_color_string(PRINT_ANY, color,
+				   "from", "from %s ", b1);
 	} else if (r->rtm_src_len) {
-		fprintf(fp, "from 0/%u ", r->rtm_src_len);
-	}
-	if (tb[RTA_NEWDST]) {
-		fprintf(fp, "as to %s ",
-		        format_host_rta(r->rtm_family, tb[RTA_NEWDST]));
+		snprintf(b1, sizeof(b1), "0/%u", r->rtm_src_len);
+
+		print_string(PRINT_ANY, "src", "from %s ", b1);
 	}
 
+	if (tb[RTA_NEWDST])
+		print_rta_newdst(fp, r, tb[RTA_NEWDST]);
+
 	if (tb[RTA_ENCAP])
 		lwt_print_encap(fp, tb[RTA_ENCAP_TYPE], tb[RTA_ENCAP]);
 
 	if (r->rtm_tos && filter.tosmask != -1) {
-		SPRINT_BUF(b1);
-		fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+		print_string(PRINT_ANY,
+			     "tos",
+			     "tos %s ",
+			     rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
 	}
 
-	if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
-		fprintf(fp, "via %s ",
-		        format_host_rta(r->rtm_family, tb[RTA_GATEWAY]));
-	}
-	if (tb[RTA_VIA]) {
-		size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2;
-		struct rtvia *via = RTA_DATA(tb[RTA_VIA]);
+	if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len)
+		print_rta_gateway(fp, r, tb[RTA_GATEWAY]);
+
+	if (tb[RTA_VIA])
+		print_rta_via(fp, tb[RTA_VIA]);
 
-		fprintf(fp, "via %s %s ",
-			family_name(via->rtvia_family),
-			format_host(via->rtvia_family, len, via->rtvia_addr));
-	}
 	if (tb[RTA_OIF] && filter.oifmask != -1)
-		fprintf(fp, "dev %s ", ll_index_to_name(rta_getattr_u32(tb[RTA_OIF])));
+		print_rt_if(fp, tb[RTA_OIF], "oif", "dev");
 
 	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)));
+
 	if (!(r->rtm_flags&RTM_F_CLONED)) {
-		if ((r->rtm_protocol != RTPROT_BOOT || show_details > 0) && filter.protocolmask != -1)
-			fprintf(fp, "proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1)));
-		if ((r->rtm_scope != RT_SCOPE_UNIVERSE || show_details > 0) && filter.scopemask != -1)
-			fprintf(fp, "scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1)));
+		if ((r->rtm_protocol != RTPROT_BOOT || show_details > 0) &&
+		    filter.protocolmask != -1)
+			print_string(PRINT_ANY,
+				     "protocol", "proto %s ",
+				     rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1)));
+
+		if ((r->rtm_scope != RT_SCOPE_UNIVERSE || show_details > 0) &&
+		    filter.scopemask != -1)
+			print_string(PRINT_ANY,
+				     "scope", "scope %s ",
+				     rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1)));
 	}
+
 	if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
+		const char *psrc
+			= rt_addr_n2a_rta(r->rtm_family, tb[RTA_PREFSRC]);
+
 		/* Do not use format_host(). It is our local addr
 		   and symbolic name will not be useful.
 		 */
-		fprintf(fp, "src %s ",
-			rt_addr_n2a_rta(r->rtm_family, tb[RTA_PREFSRC]));
+		if (is_json_context())
+			print_string(PRINT_JSON, "prefsrc", NULL, psrc);
+		else {
+			fprintf(fp, "src ");
+			print_color_string(PRINT_FP,
+					   ifa_family_color(r->rtm_family),
+					   NULL, "%s ", psrc);
+		}
+
 	}
+
 	if (tb[RTA_PRIORITY] && filter.metricmask != -1)
-		fprintf(fp, "metric %u ", rta_getattr_u32(tb[RTA_PRIORITY]));
-	if (r->rtm_flags & RTNH_F_DEAD)
-		fprintf(fp, "dead ");
-	if (r->rtm_flags & RTNH_F_ONLINK)
-		fprintf(fp, "onlink ");
-	if (r->rtm_flags & RTNH_F_PERVASIVE)
-		fprintf(fp, "pervasive ");
-	if (r->rtm_flags & RTNH_F_OFFLOAD)
-		fprintf(fp, "offload ");
-	if (r->rtm_flags & RTM_F_NOTIFY)
-		fprintf(fp, "notify ");
-	if (r->rtm_flags & RTNH_F_LINKDOWN)
-		fprintf(fp, "linkdown ");
-	if (r->rtm_flags & RTNH_F_UNRESOLVED)
-		fprintf(fp, "unresolved ");
+		print_uint(PRINT_ANY, "metric", "metric %u ",
+			   rta_getattr_u32(tb[RTA_PRIORITY]));
+
+	print_rt_flags(fp, r->rtm_flags);
+
 	if (tb[RTA_MARK]) {
 		unsigned int mark = rta_getattr_u32(tb[RTA_MARK]);
 
 		if (mark) {
-			if (mark >= 16)
-				fprintf(fp, "mark 0x%x ", mark);
+			if (is_json_context())
+				print_uint(PRINT_JSON, "mark", NULL, mark);
+			else if (mark >= 16)
+				print_0xhex(PRINT_FP, NULL,
+					    "mark 0x%x ", mark);
 			else
-				fprintf(fp, "mark %u ", mark);
+				print_uint(PRINT_FP, NULL,
+					   "mark %u ", mark);
 		}
 	}
 
-	if (tb[RTA_FLOW] && filter.realmmask != ~0U) {
-		__u32 to = rta_getattr_u32(tb[RTA_FLOW]);
-		__u32 from = to>>16;
-
-		to &= 0xFFFF;
-		fprintf(fp, "realm%s ", from ? "s" : "");
-		if (from) {
-			fprintf(fp, "%s/",
-				rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
-		}
-		fprintf(fp, "%s ",
-			rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
-	}
+	if (tb[RTA_FLOW] && filter.realmmask != ~0U)
+		print_rta_flow(fp, tb[RTA_FLOW]);
 
 	if (tb[RTA_UID])
-		fprintf(fp, "uid %u ", rta_getattr_u32(tb[RTA_UID]));
-
-	if ((r->rtm_flags&RTM_F_CLONED) && r->rtm_family == AF_INET) {
-		__u32 flags = r->rtm_flags&~0xFFFF;
-		int first = 1;
-
-		fprintf(fp, "%s    cache ", _SL_);
-
-#define PRTFL(fl, flname) if (flags&RTCF_##fl) { \
-  flags &= ~RTCF_##fl; \
-  fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \
-  first = 0; }
-		PRTFL(LOCAL, "local");
-		PRTFL(REJECT, "reject");
-		PRTFL(MULTICAST, "mc");
-		PRTFL(BROADCAST, "brd");
-		PRTFL(DNAT, "dst-nat");
-		PRTFL(SNAT, "src-nat");
-		PRTFL(MASQ, "masq");
-		PRTFL(DIRECTDST, "dst-direct");
-		PRTFL(DIRECTSRC, "src-direct");
-		PRTFL(REDIRECTED, "redirected");
-		PRTFL(DOREDIRECT, "redirect");
-		PRTFL(FAST, "fastroute");
-		PRTFL(NOTIFY, "notify");
-		PRTFL(TPROXY, "proxy");
-
-		if (flags)
-			fprintf(fp, "%s%x> ", first ? "<" : "", flags);
-		if (tb[RTA_CACHEINFO]) {
-			struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]);
-
-			if (!hz)
-				hz = get_user_hz();
-			if (ci->rta_expires != 0)
-				fprintf(fp, "expires %dsec ", ci->rta_expires/hz);
-			if (ci->rta_error != 0)
-				fprintf(fp, "error %d ", ci->rta_error);
-			if (show_stats) {
-				if (ci->rta_clntref)
-					fprintf(fp, "users %d ", ci->rta_clntref);
-				if (ci->rta_used != 0)
-					fprintf(fp, "used %d ", ci->rta_used);
-				if (ci->rta_lastuse != 0)
-					fprintf(fp, "age %dsec ", ci->rta_lastuse/hz);
-			}
-			if (ci->rta_id)
-				fprintf(fp, "ipid 0x%04x ", ci->rta_id);
-			if (ci->rta_ts || ci->rta_tsage)
-				fprintf(fp, "ts 0x%x tsage %dsec ",
-					ci->rta_ts, ci->rta_tsage);
+		print_uint(PRINT_ANY, "uid", "uid %u ",
+			   rta_getattr_u32(tb[RTA_UID]));
+
+	if (r->rtm_family == AF_INET) {
+		if (r->rtm_flags & RTM_F_CLONED) {
+			print_ipv4_flags(fp, r);
+
+			if (tb[RTA_CACHEINFO])
+				print_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO]));
 		}
 	} else if (r->rtm_family == AF_INET6) {
-		struct rta_cacheinfo *ci = NULL;
-
-		if (tb[RTA_CACHEINFO])
-			ci = RTA_DATA(tb[RTA_CACHEINFO]);
-		if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
-			if (!hz)
-				hz = get_user_hz();
-			if (r->rtm_flags & RTM_F_CLONED)
-				fprintf(fp, "%s    cache ", _SL_);
-			if (ci->rta_expires)
-				fprintf(fp, "expires %dsec ", ci->rta_expires/hz);
-			if (ci->rta_error != 0)
-				fprintf(fp, "error %d ", ci->rta_error);
-			if (show_stats) {
-				if (ci->rta_clntref)
-					fprintf(fp, "users %d ", ci->rta_clntref);
-				if (ci->rta_used != 0)
-					fprintf(fp, "used %d ", ci->rta_used);
-				if (ci->rta_lastuse != 0)
-					fprintf(fp, "age %dsec ", ci->rta_lastuse/hz);
-			}
-		} else if (ci) {
-			if (ci->rta_error != 0)
-				fprintf(fp, "error %d ", ci->rta_error);
+		if (r->rtm_flags & RTM_F_CLONED) {
+			if (tb[RTA_CACHEINFO])
+				print_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO]));
 		}
 	}
-	if (tb[RTA_METRICS]) {
-		int i;
-		unsigned int mxlock = 0;
-		struct rtattr *mxrta[RTAX_MAX+1];
-
-		parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]),
-			    RTA_PAYLOAD(tb[RTA_METRICS]));
-		if (mxrta[RTAX_LOCK])
-			mxlock = rta_getattr_u32(mxrta[RTAX_LOCK]);
-
-		for (i = 2; i <= RTAX_MAX; i++) {
-			__u32 val = 0U;
-
-			if (mxrta[i] == NULL && !(mxlock & (1 << i)))
-				continue;
-
-			if (mxrta[i] != NULL && i != RTAX_CC_ALGO)
-				val = rta_getattr_u32(mxrta[i]);
 
-			if (i == RTAX_HOPLIMIT && (int)val == -1)
-				continue;
+	if (tb[RTA_METRICS])
+		print_rt_metrics(fp, tb[RTA_METRICS]);
 
-			if (i < sizeof(mx_names)/sizeof(char *) && mx_names[i])
-				fprintf(fp, "%s ", mx_names[i]);
-			else
-				fprintf(fp, "metric %d ", i);
-
-			if (mxlock & (1<<i))
-				fprintf(fp, "lock ");
-
-			switch (i) {
-			case RTAX_FEATURES:
-				print_rtax_features(fp, val);
-				break;
-			default:
-				fprintf(fp, "%u ", val);
-				break;
-
-			case RTAX_RTT:
-			case RTAX_RTTVAR:
-			case RTAX_RTO_MIN:
-				if (i == RTAX_RTT)
-					val /= 8;
-				else if (i == RTAX_RTTVAR)
-					val /= 4;
-
-				if (val >= 1000)
-					fprintf(fp, "%gs ", val/1e3);
-				else
-					fprintf(fp, "%ums ", val);
-				break;
-			case RTAX_CC_ALGO:
-				fprintf(fp, "%s ", rta_getattr_str(mxrta[i]));
-				break;
-			}
-		}
-	}
-	if (tb[RTA_IIF] && filter.iifmask != -1) {
-		fprintf(fp, "iif %s ",
-			ll_index_to_name(rta_getattr_u32(tb[RTA_IIF])));
-	}
-	if (tb[RTA_MULTIPATH]) {
-		struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
-		int first = 1;
+	if (tb[RTA_IIF] && filter.iifmask != -1)
+		print_rt_if(fp, tb[RTA_IIF], "iif", "iif");
 
-		len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
+	if (tb[RTA_MULTIPATH])
+		print_multipath(fp, r, tb[RTA_MULTIPATH]);
 
-		for (;;) {
-			if (len < sizeof(*nh))
-				break;
-			if (nh->rtnh_len > len)
-				break;
-			if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) {
-				if (first) {
-					fprintf(fp, "Oifs: ");
-					first = 0;
-				} else {
-					fprintf(fp, " ");
-				}
-			} else
-				fprintf(fp, "%s\tnexthop ", _SL_);
-			if (nh->rtnh_len > sizeof(*nh)) {
-				parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh));
-
-				if (tb[RTA_ENCAP])
-					lwt_print_encap(fp,
-							tb[RTA_ENCAP_TYPE],
-							tb[RTA_ENCAP]);
-				if (tb[RTA_NEWDST]) {
-					fprintf(fp, "as to %s ",
-						format_host_rta(r->rtm_family,
-								tb[RTA_NEWDST]));
-				}
-				if (tb[RTA_GATEWAY]) {
-					fprintf(fp, "via %s ",
-						format_host_rta(r->rtm_family,
-								tb[RTA_GATEWAY]));
-				}
-				if (tb[RTA_VIA]) {
-					size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2;
-					struct rtvia *via = RTA_DATA(tb[RTA_VIA]);
-
-					fprintf(fp, "via %s %s ",
-						family_name(via->rtvia_family),
-						format_host(via->rtvia_family, len, via->rtvia_addr));
-				}
-				if (tb[RTA_FLOW]) {
-					__u32 to = rta_getattr_u32(tb[RTA_FLOW]);
-					__u32 from = to>>16;
-
-					to &= 0xFFFF;
-					fprintf(fp, "realm%s ", from ? "s" : "");
-					if (from) {
-						fprintf(fp, "%s/",
-							rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
-					}
-					fprintf(fp, "%s ",
-						rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
-				}
-			}
-			if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) {
-				fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex));
-				if (nh->rtnh_hops != 1)
-					fprintf(fp, "(ttl>%d)", nh->rtnh_hops);
-				fprintf(fp, " ");
-			} else {
-				fprintf(fp, "dev %s ", ll_index_to_name(nh->rtnh_ifindex));
-				if (r->rtm_family != AF_MPLS)
-					fprintf(fp, "weight %d ",
-						nh->rtnh_hops+1);
-			}
-			if (nh->rtnh_flags & RTNH_F_DEAD)
-				fprintf(fp, "dead ");
-			if (nh->rtnh_flags & RTNH_F_ONLINK)
-				fprintf(fp, "onlink ");
-			if (nh->rtnh_flags & RTNH_F_PERVASIVE)
-				fprintf(fp, "pervasive ");
-			if (nh->rtnh_flags & RTNH_F_OFFLOAD)
-				fprintf(fp, "offload ");
-			if (nh->rtnh_flags & RTNH_F_LINKDOWN)
-				fprintf(fp, "linkdown ");
-			len -= NLMSG_ALIGN(nh->rtnh_len);
-			nh = RTNH_NEXT(nh);
-		}
-	}
 	if (tb[RTA_PREF]) {
 		unsigned int pref = rta_getattr_u8(tb[RTA_PREF]);
 
-		fprintf(fp, "pref ");
 
 		switch (pref) {
 		case ICMPV6_ROUTER_PREF_LOW:
-			fprintf(fp, "low");
+			print_string(PRINT_ANY,
+				     "pref", "pref %s", "low");
 			break;
 		case ICMPV6_ROUTER_PREF_MEDIUM:
-			fprintf(fp, "medium");
+			print_string(PRINT_ANY,
+				     "pref", "pref %s", "medium");
 			break;
 		case ICMPV6_ROUTER_PREF_HIGH:
-			fprintf(fp, "high");
+			print_string(PRINT_ANY,
+				     "pref", "pref %s", "high");
 			break;
 		default:
-			fprintf(fp, "%u", pref);
+			print_uint(PRINT_ANY,
+				     "pref", "%u", pref);
 		}
 	}
+
 	if (tb[RTA_TTL_PROPAGATE]) {
-		fprintf(fp, "ttl-propagate ");
-		if (rta_getattr_u8(tb[RTA_TTL_PROPAGATE]))
-			fprintf(fp, "enabled");
+		bool propogate = rta_getattr_u8(tb[RTA_TTL_PROPAGATE]);
+
+		if (is_json_context())
+			print_bool(PRINT_JSON, "ttl-propogate", NULL,
+				   propogate);
 		else
-			fprintf(fp, "disabled");
+			print_string(PRINT_FP, NULL,
+				     "ttl-propogate %s",
+				     propogate ? "enabled" : "disabled");
 	}
 	fprintf(fp, "\n");
-	fflush(fp);
+
 	return 0;
 }
 
@@ -1709,23 +1868,30 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action)
 		}
 	}
 
+	new_json_obj(json);
+
 	if (!filter.cloned) {
 		if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
 			perror("Cannot send dump request");
+			delete_json_obj();
 			return -2;
 		}
 	} else {
 		if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
 			perror("Cannot send dump request");
+			delete_json_obj();
 			return -2;
 		}
 	}
 
 	if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
+		delete_json_obj();
 		return -2;
 	}
 
+	close_json_object();
+	fflush(stdout);
 	return 0;
 }
 
-- 
2.15.1

Re: [RFC] iproute2: add json and color support

From: David Ahern <hidden>
Date: 2018-01-30 02:58:38

On 1/29/18 1:29 PM, Stephen Hemminger wrote:
This patch makes ip route command have json and color output in
a similar fashion of ip link and address.

The printing is split into functions and duplicate code
removed.

Probably incomplete, and has formatting glitches.

---
 include/utils.h |   4 +
 ip/iproute.c    | 764 ++++++++++++++++++++++++++++++++++----------------------
 2 files changed, 469 insertions(+), 299 deletions(-)
good idea. 469 insert, 299 delete makes this really complicated.
Splitting this into smaller patches will be helpful.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help