From: Mitesh Kanjariya Date: Fri, 21 Jul 2017 00:42:20 +0000 (-0700) Subject: bgpd: Json support for evpn commands X-Git-Tag: frr-4.0-dev~362^2~22 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=9c92b5f76819eb346d6143f81a812c4dade83e5c;p=mirror%2Ffrr.git bgpd: Json support for evpn commands Ticket:CM-16241 Reviewed By:CCR-6451 Testing: Manual Signed-off-by: Mitesh Kanjariya --- diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index d9735e7ffa..707728f9da 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -2187,6 +2187,61 @@ char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len) return buf; } +/* + * Function to convert evpn route to json format. + * NOTE: We don't use prefix2str as the output here is a bit different. + */ +void +bgp_evpn_route2json (struct prefix_evpn *p, json_object *json) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[PREFIX2STR_BUFFER]; + + if (!json) + return; + + if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) + { + json_object_int_add (json, "routeType", p->prefix.route_type); + json_object_int_add (json, "ethTag", 0); + json_object_int_add (json, "ipLen", IS_EVPN_PREFIX_IPADDR_V4 (p) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN); + json_object_string_add (json, "ip", inet_ntoa (p->prefix.ip.ipaddr_v4)); + } + else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + { + if (IS_EVPN_PREFIX_IPADDR_NONE(p)) + { + json_object_int_add (json, "routeType", p->prefix.route_type); + json_object_int_add (json, "esi", 0); /* TODO: we don't support esi yet */ + json_object_int_add (json, "ethTag", 0); + json_object_int_add (json, "macLen", 8 * ETHER_ADDR_LEN); + json_object_string_add (json, "mac", prefix_mac2str (&p->prefix.mac, buf1, sizeof (buf1))); + } + else + { + u_char family; + + family = IS_EVPN_PREFIX_IPADDR_V4(p) ? \ + AF_INET : AF_INET6; + + json_object_int_add (json, "routeType", p->prefix.route_type); + json_object_int_add (json, "esi", 0); /* TODO: we don't support esi yet */ + json_object_int_add (json, "ethTag", 0); + json_object_int_add (json, "macLen", 8 * ETHER_ADDR_LEN); + json_object_string_add (json, "mac", prefix_mac2str (&p->prefix.mac, buf1, sizeof (buf1))); + json_object_int_add (json, "ipLen", IS_EVPN_PREFIX_IPADDR_V4 (p) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN); + json_object_string_add (json, "ip", inet_ntop (family, &p->prefix.ip.ip.addr, buf2, PREFIX2STR_BUFFER)); + } + } + else + { + /* Currently, this is to cater to other AF_ETHERNET code. */ + } + + return; +} + + /* * Function to convert evpn route to string. * NOTE: We don't use prefix2str as the output here is a bit different. diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index ef63199395..01474b09d8 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -28,6 +28,8 @@ extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw); extern char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len); extern char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len); +extern void +bgp_evpn_route2json (struct prefix_evpn *p, json_object *json); extern void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, struct prefix_rd *prd, mpls_label_t *label, struct attr *attr, int addpath_encode, diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 9dc459cd4e..7102038f17 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -28,6 +28,8 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" +#define RT_ADDRSTRLEN 28 + /* EVPN prefix lengths. */ #define EVPN_TYPE_2_ROUTE_PREFIXLEN 224 #define EVPN_TYPE_3_ROUTE_PREFIXLEN 224 diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 948c7f50f2..2ba8f29260 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -38,6 +38,7 @@ #define SHOW_DISPLAY_STANDARD 0 #define SHOW_DISPLAY_TAGS 1 #define SHOW_DISPLAY_OVERLAY 2 +#define VNI_STR_LEN 32 /* * Context for VNI hash walk - used by callbacks. @@ -46,6 +47,7 @@ struct vni_walk_ctx { struct bgp *bgp; struct vty *vty; struct in_addr vtep_ip; + json_object *json; }; struct evpn_config_write { @@ -54,7 +56,8 @@ struct evpn_config_write { }; #if defined(HAVE_CUMULUS) -static void display_import_rt(struct vty *vty, struct irt_node *irt) +static void display_import_rt(struct vty *vty, struct irt_node *irt, + json_object *json) { u_char *pnt; u_char type, sub_type; @@ -68,7 +71,14 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt) } eip; struct listnode *node, *nnode; struct bgpevpn *tmp_vpn; + json_object *json_rt = NULL; + json_object *json_vnis = NULL; + char rt_buf[RT_ADDRSTRLEN]; + if (json) { + json_rt = json_object_new_object (); + json_vnis = json_object_new_array (); + } /* TODO: This needs to go into a function */ @@ -88,7 +98,13 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt) eas.val |= (*pnt++ << 8); eas.val |= (*pnt++); - vty_out(vty, "Route-target: %u:%u", eas.as, eas.val); + snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + break; case ECOMMUNITY_ENCODE_IP: @@ -97,7 +113,14 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt) eip.val = (*pnt++ << 8); eip.val |= (*pnt++); - vty_out(vty, "Route-target: %s:%u", inet_ntoa(eip.ip), eip.val); + snprintf(rt_buf, RT_ADDRSTRLEN, "%s:%u", inet_ntoa(eip.ip), + eip.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + break; case ECOMMUNITY_ENCODE_AS4: @@ -109,65 +132,103 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt) eas.val = (*pnt++ << 8); eas.val |= (*pnt++); - vty_out(vty, "Route-target: %u:%u", eas.as, eas.val); + snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + break; default: return; } - vty_out(vty, "\n"); - vty_out(vty, "List of VNIs importing routes with this route-target:\n"); + if (!json) { + vty_out(vty, + "\nList of VNIs importing routes with this route-target:\n"); + } + + for (ALL_LIST_ELEMENTS(irt->vnis, node, nnode, tmp_vpn)) { + if (json) + json_object_array_add( + json_vnis, json_object_new_int64(tmp_vpn->vni)); + else + vty_out(vty, " %u\n", tmp_vpn->vni); + } + + if (json) { + json_object_object_add(json_rt, "vnis", json_vnis); + json_object_object_add(json, rt_buf, json_rt); + } - for (ALL_LIST_ELEMENTS(irt->vnis, node, nnode, tmp_vpn)) - vty_out(vty, " %u\n", tmp_vpn->vni); } -static void show_import_rt_entry(struct hash_backet *backet, struct vty *vty) +static void show_import_rt_entry(struct hash_backet *backet, void *args[]) { + json_object *json = NULL; + struct vty *vty = NULL; struct irt_node *irt = (struct irt_node *)backet->data; + + vty = args[0]; + json = args[1]; + display_import_rt(vty, irt); + + return; } static void bgp_evpn_show_route_rd_header(struct vty *vty, - struct bgp_node *rd_rn) + struct bgp_node *rd_rn, + json_object *json) { u_int16_t type; struct rd_as rd_as; struct rd_ip rd_ip; u_char *pnt; + char rd_str[RD_ADDRSTRLEN]; pnt = rd_rn->p.u.val; /* Decode RD type. */ type = decode_rd_type(pnt); + if (json) + return; + vty_out(vty, "Route Distinguisher: "); switch (type) { case RD_TYPE_AS: decode_rd_as(pnt + 2, &rd_as); - vty_out(vty, "%u:%d", rd_as.as, rd_as.val); + snprintf(rd_str, RD_ADDRSTRLEN, "%u:%d", rd_as.as, rd_as.val); break; case RD_TYPE_IP: decode_rd_ip(pnt + 2, &rd_ip); - vty_out(vty, "%s:%d", inet_ntoa(rd_ip.ip), rd_ip.val); + snprintf(rd_str, RD_ADDRSTRLEN, "%s:%d", inet_ntoa(rd_ip.ip), + rd_ip.val); break; default: - vty_out(vty, "Unknown RD type"); + snprintf(rd_str, RD_ADDRSTRLEN, "Unknown RD type"); break; } - vty_out(vty, "\n"); + vty_out(vty, "%s\n", rd_str); } -static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp) +static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, + json_object *json) { char ri_header[] = " Network Next Hop Metric LocPrf Weight Path\n"; + if (json) + return; + + vty_out(vty, "BGP table version is 0, local router ID is %s\n", inet_ntoa(bgp->router_id)); vty_out(vty, @@ -180,43 +241,83 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp) vty_out(vty, "%s", ri_header); } -static void display_vni(struct vty *vty, struct bgpevpn *vpn) +static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) { char buf1[INET6_ADDRSTRLEN]; char *ecom_str; struct listnode *node, *nnode; struct ecommunity *ecom; + json_object *json_import_rtl; + json_object *json_export_rtl; + + if (json) { + json_import_rtl = json_object_new_array(); + json_export_rtl = json_object_new_array(); + json_object_int_add(json, "vni", vpn->vni); + json_object_string_add(json, "kernelFlag", + is_vni_live(vpn) ? "Yes" : "No"); + json_object_string_add( + json, "rd", + prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); + json_object_string_add(json, "originatorIp", + inet_ntoa(vpn->originator_ip)); + json_object_string_add(json, "advertiseGatewayMacip", + vpn->advertise_gw_macip ? "Yes" : "No"); + } else { + vty_out(vty, "VNI: %d", vpn->vni); + if (is_vni_live(vpn)) + vty_out(vty, " (known to the kernel)"); + vty_out(vty, "\n"); - vty_out(vty, "VNI: %d", vpn->vni); - if (is_vni_live(vpn)) - vty_out(vty, " (known to the kernel)"); - vty_out(vty, "\n"); + vty_out(vty, " RD: %s\n", + prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); + vty_out(vty, " Originator IP: %s\n", + inet_ntoa(vpn->originator_ip)); + vty_out(vty, " Advertise-gw-macip : %s\n", + vpn->advertise_gw_macip ? "Yes" : "No"); + } - vty_out(vty, " RD: %s\n", - prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); - vty_out(vty, " Originator IP: %s\n", inet_ntoa(vpn->originator_ip)); - vty_out(vty, " Advertise-gw-macip : %s\n", - vpn->advertise_gw_macip ? "Yes" : "No"); + if (!json) + vty_out(vty, " Import Route Target:\n"); - vty_out(vty, " Import Route Target:\n"); for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " %s\n", ecom_str); + + if (json) + json_object_array_add(json_import_rtl, + json_object_new_string(ecom_str)); + else + vty_out(vty, " %s\n", ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } - vty_out(vty, " Export Route Target:\n"); + if (json) + json_object_object_add(json, "importRts", json_import_rtl); + else + vty_out(vty, " Export Route Target:\n"); + for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " %s\n", ecom_str); + + if (json) + json_object_array_add(json_export_rtl, + json_object_new_string(ecom_str)); + else + vty_out(vty, " %s\n", ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } + + if (json) + json_object_object_add(json, "exportRts", json_export_rtl); } static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, - struct vty *vty, struct in_addr vtep_ip) + struct vty *vty, struct in_addr vtep_ip, + json_object *json) { struct bgp_node *rn; struct bgp_info *ri; @@ -228,41 +329,79 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, for (rn = bgp_table_top(vpn->route_table); rn; rn = bgp_route_next(rn)) { struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + int add_prefix_to_json = 0; + char prefix_str[BUFSIZ]; + json_object *json_paths = NULL; + json_object *json_prefix = NULL; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; + if (json) + json_prefix = json_object_new_object(); + if (rn->info) { /* Overall header/legend displayed once. */ if (header) { - bgp_evpn_show_route_header(vty, bgp); + bgp_evpn_show_route_header(vty, bgp, json); header = 0; } prefix_cnt++; } + if (json) + json_paths = json_object_new_array(); + /* For EVPN, the prefix is displayed for each path (to fit in * with code that already exists). */ for (ri = rn->info; ri; ri = ri->next) { + json_object *json_path = NULL; + if (vtep_ip.s_addr && !IPV4_ADDR_SAME(&(vtep_ip), &(ri->attr->nexthop))) continue; + if (json) + json_path = json_object_new_array(); + + route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; - route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL); + add_prefix_to_json = 1; + } + + if (json && add_prefix_to_json) { + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + rn->p.prefixlen); + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json, prefix_str, json_prefix); } } - if (prefix_cnt == 0) - vty_out(vty, "No EVPN prefixes %sexist for this VNI\n", - type ? "(of requested type) " : ""); - else - vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n", - prefix_cnt, path_cnt, - type ? " (of requested type)" : ""); + if (json) { + json_object_int_add(json, "numPrefix", prefix_cnt); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (prefix_cnt == 0) + vty_out(vty, "No EVPN prefixes %sexist for this VNI", + type ? "(of requested type) " : ""); + else + vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); + } } static void show_vni_routes_hash(struct hash_backet *backet, void *arg) @@ -270,13 +409,31 @@ static void show_vni_routes_hash(struct hash_backet *backet, void *arg) struct bgpevpn *vpn = (struct bgpevpn *)backet->data; struct vni_walk_ctx *wctx = arg; struct vty *vty = wctx->vty; + json_object *json = wctx->json; + json_object *json_vni = NULL; + char vni_str[VNI_STR_LEN]; + + snprintf(vni_str, VNI_STR_LEN, "%d", vpn->vni); + if (json) { + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "vni", vpn->vni); + } else { + vty_out(vty, "\nVNI: %d\n\n", vpn->vni); + } + + show_vni_routes(wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip, json_vni); - vty_out(vty, "\nVNI: %d\n\n", vpn->vni); - show_vni_routes(wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip); + if (json) + json_object_object_add(json, vni_str, json_vni); } -static void show_vni_entry(struct hash_backet *backet, struct vty *vty) +static void show_vni_entry(struct hash_backet *backet, void *args[]) { + struct vty *vty; + json_object *json; + json_object *json_vni; + json_object *json_import_rtl; + json_object *json_export_rtl; struct bgpevpn *vpn = (struct bgpevpn *)backet->data; char buf1[10]; char buf2[INET6_ADDRSTRLEN]; @@ -285,42 +442,91 @@ static void show_vni_entry(struct hash_backet *backet, struct vty *vty) struct listnode *node, *nnode; struct ecommunity *ecom; + vty = args[0]; + json = args[1]; + + if (json) { + json_vni = json_object_new_object(); + json_import_rtl = json_object_new_array(); + json_export_rtl = json_object_new_array(); + } + buf1[0] = '\0'; if (is_vni_live(vpn)) sprintf(buf1, "*"); - vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni, - inet_ntoa(vpn->originator_ip), - prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); + if (json) { + json_object_int_add(json_vni, "vni", vpn->vni); + json_object_string_add(json_vni, "inKernel", + is_vni_live(vpn) ? "True" : "False"); + json_object_string_add(json_vni, "originatorIp", + inet_ntoa(vpn->originator_ip)); + json_object_string_add( + json_vni, "rd", + prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); + } else { + vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni, + inet_ntoa(vpn->originator_ip), + prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); + } for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - if (listcount(vpn->import_rtl) > 1) - sprintf(rt_buf, "%s, ...", ecom_str); - else - sprintf(rt_buf, "%s", ecom_str); - vty_out(vty, " %-25s", rt_buf); + if (json) { + json_object_array_add(json_import_rtl, + json_object_new_string(ecom_str)); + } else { + if (listcount(vpn->import_rtl) > 1) + sprintf(rt_buf, "%s, ...", ecom_str); + else + sprintf(rt_buf, "%s", ecom_str); + vty_out(vty, " %-25s", rt_buf); + } XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); - break; + + /* If there are multiple import RTs we break here and show only + * one */ + if (!json) + break; } + if (json) + json_object_object_add(json_vni, "importRTs", json_import_rtl); + for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - if (listcount(vpn->export_rtl) > 1) - sprintf(rt_buf, "%s, ...", ecom_str); - else - sprintf(rt_buf, "%s", ecom_str); - vty_out(vty, " %-25s", rt_buf); + if (json) { + json_object_array_add(json_export_rtl, + json_object_new_string(ecom_str)); + } else { + if (listcount(vpn->export_rtl) > 1) + sprintf(rt_buf, "%s, ...", ecom_str); + else + sprintf(rt_buf, "%s", ecom_str); + vty_out(vty, " %-25s", rt_buf); + } XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); - break; + + /* If there are multiple export RTs we break here and show only + * one */ + if (!json) + break; + } + + if (json) { + char vni_str[VNI_STR_LEN]; + json_object_object_add(json_vni, "exportRTs", json_export_rtl); + snprintf(vni_str, VNI_STR_LEN, "%u", vpn->vni); + json_object_object_add(json, vni_str, json_vni); + } else { + vty_out(vty, "\n"); } - vty_out(vty, "\n"); } #endif /* HAVE_CUMULUS */ @@ -1281,19 +1487,26 @@ static int evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn) /* * Display import RT mapping to VNIs (vty handler) */ -static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp) +static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp, + json_object *json) { + void *args[2]; + + args[0] = vty; + args[1] = json; + hash_iterate( bgp->import_rt_hash, (void (*)(struct hash_backet *, void *))show_import_rt_entry, - vty); + args); } /* * Display EVPN routes for all VNIs - vty handler. */ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, - struct in_addr vtep_ip) + struct in_addr vtep_ip, + json_object *json) { u_int32_t num_vnis; struct vni_walk_ctx wctx; @@ -1305,6 +1518,7 @@ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, wctx.bgp = bgp; wctx.vty = vty; wctx.vtep_ip = vtep_ip; + wctx.json = json; hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))show_vni_routes_hash, &wctx); @@ -1314,7 +1528,8 @@ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, * Display EVPN routes for a VNI -- for specific type-3 route (vty handler). */ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, - vni_t vni, struct in_addr orig_ip) + vni_t vni, struct in_addr orig_ip, + json_object *json) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -1323,6 +1538,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, u_int32_t path_cnt = 0; afi_t afi; safi_t safi; + json_object *json_paths = NULL; afi = AFI_L2VPN; safi = SAFI_EVPN; @@ -1338,20 +1554,42 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, build_evpn_type3_prefix(&p, orig_ip); rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); if (!rn || !rn->info) { - vty_out(vty, "%% Network not in table\n"); + if (!json) + vty_out(vty, "%% Network not in table\n"); return; } + if (json) + json_paths = json_object_new_array(); + /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, NULL); + route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, json); /* Display each path for this prefix. */ for (ri = rn->info; ri; ri = ri->next) { - route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, NULL); + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + + route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; } - vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); + if (json) { + if (path_cnt) + json_object_object_add(json, "paths", json_paths); + + json_object_int_add(json, "numPaths", path_cnt); + } else { + vty_out(vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); + } } /* @@ -1360,7 +1598,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, */ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, vni_t vni, struct ethaddr *mac, - struct ipaddr *ip) + struct ipaddr *ip, json_object *json) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -1369,6 +1607,7 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, u_int32_t path_cnt = 0; afi_t afi; safi_t safi; + json_object *json_paths = NULL; afi = AFI_L2VPN; safi = SAFI_EVPN; @@ -1376,7 +1615,8 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, /* Locate VNI. */ vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - vty_out(vty, "VNI not found\n"); + if (!json) + vty_out(vty, "VNI not found\n"); return; } @@ -1384,20 +1624,42 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, build_evpn_type2_prefix(&p, mac, ip); rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); if (!rn || !rn->info) { - vty_out(vty, "%% Network not in table\n"); + if (!json) + vty_out(vty, "%% Network not in table\n"); return; } + if (json) + json_paths = json_object_new_array(); + /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, NULL); + route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, json); /* Display each path for this prefix. */ for (ri = rn->info; ri; ri = ri->next) { - route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, NULL); + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + + route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; } - vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); + if (json) { + if (path_cnt) + json_object_object_add(json, "paths", json_paths); + + json_object_int_add(json, "numPaths", path_cnt); + } else { + vty_out(vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); + } } /* @@ -1406,19 +1668,21 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, * If the vtep_ip is non zero, only routes behind that vtep are shown */ static void evpn_show_routes_vni(struct vty *vty, struct bgp *bgp, vni_t vni, - int type, struct in_addr vtep_ip) + int type, struct in_addr vtep_ip, + json_object *json) { struct bgpevpn *vpn; /* Locate VNI. */ vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - vty_out(vty, "VNI not found\n"); + if (!json) + vty_out(vty, "VNI not found\n"); return; } /* Walk this VNI's route table and display appropriate routes. */ - show_vni_routes(bgp, vpn, type, vty, vtep_ip); + show_vni_routes(bgp, vpn, type, vty, vtep_ip, json); } /* @@ -1428,7 +1692,7 @@ static void evpn_show_routes_vni(struct vty *vty, struct bgp *bgp, vni_t vni, */ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, struct prefix_rd *prd, struct ethaddr *mac, - struct ipaddr *ip) + struct ipaddr *ip, json_object *json) { struct prefix_evpn p; struct bgp_node *rn; @@ -1436,6 +1700,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, afi_t afi; safi_t safi; u_int32_t path_cnt = 0; + json_object *json_paths = NULL; + char prefix_str[BUFSIZ]; afi = AFI_L2VPN; safi = SAFI_EVPN; @@ -1445,20 +1711,44 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, (struct prefix *)&p, prd); if (!rn || !rn->info) { - vty_out(vty, "%% Network not in table\n"); + if (!json) + vty_out(vty, "%% Network not in table\n"); return; } + bgp_evpn_route2str((struct prefix_evpn *)&p, prefix_str, + sizeof(prefix_str)); + /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, prd, afi, safi, NULL); + route_vty_out_detail_header(vty, bgp, rn, prd, afi, safi, json); + + if (json) + json_paths = json_object_new_array(); /* Display each path for this prefix. */ for (ri = rn->info; ri; ri = ri->next) { - route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, NULL); + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + + route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; } - vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); + if (json && path_cnt) { + if (path_cnt) + json_object_object_add(json, prefix_str, json_paths); + json_object_int_add(json, "numPaths", path_cnt); + } else { + vty_out(vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); + } } /* @@ -1466,7 +1756,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, * If 'type' is non-zero, only routes matching that type are shown. */ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, - struct prefix_rd *prd, int type) + struct prefix_rd *prd, int type, + json_object *json) { struct bgp_node *rd_rn; struct bgp_table *table; @@ -1476,28 +1767,49 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, afi_t afi; safi_t safi; u_int32_t prefix_cnt, path_cnt; + char rd_str[RD_ADDRSTRLEN]; + json_object *json_rd = NULL; + int add_rd_to_json = 0; afi = AFI_L2VPN; safi = SAFI_EVPN; prefix_cnt = path_cnt = 0; + prefix_rd2str((struct prefix_rd *)prd, rd_str, sizeof(rd_str)); + rd_rn = bgp_node_lookup(bgp->rib[afi][safi], (struct prefix *)prd); if (!rd_rn) return; + table = (struct bgp_table *)rd_rn->info; if (table == NULL) return; + if (json) { + json_rd = json_object_new_object(); + json_object_string_add(json_rd, "rd", rd_str); + } + /* Display all prefixes with this RD. */ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + json_object *json_prefix = NULL; + json_object *json_paths = NULL; + char prefix_str[BUFSIZ]; + int add_prefix_to_json = 0; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; + if (json) + json_prefix = json_object_new_object(); + if (rn->info) { /* RD header and legend - once overall. */ - if (rd_header) { + if (rd_header && !json) { vty_out(vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:" "[MAC]\n"); @@ -1509,34 +1821,64 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* Prefix and num paths displayed once per prefix. */ route_vty_out_detail_header(vty, bgp, rn, prd, afi, - safi, NULL); + safi, json_prefix); prefix_cnt++; } + if (json) + json_paths = json_object_new_array(); + /* Display each path for this prefix. */ for (ri = rn->info; ri; ri = ri->next) { + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, - NULL); + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; + add_prefix_to_json = 1; + add_rd_to_json = 1; + } + + if (json && add_prefix_to_json) { + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json_rd, prefix_str, + json_prefix); } } - if (prefix_cnt == 0) - vty_out(vty, "No prefixes exist with this RD%s\n", - type ? " (of requested type)" : ""); - else - vty_out(vty, - "\nDisplayed %u prefixes (%u paths) with this RD%s\n", - prefix_cnt, path_cnt, - type ? " (of requested type)" : ""); + if (json && add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); + + if (json) { + json_object_int_add(json, "numPrefix", prefix_cnt); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (prefix_cnt == 0) + vty_out(vty, "No prefixes exist with this RD%s\n", + type ? " (of requested type)" : ""); + else + vty_out(vty, + "\nDisplayed %u prefixes (%u paths) with this RD%s\n", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); + } } /* * Display BGP EVPN routing table - all routes (vty handler). * If 'type' is non-zero, only routes matching that type are shown. */ -static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type) +static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, + json_object *json) { struct bgp_node *rd_rn; struct bgp_table *table; @@ -1557,15 +1899,36 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type) */ for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; rd_rn = bgp_route_next(rd_rn)) { + char rd_str[RD_ADDRSTRLEN]; + json_object *json_rd = NULL; /* contains routes for an RD */ + int add_rd_to_json = 0; + table = (struct bgp_table *)rd_rn->info; if (table == NULL) continue; + prefix_rd2str((struct prefix_rd *)&rd_rn->p, rd_str, + sizeof(rd_str)); + + if (json) { + json_rd = json_object_new_object(); + json_object_string_add(json_rd, "rd", rd_str); + } + rd_header = 1; /* Display all prefixes for an RD */ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + json_object *json_prefix = + NULL; /* contains prefix under a RD */ + json_object *json_paths = + NULL; /* array of paths under a prefix*/ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + char prefix_str[BUFSIZ]; + int add_prefix_to_json = 0; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, + prefix_str, sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; @@ -1573,74 +1936,126 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type) if (rn->info) { /* Overall header/legend displayed once. */ if (header) { - bgp_evpn_show_route_header(vty, bgp); + bgp_evpn_show_route_header(vty, bgp, + json); header = 0; } /* RD header - per RD. */ if (rd_header) { - bgp_evpn_show_route_rd_header(vty, - rd_rn); + bgp_evpn_show_route_rd_header( + vty, rd_rn, json); rd_header = 0; } prefix_cnt++; } + if (json) { + json_prefix = json_object_new_object(); + json_paths = json_object_new_array(); + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + rn->p.prefixlen); + } + /* For EVPN, the prefix is displayed for each path (to * fit in * with code that already exists). */ for (ri = rn->info; ri; ri = ri->next) { + json_object *json_path = NULL; path_cnt++; + add_prefix_to_json = 1; + add_rd_to_json = 1; + + if (json) + json_path = json_object_new_array(); + route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, - NULL); + json_path); + + if (json) + json_object_array_add(json_paths, + json_path); + } + + if (json && add_prefix_to_json) { + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json_rd, prefix_str, + json_prefix); } } + + if (json && add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); } - if (prefix_cnt == 0) - vty_out(vty, "No EVPN prefixes %sexist\n", - type ? "(of requested type) " : ""); - else - vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n", - prefix_cnt, path_cnt, - type ? " (of requested type)" : ""); + if (json) { + json_object_int_add(json, "numPrefix", prefix_cnt); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (prefix_cnt == 0) { + vty_out(vty, "No EVPN prefixes %sexist\n", + type ? "(of requested type) " : ""); + } else { + vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); + } + } } /* * Display specified VNI (vty handler) */ -static void evpn_show_vni(struct vty *vty, struct bgp *bgp, vni_t vni) +static void evpn_show_vni(struct vty *vty, struct bgp *bgp, vni_t vni, + json_object *json) { struct bgpevpn *vpn; vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - vty_out(vty, "VNI not found\n"); - return; + if (json) { + vty_out(vty, "{}\n"); + } else { + vty_out(vty, "VNI not found\n"); + return; + } } - display_vni(vty, vpn); + display_vni(vty, vpn, json); } /* * Display a VNI (upon user query). */ -static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp) +static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp, + json_object *json) { u_int32_t num_vnis; + void *args[2]; num_vnis = hashcount(bgp->vnihash); if (!num_vnis) return; - vty_out(vty, "Number of VNIs: %u\n", num_vnis); - vty_out(vty, "Flags: * - Kernel \n"); - vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n", "VNI", "Orig IP", - "RD", "Import RT", "Export RT"); + + if (json) { + json_object_int_add(json, "numVnis", num_vnis); + } else { + vty_out(vty, "Number of VNIs: %u\n", num_vnis); + vty_out(vty, "Flags: * - Kernel\n"); + vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n", "VNI", + "Orig IP", "RD", "Import RT", "Export RT"); + } + + args[0] = vty; + args[1] = json; hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))show_vni_entry, - vty); + args); } /* @@ -1863,19 +2278,24 @@ DEFUN (no_bgp_evpn_advertise_all_vni, /* * Display VNI information - for all or a specific VNI */ -DEFUN (show_bgp_l2vpn_evpn_vni, - show_bgp_l2vpn_evpn_vni_cmd, - "show bgp l2vpn evpn vni [(1-16777215)]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "Show VNI\n" - "VNI number\n") +DEFUN(show_bgp_l2vpn_evpn_vni, + show_bgp_l2vpn_evpn_vni_cmd, + "show bgp l2vpn evpn vni [(1-16777215)] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show VNI\n" + "VNI number\n" + JSON_STR) { struct bgp *bgp; vni_t vni; int idx = 0; + u_char uj = 0; + json_object *json = NULL; + + uj = use_json(argc, argv); bgp = bgp_get_default(); if (!bgp) @@ -1884,18 +2304,46 @@ DEFUN (show_bgp_l2vpn_evpn_vni, if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; - if (argc == ((idx + 1) + 1)) { - vty_out(vty, "Advertise gateway macip flag: %s\n", - bgp->advertise_gw_macip ? "Enabled" : "Disabled"); + if (uj) + json = json_object_new_object(); - /* Display all VNIs */ - vty_out(vty, "Advertise All VNI flag: %s\n", - bgp->advertise_all_vni ? "Enabled" : "Disabled"); - evpn_show_all_vnis(vty, bgp); + if ((uj && argc == ((idx + 1) + 2)) || (!uj && argc == (idx + 1) + 1)) { + if (uj) { + json_object_string_add(json, "advertiseGatewayMacip", + bgp->advertise_gw_macip + ? "Enabled" + : "Disabled"); + json_object_string_add(json, "advertiseAllVnis", + bgp->advertise_all_vni + ? "Enabled" + : "Disabled"); + } else { + vty_out(vty, "Advertise Gateway Macip: %s\n", + bgp->advertise_gw_macip ? "Enabled" + : "Disabled"); + + /* Display all VNIs */ + vty_out(vty, "Advertise All VNI flag: %s\n", + bgp->advertise_all_vni ? "Enabled" + : "Disabled"); + } + + evpn_show_all_vnis(vty, bgp, json); } else { + int vni_idx = 0; + + if (!argv_find(argv, argc, "vni", &vni_idx)) + return CMD_WARNING; + /* Display specific VNI */ - vni = strtoul(argv[argc - 1]->arg, NULL, 10); - evpn_show_vni(vty, bgp, vni); + vni = strtoul(argv[vni_idx + 1]->arg, NULL, 10); + evpn_show_vni(vty, bgp, vni, json); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); } return CMD_SUCCESS; @@ -1904,15 +2352,15 @@ DEFUN (show_bgp_l2vpn_evpn_vni, /* * Display EVPN neighbor summary. */ -DEFUN (show_bgp_l2vpn_evpn_summary, - show_bgp_l2vpn_evpn_summary_cmd, - "show bgp l2vpn evpn summary [json]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "Summary of BGP neighbor status\n" - JSON_STR) +DEFUN(show_bgp_l2vpn_evpn_summary, + show_bgp_l2vpn_evpn_summary_cmd, + "show bgp l2vpn evpn summary [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Summary of BGP neighbor status\n" + JSON_STR) { u_char uj = use_json(argc, argv); return bgp_show_summary_vty(vty, NULL, AFI_L2VPN, SAFI_EVPN, uj); @@ -1921,176 +2369,236 @@ DEFUN (show_bgp_l2vpn_evpn_summary, /* * Display global EVPN routing table. */ -DEFUN (show_bgp_l2vpn_evpn_route, - show_bgp_l2vpn_evpn_route_cmd, - "show bgp l2vpn evpn route [type ]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "Specify Route type\n" - "MAC-IP (Type-2) route\n" - "Multicast (Type-3) route\n") +DEFUN(show_bgp_l2vpn_evpn_route, show_bgp_l2vpn_evpn_route_cmd, + "show bgp l2vpn evpn route [type ] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n" + JSON_STR) { struct bgp *bgp; - int idx = 0; + int type_idx; int type = 0; + u_char uj = 0; + json_object *json = NULL; + + uj = use_json(argc, argv); bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - if (!argv_find(argv, argc, "evpn", &idx)) - return CMD_WARNING; + if (uj) + json = json_object_new_object(); - if (argc == ((idx + 1) + 3)) { + /* get the type */ + if (argv_find(argv, argc, "type", &type_idx)) { /* Specific type is requested */ - if (strncmp(argv[argc - 1]->arg, "ma", 2) == 0) + if (strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; - else if (strncmp(argv[argc - 1]->arg, "mu", 2) == 0) + else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; else return CMD_WARNING; } - evpn_show_all_routes(vty, bgp, type); + evpn_show_all_routes(vty, bgp, type, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display global EVPN routing table for specific RD. */ -DEFUN (show_bgp_l2vpn_evpn_route_rd, - show_bgp_l2vpn_evpn_route_rd_cmd, - "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn [type ]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "Route Distinguisher\n" - "ASN:XX or A.B.C.D:XX\n" - "Specify Route type\n" - "MAC-IP (Type-2) route\n" - "Multicast (Type-3) route\n") +DEFUN(show_bgp_l2vpn_evpn_route_rd, show_bgp_l2vpn_evpn_route_rd_cmd, + "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn [type ] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n" + JSON_STR) { struct bgp *bgp; int ret; struct prefix_rd prd; - int idx = 0; int type = 0; + int rd_idx = 0; + int type_idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - if (!argv_find(argv, argc, "evpn", &idx)) - return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); - ret = str2prefix_rd(argv[idx + 3]->arg, &prd); - if (!ret) { - vty_out(vty, "%% Malformed Route Distinguisher\n"); - return CMD_WARNING; + /* get the RD */ + if (argv_find(argv, argc, "rd", &rd_idx)) { + ret = str2prefix_rd(argv[rd_idx + 1]->arg, &prd); + + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } } - if (argc == ((idx + 1) + 5)) { + /* get the type */ + if (argv_find(argv, argc, "type", &type_idx)) { /* Specific type is requested */ - if (strncmp(argv[argc - 1]->arg, "ma", 2) == 0) + if (strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; - else if (strncmp(argv[argc - 1]->arg, "mu", 2) == 0) + else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; else return CMD_WARNING; } - evpn_show_route_rd(vty, bgp, &prd, type); + evpn_show_route_rd(vty, bgp, &prd, type, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display global EVPN routing table for specific RD and MACIP. */ -DEFUN (show_bgp_l2vpn_evpn_route_rd_macip, - show_bgp_l2vpn_evpn_route_rd_macip_cmd, - "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "Route Distinguisher\n" - "ASN:XX or A.B.C.D:XX\n" - "MAC\n" - "MAC address (e.g., 00:e0:ec:20:12:62)\n" - "IP\n" - "IP address (IPv4 or IPv6)\n") -{ - struct bgp *bgp; - int ret; - struct prefix_rd prd; - struct ethaddr mac; - struct ipaddr ip; - int idx = 0; +DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, + show_bgp_l2vpn_evpn_route_rd_macip_cmd, + "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n" + "MAC\n" + "MAC address (e.g., 00:e0:ec:20:12:62)\n" + "IP\n" + "IP address (IPv4 or IPv6)\n" + JSON_STR) +{ + struct bgp *bgp; + int ret; + struct prefix_rd prd; + struct ethaddr mac; + struct ipaddr ip; + int rd_idx = 0; + int mac_idx = 0; + int ip_idx = 0; + int uj = 0; + json_object *json = NULL; + + memset(&mac, 0, sizeof(struct ethaddr)); + memset(&ip, 0, sizeof(struct ipaddr)); bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - if (!argv_find(argv, argc, "evpn", &idx)) - return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); - ret = str2prefix_rd(argv[idx + 3]->arg, &prd); - if (!ret) { - vty_out(vty, "%% Malformed Route Distinguisher\n"); - return CMD_WARNING; + /* get the prd */ + if (argv_find(argv, argc, "rd", &rd_idx)) { + ret = str2prefix_rd(argv[rd_idx + 1]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } } - if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) { - vty_out(vty, "%% Malformed MAC address\n"); - return CMD_WARNING; + + /* get the mac */ + if (argv_find(argv, argc, "mac", &mac_idx)) { + if (!prefix_str2mac(argv[mac_idx + 1]->arg, &mac)) { + vty_out(vty, "%% Malformed MAC address\n"); + return CMD_WARNING; + } } - memset(&ip, 0, sizeof(ip)); - if (argc == (idx + 1 + 7) && argv[argc - 1]->arg != NULL) { - /* Specific MAC+IP requested */ - if (str2ipaddr(argv[argc - 1]->arg, &ip) != 0) { + + /* get the ip if specified */ + if (argv_find(argv, argc, "ip", &ip_idx)) { + if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) { vty_out(vty, "%% Malformed IP address\n"); return CMD_WARNING; } } - evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip); + evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display per-VNI EVPN routing table. */ -DEFUN (show_bgp_l2vpn_evpn_route_vni, - show_bgp_l2vpn_evpn_route_vni_cmd, - "show bgp l2vpn evpn route vni (1-16777215) [ | vtep A.B.C.D>]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VXLAN Network Identifier\n" - "VNI number\n" - "Specify Route type\n" - "MAC-IP (Type-2) route\n" - "Multicast (Type-3) route\n" - "Remote VTEP\n" - "Remote VTEP IP address\n") +DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd, + "show bgp l2vpn evpn route vni (1-16777215) [ | vtep A.B.C.D>] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n" + "Remote VTEP\n" + "Remote VTEP IP address\n" + JSON_STR) { vni_t vni; struct bgp *bgp; struct in_addr vtep_ip; int type = 0; int idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; @@ -2098,7 +2606,8 @@ DEFUN (show_bgp_l2vpn_evpn_route_vni, vni = strtoul(argv[idx + 3]->arg, NULL, 10); - if (argc == (idx + 1 + 5) && argv[idx + 4]->arg) { + if ((!uj && ((argc == (idx + 1 + 5)) && argv[idx + 4]->arg)) + || (uj && ((argc == (idx + 1 + 6)) && argv[idx + 4]->arg))) { if (strncmp(argv[idx + 4]->arg, "type", 4) == 0) { if (strncmp(argv[idx + 5]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; @@ -2115,156 +2624,231 @@ DEFUN (show_bgp_l2vpn_evpn_route_vni, return CMD_WARNING; } - evpn_show_routes_vni(vty, bgp, vni, type, vtep_ip); + evpn_show_routes_vni(vty, bgp, vni, type, vtep_ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display per-VNI EVPN routing table for specific MACIP. */ -DEFUN (show_bgp_l2vpn_evpn_route_vni_macip, - show_bgp_l2vpn_evpn_route_vni_macip_cmd, - "show bgp l2vpn evpn route vni (1-16777215) mac WORD [ip WORD]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VXLAN Network Identifier\n" - "VNI number\n" - "MAC\n" - "MAC address (e.g., 00:e0:ec:20:12:62)\n" - "IP\n" - "IP address (IPv4 or IPv6)\n") +DEFUN(show_bgp_l2vpn_evpn_route_vni_macip, + show_bgp_l2vpn_evpn_route_vni_macip_cmd, + "show bgp l2vpn evpn route vni (1-16777215) mac WORD [ip WORD] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "MAC\n" + "MAC address (e.g., 00:e0:ec:20:12:62)\n" + "IP\n" + "IP address (IPv4 or IPv6)\n" + JSON_STR) { vni_t vni; struct bgp *bgp; struct ethaddr mac; struct ipaddr ip; int idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; + /* get the VNI */ vni = strtoul(argv[idx + 3]->arg, NULL, 10); + + /* get the mac */ if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) { vty_out(vty, "%% Malformed MAC address\n"); return CMD_WARNING; } + + /* get the ip */ memset(&ip, 0, sizeof(ip)); - if (argc == (idx + 1 + 7) && argv[idx + 7]->arg != NULL) { + if ((!uj && ((argc == (idx + 1 + 7)) && argv[idx + 7]->arg != NULL)) + || (uj + && ((argc == (idx + 1 + 8)) && argv[idx + 7]->arg != NULL))) { if (str2ipaddr(argv[idx + 7]->arg, &ip) != 0) { vty_out(vty, "%% Malformed IP address\n"); return CMD_WARNING; } } - evpn_show_route_vni_macip(vty, bgp, vni, &mac, &ip); + evpn_show_route_vni_macip(vty, bgp, vni, &mac, &ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display per-VNI EVPN routing table for specific multicast IP (remote VTEP). */ -DEFUN (show_bgp_l2vpn_evpn_route_vni_multicast, - show_bgp_l2vpn_evpn_route_vni_multicast_cmd, - "show bgp l2vpn evpn route vni (1-16777215) multicast A.B.C.D", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VXLAN Network Identifier\n" - "VNI number\n" - "Multicast (Type-3) route\n" - "Originating Router IP address\n") +DEFUN(show_bgp_l2vpn_evpn_route_vni_multicast, + show_bgp_l2vpn_evpn_route_vni_multicast_cmd, + "show bgp l2vpn evpn route vni (1-16777215) multicast A.B.C.D [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "Multicast (Type-3) route\n" + "Originating Router IP address\n" + JSON_STR) { vni_t vni; struct bgp *bgp; int ret; struct in_addr orig_ip; int idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; + /* get the VNI */ vni = strtoul(argv[idx + 3]->arg, NULL, 10); + + /* get the ip */ ret = inet_aton(argv[idx + 5]->arg, &orig_ip); if (!ret) { vty_out(vty, "%% Malformed Originating Router IP address\n"); return CMD_WARNING; } - evpn_show_route_vni_multicast(vty, bgp, vni, orig_ip); + evpn_show_route_vni_multicast(vty, bgp, vni, orig_ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display per-VNI EVPN routing table - for all VNIs. */ -DEFUN (show_bgp_l2vpn_evpn_route_vni_all, - show_bgp_l2vpn_evpn_route_vni_all_cmd, - "show bgp l2vpn evpn route vni all [vtep A.B.C.D]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VXLAN Network Identifier\n" - "All VNIs\n" - "Remote VTEP\n" - "Remote VTEP IP address\n") +DEFUN(show_bgp_l2vpn_evpn_route_vni_all, show_bgp_l2vpn_evpn_route_vni_all_cmd, + "show bgp l2vpn evpn route vni all [vtep A.B.C.D] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "All VNIs\n" + "Remote VTEP\n" + "Remote VTEP IP address\n" + JSON_STR) { struct bgp *bgp; struct in_addr vtep_ip; int idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; vtep_ip.s_addr = 0; - if (argc == (idx + 1 + 5) && argv[idx + 5]->arg) { + if ((!uj && (argc == (idx + 1 + 5) && argv[idx + 5]->arg)) + || (uj && (argc == (idx + 1 + 6) && argv[idx + 5]->arg))) { if (!inet_aton(argv[idx + 5]->arg, &vtep_ip)) { vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } } - evpn_show_routes_vni_all(vty, bgp, vtep_ip); + evpn_show_routes_vni_all(vty, bgp, vtep_ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display EVPN import route-target hash table */ -DEFUN (show_bgp_l2vpn_evpn_import_rt, - show_bgp_l2vpn_evpn_import_rt_cmd, - "show bgp l2vpn evpn import-rt", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "Show import route target\n") +DEFUN(show_bgp_l2vpn_evpn_import_rt, show_bgp_l2vpn_evpn_import_rt_cmd, + "show bgp l2vpn evpn import-rt [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show import route target\n" + JSON_STR) { struct bgp *bgp; + u_char uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - evpn_show_import_rts(vty, bgp); + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + + evpn_show_import_rts(vty, bgp, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index fc22642833..40d6fd7bfd 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -6227,7 +6227,8 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type, } /* Static function to display route. */ -static void route_vty_out_route(struct prefix *p, struct vty *vty) +static void route_vty_out_route(struct prefix *p, struct vty *vty, + json_object *json) { int len; u_int32_t destination; @@ -6250,23 +6251,29 @@ static void route_vty_out_route(struct prefix *p, struct vty *vty) len = vty_out(vty, "%s", buf); } else if (p->family == AF_EVPN) { #if defined(HAVE_CUMULUS) - len = vty_out(vty, "%s", - bgp_evpn_route2str((struct prefix_evpn *)p, buf, - BUFSIZ)); + if (!json) + len = vty_out (vty, "%s", + bgp_evpn_route2str((struct prefix_evpn *)p, + buf, BUFSIZ)); + else + bgp_evpn_route2json ( (struct prefix_evpn *) p, json); #else prefix2str(p, buf, PREFIX_STRLEN); len = vty_out(vty, "%s", buf); #endif - } else + } else { len = vty_out(vty, "%s/%d", inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); + } - len = 17 - len; - if (len < 1) - vty_out(vty, "\n%*s", 20, " "); - else - vty_out(vty, "%*s", len, " "); + if (!json) { + len = 17 - len; + if (len < 1) + vty_out(vty, "\n%*s", 20, " "); + else + vty_out(vty, "%*s", len, " "); + } } enum bgp_display_type { @@ -6370,9 +6377,11 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, if (!json_paths) { /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, json_path); else vty_out(vty, "%*s", 17, " "); + } else { + route_vty_out_route (p, vty, json_path); } /* Print attribute */ @@ -6668,7 +6677,7 @@ void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, json_net, "addrPrefix", inet_ntop(p->family, &p->u.prefix, buff, BUFSIZ)); else - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); /* Print attribute */ if (attr) { @@ -6801,7 +6810,7 @@ void route_vty_out_tag(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (json == NULL) { if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); else vty_out(vty, "%*s", 17, " "); } @@ -6916,7 +6925,7 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); else vty_out(vty, "%*s", 17, " "); @@ -6985,7 +6994,7 @@ static void damp_route_vty_out(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (!use_json) { if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); else vty_out(vty, "%*s", 17, " "); } @@ -7054,7 +7063,7 @@ static void flap_route_vty_out(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (!use_json) { if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); else vty_out(vty, "%*s", 17, " "); } @@ -8336,6 +8345,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, #if defined(HAVE_CUMULUS) char buf3[EVPN_ROUTE_STRLEN]; #endif + char prefix_str[BUFSIZ]; int count = 0; int best = 0; int suppress = 0; @@ -8357,10 +8367,9 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, if (has_valid_label) json_object_int_add(json, "localLabel", label); - json_object_string_add(json, "prefix", - inet_ntop(p->family, &p->u.prefix, buf2, - INET6_ADDRSTRLEN)); - json_object_int_add(json, "prefixlen", p->prefixlen); + json_object_string_add (json, "prefix", + prefix2str (p, prefix_str, + sizeof (prefix_str))); } else { #if defined(HAVE_CUMULUS) if (safi == SAFI_EVPN)