From a233caa0aaee44acd09e4c27ae32511939eab243 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 8 Feb 2018 08:26:22 -0800 Subject: [PATCH 1/4] json: make pretty printing optional Since JSON is intended for programmatic consumption, it makes sense for the default output format to be concise as possible. For programmer and other uses, it is helpful to keep the pretty whitespace format; therefore enable it with -p flag. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- include/json_print.h | 2 ++ ip/ip.c | 3 +++ lib/json_print.c | 3 ++- tc/tc.c | 3 +++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/json_print.h b/include/json_print.h index 2ca7830a..45a817ce 100644 --- a/include/json_print.h +++ b/include/json_print.h @@ -15,6 +15,8 @@ #include "json_writer.h" #include "color.h" +extern int show_pretty; + json_writer_t *get_json_writer(void); /* diff --git a/ip/ip.c b/ip/ip.c index b15e6b66..a6611292 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -31,6 +31,7 @@ int show_stats; int show_details; int oneline; int brief; +int show_pretty; int json; int timestamp; const char *_SL_; @@ -259,6 +260,8 @@ int main(int argc, char **argv) ++brief; } else if (matches(opt, "-json") == 0) { ++json; + } else if (matches(opt, "-pretty") == 0) { + ++show_pretty; } else if (matches(opt, "-rcvbuf") == 0) { unsigned int size; diff --git a/lib/json_print.c b/lib/json_print.c index 6518ba98..e3da1bdf 100644 --- a/lib/json_print.c +++ b/lib/json_print.c @@ -28,7 +28,8 @@ void new_json_obj(int json) perror("json object"); exit(1); } - jsonw_pretty(_jw, true); + if (show_pretty) + jsonw_pretty(_jw, true); jsonw_start_array(_jw); } } diff --git a/tc/tc.c b/tc/tc.c index 63e64fec..aba5c101 100644 --- a/tc/tc.c +++ b/tc/tc.c @@ -42,6 +42,7 @@ int use_iec; int force; bool use_names; int json; +int pretty; static char *conf_file; @@ -484,6 +485,8 @@ int main(int argc, char **argv) ++timestamp_short; } else if (matches(argv[1], "-json") == 0) { ++json; + } else if (matches(argv[1], "-pretty") == 0) { + ++pretty; } else { fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]); return -1; From bff0f2524103d3c1c153887ee823ec36a6732af7 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 8 Feb 2018 08:26:23 -0800 Subject: [PATCH 2/4] man: add documentation for json and pretty flags Add description for -json and -pretty options. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/ip.c | 4 ++-- man/man8/ip.8 | 18 ++++++++++++++---- man/man8/tc.8 | 3 ++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/ip/ip.c b/ip/ip.c index a6611292..233a9d77 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -54,12 +54,12 @@ static void usage(void) " netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n" " vrf | sr }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" -" -h[uman-readable] | -iec |\n" +" -h[uman-readable] | -iec | -j[son] | -p[retty] |\n" " -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |\n" " -4 | -6 | -I | -D | -B | -0 |\n" " -l[oops] { maximum-addr-flush-attempts } | -br[ief] |\n" " -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |\n" -" -rc[vbuf] [size] | -n[etns] name | -a[ll] | -c[olor]}\n"); +" -rc[vbuf] [size] | -n[etns] name | -a[ll] | -c[olor]}\n"); exit(-1); } diff --git a/man/man8/ip.8 b/man/man8/ip.8 index 7f26582d..0087d18b 100644 --- a/man/man8/ip.8 +++ b/man/man8/ip.8 @@ -48,9 +48,10 @@ ip \- show / manipulate routing, network devices, interfaces and tunnels \fB\-ts\fR[\fIhort\fR] | \fB\-n\fR[\fIetns\fR] name | \fB\-a\fR[\fIll\fR] | -\fB\-c\fR[\fIolor\fR] -\fB\-br\fR[\fIief\fR] } - +\fB\-c\fR[\fIolor\fR] | +\fB\-br\fR[\fIief\fR] | +\fB\-j\fR[son\fR] | +\fB\-p\fR[retty\fR] } .SH OPTIONS @@ -208,10 +209,19 @@ Set the netlink socket receive buffer size, defaults to 1MB. print human readable rates in IEC units (e.g. 1Ki = 1024). .TP -.BR "\-br" , "\-brief" +.BR "\-br" , " \-brief" Print only basic information in a tabular format for better readability. This option is currently only supported by .BR "ip addr show " and " ip link show " commands. +.TP +.BR "\-j", " \-json" +Output results in JavaScript Object Notation (JSON). + +.TP +.BR "\-p", " \-pretty" +The default JSON format is compact and more efficient to parse but hard for most users to read. +This flag adds indentation for readability. + .SH IP - COMMAND SYNTAX .SS diff --git a/man/man8/tc.8 b/man/man8/tc.8 index 5ffea373..a58f4654 100644 --- a/man/man8/tc.8 +++ b/man/man8/tc.8 @@ -670,7 +670,8 @@ output raw hex values for handles. .TP .BR "\-p", " \-pretty" -decode filter offset and mask values to equivalent filter commands based on TCP/IP. +for u32 filter, decode offset and mask values to equivalent filter commands based on TCP/IP. +In JSON output, add whitespace to improve readability. .TP .BR "\-iec" From 6cbd9465bc46ebf8e008084292273af592d9f3be Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 8 Feb 2018 08:26:24 -0800 Subject: [PATCH 3/4] json: fix newline at end of array The json print library was toggling pretty print at the end of an array to workaround a bug in underlying json_writer. Instead, just fix json_writer to pretty print array correctly. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- lib/json_print.c | 2 -- lib/json_writer.c | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/json_print.c b/lib/json_print.c index e3da1bdf..b507b14b 100644 --- a/lib/json_print.c +++ b/lib/json_print.c @@ -89,9 +89,7 @@ void open_json_array(enum output_type type, const char *str) void close_json_array(enum output_type type, const char *str) { if (_IS_JSON_CONTEXT(type)) { - jsonw_pretty(_jw, false); jsonw_end_array(_jw); - jsonw_pretty(_jw, true); } else if (_IS_FP_CONTEXT(type)) { printf("%s", str); } diff --git a/lib/json_writer.c b/lib/json_writer.c index f3eeaf7b..0d910dc0 100644 --- a/lib/json_writer.c +++ b/lib/json_writer.c @@ -180,10 +180,15 @@ void jsonw_end_object(json_writer_t *self) void jsonw_start_array(json_writer_t *self) { jsonw_begin(self, '['); + if (self->pretty) + putc(' ', self->out); } void jsonw_end_array(json_writer_t *self) { + if (self->pretty && self->sep) + putc(' ', self->out); + self->sep = '\0'; jsonw_end(self, ']'); } From 663c3cb23103f4e3bc4e9610fa54f15d6701f20f Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 8 Feb 2018 08:26:25 -0800 Subject: [PATCH 4/4] iproute: implement JSON and color output Add JSON and color output formatting to ip route command. Similar to existing address and link output. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- include/utils.h | 5 + ip/iproute.c | 376 +++++++++++++++++++++++++++++------------- ip/iproute_lwtunnel.c | 129 +++++++++------ 3 files changed, 348 insertions(+), 162 deletions(-) diff --git a/include/utils.h b/include/utils.h index 8b8ee2e5..4dc514d6 100644 --- a/include/utils.h +++ b/include/utils.h @@ -23,6 +23,7 @@ extern int resolve_hosts; extern int oneline; extern int brief; extern int json; +extern int pretty; extern int timestamp; extern int timestamp_short; extern const char * _SL_; @@ -155,6 +156,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 3c56240f..e4809a43 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -339,72 +339,95 @@ 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) - fprintf(fp, "dead "); + print_string(PRINT_ANY, NULL, "%s ", "dead"); if (flags & RTNH_F_ONLINK) - fprintf(fp, "onlink "); + print_string(PRINT_ANY, NULL, "%s ", "onlink"); if (flags & RTNH_F_PERVASIVE) - fprintf(fp, "pervasive "); + print_string(PRINT_ANY, NULL, "%s ", "pervasive"); if (flags & RTNH_F_OFFLOAD) - fprintf(fp, "offload "); + print_string(PRINT_ANY, NULL, "%s ", "offload"); + if (flags & RTM_F_NOTIFY) + print_string(PRINT_ANY, NULL, "%s ", "notify"); if (flags & RTNH_F_LINKDOWN) - fprintf(fp, "linkdown "); + print_string(PRINT_ANY, NULL, "%s ", "linkdown"); if (flags & RTNH_F_UNRESOLVED) - fprintf(fp, "unresolved "); + print_string(PRINT_ANY, NULL, "%s ", "unresolved"); + + close_json_array(PRINT_JSON, NULL); } static void print_rt_pref(FILE *fp, unsigned int 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); } } static void print_rta_if(FILE *fp, const struct rtattr *rta, - const char *prefix) + const char *prefix) { const char *ifname = ll_index_to_name(rta_getattr_u32(rta)); - fprintf(fp, "%s %s ", prefix, ifname); + if (is_json_context()) + print_string(PRINT_JSON, prefix, NULL, ifname); + else { + fprintf(fp, "%s ", prefix); + color_fprintf(fp, COLOR_IFNAME, "%s ", ifname); + } } static void print_cache_flags(FILE *fp, __u32 flags) { + json_writer_t *jw = get_json_writer(); flags &= ~0xFFFF; - fprintf(fp, "%s cache ", _SL_); - - if (flags == 0) - return; - - putc('<', fp); + if (jw) { + jsonw_name(jw, "cache"); + jsonw_start_array(jw); + } else { + fprintf(fp, "%s cache ", _SL_); + if (flags == 0) + return; + putc('<', fp); + } #define PRTFL(fl, flname) \ if (flags & RTCF_##fl) { \ flags &= ~RTCF_##fl; \ - fprintf(fp, "%s%s", flname, flags ? "," : "> "); \ + if (jw) \ + jsonw_string(jw, flname); \ + else \ + fprintf(fp, "%s%s", flname, flags ? "," : "> "); \ } PRTFL(LOCAL, "local"); @@ -424,7 +447,12 @@ static void print_cache_flags(FILE *fp, __u32 flags) #undef PRTFL if (flags) - fprintf(fp, "%#x> ", flags); + print_hex(PRINT_ANY, "flags", "%x>", flags); + + if (jw) { + jsonw_end_array(jw); + jsonw_destroy(&jw); + } } static void print_rta_cacheinfo(FILE *fp, const struct rta_cacheinfo *ci) @@ -433,23 +461,34 @@ static void print_rta_cacheinfo(FILE *fp, const struct rta_cacheinfo *ci) if (!hz) hz = get_user_hz(); + if (ci->rta_expires != 0) - fprintf(fp, "expires %dsec ", ci->rta_expires/hz); + print_uint(PRINT_ANY, "expires", + "expires %usec ", ci->rta_expires/hz); if (ci->rta_error != 0) - fprintf(fp, "error %d ", ci->rta_error); + print_uint(PRINT_ANY, "error", + "error %u ", ci->rta_error); + if (show_stats) { if (ci->rta_clntref) - fprintf(fp, "users %d ", ci->rta_clntref); + print_uint(PRINT_ANY, "users", + "users %u ", ci->rta_clntref); if (ci->rta_used != 0) - fprintf(fp, "used %d ", ci->rta_used); + print_uint(PRINT_ANY, "used", + "used %u ", ci->rta_used); if (ci->rta_lastuse != 0) - fprintf(fp, "age %dsec ", ci->rta_lastuse/hz); + print_uint(PRINT_ANY, "age", + "age %usec ", 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_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) @@ -459,13 +498,24 @@ static void print_rta_flow(FILE *fp, const struct rtattr *rta) SPRINT_BUF(b1); to &= 0xFFFF; - fprintf(fp, "realm%s ", from ? "s" : ""); - if (from) { - fprintf(fp, "%s/", - rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + 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))); } - fprintf(fp, "%s ", - rtnl_rtrealm_n2a(to, b1, sizeof(b1))); } static void print_rta_newdst(FILE *fp, const struct rtmsg *r, @@ -473,7 +523,14 @@ static void print_rta_newdst(FILE *fp, const struct rtmsg *r, { const char *newdst = format_host_rta(r->rtm_family, rta); - fprintf(fp, "as to %s ", newdst); + 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, @@ -481,17 +538,38 @@ static void print_rta_gateway(FILE *fp, const struct rtmsg *r, { const char *gateway = format_host_rta(r->rtm_family, rta); - fprintf(fp, "via %s ", gateway); + 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); - size_t len = RTA_PAYLOAD(rta); - fprintf(fp, "via %s %s ", - family_name(via->rtvia_family), - format_host(via->rtvia_family, len, via->rtvia_addr)); + 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_rta_metrics(FILE *fp, const struct rtattr *rta) @@ -500,6 +578,8 @@ static void print_rta_metrics(FILE *fp, const struct rtattr *rta) unsigned int mxlock = 0; int i; + open_json_array(PRINT_JSON, "metrics"); + parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)); if (mxrta[RTAX_LOCK]) @@ -517,13 +597,15 @@ static void print_rta_metrics(FILE *fp, const struct rtattr *rta) if (i == RTAX_HOPLIMIT && (int)val == -1) continue; - if (i < sizeof(mx_names)/sizeof(char *) && mx_names[i]) - fprintf(fp, "%s ", mx_names[i]); - else - fprintf(fp, "metric %d ", i); + 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<= 1000) - fprintf(fp, "%gs ", val/1e3); - else - fprintf(fp, "%ums ", val); + 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: - fprintf(fp, "%s ", rta_getattr_str(mxrta[i])); + print_string(PRINT_ANY, "congestion", + "%s ", rta_getattr_str(mxrta[i])); break; } } + + close_json_array(PRINT_JSON, NULL); } static void print_rta_multipath(FILE *fp, const struct rtmsg *r, @@ -566,16 +656,18 @@ static void print_rta_multipath(FILE *fp, const struct rtmsg *r, 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 (!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)) { parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), @@ -602,8 +694,7 @@ static void print_rta_multipath(FILE *fp, const struct rtmsg *r, fprintf(fp, "(ttl>%d)", nh->rtnh_hops); fprintf(fp, " "); } else { - fprintf(fp, "dev %s ", - ll_index_to_name(nh->rtnh_ifindex)); + 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); @@ -622,7 +713,7 @@ 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; @@ -668,39 +759,56 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) return 0; } + open_json_object(NULL); 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 ", - rt_addr_n2a_rta(family, tb[RTA_SRC]), - r->rtm_src_len); + 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); + snprintf(b1, sizeof(b1), "0/%u", r->rtm_src_len); + + print_string(PRINT_ANY, "src", "from %s ", b1); } if (tb[RTA_NEWDST]) @@ -710,8 +818,8 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) 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) @@ -721,25 +829,50 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) print_rta_via(fp, tb[RTA_VIA]); if (tb[RTA_OIF] && filter.oifmask != -1) - print_rta_if(fp, tb[RTA_OIF], "dev"); + print_rta_if(fp, tb[RTA_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])); + print_uint(PRINT_ANY, "metric", "metric %u ", + rta_getattr_u32(tb[RTA_PRIORITY])); print_rt_flags(fp, r->rtm_flags); @@ -747,10 +880,14 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) 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); } } @@ -758,20 +895,21 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) print_rta_flow(fp, tb[RTA_FLOW]); if (tb[RTA_UID]) - fprintf(fp, "uid %u ", rta_getattr_u32(tb[RTA_UID])); + print_uint(PRINT_ANY, "uid", "uid %u ", + rta_getattr_u32(tb[RTA_UID])); - if ((r->rtm_flags & RTM_F_CLONED) && r->rtm_family == AF_INET) { - print_cache_flags(fp, r->rtm_flags); - - if (tb[RTA_CACHEINFO]) - print_rta_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO])); + if (r->rtm_family == AF_INET) { + if (r->rtm_flags & RTM_F_CLONED) { + print_cache_flags(fp, r->rtm_flags); + if (tb[RTA_CACHEINFO]) + print_rta_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO])); + } } else if (r->rtm_family == AF_INET6) { - if (r->rtm_flags & RTM_F_CLONED) - fprintf(fp, "%s cache ", _SL_); - - if (tb[RTA_CACHEINFO]) - print_rta_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO])); + if (r->rtm_flags & RTM_F_CLONED) { + if (tb[RTA_CACHEINFO]) + print_rta_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO])); + } } if (tb[RTA_METRICS]) @@ -787,13 +925,19 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) print_rt_pref(fp, rta_getattr_u8(tb[RTA_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"); + + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); fflush(fp); return 0; } @@ -1756,11 +1900,15 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) } } + new_json_obj(json); + if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return -2; } + delete_json_obj(); + fflush(stdout); return 0; } diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c index f7fbc628..fa3feaea 100644 --- a/ip/iproute_lwtunnel.c +++ b/ip/iproute_lwtunnel.c @@ -94,20 +94,28 @@ static void print_srh(FILE *fp, struct ipv6_sr_hdr *srh) { int i; - fprintf(fp, "segs %d [ ", srh->first_segment + 1); + if (is_json_context()) + open_json_array(PRINT_JSON, "segs"); + else + fprintf(fp, "segs %d [ ", srh->first_segment + 1); for (i = srh->first_segment; i >= 0; i--) - fprintf(fp, "%s ", - rt_addr_n2a(AF_INET6, 16, &srh->segments[i])); + print_color_string(PRINT_ANY, COLOR_INET6, + NULL, "%s ", + rt_addr_n2a(AF_INET6, 16, &srh->segments[i])); - fprintf(fp, "] "); + if (is_json_context()) + close_json_array(PRINT_JSON, NULL); + else + fprintf(fp, "] "); if (sr_has_hmac(srh)) { unsigned int offset = ((srh->hdrlen + 1) << 3) - 40; struct sr6_tlv_hmac *tlv; tlv = (struct sr6_tlv_hmac *)((char *)srh + offset); - fprintf(fp, "hmac 0x%X ", ntohl(tlv->hmackeyid)); + print_0xhex(PRINT_ANY, "hmac", + "hmac 0x%X ", ntohl(tlv->hmackeyid)); } } @@ -148,7 +156,8 @@ static void print_encap_seg6(FILE *fp, struct rtattr *encap) return; tuninfo = RTA_DATA(tb[SEG6_IPTUNNEL_SRH]); - fprintf(fp, "mode %s ", format_seg6mode_type(tuninfo->mode)); + print_string(PRINT_ANY, "mode", + "mode %s ", format_seg6mode_type(tuninfo->mode)); print_srh(fp, tuninfo->srh); } @@ -205,36 +214,41 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap) action = rta_getattr_u32(tb[SEG6_LOCAL_ACTION]); - fprintf(fp, "action %s ", format_action_type(action)); + print_string(PRINT_ANY, "action", + "action %s ", format_action_type(action)); if (tb[SEG6_LOCAL_SRH]) { - fprintf(fp, "srh "); + open_json_object("srh"); print_srh(fp, RTA_DATA(tb[SEG6_LOCAL_SRH])); + close_json_object(); } if (tb[SEG6_LOCAL_TABLE]) - fprintf(fp, "table %u ", rta_getattr_u32(tb[SEG6_LOCAL_TABLE])); + print_uint(PRINT_ANY, "table", + "table %u ", rta_getattr_u32(tb[SEG6_LOCAL_TABLE])); if (tb[SEG6_LOCAL_NH4]) { - fprintf(fp, "nh4 %s ", - rt_addr_n2a_rta(AF_INET, tb[SEG6_LOCAL_NH4])); + print_string(PRINT_ANY, "nh4", + "nh4 %s ", rt_addr_n2a_rta(AF_INET, tb[SEG6_LOCAL_NH4])); } if (tb[SEG6_LOCAL_NH6]) { - fprintf(fp, "nh6 %s ", - rt_addr_n2a_rta(AF_INET6, tb[SEG6_LOCAL_NH6])); + print_string(PRINT_ANY, "nh6", + "nh6 %s ", rt_addr_n2a_rta(AF_INET6, tb[SEG6_LOCAL_NH6])); } if (tb[SEG6_LOCAL_IIF]) { int iif = rta_getattr_u32(tb[SEG6_LOCAL_IIF]); - fprintf(fp, "iif %s ", ll_index_to_name(iif)); + print_string(PRINT_ANY, "iif", + "iif %s ", ll_index_to_name(iif)); } if (tb[SEG6_LOCAL_OIF]) { int oif = rta_getattr_u32(tb[SEG6_LOCAL_OIF]); - fprintf(fp, "oif %s ", ll_index_to_name(oif)); + print_string(PRINT_ANY, "oif", + "oif %s ", ll_index_to_name(oif)); } } @@ -245,10 +259,10 @@ static void print_encap_mpls(FILE *fp, struct rtattr *encap) parse_rtattr_nested(tb, MPLS_IPTUNNEL_MAX, encap); if (tb[MPLS_IPTUNNEL_DST]) - fprintf(fp, " %s ", + print_string(PRINT_ANY, "dst", " %s ", format_host_rta(AF_MPLS, tb[MPLS_IPTUNNEL_DST])); if (tb[MPLS_IPTUNNEL_TTL]) - fprintf(fp, "ttl %u ", + print_uint(PRINT_ANY, "ttl", "ttl %u ", rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL])); } @@ -259,22 +273,26 @@ static void print_encap_ip(FILE *fp, struct rtattr *encap) parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap); if (tb[LWTUNNEL_IP_ID]) - fprintf(fp, "id %llu ", - ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID]))); + print_uint(PRINT_ANY, "id", "id %llu ", + ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID]))); if (tb[LWTUNNEL_IP_SRC]) - fprintf(fp, "src %s ", - rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_SRC])); + print_color_string(PRINT_ANY, COLOR_INET, + "src", "src %s ", + rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_SRC])); if (tb[LWTUNNEL_IP_DST]) - fprintf(fp, "dst %s ", - rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_DST])); + print_color_string(PRINT_ANY, COLOR_INET, + "dst", "dst %s ", + rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_DST])); if (tb[LWTUNNEL_IP_TTL]) - fprintf(fp, "ttl %u ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL])); + print_uint(PRINT_ANY, "ttl", + "ttl %u ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL])); if (tb[LWTUNNEL_IP_TOS]) - fprintf(fp, "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS])); + print_uint(PRINT_ANY, "tos", + "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS])); } static void print_encap_ila(FILE *fp, struct rtattr *encap) @@ -288,23 +306,24 @@ static void print_encap_ila(FILE *fp, struct rtattr *encap) addr64_n2a(rta_getattr_u64(tb[ILA_ATTR_LOCATOR]), abuf, sizeof(abuf)); - fprintf(fp, " %s ", abuf); + print_string(PRINT_ANY, "locator", + " %s ", abuf); } if (tb[ILA_ATTR_CSUM_MODE]) - fprintf(fp, " csum-mode %s ", - ila_csum_mode2name(rta_getattr_u8( - tb[ILA_ATTR_CSUM_MODE]))); + print_string(PRINT_ANY, "csum_mode", + " csum-mode %s ", + ila_csum_mode2name(rta_getattr_u8(tb[ILA_ATTR_CSUM_MODE]))); if (tb[ILA_ATTR_IDENT_TYPE]) - fprintf(fp, " ident-type %s ", - ila_ident_type2name(rta_getattr_u8( - tb[ILA_ATTR_IDENT_TYPE]))); + print_string(PRINT_ANY, "ident_type", + " ident-type %s ", + ila_ident_type2name(rta_getattr_u8(tb[ILA_ATTR_IDENT_TYPE]))); if (tb[ILA_ATTR_HOOK_TYPE]) - fprintf(fp, " hook-type %s ", - ila_hook_type2name(rta_getattr_u8( - tb[ILA_ATTR_HOOK_TYPE]))); + print_string(PRINT_ANY, "hook_type", + " hook-type %s ", + ila_hook_type2name(rta_getattr_u8(tb[ILA_ATTR_HOOK_TYPE]))); } static void print_encap_ip6(FILE *fp, struct rtattr *encap) @@ -314,35 +333,48 @@ static void print_encap_ip6(FILE *fp, struct rtattr *encap) parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap); if (tb[LWTUNNEL_IP6_ID]) - fprintf(fp, "id %llu ", - ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID]))); + print_uint(PRINT_ANY, "id", "id %llu ", + ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID]))); if (tb[LWTUNNEL_IP6_SRC]) - fprintf(fp, "src %s ", - rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_SRC])); + print_color_string(PRINT_ANY, COLOR_INET6, + "src", "src %s ", + rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_SRC])); if (tb[LWTUNNEL_IP6_DST]) - fprintf(fp, "dst %s ", - rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_DST])); + print_color_string(PRINT_ANY, COLOR_INET6, + "dst", "dst %s ", + rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_DST])); if (tb[LWTUNNEL_IP6_HOPLIMIT]) - fprintf(fp, "hoplimit %u ", - rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT])); + print_uint(PRINT_ANY, "hoplimit", + "hoplimit %u ", + rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT])); if (tb[LWTUNNEL_IP6_TC]) - fprintf(fp, "tc %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC])); + print_uint(PRINT_ANY, "tc", + "tc %u ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC])); } static void print_encap_bpf_prog(FILE *fp, struct rtattr *encap, const char *str) { struct rtattr *tb[LWT_BPF_PROG_MAX+1]; + const char *progname = NULL; parse_rtattr_nested(tb, LWT_BPF_PROG_MAX, encap); - fprintf(fp, "%s ", str); if (tb[LWT_BPF_PROG_NAME]) - fprintf(fp, "%s ", rta_getattr_str(tb[LWT_BPF_PROG_NAME])); + progname = rta_getattr_str(tb[LWT_BPF_PROG_NAME]); + + if (is_json_context()) + print_string(PRINT_JSON, str, NULL, + progname ? : ""); + else { + fprintf(fp, "%s ", str); + if (progname) + fprintf(fp, "%s ", progname); + } } static void print_encap_bpf(FILE *fp, struct rtattr *encap) @@ -358,7 +390,8 @@ static void print_encap_bpf(FILE *fp, struct rtattr *encap) if (tb[LWT_BPF_XMIT]) print_encap_bpf_prog(fp, tb[LWT_BPF_XMIT], "xmit"); if (tb[LWT_BPF_XMIT_HEADROOM]) - fprintf(fp, "%d ", rta_getattr_u32(tb[LWT_BPF_XMIT_HEADROOM])); + print_uint(PRINT_ANY, "headroom", + " %u ", rta_getattr_u32(tb[LWT_BPF_XMIT_HEADROOM])); } void lwt_print_encap(FILE *fp, struct rtattr *encap_type, @@ -371,7 +404,7 @@ void lwt_print_encap(FILE *fp, struct rtattr *encap_type, et = rta_getattr_u16(encap_type); - fprintf(fp, " encap %s ", format_encap_type(et)); + print_string(PRINT_ANY, "encap", " encap %s ", format_encap_type(et)); switch (et) { case LWTUNNEL_ENCAP_MPLS: