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