diff --git a/bridge/mdb.c b/bridge/mdb.c index e60ff3ef..748091b8 100644 --- a/bridge/mdb.c +++ b/bridge/mdb.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "libnetlink.h" #include "br_common.h" @@ -25,6 +26,9 @@ #endif static unsigned int filter_index, filter_vlan; +json_writer_t *jw_global; +static bool print_mdb_entries = true; +static bool print_mdb_router = true; static void usage(void) { @@ -49,13 +53,26 @@ static void __print_router_port_stats(FILE *f, struct rtattr *pattr) if (tb[MDBA_ROUTER_PATTR_TIMER]) { __jiffies_to_tv(&tv, rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER])); - fprintf(f, " %4i.%.2i", - (int)tv.tv_sec, (int)tv.tv_usec/10000); + if (jw_global) { + char formatted_time[9]; + + snprintf(formatted_time, sizeof(formatted_time), + "%4i.%.2i", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + jsonw_string_field(jw_global, "timer", formatted_time); + } else { + fprintf(f, " %4i.%.2i", + (int)tv.tv_sec, (int)tv.tv_usec/10000); + } } if (tb[MDBA_ROUTER_PATTR_TYPE]) { type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]); - fprintf(f, " %s", - is_temp_mcast_rtr(type) ? "temp" : "permanent"); + if (jw_global) + jsonw_string_field(jw_global, "type", + is_temp_mcast_rtr(type) ? "temp" : "permanent"); + else + fprintf(f, " %s", + is_temp_mcast_rtr(type) ? "temp" : "permanent"); } } @@ -65,24 +82,50 @@ static void br_print_router_ports(FILE *f, struct rtattr *attr, __u32 brifidx) struct rtattr *i; int rem; - if (!show_stats) - fprintf(f, "router ports on %s: ", ll_index_to_name(brifidx)); - rem = RTA_PAYLOAD(attr); - for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { - port_ifindex = RTA_DATA(i); - if (show_stats) { - fprintf(f, "router ports on %s: %s", - ll_index_to_name(brifidx), - ll_index_to_name(*port_ifindex)); - __print_router_port_stats(f, i); - fprintf(f, "\n"); - } else { - fprintf(f, "%s ", ll_index_to_name(*port_ifindex)); + if (jw_global) { + jsonw_name(jw_global, ll_index_to_name(brifidx)); + jsonw_start_array(jw_global); + for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + port_ifindex = RTA_DATA(i); + jsonw_start_object(jw_global); + jsonw_string_field(jw_global, + "port", + ll_index_to_name(*port_ifindex)); + if (show_stats) + __print_router_port_stats(f, i); + jsonw_end_object(jw_global); } + jsonw_end_array(jw_global); + } else { + if (!show_stats) + fprintf(f, "router ports on %s: ", + ll_index_to_name(brifidx)); + for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + port_ifindex = RTA_DATA(i); + if (show_stats) { + fprintf(f, "router ports on %s: %s", + ll_index_to_name(brifidx), + ll_index_to_name(*port_ifindex)); + __print_router_port_stats(f, i); + fprintf(f, "\n"); + } else{ + fprintf(f, "%s ", + ll_index_to_name(*port_ifindex)); + } + } + if (!show_stats) + fprintf(f, "\n"); } - if (!show_stats) - fprintf(f, "\n"); +} + +static void start_json_mdb_flags_array(bool *mdb_flags) +{ + if (*mdb_flags) + return; + jsonw_name(jw_global, "flags"); + jsonw_start_array(jw_global); + *mdb_flags = true; } static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e, @@ -91,28 +134,70 @@ static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e, SPRINT_BUF(abuf); const void *src; int af; + bool mdb_flags = false; if (filter_vlan && e->vid != filter_vlan) return; af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6; src = af == AF_INET ? (const void *)&e->addr.u.ip4 : (const void *)&e->addr.u.ip6; - if (n->nlmsg_type == RTM_DELMDB) - fprintf(f, "Deleted "); - fprintf(f, "dev %s port %s grp %s %s %s", ll_index_to_name(ifindex), - ll_index_to_name(e->ifindex), - inet_ntop(af, src, abuf, sizeof(abuf)), - (e->state & MDB_PERMANENT) ? "permanent" : "temp", - (e->flags & MDB_FLAGS_OFFLOAD) ? "offload" : ""); - if (e->vid) - fprintf(f, " vid %hu", e->vid); + if (jw_global) + jsonw_start_object(jw_global); + if (n->nlmsg_type == RTM_DELMDB) { + if (jw_global) + jsonw_string_field(jw_global, "opCode", "deleted"); + else + fprintf(f, "Deleted "); + } + if (jw_global) { + jsonw_string_field(jw_global, "dev", ll_index_to_name(ifindex)); + jsonw_string_field(jw_global, + "port", + ll_index_to_name(e->ifindex)); + jsonw_string_field(jw_global, "grp", inet_ntop(af, src, + abuf, sizeof(abuf))); + jsonw_string_field(jw_global, "state", + (e->state & MDB_PERMANENT) ? "permanent" : "temp"); + if (e->flags & MDB_FLAGS_OFFLOAD) { + start_json_mdb_flags_array(&mdb_flags); + jsonw_string(jw_global, "offload"); + } + if (mdb_flags) + jsonw_end_array(jw_global); + } else{ + fprintf(f, "dev %s port %s grp %s %s %s", + ll_index_to_name(ifindex), + ll_index_to_name(e->ifindex), + inet_ntop(af, src, abuf, sizeof(abuf)), + (e->state & MDB_PERMANENT) ? "permanent" : "temp", + (e->flags & MDB_FLAGS_OFFLOAD) ? "offload" : ""); + } + if (e->vid) { + if (jw_global) + jsonw_uint_field(jw_global, "vid", e->vid); + else + fprintf(f, " vid %hu", e->vid); + } if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) { struct timeval tv; __jiffies_to_tv(&tv, rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER])); - fprintf(f, "%4i.%.2i", (int)tv.tv_sec, (int)tv.tv_usec/10000); + if (jw_global) { + char formatted_time[9]; + + snprintf(formatted_time, sizeof(formatted_time), + "%4i.%.2i", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + jsonw_string_field(jw_global, "timer", formatted_time); + } else { + fprintf(f, "%4i.%.2i", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } } - fprintf(f, "\n"); + if (jw_global) + jsonw_end_object(jw_global); + else + fprintf(f, "\n"); } static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr, @@ -157,14 +242,14 @@ int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); - if (tb[MDBA_MDB]) { + if (tb[MDBA_MDB] && print_mdb_entries) { int rem = RTA_PAYLOAD(tb[MDBA_MDB]); for (i = RTA_DATA(tb[MDBA_MDB]); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) br_print_mdb_entry(fp, r->ifindex, i, n); } - if (tb[MDBA_ROUTER]) { + if (tb[MDBA_ROUTER] && print_mdb_router) { if (n->nlmsg_type == RTM_GETMDB) { if (show_details) br_print_router_ports(fp, tb[MDBA_ROUTER], @@ -174,15 +259,33 @@ int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) i = RTA_DATA(tb[MDBA_ROUTER]); port_ifindex = RTA_DATA(i); - if (n->nlmsg_type == RTM_DELMDB) - fprintf(fp, "Deleted "); - fprintf(fp, "router port dev %s master %s\n", - ll_index_to_name(*port_ifindex), - ll_index_to_name(r->ifindex)); + if (n->nlmsg_type == RTM_DELMDB) { + if (jw_global) + jsonw_string_field(jw_global, + "opCode", + "deleted"); + else + fprintf(fp, "Deleted "); + } + if (jw_global) { + jsonw_name(jw_global, + ll_index_to_name(r->ifindex)); + jsonw_start_array(jw_global); + jsonw_start_object(jw_global); + jsonw_string_field(jw_global, "port", + ll_index_to_name(*port_ifindex)); + jsonw_end_object(jw_global); + jsonw_end_array(jw_global); + } else { + fprintf(fp, "router port dev %s master %s\n", + ll_index_to_name(*port_ifindex), + ll_index_to_name(r->ifindex)); + } } } - fflush(fp); + if (!jw_global) + fflush(fp); return 0; } @@ -215,15 +318,54 @@ static int mdb_show(int argc, char **argv) } } + /* get mdb entries*/ if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) { perror("Cannot send dump request"); return -1; } + if (!json_output) { + /* Normal output */ + if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + return 0; + } + /* Json output */ + jw_global = jsonw_new(stdout); + jsonw_pretty(jw_global, 1); + jsonw_start_object(jw_global); + jsonw_name(jw_global, "mdb"); + jsonw_start_array(jw_global); + + /* print mdb entries */ + print_mdb_entries = true; + print_mdb_router = false; if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return -1; } + jsonw_end_array(jw_global); + + /* get router ports */ + if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) { + perror("Cannot send dump request"); + return -1; + } + jsonw_name(jw_global, "router"); + jsonw_start_object(jw_global); + + /* print router ports */ + print_mdb_entries = false; + print_mdb_router = true; + if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + jsonw_end_object(jw_global); + jsonw_end_object(jw_global); + jsonw_destroy(&jw_global); return 0; }