diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 0560dc46f9..20955fb83c 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -45,6 +45,8 @@ #include "bgpd/bgp_encap_types.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_nexthop.h" /* * Definitions and external declarations. @@ -1199,6 +1201,13 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, return 0; } + /* Update the tunnel-ip hash */ + bgp_tip_del(bgp, &vpn->originator_ip); + bgp_tip_add(bgp, &originator_ip); + + /* filter routes as martian nexthop db has changed */ + bgp_filter_evpn_routes_upon_martian_nh_change(bgp); + /* Need to withdraw type-3 route as the originator IP is part * of the key. */ @@ -1431,7 +1440,7 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, if (is_route_matching_for_vni(bgp, vpn, ri)) { if (install) ret = install_evpn_route_entry( - bgp, vpn, evp, ri); + bgp, vpn, evp, ri); else ret = uninstall_evpn_route_entry( bgp, vpn, evp, ri); @@ -2178,6 +2187,71 @@ 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 * ETH_ALEN); + 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 * ETH_ALEN); + 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. */ + } +} + /* * Function to convert evpn route to string. * NOTE: We don't use prefix2str as the output here is a bit different. @@ -2562,6 +2636,76 @@ int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, return install_uninstall_evpn_route(bgp, afi, safi, p, ri, 0); } +/* filter routes which have martian next hops */ +int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) +{ + afi_t afi; + safi_t safi; + struct bgp_node *rd_rn, *rn; + struct bgp_table *table; + struct bgp_info *ri; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + + /* Walk entire global routing table and evaluate routes which could be + * imported into this VPN. Note that we cannot just look at the routes + * for the VNI's RD - + * remote routes applicable for this VNI could have any RD. + */ + /* EVPN routes are a 2-level table. */ + 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) + continue; + + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + + for (ri = rn->info; ri; ri = ri->next) { + + /* Consider "valid" remote routes applicable for + * this VNI. */ + if (!(ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (bgp_nexthop_self(bgp, + ri->attr->nexthop)) { + + char attr_str[BUFSIZ]; + char pbuf[PREFIX_STRLEN]; + + bgp_dump_attr(ri->attr, attr_str, + BUFSIZ); + + if (bgp_debug_update(ri->peer, &rn->p, + NULL, 1)) + zlog_debug( + "%u: prefix %s with attr %s - DENIED due to martian or self nexthop", + bgp->vrf_id, + prefix2str( + &rn->p, + pbuf, + sizeof(pbuf)), + attr_str); + + bgp_evpn_unimport_route(bgp, afi, safi, + &rn->p, ri); + + bgp_rib_remove(rn, ri, ri->peer, + afi, safi); + + + } + + } + } + } + + return 0; +} + /* * Handle del of a local MACIP. */ @@ -2657,6 +2801,11 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) */ delete_routes_for_vni(bgp, vpn); + /* + * tunnel is no longer active, del tunnel ip address from tip_hash + */ + bgp_tip_del(bgp, &vpn->originator_ip); + /* Clear "live" flag and see if hash needs to be freed. */ UNSET_FLAG(vpn->flags, VNI_FLAG_LIVE); if (!is_vni_configured(vpn)) @@ -2702,15 +2851,22 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, bgp->vrf_id, vni); return -1; } + } - /* if the VNI is live already, there is nothibng more to do */ + /* if the VNI is live already, there is nothing more to do */ if (is_vni_live(vpn)) return 0; /* Mark as "live" */ SET_FLAG(vpn->flags, VNI_FLAG_LIVE); + /* tunnel is now active, add tunnel-ip to db */ + bgp_tip_add(bgp, &originator_ip); + + /* filter routes as nexthop database has changed */ + bgp_filter_evpn_routes_upon_martian_nh_change(bgp); + /* Create EVPN type-3 route and schedule for processing. */ build_evpn_type3_prefix(&p, vpn->originator_ip); if (update_evpn_route(bgp, vpn, &p, 0)) { @@ -2725,6 +2881,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, */ install_routes_for_vni(bgp, vpn); + /* If we are advertising gateway mac-ip + It needs to be conveyed again to zebra */ + bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip, vpn->vni); + return 0; } diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index e9b7857212..985f41f586 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -28,6 +28,7 @@ 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, @@ -38,6 +39,7 @@ extern int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, struct prefix *p, struct bgp_info *ri); extern int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, struct prefix *p, struct bgp_info *ri); +extern int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp); extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip); extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, 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..e8e986d1fc 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; - display_import_rt(vty, irt); + + vty = args[0]; + json = args[1]; + + display_import_rt(vty, irt, json); + + 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; - vty_out(vty, "VNI: %d", vpn->vni); - if (is_vni_live(vpn)) - vty_out(vty, " (known to the kernel)"); - vty_out(vty, "\n"); + 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, " 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]; - vty_out(vty, "\nVNI: %d\n\n", vpn->vni); - show_vni_routes(wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip); + 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); + + 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,92 @@ 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 +1488,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 +1519,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 +1529,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 +1539,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 +1555,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 +1599,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 +1608,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 +1616,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 +1625,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 +1669,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 +1693,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 +1701,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 +1712,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 +1757,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 +1768,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 +1822,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 +1900,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 +1937,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 +2279,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 +2305,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 +2353,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 +2370,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") +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 idx = 0; + 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 +2607,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 +2625,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_memory.c b/bgpd/bgp_memory.c index 3df40fa87a..37054ce425 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -101,6 +101,7 @@ DEFINE_MTYPE(BGPD, BGP_DAMP_ARRAY, "BGP Dampening array") DEFINE_MTYPE(BGPD, BGP_REGEXP, "BGP regexp") DEFINE_MTYPE(BGPD, BGP_AGGREGATE, "BGP aggregate") DEFINE_MTYPE(BGPD, BGP_ADDR, "BGP own address") +DEFINE_MTYPE(BGPD, TIP_ADDR, "BGP own tunnel-ip address") DEFINE_MTYPE(BGPD, BGP_REDIST, "BGP redistribution") DEFINE_MTYPE(BGPD, BGP_FILTER_NAME, "BGP Filter Information") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 152cfaeaf2..35b83a0401 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -97,6 +97,7 @@ DECLARE_MTYPE(BGP_DAMP_ARRAY) DECLARE_MTYPE(BGP_REGEXP) DECLARE_MTYPE(BGP_AGGREGATE) DECLARE_MTYPE(BGP_ADDR) +DECLARE_MTYPE(TIP_ADDR) DECLARE_MTYPE(BGP_REDIST) DECLARE_MTYPE(BGP_FILTER_NAME) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 63a84684bb..69c4ee1b67 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -88,11 +88,86 @@ static void bgp_nexthop_cache_reset(struct bgp_table *table) } } -/* BGP own address structure */ -struct bgp_addr { - struct in_addr addr; - int refcnt; -}; +static void *bgp_tip_hash_alloc(void *p) +{ + const struct in_addr *val = (const struct in_addr *)p; + struct tip_addr *addr; + + addr = XMALLOC(MTYPE_TIP_ADDR, sizeof(struct tip_addr)); + addr->refcnt = 0; + addr->addr.s_addr = val->s_addr; + + return addr; +} + +static void bgp_tip_hash_free(void *addr) +{ + XFREE(MTYPE_TIP_ADDR, addr); +} + +static unsigned int bgp_tip_hash_key_make(void *p) +{ + const struct tip_addr *addr = p; + + return jhash_1word(addr->addr.s_addr, 0); +} + +static int bgp_tip_hash_cmp(const void *p1, const void *p2) +{ + const struct tip_addr *addr1 = p1; + const struct tip_addr *addr2 = p2; + + return addr1->addr.s_addr == addr2->addr.s_addr; +} + +void bgp_tip_hash_init(struct bgp *bgp) +{ + bgp->tip_hash = hash_create(bgp_tip_hash_key_make, + bgp_tip_hash_cmp, NULL); +} + +void bgp_tip_hash_destroy(struct bgp *bgp) +{ + if (bgp->tip_hash == NULL) + return; + hash_clean(bgp->tip_hash, bgp_tip_hash_free); + hash_free(bgp->tip_hash); + bgp->tip_hash = NULL; +} + +void bgp_tip_add(struct bgp *bgp, struct in_addr *tip) +{ + struct tip_addr tmp; + struct tip_addr *addr; + + tmp.addr = *tip; + + addr = hash_get(bgp->tip_hash, &tmp, bgp_tip_hash_alloc); + if (!addr) + return; + + addr->refcnt++; +} + +void bgp_tip_del(struct bgp *bgp, struct in_addr *tip) +{ + struct tip_addr tmp; + struct tip_addr *addr; + + tmp.addr = *tip; + + addr = hash_lookup(bgp->tip_hash, &tmp); + /* may have been deleted earlier by bgp_interface_down() */ + if (addr == NULL) + return; + + addr->refcnt--; + + if (addr->refcnt == 0) { + hash_release(bgp->tip_hash, addr); + XFREE(MTYPE_TIP_ADDR, addr); + } +} static void *bgp_address_hash_alloc(void *p) { @@ -304,6 +379,7 @@ void bgp_connected_delete(struct bgp *bgp, struct connected *ifc) int bgp_nexthop_self(struct bgp *bgp, struct in_addr nh_addr) { struct bgp_addr tmp, *addr; + struct tip_addr tmp_tip, *tip; tmp.addr = nh_addr; @@ -311,6 +387,11 @@ int bgp_nexthop_self(struct bgp *bgp, struct in_addr nh_addr) if (addr) return 1; + tmp_tip.addr = nh_addr; + tip = hash_lookup(bgp->tip_hash, &tmp_tip); + if (tip) + return 1; + return 0; } diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 37dad577c2..b482778fdf 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -66,6 +66,18 @@ struct bgp_nexthop_cache { struct bgp *bgp; }; +/* BGP own address structure */ +struct bgp_addr { + struct in_addr addr; + int refcnt; +}; + +/* Own tunnel-ip address structure */ +struct tip_addr { + struct in_addr addr; + int refcnt; +}; + extern int bgp_nexthop_lookup(afi_t, struct peer *peer, struct bgp_info *, int *, int *); extern void bgp_connected_add(struct bgp *bgp, struct connected *c); @@ -82,5 +94,9 @@ extern void bgp_scan_finish(struct bgp *bgp); extern void bgp_scan_vty_init(void); extern void bgp_address_init(struct bgp *bgp); extern void bgp_address_destroy(struct bgp *bgp); +extern void bgp_tip_add(struct bgp *bgp, struct in_addr *tip); +extern void bgp_tip_del(struct bgp *bgp, struct in_addr *tip); +extern void bgp_tip_hash_init(struct bgp *bgp); +extern void bgp_tip_hash_destroy(struct bgp *bgp); #endif /* _QUAGGA_BGP_NEXTHOP_H */ diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 3a7a60b14d..a0fc10c745 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -482,6 +482,34 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) evaluate_paths(bnc); } +/* + * Cleanup nexthop registration and status information for BGP nexthops + * pertaining to this VRF. This is invoked upon VRF deletion. + */ +void bgp_cleanup_nexthops(struct bgp *bgp) +{ + afi_t afi; + struct bgp_node *rn; + struct bgp_nexthop_cache *bnc; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + if (!bgp->nexthop_cache_table[afi]) + continue; + + for (rn = bgp_table_top(bgp->nexthop_cache_table[afi]); rn; + rn = bgp_route_next(rn)) { + bnc = rn->info; + if (!bnc) + continue; + + /* Clear relevant flags. */ + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + } + } +} + /** * make_prefix - make a prefix structure from the path (essentially * path's node. diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index f649bb2259..4b297f410c 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -66,4 +66,10 @@ void bgp_unlink_nexthop_by_peer(struct peer *); */ extern void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer); +/* + * Cleanup nexthop registration and status information for BGP nexthops + * pertaining to this VRF. This is invoked upon VRF deletion. + */ +extern void bgp_cleanup_nexthops(struct bgp *bgp); + #endif /* _BGP_NHT_H */ diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 3ee865e3ba..5c9ba89a57 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -1271,7 +1271,6 @@ void bgp_open_capability(struct stream *s, struct peer *peer) as_t local_as; u_int32_t restart_time; u_char afi_safi_count = 0; - struct utsname names; int adv_addpath_tx = 0; /* Remember current pointer for Opt Parm Len. */ @@ -1441,8 +1440,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer) } /* Hostname capability */ - uname(&names); - if (names.nodename[0] != '\0') { + if (cmd_hostname_get()) { SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_ADV); stream_putc(s, BGP_OPEN_OPT_CAP); rcapp = stream_get_endp(s); /* Ptr to length placeholder */ @@ -1450,26 +1448,21 @@ void bgp_open_capability(struct stream *s, struct peer *peer) stream_putc(s, CAPABILITY_CODE_FQDN); capp = stream_get_endp(s); stream_putc(s, 0); /* dummy len for now */ - len = strlen(names.nodename); + len = strlen(cmd_hostname_get()); if (len > BGP_MAX_HOSTNAME) len = BGP_MAX_HOSTNAME; stream_putc(s, len); - stream_put(s, names.nodename, len); -#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME - if ((names.domainname[0] != '\0') - && (strcmp(names.domainname, "(none)") != 0)) { - len = strlen(names.domainname); + stream_put(s, cmd_hostname_get(), len); + if (cmd_domainname_get()) { + len = strlen(cmd_domainname_get()); if (len > BGP_MAX_HOSTNAME) len = BGP_MAX_HOSTNAME; stream_putc(s, len); - stream_put(s, names.domainname, len); + stream_put(s, cmd_domainname_get(), len); } else -#endif - { stream_putc(s, 0); /* 0 length */ - } /* Set the lengths straight */ len = stream_get_endp(s) - rcapp - 1; @@ -1478,14 +1471,10 @@ void bgp_open_capability(struct stream *s, struct peer *peer) stream_putc_at(s, capp, len); if (bgp_debug_neighbor_events(peer)) -#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME zlog_debug( "%s Sending hostname cap with hn = %s, dn = %s", - peer->host, names.nodename, names.domainname); -#else - zlog_debug("%s Sending hostname cap with hn = %s", - peer->host, names.nodename); -#endif + peer->host, cmd_hostname_get(), + cmd_domainname_get()); } /* Sending base graceful-restart capability irrespective of the config diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 288271e5a1..b18ed9c71f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2042,9 +2042,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, /* Do we need to allocate or free labels? * Right now, since we only deal with per-prefix labels, it is not - * necessary - * to do this upon changes to best path except of the label index - * changes. + * necessary to do this upon changes to best path except if the label + * index changes */ if (bgp->allocate_mpls_labels[afi][safi]) { if (new_select) { @@ -2425,7 +2424,7 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, /* Unconditionally remove the route from the RIB, without taking * damping into consideration (eg, because the session went down) */ -static void bgp_rib_remove(struct bgp_node *rn, struct bgp_info *ri, +void bgp_rib_remove(struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, afi_t afi, safi_t safi) { bgp_aggregate_decrement(peer->bgp, &rn->p, ri, afi, safi); @@ -6270,46 +6269,65 @@ 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; char buf[BUFSIZ]; if (p->family == AF_INET) { - len = vty_out(vty, "%s", - inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ)); - destination = ntohl(p->u.prefix4.s_addr); + if (!json) { + len = vty_out(vty, "%s", + inet_ntop(p->family, &p->u.prefix, buf, + BUFSIZ)); + destination = ntohl(p->u.prefix4.s_addr); - if ((IN_CLASSC(destination) && p->prefixlen == 24) - || (IN_CLASSB(destination) && p->prefixlen == 16) - || (IN_CLASSA(destination) && p->prefixlen == 8) - || p->u.prefix4.s_addr == 0) { - /* When mask is natural, mask is not displayed. */ - } else - len += vty_out(vty, "/%d", p->prefixlen); + if ((IN_CLASSC(destination) && p->prefixlen == 24) + || (IN_CLASSB(destination) && p->prefixlen == 16) + || (IN_CLASSA(destination) && p->prefixlen == 8) + || p->u.prefix4.s_addr == 0) { + /* When mask is natural, + mask is not displayed. */ + } else + len += vty_out(vty, "/%d", p->prefixlen); + } else { + json_object_string_add(json, "prefix", + inet_ntop(p->family, + &p->u.prefix, buf, + BUFSIZ)); + json_object_int_add(json, "prefixLen", p->prefixlen); + } } else if (p->family == AF_ETHERNET) { prefix2str(p, buf, PREFIX_STRLEN); 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 - len = vty_out(vty, "%s/%d", - inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), - p->prefixlen); + } else { + if (!json) + 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 { @@ -6413,9 +6431,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 */ @@ -6711,7 +6731,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) { @@ -6844,7 +6864,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, " "); } @@ -6959,7 +6979,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, " "); @@ -7028,7 +7048,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, " "); } @@ -7097,7 +7117,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, " "); } @@ -8380,6 +8400,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; @@ -8402,9 +8423,8 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, 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); + prefix2str(p, prefix_str, + sizeof(prefix_str))); } else { #if defined(HAVE_CUMULUS) if (safi == SAFI_EVPN) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 55f812d4a0..1767494ac1 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -294,6 +294,8 @@ static inline int bgp_fibupd_safi(safi_t safi) } /* Prototypes. */ +extern void bgp_rib_remove(struct bgp_node *rn, struct bgp_info *ri, + struct peer *peer, afi_t afi, safi_t safi); extern void bgp_process_queue_init(void); extern void bgp_route_init(void); extern void bgp_route_finish(void); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index f040ed9a08..761c586415 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6424,6 +6424,59 @@ DEFUN (show_bgp_vrfs, return CMD_SUCCESS; } +static void show_address_entry(struct hash_backet *backet, void *args) +{ + struct vty *vty = (struct vty *) args; + struct bgp_addr *addr = (struct bgp_addr *) backet->data; + + vty_out(vty, "addr: %s, count: %d\n", + inet_ntoa(addr->addr), + addr->refcnt); +} + +static void show_tip_entry(struct hash_backet *backet, void *args) +{ + struct vty *vty = (struct vty *)args; + struct tip_addr *tip = (struct tip_addr *) backet->data; + + vty_out(vty, "addr: %s, count: %d\n", + inet_ntoa(tip->addr), + tip->refcnt); +} + +static void bgp_show_martian_nexthops(struct vty *vty, struct bgp *bgp) +{ + vty_out(vty, "self nexthop database:\n"); + hash_iterate(bgp->address_hash, + (void (*)(struct hash_backet *, void *))show_address_entry, + vty); + + vty_out(vty, "Tunnel-ip database:\n"); + hash_iterate(bgp->tip_hash, + (void (*)(struct hash_backet *, void *))show_tip_entry, + vty); +} + +DEFUN (show_bgp_martian_nexthop_db, + show_bgp_martian_nexthop_db_cmd, + "show bgp martian next-hop", + SHOW_STR + BGP_STR + "martian next-hops\n" + "martian next-hop database\n") +{ + struct bgp *bgp = NULL; + + bgp = bgp_get_default(); + if (!bgp) { + vty_out(vty, "%% No BGP process is configured\n"); + return CMD_WARNING; + } + bgp_show_martian_nexthops(vty, bgp); + + return CMD_SUCCESS; +} + DEFUN (show_bgp_memory, show_bgp_memory_cmd, "show [ip] bgp memory", @@ -7528,6 +7581,12 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, "defaultNotSent"); } + if (afi == AFI_L2VPN && safi == SAFI_EVPN) { + if (p->bgp->advertise_all_vni) + json_object_boolean_true_add(json_addr, + "advertiseAllVnis"); + } + if (filter->plist[FILTER_IN].name || filter->dlist[FILTER_IN].name || filter->aslist[FILTER_IN].name @@ -7793,6 +7852,12 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, vty_out(vty, " default not sent\n"); } + /* advertise-vni-all */ + if (afi == AFI_L2VPN && safi == SAFI_EVPN) { + if (p->bgp->advertise_all_vni) + vty_out(vty, " advertise-all-vni\n"); + } + if (filter->plist[FILTER_IN].name || filter->dlist[FILTER_IN].name || filter->aslist[FILTER_IN].name @@ -8577,6 +8642,46 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, json_cap, "multiprotocolExtensions", json_multi); + /* Hostname capabilities */ + json_object *json_hname = NULL; + + json_hname = json_object_new_object(); + + if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_ADV)) { + json_object_string_add( + json_hname, + "advHostName", + bgp->peer_self->hostname ? + bgp->peer_self->hostname + : "n/a"); + json_object_string_add( + json_hname, + "advDomainName", + bgp->peer_self->domainname ? + bgp->peer_self->domainname + : "n/a"); + } + + + if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_RCV)) { + json_object_string_add( + json_hname, + "rcvHostName", + p->hostname ? + p->hostname : + "n/a"); + json_object_string_add( + json_hname, + "rcvDomainName", + p->domainname ? + p->domainname : + "n/a"); + } + + json_object_object_add(json_cap, + "hostName", + json_hname); + /* Gracefull Restart */ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || CHECK_FLAG(p->cap, @@ -8910,25 +9015,35 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, } /* Hostname capability */ - if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_ADV) - || CHECK_FLAG(p->cap, - PEER_CAP_HOSTNAME_RCV)) { + vty_out(vty, + " Hostname Capability:"); + + if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_ADV)) { vty_out(vty, - " Hostname Capability:"); - if (CHECK_FLAG(p->cap, - PEER_CAP_HOSTNAME_ADV)) - vty_out(vty, " advertised"); - if (CHECK_FLAG(p->cap, - PEER_CAP_HOSTNAME_RCV)) - vty_out(vty, " %sreceived", - CHECK_FLAG( - p->cap, - PEER_CAP_HOSTNAME_ADV) - ? "and " - : ""); - vty_out(vty, "\n"); + " advertised (name: %s,domain name: %s)", + bgp->peer_self->hostname ? + bgp->peer_self->hostname + : "n/a", + bgp->peer_self->domainname ? + bgp->peer_self->domainname + : "n/a"); + } else { + vty_out(vty, " not advertised"); } + if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_RCV)) { + vty_out(vty, + " received (name: %s,domain name: %s)", + p->hostname ? + p->hostname : "n/a", + p->domainname ? + p->domainname : "n/a"); + } else { + vty_out(vty, " not received"); + } + + vty_out(vty, "\n"); + /* Gracefull Restart */ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || CHECK_FLAG(p->cap, @@ -12264,6 +12379,9 @@ void bgp_vty_init(void) /* "show [ip] bgp memory" commands. */ install_element(VIEW_NODE, &show_bgp_memory_cmd); + /* "show bgp martian next-hop" */ + install_element(VIEW_NODE, &show_bgp_martian_nexthop_db_cmd); + /* "show [ip] bgp views" commands. */ install_element(VIEW_NODE, &show_bgp_views_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 92121f6aef..3fc3fe85f3 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2816,6 +2816,21 @@ static struct bgp *bgp_create(as_t *as, const char *name, XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->host); bgp->peer_self->host = XSTRDUP(MTYPE_BGP_PEER_HOST, "Static announcement"); + if (bgp->peer_self->hostname != NULL) { + XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname); + bgp->peer_self->hostname = NULL; + } + if (cmd_hostname_get()) + bgp->peer_self->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, + cmd_hostname_get()); + + if (bgp->peer_self->domainname != NULL) { + XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname); + bgp->peer_self->domainname = NULL; + } + if (cmd_domainname_get()) + bgp->peer_self->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, + cmd_domainname_get()); bgp->peer = list_new(); bgp->peer->cmp = (int (*)(void *, void *))peer_cmp; bgp->peerhash = hash_create(peer_hash_key_make, peer_hash_same, NULL); @@ -2988,6 +3003,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp = bgp_create(as, name, inst_type); bgp_router_id_set(bgp, &bgp->router_id_zebra); bgp_address_init(bgp); + bgp_tip_hash_init(bgp); bgp_scan_init(bgp); *bgp_val = bgp; @@ -3069,6 +3085,9 @@ void bgp_instance_down(struct bgp *bgp) /* Purge network and redistributed routes. */ bgp_purge_static_redist_routes(bgp); + + /* Cleanup registered nexthops (flags) */ + bgp_cleanup_nexthops(bgp); } /* Delete BGP instance. */ @@ -3200,6 +3219,7 @@ void bgp_free(struct bgp *bgp) bgp_scan_finish(bgp); bgp_address_destroy(bgp); + bgp_tip_hash_destroy(bgp); bgp_evpn_cleanup(bgp); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 5ede9ba13d..0d7092c98c 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -318,6 +318,10 @@ struct bgp { struct hash *address_hash; + /* DB for all local tunnel-ips - used mainly for martian checks + Currently it only has all VxLan tunnel IPs*/ + struct hash *tip_hash; + /* Static route configuration. */ struct bgp_table *route[AFI_MAX][SAFI_MAX]; diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 6fa7988304..17b043444c 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -146,6 +146,6 @@ void dynhn_print_all(struct vty *vty) } vty_out(vty, " * %s %s\n", sysid_print(isis->sysid), - unix_hostname()); + cmd_hostname_get()); return; } diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index e324a303d8..2e1e8e5fc3 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -607,7 +607,7 @@ static void lspid_print(u_char *lsp_id, u_char *trg, char dynhost, char frag) if (dyn) sprintf((char *)id, "%.14s", dyn->hostname); else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) - sprintf((char *)id, "%.14s", unix_hostname()); + sprintf((char *)id, "%.14s", cmd_hostname_get()); else memcpy(id, sysid_print(lsp_id), 15); if (frag) @@ -887,9 +887,9 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) } /* Dynamic Hostname */ if (area->dynhostname) { - isis_tlvs_set_dynamic_hostname(lsp->tlvs, unix_hostname()); + isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get()); lsp_debug("ISIS (%s): Adding dynamic hostname '%s'", - area->area_tag, unix_hostname()); + area->area_tag, cmd_hostname_get()); } else { lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)", area->area_tag); diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index e8888a5f5b..0a1d9aaa1a 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -431,24 +431,6 @@ struct in_addr newprefix2inaddr(u_char *prefix_start, u_char prefix_masklen) return new_prefix; } -/* - * Returns host.name if any, otherwise - * it returns the system hostname. - */ -const char *unix_hostname(void) -{ - static struct utsname names; - const char *hostname; - - hostname = host.name; - if (!hostname) { - uname(&names); - hostname = names.nodename; - } - - return hostname; -} - /* * Returns the dynamic hostname associated with the passed system ID. * If no dynamic hostname found then returns formatted system ID. @@ -462,7 +444,7 @@ const char *print_sys_hostname(const u_char *sysid) /* For our system ID return our host name */ if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0) - return unix_hostname(); + return cmd_hostname_get(); dyn = dynhn_find_by_id(sysid); if (dyn) diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h index 5a19a1ffa0..b81db34df6 100644 --- a/isisd/isis_misc.h +++ b/isisd/isis_misc.h @@ -56,7 +56,6 @@ void zlog_dump_data(void *data, int len); * misc functions */ unsigned long isis_jitter(unsigned long timer, unsigned long jitter); -const char *unix_hostname(void); /* * macros diff --git a/isisd/isisd.c b/isisd/isisd.c index 3d39a1ed52..bdc1d836db 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -1441,7 +1441,7 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level) lsp = lsp_search( lspid, area->lspdb[level]); - } else if (strncmp(unix_hostname(), + } else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) { memcpy(lspid, isis->sysid, diff --git a/lib/command.c b/lib/command.c index c86025a3bd..24095de4f0 100644 --- a/lib/command.c +++ b/lib/command.c @@ -125,6 +125,23 @@ vector cmdvec = NULL; /* Host information structure. */ struct host host; +/* + * Returns host.name if any, otherwise + * it returns the system hostname. + */ +const char *cmd_hostname_get(void) +{ + return host.name; +} + +/* + * Returns unix domainname + */ +const char *cmd_domainname_get(void) +{ + return host.domainname; +} + /* Standard command node structures. */ static struct cmd_node auth_node = { AUTH_NODE, "Password: ", @@ -475,8 +492,8 @@ static char *zencrypt(const char *passwd) /* This function write configuration of this host. */ static int config_write_host(struct vty *vty) { - if (host.name) - vty_out(vty, "hostname %s\n", host.name); + if (cmd_hostname_get()) + vty_out(vty, "hostname %s\n", cmd_hostname_get()); if (host.encrypt) { if (host.password_encrypt) @@ -1411,7 +1428,7 @@ DEFUN (show_version, "Displays zebra version\n") { vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION, - host.name ? host.name : ""); + cmd_hostname_get() ? cmd_hostname_get() : ""); vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO); vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS); @@ -1745,6 +1762,40 @@ DEFUN (show_startup_config, return CMD_SUCCESS; } +int cmd_domainname_set(const char *domainname) +{ + XFREE(MTYPE_HOST, host.domainname); + host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL; + return CMD_SUCCESS; +} + +/* Hostname configuration */ +DEFUN (config_domainname, + domainname_cmd, + "domainname WORD", + "Set system's domain name\n" + "This system's domain name\n") +{ + struct cmd_token *word = argv[1]; + + if (!isalpha((int)word->arg[0])) { + vty_out(vty, "Please specify string starting with alphabet\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return cmd_domainname_set(word->arg); +} + +DEFUN (config_no_domainname, + no_domainname_cmd, + "no domainname [DOMAINNAME]", + NO_STR + "Reset system's domain name\n" + "domain name of this router\n") +{ + return cmd_domainname_set(NULL); +} + int cmd_hostname_set(const char *hostname) { XFREE(MTYPE_HOST, host.name); @@ -2515,9 +2566,12 @@ void install_default(enum node_type node) * terminal = -1 -- watchfrr / no logging, but minimal config control */ void cmd_init(int terminal) { + struct utsname names; + if (array_size(node_names) != NODE_TYPE_MAX) assert(!"Update the CLI node description array!"); + uname(&names); qobj_init(); varhandlers = list_new(); @@ -2526,7 +2580,15 @@ void cmd_init(int terminal) cmdvec = vector_init(VECTOR_MIN_SIZE); /* Default host value settings. */ - host.name = NULL; + host.name = XSTRDUP(MTYPE_HOST, names.nodename); +#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME + if ((strcmp(names.domainname, "(none)") == 0)) + host.domainname = NULL; + else + host.domainname = XSTRDUP(MTYPE_HOST, names.domainname); +#else + host.domainname = NULL; +#endif host.password = NULL; host.enable = NULL; host.logfile = NULL; @@ -2579,6 +2641,8 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &hostname_cmd); install_element(CONFIG_NODE, &no_hostname_cmd); + install_element(CONFIG_NODE, &domainname_cmd); + install_element(CONFIG_NODE, &no_domainname_cmd); install_element(CONFIG_NODE, &frr_version_defaults_cmd); install_element(CONFIG_NODE, &debug_memstats_cmd); @@ -2644,6 +2708,8 @@ void cmd_terminate() if (host.name) XFREE(MTYPE_HOST, host.name); + if (host.domainname) + XFREE(MTYPE_HOST, host.domainname); if (host.password) XFREE(MTYPE_HOST, host.password); if (host.password_encrypt) diff --git a/lib/command.h b/lib/command.h index 8f12e2aabd..1c6938523c 100644 --- a/lib/command.h +++ b/lib/command.h @@ -41,6 +41,9 @@ struct host { /* Host name of this router. */ char *name; + /* Domainname of this router */ + char *domainname; + /* Password for vty interface. */ char *password; char *password_encrypt; @@ -398,7 +401,10 @@ extern void cmd_terminate(void); extern void cmd_exit(struct vty *vty); extern int cmd_list_cmds(struct vty *vty, int do_permute); +extern int cmd_domainname_set(const char *domainname); extern int cmd_hostname_set(const char *hostname); +extern const char *cmd_hostname_get(void); +extern const char *cmd_domainname_get(void); /* NOT safe for general use; call this only if DEV_BUILD! */ extern void grammar_sandbox_init(void); diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index 89b0993d1d..264c7c48f0 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -50,6 +50,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); host.name = strdup("test"); + host.domainname = strdup("testdomainname"); vty_init(master); memory_init(); diff --git a/lib/vty.c b/lib/vty.c index 59a8825357..cec5a916f3 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -250,16 +250,9 @@ void vty_hello(struct vty *vty) /* Put out prompt and wait input from user. */ static void vty_prompt(struct vty *vty) { - struct utsname names; - const char *hostname; - if (vty->type == VTY_TERM) { - hostname = host.name; - if (!hostname) { - uname(&names); - hostname = names.nodename; - } - vty_out(vty, cmd_prompt(vty->node), hostname); + vty_out(vty, cmd_prompt(vty->node), + cmd_hostname_get()); } } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 85cbcae4df..34d66735df 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2891,21 +2891,9 @@ void vtysh_readline_init(void) char *vtysh_prompt(void) { - static struct utsname names; static char buf[100]; - const char *hostname; - extern struct host host; - - hostname = host.name; - - if (!hostname) { - if (!names.nodename[0]) - uname(&names); - hostname = names.nodename; - } - - snprintf(buf, sizeof buf, cmd_prompt(vty->node), hostname); + snprintf(buf, sizeof buf, cmd_prompt(vty->node), cmd_hostname_get()); return buf; } diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 58772ec549..d7e79d6b2c 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -423,10 +423,9 @@ int vtysh_read_config(const char *config_default_dir) void vtysh_config_write() { char line[81]; - extern struct host host; - if (host.name) { - sprintf(line, "hostname %s", host.name); + if (cmd_hostname_get()) { + sprintf(line, "hostname %s", cmd_hostname_get()); vtysh_config_parse_line(NULL, line); } if (vtysh_write_integrated == WRITE_INTEGRATED_NO) diff --git a/zebra/interface.c b/zebra/interface.c index c17e408ea0..a03b7f9615 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -873,7 +873,8 @@ void if_up(struct interface *ifp) link_if = ifp; zebra_vxlan_svi_up(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = zif->link; + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + zif->link_ifindex); if (link_if) zebra_vxlan_svi_up(ifp, link_if); } @@ -901,7 +902,8 @@ void if_down(struct interface *ifp) link_if = ifp; zebra_vxlan_svi_down(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = zif->link; + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + zif->link_ifindex); if (link_if) zebra_vxlan_svi_down(ifp, link_if); } diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 0076e91569..3f9782d442 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2014,7 +2014,8 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, * itself */ if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = zif->link; + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + zif->link_ifindex); if (!link_if) return 0; } else if (IS_ZEBRA_IF_BRIDGE(ifp)) diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 5d059a4502..3d505857c2 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -224,12 +224,19 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, zif->brslave_info.bridge_ifindex = bridge_ifindex; /* Set up or remove link with master */ - if (bridge_ifindex != IFINDEX_INTERNAL) + if (bridge_ifindex != IFINDEX_INTERNAL) { zebra_l2_map_slave_to_bridge(&zif->brslave_info); - else if (old_bridge_ifindex != IFINDEX_INTERNAL) + /* In the case of VxLAN, invoke the handler for EVPN. */ + if (zif->zif_type == ZEBRA_IF_VXLAN) + zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); + } else if (old_bridge_ifindex != IFINDEX_INTERNAL) { + /* + * In the case of VxLAN, invoke the handler for EVPN. + * Note that this should be done *prior* + * to unmapping the interface from the bridge. + */ + if (zif->zif_type == ZEBRA_IF_VXLAN) + zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); zebra_l2_unmap_slave_from_bridge(&zif->brslave_info); - - /* In the case of VxLAN, invoke the handler for EVPN. */ - if (zif->zif_type == ZEBRA_IF_VXLAN) - zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); + } } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index f99c16ae91..32c0e20a91 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -204,13 +204,18 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) if (json == NULL) { vty_out(vty, " Remote VTEP: %s", inet_ntoa(n->r_vtep_ip)); - vty_out(vty, " State: %s", IS_ZEBRA_NEIGH_ACTIVE(n) - ? "Active" - : "Inactive"); } else json_object_string_add(json, "remoteVtep", inet_ntoa(n->r_vtep_ip)); } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (!json) { + vty_out(vty, "\n"); + vty_out(vty, " State: %s", + IS_ZEBRA_NEIGH_ACTIVE(n) ? "Active" + : "Inactive"); + } + } if (json == NULL) vty_out(vty, "\n"); } @@ -384,8 +389,8 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt) } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { vty_out(vty, " Auto Mac "); } - vty_out(vty, " ARP ref: %u\n", mac->neigh_refcnt); + vty_out(vty, "\n"); /* print all the associated neigh */ vty_out(vty, " Neighbors:\n"); if (!listcount(mac->neigh_list)) @@ -1355,9 +1360,6 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, memcpy(&n->emac, macaddr, ETH_ALEN); n->ifindex = ifp->ifindex; - /* We have a neigh associated to mac increment the refcnt*/ - mac->neigh_refcnt++; - if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP", @@ -1431,13 +1433,22 @@ static void zvni_gw_macip_del_for_vni_hash(struct hash_backet *backet, struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; + struct interface *ifp; /* Add primary SVI MAC*/ zvni = (zebra_vni_t *)backet->data; if (!zvni) return; - zif = zvni->vxlan_if->info; + ifp = zvni->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + zl2_info = zif->l2info.vxl; vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, @@ -1464,6 +1475,7 @@ static void zvni_gw_macip_add_for_vni_hash(struct hash_backet *backet, struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; + struct interface *ifp = NULL; zvni = (zebra_vni_t *)backet->data; if (!zvni) @@ -1472,7 +1484,14 @@ static void zvni_gw_macip_add_for_vni_hash(struct hash_backet *backet, if (!advertise_gw_macip_enabled(zvrf, zvni)) return; - zif = zvni->vxlan_if->info; + ifp = zvni->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; zl2_info = zif->l2info.vxl; vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, @@ -1758,6 +1777,9 @@ static zebra_vni_t *zvni_map_svi(struct interface *ifp, struct interface *br_if) vlanid_t vid = 0; zebra_vni_t *zvni; + if (!br_if) + return NULL; + /* Make sure the linked interface is a bridge. */ if (!IS_ZEBRA_IF_BRIDGE(br_if)) return NULL; @@ -1827,6 +1849,10 @@ static struct interface *zvni_map_to_svi(struct zebra_vrf *zvrf, vlanid_t vid, struct zebra_l2info_vlan *vl; u_char bridge_vlan_aware; + /* Defensive check, caller expected to invoke only with valid bridge. */ + if (!br_if) + return NULL; + /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); @@ -1943,10 +1969,8 @@ static void zvni_install_mac_hash(struct hash_backet *backet, void *ctxt) static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac, int uninstall) { - if (mac->neigh_refcnt) - mac->neigh_refcnt--; - - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO) || mac->neigh_refcnt > 0) + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO) || + !list_isempty(mac->neigh_list)) return; if (uninstall) @@ -2853,7 +2877,7 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp, /* see if the AUTO mac needs to be deleted */ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) - || !listcount(zmac->neigh_list)) + && !listcount(zmac->neigh_list)) zvni_mac_del(zvni, zmac); return 0; @@ -2872,10 +2896,9 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, zebra_vni_t *zvni; zebra_neigh_t *n; struct zebra_vrf *zvrf; - zebra_mac_t *zmac; + zebra_mac_t *zmac, *old_zmac; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - int send_upd = 1, send_del = 0; /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. @@ -2930,19 +2953,35 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, if (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0) { - if (n->ifindex == ifp->ifindex) - /* we're not interested in whatever has - * changed. */ - return 0; - /* client doesn't care about a purely local - * change. */ - send_upd = 0; - } else - /* If the MAC has changed, issue a delete first - * as this means a - * different MACIP route. + /* Update any params and return - client doesn't + * care about a purely local change. */ - send_del = 1; + n->ifindex = ifp->ifindex; + return 0; + } + + /* If the MAC has changed, + * need to issue a delete first + * as this means a different MACIP route. + * Also, need to do some unlinking/relinking. + */ + zvni_neigh_send_del_to_client(zvrf, zvni->vni, + &n->ip, &n->emac, + 0); + old_zmac = zvni_mac_lookup(zvni, &n->emac); + if (old_zmac) { + listnode_delete(old_zmac->neigh_list, + n); + zvni_deref_ip2mac(zvni, old_zmac, 0); + } + + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + memcpy(&n->emac, macaddr, ETH_ALEN); + + /* Link to new MAC */ + listnode_add_sort(zmac->neigh_list, n); } else if (ext_learned) /* The neighbor is remote and that is the notification we got. */ @@ -2954,6 +2993,8 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, { UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); n->r_vtep_ip.s_addr = 0; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; } } else { n = zvni_neigh_add(zvni, ip, macaddr); @@ -2965,17 +3006,11 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, ifp->name, ifp->ifindex, zvni->vni); return -1; } + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; } - /* Issue delete for older info, if needed. */ - if (send_del) - zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac, - 0); - - /* Set "local" forwarding info. */ - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->ifindex = ifp->ifindex; - /* Before we program this in BGP, we need to check if MAC is locally * learnt as well */ if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { @@ -2989,23 +3024,20 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, return 0; } - /* Inform BGP if required. */ - if (send_upd) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%u: neigh %s (MAC %s) is now ACTIVE on VNI %u", - ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni); + /* Inform BGP. */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: neigh %s (MAC %s) is now ACTIVE on VNI %u", + ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + zvni->vni); - ZEBRA_NEIGH_SET_ACTIVE(n); - return zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip, - macaddr, 0); - } - - return 0; + ZEBRA_NEIGH_SET_ACTIVE(n); + return zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip, + macaddr, 0); } + /* * Handle message from client to delete a remote MACIP for a VNI. */ @@ -3023,6 +3055,8 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, u_short l = 0, ipa_len; char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; s = client->ibuf; @@ -3065,12 +3099,18 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, zvrf_id(zvrf), vni); continue; } - if (!zvni->vxlan_if) { + ifp = zvni->vxlan_if; + if (!ifp) { zlog_err( "VNI %u hash %p doesn't have intf upon remote MACIP DEL", vni, zvni); continue; } + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + continue; /* The remote VTEP specified is normally expected to exist, but * it is @@ -3083,12 +3123,6 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, if (!zvni_vtep_find(zvni, &vtep_ip)) continue; - /* If the local VxLAN interface is not up (should be a transient - * event), there's nothing more to do. - */ - if (!if_is_operative(zvni->vxlan_if)) - continue; - mac = zvni_mac_lookup(zvni, &macaddr); if (ipa_len) n = zvni_neigh_lookup(zvni, &ip); @@ -3132,7 +3166,7 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, zvni_process_neigh_on_remote_mac_del(zvrf, zvni, mac); - if (!mac->neigh_refcnt) { + if (list_isempty(mac->neigh_list)) { zvni_mac_uninstall(zvni, mac, 0); zvni_mac_del(zvni, mac); } else @@ -3166,6 +3200,8 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; u_char sticky; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; assert(EVPN_ENABLED(zvrf)); @@ -3213,16 +3249,17 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, zvrf_id(zvrf), vni); continue; } - if (!zvni->vxlan_if) { + ifp = zvni->vxlan_if; + if (!ifp) { zlog_err( "VNI %u hash %p doesn't have intf upon remote MACIP add", vni, zvni); continue; } - /* If the local VxLAN interface is not up (should be a transient - * event), there's nothing more to do. - */ - if (!if_is_operative(zvni->vxlan_if)) + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) continue; /* The remote VTEP specified should normally exist, but it is @@ -3272,9 +3309,6 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, /* Is this MAC created for a MACIP? */ if (ipa_len) SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - /* Moving from local to remote, issue delete. */ - zvni_mac_uninstall(zvni, mac, 1); } /* Set "auto" and "remote" forwarding info. */ @@ -3326,16 +3360,16 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, return -1; } - /* New neighbor referring to this MAC. */ - mac->neigh_refcnt++; } else if (memcmp(&n->emac, &macaddr, sizeof(macaddr)) != 0) { - /* MAC change, update ref counts for old and new - * MAC. */ + /* MAC change, update neigh list for old and new + * mac */ old_mac = zvni_mac_lookup(zvni, &n->emac); - if (old_mac) + if (old_mac) { + listnode_delete(old_mac->neigh_list, n); zvni_deref_ip2mac(zvni, old_mac, 1); - mac->neigh_refcnt++; + } + listnode_add_sort(mac->neigh_list, n); memcpy(&n->emac, &macaddr, ETH_ALEN); } @@ -3687,6 +3721,8 @@ int zebra_vxlan_remote_vtep_del(struct zserv *client, int sock, u_short length, struct in_addr vtep_ip; zebra_vni_t *zvni; zebra_vtep_t *zvtep; + struct interface *ifp; + struct zebra_if *zif; s = client->ibuf; @@ -3713,6 +3749,18 @@ int zebra_vxlan_remote_vtep_del(struct zserv *client, int sock, u_short length, continue; } + ifp = zvni->vxlan_if; + if (!ifp) { + zlog_err("VNI %u hash %p doesn't have intf upon remote VTEP DEL", + zvni->vni, zvni); + continue; + } + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + continue; + /* If the remote VTEP does not exist, there's nothing more to * do. * Otherwise, uninstall any remote MACs pointing to this VTEP @@ -3743,6 +3791,8 @@ int zebra_vxlan_remote_vtep_add(struct zserv *client, int sock, u_short length, vni_t vni; struct in_addr vtep_ip; zebra_vni_t *zvni; + struct interface *ifp; + struct zebra_if *zif; assert(EVPN_ENABLED(zvrf)); @@ -3768,24 +3818,24 @@ int zebra_vxlan_remote_vtep_add(struct zserv *client, int sock, u_short length, zvrf_id(zvrf), vni); continue; } - if (!zvni->vxlan_if) { + + ifp = zvni->vxlan_if; + if (!ifp) { zlog_err( "VNI %u hash %p doesn't have intf upon remote VTEP ADD", zvni->vni, zvni); continue; } + zif = ifp->info; - /* If the remote VTEP already exists, or the local VxLAN - * interface is - * not up (should be a transient event), there's nothing more - * to do. - * Otherwise, add and install the entry. - */ - if (zvni_vtep_find(zvni, &vtep_ip)) + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) continue; - if (!if_is_operative(zvni->vxlan_if)) + /* If the remote VTEP already exists, + there's nothing more to do. */ + if (zvni_vtep_find(zvni, &vtep_ip)) continue; if (zvni_vtep_add(zvni, &vtep_ip) == NULL) { @@ -3839,7 +3889,11 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, if (!ifp_zif) return -1; - svi_if = ifp_zif->link; + /* + * for a MACVLAN interface the link represents the svi_if + */ + svi_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + ifp_zif->link_ifindex); if (!svi_if) { zlog_err("%u:MACVLAN %s(%u) without link information", ifp->vrf_id, ifp->name, ifp->ifindex); @@ -3847,19 +3901,39 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, } if (IS_ZEBRA_IF_VLAN(svi_if)) { + /* + * If it is a vlan aware bridge then the link gives the + * bridge information + */ + struct interface *svi_if_link = NULL; + svi_if_zif = svi_if->info; - if (svi_if_zif) - zvni = zvni_map_svi(svi_if, svi_if_zif->link); + if (svi_if_zif) { + svi_if_link = if_lookup_by_index_per_ns( + zebra_ns_lookup( + NS_DEFAULT), + svi_if_zif->link_ifindex); + zvni = zvni_map_svi(svi_if, svi_if_link); + } } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) { + /* + * If it is a vlan unaware bridge then svi is the bridge + * itself + */ zvni = zvni_map_svi(svi_if, svi_if); } } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_if *svi_if_zif = - NULL; /* Zebra daemon specific info for SVI*/ + NULL; /* Zebra daemon specific info for SVI */ + struct interface *svi_if_link = + NULL; /* link info for the SVI = bridge info */ svi_if_zif = ifp->info; - if (svi_if_zif) - zvni = zvni_map_svi(ifp, svi_if_zif->link); + svi_if_link = + if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + svi_if_zif->link_ifindex); + if (svi_if_zif && svi_if_link) + zvni = zvni_map_svi(ifp, svi_if_link); } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { zvni = zvni_map_svi(ifp, ifp); } @@ -4141,7 +4215,7 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) vxl->access_vlan, inet_ntoa(vxl->vtep_ip), zif->brslave_info.bridge_ifindex, chgflags); - /* Removed from bridge? */ + /* Removed from bridge? Cleanup and return */ if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { /* Delete from client, remove all remote VTEPs */ @@ -4150,7 +4224,11 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) zvni_neigh_del_all(zvrf, zvni, 1, 0, DEL_ALL_NEIGH); zvni_mac_del_all(zvrf, zvni, 1, 0, DEL_ALL_MAC); zvni_vtep_del_all(zvni, 1); - } else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + return 0; + } + + /* Handle other changes. */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { /* Remove all existing local neighbors and MACs for this VNI * (including from BGP) */ @@ -4268,6 +4346,7 @@ int zebra_vxlan_advertise_gw_macip(struct zserv *client, int sock, int advertise; vni_t vni = 0; zebra_vni_t *zvni = NULL; + struct interface *ifp = NULL; s = client->ibuf; advertise = stream_getc(s); @@ -4318,7 +4397,16 @@ int zebra_vxlan_advertise_gw_macip(struct zserv *client, int sock, zvni->advertise_gw_macip = advertise; - zif = zvni->vxlan_if->info; + ifp = zvni->vxlan_if; + if (!ifp) + return 0; + + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; + zl2_info = zif->l2info.vxl; vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 8539311eab..fa7d0e9457 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -114,8 +114,6 @@ struct zebra_mac_t_ { struct in_addr r_vtep_ip; } fwd_info; - u_int32_t neigh_refcnt; - /* List of neigh associated with this mac */ struct list *neigh_list; };