From 229587fb469a9328febb9a08dfd5cbc08f2d1b6b Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Fri, 8 May 2020 19:49:33 -0700 Subject: [PATCH] bgpd: commands to display L3 NHGs and MAC-IP paths linked to an ES Sample output - =============== torm-11# sh bgp l2vpn evpn es-vrf ES-VRF Flags: A Active ESI VRF Flags IPv4-NHG IPv6-NHG Ref 03:44:38:39:ff:ff:01:00:00:01 vrf3 A 1 0 2 03:44:38:39:ff:ff:01:00:00:01 vrf2 A 6 0 4 03:44:38:39:ff:ff:01:00:00:01 vrf1 A 7 0 4 03:44:38:39:ff:ff:01:00:00:02 vrf3 A 2 0 2 03:44:38:39:ff:ff:01:00:00:02 vrf2 A 4 0 4 03:44:38:39:ff:ff:01:00:00:02 vrf1 A 8 0 4 Signed-off-by: Anuradha Karuppiah --- bgpd/bgp_evpn_mh.c | 132 ++++++++++++++++++++++++++++++++++++++----- bgpd/bgp_evpn_mh.h | 3 + bgpd/bgp_evpn_vty.c | 133 ++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_route.c | 23 ++++++-- 4 files changed, 270 insertions(+), 21 deletions(-) diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 6a8799d4cd..f559fc7f4a 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -1381,17 +1381,14 @@ void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) { struct bgp_evpn_es *es = es_info->es; struct bgp_path_info *pi; - char prefix_buf[PREFIX_STRLEN]; if (!es) return; pi = es_info->pi; if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) - zlog_debug( - "path %s unlinked from es %s", - prefix2str(&pi->net->p, prefix_buf, sizeof(prefix_buf)), - es->esi_str); + zlog_debug("vni %u path %pFX unlinked from es %s", es_info->vni, + &pi->net->p, es->esi_str); list_delete_node(es->macip_path_list, &es_info->es_listnode); es_info->es = NULL; @@ -1411,7 +1408,6 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) struct bgp_path_es_info *es_info; struct bgp_evpn_es *es; struct bgp *bgp_evpn = bgp_get_evpn(); - char prefix_buf[PREFIX_STRLEN]; es_info = pi->extra ? pi->extra->es_info : NULL; /* if the esi is zero just unlink the path from the old es */ @@ -1441,15 +1437,13 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) bgp_evpn_path_es_unlink(es_info); if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) - zlog_debug( - "path %s linked to es %s", - prefix2str(&pi->net->p, prefix_buf, sizeof(prefix_buf)), - es->esi_str); + zlog_debug("vni %u path %pFX linked to es %s", vni, &pi->net->p, + es->esi_str); /* link mac-ip path to the new destination ES */ es_info->es = es; listnode_init(&es_info->es_listnode, es_info); - listnode_add_sort(es->macip_path_list, &es_info->es_listnode); + listnode_add(es->macip_path_list, &es_info->es_listnode); } static void bgp_evpn_es_path_all_update(struct bgp_evpn_es_vtep *es_vtep, @@ -1467,6 +1461,9 @@ static void bgp_evpn_es_path_all_update(struct bgp_evpn_es_vtep *es_vtep, for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) { pi = es_info->pi; + if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + continue; + if (pi->sub_type != BGP_ROUTE_IMPORTED) continue; @@ -2003,6 +2000,10 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, ip_buf, sizeof(ip_buf))); json_object_int_add(json, "remoteVniCount", es->remote_es_evi_cnt); + json_object_int_add(json, "vrfCount", + listcount(es->es_vrf_list)); + json_object_int_add(json, "macipPathCount", + listcount(es->macip_path_list)); json_object_int_add(json, "inconsistentVniVtepCount", es->incons_evi_vtep_cnt); if (listcount(es->es_vtep_list)) { @@ -2047,6 +2048,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list)); vty_out(vty, " Remote VNI Count: %d\n", es->remote_es_evi_cnt); + vty_out(vty, " VRF Count: %d\n", listcount(es->es_vrf_list)); + vty_out(vty, " MACIP Path Count: %d\n", + listcount(es->macip_path_list)); vty_out(vty, " Inconsistent VNI VTEP Count: %d\n", es->incons_evi_vtep_cnt); if (es->inconsistencies) { @@ -2411,6 +2415,10 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, *nhg_p = 0; + /* L3NHG support is disabled, use legacy-exploded multipath */ + if (!bgp_mh_info->host_routes_use_l3nhg) + return false; + parent_pi = get_route_parent_evpn(pi); if (!parent_pi) return false; @@ -2423,10 +2431,6 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) return false; - /* L3NHG support is disabled, use legacy-exploded multipath */ - if (!bgp_mh_info->host_routes_use_l3nhg) - return false; - /* non-es path, use legacy-exploded multipath */ esi = bgp_evpn_attr_get_esi(parent_pi->attr); if (!memcmp(esi, zero_esi, sizeof(*esi))) @@ -2463,6 +2467,104 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, return true; } +static void bgp_evpn_es_vrf_show_entry(struct vty *vty, + struct bgp_evpn_es_vrf *es_vrf, + json_object *json) +{ + struct bgp_evpn_es *es = es_vrf->es; + struct bgp *bgp_vrf = es_vrf->bgp_vrf; + + if (json) { + json_object *json_types; + + json_object_string_add(json, "esi", es->esi_str); + json_object_string_add(json, "vrf", bgp_vrf->name); + + if (es_vrf->flags & (BGP_EVPNES_VRF_NHG_ACTIVE)) { + json_types = json_object_new_array(); + if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE) + json_array_string_add(json_types, "active"); + json_object_object_add(json, "flags", json_types); + } + + json_object_int_add(json, "ipv4NHG", es_vrf->nhg_id); + json_object_int_add(json, "ipv6NHG", es_vrf->v6_nhg_id); + json_object_int_add(json, "refCount", es_vrf->ref_cnt); + } else { + char flags_str[4]; + + flags_str[0] = '\0'; + if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE) + strlcat(flags_str, "A", sizeof(flags_str)); + + vty_out(vty, "%-30s %-15s %-5s %-8u %-8u %u\n", es->esi_str, + bgp_vrf->name, flags_str, es_vrf->nhg_id, + es_vrf->v6_nhg_id, es_vrf->ref_cnt); + } +} + +static void bgp_evpn_es_vrf_show_es(struct vty *vty, json_object *json_array, + struct bgp_evpn_es *es) +{ + json_object *json = NULL; + struct listnode *es_vrf_node; + struct bgp_evpn_es_vrf *es_vrf; + + for (ALL_LIST_ELEMENTS_RO(es->es_vrf_list, es_vrf_node, es_vrf)) { + /* create a separate json object for each ES-VRF */ + if (json_array) + json = json_object_new_object(); + bgp_evpn_es_vrf_show_entry(vty, es_vrf, json); + /* add ES-VRF to the json array */ + if (json_array) + json_object_array_add(json_array, json); + } +} + +/* Display all ES VRFs */ +void bgp_evpn_es_vrf_show(struct vty *vty, bool uj, struct bgp_evpn_es *es) +{ + json_object *json_array = NULL; + + if (uj) { + /* create an array of ESs */ + json_array = json_object_new_array(); + } else { + vty_out(vty, "ES-VRF Flags: A Active\n"); + vty_out(vty, "%-30s %-15s %-5s %-8s %-8s %s\n", "ESI", "VRF", + "Flags", "IPv4-NHG", "IPv6-NHG", "Ref"); + } + + if (es) { + bgp_evpn_es_vrf_show_es(vty, json_array, es); + } else { + RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) + bgp_evpn_es_vrf_show_es(vty, json_array, es); + } + + /* print the array of json-ESs */ + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_array, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_array); + } +} + +/* Display specific ES VRF */ +void bgp_evpn_es_vrf_show_esi(struct vty *vty, esi_t *esi, bool uj) +{ + struct bgp_evpn_es *es; + + es = bgp_evpn_es_find(esi); + if (es) { + bgp_evpn_es_vrf_show(vty, uj, es); + } else { + if (!uj) + vty_out(vty, "ESI not found\n"); + } +} + /*****************************************************************************/ /* Ethernet Segment to EVI association - * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index f7aaf157c6..bde46e272b 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -358,5 +358,8 @@ extern void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, extern bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh); extern bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, uint32_t *nhg_p); +extern void bgp_evpn_es_vrf_show(struct vty *vty, bool uj, + struct bgp_evpn_es *es); +extern void bgp_evpn_es_vrf_show_esi(struct vty *vty, esi_t *esi, bool uj); #endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index e9e2aafebb..e5e91deb25 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -686,6 +686,78 @@ static void show_esi_routes(struct bgp *bgp, } } +/* Display all MAC-IP VNI routes linked to an ES */ +static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, + json_object *json, int detail) +{ + struct bgp_node *rn; + struct bgp_path_info *pi; + int header = detail ? 0 : 1; + uint32_t path_cnt; + struct listnode *node; + struct bgp_evpn_es *es; + struct bgp_path_es_info *es_info; + struct bgp *bgp = bgp_get_evpn(); + json_object *json_paths = NULL; + + if (!bgp) + return; + + path_cnt = 0; + + if (json) + json_paths = json_object_new_array(); + + RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) { + + if (esi && memcmp(esi, &es->esi, sizeof(*esi))) + continue; + + for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) { + json_object *json_path = NULL; + + pi = es_info->pi; + rn = pi->net; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + continue; + + /* Overall header/legend displayed once. */ + if (header) { + bgp_evpn_show_route_header(vty, bgp, 0, json); + header = 0; + } + + path_cnt++; + + if (json) + json_path = json_object_new_array(); + + if (detail) + route_vty_out_detail(vty, bgp, rn, pi, + AFI_L2VPN, SAFI_EVPN, + json_path); + else + route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, + json_path, false); + + if (json) + json_object_array_add(json_paths, json_path); + } + } + + if (json) { + json_object_object_add(json, "paths", json_paths); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (path_cnt == 0) + vty_out(vty, "There are no MAC-IP ES paths"); + else + vty_out(vty, "\nDisplayed %u paths\n", path_cnt); + vty_out(vty, "\n"); + } +} + static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, struct vty *vty, struct in_addr vtep_ip, json_object *json, int detail) @@ -3967,6 +4039,28 @@ DEFPY(show_bgp_l2vpn_evpn_es, return CMD_SUCCESS; } +DEFPY(show_bgp_l2vpn_evpn_es_vrf, show_bgp_l2vpn_evpn_es_vrf_cmd, + "show bgp l2vpn evpn es-vrf [NAME$esi_str] [json$uj]", + SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR + "Ethernet Segment\n" + "ES ID\n" JSON_STR) +{ + esi_t esi; + + if (esi_str) { + if (!str_to_esi(esi_str, &esi)) { + vty_out(vty, "%%Malformed ESI\n"); + return CMD_WARNING; + } + bgp_evpn_es_vrf_show_esi(vty, &esi, uj); + } else { + + bgp_evpn_es_vrf_show(vty, uj, NULL); + } + + return CMD_SUCCESS; +} + /* * Display EVPN neighbor summary. */ @@ -4517,6 +4611,43 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all, return CMD_SUCCESS; } +DEFPY_HIDDEN( + show_bgp_l2vpn_evpn_route_mac_ip_es, + show_bgp_l2vpn_evpn_route_mac_ip_es_cmd, + "show bgp l2vpn evpn route mac-ip-es [NAME$esi_str|detail$detail] [json$uj]", + SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR + "EVPN route information\n" + "MAC IP routes linked to the ES\n" + "ES ID\n" + "Detailed information\n" JSON_STR) +{ + esi_t esi; + esi_t *esi_p; + json_object *json = NULL; + + if (esi_str) { + if (!str_to_esi(esi_str, &esi)) { + vty_out(vty, "%%Malformed ESI\n"); + return CMD_WARNING; + } + esi_p = &esi; + } else { + esi_p = NULL; + } + + if (uj) + json = json_object_new_object(); + bgp_evpn_show_routes_mac_ip_es(vty, esi_p, json, !!detail); + 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 */ @@ -5754,6 +5885,7 @@ void bgp_ethernetvpn_init(void) /* "show bgp l2vpn evpn" commands. */ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_evi_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); @@ -5765,6 +5897,7 @@ void bgp_ethernetvpn_init(void) &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_mac_ip_es_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 29dbdbad89..db3eb95353 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -8620,11 +8620,21 @@ void route_vty_out(struct vty *vty, const struct prefix *p, vty_out(vty, "\n"); if (safi == SAFI_EVPN) { + struct bgp_path_es_info *path_es_info = NULL; + + if (path->extra) + path_es_info = path->extra->es_info; + if (bgp_evpn_is_esi_valid(&attr->esi)) { + /* XXX - add these params to the json out */ vty_out(vty, "%*s", 20, " "); - vty_out(vty, "ESI:%s\n", - esi_to_str(&attr->esi, - esi_buf, sizeof(esi_buf))); + vty_out(vty, "ESI:%s", + esi_to_str(&attr->esi, esi_buf, + sizeof(esi_buf))); + if (path_es_info && path_es_info->es) + vty_out(vty, " VNI: %u", + path_es_info->vni); + vty_out(vty, "\n"); } if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) { @@ -9301,7 +9311,9 @@ static const char *bgp_path_selection_reason2str( } static void route_vty_out_detail_es_info(struct vty *vty, - struct attr *attr, json_object *json_path) + struct bgp_path_info *pi, + struct attr *attr, + json_object *json_path) { char esi_buf[ESI_STR_LEN]; bool es_local = !!CHECK_FLAG(attr->es_flags, ATTR_ES_IS_LOCAL); @@ -9311,7 +9323,6 @@ static void route_vty_out_detail_es_info(struct vty *vty, ATTR_ES_PEER_ACTIVE); bool peer_proxy = !!CHECK_FLAG(attr->es_flags, ATTR_ES_PEER_PROXY); - esi_to_str(&attr->esi, esi_buf, sizeof(esi_buf)); if (json_path) { json_object *json_es_info = NULL; @@ -9846,7 +9857,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, if (safi == SAFI_EVPN && bgp_evpn_is_esi_valid(&attr->esi)) { - route_vty_out_detail_es_info(vty, attr, json_path); + route_vty_out_detail_es_info(vty, path, attr, json_path); } /* Line 3 display Origin, Med, Locpref, Weight, Tag, valid, -- 2.39.5