From 520d5d768b996e96c375562d2b92c37801f9081b Mon Sep 17 00:00:00 2001 From: vivek Date: Mon, 15 May 2017 14:53:31 -0700 Subject: [PATCH] bgpd: Display (show) commands for EVPN Implement various operational/display commands for EVPN: - show bgp evpn summary - show bgp evpn vni [] - show bgp evpn route [type ] - show bgp evpn route [rd [type ]] - show bgp evpn route [rd [mac [ip ]]] - show bgp evpn route vni [type | vtep ] - show bgp evpn route vni [mac [ip ]]] - show bgp evpn route vni [multicast ] - show bgp evpn route vni all [vtep ] - show bgp evpn import-rt Signed-off-by: Vivek Venkatraman Signed-off-by: Daniel Walton Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn.c | 45 ++ bgpd/bgp_evpn.h | 4 + bgpd/bgp_evpn_vty.c | 1064 +++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_route.c | 96 +++- bgpd/bgp_route.h | 9 + bgpd/bgp_vty.c | 2 +- bgpd/bgp_vty.h | 3 + 7 files changed, 1215 insertions(+), 8 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 5bff857236..f17bcfab1e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -1981,6 +1981,51 @@ bgp_evpn_label2str (mpls_label_t *label, char *buf, int len) return buf; } +/* + * Function to convert evpn route to string. + * NOTE: We don't use prefix2str as the output here is a bit different. + */ +char * +bgp_evpn_route2str (struct prefix_evpn *p, char *buf, int len) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[PREFIX2STR_BUFFER]; + + if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) + { + snprintf (buf, len, "[%d]:[0]:[%d]:[%s]", + p->prefix.route_type, IS_EVPN_PREFIX_IPADDR_V4(p) ? \ + IPV4_MAX_BITLEN : IPV6_MAX_BITLEN, + 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)) + snprintf (buf, len, "[%d]:[0]:[0]:[%d]:[%s]", + p->prefix.route_type, 8*ETHER_ADDR_LEN, + prefix_mac2str (&p->prefix.mac, buf1, sizeof(buf1))); + else + { + u_char family; + + family = IS_EVPN_PREFIX_IPADDR_V4(p) ? \ + AF_INET : AF_INET6; + snprintf (buf, len, "[%d]:[0]:[0]:[%d]:[%s]:[%d]:[%s]", + p->prefix.route_type, 8*ETHER_ADDR_LEN, + prefix_mac2str (&p->prefix.mac, buf1, sizeof(buf1)), + family == AF_INET ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN, + inet_ntop (family, &p->prefix.ip.ip.addr, + buf2, PREFIX2STR_BUFFER)); + } + } + else + { + /* Currently, this is to cater to other AF_ETHERNET code. */ + } + + return(buf); +} + /* * Encode EVPN prefix in Update (MP_REACH) */ diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 9e471138f5..1a71ec284a 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -23,8 +23,12 @@ #include "vxlan.h" +#define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */ + 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_encode_prefix (struct stream *s, struct prefix *p, struct prefix_rd *prd, mpls_label_t *label, diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 6335b9d468..9a8078ca99 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -33,11 +33,307 @@ #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_evpn_private.h" #include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_vty.h" #define SHOW_DISPLAY_STANDARD 0 #define SHOW_DISPLAY_TAGS 1 #define SHOW_DISPLAY_OVERLAY 2 +/* + * Context for VNI hash walk - used by callbacks. + */ +struct vni_walk_ctx +{ + struct bgp *bgp; + struct vty *vty; + struct in_addr vtep_ip; +}; + +static void +display_import_rt (struct vty *vty, struct irt_node *irt) +{ + u_char *pnt; + u_char type, sub_type; + struct ecommunity_as + { + as_t as; + u_int32_t val; + } eas; + struct ecommunity_ip + { + struct in_addr ip; + u_int16_t val; + } eip; + struct listnode *node, *nnode; + struct bgpevpn *tmp_vpn; + + + /* TODO: This needs to go into a function */ + + pnt = (u_char *)&irt->rt.val; + type = *pnt++; + sub_type = *pnt++; + if (sub_type != ECOMMUNITY_ROUTE_TARGET) + return; + + switch (type) + { + case ECOMMUNITY_ENCODE_AS: + eas.as = (*pnt++ << 8); + eas.as |= (*pnt++); + + eas.val = (*pnt++ << 24); + eas.val |= (*pnt++ << 16); + eas.val |= (*pnt++ << 8); + eas.val |= (*pnt++); + + vty_out (vty, "Route-target: %u:%u", eas.as, eas.val); + break; + + case ECOMMUNITY_ENCODE_IP: + memcpy (&eip.ip, pnt, 4); + pnt += 4; + eip.val = (*pnt++ << 8); + eip.val |= (*pnt++); + + vty_out (vty, "Route-target: %s:%u", + inet_ntoa (eip.ip), eip.val); + break; + + case ECOMMUNITY_ENCODE_AS4: + eas.as = (*pnt++ << 24); + eas.as |= (*pnt++ << 16); + eas.as |= (*pnt++ << 8); + eas.as |= (*pnt++); + + eas.val = (*pnt++ << 8); + eas.val |= (*pnt++); + + vty_out (vty, "Route-target: %u:%u", eas.as, eas.val); + break; + + default: + return; + } + + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "List of VNIs importing routes with this route-target:%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS (irt->vnis, node, nnode, tmp_vpn)) + vty_out (vty, " %u%s", tmp_vpn->vni, VTY_NEWLINE); +} + +static void +show_import_rt_entry (struct hash_backet *backet, struct vty *vty) +{ + struct irt_node *irt = (struct irt_node *) backet->data; + display_import_rt (vty, irt); +} + +static void +bgp_evpn_show_route_rd_header (struct vty *vty, struct bgp_node *rd_rn) +{ + u_int16_t type; + struct rd_as rd_as; + struct rd_ip rd_ip; + u_char *pnt; + + pnt = rd_rn->p.u.val; + + /* Decode RD type. */ + type = decode_rd_type (pnt); + + 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); + 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); + break; + + default: + vty_out (vty, "Unknown RD type"); + break; + } + + vty_out (vty, "%s", VTY_NEWLINE); +} + +static void +bgp_evpn_show_route_header (struct vty *vty, struct bgp *bgp) +{ + char ri_header[] = " Network Next Hop Metric LocPrf Weight Path%s"; + + vty_out (vty, "BGP table version is 0, local router ID is %s%s", + inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, "Status codes: s suppressed, d damped, h history, " + "* valid, > best, i - internal%s", VTY_NEWLINE); + vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s", + VTY_NEWLINE); + vty_out (vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]%s", + VTY_NEWLINE); + vty_out (vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]%s%s", + VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, ri_header, VTY_NEWLINE); +} + +static void +display_vni (struct vty *vty, struct bgpevpn *vpn) +{ + char buf1[INET6_ADDRSTRLEN]; + char *ecom_str; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + vty_out (vty, "VNI: %d", vpn->vni); + if (is_vni_live (vpn)) + vty_out (vty, " (known to the kernel)"); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, " RD: %s%s", + prefix_rd2str (&vpn->prd, buf1, RD_ADDRSTRLEN), + VTY_NEWLINE); + vty_out (vty, " Originator IP: %s%s", + inet_ntoa(vpn->originator_ip), VTY_NEWLINE); + + vty_out (vty, " Import Route Target:%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom)) + { + ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out (vty, " %s%s", ecom_str, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); + } + + vty_out (vty, " Export Route Target:%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom)) + { + ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out (vty, " %s%s", ecom_str, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); + } +} + +static void +show_vni_routes (struct bgp *bgp, struct bgpevpn *vpn, int type, + struct vty *vty, struct in_addr vtep_ip) +{ + struct bgp_node *rn; + struct bgp_info *ri; + int header = 1; + u_int32_t prefix_cnt, path_cnt; + + prefix_cnt = path_cnt = 0; + + for (rn = bgp_table_top (vpn->route_table); rn; rn = bgp_route_next (rn)) + { + struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + + if (type && + evp->prefix.route_type != type) + continue; + + if (rn->info) + { + /* Overall header/legend displayed once. */ + if (header) + { + bgp_evpn_show_route_header (vty, bgp); + header = 0; + } + + prefix_cnt++; + } + + /* 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) + { + if (vtep_ip.s_addr && + !IPV4_ADDR_SAME(&(vtep_ip), &(ri->attr->nexthop))) + continue; + + path_cnt++; + route_vty_out (vty, &rn->p, ri, 0, SAFI_EVPN, NULL); + } + } + + if (prefix_cnt == 0) + vty_out (vty, "No EVPN prefixes %sexist for this VNI%s", + type ? "(of requested type) " : "", VTY_NEWLINE); + else + vty_out (vty, "%sDisplayed %u prefixes (%u paths)%s%s", + VTY_NEWLINE, prefix_cnt, path_cnt, + type ? " (of requested type)" : "", VTY_NEWLINE); +} + +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; + + vty_out (vty, "%sVNI: %d%s%s", VTY_NEWLINE, vpn->vni, VTY_NEWLINE, VTY_NEWLINE); + show_vni_routes (wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip); +} + +static void +show_vni_entry (struct hash_backet *backet, struct vty *vty) +{ + struct bgpevpn *vpn = (struct bgpevpn *) backet->data; + char buf1[10]; + char buf2[INET6_ADDRSTRLEN]; + char rt_buf[25]; + char *ecom_str; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + 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)); + + 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); + + XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); + break; + } + + 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); + + XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); + break; + } + vty_out (vty, "%s", VTY_NEWLINE); +} + static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, enum bgp_show_type type, void *output_arg, int option, @@ -718,6 +1014,397 @@ DEFUN(no_evpnrt5_network, argv[idx_ethtag]->arg); } +/* + * Display import RT mapping to VNIs (vty handler) + */ +static void +evpn_show_import_rts (struct vty *vty, struct bgp *bgp) +{ + hash_iterate (bgp->import_rt_hash, + (void (*) (struct hash_backet *, void *)) + show_import_rt_entry, vty); +} + +/* + * 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) +{ + u_int32_t num_vnis; + struct vni_walk_ctx wctx; + + num_vnis = hashcount(bgp->vnihash); + if (!num_vnis) + return; + memset (&wctx, 0, sizeof (struct vni_walk_ctx)); + wctx.bgp = bgp; + wctx.vty = vty; + wctx.vtep_ip = vtep_ip; + hash_iterate (bgp->vnihash, + (void (*) (struct hash_backet *, void *)) + show_vni_routes_hash, &wctx); +} + +/* + * 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) +{ + struct bgpevpn *vpn; + struct prefix_evpn p; + struct bgp_node *rn; + struct bgp_info *ri; + u_int32_t path_cnt = 0; + afi_t afi; + safi_t safi; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + + /* Locate VNI. */ + vpn = bgp_evpn_lookup_vni (bgp, vni); + if (!vpn) + { + vty_out (vty, "VNI not found%s", VTY_NEWLINE); + return; + } + + /* See if route exists. */ + 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%s", VTY_NEWLINE); + return; + } + + /* Prefix and num paths displayed once per prefix. */ + route_vty_out_detail_header (vty, bgp, rn, NULL, afi, safi, NULL); + + /* 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); + path_cnt++; + } + + vty_out (vty, "%sDisplayed %u paths for requested prefix%s", + VTY_NEWLINE, path_cnt, VTY_NEWLINE); +} + +/* + * Display EVPN routes for a VNI -- for specific MAC and/or IP (vty handler). + * By definition, only matching type-2 route will be displayed. + */ +static void +evpn_show_route_vni_macip (struct vty *vty, struct bgp *bgp, + vni_t vni, struct ethaddr *mac, + struct ipaddr *ip) +{ + struct bgpevpn *vpn; + struct prefix_evpn p; + struct bgp_node *rn; + struct bgp_info *ri; + u_int32_t path_cnt = 0; + afi_t afi; + safi_t safi; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + + /* Locate VNI. */ + vpn = bgp_evpn_lookup_vni (bgp, vni); + if (!vpn) + { + vty_out (vty, "VNI not found%s", VTY_NEWLINE); + return; + } + + /* See if route exists. Look for both non-sticky and sticky. */ + 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%s", VTY_NEWLINE); + return; + } + + /* Prefix and num paths displayed once per prefix. */ + route_vty_out_detail_header (vty, bgp, rn, NULL, afi, safi, NULL); + + /* 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); + path_cnt++; + } + + vty_out (vty, "%sDisplayed %u paths for requested prefix%s", + VTY_NEWLINE, path_cnt, VTY_NEWLINE); +} + +/* + * Display EVPN routes for a VNI - vty handler. + * If 'type' is non-zero, only routes matching that type are shown. + * 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) +{ + struct bgpevpn *vpn; + + /* Locate VNI. */ + vpn = bgp_evpn_lookup_vni (bgp, vni); + if (!vpn) + { + vty_out (vty, "VNI not found%s", VTY_NEWLINE); + return; + } + + /* Walk this VNI's route table and display appropriate routes. */ + show_vni_routes (bgp, vpn, type, vty, vtep_ip); +} + +/* + * Display BGP EVPN routing table -- for specific RD and MAC and/or + * IP (vty handler). By definition, only matching type-2 route will be + * displayed. + */ +static void +evpn_show_route_rd_macip (struct vty *vty, struct bgp *bgp, + struct prefix_rd *prd, struct ethaddr *mac, + struct ipaddr *ip) +{ + struct prefix_evpn p; + struct bgp_node *rn; + struct bgp_info *ri; + afi_t afi; + safi_t safi; + u_int32_t path_cnt = 0; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + + /* See if route exists. Look for both non-sticky and sticky. */ + build_evpn_type2_prefix (&p, mac, ip); + 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%s", VTY_NEWLINE); + return; + } + + /* Prefix and num paths displayed once per prefix. */ + route_vty_out_detail_header (vty, bgp, rn, prd, afi, safi, NULL); + + /* 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); + path_cnt++; + } + + vty_out (vty, "%sDisplayed %u paths for requested prefix%s", + VTY_NEWLINE, path_cnt, VTY_NEWLINE); +} + +/* + * Display BGP EVPN routing table -- for specific RD (vty handler) + * 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 bgp_node *rd_rn; + struct bgp_table *table; + struct bgp_node *rn; + struct bgp_info *ri; + int rd_header = 1; + afi_t afi; + safi_t safi; + u_int32_t prefix_cnt, path_cnt; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + prefix_cnt = path_cnt = 0; + + 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; + + /* 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; + + if (type && + evp->prefix.route_type != type) + continue; + + if (rn->info) + { + /* RD header and legend - once overall. */ + if (rd_header) + { + vty_out (vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:" + "[MAC]%s", VTY_NEWLINE); + vty_out (vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:" + "[OrigIP]%s%s", VTY_NEWLINE, VTY_NEWLINE); + rd_header = 0; + } + + /* Prefix and num paths displayed once per prefix. */ + route_vty_out_detail_header (vty, bgp, rn, prd, afi, safi, NULL); + + prefix_cnt++; + } + + /* 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); + path_cnt++; + } + } + + if (prefix_cnt == 0) + vty_out (vty, "No prefixes exist with this RD%s%s", + type ? " (of requested type)" : "", VTY_NEWLINE); + else + vty_out (vty, "%sDisplayed %u prefixes (%u paths) with this RD%s%s", + VTY_NEWLINE, prefix_cnt, path_cnt, + type ? " (of requested type)" : "", VTY_NEWLINE); +} + +/* + * 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) +{ + struct bgp_node *rd_rn; + struct bgp_table *table; + struct bgp_node *rn; + struct bgp_info *ri; + int header = 1; + int rd_header; + afi_t afi; + safi_t safi; + u_int32_t prefix_cnt, path_cnt; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + prefix_cnt = path_cnt = 0; + + /* EVPN routing table is a 2-level table with the first level being + * the RD. + */ + for (rd_rn = bgp_table_top (bgp->rib[afi][safi]); rd_rn; + rd_rn = bgp_route_next (rd_rn)) + { + table = (struct bgp_table *)rd_rn->info; + if (table == NULL) + continue; + + rd_header = 1; + + /* Display all prefixes for an RD */ + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + + if (type && + evp->prefix.route_type != type) + continue; + + if (rn->info) + { + /* Overall header/legend displayed once. */ + if (header) + { + bgp_evpn_show_route_header (vty, bgp); + header = 0; + } + + /* RD header - per RD. */ + if (rd_header) + { + bgp_evpn_show_route_rd_header (vty, rd_rn); + rd_header = 0; + } + + prefix_cnt++; + } + + /* 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) + { + path_cnt++; + route_vty_out (vty, &rn->p, ri, 0, SAFI_EVPN, NULL); + } + } + } + + if (prefix_cnt == 0) + vty_out (vty, "No EVPN prefixes %sexist%s", + type ? "(of requested type) " : "", VTY_NEWLINE); + else + vty_out (vty, "%sDisplayed %u prefixes (%u paths)%s%s", + VTY_NEWLINE, prefix_cnt, path_cnt, + type ? " (of requested type)" : "", VTY_NEWLINE); +} + +/* + * Display specified VNI (vty handler) + */ +static void +evpn_show_vni (struct vty *vty, struct bgp *bgp, vni_t vni) +{ + struct bgpevpn *vpn; + + vpn = bgp_evpn_lookup_vni (bgp, vni); + if (!vpn) + { + vty_out (vty, "VNI not found%s", VTY_NEWLINE); + return; + } + + display_vni (vty, vpn); +} + +/* + * Display a VNI (upon user query). + */ +static void +evpn_show_all_vnis (struct vty *vty, struct bgp *bgp) +{ + u_int32_t num_vnis; + + num_vnis = hashcount(bgp->vnihash); + if (!num_vnis) + return; + vty_out(vty, "Number of VNIs: %u%s", + num_vnis, VTY_NEWLINE); + vty_out(vty, "Flags: * - Kernel %s", VTY_NEWLINE); + vty_out(vty, " %-10s %-15s %-21s %-25s %-25s%s", + "VNI", "Orig IP", "RD", "Import RT", "Export RT", VTY_NEWLINE); + hash_iterate (bgp->vnihash, + (void (*) (struct hash_backet *, void *)) + show_vni_entry, vty); +} + /* * EVPN (VNI advertisement) enabled. Register with zebra. */ @@ -767,6 +1454,370 @@ DEFUN (no_bgp_evpn_advertise_all_vni, return CMD_SUCCESS; } +DEFUN (show_bgp_evpn_vni, + show_bgp_evpn_vni_cmd, + "show bgp evpn vni", + SHOW_STR + BGP_STR + EVPN_HELP_STR + "Show VNI\n") +{ + struct bgp *bgp; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + vty_out (vty, "Advertise All VNI flag: %s%s", + bgp->advertise_all_vni? "Enabled" : "Disabled", VTY_NEWLINE); + + evpn_show_all_vnis (vty, bgp); + return CMD_SUCCESS; +} + +DEFUN (show_bgp_evpn_vni_num, + show_bgp_evpn_vni_num_cmd, + "show bgp evpn vni (1-16777215)", + SHOW_STR + BGP_STR + "Address family modifier\n" + "Show VNI\n" + "VNI number\n") +{ + vni_t vni; + struct bgp *bgp; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE ("VNI", vni, argv[4]->arg, 1, VNI_MAX); + + evpn_show_vni (vty, bgp, vni); + return CMD_SUCCESS; +} + +/* `show bgp evpn summary' commands. */ +DEFUN (show_bgp_evpn_summary, + show_bgp_evpn_summary_cmd, + "show bgp evpn summary [json]", + SHOW_STR + BGP_STR + "EVPN\n" + "Summary of BGP neighbor status\n" + "JavaScript Object Notation\n") +{ + u_char uj = use_json(argc, argv); + return bgp_show_summary_vty (vty, NULL, AFI_L2VPN, SAFI_EVPN, uj); +} + +/* Show bgp evpn route */ +DEFUN (show_bgp_evpn_route, + show_bgp_evpn_route_cmd, + "show bgp evpn route [type ]", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Display EVPN route information\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n") +{ + struct bgp *bgp; + int type = 0; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + if (argc == 6) + { + if (strncmp (argv[5]->arg, "ma", 2) == 0) + type = BGP_EVPN_MAC_IP_ROUTE; + else if (strncmp (argv[5]->arg, "mu", 2) == 0) + type = BGP_EVPN_IMET_ROUTE; + else + return CMD_WARNING; + } + + evpn_show_all_routes (vty, bgp, type); + return CMD_SUCCESS; +} + +DEFUN (show_bgp_evpn_route_rd, + show_bgp_evpn_route_rd_cmd, + "show bgp evpn route rd ASN:nn_or_IP-address:nn [type ]", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Display 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") +{ + struct bgp *bgp; + int ret; + struct prefix_rd prd; + int type = 0; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + ret = str2prefix_rd (argv[5]->arg, &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 8) + { + if (strncmp (argv[7]->arg, "ma", 2) == 0) + type = BGP_EVPN_MAC_IP_ROUTE; + else if (strncmp (argv[7]->arg, "mu", 2) == 0) + type = BGP_EVPN_IMET_ROUTE; + else + return CMD_WARNING; + } + + evpn_show_route_rd (vty, bgp, &prd, type); + return CMD_SUCCESS; +} + +DEFUN (show_bgp_evpn_route_rd_macip, + show_bgp_evpn_route_rd_macip_cmd, + "show bgp evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Display 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; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + ret = str2prefix_rd (argv[5]->arg, &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!prefix_str2mac (argv[7]->arg, &mac)) + { + vty_out (vty, "%% Malformed MAC address%s", VTY_NEWLINE); + return CMD_WARNING; + } + memset (&ip, 0, sizeof (ip)); + if (argc == 10 && argv[9]->arg != NULL) + { + if (str2ipaddr (argv[9]->arg, &ip) != 0) + { + vty_out (vty, "%% Malformed IP address%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + evpn_show_route_rd_macip (vty, bgp, &prd, &mac, &ip); + return CMD_SUCCESS; +} + +DEFUN (show_bgp_evpn_route_vni, + show_bgp_evpn_route_vni_cmd, + "show bgp evpn route vni (1-16777215) [ | vtep A.B.C.D>]", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Display 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") +{ + vni_t vni; + struct bgp *bgp; + struct in_addr vtep_ip; + int type = 0; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + vtep_ip.s_addr = 0; + + VTY_GET_INTEGER_RANGE ("VNI", vni, argv[5]->arg, 1, VNI_MAX); + + if (argc == 8 && argv[6]->arg) + { + if (strncmp (argv[6]->arg, "type", 4) == 0) + { + if (strncmp (argv[7]->arg, "ma", 2) == 0) + type = BGP_EVPN_MAC_IP_ROUTE; + else if (strncmp (argv[7]->arg, "mu", 2) == 0) + type = BGP_EVPN_IMET_ROUTE; + else + return CMD_WARNING; + } + else if (strncmp (argv[6]->arg, "vtep", 4) == 0) + { + if (!inet_aton (argv[7]->arg, &vtep_ip)) + { + vty_out (vty, "%% Malformed VTEP IP address%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + else + return CMD_WARNING; + } + + evpn_show_routes_vni (vty, bgp, vni, type, vtep_ip); + return CMD_SUCCESS; +} + +DEFUN (show_bgp_evpn_route_vni_macip, + show_bgp_evpn_route_vni_macip_cmd, + "show bgp evpn route vni (1-16777215) mac WORD [ip WORD]", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Display 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") +{ + vni_t vni; + struct bgp *bgp; + struct ethaddr mac; + struct ipaddr ip; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE ("VNI", vni, argv[5]->arg, 1, VNI_MAX); + if (!prefix_str2mac (argv[7]->arg, &mac)) + { + vty_out (vty, "%% Malformed MAC address%s", VTY_NEWLINE); + return CMD_WARNING; + } + memset (&ip, 0, sizeof (ip)); + if (argc == 10 && argv[9]->arg != NULL) + { + if (str2ipaddr (argv[9]->arg, &ip) != 0) + { + vty_out (vty, "%% Malformed IP address%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + evpn_show_route_vni_macip (vty, bgp, vni, &mac, &ip); + return CMD_SUCCESS; +} + +DEFUN (show_bgp_evpn_route_vni_multicast, + show_bgp_evpn_route_vni_multicast_cmd, + "show bgp evpn route vni (1-16777215) multicast A.B.C.D", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Display EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "Multicast (Type-3) route\n" + "Originating Router IP address\n") +{ + vni_t vni; + struct bgp *bgp; + int ret; + struct in_addr orig_ip; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE ("VNI", vni, argv[5]->arg, 1, VNI_MAX); + ret = inet_aton (argv[7]->arg, &orig_ip); + if (!ret) + { + vty_out (vty, "%% Malformed Originating Router IP address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + evpn_show_route_vni_multicast (vty, bgp, vni, orig_ip); + return CMD_SUCCESS; +} + +DEFUN (show_bgp_evpn_route_vni_all, + show_bgp_evpn_route_vni_all_cmd, + "show bgp evpn route vni all [vtep A.B.C.D]", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Display EVPN route information\n" + "VXLAN Network Identifier\n" + "All VNIs\n" + "Remote VTEP\n" + "Remote VTEP IP address\n") +{ + struct bgp *bgp; + struct in_addr vtep_ip; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + vtep_ip.s_addr = 0; + if (argc == 8 && argv[7]->arg) + { + if (!inet_aton (argv[7]->arg, &vtep_ip)) + { + vty_out (vty, "%% Malformed VTEP IP address%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + evpn_show_routes_vni_all (vty, bgp, vtep_ip); + return CMD_SUCCESS; +} + +DEFUN (show_bgp_evpn_import_rt, + show_bgp_evpn_import_rt_cmd, + "show bgp evpn import-rt", + SHOW_STR + BGP_STR + "Address family modifier\n" + "Show import route target\n") +{ + struct bgp *bgp; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + evpn_show_import_rts (vty, bgp); + return CMD_SUCCESS; +} + void bgp_ethernetvpn_init(void) { install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_cmd); @@ -785,4 +1836,17 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &evpnrt5_network_cmd); install_element (BGP_EVPN_NODE, &bgp_evpn_advertise_all_vni_cmd); install_element (BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd); + + /* "show bgp evpn" commands. */ + install_element (VIEW_NODE, &show_bgp_evpn_vni_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_vni_num_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_summary_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_route_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_route_rd_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_route_rd_macip_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_route_vni_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_route_vni_multicast_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd); + install_element (VIEW_NODE, &show_bgp_evpn_import_rt_cmd); } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 6a2cc8d4d3..cd21c25c77 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -6349,8 +6349,13 @@ route_vty_out_route (struct prefix *p, struct vty *vty) } else if (p->family == AF_ETHERNET) { +#if defined (HAVE_CUMULUS) + len = vty_out (vty, "%s", + bgp_evpn_route2str((struct prefix_evpn *)p, buf, BUFSIZ)); +#else prefix2str(p, buf, PREFIX_STRLEN); len = vty_out (vty, "%s", buf); +#endif } else len = vty_out (vty, "%s/%d", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), @@ -6477,11 +6482,14 @@ route_vty_out (struct vty *vty, struct prefix *p, if (attr) { /* - * For ENCAP routes, nexthop address family is not + * For ENCAP and EVPN routes, nexthop address family is not * neccessarily the same as the prefix address family. * Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field + * EVPN routes are also exchanged with a MP nexthop. Currently, this + * is only IPv4, the value will be present in either attr->nexthop or + * attr->extra->mp_nexthop_global_in */ - if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) + if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN)) { if (attr->extra) { @@ -6506,6 +6514,19 @@ route_vty_out (struct vty *vty, struct prefix *p, else vty_out(vty, "?"); } + else if (safi == SAFI_EVPN) + { + if (json_paths) + { + json_nexthop_global = json_object_new_object(); + + json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->nexthop)); + json_object_string_add(json_nexthop_global, "afi", "ipv4"); + json_object_boolean_true_add(json_nexthop_global, "used"); + } + else + vty_out (vty, "%-16s", inet_ntoa (attr->nexthop)); + } /* IPv4 Next Hop */ else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { @@ -7240,13 +7261,14 @@ route_vty_out_advertised_to (struct vty *vty, struct peer *peer, int *first, } } -static void +void route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, struct bgp_info *binfo, afi_t afi, safi_t safi, json_object *json_paths) { char buf[INET6_ADDRSTRLEN]; char buf1[BUFSIZ]; + char buf2[EVPN_ROUTE_STRLEN]; struct attr *attr; int sockunion_vty_out (struct vty *, union sockunion *); time_t tbuf; @@ -7276,6 +7298,39 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, json_nexthop_global = json_object_new_object(); } +#if defined (HAVE_CUMULUS) + if (!json_paths && safi == SAFI_EVPN) + { + char tag_buf[20]; + + bgp_evpn_route2str ((struct prefix_evpn *)p, buf2, sizeof (buf2)); + vty_out (vty, " Route %s", buf2); + tag_buf[0] = '\0'; + if (binfo->extra) + { + bgp_evpn_label2str (&binfo->extra->label, tag_buf, sizeof (tag_buf)); + vty_out (vty, " VNI %s", tag_buf); + } + vty_out (vty, "%s", VTY_NEWLINE); + if (binfo->extra && binfo->extra->parent) + { + struct bgp_info *parent_ri; + struct bgp_node *rn, *prn; + + parent_ri = (struct bgp_info *)binfo->extra->parent; + rn = parent_ri->net; + if (rn && rn->prn) + { + prn = rn->prn; + vty_out (vty, " Imported from %s:%s%s", + prefix_rd2str ((struct prefix_rd *)&prn->p, + buf1, RD_ADDRSTRLEN), + buf2, VTY_NEWLINE); + } + } + } +#endif + attr = binfo->attr; if (attr) @@ -7442,7 +7497,8 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (binfo->peer == bgp->peer_self) { - if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) + if (safi == SAFI_EVPN || + (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { if (json_paths) json_object_string_add(json_peer, "peerId", "0.0.0.0"); @@ -7823,7 +7879,11 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, bgp_damp_info_vty (vty, binfo, json_path); /* Remote Label */ +#if defined (HAVE_CUMULUS) + if (binfo->extra && bgp_is_valid_label(&binfo->extra->label) && safi != SAFI_EVPN) +#else if (binfo->extra && bgp_is_valid_label(&binfo->extra->label)) +#endif { mpls_label_t label = label_pton(&binfo->extra->label); if (json_paths) @@ -8285,7 +8345,7 @@ bgp_show_all_instances_routes_vty (struct vty *vty, afi_t afi, safi_t safi, } /* Header of detailed BGP route information */ -static void +void route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, struct bgp_node *rn, struct prefix_rd *prd, afi_t afi, safi_t safi, @@ -8297,6 +8357,7 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, struct listnode *node, *nnode; char buf1[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; + char buf3[EVPN_ROUTE_STRLEN]; int count = 0; int best = 0; int suppress = 0; @@ -8324,6 +8385,22 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, } else { +#if defined (HAVE_CUMULUS) + if (safi == SAFI_EVPN) + vty_out (vty, "BGP routing table entry for %s%s%s%s", + prd ? prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : "", + prd ? ":" : "", + bgp_evpn_route2str ((struct prefix_evpn *)p, + buf3, sizeof (buf3)), + VTY_NEWLINE); + else + vty_out (vty, "BGP routing table entry for %s%s%s/%d%s", + ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) ? + prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""), + safi == SAFI_MPLS_VPN ? ":" : "", + inet_ntop (p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN), + p->prefixlen, VTY_NEWLINE); +#else if (p->family == AF_ETHERNET) prefix2str (p, buf2, INET6_ADDRSTRLEN); else @@ -8334,11 +8411,16 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) ? ":" : "", buf2, p->prefixlen); +#endif if (has_valid_label) vty_outln (vty, "Local label: %d", label); - else if (bgp_labeled_safi(safi)) - vty_outln (vty, "Local label: not allocated"); +#if defined (HAVE_CUMULUS) + if (bgp_labeled_safi(safi) && safi != SAFI_EVPN) +#else + if (bgp_labeled_safi(safi)) +#endif + vty_out(vty, "not allocated%s", VTY_NEWLINE); } for (ri = rn->info; ri; ri = ri->next) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 01d8e62d43..cb918718fe 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -430,4 +430,13 @@ extern void bgp_zebra_clear_route_change_flags (struct bgp_node *rn); extern int bgp_zebra_has_route_changed (struct bgp_node *rn, struct bgp_info *selected); +extern void +route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, + struct bgp_node *rn, + struct prefix_rd *prd, afi_t afi, safi_t safi, + json_object *json); +extern void +route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, + struct bgp_info *binfo, afi_t afi, safi_t safi, + json_object *json_paths); #endif /* _QUAGGA_BGP_ROUTE_H */ diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 75b4739956..183a5832dc 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -7282,7 +7282,7 @@ bgp_show_all_instances_summary_vty (struct vty *vty, afi_t afi, safi_t safi, } -static int +int bgp_show_summary_vty (struct vty *vty, const char *name, afi_t afi, safi_t safi, u_char use_json) { diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index edc994a57c..62228c64ed 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -69,4 +69,7 @@ argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, safi_t * extern int bgp_vty_find_and_parse_afi_safi_bgp (struct vty *vty, struct cmd_token **argv, int argc, int *idx, afi_t *afi, safi_t *safi, struct bgp **bgp); +extern int +bgp_show_summary_vty (struct vty *vty, const char *name, + afi_t afi, safi_t safi, u_char use_json); #endif /* _QUAGGA_BGP_VTY_H */ -- 2.39.5