From aac7f725fa464bfd7f3e4604d88beeed10cbe767 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:09 -0800 Subject: [PATCH 01/12] ipneigh: add color and json support Use json_print to provide json (and color) support to ip neigh command. Example: $ ip -j -p neigh [ { "dst": "192.168.1.29", "dev": "enp12s0", "state": [ "FAILED" ] },{ "dst": "192.168.1.130", "dev": "enp12s0", "state": [ "FAILED" ] },{ "dst": "192.168.1.131", "dev": "enp12s0", "lladdr": "00:15:5d:2a:16:4f", "state": [ "STALE" ] } ... Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/ipneigh.c | 138 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 46 deletions(-) diff --git a/ip/ipneigh.c b/ip/ipneigh.c index 07354249..925494db 100644 --- a/ip/ipneigh.c +++ b/ip/ipneigh.c @@ -23,6 +23,7 @@ #include "rt_names.h" #include "utils.h" #include "ip_common.h" +#include "json_print.h" #define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) #define MAX_ROUNDS 10 @@ -189,6 +190,46 @@ static int ipneigh_modify(int cmd, int flags, int argc, char **argv) return 0; } +static void print_cacheinfo(const struct nda_cacheinfo *ci) +{ + static int hz; + + if (!hz) + hz = get_user_hz(); + + if (ci->ndm_refcnt) + print_uint(PRINT_ANY, "refcnt", + " ref %u", ci->ndm_refcnt); + + print_uint(PRINT_ANY, "used", " used %u", ci->ndm_used / hz); + print_uint(PRINT_ANY, "confirmed", "/%u", ci->ndm_confirmed / hz); + print_uint(PRINT_ANY, "updated", "/u", ci->ndm_updated / hz); +} + +static void print_neigh_state(unsigned int nud) +{ + + open_json_array(PRINT_JSON, + is_json_context() ? "state" : ""); + +#define PRINT_FLAG(f) \ + if (nud & NUD_##f) { \ + nud &= ~NUD_##f; \ + print_string(PRINT_ANY, NULL, " %s", #f); \ + } + + PRINT_FLAG(INCOMPLETE); + PRINT_FLAG(REACHABLE); + PRINT_FLAG(STALE); + PRINT_FLAG(DELAY); + PRINT_FLAG(PROBE); + PRINT_FLAG(FAILED); + PRINT_FLAG(NOARP); + PRINT_FLAG(PERMANENT); +#undef PRINT_FLAG + + close_json_array(PRINT_JSON, NULL); +} int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { @@ -221,7 +262,7 @@ int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (!(filter.state&r->ndm_state) && !(r->ndm_flags & NTF_PROXY) && (r->ndm_state || !(filter.state&0x100)) && - (r->ndm_family != AF_DECnet)) + (r->ndm_family != AF_DECnet)) return 0; if (filter.master && !(n->nlmsg_flags & NLM_F_DUMP_FILTERED)) { @@ -262,65 +303,68 @@ int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) return 0; } + open_json_object(NULL); if (n->nlmsg_type == RTM_DELNEIGH) - fprintf(fp, "Deleted "); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); else if (n->nlmsg_type == RTM_GETNEIGH) - fprintf(fp, "miss "); + print_null(PRINT_ANY, "miss", "%s ", "miss"); + if (tb[NDA_DST]) { - fprintf(fp, "%s ", - format_host_rta(r->ndm_family, tb[NDA_DST])); + const char *dst; + + dst = format_host_rta(r->ndm_family, tb[NDA_DST]); + print_color_string(PRINT_ANY, + ifa_family_color(r->ndm_family), + "dst", "%s ", dst); } - if (!filter.index && r->ndm_ifindex) - fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); + + if (!filter.index && r->ndm_ifindex) { + if (!is_json_context()) + fprintf(fp, "dev "); + + print_color_string(PRINT_ANY, COLOR_IFNAME, + "dev", "%s ", + ll_index_to_name(r->ndm_ifindex)); + } + if (tb[NDA_LLADDR]) { + const char *lladdr; SPRINT_BUF(b1); - fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), - RTA_PAYLOAD(tb[NDA_LLADDR]), - ll_index_to_type(r->ndm_ifindex), - b1, sizeof(b1))); - } - if (r->ndm_flags & NTF_ROUTER) { - fprintf(fp, " router"); - } - if (r->ndm_flags & NTF_PROXY) { - fprintf(fp, " proxy"); - } - if (tb[NDA_CACHEINFO] && show_stats) { - struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); - int hz = get_user_hz(); - if (ci->ndm_refcnt) - printf(" ref %d", ci->ndm_refcnt); - fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz, - ci->ndm_confirmed/hz, ci->ndm_updated/hz); + lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), + RTA_PAYLOAD(tb[NDA_LLADDR]), + ll_index_to_type(r->ndm_ifindex), + b1, sizeof(b1)); + + if (!is_json_context()) + fprintf(fp, "lladdr "); + + print_color_string(PRINT_ANY, COLOR_MAC, + "lladdr", "%s", lladdr); } - if (tb[NDA_PROBES] && show_stats) { - __u32 p = rta_getattr_u32(tb[NDA_PROBES]); + if (r->ndm_flags & NTF_ROUTER) + print_null(PRINT_ANY, "router", " %s", "router"); - fprintf(fp, " probes %u", p); + if (r->ndm_flags & NTF_PROXY) + print_null(PRINT_ANY, "proxy", " %s", "proxy"); + + if (show_stats) { + if (tb[NDA_CACHEINFO]) + print_cacheinfo(RTA_DATA(tb[NDA_CACHEINFO])); + + if (tb[NDA_PROBES]) + print_uint(PRINT_ANY, "probes", " probes %u", + rta_getattr_u32(tb[NDA_PROBES])); } - if (r->ndm_state) { - int nud = r->ndm_state; + if (r->ndm_state) + print_neigh_state(r->ndm_state); - fprintf(fp, " "); + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); + fflush(stdout); -#define PRINT_FLAG(f) if (nud & NUD_##f) { \ - nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); } - PRINT_FLAG(INCOMPLETE); - PRINT_FLAG(REACHABLE); - PRINT_FLAG(STALE); - PRINT_FLAG(DELAY); - PRINT_FLAG(PROBE); - PRINT_FLAG(FAILED); - PRINT_FLAG(NOARP); - PRINT_FLAG(PERMANENT); -#undef PRINT_FLAG - } - fprintf(fp, "\n"); - - fflush(fp); return 0; } @@ -479,10 +523,12 @@ static int do_show_or_flush(int argc, char **argv, int flush) exit(1); } + new_json_obj(json); if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } + delete_json_obj(); return 0; } From a7ad1c8a6845d5ffdd9642506012213166c98b7f Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:10 -0800 Subject: [PATCH 02/12] ipaddrlabel: add json support Add missing json and color support to addrlabel display Example: $ ip -j -p addrlabel [ { "address": "::1", "prefixlen": 128, "label": 56 },{ "address": "::", "prefixlen": 96, "label": 56 },{ ... Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/ipaddrlabel.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/ip/ipaddrlabel.c b/ip/ipaddrlabel.c index 7200bf54..2f79c56d 100644 --- a/ip/ipaddrlabel.c +++ b/ip/ipaddrlabel.c @@ -38,6 +38,7 @@ #include "rt_names.h" #include "utils.h" #include "ip_common.h" +#include "json_print.h" #define IFAL_RTA(r) ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)))) #define IFAL_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ifaddrlblmsg)) @@ -55,7 +56,6 @@ static void usage(void) int print_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE *)arg; struct ifaddrlblmsg *ifal = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[IFAL_MAX+1]; @@ -69,28 +69,40 @@ int print_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg parse_rtattr(tb, IFAL_MAX, IFAL_RTA(ifal), len); + open_json_object(NULL); if (n->nlmsg_type == RTM_DELADDRLABEL) - fprintf(fp, "Deleted "); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); if (tb[IFAL_ADDRESS]) { - fprintf(fp, "prefix %s/%u ", - format_host_rta(ifal->ifal_family, - tb[IFAL_ADDRESS]), - ifal->ifal_prefixlen); + const char *host + = format_host_rta(ifal->ifal_family, + tb[IFAL_ADDRESS]); + + print_string(PRINT_FP, NULL, "prefix ", NULL); + print_color_string(PRINT_ANY, + ifa_family_color(ifal->ifal_family), + "address", "%s", host); + + print_uint(PRINT_ANY, "prefixlen", "/%u ", + ifal->ifal_prefixlen); } - if (ifal->ifal_index) - fprintf(fp, "dev %s ", ll_index_to_name(ifal->ifal_index)); + if (ifal->ifal_index) { + print_string(PRINT_FP, NULL, "dev ", NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "ifname", "%s ", + ll_index_to_name(ifal->ifal_index)); + } if (tb[IFAL_LABEL] && RTA_PAYLOAD(tb[IFAL_LABEL]) == sizeof(uint32_t)) { - uint32_t label; + uint32_t label = rta_getattr_u32(RTA_DATA(tb[IFAL_LABEL])); - memcpy(&label, RTA_DATA(tb[IFAL_LABEL]), sizeof(label)); - fprintf(fp, "label %u ", label); + print_uint(PRINT_ANY, + "label", "label %u ", label); } + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); - fprintf(fp, "\n"); - fflush(fp); return 0; } @@ -111,10 +123,12 @@ static int ipaddrlabel_list(int argc, char **argv) return 1; } + new_json_obj(json); if (rtnl_dump_filter(&rth, print_addrlabel, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return 1; } + delete_json_obj(); return 0; } From 0dd4ccc56c0e371ba62fefbffbe478c228a3b433 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:11 -0800 Subject: [PATCH 03/12] iprule: add json support More JSON and colorizing. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/iprule.c | 184 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 109 insertions(+), 75 deletions(-) diff --git a/ip/iprule.c b/ip/iprule.c index 6fdc9b5e..a49753e6 100644 --- a/ip/iprule.c +++ b/ip/iprule.c @@ -26,6 +26,7 @@ #include "rt_names.h" #include "utils.h" #include "ip_common.h" +#include "json_print.h" enum list_action { IPRULE_LIST, @@ -179,13 +180,12 @@ static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE *)arg; + FILE *fp = arg; struct fib_rule_hdr *frh = NLMSG_DATA(n); int len = n->nlmsg_len; int host_len = -1; - __u32 table; + __u32 table, prio = 0; struct rtattr *tb[FRA_MAX+1]; - SPRINT_BUF(b1); if (n->nlmsg_type != RTM_NEWRULE && n->nlmsg_type != RTM_DELRULE) @@ -202,50 +202,54 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (!filter_nlmsg(n, tb, host_len)) return 0; + open_json_object(NULL); if (n->nlmsg_type == RTM_DELRULE) - fprintf(fp, "Deleted "); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); if (tb[FRA_PRIORITY]) - fprintf(fp, "%u:\t", - rta_getattr_u32(tb[FRA_PRIORITY])); - else - fprintf(fp, "0:\t"); + prio = rta_getattr_u32(tb[FRA_PRIORITY]); + + print_uint(PRINT_ANY, "priority", "%u:\t", prio); if (frh->flags & FIB_RULE_INVERT) - fprintf(fp, "not "); + print_null(PRINT_ANY, "not", "not ", NULL); if (tb[FRA_SRC]) { - if (frh->src_len != host_len) { - fprintf(fp, "from %s/%u ", - rt_addr_n2a_rta(frh->family, tb[FRA_SRC]), - frh->src_len); - } else { - fprintf(fp, "from %s ", - format_host_rta(frh->family, tb[FRA_SRC])); - } + const char *src = rt_addr_n2a_rta(frh->family, tb[FRA_SRC]); + + print_string(PRINT_FP, NULL, "from ", NULL); + print_color_string(PRINT_ANY, ifa_family_color(frh->family), + "src", "%s", src); + if (frh->src_len != host_len) + print_uint(PRINT_ANY, "srclen", "/%u ", frh->src_len); + else + print_string(PRINT_FP, NULL, " ", NULL); } else if (frh->src_len) { - fprintf(fp, "from 0/%d ", frh->src_len); + print_string(PRINT_ANY, "src", "from %s", "0"); + print_uint(PRINT_ANY, "srclen", "/%u ", frh->src_len); } else { - fprintf(fp, "from all "); + print_string(PRINT_ANY, "src", "from %s ", "all"); } if (tb[FRA_DST]) { - if (frh->dst_len != host_len) { - fprintf(fp, "to %s/%u ", - rt_addr_n2a_rta(frh->family, tb[FRA_DST]), - frh->dst_len); - } else { - fprintf(fp, "to %s ", - format_host_rta(frh->family, tb[FRA_DST])); - } + const char *dst = rt_addr_n2a_rta(frh->family, tb[FRA_DST]); + + print_string(PRINT_FP, NULL, "to ", NULL); + print_color_string(PRINT_ANY, ifa_family_color(frh->family), + "dst", "%s ", dst); + if (frh->dst_len != host_len) + print_uint(PRINT_ANY, "dstlen", "/%u ", frh->dst_len); + else + print_string(PRINT_FP, NULL, " ", NULL); } else if (frh->dst_len) { - fprintf(fp, "to 0/%d ", frh->dst_len); + print_string(PRINT_ANY, "dst", "to %s", "0"); + print_uint(PRINT_ANY, "dstlen", "/%u ", frh->dst_len); } if (frh->tos) { - SPRINT_BUF(b1); - fprintf(fp, "tos %s ", - rtnl_dsfield_n2a(frh->tos, b1, sizeof(b1))); + print_string(PRINT_ANY, "tos", + "tos %s ", + rtnl_dsfield_n2a(frh->tos, b1, sizeof(b1))); } if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) { @@ -255,53 +259,76 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) mark = rta_getattr_u32(tb[FRA_FWMARK]); if (tb[FRA_FWMASK] && - (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF) - fprintf(fp, "fwmark 0x%x/0x%x ", mark, mask); - else - fprintf(fp, "fwmark 0x%x ", mark); + (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF) { + print_0xhex(PRINT_ANY, "fwmark", "fwmark 0x%x", mark); + print_0xhex(PRINT_ANY, "fwmask", "/0x%x ", mask); + } else { + print_0xhex(PRINT_ANY, "fwmark", "fwmark 0x%x ", mark); + } } if (tb[FRA_IFNAME]) { - fprintf(fp, "iif %s ", rta_getattr_str(tb[FRA_IFNAME])); + if (!is_json_context()) + fprintf(fp, "iif "); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "iif", "%s ", + rta_getattr_str(tb[FRA_IFNAME])); + if (frh->flags & FIB_RULE_IIF_DETACHED) - fprintf(fp, "[detached] "); + print_null(PRINT_ANY, "iif_detached", "[detached] ", + NULL); } if (tb[FRA_OIFNAME]) { - fprintf(fp, "oif %s ", rta_getattr_str(tb[FRA_OIFNAME])); + if (!is_json_context()) + fprintf(fp, "oif "); + + print_color_string(PRINT_ANY, COLOR_IFNAME, "oif", "%s ", + rta_getattr_str(tb[FRA_OIFNAME])); + if (frh->flags & FIB_RULE_OIF_DETACHED) - fprintf(fp, "[detached] "); + print_null(PRINT_ANY, "oif_detached", "[detached] ", + NULL); } if (tb[FRA_L3MDEV]) { - if (rta_getattr_u8(tb[FRA_L3MDEV])) - fprintf(fp, "lookup [l3mdev-table] "); + __u8 mdev = rta_getattr_u8(tb[FRA_L3MDEV]); + + if (mdev) + print_null(PRINT_ANY, "l3mdev", + "lookup [l3mdev-table] ", NULL); } if (tb[FRA_UID_RANGE]) { struct fib_rule_uid_range *r = RTA_DATA(tb[FRA_UID_RANGE]); - fprintf(fp, "uidrange %u-%u ", r->start, r->end); + print_uint(PRINT_ANY, "uid_start", "uidrange %u", r->start); + print_uint(PRINT_ANY, "uid_end", "-%u ", r->end); } table = frh_get_table(frh, tb); if (table) { - fprintf(fp, "lookup %s ", - rtnl_rttable_n2a(table, b1, sizeof(b1))); + print_string(PRINT_ANY, "table", + "lookup %s ", + rtnl_rttable_n2a(table, b1, sizeof(b1))); if (tb[FRA_SUPPRESS_PREFIXLEN]) { int pl = rta_getattr_u32(tb[FRA_SUPPRESS_PREFIXLEN]); if (pl != -1) - fprintf(fp, "suppress_prefixlength %d ", pl); + print_int(PRINT_ANY, "suppress_prefixlen", + "suppress_prefixlength %d ", pl); } + if (tb[FRA_SUPPRESS_IFGROUP]) { int group = rta_getattr_u32(tb[FRA_SUPPRESS_IFGROUP]); if (group != -1) { - SPRINT_BUF(b1); - fprintf(fp, "suppress_ifgroup %s ", - rtnl_group_n2a(group, b1, sizeof(b1))); + const char *grname + = rtnl_group_n2a(group, b1, sizeof(b1)); + + print_string(PRINT_ANY, "suppress_ifgroup", + "suppress_ifgroup %s ", grname); } } } @@ -311,47 +338,52 @@ int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) __u32 from = to>>16; to &= 0xFFFF; - if (from) { - fprintf(fp, "realms %s/", - rtnl_rtrealm_n2a(from, b1, sizeof(b1))); - } - fprintf(fp, "%s ", - rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + if (from) + print_string(PRINT_ANY, + "flow_from", "realms %s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + + print_string(PRINT_ANY, "flow_to", "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); } if (frh->action == RTN_NAT) { if (tb[RTA_GATEWAY]) { - fprintf(fp, "map-to %s ", - format_host_rta(frh->family, - tb[RTA_GATEWAY])); - } else - fprintf(fp, "masquerade"); + const char *gateway; + + gateway = format_host_rta(frh->family, tb[RTA_GATEWAY]); + + print_string(PRINT_ANY, "nat_gateway", + "map-to %s ", gateway); + } else { + print_null(PRINT_ANY, "masquerade", "masquerade", NULL); + } } else if (frh->action == FR_ACT_GOTO) { - fprintf(fp, "goto "); if (tb[FRA_GOTO]) - fprintf(fp, "%u", rta_getattr_u32(tb[FRA_GOTO])); + print_uint(PRINT_ANY, "goto", "goto %u", + rta_getattr_u32(tb[FRA_GOTO])); else - fprintf(fp, "none"); + print_string(PRINT_ANY, "goto", "goto %s", "none"); + if (frh->flags & FIB_RULE_UNRESOLVED) - fprintf(fp, " [unresolved]"); - } else if (frh->action == FR_ACT_NOP) - fprintf(fp, "nop"); - else if (frh->action != FR_ACT_TO_TBL) - fprintf(fp, "%s", - rtnl_rtntype_n2a(frh->action, - b1, sizeof(b1))); + print_null(PRINT_ANY, "unresolved", "unresolved", NULL); + } else if (frh->action == FR_ACT_NOP) { + print_null(PRINT_ANY, "nop", "nop", NULL); + } else if (frh->action != FR_ACT_TO_TBL) { + print_string(PRINT_ANY, "to_tbl", "%s", + rtnl_rtntype_n2a(frh->action, b1, sizeof(b1))); + } if (tb[FRA_PROTOCOL]) { __u8 protocol = rta_getattr_u8(tb[FRA_PROTOCOL]); - if ((protocol && protocol != RTPROT_KERNEL) || - show_details > 0) { - fprintf(fp, " proto %s ", - rtnl_rtprot_n2a(protocol, b1, sizeof(b1))); + if ((protocol && protocol != RTPROT_KERNEL) || show_details > 0) { + print_string(PRINT_ANY, "protocol", " proto %s ", + rtnl_rtprot_n2a(protocol, b1, sizeof(b1))); } } - - fprintf(fp, "\n"); + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); fflush(fp); return 0; } @@ -554,10 +586,12 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action) return 1; } + new_json_obj(json); if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return 1; } + delete_json_obj(); return 0; } From 3c1e087b05ebd2ade25bb4804549387fbd14f67a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:12 -0800 Subject: [PATCH 04/12] ipntable: add json support Add JSON (and limited color) to ip neighbor table parameter output. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/ipntable.c | 404 ++++++++++++++++++++++++++------------------------ 1 file changed, 214 insertions(+), 190 deletions(-) diff --git a/ip/ipntable.c b/ip/ipntable.c index 2f72c989..92024864 100644 --- a/ip/ipntable.c +++ b/ip/ipntable.c @@ -31,6 +31,7 @@ #include "utils.h" #include "ip_common.h" +#include "json_print.h" static struct { @@ -338,7 +339,191 @@ static const char *ntable_strtime_delta(__u32 msec) return str; } -static int print_ntable(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +static void print_ndtconfig(const struct ndt_config *ndtc) +{ + + print_uint(PRINT_ANY, "key_length", + " config key_len %u ", ndtc->ndtc_key_len); + print_uint(PRINT_ANY, "entry_size", + "entry_size %u ", ndtc->ndtc_entry_size); + print_uint(PRINT_ANY, "entries", "entries %u ", ndtc->ndtc_entries); + + print_string(PRINT_FP, NULL, "%s", _SL_); + + print_string(PRINT_ANY, "last_flush", + " last_flush %s ", + ntable_strtime_delta(ndtc->ndtc_last_flush)); + print_string(PRINT_ANY, "last_rand", + "last_rand %s ", + ntable_strtime_delta(ndtc->ndtc_last_rand)); + + print_string(PRINT_FP, NULL, "%s", _SL_); + + print_uint(PRINT_ANY, "hash_rnd", + " hash_rnd %u ", ndtc->ndtc_hash_rnd); + print_0xhex(PRINT_ANY, "hash_mask", + "hash_mask %08x ", ndtc->ndtc_hash_mask); + + print_uint(PRINT_ANY, "hash_chain_gc", + "hash_chain_gc %u ", ndtc->ndtc_hash_chain_gc); + print_uint(PRINT_ANY, "proxy_qlen", + "proxy_qlen %u ", ndtc->ndtc_proxy_qlen); + + print_string(PRINT_FP, NULL, "%s", _SL_); +} + +static void print_ndtparams(struct rtattr *tpb[]) +{ + + if (tpb[NDTPA_IFINDEX]) { + __u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]); + + print_string(PRINT_FP, NULL, " dev ", NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "dev", "%s ", ll_index_to_name(ifindex)); + print_string(PRINT_FP, NULL, "%s", _SL_); + } + + print_string(PRINT_FP, NULL, " ", NULL); + if (tpb[NDTPA_REFCNT]) { + __u32 refcnt = rta_getattr_u32(tpb[NDTPA_REFCNT]); + + print_uint(PRINT_ANY, "refcnt", "refcnt %u ", refcnt); + } + + if (tpb[NDTPA_REACHABLE_TIME]) { + __u64 reachable = rta_getattr_u64(tpb[NDTPA_REACHABLE_TIME]); + + print_uint(PRINT_ANY, "reachable", + "reachable %llu ", reachable); + } + + if (tpb[NDTPA_BASE_REACHABLE_TIME]) { + __u64 breachable + = rta_getattr_u64(tpb[NDTPA_BASE_REACHABLE_TIME]); + + print_uint(PRINT_ANY, "base_reachable", + "base_reachable %llu ", breachable); + } + + if (tpb[NDTPA_RETRANS_TIME]) { + __u64 retrans = rta_getattr_u64(tpb[NDTPA_RETRANS_TIME]); + + print_uint(PRINT_ANY, "retrans", "retrans %llu ", retrans); + } + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + if (tpb[NDTPA_GC_STALETIME]) { + __u64 gc_stale = rta_getattr_u64(tpb[NDTPA_GC_STALETIME]); + + print_uint(PRINT_ANY, "gc_stale", "gc_stale %llu ", gc_stale); + } + + if (tpb[NDTPA_DELAY_PROBE_TIME]) { + __u64 delay_probe + = rta_getattr_u64(tpb[NDTPA_DELAY_PROBE_TIME]); + + print_uint(PRINT_ANY, "delay_probe", + "delay_probe %llu ", delay_probe); + } + + if (tpb[NDTPA_QUEUE_LEN]) { + __u32 queue = rta_getattr_u32(tpb[NDTPA_QUEUE_LEN]); + + print_uint(PRINT_ANY, "queue", "queue %u ", queue); + } + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + if (tpb[NDTPA_APP_PROBES]) { + __u32 aprobe = rta_getattr_u32(tpb[NDTPA_APP_PROBES]); + + print_uint(PRINT_ANY, "app_probes", "app_probes %u ", aprobe); + } + + if (tpb[NDTPA_UCAST_PROBES]) { + __u32 uprobe = rta_getattr_u32(tpb[NDTPA_UCAST_PROBES]); + + print_uint(PRINT_ANY, "ucast_probes", + "ucast_probes %u ", uprobe); + } + + if (tpb[NDTPA_MCAST_PROBES]) { + __u32 mprobe = rta_getattr_u32(tpb[NDTPA_MCAST_PROBES]); + + print_uint(PRINT_ANY, "mcast_probes", + "mcast_probes %u ", mprobe); + } + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + if (tpb[NDTPA_ANYCAST_DELAY]) { + __u64 anycast_delay = rta_getattr_u64(tpb[NDTPA_ANYCAST_DELAY]); + + print_uint(PRINT_ANY, "anycast_delay", + "anycast_delay %llu ", anycast_delay); + } + + if (tpb[NDTPA_PROXY_DELAY]) { + __u64 proxy_delay = rta_getattr_u64(tpb[NDTPA_PROXY_DELAY]); + + print_uint(PRINT_ANY, "proxy_delay", + "proxy_delay %llu ", proxy_delay); + } + + if (tpb[NDTPA_PROXY_QLEN]) { + __u32 pqueue = rta_getattr_u32(tpb[NDTPA_PROXY_QLEN]); + + print_uint(PRINT_ANY, "proxy_queue", "proxy_queue %u ", pqueue); + } + + if (tpb[NDTPA_LOCKTIME]) { + __u64 locktime = rta_getattr_u64(tpb[NDTPA_LOCKTIME]); + + print_uint(PRINT_ANY, "locktime", "locktime %llu ", locktime); + } + + print_string(PRINT_FP, NULL, "%s", _SL_); +} + +static void print_ndtstats(const struct ndt_stats *ndts) +{ + + print_string(PRINT_FP, NULL, " stats ", NULL); + + print_uint(PRINT_ANY, "allocs", "allocs %llu ", ndts->ndts_allocs); + print_uint(PRINT_ANY, "destroys", "destroys %llu ", + ndts->ndts_destroys); + print_uint(PRINT_ANY, "hash_grows", "hash_grows %llu ", + ndts->ndts_hash_grows); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + print_uint(PRINT_ANY, "res_failed", "res_failed %llu ", + ndts->ndts_res_failed); + print_uint(PRINT_ANY, "lookups", "lookups %llu ", ndts->ndts_lookups); + print_uint(PRINT_ANY, "hits", "hits %llu ", ndts->ndts_hits); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + print_uint(PRINT_ANY, "rcv_probes_mcast", "rcv_probes_mcast %llu ", + ndts->ndts_rcv_probes_mcast); + print_uint(PRINT_ANY, "rcv_probes_ucast", "rcv_probes_ucast %llu ", + ndts->ndts_rcv_probes_ucast); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + print_uint(PRINT_ANY, "periodic_gc_runs", "periodic_gc_runs %llu ", + ndts->ndts_periodic_gc_runs); + print_uint(PRINT_ANY, "forced_gc_runs", "forced_gc_runs %llu ", + ndts->ndts_forced_gc_runs); + + print_string(PRINT_FP, NULL, "%s", _SL_); +} + +static int print_ntable(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) { FILE *fp = (FILE *)arg; struct ndtmsg *ndtm = NLMSG_DATA(n); @@ -370,6 +555,7 @@ static int print_ntable(const struct sockaddr_nl *who, struct nlmsghdr *n, void if (filter.name && strcmp(filter.name, name)) return 0; } + if (tb[NDTA_PARMS]) { parse_rtattr(tpb, NDTPA_MAX, RTA_DATA(tb[NDTA_PARMS]), RTA_PAYLOAD(tb[NDTA_PARMS])); @@ -385,227 +571,63 @@ static int print_ntable(const struct sockaddr_nl *who, struct nlmsghdr *n, void } } - if (ndtm->ndtm_family == AF_INET) - fprintf(fp, "inet "); - else if (ndtm->ndtm_family == AF_INET6) - fprintf(fp, "inet6 "); - else if (ndtm->ndtm_family == AF_DECnet) - fprintf(fp, "dnet "); - else - fprintf(fp, "(%d) ", ndtm->ndtm_family); + open_json_object(NULL); + print_string(PRINT_ANY, "family", + "%s ", family_name(ndtm->ndtm_family)); if (tb[NDTA_NAME]) { const char *name = rta_getattr_str(tb[NDTA_NAME]); - fprintf(fp, "%s ", name); + print_string(PRINT_ANY, "name", "%s ", name); } - fprintf(fp, "%s", _SL_); + print_string(PRINT_FP, NULL, "%s", _SL_); ret = (tb[NDTA_THRESH1] || tb[NDTA_THRESH2] || tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]); if (ret) - fprintf(fp, " "); + print_string(PRINT_FP, NULL, " ", NULL); if (tb[NDTA_THRESH1]) { __u32 thresh1 = rta_getattr_u32(tb[NDTA_THRESH1]); - fprintf(fp, "thresh1 %u ", thresh1); + print_uint(PRINT_ANY, "thresh1", "thresh1 %u ", thresh1); } + if (tb[NDTA_THRESH2]) { __u32 thresh2 = rta_getattr_u32(tb[NDTA_THRESH2]); - fprintf(fp, "thresh2 %u ", thresh2); + print_uint(PRINT_ANY, "thresh2", "thresh2 %u ", thresh2); } + if (tb[NDTA_THRESH3]) { __u32 thresh3 = rta_getattr_u32(tb[NDTA_THRESH3]); - fprintf(fp, "thresh3 %u ", thresh3); + print_uint(PRINT_ANY, "thresh3", "thresh3 %u ", thresh3); } - if (tb[NDTA_GC_INTERVAL]) { - unsigned long long gc_int = rta_getattr_u64(tb[NDTA_GC_INTERVAL]); - fprintf(fp, "gc_int %llu ", gc_int); + if (tb[NDTA_GC_INTERVAL]) { + __u64 gc_int = rta_getattr_u64(tb[NDTA_GC_INTERVAL]); + + print_uint(PRINT_ANY, "gc_interval", "gc_int %llu ", gc_int); } if (ret) - fprintf(fp, "%s", _SL_); + print_string(PRINT_FP, NULL, "%s", _SL_); - if (tb[NDTA_CONFIG] && show_stats) { - struct ndt_config *ndtc = RTA_DATA(tb[NDTA_CONFIG]); + if (tb[NDTA_CONFIG] && show_stats) + print_ndtconfig(RTA_DATA(tb[NDTA_CONFIG])); - fprintf(fp, " "); - fprintf(fp, "config "); + if (tb[NDTA_PARMS]) + print_ndtparams(tpb); - fprintf(fp, "key_len %u ", ndtc->ndtc_key_len); - fprintf(fp, "entry_size %u ", ndtc->ndtc_entry_size); - fprintf(fp, "entries %u ", ndtc->ndtc_entries); - - fprintf(fp, "%s", _SL_); - fprintf(fp, " "); - - fprintf(fp, "last_flush %s ", - ntable_strtime_delta(ndtc->ndtc_last_flush)); - fprintf(fp, "last_rand %s ", - ntable_strtime_delta(ndtc->ndtc_last_rand)); - - fprintf(fp, "%s", _SL_); - fprintf(fp, " "); - - fprintf(fp, "hash_rnd %u ", ndtc->ndtc_hash_rnd); - fprintf(fp, "hash_mask %08x ", ndtc->ndtc_hash_mask); - - fprintf(fp, "hash_chain_gc %u ", ndtc->ndtc_hash_chain_gc); - fprintf(fp, "proxy_qlen %u ", ndtc->ndtc_proxy_qlen); - - fprintf(fp, "%s", _SL_); - } - - if (tb[NDTA_PARMS]) { - if (tpb[NDTPA_IFINDEX]) { - __u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]); - - fprintf(fp, " "); - fprintf(fp, "dev %s ", ll_index_to_name(ifindex)); - fprintf(fp, "%s", _SL_); - } - - fprintf(fp, " "); - - if (tpb[NDTPA_REFCNT]) { - __u32 refcnt = rta_getattr_u32(tpb[NDTPA_REFCNT]); - - fprintf(fp, "refcnt %u ", refcnt); - } - if (tpb[NDTPA_REACHABLE_TIME]) { - unsigned long long reachable = rta_getattr_u64(tpb[NDTPA_REACHABLE_TIME]); - - fprintf(fp, "reachable %llu ", reachable); - } - if (tpb[NDTPA_BASE_REACHABLE_TIME]) { - unsigned long long breachable = rta_getattr_u64(tpb[NDTPA_BASE_REACHABLE_TIME]); - - fprintf(fp, "base_reachable %llu ", breachable); - } - if (tpb[NDTPA_RETRANS_TIME]) { - unsigned long long retrans = rta_getattr_u64(tpb[NDTPA_RETRANS_TIME]); - - fprintf(fp, "retrans %llu ", retrans); - } - - fprintf(fp, "%s", _SL_); - - fprintf(fp, " "); - - if (tpb[NDTPA_GC_STALETIME]) { - unsigned long long gc_stale = rta_getattr_u64(tpb[NDTPA_GC_STALETIME]); - - fprintf(fp, "gc_stale %llu ", gc_stale); - } - if (tpb[NDTPA_DELAY_PROBE_TIME]) { - unsigned long long delay_probe = rta_getattr_u64(tpb[NDTPA_DELAY_PROBE_TIME]); - - fprintf(fp, "delay_probe %llu ", delay_probe); - } - if (tpb[NDTPA_QUEUE_LEN]) { - __u32 queue = rta_getattr_u32(tpb[NDTPA_QUEUE_LEN]); - - fprintf(fp, "queue %u ", queue); - } - - fprintf(fp, "%s", _SL_); - - fprintf(fp, " "); - - if (tpb[NDTPA_APP_PROBES]) { - __u32 aprobe = rta_getattr_u32(tpb[NDTPA_APP_PROBES]); - - fprintf(fp, "app_probes %u ", aprobe); - } - if (tpb[NDTPA_UCAST_PROBES]) { - __u32 uprobe = rta_getattr_u32(tpb[NDTPA_UCAST_PROBES]); - - fprintf(fp, "ucast_probes %u ", uprobe); - } - if (tpb[NDTPA_MCAST_PROBES]) { - __u32 mprobe = rta_getattr_u32(tpb[NDTPA_MCAST_PROBES]); - - fprintf(fp, "mcast_probes %u ", mprobe); - } - - fprintf(fp, "%s", _SL_); - - fprintf(fp, " "); - - if (tpb[NDTPA_ANYCAST_DELAY]) { - unsigned long long anycast_delay = rta_getattr_u64(tpb[NDTPA_ANYCAST_DELAY]); - - fprintf(fp, "anycast_delay %llu ", anycast_delay); - } - if (tpb[NDTPA_PROXY_DELAY]) { - unsigned long long proxy_delay = rta_getattr_u64(tpb[NDTPA_PROXY_DELAY]); - - fprintf(fp, "proxy_delay %llu ", proxy_delay); - } - if (tpb[NDTPA_PROXY_QLEN]) { - __u32 pqueue = rta_getattr_u32(tpb[NDTPA_PROXY_QLEN]); - - fprintf(fp, "proxy_queue %u ", pqueue); - } - if (tpb[NDTPA_LOCKTIME]) { - unsigned long long locktime = rta_getattr_u64(tpb[NDTPA_LOCKTIME]); - - fprintf(fp, "locktime %llu ", locktime); - } - - fprintf(fp, "%s", _SL_); - } - - if (tb[NDTA_STATS] && show_stats) { - struct ndt_stats *ndts = RTA_DATA(tb[NDTA_STATS]); - - fprintf(fp, " "); - fprintf(fp, "stats "); - - fprintf(fp, "allocs %llu ", - (unsigned long long) ndts->ndts_allocs); - fprintf(fp, "destroys %llu ", - (unsigned long long) ndts->ndts_destroys); - fprintf(fp, "hash_grows %llu ", - (unsigned long long) ndts->ndts_hash_grows); - - fprintf(fp, "%s", _SL_); - fprintf(fp, " "); - - fprintf(fp, "res_failed %llu ", - (unsigned long long) ndts->ndts_res_failed); - fprintf(fp, "lookups %llu ", - (unsigned long long) ndts->ndts_lookups); - fprintf(fp, "hits %llu ", - (unsigned long long) ndts->ndts_hits); - - fprintf(fp, "%s", _SL_); - fprintf(fp, " "); - - fprintf(fp, "rcv_probes_mcast %llu ", - (unsigned long long) ndts->ndts_rcv_probes_mcast); - fprintf(fp, "rcv_probes_ucast %llu ", - (unsigned long long) ndts->ndts_rcv_probes_ucast); - - fprintf(fp, "%s", _SL_); - fprintf(fp, " "); - - fprintf(fp, "periodic_gc_runs %llu ", - (unsigned long long) ndts->ndts_periodic_gc_runs); - fprintf(fp, "forced_gc_runs %llu ", - (unsigned long long) ndts->ndts_forced_gc_runs); - - fprintf(fp, "%s", _SL_); - } - - fprintf(fp, "\n"); + if (tb[NDTA_STATS] && show_stats) + print_ndtstats(RTA_DATA(tb[NDTA_STATS])); + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); fflush(fp); + return 0; } @@ -643,10 +665,12 @@ static int ipntable_show(int argc, char **argv) exit(1); } + new_json_obj(json); if (rtnl_dump_filter(&rth, print_ntable, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } + delete_json_obj(); return 0; } From 96032aaf7d43129df8056344dd075c9dcfe420d0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:13 -0800 Subject: [PATCH 05/12] ipnetconf: add JSON support Basic JSON support for ip netconf command. Also cleanup some checkpatch warnings about long lines. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/ipnetconf.c | 69 ++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/ip/ipnetconf.c b/ip/ipnetconf.c index 76e639a8..03f98ace 100644 --- a/ip/ipnetconf.c +++ b/ip/ipnetconf.c @@ -29,6 +29,10 @@ static struct { int ifindex; } filter; +static const char * const rp_filter_names[] = { + "off", "strict", "loose" +}; + static void usage(void) __attribute__((noreturn)); static void usage(void) @@ -37,9 +41,12 @@ static void usage(void) exit(-1); } -static void print_onoff(FILE *f, const char *flag, __u32 val) +static void print_onoff(FILE *fp, const char *flag, __u32 val) { - fprintf(f, "%s %s ", flag, val ? "on" : "off"); + if (is_json_context()) + print_bool(PRINT_JSON, flag, NULL, val); + else + fprintf(fp, "%s %s ", flag, val ? "on" : "off"); } static struct rtattr *netconf_rta(struct netconfmsg *ncm) @@ -83,50 +90,44 @@ int print_netconf(const struct sockaddr_nl *who, struct rtnl_ctrl_data *ctrl, if (filter.ifindex && filter.ifindex != ifindex) return 0; - switch (ncm->ncm_family) { - case AF_INET: - fprintf(fp, "ipv4 "); - break; - case AF_INET6: - fprintf(fp, "ipv6 "); - break; - case AF_MPLS: - fprintf(fp, "mpls "); - break; - default: - fprintf(fp, "unknown "); - break; - } + open_json_object(NULL); + print_string(PRINT_ANY, "family", + "%s ", family_name(ncm->ncm_family)); if (tb[NETCONFA_IFINDEX]) { + const char *dev; + switch (ifindex) { case NETCONFA_IFINDEX_ALL: - fprintf(fp, "all "); + dev = "all"; break; case NETCONFA_IFINDEX_DEFAULT: - fprintf(fp, "default "); + dev = "default"; break; default: - fprintf(fp, "dev %s ", ll_index_to_name(ifindex)); + dev = ll_index_to_name(ifindex); break; } + print_color_string(PRINT_ANY, COLOR_IFNAME, + "interface", "%s ", dev); } if (tb[NETCONFA_FORWARDING]) print_onoff(fp, "forwarding", rta_getattr_u32(tb[NETCONFA_FORWARDING])); + if (tb[NETCONFA_RP_FILTER]) { __u32 rp_filter = rta_getattr_u32(tb[NETCONFA_RP_FILTER]); - if (rp_filter == 0) - fprintf(fp, "rp_filter off "); - else if (rp_filter == 1) - fprintf(fp, "rp_filter strict "); - else if (rp_filter == 2) - fprintf(fp, "rp_filter loose "); + if (rp_filter < ARRAY_SIZE(rp_filter_names)) + print_string(PRINT_ANY, "rp_filter", + "rp_filter %s ", + rp_filter_names[rp_filter]); else - fprintf(fp, "rp_filter unknown mode "); + print_uint(PRINT_ANY, "rp_filter", + "rp_filter %u ", rp_filter); } + if (tb[NETCONFA_MC_FORWARDING]) print_onoff(fp, "mc_forwarding", rta_getattr_u32(tb[NETCONFA_MC_FORWARDING])); @@ -142,7 +143,8 @@ int print_netconf(const struct sockaddr_nl *who, struct rtnl_ctrl_data *ctrl, if (tb[NETCONFA_INPUT]) print_onoff(fp, "input", rta_getattr_u32(tb[NETCONFA_INPUT])); - fprintf(fp, "\n"); + close_json_object(); + print_string(PRINT_FP, NULL, "\n", NULL); fflush(fp); return 0; } @@ -179,7 +181,8 @@ static int do_show(int argc, char **argv) NEXT_ARG(); filter.ifindex = ll_name_to_index(*argv); if (filter.ifindex <= 0) { - fprintf(stderr, "Device \"%s\" does not exist.\n", + fprintf(stderr, + "Device \"%s\" does not exist.\n", *argv); return -1; } @@ -202,10 +205,13 @@ static int do_show(int argc, char **argv) } else { rth.flags = RTNL_HANDLE_F_SUPPRESS_NLERR; dump: - if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNETCONF) < 0) { + if (rtnl_wilddump_request(&rth, filter.family, + RTM_GETNETCONF) < 0) { perror("Cannot send dump request"); exit(1); } + + new_json_obj(json); if (rtnl_dump_filter(&rth, print_netconf2, stdout) < 0) { /* kernel does not support netconf dump on AF_UNSPEC; * fall back to requesting by family @@ -219,6 +225,7 @@ dump: fprintf(stderr, "Dump terminated\n"); exit(1); } + delete_json_obj(); if (preferred_family == AF_UNSPEC && filter.family == AF_INET) { preferred_family = AF_INET6; filter.family = AF_INET6; @@ -240,6 +247,8 @@ int do_ipnetconf(int argc, char **argv) } else return do_show(0, NULL); - fprintf(stderr, "Command \"%s\" is unknown, try \"ip netconf help\".\n", *argv); + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip netconf help\".\n", + *argv); exit(-1); } From 8a61d8968c4312dcb59850ce3545726a98f3e36c Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:14 -0800 Subject: [PATCH 06/12] tcp_metrics; make tables const Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/tcp_metrics.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ip/tcp_metrics.c b/ip/tcp_metrics.c index 7e2d9eb3..5f394765 100644 --- a/ip/tcp_metrics.c +++ b/ip/tcp_metrics.c @@ -47,8 +47,8 @@ static int genl_family = -1; #define CMD_DEL 0x0002 /* delete, remove */ #define CMD_FLUSH 0x0004 /* flush */ -static struct { - char *name; +static const struct { + const char *name; int code; } cmds[] = { { "list", CMD_LIST }, @@ -59,7 +59,7 @@ static struct { { "flush", CMD_FLUSH }, }; -static char *metric_name[TCP_METRIC_MAX + 1] = { +static const char *metric_name[TCP_METRIC_MAX + 1] = { [TCP_METRIC_RTT] = "rtt", [TCP_METRIC_RTTVAR] = "rttvar", [TCP_METRIC_SSTHRESH] = "ssthresh", @@ -67,8 +67,7 @@ static char *metric_name[TCP_METRIC_MAX + 1] = { [TCP_METRIC_REORDERING] = "reordering", }; -static struct -{ +static struct { int flushed; char *flushb; int flushp; From 74498126fd6528b503544c328bb6609279bd65ac Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:15 -0800 Subject: [PATCH 07/12] tcp_metrics: add json support Add JSON support to the ip tcp_metrics output. $ ip -j -p tcp_metrics show [ { "dst": "192.18.1.11", "age": 23617.8, "ssthresh": 7, "cwnd": 3, "rtt": 0.039176, "rttvar": 0.039176, "source": "192.18.1.2" } ... The JSON output does scale values differently since there is no good way to indicate units. The rtt values are displayed in seconds in JSON and microseconds in the original (non JSON) mode. In the example above the output in without the -j flag, the output would be ... rtt 39176us rttvar 39176us I did this since all the other values in the JSON record are also in floating point seconds. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/tcp_metrics.c | 179 +++++++++++++++++++++++++++++------------------ 1 file changed, 110 insertions(+), 69 deletions(-) diff --git a/ip/tcp_metrics.c b/ip/tcp_metrics.c index 5f394765..72dc980c 100644 --- a/ip/tcp_metrics.c +++ b/ip/tcp_metrics.c @@ -38,6 +38,7 @@ static void usage(void) /* netlink socket */ static struct rtnl_handle grth = { .fd = -1 }; static int genl_family = -1; +static const double usec_per_sec = 1000000.; #define TCPM_REQUEST(_req, _bufsiz, _cmd, _flags) \ GENL_REQUEST(_req, _bufsiz, genl_family, 0, \ @@ -87,15 +88,84 @@ static int flush_update(void) return 0; } +static void print_tcp_metrics(struct rtattr *a) +{ + struct rtattr *m[TCP_METRIC_MAX + 1 + 1]; + unsigned long rtt = 0, rttvar = 0; + int i; + + parse_rtattr_nested(m, TCP_METRIC_MAX + 1, a); + + for (i = 0; i < TCP_METRIC_MAX + 1; i++) { + const char *name; + __u32 val; + SPRINT_BUF(b1); + + a = m[i + 1]; + if (!a) + continue; + + val = rta_getattr_u32(a); + + switch (i) { + case TCP_METRIC_RTT: + if (!rtt) + rtt = (val * 1000UL) >> 3; + continue; + case TCP_METRIC_RTTVAR: + if (!rttvar) + rttvar = (val * 1000UL) >> 2; + continue; + case TCP_METRIC_RTT_US: + rtt = val >> 3; + continue; + + case TCP_METRIC_RTTVAR_US: + rttvar = val >> 2; + continue; + + case TCP_METRIC_SSTHRESH: + case TCP_METRIC_CWND: + case TCP_METRIC_REORDERING: + name = metric_name[i]; + break; + + default: + snprintf(b1, sizeof(b1), + " metric_%d ", i); + name = b1; + } + + + print_uint(PRINT_JSON, name, NULL, val); + print_string(PRINT_FP, NULL, " %s ", name); + print_uint(PRINT_FP, NULL, "%lu", val); + } + + if (rtt) { + print_float(PRINT_JSON, "rtt", NULL, + (double)rtt / usec_per_sec); + print_uint(PRINT_FP, NULL, + " rtt %luus", rtt); + } + if (rttvar) { + print_float(PRINT_JSON, "rttvar", NULL, + (double) rttvar / usec_per_sec); + print_uint(PRINT_FP, NULL, + " rttvar %luus", rttvar); + } +} + static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { FILE *fp = (FILE *) arg; struct genlmsghdr *ghdr; struct rtattr *attrs[TCP_METRICS_ATTR_MAX + 1], *a; + const char *h; int len = n->nlmsg_len; inet_prefix daddr, saddr; - int i, atype, stype; + int atype, stype; if (n->nlmsg_type != genl_family) return -1; @@ -185,96 +255,60 @@ static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n, return 0; } + open_json_object(NULL); if (f.cmd & (CMD_DEL | CMD_FLUSH)) - fprintf(fp, "Deleted "); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); - fprintf(fp, "%s", - format_host(daddr.family, daddr.bytelen, daddr.data)); + h = format_host(daddr.family, daddr.bytelen, daddr.data); + print_color_string(PRINT_ANY, + ifa_family_color(daddr.family), + "dst", "%s", h); a = attrs[TCP_METRICS_ATTR_AGE]; if (a) { - unsigned long long val = rta_getattr_u64(a); + __u64 val = rta_getattr_u64(a); + double age = val / 1000.; - fprintf(fp, " age %llu.%03llusec", - val / 1000, val % 1000); + print_float(PRINT_ANY, "age", + " age %.03fsec", age); } a = attrs[TCP_METRICS_ATTR_TW_TS_STAMP]; if (a) { __s32 val = (__s32) rta_getattr_u32(a); __u32 tsval; + char tw_ts[64]; a = attrs[TCP_METRICS_ATTR_TW_TSVAL]; tsval = a ? rta_getattr_u32(a) : 0; - fprintf(fp, " tw_ts %u/%dsec ago", tsval, val); + snprintf(tw_ts, sizeof(tw_ts), + "%u/%d", tsval, val); + print_string(PRINT_ANY, "tw_ts_stamp", + " tw_ts %s ago", tw_ts); } - a = attrs[TCP_METRICS_ATTR_VALS]; - if (a) { - struct rtattr *m[TCP_METRIC_MAX + 1 + 1]; - unsigned long rtt = 0, rttvar = 0; - - parse_rtattr_nested(m, TCP_METRIC_MAX + 1, a); - - for (i = 0; i < TCP_METRIC_MAX + 1; i++) { - unsigned long val; - - a = m[i + 1]; - if (!a) - continue; - if (i != TCP_METRIC_RTT && - i != TCP_METRIC_RTT_US && - i != TCP_METRIC_RTTVAR && - i != TCP_METRIC_RTTVAR_US) { - if (metric_name[i]) - fprintf(fp, " %s ", metric_name[i]); - else - fprintf(fp, " metric_%d ", i); - } - val = rta_getattr_u32(a); - switch (i) { - case TCP_METRIC_RTT: - if (!rtt) - rtt = (val * 1000UL) >> 3; - break; - case TCP_METRIC_RTTVAR: - if (!rttvar) - rttvar = (val * 1000UL) >> 2; - break; - case TCP_METRIC_RTT_US: - rtt = val >> 3; - break; - case TCP_METRIC_RTTVAR_US: - rttvar = val >> 2; - break; - case TCP_METRIC_SSTHRESH: - case TCP_METRIC_CWND: - case TCP_METRIC_REORDERING: - default: - fprintf(fp, "%lu", val); - break; - } - } - if (rtt) - fprintf(fp, " rtt %luus", rtt); - if (rttvar) - fprintf(fp, " rttvar %luus", rttvar); - } + if (attrs[TCP_METRICS_ATTR_VALS]) + print_tcp_metrics(attrs[TCP_METRICS_ATTR_VALS]); a = attrs[TCP_METRICS_ATTR_FOPEN_MSS]; - if (a) - fprintf(fp, " fo_mss %u", rta_getattr_u16(a)); + if (a) { + print_uint(PRINT_ANY, "fopen_miss", " fo_mss %u", + rta_getattr_u16(a)); + } a = attrs[TCP_METRICS_ATTR_FOPEN_SYN_DROPS]; if (a) { __u16 syn_loss = rta_getattr_u16(a); - unsigned long long ts; + double ts; a = attrs[TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS]; ts = a ? rta_getattr_u64(a) : 0; - fprintf(fp, " fo_syn_drops %u/%llu.%03llusec ago", - syn_loss, ts / 1000, ts % 1000); + print_uint(PRINT_ANY, "fopen_syn_drops", + " fo_syn_drops %u", syn_loss); + print_float(PRINT_ANY, "fopen_syn_drop_ts", + "/%.03fusec ago", + ts / 1000000.); } a = attrs[TCP_METRICS_ATTR_FOPEN_COOKIE]; @@ -288,16 +322,21 @@ static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n, cookie[0] = 0; for (i = 0; i < max; i++) sprintf(cookie + i + i, "%02x", ptr[i]); - fprintf(fp, " fo_cookie %s", cookie); + + print_string(PRINT_ANY, "fo_cookie", + " fo_cookie %s", cookie); } if (saddr.family) { - fprintf(fp, " source %s", - format_host(saddr.family, saddr.bytelen, saddr.data)); + const char *src; + + src = format_host(saddr.family, saddr.bytelen, saddr.data); + print_string(PRINT_ANY, "source", + " source %s", src); } - fprintf(fp, "\n"); - + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); fflush(fp); return 0; } @@ -474,10 +513,12 @@ static int tcpm_do_cmd(int cmd, int argc, char **argv) exit(1); } + new_json_obj(json); if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } + delete_json_obj(); } return 0; } From 111f79ad3863c1d1f20b277d6fc8f9d2e76cd846 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:16 -0800 Subject: [PATCH 08/12] ipsr: add json support Add json flag to ip sr command outputs. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/ipseg6.c | 93 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/ip/ipseg6.c b/ip/ipseg6.c index e3ab31a5..6f5ae4d2 100644 --- a/ip/ipseg6.c +++ b/ip/ipseg6.c @@ -26,6 +26,7 @@ #include "utils.h" #include "ip_common.h" #include "libgenl.h" +#include "json_print.h" #define HMAC_KEY_PROMPT "Enter secret for HMAC key ID (blank to delete): " @@ -55,12 +56,54 @@ static struct { __u8 alg_id; } opts; +static void print_dumphmac(struct rtattr *attrs[]) +{ + char secret[64]; + char *algstr; + __u8 slen = rta_getattr_u8(attrs[SEG6_ATTR_SECRETLEN]); + __u8 alg_id = rta_getattr_u8(attrs[SEG6_ATTR_ALGID]); + + memset(secret, 0, 64); + + if (slen > 63) { + fprintf(stderr, "HMAC secret length %d > 63, truncated\n", slen); + slen = 63; + } + + memcpy(secret, RTA_DATA(attrs[SEG6_ATTR_SECRET]), slen); + + switch (alg_id) { + case SEG6_HMAC_ALGO_SHA1: + algstr = "sha1"; + break; + case SEG6_HMAC_ALGO_SHA256: + algstr = "sha256"; + break; + default: + algstr = ""; + } + + print_uint(PRINT_ANY, "hmac", "hmac %u ", + rta_getattr_u32(attrs[SEG6_ATTR_HMACKEYID])); + print_string(PRINT_ANY, "algo", "algo %s ", algstr); + print_string(PRINT_ANY, "secret", "secret \"%s\"\n", secret); +} + +static void print_tunsrc(struct rtattr *attrs[]) +{ + const char *dst + = rt_addr_n2a(AF_INET6, 16, + RTA_DATA(attrs[SEG6_ATTR_DST])); + + print_string(PRINT_ANY, "tunsrc", + "tunsrc addr %s\n", dst); +} + static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { struct rtattr *attrs[SEG6_ATTR_MAX + 1]; struct genlmsghdr *ghdr; - FILE *fp = (FILE *)arg; int len = n->nlmsg_len; if (n->nlmsg_type != genl_family) @@ -74,50 +117,17 @@ static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n, parse_rtattr(attrs, SEG6_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len); + open_json_object(NULL); switch (ghdr->cmd) { case SEG6_CMD_DUMPHMAC: - { - char secret[64]; - char *algstr; - __u8 slen = rta_getattr_u8(attrs[SEG6_ATTR_SECRETLEN]); - __u8 alg_id = rta_getattr_u8(attrs[SEG6_ATTR_ALGID]); - - memset(secret, 0, 64); - - if (slen > 63) { - fprintf(stderr, "HMAC secret length %d > 63, " - "truncated\n", slen); - slen = 63; - } - memcpy(secret, RTA_DATA(attrs[SEG6_ATTR_SECRET]), slen); - - switch (alg_id) { - case SEG6_HMAC_ALGO_SHA1: - algstr = "sha1"; - break; - case SEG6_HMAC_ALGO_SHA256: - algstr = "sha256"; - break; - default: - algstr = ""; - } - - fprintf(fp, "hmac %u ", - rta_getattr_u32(attrs[SEG6_ATTR_HMACKEYID])); - fprintf(fp, "algo %s ", algstr); - fprintf(fp, "secret \"%s\" ", secret); - - fprintf(fp, "\n"); + print_dumphmac(attrs); break; - } + case SEG6_CMD_GET_TUNSRC: - { - fprintf(fp, "tunsrc addr %s\n", - rt_addr_n2a(AF_INET6, 16, - RTA_DATA(attrs[SEG6_ATTR_DST]))); + print_tunsrc(attrs); break; } - } + close_json_object(); return 0; } @@ -169,10 +179,12 @@ static int seg6_do_cmd(void) } else if (repl) { if (rtnl_talk(&grth, &req.n, &answer) < 0) return -2; + new_json_obj(json); if (process_msg(NULL, answer, stdout) < 0) { fprintf(stderr, "Error parsing reply\n"); exit(1); } + delete_json_obj(); free(answer); } else { req.n.nlmsg_flags |= NLM_F_DUMP; @@ -182,10 +194,13 @@ static int seg6_do_cmd(void) exit(1); } + new_json_obj(json); if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } + delete_json_obj(); + fflush(stdout); } return 0; From b62ec792a9862875fea65896f765b7c9eed0dd90 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:17 -0800 Subject: [PATCH 09/12] token: support JSON Add JSON output to ip token command. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/iptoken.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ip/iptoken.c b/ip/iptoken.c index 48cc14b5..fb64da4e 100644 --- a/ip/iptoken.c +++ b/ip/iptoken.c @@ -25,6 +25,7 @@ #include "rt_names.h" #include "utils.h" #include "ip_common.h" +#include "json_print.h" extern struct rtnl_handle rth; @@ -77,9 +78,17 @@ static int print_token(const struct sockaddr_nl *who, struct nlmsghdr *n, void * return -1; } - fprintf(fp, "token %s dev %s\n", - format_host_rta(ifi->ifi_family, ltb[IFLA_INET6_TOKEN]), - ll_index_to_name(ifi->ifi_index)); + open_json_object(NULL); + print_string(PRINT_FP, NULL, "token ", NULL); + print_color_string(PRINT_ANY, + ifa_family_color(ifi->ifi_family), + "token", "%s", + format_host_rta(ifi->ifi_family, ltb[IFLA_INET6_TOKEN])); + print_string(PRINT_FP, NULL, " dev ", NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "ifname", "%s\n", + ll_index_to_name(ifi->ifi_index)); + close_json_object(); fflush(fp); return 0; @@ -105,10 +114,13 @@ static int iptoken_list(int argc, char **argv) return -1; } + new_json_obj(json); if (rtnl_dump_filter(&rth, print_token, &da) < 0) { + delete_json_obj(); fprintf(stderr, "Dump terminated\n"); return -1; } + delete_json_obj(); return 0; } From 689bef5dc97aeb446f34dc1f4158cf92de456866 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:18 -0800 Subject: [PATCH 10/12] tuntap: support JSON output Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/iptuntap.c | 57 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/ip/iptuntap.c b/ip/iptuntap.c index 2f49ff6d..6c5a7259 100644 --- a/ip/iptuntap.c +++ b/ip/iptuntap.c @@ -228,24 +228,35 @@ static int do_del(int argc, char **argv) static void print_flags(long flags) { + open_json_array(PRINT_JSON, "flags"); + if (flags & IFF_TUN) - printf(" tun"); + print_string(PRINT_ANY, NULL, " %s", "tun"); if (flags & IFF_TAP) - printf(" tap"); + print_string(PRINT_ANY, NULL, " %s", "tap"); if (!(flags & IFF_NO_PI)) - printf(" pi"); + print_string(PRINT_ANY, NULL, " %s", "pi"); if (flags & IFF_ONE_QUEUE) - printf(" one_queue"); + print_string(PRINT_ANY, NULL, " %s", "one_queue"); if (flags & IFF_VNET_HDR) - printf(" vnet_hdr"); + print_string(PRINT_ANY, NULL, " %s", "vnet_hdr"); - flags &= ~(IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE|IFF_VNET_HDR); + if (flags & IFF_PERSIST) + print_string(PRINT_ANY, NULL, " %s", "persist"); + + if (!(flags & IFF_NOFILTER)) + print_string(PRINT_ANY, NULL, " %s", "filter"); + + flags &= ~(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | + IFF_VNET_HDR | IFF_PERSIST | IFF_NOFILTER); if (flags) - printf(" UNKNOWN_FLAGS:%lx", flags); + print_0xhex(PRINT_ANY, NULL, "%#x", flags); + + close_json_array(PRINT_JSON, NULL); } static char *pid_name(pid_t pid) @@ -288,6 +299,8 @@ static void show_processes(const char *name) if (err) return; + open_json_array(PRINT_JSON, "processes"); + fd_path = globbuf.gl_pathv; while (*fd_path) { const char *dev_net_tun = "/dev/net/tun"; @@ -334,7 +347,11 @@ static void show_processes(const char *name) !strcmp(name, value)) { char *pname = pid_name(pid); - printf(" %s(%d)", pname ? : "", pid); + print_string(PRINT_ANY, "name", + "%s", pname ? : ""); + + print_uint(PRINT_ANY, "pid", + "(%d)", pid); free(pname); } @@ -347,6 +364,7 @@ static void show_processes(const char *name) next: ++fd_path; } + close_json_array(PRINT_JSON, NULL); globfree(&globbuf); } @@ -417,18 +435,24 @@ static int print_tuntap(const struct sockaddr_nl *who, if (read_prop(name, "group", &group)) return 0; - printf("%s:", name); + open_json_object(NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "ifname", "%s:", name); print_flags(flags); if (owner != -1) - printf(" user %ld", owner); + print_uint(PRINT_ANY, "user", + " user %ld", owner); if (group != -1) - printf(" group %ld", group); - fputc('\n', stdout); + print_uint(PRINT_ANY, "group", + " group %ld", group); + if (show_details) { - printf("\tAttached to processes:"); + print_string(PRINT_FP, NULL, + "%s\tAttached to processes:", _SL_); show_processes(name); - fputc('\n', stdout); } + close_json_object(); + print_string(PRINT_FP, NULL, "%s", "\n"); return 0; } @@ -441,11 +465,16 @@ static int do_show(int argc, char **argv) return -1; } + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_tuntap, NULL) < 0) { fprintf(stderr, "Dump terminated\n"); return -1; } + delete_json_obj(); + fflush(stdout); + return 0; } From 5c92c2eee5044333568f1f2c232a3c92241e412a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:19 -0800 Subject: [PATCH 11/12] fou: break long lines Split up long lines. Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/ipfou.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/ip/ipfou.c b/ip/ipfou.c index 1f392ade..a5d1a4bb 100644 --- a/ip/ipfou.c +++ b/ip/ipfou.c @@ -25,13 +25,13 @@ static void usage(void) { - fprintf(stderr, "Usage: ip fou add port PORT " - "{ ipproto PROTO | gue } [ -6 ]\n"); - fprintf(stderr, " ip fou del port PORT [ -6 ]\n"); - fprintf(stderr, " ip fou show\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Where: PROTO { ipproto-name | 1..255 }\n"); - fprintf(stderr, " PORT { 1..65535 }\n"); + fprintf(stderr, + "Usage: ip fou add port PORT { ipproto PROTO | gue } [ -6 ]\n" + " ip fou del port PORT [ -6 ]\n" + " ip fou show\n" + "\n" + "Where: PROTO { ipproto-name | 1..255 }\n" + " PORT { 1..65535 }\n"); exit(-1); } @@ -77,7 +77,8 @@ static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n, } else if (!matches(*argv, "-6")) { family = AF_INET6; } else { - fprintf(stderr, "fou: unknown command \"%s\"?\n", *argv); + fprintf(stderr + , "fou: unknown command \"%s\"?\n", *argv); usage(); return -1; } @@ -142,7 +143,7 @@ static int print_fou_mapping(const struct sockaddr_nl *who, struct genlmsghdr *ghdr; struct rtattr *tb[FOU_ATTR_MAX + 1]; int len = n->nlmsg_len; - unsigned family; + unsigned int family; if (n->nlmsg_type != genl_family) return 0; @@ -175,7 +176,8 @@ static int do_show(int argc, char **argv) FOU_REQUEST(req, 4096, FOU_CMD_GET, NLM_F_REQUEST | NLM_F_DUMP); if (argc > 0) { - fprintf(stderr, "\"ip fou show\" does not take any arguments.\n"); + fprintf(stderr, + "\"ip fou show\" does not take any arguments.\n"); return -1; } @@ -209,6 +211,8 @@ int do_ipfou(int argc, char **argv) return do_del(argc-1, argv+1); if (matches(*argv, "show") == 0) return do_show(argc-1, argv+1); - fprintf(stderr, "Command \"%s\" is unknown, try \"ip fou help\".\n", *argv); + + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip fou help\".\n", *argv); exit(-1); } From 41b99db1c6a572aff547a9132c9bab6b315d086e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Mar 2018 13:07:20 -0800 Subject: [PATCH 12/12] fou: support JSON output Signed-off-by: Stephen Hemminger Signed-off-by: David Ahern --- ip/ipfou.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/ip/ipfou.c b/ip/ipfou.c index a5d1a4bb..0cb5e3c7 100644 --- a/ip/ipfou.c +++ b/ip/ipfou.c @@ -22,6 +22,7 @@ #include "libgenl.h" #include "utils.h" #include "ip_common.h" +#include "json_print.h" static void usage(void) { @@ -139,11 +140,9 @@ static int do_del(int argc, char **argv) static int print_fou_mapping(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE *)arg; struct genlmsghdr *ghdr; struct rtattr *tb[FOU_ATTR_MAX + 1]; int len = n->nlmsg_len; - unsigned int family; if (n->nlmsg_type != genl_family) return 0; @@ -155,18 +154,30 @@ static int print_fou_mapping(const struct sockaddr_nl *who, ghdr = NLMSG_DATA(n); parse_rtattr(tb, FOU_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); + open_json_object(NULL); if (tb[FOU_ATTR_PORT]) - fprintf(fp, "port %u", ntohs(rta_getattr_u16(tb[FOU_ATTR_PORT]))); - if (tb[FOU_ATTR_TYPE] && rta_getattr_u8(tb[FOU_ATTR_TYPE]) == FOU_ENCAP_GUE) - fprintf(fp, " gue"); + print_uint(PRINT_ANY, "port", "port %u", + ntohs(rta_getattr_u16(tb[FOU_ATTR_PORT]))); + + if (tb[FOU_ATTR_TYPE] && + rta_getattr_u8(tb[FOU_ATTR_TYPE]) == FOU_ENCAP_GUE) + print_null(PRINT_ANY, "gue", " gue", NULL); else if (tb[FOU_ATTR_IPPROTO]) - fprintf(fp, " ipproto %u", rta_getattr_u8(tb[FOU_ATTR_IPPROTO])); + print_uint(PRINT_ANY, "ipproto", + " ipproto %u", rta_getattr_u8(tb[FOU_ATTR_IPPROTO])); + if (tb[FOU_ATTR_AF]) { - family = rta_getattr_u8(tb[FOU_ATTR_AF]); + __u8 family = rta_getattr_u8(tb[FOU_ATTR_AF]); + + print_string(PRINT_JSON, "family", NULL, + family_name(family)); + if (family == AF_INET6) - fprintf(fp, " -6"); + print_string(PRINT_FP, NULL, + " -6", NULL); } - fprintf(fp, "\n"); + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); return 0; } @@ -186,10 +197,13 @@ static int do_show(int argc, char **argv) exit(1); } + new_json_obj(json); if (rtnl_dump_filter(&genl_rth, print_fou_mapping, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return 1; } + delete_json_obj(); + fflush(stdout); return 0; }