diff options
49 files changed, 5848 insertions, 421 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 5b917fc814..206d91bfe9 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1876,6 +1876,9 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) attr->mm_seqnum = bgp_attr_mac_mobility_seqnum(attr, &sticky); attr->sticky = sticky; + /* Extract the Rmac, if any */ + bgp_attr_rmac(attr, &attr->rmac); + return BGP_ATTR_PARSE_PROCEED; } diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index f694f01adb..1de1bee0f9 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -182,6 +182,9 @@ struct attr { /* EVPN MAC Mobility sequence number, if any. */ u_int32_t mm_seqnum; + + /* EVPN local router-mac */ + struct ethaddr rmac; }; /* rmap_change_flags definition */ diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index 300c9ddb50..eaa4e329d4 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -105,6 +105,35 @@ char *ecom_mac2str(char *ecom_mac) return prefix_mac2str((struct ethaddr *)en, NULL, 0); } +/* Fetch router-mac from extended community */ +void bgp_attr_rmac(struct attr *attr, + struct ethaddr *rmac) +{ + int i = 0; + struct ecommunity *ecom; + + ecom = attr->ecommunity; + if (!ecom || !ecom->size) + return; + + /* If there is a router mac extended community, set RMAC in attr */ + for (i = 0; i < ecom->size; i++) { + u_char *pnt = NULL; + u_char type = 0; + u_char sub_type = 0; + + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + type = *pnt++; + sub_type = *pnt++; + + if (!(type == ECOMMUNITY_ENCODE_EVPN && + sub_type == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC)) + continue; + + memcpy(rmac, pnt, ETH_ALEN); + } +} + /* * Fetch and return the sequence number from MAC Mobility extended * community, if present, else 0. diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h index 15d9e126e4..8b55cb3002 100644 --- a/bgpd/bgp_attr_evpn.h +++ b/bgpd/bgp_attr_evpn.h @@ -59,7 +59,7 @@ extern void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac); extern int bgp_build_evpn_prefix(int type, uint32_t eth_tag, struct prefix *dst); - +extern void bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac); extern u_int32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, u_char *sticky); diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index e19f516505..9caf38d569 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -695,19 +695,19 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (type == ECOMMUNITY_ENCODE_EVPN) { if (filter == ECOMMUNITY_ROUTE_TARGET) continue; - if (*pnt == ECOMMUNITY_SITE_ORIGIN) { - char macaddr[6]; + if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) { + struct ethaddr rmac; pnt++; - memcpy(&macaddr, pnt, 6); + memcpy(&rmac, pnt, ETH_ALEN); len = sprintf( str_buf + str_pnt, - "EVPN:%02x:%02x:%02x:%02x:%02x:%02x", - (uint8_t)macaddr[0], - (uint8_t)macaddr[1], - (uint8_t)macaddr[2], - (uint8_t)macaddr[3], - (uint8_t)macaddr[4], - (uint8_t)macaddr[5]); + "Rmac:%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)rmac.octet[0], + (uint8_t)rmac.octet[1], + (uint8_t)rmac.octet[2], + (uint8_t)rmac.octet[3], + (uint8_t)rmac.octet[4], + (uint8_t)rmac.octet[5]); } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { u_int32_t seqnum; diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index ca2e1a54d4..12cc425bd3 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -93,6 +93,131 @@ static int vni_hash_cmp(const void *p1, const void *p2) } /* + * Make vrf import route target hash key. + */ +static unsigned int vrf_import_rt_hash_key_make(void *p) +{ + struct vrf_irt_node *irt = p; + char *pnt = irt->rt.val; + unsigned int key = 0; + int c = 0; + + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + key += pnt[c + 4]; + key += pnt[c + 5]; + key += pnt[c + 6]; + key += pnt[c + 7]; + + return key; +} + +/* + * Comparison function for vrf import rt hash + */ +static int vrf_import_rt_hash_cmp(const void *p1, const void *p2) +{ + const struct vrf_irt_node *irt1 = p1; + const struct vrf_irt_node *irt2 = p2; + + if (irt1 == NULL && irt2 == NULL) + return 1; + + if (irt1 == NULL || irt2 == NULL) + return 0; + + return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0); +} + +/* + * Create a new vrf import_rt in default instance + */ +static struct vrf_irt_node *vrf_import_rt_new(struct ecommunity_val *rt) +{ + struct bgp *bgp_def = NULL; + struct vrf_irt_node *irt; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt new - def instance not created yet"); + return NULL; + } + + irt = XCALLOC(MTYPE_BGP_EVPN_VRF_IMPORT_RT, + sizeof(struct vrf_irt_node)); + if (!irt) + return NULL; + + irt->rt = *rt; + irt->vrfs = list_new(); + + /* Add to hash */ + if (!hash_get(bgp_def->vrf_import_rt_hash, irt, hash_alloc_intern)) { + XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt); + return NULL; + } + + return irt; +} + +/* + * Free the vrf import rt node + */ +static void vrf_import_rt_free(struct vrf_irt_node *irt) +{ + struct bgp *bgp_def = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt free - def instance not created yet"); + return; + } + + hash_release(bgp_def->vrf_import_rt_hash, irt); + XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt); +} + +/* + * Function to lookup Import RT node - used to map a RT to set of + * VNIs importing routes with that RT. + */ +static struct vrf_irt_node *lookup_vrf_import_rt(struct ecommunity_val *rt) +{ + struct bgp *bgp_def = NULL; + struct vrf_irt_node *irt; + struct vrf_irt_node tmp; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt lookup - def instance not created yet"); + return NULL; + } + + memset(&tmp, 0, sizeof(struct vrf_irt_node)); + memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE); + irt = hash_lookup(bgp_def->vrf_import_rt_hash, &tmp); + return irt; +} + +/* + * Is specified VRF present on the RT's list of "importing" VRFs? + */ +static int is_vrf_present_in_irt_vrfs(struct list *vrfs, + struct bgp *bgp_vrf) +{ + struct listnode *node = NULL, *nnode = NULL; + struct bgp *tmp_bgp_vrf = NULL; + + for (ALL_LIST_ELEMENTS(vrfs, node, nnode, tmp_bgp_vrf)) { + if (tmp_bgp_vrf == bgp_vrf) + return 1; + } + return 0; +} + +/* * Make import route target hash key. */ static unsigned int import_rt_hash_key_make(void *p) @@ -247,6 +372,57 @@ static inline void mask_ecom_global_admin(struct ecommunity_val *dst, } /* + * Map one RT to specified VRF. + * bgp_vrf = BGP vrf instance + */ +static void map_vrf_to_rt(struct bgp *bgp_vrf, + struct ecommunity_val *eval) +{ + struct vrf_irt_node *irt = NULL; + struct ecommunity_val eval_tmp; + + /* If using "automatic" RT, + * we only care about the local-admin sub-field. + * This is to facilitate using L3VNI(VRF-VNI) + * as the RT for EBGP peering too. + */ + memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_IMPORT_RT_CFGD)) + mask_ecom_global_admin(&eval_tmp, eval); + + irt = lookup_vrf_import_rt(&eval_tmp); + if (irt && irt->vrfs) + if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) + /* Already mapped. */ + return; + + if (!irt) { + irt = vrf_import_rt_new(&eval_tmp); + assert(irt); + } + + /* Add VRF to the list for this RT. */ + listnode_add(irt->vrfs, bgp_vrf); +} + +/* + * Unmap specified VRF from specified RT. If there are no other + * VRFs for this RT, then the RT hash is deleted. + * bgp_vrf: BGP VRF specific instance + */ +static void unmap_vrf_from_rt(struct bgp *bgp_vrf, + struct vrf_irt_node *irt) +{ + /* Delete VRF from list for this RT. */ + listnode_delete(irt->vrfs, bgp_vrf); + if (!listnode_head(irt->vrfs)) { + list_delete_and_null(&irt->vrfs); + vrf_import_rt_free(irt); + } +} + +/* * Map one RT to specified VNI. */ static void map_vni_to_rt(struct bgp *bgp, struct bgpevpn *vpn, @@ -301,12 +477,12 @@ static void unmap_vni_from_rt(struct bgp *bgp, struct bgpevpn *vpn, * VNIs but the same across routers (in the same AS) for a particular * VNI. */ -static void form_auto_rt(struct bgp *bgp, struct bgpevpn *vpn, struct list *rtl) +static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) { struct ecommunity_val eval; struct ecommunity *ecomadd; - encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval); + encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecomadd = ecommunity_new(); ecommunity_add_val(ecomadd, &eval); @@ -426,19 +602,75 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, } /* + * Build extended communities for EVPN prefix route. + */ +static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, + struct attr *attr) +{ + struct ecommunity ecom_encap; + struct ecommunity ecom_rmac; + struct ecommunity_val eval; + struct ecommunity_val eval_rmac; + bgp_encap_types tnl_type; + struct listnode *node, *nnode; + struct ecommunity *ecom; + struct list *vrf_export_rtl = NULL; + + /* Encap */ + tnl_type = BGP_ENCAP_TYPE_VXLAN; + memset(&ecom_encap, 0, sizeof(ecom_encap)); + encode_encap_extcomm(tnl_type, &eval); + ecom_encap.size = 1; + ecom_encap.val = (u_int8_t *)eval.val; + + /* Add Encap */ + attr->ecommunity = ecommunity_dup(&ecom_encap); + + /* Add the export RTs for L3VNI/VRF */ + vrf_export_rtl = bgp_vrf->vrf_export_rtl; + if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) { + for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom)) + attr->ecommunity = ecommunity_merge(attr->ecommunity, + ecom); + } + + /* add the router mac extended community */ + if (!is_zero_mac(&attr->rmac)) { + memset(&ecom_rmac, 0, sizeof(ecom_rmac)); + encode_rmac_extcomm(&eval_rmac, &attr->rmac); + ecom_rmac.size = 1; + ecom_rmac.val = (uint8_t *)eval_rmac.val; + attr->ecommunity = ecommunity_merge(attr->ecommunity, + &ecom_rmac); + } + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); +} + +/* * Build extended communities for EVPN route. RT and ENCAP are * applicable to all routes. + * TODO: currently kernel doesnt support ipv6 routes with ipv4 nexthops. + * This means that we can't do symmetric routing for ipv6 hosts routes + * in the same way as ipv4 host routes. + * We wont attach l3-vni related RTs for ipv6 routes. + * For now, We will only adevrtise ipv4 host routes + * with L3-VNI related ext-comm. */ -static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr) +static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, + afi_t afi) { struct ecommunity ecom_encap; struct ecommunity ecom_sticky; + struct ecommunity ecom_rmac; struct ecommunity_val eval; struct ecommunity_val eval_sticky; + struct ecommunity_val eval_rmac; bgp_encap_types tnl_type; struct listnode *node, *nnode; struct ecommunity *ecom; u_int32_t seqnum; + struct list *vrf_export_rtl = NULL; /* Encap */ tnl_type = BGP_ENCAP_TYPE_VXLAN; @@ -450,10 +682,24 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr) /* Add Encap */ attr->ecommunity = ecommunity_dup(&ecom_encap); - /* Add the export RTs */ + /* Add the export RTs for L2VNI */ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom); + /* Add the export RTs for L3VNI - currently only supported for IPV4 host + * routes + */ + if (afi == AFI_IP) { + vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn); + if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) { + for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, + ecom)) + attr->ecommunity = + ecommunity_merge(attr->ecommunity, + ecom); + } + } + if (attr->sticky) { seqnum = 0; memset(&ecom_sticky, 0, sizeof(ecom_sticky)); @@ -464,6 +710,15 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr) ecommunity_merge(attr->ecommunity, &ecom_sticky); } + if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) { + memset(&ecom_rmac, 0, sizeof(ecom_rmac)); + encode_rmac_extcomm(&eval_rmac, &attr->rmac); + ecom_rmac.size = 1; + ecom_rmac.val = (uint8_t *)eval_rmac.val; + attr->ecommunity = ecommunity_merge(attr->ecommunity, + &ecom_rmac); + } + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); } @@ -699,6 +954,123 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn) return local_ri->attr->sticky; } +static int update_evpn_type5_route_entry(struct bgp *bgp_def, + struct bgp *bgp_vrf, afi_t afi, + safi_t safi, struct bgp_node *rn, + struct attr *attr, int *route_changed) +{ + struct attr *attr_new = NULL; + struct bgp_info *ri = NULL; + mpls_label_t label = MPLS_INVALID_LABEL; + struct bgp_info *local_ri = NULL; + struct bgp_info *tmp_ri = NULL; + + *route_changed = 0; + /* locate the local route entry if any */ + for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) { + if (tmp_ri->peer == bgp_def->peer_self + && tmp_ri->type == ZEBRA_ROUTE_BGP + && tmp_ri->sub_type == BGP_ROUTE_STATIC) + local_ri = tmp_ri; + } + + /* create a new route entry if one doesnt exist. + Otherwise see if route attr has changed + */ + if (!local_ri) { + + /* route has changed as this is the first entry */ + *route_changed = 1; + + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(attr); + + /* create the route info from attribute */ + ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, + bgp_def->peer_self, attr_new, rn); + SET_FLAG(ri->flags, BGP_INFO_VALID); + + /* L3-VNI goes in the label2 field */ + bgp_info_extra_get(ri); + vni2label(bgp_vrf->l3vni, &label); + memcpy(&ri->extra->label2, &label, BGP_LABEL_BYTES); + + /* add the route entry to route node*/ + bgp_info_add(rn, ri); + } else { + + tmp_ri = local_ri; + if (!attrhash_cmp(tmp_ri->attr, attr)) { + + /* attribute changed */ + *route_changed = 1; + + /* The attribute has changed. */ + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(attr); + bgp_info_set_flag(rn, tmp_ri, BGP_INFO_ATTR_CHANGED); + + /* Restore route, if needed. */ + if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED)) + bgp_info_restore(rn, tmp_ri); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&tmp_ri->attr); + tmp_ri->attr = attr_new; + tmp_ri->uptime = bgp_clock(); + } + } + return 0; +} + +/* update evpn type-5 route entry */ +static int update_evpn_type5_route(struct bgp *bgp_vrf, + struct prefix_evpn *evp) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct attr attr; + struct bgp_node *rn = NULL; + struct bgp *bgp_def = NULL; + int route_changed = 0; + + bgp_def = bgp_get_default(); + if (!bgp_def) + return -1; + + /* build path attribute for this route */ + memset(&attr, 0, sizeof(struct attr)); + bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + attr.nexthop = bgp_vrf->originator_ip; + attr.mp_nexthop_global_in = bgp_vrf->originator_ip; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + memcpy(&attr.rmac, &bgp_vrf->rmac, sizeof(struct ethaddr)); + + /* Setup RT and encap extended community */ + build_evpn_type5_route_extcomm(bgp_vrf, &attr); + + /* get the route node in global table */ + rn = bgp_afi_node_get(bgp_def->rib[afi][safi], afi, safi, + (struct prefix *)evp, + &bgp_vrf->vrf_prd); + assert(rn); + + /* create or update the route entry within the route node */ + update_evpn_type5_route_entry(bgp_def, bgp_vrf, + afi, safi, + rn, &attr, &route_changed); + + /* schedule for processing and unlock node */ + if (route_changed) { + bgp_process(bgp_def, rn, afi, safi); + bgp_unlock_node(rn); + } + + /* uninten temporary */ + aspath_unintern(&attr.aspath); + return 0; +} + /* * Create or update EVPN route entry. This could be in the VNI route table * or the global route table. @@ -834,11 +1206,14 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, attr.mp_nexthop_global_in = vpn->originator_ip; attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; attr.sticky = CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? 1 : 0; - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL); + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL); + bgpevpn_get_rmac(vpn, &attr.rmac); vni2label(vpn->vni, &(attr.label)); /* Set up RT and ENCAP extended community. */ - build_evpn_route_extcomm(vpn, &attr); + build_evpn_route_extcomm(vpn, &attr, + IS_EVPN_PREFIX_IPADDR_V4(p) ? + AFI_IP : AFI_IP6); /* First, create (or fetch) route node within the VNI. */ /* NOTE: There is no RD here. */ @@ -880,6 +1255,58 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +/* Delete EVPN type5 route entry from global table */ +static void delete_evpn_type5_route_entry(struct bgp *bgp_def, + struct bgp *bgp_vrf, + afi_t afi, safi_t safi, + struct bgp_node *rn, + struct bgp_info **ri) +{ + struct bgp_info *tmp_ri = NULL; + + *ri = NULL; + + /* find the matching route entry */ + for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) + if (tmp_ri->peer == bgp_def->peer_self + && tmp_ri->type == ZEBRA_ROUTE_BGP + && tmp_ri->sub_type == BGP_ROUTE_STATIC) + break; + + *ri = tmp_ri; + + /* Mark route for delete. */ + if (tmp_ri) + bgp_info_delete(rn, tmp_ri); +} + +/* Delete EVPN type5 route */ +static int delete_evpn_type5_route(struct bgp *bgp_vrf, + struct prefix_evpn *evp) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp_node *rn = NULL; + struct bgp_info *ri = NULL; + struct bgp *bgp_def = NULL; /* default bgp instance */ + + bgp_def = bgp_get_default(); + if (!bgp_def) + return -1; + + /* locate the global route entry for this type-5 prefix */ + rn = bgp_afi_node_lookup(bgp_def->rib[afi][safi], afi, safi, + (struct prefix *)evp, &bgp_vrf->vrf_prd); + if (!rn) + return 0; + + delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri); + if (ri) + bgp_process(bgp_def, rn, afi, safi); + bgp_unlock_node(rn); + return 0; +} + /* * Delete EVPN route entry. This could be in the VNI route table * or the global route table. @@ -967,12 +1394,16 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) struct bgp_info *ri; struct attr attr; struct attr attr_sticky; + struct attr attr_ip6; + struct attr attr_sticky_ip6; struct attr *attr_new; afi = AFI_L2VPN; safi = SAFI_EVPN; memset(&attr, 0, sizeof(struct attr)); memset(&attr_sticky, 0, sizeof(struct attr)); + memset(&attr_ip6, 0, sizeof(struct attr)); + memset(&attr_sticky_ip6, 0, sizeof(struct attr)); /* Build path-attribute - all type-2 routes for this VNI will share the * same path attribute. @@ -982,14 +1413,29 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) attr.nexthop = vpn->originator_ip; attr.mp_nexthop_global_in = vpn->originator_ip; attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + bgpevpn_get_rmac(vpn, &attr.rmac); attr_sticky.nexthop = vpn->originator_ip; attr_sticky.mp_nexthop_global_in = vpn->originator_ip; attr_sticky.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; attr_sticky.sticky = 1; + bgpevpn_get_rmac(vpn, &attr_sticky.rmac); + bgp_attr_default_set(&attr_ip6, BGP_ORIGIN_IGP); + bgp_attr_default_set(&attr_sticky_ip6, BGP_ORIGIN_IGP); + attr_ip6.nexthop = vpn->originator_ip; + attr_ip6.mp_nexthop_global_in = vpn->originator_ip; + attr_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + bgpevpn_get_rmac(vpn, &attr_ip6.rmac); + attr_sticky_ip6.nexthop = vpn->originator_ip; + attr_sticky_ip6.mp_nexthop_global_in = vpn->originator_ip; + attr_sticky_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + attr_sticky_ip6.sticky = 1; + bgpevpn_get_rmac(vpn, &attr_sticky_ip6.rmac); /* Set up RT, ENCAP and sticky MAC extended community. */ - build_evpn_route_extcomm(vpn, &attr); - build_evpn_route_extcomm(vpn, &attr_sticky); + build_evpn_route_extcomm(vpn, &attr, AFI_IP); + build_evpn_route_extcomm(vpn, &attr_sticky, AFI_IP); + build_evpn_route_extcomm(vpn, &attr_ip6, AFI_IP6); + build_evpn_route_extcomm(vpn, &attr_sticky_ip6, AFI_IP6); /* Walk this VNI's route table and update local type-2 routes. For any * routes updated, update corresponding entry in the global table too. @@ -1003,12 +1449,24 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; - if (evpn_route_is_sticky(bgp, rn)) - update_evpn_route_entry(bgp, vpn, afi, safi, rn, - &attr_sticky, 0, 1, &ri, 0); - else - update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr, - 0, 1, &ri, 0); + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + if (evpn_route_is_sticky(bgp, rn)) + update_evpn_route_entry(bgp, vpn, afi, safi, rn, + &attr_sticky, 0, 1, + &ri, 0); + else + update_evpn_route_entry(bgp, vpn, afi, safi, rn, + &attr, 0, 1, &ri, 0); + } else { + if (evpn_route_is_sticky(bgp, rn)) + update_evpn_route_entry(bgp, vpn, afi, safi, rn, + &attr_sticky_ip6, 0, 1, + &ri, 0); + else + update_evpn_route_entry(bgp, vpn, afi, safi, rn, + &attr_ip6, 0, 1, + &ri, 0); + } /* If a local route exists for this prefix, we need to update * the global routing table too. @@ -1222,6 +1680,100 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, } /* + * Install route entry into the VRF routing table and invoke route selection. + */ +static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, + struct prefix_evpn *evp, + struct bgp_info *parent_ri) +{ + struct bgp_node *rn; + struct bgp_info *ri; + struct attr *attr_new; + int ret = 0; + struct prefix p; + struct prefix *pp = &p; + afi_t afi = 0; + safi_t safi = 0; + char buf[PREFIX_STRLEN]; + char buf1[PREFIX_STRLEN]; + + memset(pp, 0, sizeof(struct prefix)); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + ip_prefix_from_type2_prefix(evp, pp); + else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) + ip_prefix_from_type5_prefix(evp, pp); + + if (bgp_debug_zebra(NULL)) { + zlog_debug("installing evpn prefix %s as ip prefix %s in vrf %s", + prefix2str(evp, buf, sizeof(buf)), + prefix2str(pp, buf1, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } + + /* Create (or fetch) route within the VRF. */ + /* NOTE: There is no RD here. */ + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + afi = AFI_IP; + safi = SAFI_UNICAST; + rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); + } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + afi = AFI_IP6; + safi = SAFI_UNICAST; + rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); + } else + return 0; + + /* Check if route entry is already present. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->extra + && (struct bgp_info *)ri->extra->parent == parent_ri) + break; + + if (!ri) { + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_ri->attr); + + /* Create new route with its attribute. */ + ri = info_make(parent_ri->type, parent_ri->sub_type, 0, + parent_ri->peer, attr_new, rn); + SET_FLAG(ri->flags, BGP_INFO_VALID); + bgp_info_extra_get(ri); + ri->extra->parent = parent_ri; + if (parent_ri->extra) + memcpy(&ri->extra->label, &parent_ri->extra->label, + BGP_LABEL_BYTES); + bgp_info_add(rn, ri); + } else { + if (attrhash_cmp(ri->attr, parent_ri->attr) + && !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) { + bgp_unlock_node(rn); + return 0; + } + /* The attribute has changed. */ + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_ri->attr); + + /* Restore route, if needed. */ + if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) + bgp_info_restore(rn, ri); + + /* Mark if nexthop has changed. */ + if (!IPV4_ADDR_SAME(&ri->attr->nexthop, &attr_new->nexthop)) + SET_FLAG(ri->flags, BGP_INFO_IGP_CHANGED); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&ri->attr); + ri->attr = attr_new; + ri->uptime = bgp_clock(); + } + + /* Perform route selection and update zebra, if required. */ + bgp_process(bgp_vrf, rn, afi, safi); + + return ret; +} + +/* * Install route entry into the VNI routing table and invoke route selection. */ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, @@ -1288,6 +1840,73 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, } /* + * Uninstall route entry from the VRF routing table and send message + * to zebra, if appropriate. + */ +static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, + struct prefix_evpn *evp, + struct bgp_info *parent_ri) +{ + struct bgp_node *rn; + struct bgp_info *ri; + int ret = 0; + struct prefix p; + struct prefix *pp = &p; + afi_t afi = 0; + safi_t safi = 0; + char buf[PREFIX_STRLEN]; + char buf1[PREFIX_STRLEN]; + + memset(pp, 0, sizeof(struct prefix)); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + ip_prefix_from_type2_prefix(evp, pp); + else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) + ip_prefix_from_type5_prefix(evp, pp); + + if (bgp_debug_zebra(NULL)) { + zlog_debug("uninstalling evpn prefix %s as ip prefix %s in vrf %s", + prefix2str(evp, buf, sizeof(buf)), + prefix2str(pp, buf1, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } + + /* Locate route within the VRF. */ + /* NOTE: There is no RD here. */ + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + afi = AFI_IP; + safi = SAFI_UNICAST; + rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); + } else { + afi = AFI_IP6; + safi = SAFI_UNICAST; + rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); + } + + if (!rn) + return 0; + + /* Find matching route entry. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->extra + && (struct bgp_info *)ri->extra->parent == parent_ri) + break; + + if (!ri) + return 0; + + /* Mark entry for deletion */ + bgp_info_delete(rn, ri); + + /* Perform route selection and update zebra, if required. */ + bgp_process(bgp_vrf, rn, afi, safi); + + /* Unlock route node. */ + bgp_unlock_node(rn); + + return ret; +} + +/* * Uninstall route entry from the VNI routing table and send message * to zebra, if appropriate. */ @@ -1327,6 +1946,73 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, } /* + * Given a route entry and a VRF, see if this route entry should be + * imported into the VRF i.e., RTs match. + */ +static int is_route_matching_for_vrf(struct bgp *bgp_vrf, + struct bgp_info *ri) +{ + struct attr *attr = ri->attr; + struct ecommunity *ecom; + int i; + + assert(attr); + /* Route should have valid RT to be even considered. */ + if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) + return 0; + + ecom = attr->ecommunity; + if (!ecom || !ecom->size) + return 0; + + /* For each extended community RT, see if it matches this VNI. If any RT + * matches, we're done. + */ + for (i = 0; i < ecom->size; i++) { + u_char *pnt; + u_char type, sub_type; + struct ecommunity_val *eval; + struct ecommunity_val eval_tmp; + struct vrf_irt_node *irt; + + /* Only deal with RTs */ + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + eval = (struct ecommunity_val *)(ecom->val + + (i * ECOMMUNITY_SIZE)); + type = *pnt++; + sub_type = *pnt++; + if (sub_type != ECOMMUNITY_ROUTE_TARGET) + continue; + + /* See if this RT matches specified VNIs import RTs */ + irt = lookup_vrf_import_rt(eval); + if (irt && irt->vrfs) + if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) + return 1; + + /* Also check for non-exact match. In this, we mask out the AS + * and + * only check on the local-admin sub-field. This is to + * facilitate using + * VNI as the RT for EBGP peering too. + */ + irt = NULL; + if (type == ECOMMUNITY_ENCODE_AS + || type == ECOMMUNITY_ENCODE_AS4 + || type == ECOMMUNITY_ENCODE_IP) { + memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + mask_ecom_global_admin(&eval_tmp, eval); + irt = lookup_vrf_import_rt(&eval_tmp); + } + if (irt && irt->vrfs) + if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) + return 1; + } + + return 0; +} + +/* * Given a route entry and a VNI, see if this route entry should be * imported into the VNI i.e., RTs match. */ @@ -1394,6 +2080,88 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, } /* + * Install or uninstall mac-ip routes are appropriate for this + * particular VRF. + */ +static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, + int install) +{ + afi_t afi; + safi_t safi; + struct bgp_node *rd_rn, *rn; + struct bgp_table *table; + struct bgp_info *ri; + int ret; + char buf[PREFIX_STRLEN]; + struct bgp *bgp_def = NULL; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + bgp_def = bgp_get_default(); + if (!bgp_def) + return -1; + + /* Walk entire global routing table and evaluate routes which could be + * imported into this VRF. Note that we need to loop through all global + * routes to determine which route matches the import rt on vrf + */ + for (rd_rn = bgp_table_top(bgp_def->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)) { + struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + + /* if not mac-ip route skip this route */ + if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || + evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) + continue; + + /* if not a mac+ip route skip this route */ + if (!(IS_EVPN_PREFIX_IPADDR_V4(evp) || + IS_EVPN_PREFIX_IPADDR_V6(evp))) + continue; + + for (ri = rn->info; ri; ri = ri->next) { + /* Consider "valid" remote routes applicable for + * this VRF. + */ + if (!(CHECK_FLAG(ri->flags, BGP_INFO_VALID) + && ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (is_route_matching_for_vrf(bgp_vrf, ri)) { + if (install) + ret = + install_evpn_route_entry_in_vrf( + bgp_vrf, evp, ri); + else + ret = + uninstall_evpn_route_entry_in_vrf( + bgp_vrf, evp, ri); + + if (ret) { + zlog_err( + "Failed to %s EVPN %s route in VRF %s", + install ? "install" + : "uninstall", + prefix2str(evp, buf, + sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + return ret; + } + } + } + } + } + + return 0; +} + +/* * Install or uninstall routes of specified type that are appropriate for this * particular VNI. */ @@ -1467,6 +2235,15 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, return 0; } +/* Install any existing remote routes applicable for this VRF into VRF RIB. This + * is invoked upon l3vni-add or l3vni import rt change + */ +static int install_routes_for_vrf(struct bgp *bgp_vrf) +{ + install_uninstall_routes_for_vrf(bgp_vrf, 1); + return 0; +} + /* * Install any existing remote routes applicable for this VNI into its * routing table. This is invoked when a VNI becomes "live" or its Import @@ -1488,6 +2265,13 @@ static int install_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) 1); } +/* uninstall routes from l3vni vrf. */ +static int uninstall_routes_for_vrf(struct bgp *bgp_vrf) +{ + install_uninstall_routes_for_vrf(bgp_vrf, 0); + return 0; +} + /* * Uninstall any existing remote routes for this VNI. One scenario in which * this is invoked is upon an import RT change. @@ -1510,6 +2294,51 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) } /* + * Install or uninstall route in matching VRFs (list). + */ +static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi, + safi_t safi, struct prefix_evpn *evp, + struct bgp_info *ri, + struct list *vrfs, int install) +{ + char buf[PREFIX2STR_BUFFER]; + struct bgp *bgp_vrf; + struct listnode *node, *nnode; + + /* Only type-2/type-5 routes go into a VRF */ + if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || + evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) + return 0; + + /* if it is type-2 route and not a mac+ip route skip this route */ + if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) && + !(IS_EVPN_PREFIX_IPADDR_V4(evp) || IS_EVPN_PREFIX_IPADDR_V6(evp))) + return 0; + + for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) { + int ret; + + if (install) + ret = install_evpn_route_entry_in_vrf(bgp_vrf, + evp, ri); + else + ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, + evp, ri); + + if (ret) { + zlog_err("%u: Failed to %s prefix %s in VRF %s", + bgp_def->vrf_id, + install ? "install" : "uninstall", + prefix2str(evp, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + return ret; + } + } + + return 0; +} + +/* * Install or uninstall route in matching VNIs (list). */ static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi, @@ -1559,9 +2388,10 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, assert(attr); - /* Only type-2 and type-3 routes go into a L2 VNI. */ + /* Only type-2 and type-3 and type-5 are supported currently */ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE - || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE)) + || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE + || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) return 0; /* If we don't have Route Target, nothing much to do. */ @@ -1572,15 +2402,16 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (!ecom || !ecom->size) return -1; - /* For each extended community RT, see which VNIs match and import - * the route into matching VNIs. + /* For each extended community RT, see which VNIs/VRFs match and import + * the route into matching VNIs/VRFs. */ for (i = 0; i < ecom->size; i++) { u_char *pnt; u_char type, sub_type; struct ecommunity_val *eval; struct ecommunity_val eval_tmp; - struct irt_node *irt; + struct irt_node *irt; /* import rt for l2vni */ + struct vrf_irt_node *vrf_irt; /* import rt for l3vni */ /* Only deal with RTs */ pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); @@ -1591,34 +2422,106 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (sub_type != ECOMMUNITY_ROUTE_TARGET) continue; - /* Are we interested in this RT? */ + /* Import route into matching l2-vnis (type-2/type-3 routes go + * into l2vni table) + */ irt = lookup_import_rt(bgp, eval); if (irt && irt->vnis) install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, irt->vnis, import); - /* Also check for non-exact match. In this, we mask out the AS - * and - * only check on the local-admin sub-field. This is to - * facilitate using + /* Import route into matching l3-vnis (type-2/type-5 routes go + * into l3vni/vrf table) + */ + vrf_irt = lookup_vrf_import_rt(eval); + if (vrf_irt && vrf_irt->vrfs) + install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri, + vrf_irt->vrfs, import); + + /* Also check for non-exact match. In this, + * we mask out the AS and + * only check on the local-admin sub-field. + * This is to facilitate using * VNI as the RT for EBGP peering too. */ irt = NULL; + vrf_irt = NULL; if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); + vrf_irt = lookup_vrf_import_rt(&eval_tmp); } if (irt && irt->vnis) install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, irt->vnis, import); + if (vrf_irt && vrf_irt->vrfs) + install_uninstall_route_in_vrfs(bgp, afi, safi, evp, + ri, vrf_irt->vrfs, + import); } return 0; } +/* delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5 + * routes */ +static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf) +{ + /* delete all ipv4 routes and withdraw from peers */ + bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST); + + /* delete all ipv6 routes and withdraw from peers */ + bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST); +} + +/* update and advertise all ipv4 and ipv6 routes in thr vrf table as type-5 + * routes */ +static void update_advertise_vrf_routes(struct bgp *bgp_vrf) +{ + /* update all ipv4 routes */ + bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST); + + /* update all ipv6 routes */ + bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST); +} + +/* + * update and advertise local routes for a VRF as type-5 routes. + * This is invoked upon RD change for a VRF. Note taht the processing is only + * done in the global route table using the routes which already exist in the + * VRF routing table + */ +static void update_router_id_vrf(struct bgp *bgp_vrf) +{ + /* skip if the RD is configured */ + if (is_vrf_rd_configured(bgp_vrf)) + return; + + /* derive the RD for the VRF based on new router-id */ + bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); + + /* update advertise ipv4|ipv6 routes as type-5 routes */ + update_advertise_vrf_routes(bgp_vrf); +} + +/* + * Delete and withdraw all type-5 routes for the RD corresponding to VRF. + * This is invoked upon VRF RD change. The processing is done only from global + * table. + */ +static void withdraw_router_id_vrf(struct bgp *bgp_vrf) +{ + /* skip if the RD is configured */ + if (is_vrf_rd_configured(bgp_vrf)) + return; + + /* delete/withdraw ipv4|ipv6 routes as type-5 routes */ + delete_withdraw_vrf_routes(bgp_vrf); +} + /* * Update and advertise local routes for a VNI. Invoked upon router-id * change. Note that the processing is done only on the global route table @@ -1973,6 +2876,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, /* Make EVPN prefix. */ memset(&p, 0, sizeof(struct prefix_evpn)); p.family = AF_EVPN; + p.prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; /* Additional information outside of prefix - ESI and GW IP */ @@ -2007,14 +2911,12 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, pfx += 4; memcpy(&evpn.gw_ip.ipv4, pfx, 4); pfx += 4; - p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV4; } else { SET_IPADDR_V6(&p.prefix.ip); memcpy(&p.prefix.ip.ipaddr_v6, pfx, 16); pfx += 16; memcpy(&evpn.gw_ip.ipv6, pfx, 16); pfx += 16; - p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV6; } label_pnt = (mpls_label_t *)pfx; @@ -2045,10 +2947,13 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p, return; p_evpn_p = &(p->u.prefix_evpn); + /* len denites the total len of IP and GW-IP in the route + IP and GW-IP have to be both ipv4 or ipv6 + */ if (IS_IPADDR_V4(&p_evpn_p->ip)) - len = 8; /* ipv4 */ + len = 8; /* IP and GWIP are both ipv4 */ else - len = 32; /* ipv6 */ + len = 32; /* IP and GWIP are both ipv6 */ /* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */ stream_putc(s, 8 + 10 + 4 + 1 + len + 3); stream_put(s, prd->val, 8); @@ -2109,30 +3014,311 @@ static void free_vni_entry(struct hash_backet *backet, struct bgp *bgp) bgp_evpn_free(bgp, vpn); } +/* + * Derive AUTO import RT for BGP VRF - L3VNI + */ +static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf) +{ + struct bgp *bgp_def = NULL; + + form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl); + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + + /* Map RT to VRF */ + bgp_def = bgp_get_default(); + if (!bgp_def) + return; + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); +} + +/* + * Delete AUTO import RT from BGP VRF - L3VNI + */ +static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf) +{ + evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl); +} + +/* + * Derive AUTO export RT for BGP VRF - L3VNI + */ +static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf) +{ + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); + form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl); +} + +/* + * Delete AUTO export RT from BGP VRF - L3VNI + */ +static void evpn_auto_rt_export_delete_for_vrf(struct bgp *bgp_vrf) +{ + evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl); +} + +static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf) +{ + struct bgp *bgp_def = NULL; + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) + return; + + /* update all type-5 routes */ + update_advertise_vrf_routes(bgp_vrf); + + /* update all type-2 routes */ + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) + update_routes_for_vni(bgp_def, vpn); +} /* * Public functions. */ +/* withdraw type-5 route corresponding to ip prefix */ +void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn, + afi_t afi, safi_t safi) +{ + int ret = 0; + struct prefix_evpn evp; + char buf[PREFIX_STRLEN]; + + build_type5_prefix_from_ip_prefix(&evp, &rn->p); + ret = delete_evpn_type5_route(bgp_vrf, &evp); + if (ret) { + zlog_err( + "%u failed to delete type-5 route for prefix %s in vrf %s", + bgp_vrf->vrf_id, + prefix2str(&rn->p, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } +} + +/* withdraw all type-5 routes for an address family */ +void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, + afi_t afi, safi_t safi) +{ + struct bgp_table *table = NULL; + struct bgp_node *rn = NULL; + + if (!advertise_type5_routes(bgp_vrf, afi)) + return; + + table = bgp_vrf->rib[afi][safi]; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) + bgp_evpn_withdraw_type5_route(bgp_vrf, rn, afi, safi); + +} + +/* advertise ip prefix as type-5 route*/ +void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn, + afi_t afi, safi_t safi) +{ + int ret = 0; + struct prefix_evpn evp; + char buf[PREFIX_STRLEN]; + + if (!advertise_type5_routes(bgp_vrf, afi)) + return; + + if (!rn->info) + return; + + /* only advertise subnet routes as type-5 */ + if (is_host_route(&rn->p)) + return; + + build_type5_prefix_from_ip_prefix(&evp, &rn->p); + ret = update_evpn_type5_route(bgp_vrf, &evp); + if (ret) { + zlog_err( + "%u failed to create type-5 route for prefix %s in vrf %s", + bgp_vrf->vrf_id, + prefix2str(&rn->p, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } +} + +/* advertise all type-5 routes for an address family */ +void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, + afi_t afi, safi_t safi) +{ + struct bgp_table *table = NULL; + struct bgp_node *rn = NULL; + + table = bgp_vrf->rib[afi][safi]; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) + bgp_evpn_advertise_type5_route(bgp_vrf, rn, afi, safi); +} + +void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, + struct list *rtl) +{ + struct listnode *node, *nnode, *node_to_del; + struct ecommunity *ecom, *ecom_auto; + struct ecommunity_val eval; + + encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); + + ecom_auto = ecommunity_new(); + ecommunity_add_val(ecom_auto, &eval); + node_to_del = NULL; + + for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { + if (ecommunity_match(ecom, ecom_auto)) { + ecommunity_free(&ecom); + node_to_del = node; + } + } + + if (node_to_del) + list_delete_node(rtl, node_to_del); + + ecommunity_free(&ecom_auto); +} + +void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd) +{ + /* uninstall routes from vrf */ + uninstall_routes_for_vrf(bgp_vrf); + + /* Cleanup the RT to VRF mapping */ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + + /* Remove auto generated RT */ + evpn_auto_rt_import_delete_for_vrf(bgp_vrf); + + /* Add the newly configured RT to RT list */ + listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd); + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + + /* map VRF to its RTs */ + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + + /* install routes matching the new VRF */ + install_routes_for_vrf(bgp_vrf); +} + +void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomdel) +{ + struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; + struct ecommunity *ecom = NULL; + + /* uninstall routes from vrf */ + uninstall_routes_for_vrf(bgp_vrf); + + /* Cleanup the RT to VRF mapping */ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + + /* remove the RT from the RT list */ + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { + if (ecommunity_match(ecom, ecomdel)) { + ecommunity_free(&ecom); + node_to_del = node; + break; + } + } + + if (node_to_del) + list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del); + + /* fallback to auto import rt, if this was the last RT */ + if (list_isempty(bgp_vrf->vrf_import_rtl)) { + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + evpn_auto_rt_import_add_for_vrf(bgp_vrf); + } + + /* map VRFs to its RTs */ + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + + /* install routes matching this new RT */ + install_routes_for_vrf(bgp_vrf); +} + +void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd) +{ + /* remove auto-generated RT */ + evpn_auto_rt_export_delete_for_vrf(bgp_vrf); + + /* Add the new RT to the RT list */ + listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd); + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); + + bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); + +} + +void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomdel) +{ + struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; + struct ecommunity *ecom = NULL; + + /* Remove the RT from the RT list */ + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) { + if (ecommunity_match(ecom, ecomdel)) { + ecommunity_free(&ecom); + node_to_del = node; + break; + } + } + + if (node_to_del) + list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del); + + /* fall back to auto-generated RT if this was the last RT */ + if (bgp_vrf->vrf_export_rtl && list_isempty(bgp_vrf->vrf_export_rtl)) { + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); + evpn_auto_rt_export_add_for_vrf(bgp_vrf); + } + + bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); +} + /* * Handle change to BGP router id. This is invoked twice by the change * handler, first before the router id has been changed and then after * the router id has been changed. The first invocation will result in - * local routes for all VNIs being deleted and withdrawn and the next + * local routes for all VNIs/VRF being deleted and withdrawn and the next * will result in the routes being re-advertised. */ void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw) { - if (withdraw) + if (withdraw) { + + /* delete and withdraw all the type-5 routes + stored in the global table for this vrf + */ + withdraw_router_id_vrf(bgp); + + /* delete all the VNI routes (type-2/type-3) routes for all the + * L2-VNIs + */ hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))withdraw_router_id_vni, bgp); - else + } else { + + /* advertise all routes in the vrf as type-5 routes with the new + * RD + */ + update_router_id_vrf(bgp); + + /* advertise all the VNI routes (type-2/type-3) routes with the + * new RD + */ hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))update_router_id_vni, bgp); + } } /* @@ -2143,6 +3329,15 @@ int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn) return update_routes_for_vni(bgp, vpn); } +void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, + int withdraw) +{ + if (withdraw) + delete_withdraw_vrf_routes(bgp_vrf); + else + update_advertise_vrf_routes(bgp_vrf); +} + /* * Handle change to RD. This is invoked twice by the change handler, * first before the RD has been changed and then after the RD has @@ -2287,6 +3482,13 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) inet_ntop(family, &p->prefix.ip.ip.addr, buf2, PREFIX2STR_BUFFER)); } + } else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { + snprintf(buf, len, "[%d]:[0]:[%d]:[%s]", + p->prefix.route_type, + p->prefix.ip_prefix_length, + IS_EVPN_PREFIX_IPADDR_V4(p) ? + inet_ntoa(p->prefix.ip.ipaddr_v4) : + inet6_ntoa(p->prefix.ip.ipaddr_v6)); } else { /* For EVPN route types not supported yet. */ snprintf(buf, len, "(unsupported route type %d)", @@ -2455,6 +3657,65 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, return 0; } +/* + * Map the RTs (configured or automatically derived) of a VRF to the VRF. + * The mapping will be used during route processing. + * bgp_def: default bgp instance + * bgp_vrf: specific bgp vrf instance on which RT is configured + */ +void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf) +{ + int i = 0; + struct ecommunity_val *eval = NULL; + struct listnode *node = NULL, *nnode = NULL; + struct ecommunity *ecom = NULL; + + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { + for (i = 0; i < ecom->size; i++) { + eval = (struct ecommunity_val *)(ecom->val + + (i + * ECOMMUNITY_SIZE)); + map_vrf_to_rt(bgp_vrf, eval); + } + } +} + +/* + * Unmap the RTs (configured or automatically derived) of a VRF from the VRF. + */ +void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf) +{ + int i; + struct ecommunity_val *eval; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { + for (i = 0; i < ecom->size; i++) { + struct vrf_irt_node *irt; + struct ecommunity_val eval_tmp; + + eval = (struct ecommunity_val *)(ecom->val + + (i + * ECOMMUNITY_SIZE)); + /* If using "automatic" RT, we only care about the + * local-admin sub-field. + * This is to facilitate using VNI as the RT for EBGP + * peering too. + */ + memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_IMPORT_RT_CFGD)) + mask_ecom_global_admin(&eval_tmp, eval); + + irt = lookup_vrf_import_rt(&eval_tmp); + if (irt) + unmap_vrf_from_rt(bgp_vrf, irt); + } + } +} + + /* * Map the RTs (configured or automatically derived) of a VNI to the VNI. @@ -2517,7 +3778,7 @@ void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn) */ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn) { - form_auto_rt(bgp, vpn, vpn->import_rtl); + form_auto_rt(bgp, vpn->vni, vpn->import_rtl); UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD); /* Map RT to VNI */ @@ -2529,7 +3790,7 @@ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn) */ void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn) { - form_auto_rt(bgp, vpn, vpn->export_rtl); + form_auto_rt(bgp, vpn->vni, vpn->export_rtl); UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD); } @@ -2537,6 +3798,20 @@ void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn) * Derive RD automatically for VNI using passed information - it * is of the form RouterId:unique-id-for-vni. */ +void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp) +{ + char buf[100]; + + bgp->vrf_prd.family = AF_UNSPEC; + bgp->vrf_prd.prefixlen = 64; + sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), bgp->vrf_rd_id); + str2prefix_rd(buf, &bgp->vrf_prd); +} + +/* + * Derive RD automatically for VNI using passed information - it + * is of the form RouterId:unique-id-for-vni. + */ void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn) { char buf[100]; @@ -2566,7 +3841,8 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni) * Create a new vpn - invoked upon configuration or zebra notification. */ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip) + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id) { struct bgpevpn *vpn; @@ -2580,13 +3856,14 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, /* Set values - RD and RT set to defaults. */ vpn->vni = vni; vpn->originator_ip = originator_ip; + vpn->tenant_vrf_id = tenant_vrf_id; /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; vpn->export_rtl = list_new(); vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; - bf_assign_index(bgp->rd_idspace, vpn->rd_id); + bf_assign_index(bm->rd_idspace, vpn->rd_id); derive_rd_rt_for_vni(bgp, vpn); /* Initialize EVPN route table. */ @@ -2597,6 +3874,10 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, XFREE(MTYPE_BGP_EVPN, vpn); return NULL; } + + /* add to l2vni list on corresponding vrf */ + bgpevpn_link_to_l3vni(vpn); + QOBJ_REG(vpn, bgpevpn); return vpn; } @@ -2609,11 +3890,12 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, */ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) { + bgpevpn_unlink_from_l3vni(vpn); bgp_table_unlock(vpn->route_table); bgp_evpn_unmap_vni_from_its_rts(bgp, vpn); list_delete_and_null(&vpn->import_rtl); list_delete_and_null(&vpn->export_rtl); - bf_release_index(bgp->rd_idspace, vpn->rd_id); + bf_release_index(bm->rd_idspace, vpn->rd_id); hash_release(bgp->vnihash, vpn); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); @@ -2772,6 +4054,160 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, return 0; } +static void link_l2vni_hash_to_l3vni(struct hash_backet *backet, + struct bgp *bgp_vrf) +{ + struct bgpevpn *vpn = NULL; + struct bgp *bgp_def = NULL; + + bgp_def = bgp_get_default(); + assert(bgp_def); + + vpn = (struct bgpevpn *)backet->data; + if (vpn->tenant_vrf_id == bgp_vrf->vrf_id) + bgpevpn_link_to_l3vni(vpn); +} + +int bgp_evpn_local_l3vni_add(vni_t l3vni, + vrf_id_t vrf_id, + struct ethaddr *rmac, + struct in_addr originator_ip) +{ + struct bgp *bgp_vrf = NULL; /* bgp VRF instance */ + struct bgp *bgp_def = NULL; /* default bgp instance */ + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + as_t as = 0; + + /* get the default instamce - required to get the AS number for VRF + * auto-creatio + */ + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("Cannot process L3VNI %u ADD - default BGP instance not yet created", + l3vni); + return -1; + } + as = bgp_def->as; + + /* if the BGP vrf instance doesnt exist - create one */ + bgp_vrf = bgp_lookup_by_name(vrf_id_to_name(vrf_id)); + if (!bgp_vrf) { + + int ret = 0; + + ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id), + BGP_INSTANCE_TYPE_VRF); + switch (ret) { + case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET: + zlog_err("'bgp multiple-instance' not present\n"); + return -1; + case BGP_ERR_AS_MISMATCH: + zlog_err("BGP is already running; AS is %u\n", as); + return -1; + case BGP_ERR_INSTANCE_MISMATCH: + zlog_err("BGP instance name and AS number mismatch\n"); + return -1; + } + + /* mark as auto created */ + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO); + } + + /* associate with l3vni */ + bgp_vrf->l3vni = l3vni; + + /* set the router mac - to be used in mac-ip routes for this vrf */ + memcpy(&bgp_vrf->rmac, rmac, sizeof(struct ethaddr)); + + /* set the originator ip */ + bgp_vrf->originator_ip = originator_ip; + + /* auto derive RD/RT */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) + evpn_auto_rt_import_add_for_vrf(bgp_vrf); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) + evpn_auto_rt_export_add_for_vrf(bgp_vrf); + bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); + + /* link all corresponding l2vnis */ + hash_iterate(bgp_def->vnihash, + (void (*)(struct hash_backet *, void *)) + link_l2vni_hash_to_l3vni, + bgp_vrf); + + /* updates all corresponding local mac-ip routes */ + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) + update_routes_for_vni(bgp_def, vpn); + + /* advertise type-5 routes if needed */ + update_advertise_vrf_routes(bgp_vrf); + + /* install all remote routes belonging to this l3vni into correspondng + * vrf */ + install_routes_for_vrf(bgp_vrf); + + return 0; +} + +int bgp_evpn_local_l3vni_del(vni_t l3vni, + vrf_id_t vrf_id) +{ + struct bgp *bgp_vrf = NULL; /* bgp vrf instance */ + struct bgp *bgp_def = NULL; /* default bgp instance */ + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vrf_id); + if (!bgp_vrf) { + zlog_err("Cannot process L3VNI %u Del - Could not find BGP instance", + l3vni); + return -1; + } + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("Cannot process L3VNI %u Del - Could not find default BGP instance", + l3vni); + return -1; + } + + /* unimport remote routes from VRF, if it is AUTO vrf bgp_delete will + * take care of uninstalling the routes from zebra + */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO)) + uninstall_routes_for_vrf(bgp_vrf); + + /* delete/withdraw all type-5 routes */ + delete_withdraw_vrf_routes(bgp_vrf); + + /* remove the l3vni from vrf instance */ + bgp_vrf->l3vni = 0; + + /* remove the Rmac from the BGP vrf */ + memset(&bgp_vrf->rmac, 0, sizeof(struct ethaddr)); + + /* delete RD/RT */ + if (bgp_vrf->vrf_import_rtl && !list_isempty(bgp_vrf->vrf_import_rtl)) { + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + list_delete_all_node(bgp_vrf->vrf_import_rtl); + } + if (bgp_vrf->vrf_export_rtl && !list_isempty(bgp_vrf->vrf_export_rtl)) { + list_delete_all_node(bgp_vrf->vrf_export_rtl); + } + + /* update all corresponding local mac-ip routes */ + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) + update_routes_for_vni(bgp_def, vpn); + + + /* Delete the instance if it was autocreated */ + if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO)) + bgp_delete(bgp_vrf); + + return 0; +} + /* * Handle del of a local VNI. */ @@ -2815,7 +4251,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) * about is change to local-tunnel-ip. */ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip) + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -2828,6 +4265,17 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Lookup VNI. If present and no change, exit. */ vpn = bgp_evpn_lookup_vni(bgp, vni); if (vpn) { + + /* update tenant_vrf_id if required */ + if (vpn->tenant_vrf_id != tenant_vrf_id) { + bgpevpn_unlink_from_l3vni(vpn); + vpn->tenant_vrf_id = tenant_vrf_id; + bgpevpn_link_to_l3vni(vpn); + + /* update all routes with new export RT for VRFs */ + update_routes_for_vni(bgp, vpn); + } + if (is_vni_live(vpn) && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)) /* Probably some other param has changed that we don't @@ -2840,7 +4288,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Create or update as appropriate. */ if (!vpn) { - vpn = bgp_evpn_new(bgp, vni, originator_ip); + vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id); if (!vpn) { zlog_err( "%u: Failed to allocate VNI entry for VNI %u - at Add", @@ -2907,10 +4355,19 @@ void bgp_evpn_cleanup(struct bgp *bgp) if (bgp->import_rt_hash) hash_free(bgp->import_rt_hash); bgp->import_rt_hash = NULL; + if (bgp->vrf_import_rt_hash) + hash_free(bgp->vrf_import_rt_hash); + bgp->vrf_import_rt_hash = NULL; if (bgp->vnihash) hash_free(bgp->vnihash); bgp->vnihash = NULL; - bf_free(bgp->rd_idspace); + if (bgp->vrf_import_rtl) + list_delete_and_null(&bgp->vrf_import_rtl); + if (bgp->vrf_export_rtl) + list_delete_and_null(&bgp->vrf_export_rtl); + if (bgp->l2vnis) + list_delete_and_null(&bgp->l2vnis); + bf_release_index(bm->rd_idspace, bgp->vrf_rd_id); } /* @@ -2918,7 +4375,7 @@ void bgp_evpn_cleanup(struct bgp *bgp) * Create * VNI hash table * hash for RT to VNI - * unique rd id space for auto derivation of RD for VNIs + * assign a unique rd id for auto derivation of vrf_prd */ void bgp_evpn_init(struct bgp *bgp) { @@ -2927,7 +4384,24 @@ void bgp_evpn_init(struct bgp *bgp) bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); - bf_init(bgp->rd_idspace, UINT16_MAX); - /*assign 0th index in the bitfield, so that we start with id 1*/ - bf_assign_zero_index(bgp->rd_idspace); + bgp->vrf_import_rt_hash = + hash_create(vrf_import_rt_hash_key_make, vrf_import_rt_hash_cmp, + "BGP VRF Import RT Hash"); + bgp->vrf_import_rtl = list_new(); + bgp->vrf_import_rtl->cmp = + (int (*)(void *, void *))evpn_route_target_cmp; + + bgp->vrf_export_rtl = list_new(); + bgp->vrf_export_rtl->cmp = + (int (*)(void *, void *))evpn_route_target_cmp; + bgp->l2vnis = list_new(); + bgp->l2vnis->cmp = + (int (*)(void *, void *))vni_hash_cmp; + bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id); + +} + +void bgp_evpn_vrf_delete(struct bgp *bgp_vrf) +{ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); } diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 985f41f586..9400916845 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -22,9 +22,29 @@ #define _QUAGGA_BGP_EVPN_H #include "vxlan.h" +#include "bgpd.h" #define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */ +static inline int is_evpn_enabled(void) +{ + struct bgp *bgp = NULL; + + bgp = bgp_get_default(); + return bgp ? bgp->advertise_all_vni : 0; +} + +extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, + struct bgp_node *rn, + afi_t afi, safi_t safi); +extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, + struct bgp_node *rn, + afi_t afi, safi_t safi); +extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi, + safi_t safi); +extern void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, + safi_t safi); +extern void bgp_evpn_vrf_delete(struct bgp *bgp_vrf); 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); @@ -45,9 +65,14 @@ extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip, u_char flags); +extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id, + struct ethaddr *rmac, + struct in_addr originator_ip); +extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id); extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni); extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip); + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id); extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); extern void bgp_evpn_cleanup(struct bgp *bgp); extern void bgp_evpn_init(struct bgp *bgp); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index a58f73f4bc..2d52e1995d 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -30,9 +30,10 @@ #define RT_ADDRSTRLEN 28 -/* EVPN prefix lengths. */ +/* EVPN prefix lengths. This reprsent the sizeof struct prefix_evpn */ #define EVPN_TYPE_2_ROUTE_PREFIXLEN 224 #define EVPN_TYPE_3_ROUTE_PREFIXLEN 224 +#define EVPN_TYPE_5_ROUTE_PREFIXLEN 224 /* EVPN route types. */ typedef enum { @@ -53,6 +54,7 @@ typedef enum { */ struct bgpevpn { vni_t vni; + vrf_id_t tenant_vrf_id; u_int32_t flags; #define VNI_FLAG_CFGD 0x1 /* VNI is user configured */ #define VNI_FLAG_LIVE 0x2 /* VNI is "live" */ @@ -96,10 +98,99 @@ struct irt_node { struct list *vnis; }; +/* Mapping of Import RT to VRFs. + * The Import RTs of all VRFss are maintained in a hash table with each + * RT linking to all VRFs that will import routes matching this RT. + */ +struct vrf_irt_node { + /* RT */ + struct ecommunity_val rt; + + /* List of VNIs importing routes matching this RT. */ + struct list *vrfs; +}; + + #define RT_TYPE_IMPORT 1 #define RT_TYPE_EXPORT 2 #define RT_TYPE_BOTH 3 +static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) +{ + return (CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_RD_CFGD)); +} + +static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf, + struct prefix_rd *prd) +{ + return (memcmp(&bgp_vrf->vrf_prd.val, prd->val, ECOMMUNITY_SIZE) == 0); +} + +static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf) + return 0; + + return bgp_vrf->l3vni; +} + +static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac) +{ + struct bgp *bgp_vrf = NULL; + + memset(rmac, 0, sizeof(struct ethaddr)); + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf) + return; + memcpy(rmac, &bgp_vrf->rmac, sizeof(struct ethaddr)); +} + +static inline struct list *bgpevpn_get_vrf_export_rtl(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf) + return NULL; + + return bgp_vrf->vrf_export_rtl; +} + +static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf) + return NULL; + + return bgp_vrf->vrf_import_rtl; +} + +static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf || !bgp_vrf->l2vnis) + return; + listnode_delete(bgp_vrf->l2vnis, vpn); +} + +static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf || !bgp_vrf->l2vnis) + return; + listnode_add_sort(bgp_vrf->l2vnis, vpn); +} + static inline int is_vni_configured(struct bgpevpn *vpn) { return (CHECK_FLAG(vpn->flags, VNI_FLAG_CFGD)); @@ -157,6 +248,15 @@ static inline vni_t label2vni(mpls_label_t *label) return vni; } +static inline void encode_rmac_extcomm(struct ecommunity_val *eval, + struct ethaddr *rmac) +{ + memset(eval, 0, sizeof(*eval)); + eval->val[0] = ECOMMUNITY_ENCODE_EVPN; + eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC; + memcpy(&eval->val[2], rmac, ETH_ALEN); +} + static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq, struct ecommunity_val *eval) { @@ -171,6 +271,44 @@ static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq, eval->val[7] = seq & 0xff; } +static inline void ip_prefix_from_type5_prefix(struct prefix_evpn *evp, + struct prefix *ip) +{ + memset(ip, 0, sizeof(struct prefix)); + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + ip->family = AF_INET; + ip->prefixlen = evp->prefix.ip_prefix_length; + memcpy(&(ip->u.prefix4), + &(evp->prefix.ip.ip), + IPV4_MAX_BYTELEN); + } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + ip->family = AF_INET6; + ip->prefixlen = evp->prefix.ip_prefix_length; + memcpy(&(ip->u.prefix6), + &(evp->prefix.ip.ip), + IPV6_MAX_BYTELEN); + } +} + +static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp, + struct prefix *ip) +{ + memset(ip, 0, sizeof(struct prefix)); + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + ip->family = AF_INET; + ip->prefixlen = IPV4_MAX_BITLEN; + memcpy(&(ip->u.prefix4), + &(evp->prefix.ip.ip), + IPV4_MAX_BYTELEN); + } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + ip->family = AF_INET6; + ip->prefixlen = IPV6_MAX_BITLEN; + memcpy(&(ip->u.prefix6), + &(evp->prefix.ip.ip), + IPV6_MAX_BYTELEN); + } +} + static inline void build_evpn_type2_prefix(struct prefix_evpn *p, struct ethaddr *mac, struct ipaddr *ip) @@ -185,6 +323,31 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p, memcpy(&p->prefix.ip, ip, sizeof(*ip)); } +static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp, + struct prefix *ip_prefix) +{ + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (ip_prefix->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&ip.ipaddr_v4, &ip_prefix->u.prefix4, + sizeof(struct in_addr)); + } else { + ip.ipa_type = IPADDR_V6; + memcpy(&ip.ipaddr_v6, &ip_prefix->u.prefix6, + sizeof(struct in6_addr)); + } + + memset(evp, 0, sizeof(struct prefix_evpn)); + evp->family = AF_EVPN; + evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN; + evp->prefix.ip_prefix_length = ip_prefix->prefixlen; + evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; + evp->prefix.ip.ipa_type = ip.ipa_type; + memcpy(&evp->prefix.ip, &ip, sizeof(struct ipaddr)); +} + static inline void build_evpn_type3_prefix(struct prefix_evpn *p, struct in_addr originator_ip) { @@ -196,13 +359,41 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p, p->prefix.ip.ipaddr_v4 = originator_ip; } +static inline int advertise_type5_routes(struct bgp *bgp_vrf, + afi_t afi) +{ + if (!bgp_vrf->l3vni) + return 0; + + if (afi == AFI_IP && + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) + return 1; + + if (afi == AFI_IP6 && + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) + return 1; + + return 0; +} +extern void evpn_rt_delete_auto(struct bgp*, vni_t, struct list*); +extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd); +extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomdel); +extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd); +extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomdel); extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn); +extern void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, int withdraw); extern void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn, int withdraw); extern int bgp_evpn_install_routes(struct bgp *bgp, struct bgpevpn *vpn); extern int bgp_evpn_uninstall_routes(struct bgp *bgp, struct bgpevpn *vpn); +extern void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf); +extern void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf); extern void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn); @@ -211,8 +402,10 @@ extern void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, extern void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn); +extern void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp); extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni); extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip); + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id); extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 4114c45221..b463896c49 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -53,6 +53,117 @@ struct vni_walk_ctx { }; #if defined(HAVE_CUMULUS) +static void display_vrf_import_rt(struct vty *vty, + struct vrf_irt_node *irt, + json_object *json) +{ + u_char *pnt; + u_char type, sub_type; + struct ecommunity_as eas; + struct ecommunity_ip eip; + struct listnode *node, *nnode; + struct bgp *tmp_bgp_vrf = NULL; + json_object *json_rt = NULL; + json_object *json_vrfs = NULL; + char rt_buf[RT_ADDRSTRLEN]; + + if (json) { + json_rt = json_object_new_object(); + json_vrfs = json_object_new_array(); + } + + pnt = (u_char *)&irt->rt.val; + type = *pnt++; + sub_type = *pnt++; + if (sub_type != ECOMMUNITY_ROUTE_TARGET) + return; + + memset(&eas, 0, sizeof(eas)); + switch (type) { + case ECOMMUNITY_ENCODE_AS: + eas.as = (*pnt++ << 8); + eas.as |= (*pnt++); + pnt = ptr_get_be32(pnt, &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: + memcpy(&eip.ip, pnt, 4); + pnt += 4; + eip.val = (*pnt++ << 8); + eip.val |= (*pnt++); + + 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: + pnt = ptr_get_be32(pnt, &eas.val); + eas.val = (*pnt++ << 8); + eas.val |= (*pnt++); + + 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; + } + + if (!json) { + vty_out(vty, + "\nList of VRFs importing routes with this route-target:\n"); + } + + for (ALL_LIST_ELEMENTS(irt->vrfs, node, nnode, tmp_bgp_vrf)) { + if (json) + json_object_array_add( + json_vrfs, + json_object_new_string( + vrf_id_to_name( + tmp_bgp_vrf->vrf_id))); + else + vty_out(vty, " %s\n", + vrf_id_to_name(tmp_bgp_vrf->vrf_id)); + } + + if (json) { + json_object_object_add(json_rt, "vrfs", json_vrfs); + json_object_object_add(json, rt_buf, json_rt); + } +} + +static void show_vrf_import_rt_entry(struct hash_backet *backet, + void *args[]) +{ + json_object *json = NULL; + struct vty *vty = NULL; + struct vrf_irt_node *irt = (struct vrf_irt_node *)backet->data; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + display_vrf_import_rt(vty, irt, json); +} + static void display_import_rt(struct vty *vty, struct irt_node *irt, json_object *json) { @@ -252,6 +363,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) vty_out(vty, " (known to the kernel)"); vty_out(vty, "\n"); + vty_out(vty, " Tenant-Vrf: %s\n", + vrf_id_to_name(vpn->tenant_vrf_id)); vty_out(vty, " RD: %s\n", prefix_rd2str(&vpn->prd, buf1, sizeof(buf1))); vty_out(vty, " Originator IP: %s\n", @@ -298,6 +411,53 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object_object_add(json, "exportRts", json_export_rtl); } +static void evpn_show_vrf_routes(struct vty *vty, + struct bgp *bgp_vrf) +{ + struct bgp *bgp_def = NULL; + struct bgp_node *rn; + struct bgp_info *ri; + int header = 1; + u_int32_t prefix_cnt, path_cnt; + struct bgp_table *table; + + prefix_cnt = path_cnt = 0; + bgp_def = bgp_get_default(); + if (!bgp_def) + return; + + table = (struct bgp_table *)bgp_vrf->rib[AFI_L2VPN][SAFI_EVPN]; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + char prefix_str[BUFSIZ]; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + sizeof(prefix_str)); + + if (rn->info) { + /* Overall header/legend displayed once. */ + if (header) { + bgp_evpn_show_route_header(vty, bgp_def, NULL); + header = 0; + } + prefix_cnt++; + } + + /* For EVPN, the prefix is displayed for each path (to fit in + * with code that already exists). + */ + for (ri = rn->info; ri; ri = ri->next) { + route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL); + path_cnt++; + } + } + + if (prefix_cnt == 0) + vty_out(vty, "No EVPN prefixes exist for this VRF"); + else + vty_out(vty, "\nDisplayed %u prefixes (%u paths)", + prefix_cnt, path_cnt); +} + static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, struct vty *vty, struct in_addr vtep_ip, json_object *json) @@ -450,7 +610,7 @@ static void show_vni_entry(struct hash_backet *backet, void *args[]) } else { vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni, inet_ntoa(vpn->originator_ip), - prefix_rd2str(&vpn->prd, buf2, sizeof(buf2))); + prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); } for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { @@ -502,6 +662,9 @@ static void show_vni_entry(struct hash_backet *backet, void *args[]) break; } + if (!json) + vty_out(vty, "%-37s", vrf_id_to_name(vpn->tenant_vrf_id)); + if (json) { char vni_str[VNI_STR_LEN]; @@ -1166,40 +1329,15 @@ DEFUN(no_evpnrt5_network, } #if defined(HAVE_CUMULUS) -static void evpn_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn, - struct list *rtl) -{ - struct listnode *node, *nnode, *node_to_del; - struct ecommunity *ecom, *ecom_auto; - struct ecommunity_val eval; - - encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval); - - ecom_auto = ecommunity_new(); - ecommunity_add_val(ecom_auto, &eval); - node_to_del = NULL; - - for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { - if (ecommunity_match(ecom, ecom_auto)) { - ecommunity_free(&ecom); - node_to_del = node; - } - } - - if (node_to_del) - list_delete_node(rtl, node_to_del); - - ecommunity_free(&ecom_auto); -} static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { - evpn_rt_delete_auto(bgp, vpn, vpn->import_rtl); + evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl); } static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { - evpn_rt_delete_auto(bgp, vpn, vpn->export_rtl); + evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl); } /* @@ -1352,6 +1490,46 @@ static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn, } /* + * Configure RD for VRF + */ +static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, + struct prefix_rd *rd) +{ + /* If we have already advertise type-5 routes with a diffrent RD, we + * have to delete and withdraw them firs + */ + bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1); + + /* update RD */ + memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd)); + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD); + + /* We have a new RD for VRF. + * Advertise all type-5 routes again with the new RD + */ + bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0); +} + +/* + * Unconfigure RD for VRF + */ +static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf) +{ + /* If we have already advertise type-5 routes with a diffrent RD, we + * have to delete and withdraw them firs + */ + bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1); + + /* fall back to default RD */ + bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); + + /* We have a new RD for VRF. + * Advertise all type-5 routes again with the new RD + */ + bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0); +} + +/* * Configure RD for a VNI (vty handler) */ static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn, @@ -1403,7 +1581,10 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni) vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - vpn = bgp_evpn_new(bgp, vni, bgp->router_id); + /* tenant vrf will be updated when we get local_vni_add from + * zebra + */ + vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0); if (!vpn) { zlog_err( "%u: Failed to allocate VNI entry for VNI %u - at Config", @@ -1453,6 +1634,25 @@ static int evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn) } /* + * Display import RT mapping to VRFs (vty handler) + * bgp_def: default bgp instance + */ +static void evpn_show_vrf_import_rts(struct vty *vty, + struct bgp *bgp_def, + json_object *json) +{ + void *args[2]; + + args[0] = vty; + args[1] = json; + + hash_iterate(bgp_def->vrf_import_rt_hash, + (void (*)(struct hash_backet *, void *)) + show_vrf_import_rt_entry, + args); +} + +/* * Display import RT mapping to VNIs (vty handler) */ static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp, @@ -1778,11 +1978,11 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* RD header and legend - once overall. */ if (rd_header && !json) { vty_out(vty, - "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:" - "[MAC]\n"); + "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]\n"); vty_out(vty, - "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:" - "[OrigIP]\n\n"); + "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); + vty_out(vty, + "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n"); rd_header = 0; } @@ -2014,8 +2214,9 @@ static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp, } 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"); + vty_out(vty, " %-10s %-15s %-21s %-25s %-25s %-37s\n", "VNI", + "Orig IP", "RD", "Import RT", + "Export RT", "Tenant-Vrf"); } args[0] = vty; @@ -2239,6 +2440,115 @@ DEFUN (no_bgp_evpn_advertise_all_vni, return CMD_SUCCESS; } +DEFUN (bgp_evpn_advertise_type5, + bgp_evpn_advertise_type5_cmd, + "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR, + "Advertise prefix routes\n" + BGP_AFI_HELP_STR + BGP_SAFI_HELP_STR) +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ + int idx_afi = 0; + int idx_safi = 0; + afi_t afi = 0; + safi_t safi = 0; + + argv_find_and_parse_afi(argv, argc, &idx_afi, &afi); + argv_find_and_parse_safi(argv, argc, &idx_safi, &safi); + + if (!(afi == AFI_IP) || (afi == AFI_IP6)) { + vty_out(vty, + "%%only ipv4 or ipv6 address families are supported"); + return CMD_WARNING; + } + + if (safi != SAFI_UNICAST) { + vty_out(vty, + "%%only ipv4 unicast or ipv6 unicast are supported"); + return CMD_WARNING; + } + + if (afi == AFI_IP) { + + /* if we are already advertising ipv4 prefix as type-5 + * nothing to do + */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { + SET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN); + bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi); + } + } else { + + /* if we are already advertising ipv6 prefix as type-5 + * nothing to do + */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) { + SET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN); + bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi); + } + } + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_advertise_type5, + no_bgp_evpn_advertise_type5_cmd, + "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR, + NO_STR + "Advertise prefix routes\n" + BGP_AFI_HELP_STR + BGP_SAFI_HELP_STR) +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ + int idx_afi = 0; + int idx_safi = 0; + afi_t afi = 0; + safi_t safi = 0; + + argv_find_and_parse_afi(argv, argc, &idx_afi, &afi); + argv_find_and_parse_safi(argv, argc, &idx_safi, &safi); + + if (!(afi == AFI_IP) || (afi == AFI_IP6)) { + vty_out(vty, + "%%only ipv4 or ipv6 address families are supported"); + return CMD_WARNING; + } + + if (safi != SAFI_UNICAST) { + vty_out(vty, + "%%only ipv4 unicast or ipv6 unicast are supported"); + return CMD_WARNING; + } + + if (afi == AFI_IP) { + + /* if we are already advertising ipv4 prefix as type-5 + * nothing to do + */ + if (CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { + bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); + UNSET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN); + } + } else { + + /* if we are already advertising ipv6 prefix as type-5 + * nothing to do + */ + if (CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) { + bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); + UNSET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN); + } + } + return CMD_SUCCESS; +} + /* * Display VNI information - for all or a specific VNI */ @@ -2278,7 +2588,7 @@ DEFUN(show_bgp_l2vpn_evpn_vni, ? "Enabled" : "Disabled"); json_object_string_add(json, "advertiseAllVnis", - bgp->advertise_all_vni + is_evpn_enabled() ? "Enabled" : "Disabled"); } else { @@ -2288,8 +2598,7 @@ DEFUN(show_bgp_l2vpn_evpn_vni, /* Display all VNIs */ vty_out(vty, "Advertise All VNI flag: %s\n", - bgp->advertise_all_vni ? "Enabled" - : "Disabled"); + is_evpn_enabled() ? "Enabled" : "Disabled"); } evpn_show_all_vnis(vty, bgp, json); @@ -2530,6 +2839,33 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, } /* + * Display per-VRF EVPN routing table. + */ +DEFUN(show_bgp_l2vpn_evpn_route_vrf, show_bgp_l2vpn_evpn_route_vrf_cmd, + "show bgp l2vpn evpn route vrf VRFNAME", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VRF\n" + "VRF Name\n") +{ + int vrf_idx = 6; + char *vrf_name = NULL; + struct bgp *bgp_vrf = NULL; + + vrf_name = argv[vrf_idx]->arg; + bgp_vrf = bgp_lookup_by_name(vrf_name); + if (!bgp_vrf) + return CMD_WARNING; + + evpn_show_vrf_routes(vty, bgp_vrf); + + return CMD_SUCCESS; +} + +/* * Display per-VNI EVPN routing table. */ DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd, @@ -2787,6 +3123,42 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all, /* * Display EVPN import route-target hash table */ +DEFUN(show_bgp_l2vpn_evpn_vrf_import_rt, + show_bgp_l2vpn_evpn_vrf_import_rt_cmd, + "show bgp l2vpn evpn vrf-import-rt [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show vrf import route target\n" + JSON_STR) +{ + u_char uj = 0; + struct bgp *bgp_def = NULL; + json_object *json = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) + return CMD_WARNING; + + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + + evpn_show_vrf_import_rts(vty, bgp_def, 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 [json]", @@ -2978,6 +3350,91 @@ DEFUN_NOSH (exit_vni, return CMD_SUCCESS; } +DEFUN (bgp_evpn_vrf_rd, + bgp_evpn_vrf_rd_cmd, + "rd ASN:NN_OR_IP-ADDRESS:NN", + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n") +{ + int ret; + struct prefix_rd prd; + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + + if (!bgp_vrf) + return CMD_WARNING; + + ret = str2prefix_rd(argv[1]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + + /* If same as existing value, there is nothing more to do. */ + if (bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd)) + return CMD_SUCCESS; + + /* Configure or update the RD. */ + evpn_configure_vrf_rd(bgp_vrf, &prd); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_vrf_rd, + no_bgp_evpn_vrf_rd_cmd, + "no rd ASN:NN_OR_IP-ADDRESS:NN", + NO_STR + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n") +{ + int ret; + struct prefix_rd prd; + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + + if (!bgp_vrf) + return CMD_WARNING; + + ret = str2prefix_rd(argv[2]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + + /* Check if we should disallow. */ + if (!is_vrf_rd_configured(bgp_vrf)) { + vty_out(vty, "%% RD is not configured for this VRF\n"); + return CMD_WARNING; + } + + if (!bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd)) { + vty_out(vty, + "%% RD specified does not match configuration for this VRF\n"); + return CMD_WARNING; + } + + evpn_unconfigure_vrf_rd(bgp_vrf); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_vrf_rd_without_val, + no_bgp_evpn_vrf_rd_without_val_cmd, + "no rd", + NO_STR + "Route Distinguisher\n") +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + + if (!bgp_vrf) + return CMD_WARNING; + + /* Check if we should disallow. */ + if (!is_vrf_rd_configured(bgp_vrf)) { + vty_out(vty, "%% RD is not configured for this VRF\n"); + return CMD_WARNING; + } + + evpn_unconfigure_vrf_rd(bgp_vrf); + return CMD_SUCCESS; +} + DEFUN (bgp_evpn_vni_rd, bgp_evpn_vni_rd_cmd, "rd ASN:NN_OR_IP-ADDRESS:NN", @@ -3084,6 +3541,279 @@ static int bgp_evpn_rt_matches_existing(struct list *rtl, return 0; } +/* display L3VNI related info for a VRF instance */ +DEFUN (show_bgp_vrf_l3vni_info, + show_bgp_vrf_l3vni_info_cmd, + "show bgp vrf VRFNAME l3vni info [json]", + SHOW_STR + BGP_STR + "show bgp vrf\n" + "VRF Name\n" + "L3-VNI\n" + "L3-VNI info\n" + JSON_STR) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + int idx_vrf = 3; + const char *name = NULL; + struct bgp *bgp = NULL; + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + struct ecommunity *ecom = NULL; + json_object *json = NULL; + json_object *json_vnis = NULL; + json_object *json_export_rts = NULL; + json_object *json_import_rts = NULL; + u_char uj = use_json(argc, argv); + + if (uj) { + json = json_object_new_object(); + json_vnis = json_object_new_array(); + json_export_rts = json_object_new_array(); + json_import_rts = json_object_new_array(); + } + + name = argv[idx_vrf]->arg; + bgp = bgp_lookup_by_name(name); + if (!bgp) { + if (!uj) + vty_out(vty, "BGP instance for VRF %s not found", + name); + else { + json_object_string_add(json, "warning", + "BGP instance not found"); + vty_out(vty, "%s\n", + json_object_to_json_string(json)); + json_object_free(json); + } + return CMD_WARNING; + } + + if (!json) { + vty_out(vty, "BGP VRF: %s\n", name); + vty_out(vty, " Local-Ip: %s\n", + inet_ntoa(bgp->originator_ip)); + vty_out(vty, " L3-VNI: %u\n", bgp->l3vni); + vty_out(vty, " Rmac: %s\n", + prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); + vty_out(vty, " L2-VNI List:\n"); + vty_out(vty, " "); + for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn)) + vty_out(vty, "%u ", vpn->vni); + vty_out(vty, "\n"); + vty_out(vty, " Export-RTs:\n"); + vty_out(vty, " "); + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom)) + vty_out(vty, "%s ", ecommunity_str(ecom)); + vty_out(vty, "\n"); + vty_out(vty, " Import-RTs:\n"); + vty_out(vty, " "); + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom)) + vty_out(vty, "%s ", ecommunity_str(ecom)); + vty_out(vty, "\n"); + vty_out(vty, " RD: %s\n", + prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN)); + } else { + json_object_string_add(json, "vrf", name); + json_object_string_add(json, "local-ip", + inet_ntoa(bgp->originator_ip)); + json_object_int_add(json, "l3vni", bgp->l3vni); + json_object_string_add(json, "rmac", + prefix_mac2str(&bgp->rmac, buf, + sizeof(buf))); + /* list of l2vnis */ + for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn)) + json_object_array_add(json_vnis, + json_object_new_int(vpn->vni)); + json_object_object_add(json, "l2vnis", json_vnis); + + /* export rts */ + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom)) + json_object_array_add(json_export_rts, + json_object_new_string( + ecommunity_str(ecom))); + json_object_object_add(json, "export-rts", json_export_rts); + + /* import rts */ + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom)) + json_object_array_add(json_import_rts, + json_object_new_string( + ecommunity_str(ecom))); + json_object_object_add(json, "import-rts", json_import_rts); + json_object_string_add( + json, "rd", + prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN)); + + } + + 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; +} + +/* import/export rt for l3vni-vrf */ +DEFUN (bgp_evpn_vrf_rt, + bgp_evpn_vrf_rt_cmd, + "route-target <both|import|export> RT", + "Route Target\n" + "import and export\n" + "import\n" + "export\n" + "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + int rt_type; + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + struct ecommunity *ecomadd = NULL; + + if (!bgp) + return CMD_WARNING; + + if (!strcmp(argv[1]->arg, "import")) + rt_type = RT_TYPE_IMPORT; + else if (!strcmp(argv[1]->arg, "export")) + rt_type = RT_TYPE_EXPORT; + else if (!strcmp(argv[1]->arg, "both")) + rt_type = RT_TYPE_BOTH; + else { + vty_out(vty, "%% Invalid Route Target type\n"); + return CMD_WARNING; + } + + /* Add/update the import route-target */ + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) { + ecomadd = ecommunity_str2com(argv[2]->arg, + ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomadd); + + /* Do nothing if we already have this import route-target */ + if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, + ecomadd)) + bgp_evpn_configure_import_rt_for_vrf(bgp, ecomadd); + } + + /* Add/update the export route-target */ + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) { + ecomadd = ecommunity_str2com(argv[2]->arg, + ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomadd); + + /* Do nothing if we already have this export route-target */ + if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, + ecomadd)) + bgp_evpn_configure_export_rt_for_vrf(bgp, ecomadd); + } + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_vrf_rt, + no_bgp_evpn_vrf_rt_cmd, + "no route-target <both|import|export> RT", + NO_STR + "Route Target\n" + "import and export\n" + "import\n" + "export\n" + "ASN:XX or A.B.C.D:XX\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int rt_type, found_ecomdel; + struct ecommunity *ecomdel = NULL; + + if (!bgp) + return CMD_WARNING; + + if (!strcmp(argv[2]->arg, "import")) + rt_type = RT_TYPE_IMPORT; + else if (!strcmp(argv[2]->arg, "export")) + rt_type = RT_TYPE_EXPORT; + else if (!strcmp(argv[2]->arg, "both")) + rt_type = RT_TYPE_BOTH; + else { + vty_out(vty, "%% Invalid Route Target type\n"); + return CMD_WARNING; + } + + if (rt_type == RT_TYPE_IMPORT) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { + vty_out(vty, + "%% Import RT is not configured for this VRF\n"); + return CMD_WARNING; + } + } else if (rt_type == RT_TYPE_EXPORT) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { + vty_out(vty, + "%% Export RT is not configured for this VRF\n"); + return CMD_WARNING; + } + } else if (rt_type == RT_TYPE_BOTH) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD) + && !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { + vty_out(vty, + "%% Import/Export RT is not configured for this VRF\n"); + return CMD_WARNING; + } + } + + ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomdel) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomdel); + + if (rt_type == RT_TYPE_IMPORT) { + if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, + ecomdel)) { + vty_out(vty, + "%% RT specified does not match configuration for this VRF\n"); + return CMD_WARNING; + } + bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel); + } else if (rt_type == RT_TYPE_EXPORT) { + if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, + ecomdel)) { + vty_out(vty, + "%% RT specified does not match configuration for this VRF\n"); + return CMD_WARNING; + } + bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel); + } else if (rt_type == RT_TYPE_BOTH) { + found_ecomdel = 0; + + if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, + ecomdel)) { + bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel); + found_ecomdel = 1; + } + + if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, + ecomdel)) { + bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel); + found_ecomdel = 1; + } + + if (!found_ecomdel) { + vty_out(vty, + "%% RT specified does not match configuration for this VRF\n"); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} DEFUN (bgp_evpn_vni_rt, bgp_evpn_vni_rt_cmd, @@ -3306,6 +4036,12 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->advertise_gw_macip) vty_out(vty, " advertise-default-gw\n"); + + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) + vty_out(vty, " advertise ipv4 unicast\n"); + + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) + vty_out(vty, " advertise ipv6 unicast\n"); } void bgp_ethernetvpn_init(void) @@ -3333,6 +4069,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd); /* "show bgp l2vpn evpn" commands. */ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); @@ -3341,11 +4079,13 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd); /* "show bgp evpn" commands. */ install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd); @@ -3358,6 +4098,7 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd); install_element(VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd); install_element(VIEW_NODE, &show_bgp_evpn_import_rt_cmd); + install_element(VIEW_NODE, &show_bgp_vrf_l3vni_info_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd); @@ -3368,6 +4109,11 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rd_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rd_cmd); + install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_default_gw_vni_cmd); install_element(BGP_EVPN_VNI_NODE, diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 7dd4253b2e..a720d31a76 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -221,6 +221,7 @@ static __attribute__((__noreturn__)) void bgp_exit(int status) #endif bgp_zebra_destroy(); + bf_free(bm->rd_idspace); list_delete_and_null(&bm->bgp); memset(bm, 0, sizeof(*bm)); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 37054ce425..64543ff019 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -117,4 +117,5 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value") DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information") DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT") +DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT") DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 35b83a0401..fae98329c6 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -113,5 +113,6 @@ DECLARE_MTYPE(LCOMMUNITY_VAL) DECLARE_MTYPE(BGP_EVPN) DECLARE_MTYPE(BGP_EVPN_IMPORT_RT) +DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT) DECLARE_MTYPE(BGP_EVPN_MACIP) #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 81b605ad79..c691233c57 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2222,6 +2222,14 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, } } + /* advertise/withdraw type-5 routes */ + if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { + if (new_select) + bgp_evpn_advertise_type5_route(bgp, rn, afi, safi); + else if (old_select) + bgp_evpn_withdraw_type5_route(bgp, rn, afi, safi); + } + /* Clear any route change flags. */ bgp_zebra_clear_route_change_flags(rn); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 085de3fabb..ae4759aad0 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -73,9 +73,12 @@ struct bgp_info_extra { /* Nexthop reachability check. */ u_int32_t igpmetric; - /* MPLS label. */ + /* MPLS label - L2VNI */ mpls_label_t label; + /* MPLS label - L3-VNI */ + mpls_label_t label2; + #if ENABLE_BGP_VNC union { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 50e5bcf096..cff5c1b2e3 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -58,6 +58,7 @@ #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_bfd.h" #include "bgpd/bgp_io.h" +#include "bgpd/bgp_evpn.h" static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); @@ -873,6 +874,8 @@ DEFUN_NOSH (router_bgp, */ } + /* unset the auto created flag as the user config is now present */ + UNSET_FLAG(bgp->vrf_flags, BGP_VRF_AUTO); VTY_PUSH_CONTEXT(BGP_NODE, bgp); return CMD_SUCCESS; @@ -909,6 +912,12 @@ DEFUN (no_router_bgp, "%% Multiple BGP processes are configured\n"); return CMD_WARNING_CONFIG_FAILED; } + + if (bgp->l3vni) { + vty_out(vty, "%% Please unconfigure l3vni %u", + bgp->l3vni); + return CMD_WARNING_CONFIG_FAILED; + } } else { as = strtoul(argv[idx_asn]->arg, NULL, 10); @@ -921,6 +930,12 @@ DEFUN (no_router_bgp, vty_out(vty, "%% Can't find BGP instance\n"); return CMD_WARNING_CONFIG_FAILED; } + + if (bgp->l3vni) { + vty_out(vty, "%% Please unconfigure l3vni %u", + bgp->l3vni); + return CMD_WARNING_CONFIG_FAILED; + } } bgp_delete(bgp); @@ -6450,6 +6465,7 @@ DEFUN (show_bgp_vrfs, "Show BGP VRFs\n" JSON_STR) { + char buf[ETHER_ADDR_STRLEN]; struct list *inst = bm->bgp; struct listnode *node; struct bgp *bgp; @@ -6457,8 +6473,6 @@ DEFUN (show_bgp_vrfs, json_object *json = NULL; json_object *json_vrfs = NULL; int count = 0; - static char header[] = - "Type Id RouterId #PeersCfg #PeersEstb Name"; if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { vty_out(vty, "BGP Multiple Instance is not enabled\n"); @@ -6484,7 +6498,10 @@ DEFUN (show_bgp_vrfs, count++; if (!uj && count == 1) - vty_out(vty, "%s\n", header); + vty_out(vty, + "%4s %-5s %-16s %9s %10s %-37s %-10s %-15s\n", + "Type", "Id", "routerId", "#PeersVfg", + "#PeersEstb", "Name", "L3-VNI", "Rmac"); peers_cfg = peers_estb = 0; if (uj) @@ -6518,11 +6535,17 @@ DEFUN (show_bgp_vrfs, json_object_int_add(json_vrf, "numEstablishedPeers", peers_estb); + json_object_int_add(json_vrf, "l3vni", bgp->l3vni); + json_object_string_add(json_vrf, "rmac", + prefix_mac2str(&bgp->rmac, buf, + sizeof(buf))); json_object_object_add(json_vrfs, name, json_vrf); } else - vty_out(vty, "%4s %-5d %-16s %9u %10u %s\n", type, - vrf_id_ui, inet_ntoa(bgp->router_id), peers_cfg, - peers_estb, name); + vty_out(vty, + "%4s %-5d %-16s %9u %10u %-37s %-10u %-15s\n", + type, vrf_id_ui, inet_ntoa(bgp->router_id), + peers_cfg, peers_estb, name, bgp->l3vni, + prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); } if (uj) { @@ -7685,7 +7708,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, } if (afi == AFI_L2VPN && safi == SAFI_EVPN) { - if (p->bgp->advertise_all_vni) + if (is_evpn_enabled()) json_object_boolean_true_add( json_addr, "advertiseAllVnis"); } @@ -7957,7 +7980,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, /* advertise-vni-all */ if (afi == AFI_L2VPN && safi == SAFI_EVPN) { - if (p->bgp->advertise_all_vni) + if (is_evpn_enabled()) vty_out(vty, " advertise-all-vni\n"); } diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index b2db54ef79..30e3c6f31d 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -999,6 +999,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, /* Make Zebra API structure. */ memset(&api, 0, sizeof(api)); + memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr)); api.vrf_id = bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; api.safi = safi; @@ -1015,6 +1016,13 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (info->sub_type == BGP_ROUTE_AGGREGATE) zapi_route_set_blackhole(&api, BLACKHOLE_NULL); + /* If it is an EVPN route mark as such. + * Currently presence of rmac in attr denotes + * this is an EVPN type-2 route + */ + if (!is_zero_mac(&(info->attr->rmac))) + SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE); + if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED || info->sub_type == BGP_ROUTE_AGGREGATE) { SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); @@ -1072,7 +1080,14 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, api_nh = &api.nexthops[valid_nh_count]; api_nh->gate.ipv4 = *nexthop; - api_nh->type = NEXTHOP_TYPE_IPV4; + + /* EVPN type-2 routes are + programmed as onlink on l3-vni SVI + */ + if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + else + api_nh->type = NEXTHOP_TYPE_IPV4; } else { ifindex_t ifindex; struct in6_addr *nexthop; @@ -1126,8 +1141,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; } - if (mpinfo->extra - && bgp_is_valid_label(&mpinfo->extra->label)) { + if (mpinfo->extra && bgp_is_valid_label(&mpinfo->extra->label) + && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) { has_valid_label = 1; label = label_pton(&mpinfo->extra->label); @@ -1137,7 +1152,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, valid_nh_count++; } - if (has_valid_label) + /* if this is a evpn route we don't have to include the label */ + if (has_valid_label && + !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))) SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); if (info->sub_type != BGP_ROUTE_AGGREGATE) @@ -1179,7 +1196,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, sizeof(nh_buf)); label_buf[0] = '\0'; - if (has_valid_label) + if (has_valid_label && + !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) sprintf(label_buf, "label %u", api_nh->labels[0]); zlog_debug(" nhop [%d]: %s %s", i + 1, nh_buf, @@ -1233,11 +1251,19 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi) return; memset(&api, 0, sizeof(api)); + memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr)); api.vrf_id = peer->bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; api.safi = safi; api.prefix = *p; + /* If it is an EVPN route mark as such. + * Currently presence of rmac in attr denotes + * this is an EVPN type-2 route + */ + if (!is_zero_mac(&(info->attr->rmac))) + SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE); + if (peer->sort == BGP_PEER_IBGP) { SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); @@ -1551,7 +1577,7 @@ void bgp_zebra_instance_register(struct bgp *bgp) /* For default instance, register to learn about VNIs, if appropriate. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT - && bgp->advertise_all_vni) + && is_evpn_enabled()) bgp_zebra_advertise_all_vni(bgp, 1); } @@ -1570,7 +1596,7 @@ void bgp_zebra_instance_deregister(struct bgp *bgp) /* For default instance, unregister learning about VNIs, if appropriate. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT - && bgp->advertise_all_vni) + && is_evpn_enabled()) bgp_zebra_advertise_all_vni(bgp, 0); /* Deregister for router-id, interfaces, redistributed routes. */ @@ -1676,6 +1702,39 @@ static void bgp_zebra_connected(struct zclient *zclient) */ } +static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + char buf[ETHER_ADDR_STRLEN]; + vni_t l3vni = 0; + struct ethaddr rmac; + struct in_addr originator_ip; + struct stream *s; + + memset(&rmac, 0, sizeof(struct ethaddr)); + memset(&originator_ip, 0, sizeof(struct in_addr)); + s = zclient->ibuf; + l3vni = stream_getl(s); + if (cmd == ZEBRA_L3VNI_ADD) { + stream_get(&rmac, s, sizeof(struct ethaddr)); + originator_ip.s_addr = stream_get_ipv4(s); + } + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s", + (cmd == ZEBRA_L3VNI_ADD) ? "add" : "del", + vrf_id_to_name(vrf_id), + l3vni, + prefix_mac2str(&rmac, buf, sizeof(buf))); + + if (cmd == ZEBRA_L3VNI_ADD) + bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip); + else + bgp_evpn_local_l3vni_del(l3vni, vrf_id); + + return 0; +} + static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { @@ -1683,23 +1742,29 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, vni_t vni; struct bgp *bgp; struct in_addr vtep_ip; + vrf_id_t tenant_vrf_id = VRF_DEFAULT; s = zclient->ibuf; vni = stream_getl(s); - if (command == ZEBRA_VNI_ADD) + if (command == ZEBRA_VNI_ADD) { vtep_ip.s_addr = stream_get_ipv4(s); + stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); + } + bgp = bgp_lookup_by_vrf_id(vrf_id); if (!bgp) return 0; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx VNI %s VRF %u VNI %u", - (command == ZEBRA_VNI_ADD) ? "add" : "del", vrf_id, - vni); + zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s", + (command == ZEBRA_VNI_ADD) ? "add" : "del", + vrf_id_to_name(vrf_id), + vni, vrf_id_to_name(tenant_vrf_id)); if (command == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( - bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id); + bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id, + tenant_vrf_id); else return bgp_evpn_local_vni_del(bgp, vni); } @@ -1783,6 +1848,8 @@ void bgp_zebra_init(struct thread_master *master) zclient->local_vni_del = bgp_zebra_process_local_vni; zclient->local_macip_add = bgp_zebra_process_local_macip; zclient->local_macip_del = bgp_zebra_process_local_macip; + zclient->local_l3vni_add = bgp_zebra_process_local_l3vni; + zclient->local_l3vni_del = bgp_zebra_process_local_l3vni; } void bgp_zebra_destroy(void) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index d76d5b0b7e..5356cfd2fc 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -79,7 +79,7 @@ #include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_keepalives.h" #include "bgpd/bgp_io.h" - +#include "bgpd/bgp_ecommunity.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); DEFINE_QOBJ_TYPE(bgp_master) @@ -218,7 +218,7 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id) return 0; /* EVPN uses router id in RD, withdraw them */ - if (bgp->advertise_all_vni) + if (is_evpn_enabled()) bgp_evpn_handle_router_id_update(bgp, TRUE); IPV4_ADDR_COPY(&bgp->router_id, id); @@ -235,7 +235,7 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id) } /* EVPN uses router id in RD, update them */ - if (bgp->advertise_all_vni) + if (is_evpn_enabled()) bgp_evpn_handle_router_id_update(bgp, FALSE); return 0; @@ -1151,15 +1151,13 @@ struct peer *peer_new(struct bgp *bgp) * - We RX a BGP_UPDATE where the attributes alone are just * under BGP_MAX_PACKET_SIZE * - The user configures an outbound route-map that does many as-path - * prepends or adds many communities. At most they can have - * CMD_ARGC_MAX - * args in a route-map so there is a finite limit on how large they - * can - * make the attributes. + * prepends or adds many communities. At most they can have + * CMD_ARGC_MAX args in a route-map so there is a finite limit on how + * large they can make the attributes. * * Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid - * bounds - * checking for every single attribute as we construct an UPDATE. + * bounds checking for every single attribute as we construct an + * UPDATE. */ peer->obuf_work = stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW); @@ -3145,6 +3143,9 @@ int bgp_delete(struct bgp *bgp) bgp->name); } + /* unmap from RT list */ + bgp_evpn_vrf_delete(bgp); + /* Stop timers. */ if (bgp->t_rmap_def_originate_eval) { BGP_TIMER_OFF(bgp->t_rmap_def_originate_eval); @@ -7083,6 +7084,11 @@ int bgp_config_write(struct vty *vty) /* BGP configuration. */ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + + /* skip all auto created vrf as they dont have user config */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) + continue; + /* Router bgp ASN */ vty_out(vty, "router bgp %u", bgp->as); @@ -7307,6 +7313,38 @@ int bgp_config_write(struct vty *vty) if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) vty_out(vty, " no auto-summary\n"); + /* import route-target */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { + char *ecom_str; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, + ecom)) { + ecom_str = ecommunity_ecom2str( + ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " route-target import %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } + } + + /* export route-target */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { + char *ecom_str; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, + ecom)) { + ecom_str = ecommunity_ecom2str( + ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " route-target export %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } + } + /* IPv4 unicast configuration. */ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); @@ -7367,6 +7405,13 @@ void bgp_master_init(struct thread_master *master) bgp_process_queue_init(); + /* init the rd id space. + assign 0th index in the bitfield, + so that we start with id 1 + */ + bf_init(bm->rd_idspace, UINT16_MAX); + bf_assign_zero_index(bm->rd_idspace); + /* Enable multiple instances by default. */ bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); @@ -7531,6 +7576,7 @@ void bgp_terminate(void) */ /* reverse bgp_master_init */ bgp_close(); + if (bm->listen_sockets) list_delete_and_null(&bm->listen_sockets); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 20b99c171e..8e843eb659 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -36,6 +36,7 @@ #include "defaults.h" #include "bgp_memory.h" #include "bitfield.h" +#include "vxlan.h" #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -136,6 +137,9 @@ struct bgp_master { /* clang-format off */ #define RMAP_DEFAULT_UPDATE_TIMER 5 /* disabled by default */ + /* Id space for automatic RD derivation for an EVI/VRF */ + bitfield_t rd_idspace; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp_master) @@ -408,8 +412,41 @@ struct bgp { /* Hash table of Import RTs to EVIs */ struct hash *import_rt_hash; - /* Id space for automatic RD derivation for an EVI */ - bitfield_t rd_idspace; + /* Hash table of VRF import RTs to VRFs */ + struct hash *vrf_import_rt_hash; + + /* L3-VNI corresponding to this vrf */ + vni_t l3vni; + + /* router-mac to be used in mac-ip routes for this vrf */ + struct ethaddr rmac; + + /* originator ip - to be used as NH for type-5 routes */ + struct in_addr originator_ip; + + /* vrf flags */ + uint32_t vrf_flags; +#define BGP_VRF_AUTO (1 << 0) +#define BGP_VRF_ADVERTISE_IPV4_IN_EVPN (1 << 1) +#define BGP_VRF_ADVERTISE_IPV6_IN_EVPN (1 << 2) +#define BGP_VRF_IMPORT_RT_CFGD (1 << 3) +#define BGP_VRF_EXPORT_RT_CFGD (1 << 4) +#define BGP_VRF_RD_CFGD (1 << 5) + + /* unique ID for auto derivation of RD for this vrf */ + uint16_t vrf_rd_id; + + /* RD for this VRF */ + struct prefix_rd vrf_prd; + + /* import rt list for the vrf instance */ + struct list *vrf_import_rtl; + + /* export rt list for the vrf instance */ + struct list *vrf_export_rtl; + + /* list of corresponding l2vnis (struct bgpevpn) */ + struct list *l2vnis; QOBJ_FIELDS }; diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 7f2dbe7f9d..e3cb739464 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -4097,7 +4097,8 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) } } - if (hc->default_rd.prefixlen || hc->default_response_lifetime + if (hc->default_rd.prefixlen + || hc->default_response_lifetime != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT || hc->default_rt_import_list || hc->default_rt_export_list || hc->nve_groups_sequential->count) { @@ -4125,7 +4126,8 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) buf, sizeof(buf))); } - if (hc->default_response_lifetime) { + if (hc->default_response_lifetime + != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT) { vty_out(vty, " response-lifetime "); if (hc->default_response_lifetime != UINT32_MAX) vty_out(vty, "%d", diff --git a/lib/command.h b/lib/command.h index 42dd1c5325..fa8323bf2d 100644 --- a/lib/command.h +++ b/lib/command.h @@ -190,7 +190,7 @@ struct cmd_node { #define CMD_NOT_MY_INSTANCE 14 /* Argc max counts. */ -#define CMD_ARGC_MAX 25 +#define CMD_ARGC_MAX 256 /* Turn off these macros when uisng cpp with extract.pl */ #ifndef VTYSH_EXTRACT_PL diff --git a/lib/command_match.c b/lib/command_match.c index c60373f910..f6b07a0b20 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -28,8 +28,6 @@ DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack") -#define MAXDEPTH 256 - #ifdef TRACE_MATCHER #define TM 1 #else @@ -84,7 +82,7 @@ static enum match_type match_mac(const char *, bool); enum matcher_rv command_match(struct graph *cmdgraph, vector vline, struct list **argv, const struct cmd_element **el) { - struct graph_node *stack[MAXDEPTH]; + struct graph_node *stack[CMD_ARGC_MAX]; enum matcher_rv status; *argv = NULL; @@ -200,7 +198,7 @@ static enum matcher_rv command_match_r(struct graph_node *start, vector vline, /* check history/stack of tokens * this disallows matching the same one more than once if there is a * circle in the graph (used for keyword arguments) */ - if (n == MAXDEPTH) + if (n == CMD_ARGC_MAX) return MATCHER_NO_MATCH; if (!token->allowrepeat) for (size_t s = 0; s < n; s++) diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index 66b042ad97..79c951dd69 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -33,8 +33,6 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") -#define MAXDEPTH 64 - /** headers **/ void grammar_sandbox_init(void); void pretty_print_graph(struct vty *vty, struct graph_node *, int, int, @@ -262,7 +260,7 @@ DEFUN (grammar_test_show, { check_nodegraph(); - struct graph_node *stack[MAXDEPTH]; + struct graph_node *stack[CMD_ARGC_MAX]; pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3, stack, 0); return CMD_SUCCESS; @@ -277,8 +275,8 @@ DEFUN (grammar_test_dot, { check_nodegraph(); - struct graph_node *stack[MAXDEPTH]; - struct graph_node *visited[MAXDEPTH * MAXDEPTH]; + struct graph_node *stack[CMD_ARGC_MAX]; + struct graph_node *visited[CMD_ARGC_MAX * CMD_ARGC_MAX]; size_t vpos = 0; FILE *ofd = fopen(argv[2]->arg, "w"); @@ -334,7 +332,7 @@ static void cmd_graph_permute(struct list *out, struct graph_node **stack, return; } - if (++stackpos == MAXDEPTH) + if (++stackpos == CMD_ARGC_MAX) return; for (i = 0; i < vector_active(gn->to); i++) { @@ -354,7 +352,7 @@ static void cmd_graph_permute(struct list *out, struct graph_node **stack, static struct list *cmd_graph_permutations(struct graph *graph) { char accumulate[2048] = ""; - struct graph_node *stack[MAXDEPTH]; + struct graph_node *stack[CMD_ARGC_MAX]; struct list *rv = list_new(); rv->cmp = cmd_permute_cmp; @@ -532,7 +530,7 @@ void pretty_print_graph(struct vty *vty, struct graph_node *start, int level, vty_out(vty, " ?'%s'", tok->desc); vty_out(vty, " "); - if (stackpos == MAXDEPTH) { + if (stackpos == CMD_ARGC_MAX) { vty_out(vty, " -aborting! (depth limit)\n"); return; } @@ -586,7 +584,7 @@ static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start, if (visited[i] == start) return; visited[(*visitpos)++] = start; - if ((*visitpos) == MAXDEPTH * MAXDEPTH) + if ((*visitpos) == CMD_ARGC_MAX * CMD_ARGC_MAX) return; snprintf(tokennum, sizeof(tokennum), "%d?", tok->type); @@ -626,7 +624,7 @@ static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start, } fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color); - if (stackpos == MAXDEPTH) + if (stackpos == CMD_ARGC_MAX) return; stack[stackpos++] = start; @@ -945,6 +945,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW), DESC_ENTRY(ZEBRA_VNI_ADD), DESC_ENTRY(ZEBRA_VNI_DEL), + DESC_ENTRY(ZEBRA_L3VNI_ADD), + DESC_ENTRY(ZEBRA_L3VNI_DEL), DESC_ENTRY(ZEBRA_REMOTE_VTEP_ADD), DESC_ENTRY(ZEBRA_REMOTE_VTEP_DEL), DESC_ENTRY(ZEBRA_MACIP_ADD), diff --git a/lib/nexthop.h b/lib/nexthop.h index 20b0cd5227..a727f37057 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -80,6 +80,7 @@ struct nexthop { #define NEXTHOP_FLAG_MATCHED (1 << 4) /* Already matched vs a nexthop */ #define NEXTHOP_FLAG_FILTERED (1 << 5) /* rmap filtered, used by static only */ #define NEXTHOP_FLAG_DUPLICATE (1 << 6) /* nexthop duplicates another active one */ +#define NEXTHOP_FLAG_EVPN_RVTEP (1 << 7) /* EVPN remote vtep nexthop */ #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ && !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE)) diff --git a/lib/prefix.c b/lib/prefix.c index 10f77bda87..9f13cb8bb1 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -301,7 +301,7 @@ static const struct in6_addr maskbytes6[] = { #define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) -static int is_zero_mac(const struct ethaddr *mac) +int is_zero_mac(struct ethaddr *mac) { int i = 0; @@ -1043,10 +1043,11 @@ static const char *prefixevpn2str(const struct prefix *p, char *str, int size) family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p) ? AF_INET : AF_INET6; - snprintf(str, size, "[%d]:[%u][%s]/%d", + snprintf(str, size, "[%d]:[%u][%s/%d]/%d", p->u.prefix_evpn.route_type, p->u.prefix_evpn.eth_tag, inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf, PREFIX2STR_BUFFER), + p->u.prefix_evpn.ip_prefix_length, p->prefixlen); } else { sprintf(str, "Unsupported EVPN route type %d", diff --git a/lib/prefix.h b/lib/prefix.h index 0732cf1290..7e947ea48a 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -348,6 +348,7 @@ extern void masklen2ip6(const int, struct in6_addr *); extern const char *inet6_ntoa(struct in6_addr); +extern int is_zero_mac(struct ethaddr *mac); extern int prefix_str2mac(const char *str, struct ethaddr *mac); extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); @@ -398,4 +399,13 @@ static inline int is_default_prefix(const struct prefix *p) return 0; } +static inline int is_host_route(struct prefix *p) +{ + if (p->family == AF_INET) + return (p->prefixlen == IPV4_MAX_BITLEN); + else if (p->family == AF_INET6) + return (p->prefixlen == IPV6_MAX_BITLEN); + return 0; +} + #endif /* _ZEBRA_PREFIX_H */ @@ -225,6 +225,17 @@ static void vrf_disable(struct vrf *vrf) (*vrf_master.vrf_disable_hook)(vrf); } +const char *vrf_id_to_name(vrf_id_t vrf_id) +{ + struct vrf *vrf; + + vrf = vrf_lookup_by_id(vrf_id); + if (vrf) + return vrf->name; + + return "n/a"; +} + vrf_id_t vrf_name_to_id(const char *name) { struct vrf *vrf; @@ -103,6 +103,7 @@ extern struct vrf_name_head vrfs_by_name; extern struct vrf *vrf_lookup_by_id(vrf_id_t); extern struct vrf *vrf_lookup_by_name(const char *); extern struct vrf *vrf_get(vrf_id_t, const char *); +extern const char *vrf_id_to_name(vrf_id_t vrf_id); extern vrf_id_t vrf_name_to_id(const char *); #define VRF_GET_ID(V, NAME) \ diff --git a/lib/zclient.c b/lib/zclient.c index 655e4e1a80..4177ce1a71 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -912,6 +912,8 @@ int zapi_route_encode(u_char cmd, struct stream *s, struct zapi_route *api) stream_putl(s, api->flags); stream_putc(s, api->message); stream_putc(s, api->safi); + if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_put(s, &(api->rmac), sizeof(struct ethaddr)); /* Put prefix information. */ stream_putc(s, api->prefix.family); @@ -1032,6 +1034,8 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETL(s, api->flags); STREAM_GETC(s, api->message); STREAM_GETC(s, api->safi); + if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_get(&(api->rmac), s, sizeof(struct ethaddr)); /* Prefix. */ STREAM_GETC(s, api->prefix.family); @@ -2205,6 +2209,16 @@ static int zclient_read(struct thread *thread) (*zclient->local_vni_del)(command, zclient, length, vrf_id); break; + case ZEBRA_L3VNI_ADD: + if (zclient->local_l3vni_add) + (*zclient->local_l3vni_add)(command, zclient, length, + vrf_id); + break; + case ZEBRA_L3VNI_DEL: + if (zclient->local_l3vni_del) + (*zclient->local_l3vni_del)(command, zclient, length, + vrf_id); + break; case ZEBRA_MACIP_ADD: if (zclient->local_macip_add) (*zclient->local_macip_add)(command, zclient, length, diff --git a/lib/zclient.h b/lib/zclient.h index de58044671..cc34fd9d2c 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -112,6 +112,8 @@ typedef enum { ZEBRA_ADVERTISE_ALL_VNI, ZEBRA_VNI_ADD, ZEBRA_VNI_DEL, + ZEBRA_L3VNI_ADD, + ZEBRA_L3VNI_DEL, ZEBRA_REMOTE_VTEP_ADD, ZEBRA_REMOTE_VTEP_DEL, ZEBRA_MACIP_ADD, @@ -200,6 +202,8 @@ struct zclient { int (*fec_update)(int, struct zclient *, uint16_t); int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t); + int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t); + int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t); int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t); @@ -277,6 +281,8 @@ struct zapi_route { u_int32_t mtu; vrf_id_t vrf_id; + + struct ethaddr rmac; }; /* Zebra IPv4 route message API. */ diff --git a/lib/zebra.h b/lib/zebra.h index fa5fa89f77..1eb0c56252 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -409,6 +409,7 @@ extern const char *zserv_command_string(unsigned int command); #define ZEBRA_FLAG_STATIC 0x40 #define ZEBRA_FLAG_SCOPE_LINK 0x100 #define ZEBRA_FLAG_FIB_OVERRIDE 0x200 +#define ZEBRA_FLAG_EVPN_ROUTE 0x400 /* ZEBRA_FLAG_BLACKHOLE was 0x04 */ /* ZEBRA_FLAG_REJECT was 0x80 */ diff --git a/zebra/connected.c b/zebra/connected.c index 9c0a3af8e3..7b949c5041 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -396,10 +396,10 @@ void connected_down(struct interface *ifp, struct connected *ifc) * head. */ rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, false); + &p, NULL, &nh, 0, 0, false, NULL); rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - 0, &p, NULL, &nh, 0, 0, false); + 0, &p, NULL, &nh, 0, 0, false, NULL); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index d45a502543..9fd7bb1c24 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1039,7 +1039,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - NULL, 0, 0, true); + NULL, 0, 0, true, NULL); if (!nh.type) { nh.type = NEXTHOP_TYPE_IPV4; @@ -1054,7 +1054,7 @@ void rtm_read(struct rt_msghdr *rtm) else rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, true); + &nh, 0, 0, true, NULL); } if (dest.sa.sa_family == AF_INET6) { /* One day we might have a debug section here like one in the @@ -1085,7 +1085,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - NULL, 0, 0, true); + NULL, 0, 0, true, NULL); if (!nh.type) { nh.type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX @@ -1102,7 +1102,7 @@ void rtm_read(struct rt_msghdr *rtm) else rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, true); + &nh, 0, 0, true, NULL); } } diff --git a/zebra/main.c b/zebra/main.c index 5eb8e75b87..e26c8e3d69 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -300,7 +300,6 @@ int main(int argc, char **argv) zebra_if_init(); zebra_debug_init(); router_id_cmd_init(); - zebra_vty_init(); access_list_init(); prefix_list_init(); #if defined(HAVE_RTADV) @@ -322,6 +321,12 @@ int main(int argc, char **argv) * routing socket. */ zebra_ns_init(); + /* + * Initialize show/config command after the vrf initialization is + * complete + */ + zebra_vty_init(); + #if defined(HANDLE_ZAPI_FUZZING) if (fuzzing) { zserv_read_file(fuzzing); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index cbf77765a3..3c6a2a7daf 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -570,7 +570,7 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table, re->flags, &p, NULL, re->nexthop, - zebrad.rtm_table_default, re->metric, false); + zebrad.rtm_table_default, re->metric, false, NULL); return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index 804372f18e..c7e83480ca 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -305,7 +305,8 @@ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *, extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - u_int32_t table_id, u_int32_t metric, bool fromkernel); + u_int32_t table_id, u_int32_t metric, bool fromkernel, + struct ethaddr *rmac); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t, union g_addr *, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 910f9b3d93..a77814668d 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -543,13 +543,13 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, memcpy(&nh.gate, gate, sz); rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, NULL, &nh, - table, metric, true); + table, metric, true, NULL); } else { /* XXX: need to compare the entire list of nexthops * here for NLM_F_APPEND stupidity */ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, NULL, NULL, - table, metric, true); + table, metric, true, NULL); } } diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 602dc5ea41..5ede948e0a 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -30,6 +30,7 @@ #include "zebra_vrf.h" #include "zebra_memory.h" #include "rt.h" +#include "zebra_vxlan.h" DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -49,6 +50,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) #endif zns->if_table = route_table_init(); + zebra_vxlan_ns_init(zns); kernel_init(zns); interface_list(zns); route_read(zns); @@ -61,6 +63,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) struct zebra_ns *zns = (struct zebra_ns *)(*info); route_table_finish(zns->if_table); + zebra_vxlan_ns_disable(zns); #if defined(HAVE_RTADV) rtadv_terminate(zns); #endif diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 6cfba93e50..0c340d8d59 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -49,6 +49,9 @@ struct zebra_ns { struct route_table *if_table; + /* L3-VNI hash table (for EVPN). Only in default instance */ + struct hash *l3vni_table; + #if defined(HAVE_RTADV) struct rtadv rtadv; #endif /* HAVE_RTADV */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 83834899c8..41e14459b1 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -50,6 +50,7 @@ #include "zebra/zebra_rnh.h" #include "zebra/interface.h" #include "zebra/connected.h" +#include "zebra/zebra_vxlan.h" DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) @@ -259,7 +260,8 @@ struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, /*Pending: need to think if null ifp here is ok during bootup? There was a crash because ifp here was coming to be NULL */ if (ifp) - if (connected_is_unnumbered(ifp)) { + if (connected_is_unnumbered(ifp) || + CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); } @@ -839,7 +841,9 @@ static unsigned nexthop_active_check(struct route_node *rn, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - if (nexthop_active(AFI_IP, re, nexthop, set, rn)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_EVPN_RVTEP)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else if (nexthop_active(AFI_IP, re, nexthop, set, rn)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -2314,7 +2318,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - u_int32_t table_id, u_int32_t metric, bool fromkernel) + u_int32_t table_id, u_int32_t metric, bool fromkernel, + struct ethaddr *rmac) { struct route_table *table; struct route_node *rn; @@ -2467,6 +2472,22 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, return; } + + if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) { + struct nexthop *tmp_nh; + + for (ALL_NEXTHOPS(re->nexthop, tmp_nh)) { + struct ipaddr vtep_ip; + + memset(&vtep_ip, 0, sizeof(struct ipaddr)); + vtep_ip.ipa_type = IPADDR_V4; + memcpy(&(vtep_ip.ipaddr_v4), + &(tmp_nh->gate.ipv4), + sizeof(struct in_addr)); + zebra_vxlan_evpn_vrf_route_del(re->vrf_id, rmac, + &vtep_ip, p); + } + } rib_delnode(rn, same); } diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 0d0a8dd747..1ae9eac61f 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -250,6 +250,10 @@ static int zebra_vrf_delete(struct vrf *vrf) route_table_finish(zvrf->rnh_table[afi]); route_table_finish(zvrf->import_check_table[afi]); } + + /* cleanup evpn states for vrf */ + zebra_vxlan_vrf_delete(zvrf); + list_delete_all_node(zvrf->rid_all_sorted_list); list_delete_all_node(zvrf->rid_lo_sorted_list); XFREE(MTYPE_ZEBRA_VRF, zvrf); @@ -474,6 +478,8 @@ static int vrf_config_write(struct vty *vty) if (strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) { vty_out(vty, "vrf %s\n", zvrf_name(zvrf)); + if (zvrf->l3vni) + vty_out(vty, " vni %u\n", zvrf->l3vni); vty_out(vty, "!\n"); } } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 1dfc0b3eb8..c7a0717ee8 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -24,6 +24,7 @@ #include <zebra/zebra_ns.h> #include <zebra/zebra_pw.h> +#include <lib/vxlan.h> /* MPLS (Segment Routing) global block */ typedef struct mpls_srgb_t_ { @@ -114,6 +115,9 @@ struct zebra_vrf { */ int advertise_gw_macip; + /* l3-vni info */ + vni_t l3vni; + /* Route Installs */ uint64_t installs; uint64_t removals; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index ccd0c703d4..82b0157ad3 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -49,6 +49,7 @@ #include "zebra/zserv.h" #include "zebra/router-id.h" #include "zebra/ipforward.h" +#include "zebra/zebra_vxlan_private.h" extern int allow_delete; @@ -65,6 +66,14 @@ static void vty_show_ip_route_summary(struct vty *vty, static void vty_show_ip_route_summary_prefix(struct vty *vty, struct route_table *table); +/* + * special macro to allow us to get the correct zebra_vrf + */ +#define ZEBRA_DECLVAR_CONTEXT(A, B) \ + struct vrf *A = VTY_GET_CONTEXT(vrf); \ + struct zebra_vrf *B = \ + (vrf) ? vrf->info : NULL; \ + /* VNI range as per RFC 7432 */ #define CMD_VNI_RANGE "(1-16777215)" @@ -1896,6 +1905,110 @@ DEFUN (show_vrf, return CMD_SUCCESS; } +DEFUN (vrf_vni_mapping, + vrf_vni_mapping_cmd, + "vni " CMD_VNI_RANGE, + "VNI\n" + "VNI-ID\n") +{ + int ret = 0; + + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + vni_t vni = strtoul(argv[1]->arg, NULL, 10); + char err[ERR_STR_SZ]; + + assert(vrf); + assert(zvrf); + + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1); + if (ret != 0) { + vty_out(vty, "%s\n", err); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_vrf_vni_mapping, + no_vrf_vni_mapping_cmd, + "no vni " CMD_VNI_RANGE, + NO_STR + "VNI\n" + "VNI-ID") +{ + int ret = 0; + char err[ERR_STR_SZ]; + vni_t vni = strtoul(argv[2]->arg, NULL, 10); + + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + + assert(vrf); + assert(zvrf); + + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0); + if (ret != 0) { + vty_out(vty, "%s\n", err); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* show vrf */ +DEFUN (show_vrf_vni, + show_vrf_vni_cmd, + "show vrf vni [json]", + SHOW_STR + "VRF\n" + "VNI\n" + JSON_STR) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + json_object *json = NULL; + json_object *json_vrfs = NULL; + u_char uj = use_json(argc, argv); + + if (uj) { + json = json_object_new_object(); + json_vrfs = json_object_new_array(); + } + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + zvrf = vrf->info; + if (!zvrf) + continue; + + if (!zvrf->l3vni) + continue; + + if (!uj) { + vty_out(vty, "vrf: %s VNI: %u", + zvrf_name(zvrf), + zvrf->l3vni); + vty_out(vty, "\n"); + } else { + json_object *json_vrf = NULL; + + json_vrf = json_object_new_object(); + json_object_string_add(json_vrf, "vrf", + zvrf_name(zvrf)); + json_object_int_add(json_vrf, "l3vni", + zvrf->l3vni); + json_object_array_add(json_vrfs, json_vrf); + } + } + + if (uj) { + json_object_object_add(json, "vrfs", json_vrfs); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + DEFUN (show_evpn_vni, show_evpn_vni_cmd, "show evpn vni [json]", @@ -1931,6 +2044,161 @@ DEFUN (show_evpn_vni_vni, return CMD_SUCCESS; } +DEFUN (show_evpn_l3vni, + show_evpn_l3vni_cmd, + "show evpn l3vni [json]", + SHOW_STR + "EVPN\n" + "L3 VNI\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_l3vnis(vty, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_l3vni_vni, + show_evpn_l3vni_vni_cmd, + "show evpn l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "L3 VxLAN Network Identifier\n" + "VNI number\n" + JSON_STR) +{ + vni_t vni; + u_char uj = use_json(argc, argv); + + vni = strtoul(argv[3]->arg, NULL, 10); + zebra_vxlan_print_l3vni(vty, vni, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni_mac, + show_evpn_rmac_l3vni_mac_cmd, + "show evpn rmac l3vni " CMD_VNI_RANGE " mac WORD [json]", + SHOW_STR + "EVPN\n" + "RMAC\n" + "L3-VNI\n" + "VNI number\n" + "MAC\n" + "mac-address (e.g. 0a:0a:0a:0a:0a:0a)\n" + JSON_STR) +{ + vni_t l3vni = 0; + struct ethaddr mac; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + if (!prefix_str2mac(argv[6]->arg, &mac)) { + vty_out(vty, "%% Malformed MAC address\n"); + return CMD_WARNING; + } + zebra_vxlan_print_specific_rmac_l3vni(vty, l3vni, &mac, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni, + show_evpn_rmac_l3vni_cmd, + "show evpn rmac l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "RMAC\n" + "L3-VNI\n" + "VNI number\n" + JSON_STR) +{ + vni_t l3vni = 0; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + zebra_vxlan_print_rmacs_l3vni(vty, l3vni, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni_all, + show_evpn_rmac_l3vni_all_cmd, + "show evpn rmac l3vni all [json]", + SHOW_STR + "EVPN\n" + "RMAC addresses\n" + "L3-VNI\n" + "All VNIs\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_rmacs_all_l3vni(vty, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni_ip, + show_evpn_nh_l3vni_ip_cmd, + "show evpn next-hops l3vni " CMD_VNI_RANGE " ip WORD [json]", + SHOW_STR + "EVPN\n" + "Remote Vteps\n" + "L3-VNI\n" + "VNI number\n" + "Ip address\n" + "Host address (ipv4 or ipv6)\n" + JSON_STR) +{ + vni_t l3vni; + struct ipaddr ip; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + if (str2ipaddr(argv[6]->arg, &ip) != 0) { + if (!uj) + vty_out(vty, "%% Malformed Neighbor address\n"); + return CMD_WARNING; + } + zebra_vxlan_print_specific_nh_l3vni(vty, l3vni, &ip, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni, + show_evpn_nh_l3vni_cmd, + "show evpn next-hops l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "Remote Vteps\n" + "L3-VNI\n" + "VNI number\n" + JSON_STR) +{ + vni_t l3vni; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + zebra_vxlan_print_nh_l3vni(vty, l3vni, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni_all, + show_evpn_nh_l3vni_all_cmd, + "show evpn next-hops l3vni all [json]", + SHOW_STR + "EVPN\n" + "Remote VTEPs\n" + "L3-VNI\n" + "All VNIs\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_nh_all_l3vni(vty, uj); + + return CMD_SUCCESS; +} + DEFUN (show_evpn_mac_vni, show_evpn_mac_vni_cmd, "show evpn mac vni " CMD_VNI_RANGE "[json]", @@ -2601,6 +2869,7 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_zebra_packet_process_cmd); install_element(VIEW_NODE, &show_vrf_cmd); + install_element(VIEW_NODE, &show_vrf_vni_cmd); install_element(VIEW_NODE, &show_route_cmd); install_element(VIEW_NODE, &show_route_detail_cmd); install_element(VIEW_NODE, &show_route_summary_cmd); @@ -2626,6 +2895,14 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_vni_cmd); install_element(VIEW_NODE, &show_evpn_vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_l3vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_mac_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_all_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_ip_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_all_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd); @@ -2635,4 +2912,9 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_neigh_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd); + + install_element(CONFIG_NODE, &no_vrf_vni_mapping_cmd); + install_element(VRF_NODE, &vrf_vni_mapping_cmd); + install_element(VRF_NODE, &no_vrf_vni_mapping_cmd); + } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 9c70b55a1a..0ef1802367 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -48,7 +48,9 @@ #include "zebra/zebra_l2.h" #include "lib/json.h" +DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); +DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC"); DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); @@ -61,6 +63,10 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, void **args); +static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, + json_object *json); +static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, + json_object *json); static void zvni_print_mac(zebra_mac_t *mac, void *ctxt); static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt); @@ -91,11 +97,46 @@ static int zvni_neigh_send_del_to_client(vni_t vni, struct ethaddr *macaddr, u_char flags); static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n); static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n); -static zebra_vni_t *zvni_map_svi(struct interface *ifp, +static zebra_vni_t *zvni_from_svi(struct interface *ifp, struct interface *br_if); static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); +/* l3-vni next-hop neigh related APIs */ +static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, + struct ipaddr *ip); +static void *zl3vni_nh_alloc(void *p); +static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct ethaddr *rmac); +static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); +static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); +static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); + +/* l3-vni rmac related APIs */ +static void zl3vni_print_rmac_hash(struct hash_backet *, void *); +static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac); +static void *zl3vni_rmac_alloc(void *p); +static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac); +static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); +static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); +static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac); + +/* l3-vni related APIs*/ +static int is_vni_l3(vni_t); +static zebra_l3vni_t *zl3vni_lookup(vni_t vni); +static void *zl3vni_alloc(void *p); +static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id); +static int zl3vni_del(zebra_l3vni_t *zl3vni); +static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t); +static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); +static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); +static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni); +static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni); + static unsigned int mac_hash_keymake(void *p); static int mac_cmp(const void *p1, const void *p2); static void *zvni_mac_alloc(void *p); @@ -385,6 +426,80 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, json_object_object_add(json, vni_str, json_vni); } +/* print a specific next hop for an l3vni */ +static void zl3vni_print_nh(zebra_neigh_t *n, + struct vty *vty, + json_object *json) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct listnode *node = NULL; + struct prefix *p = NULL; + json_object *json_hosts = NULL; + + if (!json) { + vty_out(vty, "Ip: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " RMAC: %s\n", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + vty_out(vty, " Host-List:\n"); + for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p)) + vty_out(vty, " %s\n", + prefix2str(p, buf2, sizeof(buf2))); + } else { + json_hosts = json_object_new_array(); + json_object_string_add(json, "ip", + ipaddr2str(&(n->ip), buf2, + sizeof(buf2))); + json_object_string_add(json, "rmac", + prefix_mac2str(&n->emac, buf2, + sizeof(buf2))); + for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p)) + json_object_array_add(json_hosts, + json_object_new_string( + prefix2str(p, buf2, + sizeof(buf2)))); + json_object_object_add(json, "hosts", json_hosts); + } +} + +/* Print a specific RMAC entry */ +static void zl3vni_print_rmac(zebra_mac_t *zrmac, + struct vty *vty, + json_object *json) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[PREFIX_STRLEN]; + struct listnode *node = NULL; + struct prefix *p = NULL; + json_object *json_hosts = NULL; + + if (!json) { + vty_out(vty, "MAC: %s\n", + prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1))); + vty_out(vty, " Remote VTEP: %s\n", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + vty_out(vty, " Host-List:\n"); + for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p)) + vty_out(vty, " %s\n", + prefix2str(p, buf2, sizeof(buf2))); + } else { + json_hosts = json_object_new_array(); + json_object_string_add(json, "Rmac", + prefix_mac2str(&zrmac->macaddr, + buf1, + sizeof(buf1))); + json_object_string_add(json, "vtep-ip", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p)) + json_object_array_add(json_hosts, + json_object_new_string( + prefix2str(p, buf2, + sizeof(buf2)))); + json_object_object_add(json, "hosts", json_hosts); + } +} + /* * Print a specific MAC entry. */ @@ -603,6 +718,234 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) } } +static void zl3vni_print_nh_hash(struct hash_backet *backet, + void *ctx) +{ + struct nh_walk_ctx *wctx = NULL; + struct vty *vty = NULL; + struct json_object *json_vni = NULL; + struct json_object *json_nh = NULL; + zebra_neigh_t *n = NULL; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + wctx = (struct nh_walk_ctx *)ctx; + vty = wctx->vty; + json_vni = wctx->json; + if (json_vni) + json_nh = json_object_new_object(); + n = (zebra_neigh_t *)backet->data; + if (!n) + return; + + if (!json_vni) { + vty_out(vty, "%-15s %-17s %6d\n", + ipaddr2str(&(n->ip), buf2, sizeof(buf2)), + prefix_mac2str(&n->emac, buf1, sizeof(buf1)), + listcount(n->host_list)); + } else { + json_object_string_add(json_nh, "nexthop-ip", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + json_object_string_add(json_nh, "rmac", + prefix_mac2str(&n->emac, buf1, + sizeof(buf1))); + json_object_int_add(json_nh, "refCnt", listcount(n->host_list)); + json_object_object_add(json_vni, + ipaddr2str(&(n->ip), buf2, sizeof(buf2)), + json_nh); + } +} + +static void zl3vni_print_nh_hash_all_vni(struct hash_backet *backet, + void **args) +{ + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + uint32_t num_nh = 0; + struct nh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) { + if (json) + vty_out(vty, "{}\n"); + return; + } + + num_nh = hashcount(zl3vni->nh_table); + if (!num_nh) + return; + + if (json) { + json_vni = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + } + + if (json == NULL) { + vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", + zl3vni->vni, num_nh); + vty_out(vty, "%-15s %-17s %6s\n", "IP", + "RMAC", "Refcnt"); + } else + json_object_int_add(json_vni, "numNh", num_nh); + + memset(&wctx, 0, sizeof(struct nh_walk_ctx)); + wctx.vty = vty; + wctx.json = json_vni; + hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + if (json) + json_object_object_add(json, vni_str, json_vni); +} + +static void zl3vni_print_rmac_hash_all_vni(struct hash_backet *backet, + void **args) +{ + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + u_int32_t num_rmacs; + struct rmac_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) { + if (json) + vty_out(vty, "{}\n"); + return; + } + + num_rmacs = hashcount(zl3vni->rmac_table); + if (!num_rmacs) + return; + + if (json) { + json_vni = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + } + + if (json == NULL) { + vty_out(vty, "\nVNI %u #MACs %u\n\n", + zl3vni->vni, num_rmacs); + vty_out(vty, "%-17s %-21s %-6s\n", "MAC", + "Remote VTEP", "Refcnt"); + } else + json_object_int_add(json_vni, "numRmacs", num_rmacs); + + /* assign per-vni to wctx->json object to fill macs + * under the vni. Re-assign primary json object to fill + * next vni information. + */ + memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); + wctx.vty = vty; + wctx.json = json_vni; + hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); + if (json) + json_object_object_add(json, vni_str, json_vni); +} + +static void zl3vni_print_rmac_hash(struct hash_backet *backet, + void *ctx) +{ + zebra_mac_t *zrmac = NULL; + struct rmac_walk_ctx *wctx = NULL; + struct vty *vty = NULL; + struct json_object *json = NULL; + struct json_object *json_rmac = NULL; + char buf[ETHER_ADDR_STRLEN]; + + wctx = (struct rmac_walk_ctx *)ctx; + vty = wctx->vty; + json = wctx->json; + if (json) + json_rmac = json_object_new_object(); + zrmac = (zebra_mac_t *)backet->data; + if (!zrmac) + return; + + if (!json) { + vty_out(vty, "%-17s %-21s %-6d\n", + prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), + inet_ntoa(zrmac->fwd_info.r_vtep_ip), + listcount(zrmac->host_list)); + } else { + json_object_string_add(json_rmac, "rmac", + prefix_mac2str(&zrmac->macaddr, buf, + sizeof(buf))); + json_object_string_add(json_rmac, "vtep-ip", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + json_object_int_add(json_rmac, "refcnt", + listcount(zrmac->host_list)); + json_object_object_add(json, + prefix_mac2str(&zrmac->macaddr, buf, + sizeof(buf)), + json_rmac); + } +} + +/* print a specific L3 VNI entry */ +static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) +{ + char buf[ETHER_ADDR_STRLEN]; + struct vty *vty = NULL; + json_object *json = NULL; + zebra_vni_t *zvni = NULL; + json_object *json_vni_list = NULL; + struct listnode *node = NULL, *nnode = NULL; + + vty = ctx[0]; + json = ctx[1]; + + if (!json) { + vty_out(vty, "VNI: %u\n", zl3vni->vni); + vty_out(vty, " Local Vtep Ip: %s", + inet_ntoa(zl3vni->local_vtep_ip)); + vty_out(vty, " Vxlan-Intf: %s\n", + zl3vni_vxlan_if_name(zl3vni)); + vty_out(vty, " SVI-If: %s\n", + zl3vni_svi_if_name(zl3vni)); + vty_out(vty, " State: %s\n", + zl3vni_state2str(zl3vni)); + vty_out(vty, " Vrf: %s\n", + zl3vni_vrf_name(zl3vni)); + vty_out(vty, " Rmac: %s\n", + zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); + vty_out(vty, " L2-VNIs: "); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) + vty_out(vty, "%u ", zvni->vni); + vty_out(vty, "\n"); + } else { + json_vni_list = json_object_new_array(); + json_object_int_add(json, "vni", zl3vni->vni); + json_object_string_add(json, "local-vtep-ip", + inet_ntoa(zl3vni->local_vtep_ip)); + json_object_string_add(json, "vxlan-intf", + zl3vni_vxlan_if_name(zl3vni)); + json_object_string_add(json, "svi-if", + zl3vni_svi_if_name(zl3vni)); + json_object_string_add(json, "state", + zl3vni_state2str(zl3vni)); + json_object_string_add(json, "vrf", + zl3vni_vrf_name(zl3vni)); + json_object_string_add(json, "rmac", + zl3vni_rmac2str(zl3vni, buf, + sizeof(buf))); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) { + json_object_array_add(json_vni_list, + json_object_new_int(zvni->vni)); + } + json_object_object_add(json, "l2-vnis", json_vni_list); + } +} + /* * Print a specific VNI entry. */ @@ -619,10 +962,14 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt) vty = ctxt[0]; json = ctxt[1]; - if (json == NULL) + if (json == NULL) { vty_out(vty, "VNI: %u\n", zvni->vni); - else + vty_out(vty, " VRF: %s\n", vrf_id_to_name(zvni->vrf_id)); + } else { json_object_int_add(json, "vni", zvni->vni); + json_object_string_add(json, "vrf", + vrf_id_to_name(zvni->vrf_id)); + } if (!zvni->vxlan_if) { // unexpected if (json == NULL) @@ -682,6 +1029,56 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt) } } +/* print a L3 VNI hash entry */ +static void zl3vni_print_hash(struct hash_backet *backet, + void *ctx[]) +{ + char buf[ETHER_ADDR_STRLEN]; + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + + vty = (struct vty *)ctx[0]; + json = (json_object *)ctx[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) + return; + + if (!json) { + vty_out(vty, "%-10u %-15s %-20s %-20s %-5s %-37s %-18s\n", + zl3vni->vni, + inet_ntoa(zl3vni->local_vtep_ip), + zl3vni_vxlan_if_name(zl3vni), + zl3vni_svi_if_name(zl3vni), + zl3vni_state2str(zl3vni), + zl3vni_vrf_name(zl3vni), + zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); + } else { + char vni_str[VNI_STR_LEN]; + + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "vni", zl3vni->vni); + json_object_string_add(json_vni, "local-ip", + inet_ntoa(zl3vni->local_vtep_ip)); + json_object_string_add(json_vni, "vxlan-if", + zl3vni_vxlan_if_name(zl3vni)); + json_object_string_add(json_vni, "svi-if", + zl3vni_svi_if_name(zl3vni)); + json_object_string_add(json_vni, "state", + zl3vni_state2str(zl3vni)); + json_object_string_add(json_vni, "vrf", + zl3vni_vrf_name(zl3vni)); + json_object_string_add(json_vni, "rmac", + zl3vni_rmac2str(zl3vni, buf, + sizeof(buf))); + json_object_object_add(json, vni_str, json_vni); + } + +} + /* * Print a VNI hash entry - called for display of all VNIs. */ @@ -714,10 +1111,12 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]) num_macs = num_valid_macs(zvni); num_neigh = hashcount(zvni->neigh_table); if (json == NULL) - vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni, + vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u %-37s\n", + zvni->vni, zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh, - num_vteps); + num_vteps, + vrf_id_to_name(zvni->vrf_id)); else { char vni_str[VNI_STR_LEN]; snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); @@ -753,11 +1152,11 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ipaddr *ip, u_char flags, u_int16_t cmd) { - struct zserv *client; - struct stream *s; - int ipa_len; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + int ipa_len; + struct zserv *client = NULL; + struct stream *s = NULL; client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); /* BGP may not be running. */ @@ -785,12 +1184,13 @@ static int zvni_macip_send_msg_to_client(vni_t vni, stream_putc(s, flags); /* sticky mac/gateway mac */ + /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Send MACIP %s flags 0x%x MAC %s IP %s VNI %u to %s", + "Send MACIP %s flags 0x%x MAC %s IP %s L2-VNI %u to %s", (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, prefix_mac2str(macaddr, buf, sizeof(buf)), ipaddr2str(ip, buf2, sizeof(buf2)), vni, @@ -1000,7 +1400,7 @@ static void zvni_process_neigh_on_local_mac_add(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_INACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u is now ACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now ACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1040,7 +1440,7 @@ static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_ACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u is now INACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1077,7 +1477,7 @@ static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_ACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u INACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1302,12 +1702,12 @@ static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, struct ethaddr *macaddr, struct ipaddr *ip) { - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; zif = zvni->vxlan_if->info; if (!zif) @@ -1353,7 +1753,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP", + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP", ifp->name, ifp->ifindex, zvni->vni, prefix_mac2str(macaddr, buf, sizeof(buf)), ipaddr2str(ip, buf2, sizeof(buf2))); @@ -1370,10 +1770,10 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, struct ipaddr *ip) { - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; /* If the neigh entry is not present nothing to do*/ n = zvni_neigh_lookup(zvni, ip); @@ -1395,7 +1795,7 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s del to BGP", ifp->name, ifp->ifindex, zvni->vni, prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), ipaddr2str(ip, buf2, sizeof(buf2))); @@ -1747,7 +2147,8 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp, * Map SVI and associated bridge to a VNI. This is invoked upon getting * neighbor notifications, to see if they are of interest. */ -static zebra_vni_t *zvni_map_svi(struct interface *ifp, struct interface *br_if) +static zebra_vni_t *zvni_from_svi(struct interface *ifp, + struct interface *br_if) { struct zebra_ns *zns; struct route_node *rn; @@ -2133,13 +2534,15 @@ static int zvni_send_add_to_client(zebra_vni_t *zvni) zserv_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT); stream_putl(s, zvni->vni); stream_put_in_addr(s, &zvni->local_vtep_ip); + stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_ADD %u %s to %s", + zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s", zvni->vni, inet_ntoa(zvni->local_vtep_ip), + vrf_id_to_name(zvni->vrf_id), zebra_route_string(client->proto)); client->vniadd_cnt++; @@ -2189,10 +2592,9 @@ static void zvni_build_hash_table() /* Walk VxLAN interfaces and create VNI hash. */ zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + vni_t vni; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; - zebra_vni_t *zvni; - vni_t vni; ifp = (struct interface *)rn->info; if (!ifp) @@ -2200,39 +2602,83 @@ static void zvni_build_hash_table() zif = ifp->info; if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) continue; - vxl = &zif->l2info.vxl; + vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Create VNI hash for intf %s(%u) VNI %u local IP %s", - ifp->name, ifp->ifindex, vni, - inet_ntoa(vxl->vtep_ip)); + if (is_vni_l3(vni)) { + zebra_l3vni_t *zl3vni = NULL; - /* VNI hash entry is not expected to exist. */ - zvni = zvni_lookup(vni); - if (zvni) { - zlog_err( - "VNI hash already present for IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - continue; - } + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("create L3-VNI hash for Intf %s(%u) L3-VNI %u", + ifp->name, ifp->ifindex, vni); - zvni = zvni_add(vni); - if (!zvni) { - zlog_err( - "Failed to add VNI hash, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return; - } + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return; + } - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; + /* associate with vxlan_if */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->vxlan_if = ifp; - /* Inform BGP if interface is up and mapped to bridge. */ - if (if_is_operative(ifp) && zif->brslave_info.br_if) - zvni_send_add_to_client(zvni); + /* + * we need to associate with SVI. + * we can associate with svi-if only after association + * with vxlan-intf is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + + } else { + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Create L2-VNI hash for intf %s(%u) L2-VNI %u local IP %s", + ifp->name, ifp->ifindex, vni, + inet_ntoa(vxl->vtep_ip)); + + /* VNI hash entry is not expected to exist. */ + zvni = zvni_lookup(vni); + if (zvni) { + zlog_err( + "VNI hash already present for IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + continue; + } + + zvni = zvni_add(vni); + if (!zvni) { + zlog_err( + "Failed to add VNI hash, IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + return; + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + + /* Inform BGP if intf is up and mapped to bridge. */ + if (if_is_operative(ifp) && zif->brslave_info.br_if) + zvni_send_add_to_client(zvni); + } } } @@ -2351,12 +2797,18 @@ static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip) */ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf) { - zebra_vni_t *zvni; + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; zvni = (zebra_vni_t *)backet->data; if (!zvni) return; + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zvni->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zvni); + /* Free up all neighbors and MACs, if any. */ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); @@ -2368,9 +2820,1288 @@ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf) zvni_del(zvni); } +/* cleanup L3VNI */ +static void zl3vni_cleanup_all(struct hash_backet *backet, + void *args) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) + return; + + zebra_vxlan_process_l3vni_oper_down(zl3vni); +} + +static int is_host_present_in_host_list(struct list *list, + struct prefix *host) +{ + struct listnode *node = NULL; + struct prefix *p = NULL; + + for (ALL_LIST_ELEMENTS_RO(list, node, p)) { + if (prefix_same(p, host)) + return 1; + } + return 0; +} + +static void host_list_add_host(struct list *list, + struct prefix *host) +{ + struct prefix *p = NULL; + + p = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct prefix)); + memcpy(p, host, sizeof(struct prefix)); + + listnode_add_sort(list, p); +} + +static void host_list_delete_host(struct list *list, + struct prefix *host) +{ + struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; + struct prefix *p = NULL; + + for (ALL_LIST_ELEMENTS(list, node, nnode, p)) { + if (prefix_same(p, host)) { + XFREE(MTYPE_HOST_PREFIX, p); + node_to_del = node; + } + } + + if (node_to_del) + list_delete_node(list, node_to_del); +} + +/* + * Look up MAC hash entry. + */ +static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + zebra_mac_t tmp; + zebra_mac_t *pmac; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.macaddr, rmac, ETH_ALEN); + pmac = hash_lookup(zl3vni->rmac_table, &tmp); + + return pmac; +} + +/* + * Callback to allocate RMAC hash entry. + */ +static void *zl3vni_rmac_alloc(void *p) +{ + const zebra_mac_t *tmp_rmac = p; + zebra_mac_t *zrmac; + + zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + *zrmac = *tmp_rmac; + + return ((void *)zrmac); +} + +/* + * Add RMAC entry to l3-vni + */ +static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + zebra_mac_t tmp_rmac; + zebra_mac_t *zrmac = NULL; + + memset(&tmp_rmac, 0, sizeof(zebra_mac_t)); + memcpy(&tmp_rmac.macaddr, rmac, ETH_ALEN); + zrmac = hash_get(zl3vni->rmac_table, &tmp_rmac, zl3vni_rmac_alloc); + assert(zrmac); + + zrmac->host_list = list_new(); + zrmac->host_list->cmp = (int (*)(void *, void *))prefix_cmp; + + SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE); + SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC); + + return zrmac; +} + +/* + * Delete MAC entry. + */ +static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + zebra_mac_t *tmp_rmac; + + if (zrmac->host_list) + list_delete_and_null(&zrmac->host_list); + zrmac->host_list = NULL; + + tmp_rmac = hash_release(zl3vni->rmac_table, zrmac); + if (tmp_rmac) + XFREE(MTYPE_MAC, tmp_rmac); + + return 0; +} + +/* + * Install remote RMAC into the kernel. + */ +static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) || + !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC))) + return 0; + + zif = zl3vni->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + return kernel_add_mac(zl3vni->vxlan_if, vxl->access_vlan, + &zrmac->macaddr, + zrmac->fwd_info.r_vtep_ip, 0); +} + +/* + * Uninstall remote RMAC from the kernel. + */ +static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + char buf[ETHER_ADDR_STRLEN]; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) || + !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC))) + return 0; + + if (!zl3vni->vxlan_if) { + zlog_err( + "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if", + prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), + zl3vni->vni, zl3vni); + return -1; + } + + zif = zl3vni->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan, + &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0); +} + +/* handle rmac add */ +static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_mac_t *zrmac = NULL; + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) { + + zrmac = zl3vni_rmac_add(zl3vni, rmac); + if (!zrmac) { + zlog_warn( + "Failed to add RMAC %s L3VNI %u Remote VTEP %s", + prefix_mac2str(rmac, buf, + sizeof(buf)), + zl3vni->vni, ipaddr2str(vtep_ip, buf1, + sizeof(buf1))); + return -1; + } + memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info)); + zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4; + + /* install rmac in kernel */ + zl3vni_rmac_install(zl3vni, zrmac); + } + + if (!is_host_present_in_host_list(zrmac->host_list, host_prefix)) + host_list_add_host(zrmac->host_list, host_prefix); + return 0; +} + + +/* handle rmac delete */ +static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac, + struct prefix *host_prefix) +{ + zebra_mac_t *zrmac = NULL; + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) + return -1; + + host_list_delete_host(zrmac->host_list, host_prefix); + if (list_isempty(zrmac->host_list)) { + + /* uninstall from kernel */ + zl3vni_rmac_uninstall(zl3vni, zrmac); + + /* del the rmac entry */ + zl3vni_rmac_del(zl3vni, zrmac); + } + return 0; +} + +/* + * Look up nh hash entry on a l3-vni. + */ +static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, + struct ipaddr *ip) +{ + zebra_neigh_t tmp; + zebra_neigh_t *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(zl3vni->nh_table, &tmp); + + return n; +} + + +/* + * Callback to allocate NH hash entry on L3-VNI. + */ +static void *zl3vni_nh_alloc(void *p) +{ + const zebra_neigh_t *tmp_n = p; + zebra_neigh_t *n; + + n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + *n = *tmp_n; + + return ((void *)n); +} + +/* + * Add neighbor entry. + */ +static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *ip, + struct ethaddr *mac) +{ + zebra_neigh_t tmp_n; + zebra_neigh_t *n = NULL; + + memset(&tmp_n, 0, sizeof(zebra_neigh_t)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc); + assert(n); + + n->host_list = list_new(); + n->host_list->cmp = (int (*)(void *, void *))prefix_cmp; + + memcpy(&n->emac, mac, ETH_ALEN); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE_NH); + + return n; +} + +/* + * Delete neighbor entry. + */ +static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + zebra_neigh_t *tmp_n; + + if (n->host_list) + list_delete_and_null(&n->host_list); + n->host_list = NULL; + + tmp_n = hash_release(zl3vni->nh_table, n); + if (tmp_n) + XFREE(MTYPE_NEIGH, tmp_n); + + return 0; +} + +/* + * Install remote nh as neigh into the kernel. + */ +static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + if (!is_l3vni_oper_up(zl3vni)) + return -1; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE) || + !(n->flags & ZEBRA_NEIGH_REMOTE_NH)) + return 0; + + return kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac); +} + +/* + * Uninstall remote nh from the kernel. + */ +static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + if (!is_l3vni_oper_up(zl3vni)) + return -1; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE) || + !(n->flags & ZEBRA_NEIGH_REMOTE_NH)) + return 0; + + return kernel_del_neigh(zl3vni->svi_if, &n->ip); +} + +/* add remote vtep as a neigh entry */ +static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct ethaddr *rmac, + struct prefix *host_prefix) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_neigh_t *nh = NULL; + + nh = zl3vni_nh_lookup(zl3vni, vtep_ip); + if (!nh) { + nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac); + if (!nh) { + + zlog_warn( + "Failed to add NH as Neigh (IP %s MAC %s L3-VNI %u)", + ipaddr2str(vtep_ip, buf1, + sizeof(buf1)), + prefix_mac2str(rmac, buf, + sizeof(buf)), + zl3vni->vni); + return -1; + } + + /* install the nh neigh in kernel */ + zl3vni_nh_install(zl3vni, nh); + } + + if (!is_host_present_in_host_list(nh->host_list, host_prefix)) + host_list_add_host(nh->host_list, host_prefix); + + return 0; +} + +/* handle nh neigh delete */ +static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_neigh_t *nh = NULL; + + nh = zl3vni_nh_lookup(zl3vni, vtep_ip); + if (!nh) + return -1; + + host_list_delete_host(nh->host_list, host_prefix); + if (list_isempty(nh->host_list)) { + + /* uninstall from kernel */ + zl3vni_nh_uninstall(zl3vni, nh); + + /* delete the nh entry */ + zl3vni_nh_del(zl3vni, nh); + } + + return 0; +} + +/* handle neigh update from kernel - the only thing of interest is to + * readd stale entries. + */ +static int zl3vni_local_nh_add_update(zebra_l3vni_t *zl3vni, + struct ipaddr *ip, u_int16_t state) +{ +#ifdef GNU_LINUX + zebra_neigh_t *n = NULL; + + n = zl3vni_nh_lookup(zl3vni, ip); + if (!n) + return 0; + + /* all next hop neigh are remote and installed by frr. + * If the kernel has aged this entry, re-install. + */ + if (state & NUD_STALE) + zl3vni_nh_install(zl3vni, n); +#endif + return 0; +} + +/* handle neigh delete from kernel */ +static int zl3vni_local_nh_del(zebra_l3vni_t *zl3vni, + struct ipaddr *ip) +{ + zebra_neigh_t *n = NULL; + + n = zl3vni_nh_lookup(zl3vni, ip); + if (!n) + return 0; + + /* all next hop neigh are remote and installed by frr. + * If we get an age out notification for these neigh entries, we have to + * install it back + */ + zl3vni_nh_install(zl3vni, n); + + return 0; +} + +/* + * Hash function for L3 VNI. + */ +static unsigned int l3vni_hash_keymake(void *p) +{ + const zebra_l3vni_t *zl3vni = p; + + return jhash_1word(zl3vni->vni, 0); +} + +/* + * Compare 2 L3 VNI hash entries. + */ +static int l3vni_hash_cmp(const void *p1, const void *p2) +{ + const zebra_l3vni_t *zl3vni1 = p1; + const zebra_l3vni_t *zl3vni2 = p2; + + return (zl3vni1->vni == zl3vni2->vni); +} + +/* + * Callback to allocate L3 VNI hash entry. + */ +static void *zl3vni_alloc(void *p) +{ + zebra_l3vni_t *zl3vni = NULL; + const zebra_l3vni_t *tmp_l3vni = p; + + zl3vni = XCALLOC(MTYPE_ZL3VNI, sizeof(zebra_l3vni_t)); + zl3vni->vni = tmp_l3vni->vni; + return ((void *)zl3vni); +} + +/* + * Look up L3 VNI hash entry. + */ +static zebra_l3vni_t *zl3vni_lookup(vni_t vni) +{ + struct zebra_ns *zns; + zebra_l3vni_t tmp_l3vni; + zebra_l3vni_t *zl3vni = NULL; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + memset(&tmp_l3vni, 0, sizeof(zebra_l3vni_t)); + tmp_l3vni.vni = vni; + zl3vni = hash_lookup(zns->l3vni_table, &tmp_l3vni); + + return zl3vni; +} + +/* + * Add L3 VNI hash entry. + */ +static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id) +{ + zebra_l3vni_t tmp_zl3vni; + struct zebra_ns *zns = NULL; + zebra_l3vni_t *zl3vni = NULL; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + memset(&tmp_zl3vni, 0, sizeof(zebra_l3vni_t)); + tmp_zl3vni.vni = vni; + + zl3vni = hash_get(zns->l3vni_table, &tmp_zl3vni, zl3vni_alloc); + assert(zl3vni); + + zl3vni->vrf_id = vrf_id; + zl3vni->svi_if = NULL; + zl3vni->vxlan_if = NULL; + zl3vni->l2vnis = list_new(); + zl3vni->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp; + + /* Create hash table for remote RMAC */ + zl3vni->rmac_table = + hash_create(mac_hash_keymake, mac_cmp, + "Zebra L3-VNI RMAC-Table"); + + /* Create hash table for neighbors */ + zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp, + "Zebra L3-VNI next-hop table"); + + return zl3vni; +} + +/* + * Delete L3 VNI hash entry. + */ +static int zl3vni_del(zebra_l3vni_t *zl3vni) +{ + struct zebra_ns *zns; + zebra_l3vni_t *tmp_zl3vni; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + /* free the list of l2vnis */ + list_delete_and_null(&zl3vni->l2vnis); + zl3vni->l2vnis = NULL; + + /* Free the rmac table */ + hash_free(zl3vni->rmac_table); + zl3vni->rmac_table = NULL; + + /* Free the nh table */ + hash_free(zl3vni->nh_table); + zl3vni->nh_table = NULL; + + /* Free the VNI hash entry and allocated memory. */ + tmp_zl3vni = hash_release(zns->l3vni_table, zl3vni); + if (tmp_zl3vni) + XFREE(MTYPE_ZL3VNI, tmp_zl3vni); + + return 0; +} + +static int is_vni_l3(vni_t vni) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) + return 1; + return 0; +} + +static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +{ + struct zebra_ns *zns = NULL; + struct route_node *rn = NULL; + struct interface *ifp = NULL; + + /* loop through all vxlan-interface */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + ifp = (struct interface *)rn->info; + if (!ifp) + continue; + + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + + vxl = &zif->l2info.vxl; + if (vxl->vni == zl3vni->vni) { + zl3vni->local_vtep_ip = vxl->vtep_ip; + return ifp; + } + } + + return NULL; +} + +static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) +{ + struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */ + struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */ + + if (!zl3vni->vxlan_if) + return NULL; + + zif = zl3vni->vxlan_if->info; + if (!zif) + return NULL; + + vxl = &zif->l2info.vxl; + + return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); +} + +static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id) +{ + struct zebra_vrf *zvrf = NULL; + + zvrf = zebra_vrf_lookup_by_id(vrf_id); + if (!zvrf) + return NULL; + + return zl3vni_lookup(zvrf->l3vni); +} + +/* + * Map SVI and associated bridge to a VNI. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + */ +static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp, + struct interface *br_if) +{ + int found = 0; + vlanid_t vid = 0; + u_char bridge_vlan_aware = 0; + zebra_l3vni_t *zl3vni = NULL; + struct zebra_ns *zns = NULL; + struct route_node *rn = NULL; + struct zebra_if *zif = NULL; + struct interface *tmp_if = NULL; + struct zebra_l2info_bridge *br = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + if (!br_if) + return NULL; + + /* Make sure the linked interface is a bridge. */ + if (!IS_ZEBRA_IF_BRIDGE(br_if)) + return NULL; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + bridge_vlan_aware = br->vlan_aware; + if (bridge_vlan_aware) { + struct zebra_l2info_vlan *vl; + + if (!IS_ZEBRA_IF_VLAN(ifp)) + return NULL; + + zif = ifp->info; + assert(zif); + vl = &zif->l2info.vl; + vid = vl->vid; + } + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!bridge_vlan_aware || vxl->access_vlan == vid) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + zl3vni = zl3vni_lookup(vxl->vni); + return zl3vni; +} + +/* + * Inform BGP about l3-vni. + */ +static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni) +{ + struct stream *s = NULL; + struct zserv *client = NULL; + struct ethaddr rmac; + char buf[ETHER_ADDR_STRLEN]; + + client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + /* get the rmac */ + memset(&rmac, 0, sizeof(struct ethaddr)); + zl3vni_get_rmac(zl3vni, &rmac); + + s = client->obuf; + stream_reset(s); + + zserv_create_header(s, ZEBRA_L3VNI_ADD, + zl3vni_vrf_id(zl3vni)); + stream_putl(s, zl3vni->vni); + stream_put(s, &rmac, sizeof(struct ethaddr)); + stream_put_in_addr(s, &zl3vni->local_vtep_ip); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s", + zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)), + prefix_mac2str(&rmac, buf, sizeof(buf)), + inet_ntoa(zl3vni->local_vtep_ip), + zebra_route_string(client->proto)); + + client->l3vniadd_cnt++; + return zebra_server_send_message(client); +} + +/* + * Inform BGP about local l3-VNI deletion. + */ +static int zl3vni_send_del_to_client(zebra_l3vni_t *zl3vni) +{ + struct stream *s = NULL; + struct zserv *client = NULL; + + client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = client->obuf; + stream_reset(s); + + zserv_create_header(s, ZEBRA_L3VNI_DEL, + zl3vni_vrf_id(zl3vni)); + stream_putl(s, zl3vni->vni); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send L3_VNI_DEL %u VRF %s to %s", + zl3vni->vni, + vrf_id_to_name(zl3vni_vrf_id(zl3vni)), + zebra_route_string(client->proto)); + + client->l3vnidel_cnt++; + return zebra_server_send_message(client); +} + +static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("L3-VNI %u is UP - send add to BGP", + zl3vni->vni); + + /* send l3vni add to BGP */ + zl3vni_send_add_to_client(zl3vni); +} + +static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("L3-VNI %u is Down - Send del to BGP", + zl3vni->vni); + + /* send l3-vni del to BGP*/ + zl3vni_send_del_to_client(zl3vni); +} + +static void zvni_add_to_l3vni_list(struct hash_backet *backet, + void *ctxt) +{ + zebra_vni_t *zvni = (zebra_vni_t *) backet->data; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *) ctxt; + + if (zvni->vrf_id == zl3vni_vrf_id(zl3vni)) + listnode_add_sort(zl3vni->l2vnis, zvni); +} + +/* + * handle transition of vni from l2 to l3 and vice versa + */ +static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, + vni_t vni, int add) +{ + zebra_vni_t *zvni = NULL; + + /* There is a possibility that VNI notification was already received + * from kernel and we programmed it as L2-VNI + * In such a case we need to delete this L2-VNI first, so + * that it can be reprogrammed as L3-VNI in the system. It is also + * possible that the vrf-vni mapping is removed from FRR while the vxlan + * interface is still present in kernel. In this case to keep it + * symmetric, we will delete the l3-vni and reprogram it as l2-vni + */ + if (add) { + /* Locate hash entry */ + zvni = zvni_lookup(vni); + if (!zvni) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L2-VNI %u - transition to L3-VNI", + vni); + + /* Delete VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MAC, if any. */ + zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 0); + + /* Delete the hash entry. */ + if (zvni_del(zvni)) { + zlog_err("Failed to del VNI hash %p, VNI %u", + zvni, zvni->vni); + return -1; + } + } else { + /* TODO_MITESH: This needs to be thought through. We don't have + * enough information at this point to reprogram the vni as + * l2-vni. One way is to store the required info in l3-vni and + * used it solely for this purpose + */ + } + + return 0; +} + +/* delete and uninstall rmac hash entry */ +static void zl3vni_del_rmac_hash_entry(struct hash_backet *backet, + void *ctx) +{ + zebra_mac_t *zrmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + zrmac = (zebra_mac_t *)backet->data; + zl3vni = (zebra_l3vni_t *)ctx; + zl3vni_rmac_uninstall(zl3vni, zrmac); + zl3vni_rmac_del(zl3vni, zrmac); +} + +/* delete and uninstall nh hash entry */ +static void zl3vni_del_nh_hash_entry(struct hash_backet *backet, + void *ctx) +{ + zebra_neigh_t *n = NULL; + zebra_l3vni_t *zl3vni = NULL; + + n = (zebra_neigh_t *)backet->data; + zl3vni = (zebra_l3vni_t *)ctx; + zl3vni_nh_uninstall(zl3vni, n); + zl3vni_nh_del(zl3vni, n); +} /* Public functions */ +/* handle evpn route in vrf table */ +void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return; + + /* add the next hop neighbor */ + zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix); + + /* add the rmac */ + zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip, host_prefix); +} + +/* handle evpn vrf route delete */ +void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni) + return; + + /* delete the next hop entry */ + zl3vni_remote_nh_del(zl3vni, vtep_ip, host_prefix); + + /* delete the rmac entry */ + zl3vni_remote_rmac_del(zl3vni, rmac, host_prefix); +} + +void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, + vni_t l3vni, + struct ethaddr *rmac, + u_char use_json) +{ + zebra_l3vni_t *zl3vni = NULL; + zebra_mac_t *zrmac = NULL; + json_object *json = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) + json = json_object_new_object(); + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u doesnt exist\n", + l3vni); + return; + } + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, + "%% Requested RMAC doesnt exist in L3-VNI %u", + l3vni); + return; + } + + zl3vni_print_rmac(zrmac, vty, json); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, + vni_t l3vni, + u_char use_json) +{ + zebra_l3vni_t *zl3vni; + u_int32_t num_rmacs; + struct rmac_walk_ctx wctx; + json_object *json = NULL; + + if (!is_evpn_enabled()) + return; + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); + return; + } + num_rmacs = hashcount(zl3vni->rmac_table); + if (!num_rmacs) + return; + + if (use_json) + json = json_object_new_object(); + + memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); + wctx.vty = vty; + wctx.json = json; + if (!use_json) { + vty_out(vty, + "Number of Remote RMACs known for this VNI: %u\n", + num_rmacs); + vty_out(vty, "%-17s %-21s %-6s\n", "MAC", + "Remote VTEP", "Refcnt"); + } else + json_object_int_add(json, "numRmacs", num_rmacs); + + hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, + u_char use_json) +{ + struct zebra_ns *zns = NULL; + json_object *json = NULL; + void *args[2]; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, + void *))zl3vni_print_rmac_hash_all_vni, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, + vni_t l3vni, + struct ipaddr *ip, + u_char use_json) +{ + zebra_l3vni_t *zl3vni = NULL; + zebra_neigh_t *n = NULL; + json_object *json = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) + json = json_object_new_object(); + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); + return; + } + + n = zl3vni_nh_lookup(zl3vni, ip); + if (!n) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, + "%% Requested next-hop not present for L3-VNI %u", + l3vni); + return; + } + + zl3vni_print_nh(n, vty, json); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_nh_l3vni(struct vty *vty, + vni_t l3vni, + u_char use_json) +{ + u_int32_t num_nh; + struct nh_walk_ctx wctx; + json_object *json = NULL; + zebra_l3vni_t *zl3vni = NULL; + + if (!is_evpn_enabled()) + return; + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); + return; + } + + num_nh = hashcount(zl3vni->nh_table); + if (!num_nh) + return; + + if (use_json) + json = json_object_new_object(); + + wctx.vty = vty; + wctx.json = json; + if (!use_json) { + vty_out(vty, + "Number of NH Neighbors known for this VNI: %u\n", + num_nh); + vty_out(vty, "%-15s %-17s %6s\n", "IP", + "RMAC", "Refcnt"); + } else + json_object_int_add(json, "numNh", num_nh); + + hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, + u_char use_json) +{ + struct zebra_ns *zns = NULL; + json_object *json = NULL; + void *args[2]; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) + return; + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, + void *))zl3vni_print_nh_hash_all_vni, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return; +} + +/* + * Display L3 VNI information (VTY command handler). + */ +void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, u_char use_json) +{ + void *args[2]; + json_object *json = NULL; + zebra_l3vni_t *zl3vni = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + zl3vni_print(zl3vni, (void *)args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* + * Display L3 VNI hash table (VTY command handler). + */ +void zebra_vxlan_print_l3vnis(struct vty *vty, u_char use_json) +{ + u_int32_t num_vnis; + void *args[2]; + json_object *json = NULL; + struct zebra_ns *zns = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + num_vnis = hashcount(zns->l3vni_table); + if (!num_vnis) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) { + json = json_object_new_object(); + json_object_int_add(json, "numVnis", num_vnis); + } else { + vty_out(vty, "Number of L3 VNIs: %u\n", num_vnis); + vty_out(vty, "%-10s %-15s %-20s %-20s %-5s %-37s %-18s\n", + "VNI", "Local-ip", "Vx-intf", "L3-SVI", "State", + "VRF", "Rmac"); + } + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, void *))zl3vni_print_hash, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + /* * Display Neighbors for a VNI (VTY command handler). */ @@ -2789,9 +4520,9 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, vty_out(vty, "Advertise gateway mac-ip: %s\n", zvrf->advertise_gw_macip ? "Yes" : "No"); vty_out(vty, "Number of VNIs: %u\n", num_vnis); - vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", + vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s %-37s\n", "VNI", "VxLAN IF", "VTEP IP", "# MACs", "# ARPs", - "# Remote VTEPs"); + "# Remote VTEPs", "VRF"); } args[0] = vty; args[1] = json; @@ -2816,18 +4547,27 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, int zebra_vxlan_local_neigh_del(struct interface *ifp, struct interface *link_if, struct ipaddr *ip) { - zebra_vni_t *zvni; - zebra_neigh_t *n; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; - zebra_mac_t *zmac; + zebra_neigh_t *n = NULL; + zebra_vni_t *zvni = NULL; + zebra_mac_t *zmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* check if this is a remote neigh entry corresponding to remote + * next-hop + */ + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) + return zl3vni_local_nh_del(zl3vni, ip); /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_map_svi(ifp, link_if); + zvni = zvni_from_svi(ifp, link_if); if (!zvni) return 0; + if (!zvni->vxlan_if) { zlog_err( "VNI %u hash %p doesn't have intf upon local neighbor DEL", @@ -2836,7 +4576,7 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp, } if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del neighbor %s intf %s(%u) -> VNI %u", + zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u", ipaddr2str(ip, buf, sizeof(buf)), ifp->name, ifp->ifindex, zvni->vni); @@ -2891,23 +4631,30 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, struct ethaddr *macaddr, u_int16_t state, u_char ext_learned) { - zebra_vni_t *zvni; - zebra_neigh_t *n; - zebra_mac_t *zmac, *old_zmac; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_vni_t *zvni = NULL; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL, *old_zmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* check if this is a remote neigh entry corresponding to remote + * next-hop + */ + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) + return zl3vni_local_nh_add_update(zl3vni, ip, state); /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_map_svi(ifp, link_if); + zvni = zvni_from_svi(ifp, link_if); if (!zvni) return 0; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x " - "%s-> VNI %u", + "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s-> L2-VNI %u", ipaddr2str(ip, buf2, sizeof(buf2)), prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, state, ext_learned ? "ext-learned " : "", @@ -3015,12 +4762,12 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, /* Inform BGP. */ if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("neigh %s (MAC %s) is now ACTIVE on VNI %u", + zlog_debug("neigh %s (MAC %s) is now ACTIVE on L2-VNI %u", 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(zvni->vni, ip, macaddr, 0); } @@ -3045,6 +4792,10 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, u_short length, struct interface *ifp = NULL; struct zebra_if *zif = NULL; + memset(&macaddr, 0, sizeof(struct ethaddr)); + memset(&ip, 0, sizeof(struct ipaddr)); + memset(&vtep_ip, 0, sizeof(struct in_addr)); + s = client->ibuf; while (l < length) { @@ -3189,6 +4940,10 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, u_short length, struct interface *ifp = NULL; struct zebra_if *zif = NULL; + memset(&macaddr, 0, sizeof(struct ethaddr)); + memset(&ip, 0, sizeof(struct ipaddr)); + memset(&vtep_ip, 0, sizeof(struct in_addr)); + if (!EVPN_ENABLED(zvrf)) { zlog_warn("%s: EVPN Not turned on yet we have received a remote_macip add zapi callback", __PRETTY_FUNCTION__); @@ -3898,14 +5653,14 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, 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); + zvni = zvni_from_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); + zvni = zvni_from_svi(svi_if, svi_if); } } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_if *svi_if_zif = @@ -3917,9 +5672,9 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, 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); + zvni = zvni_from_svi(ifp, svi_if_link); } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { - zvni = zvni_map_svi(ifp, ifp); + zvni = zvni_from_svi(ifp, ifp); } if (!zvni) @@ -3958,56 +5713,108 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, } /* - * Handle SVI interface going down. At this point, this is a NOP since - * the kernel deletes the neighbor entries on this SVI (if any). + * Handle SVI interface going down. + * SVI can be associated to either L3-VNI or L2-VNI. + * For L2-VNI: At this point, this is a NOP since + * the kernel deletes the neighbor entries on this SVI (if any). + * We only need to update the vrf corresponding to zvni. + * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete + * from bgp */ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) { + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) { + + /* process l3-vni down */ + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* remove association with svi-if */ + zl3vni->svi_if = NULL; + } else { + zebra_vni_t *zvni = NULL; + + /* since we dont have svi corresponding to zvni, we associate it + * to default vrf. Note: the corresponding neigh entries on the + * SVI would have already been deleted */ + zvni = zvni_from_svi(ifp, link_if); + if (zvni) { + zvni->vrf_id = VRF_DEFAULT; + + /* update the tenant vrf in BGP */ + zvni_send_add_to_client(zvni); + } + } return 0; } /* - * Handle SVI interface coming up. This may or may not be of interest, - * but if this is a SVI on a VxLAN bridge, we need to install any remote - * neighbor entries (which will be used for EVPN ARP suppression). + * Handle SVI interface coming up. + * SVI can be associated to L3-VNI (l3vni vxlan interface) or L2-VNI (l2-vni + * vxlan intf). + * For L2-VNI: we need to install any remote neighbors entried (used for + * apr-suppression) + * For L3-VNI: SVI will be used to get the rmac to be used with L3-VNI */ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { - zebra_vni_t *zvni; - struct neigh_walk_ctx n_wctx; + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; - zvni = zvni_map_svi(ifp, link_if); - if (!zvni) - return 0; + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) { - if (!zvni->vxlan_if) { - zlog_err("VNI %u hash %p doesn't have intf upon SVI up", - zvni->vni, zvni); - return -1; - } + /* associate with svi */ + zl3vni->svi_if = ifp; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("SVI %s(%u) VNI %u is UP, installing neighbors", - ifp->name, ifp->ifindex, zvni->vni); + /* process oper-up */ + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + + /* process SVI up for l2-vni */ + struct neigh_walk_ctx n_wctx; - /* Install any remote neighbors for this VNI. */ - memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx); + zvni = zvni_from_svi(ifp, link_if); + if (!zvni) + return 0; + + if (!zvni->vxlan_if) { + zlog_err("VNI %u hash %p doesn't have intf upon SVI up", + zvni->vni, zvni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("SVI %s(%u) VNI %u VRF %s is UP, installing neighbors", + ifp->name, ifp->ifindex, zvni->vni, + vrf_id_to_name(ifp->vrf_id)); + + /* update the vrf information for l2-vni and inform bgp */ + zvni->vrf_id = ifp->vrf_id; + zvni_send_add_to_client(zvni); + + /* Install any remote neighbors for this VNI. */ + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.zvni = zvni; + hash_iterate(zvni->neigh_table, + zvni_install_neigh_hash, + &n_wctx); + } return 0; } /* - * Handle VxLAN interface down - update BGP if required, and do - * internal cleanup. + * Handle VxLAN interface down */ int zebra_vxlan_if_down(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4018,31 +5825,55 @@ int zebra_vxlan_if_down(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) VNI %u is DOWN", - ifp->name, ifp->ifindex, vni); - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + if (is_vni_l3(vni)) { - assert(zvni->vxlan_if == ifp); + /* process-if-down for l3-vni */ + zebra_l3vni_t *zl3vni = NULL; - /* Delete this VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is DOWN", + ifp->name, ifp->ifindex, vni); - /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at DOWN, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + } else { + /* process if-down for l2-vni */ + zebra_vni_t *zvni; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L2-VNI %u is DOWN", + ifp->name, ifp->ifindex, vni); + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zvni->vxlan_if == ifp); + + /* Delete this VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MACs, if any. */ + zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 1); + } return 0; } @@ -4051,10 +5882,9 @@ int zebra_vxlan_if_down(struct interface *ifp) */ int zebra_vxlan_if_up(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4065,26 +5895,66 @@ int zebra_vxlan_if_up(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) VNI %u is UP", - ifp->name, ifp->ifindex, vni); + if (is_vni_l3(vni)) { - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + /* Handle L3-VNI add */ + zebra_l3vni_t *zl3vni = NULL; - assert(zvni->vxlan_if == ifp); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is UP", + ifp->name, ifp->ifindex, vni); - /* If part of a bridge, inform BGP about this VNI. */ - /* Also, read and populate local MACs and neighbors. */ - if (zif->brslave_info.br_if) { - zvni_send_add_to_client(zvni); - zvni_read_mac_neigh(zvni, ifp); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + /* we need to associate with SVI, if any, we can associate with + * svi-if only after association with vxlan-intf is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + /* Handle L2-VNI add */ + + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L2-VNI %u is UP", + ifp->name, ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zvni->vxlan_if == ifp); + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + /* If part of a bridge, inform BGP about this VNI. */ + /* Also, read and populate local MACs and neighbors. */ + if (zif->brslave_info.br_if) { + zvni_send_add_to_client(zvni); + zvni_read_mac_neigh(zvni, ifp); + } } return 0; @@ -4096,10 +5966,9 @@ int zebra_vxlan_if_up(struct interface *ifp) */ int zebra_vxlan_if_del(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4110,34 +5979,69 @@ int zebra_vxlan_if_del(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del VNI %u intf %s(%u)", - vni, ifp->name, ifp->ifindex); + if (is_vni_l3(vni)) { - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at del, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return 0; - } + /* process if-del for l3-vni */ + zebra_l3vni_t *zl3vni = NULL; - /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L3-VNI %u intf %s(%u)", + vni, ifp->name, ifp->ifindex); - /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return 0; + } - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + /* process oper-down for l3-vni */ + zebra_vxlan_process_l3vni_oper_down(zl3vni); - /* Delete the hash entry. */ - if (zvni_del(zvni)) { - zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u", - zvni, ifp->name, ifp->ifindex, zvni->vni); - return -1; + /* remove the association with vxlan_if */ + memset(&zl3vni->local_vtep_ip, 0, sizeof(struct in_addr)); + zl3vni->vxlan_if = NULL; + } else { + + /* process if-del for l2-vni*/ + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L2-VNI %u intf %s(%u)", + vni, ifp->name, ifp->ifindex); + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to locate VNI hash at del, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return 0; + } + + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zvni->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zvni); + + /* Delete VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MAC, if any. */ + zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 0); + + /* Delete the hash entry. */ + if (zvni_del(zvni)) { + zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u", + zvni, ifp->name, ifp->ifindex, zvni->vni); + return -1; + } } return 0; @@ -4148,10 +6052,9 @@ int zebra_vxlan_if_del(struct interface *ifp) */ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4162,79 +6065,129 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) vxl = &zif->l2info.vxl; vni = vxl->vni; - /* Update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to find VNI hash on update, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + if (is_vni_l3(vni)) { + zebra_l3vni_t *zl3vni = NULL; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Update VNI %u intf %s(%u) VLAN %u local IP %s " - "master %u chg 0x%x", - vni, ifp->name, ifp->ifindex, - vxl->access_vlan, inet_ntoa(vxl->vtep_ip), - zif->brslave_info.bridge_ifindex, chgflags); - - /* 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 */ - /* Also, free up all MACs and neighbors. */ - zvni_send_del_to_client(zvni->vni); - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); - zvni_vtep_del_all(zvni, 1); - return 0; - } + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to find L3-VNI hash on update, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex, chgflags); + + /* Removed from bridge? Cleanup and return */ + if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) + && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + return 0; + } - /* Handle other changes. */ - if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - /* Remove all existing local neighbors and MACs for this VNI - * (including from BGP) + /* access-vlan change - process oper down, associate with new + * svi_if and then process oper up again */ - zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - } + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + if (if_is_operative(ifp)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni->svi_if = NULL; + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up( + zl3vni); + } + } - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; + /* if we have a valid new master, process l3-vni oper up */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) { + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } + } else { + zebra_vni_t *zvni = NULL; - /* Take further actions needed. Note that if we are here, there is a - * change of interest. - */ - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return 0; + /* Update VNI hash. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } - /* Inform BGP, if there is a change of interest. */ - if (chgflags - & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE)) - zvni_send_add_to_client(zvni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L2-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex, chgflags); + + /* 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 */ + /* Also, free up all MACs and neighbors. */ + zvni_send_del_to_client(zvni->vni); + zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zvni_vtep_del_all(zvni, 1); + return 0; + } - /* If there is a valid new master or a VLAN mapping change, read and - * populate local MACs and neighbors. Also, reinstall any remote MACs - * and neighbors for this VNI (based on new VLAN). - */ - if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - zvni_read_mac_neigh(zvni, ifp); - else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - struct mac_walk_ctx m_wctx; - struct neigh_walk_ctx n_wctx; + /* Handle other changes. */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + /* Remove all existing local neigh and MACs for this VNI + * (including from BGP) + */ + zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + } - zvni_read_mac_neigh(zvni, ifp); + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; - memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; - hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx); + /* Take further actions needed. + * Note that if we are here, there is a change of interest. + */ + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; - memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, - &n_wctx); + /* Inform BGP, if there is a change of interest. */ + if (chgflags + & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE)) + zvni_send_add_to_client(zvni); + + /* If there is a valid new master or a VLAN mapping change, + * read and populate local MACs and neighbors. + * Also, reinstall any remote MACs and neighbors + * for this VNI (based on new VLAN). + */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) + zvni_read_mac_neigh(zvni, ifp); + else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + struct mac_walk_ctx m_wctx; + struct neigh_walk_ctx n_wctx; + + zvni_read_mac_neigh(zvni, ifp); + + memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); + m_wctx.zvni = zvni; + hash_iterate(zvni->mac_table, + zvni_install_mac_hash, + &m_wctx); + + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.zvni = zvni; + hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, + &n_wctx); + } } return 0; @@ -4245,10 +6198,9 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) */ int zebra_vxlan_if_add(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4259,37 +6211,200 @@ int zebra_vxlan_if_add(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Add VNI %u intf %s(%u) VLAN %u local IP %s master %u", - vni, ifp->name, ifp->ifindex, - vxl->access_vlan, inet_ntoa(vxl->vtep_ip), - zif->brslave_info.bridge_ifindex); + if (is_vni_l3(vni)) { - /* Create or update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zvni = zvni_add(vni); - if (!zvni) { + /* process if-add for l3-vni*/ + zebra_l3vni_t *zl3vni = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex); + + /* + * we expect the l3-vni has entry to be present here. + * The only place l3-vni is created in zebra is vrf-vni mapping + * command. This might change when we have the switchd support + * for l3-vxlan interface. + */ + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { zlog_err( - "Failed to add VNI hash, IF %s(%u) VNI %u", + "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); + return 0; + } + + /* associate with vxlan_if */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->vxlan_if = ifp; + + /* Associate with SVI, if any. We can associate with svi-if only + * after association with vxlan_if is complete */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + + /* process if-add for l2-vni */ + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; + + /* Create or update VNI hash. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zvni = zvni_add(vni); + if (!zvni) { + zlog_err( + "Failed to add VNI hash, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s master %u", + vni, + vlan_if ? vrf_id_to_name(vlan_if->vrf_id) : + "Default", + ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex); + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; + + /* Inform BGP */ + zvni_send_add_to_client(zvni); + + /* Read and populate local MACs and neighbors */ + zvni_read_mac_neigh(zvni, ifp); + } + + return 0; +} + +int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, + vni_t vni, + char *err, int err_str_sz, + int add) +{ + zebra_l3vni_t *zl3vni = NULL; + struct zebra_vrf *zvrf_default = NULL; + + zvrf_default = zebra_vrf_lookup_by_id(VRF_DEFAULT); + if (!zvrf_default) + return -1; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("vrf %s vni %u %s", + zvrf_name(zvrf), + vni, + add ? "ADD" : "DEL"); + + if (add) { + + zebra_vxlan_handle_vni_transition(zvrf, vni, add); + + /* check if the vni is already present under zvrf */ + if (zvrf->l3vni) { + snprintf(err, err_str_sz, + "VNI is already configured under the vrf"); return -1; } + + /* check if this VNI is already present in the system */ + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + snprintf(err, err_str_sz, + "VNI is already configured as L3-VNI"); + return -1; + } + + /* add the L3-VNI to the global table */ + zl3vni = zl3vni_add(vni, zvrf_id(zvrf)); + if (!zl3vni) { + snprintf(err, err_str_sz, + "Could not add L3-VNI"); + return -1; + } + + /* associate the vrf with vni */ + zvrf->l3vni = vni; + + /* associate with vxlan-intf; + * we need to associate with the vxlan-intf first + */ + zl3vni->vxlan_if = zl3vni_map_to_vxlan_if(zl3vni); + + /* associate with corresponding SVI interface, we can associate + * with svi-if only after vxlan interface association is + * complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + /* formulate l2vni list */ + hash_iterate(zvrf_default->vni_table, + zvni_add_to_l3vni_list, zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + + } else { + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + snprintf(err, err_str_sz, "VNI doesn't exist"); + return -1; + } + + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* delete and uninstall all rmacs */ + hash_iterate(zl3vni->rmac_table, + zl3vni_del_rmac_hash_entry, + zl3vni); + + /* delete and uninstall all next-hops */ + hash_iterate(zl3vni->nh_table, + zl3vni_del_nh_hash_entry, + zl3vni); + + zvrf->l3vni = 0; + zl3vni_del(zl3vni); + + zebra_vxlan_handle_vni_transition(zvrf, vni, add); } + return 0; +} - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; +int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf) +{ + zebra_l3vni_t *zl3vni = NULL; - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + zl3vni = zl3vni_from_vrf(zvrf_id(zvrf)); + if (!zl3vni) return 0; - /* Inform BGP */ - zvni_send_add_to_client(zvni); - - /* Read and populate local MACs and neighbors */ - zvni_read_mac_neigh(zvni, ifp); + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni_del(zl3vni); + zebra_vxlan_handle_vni_transition(zvrf, zl3vni->vni, 0); return 0; } @@ -4410,8 +6525,9 @@ stream_failure: int zebra_vxlan_advertise_all_vni(struct zserv *client, u_short length, struct zebra_vrf *zvrf) { - struct stream *s; - int advertise; + struct stream *s = NULL; + int advertise = 0; + struct zebra_ns *zns = NULL; if (zvrf_id(zvrf) != VRF_DEFAULT) { zlog_err("EVPN VNI Adv for non-default VRF %u", @@ -4449,6 +6565,13 @@ int zebra_vxlan_advertise_all_vni(struct zserv *client, * kernel and free entries. */ hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + + /* cleanup all l3vnis */ + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) + return -1; + + hash_iterate(zns->l3vni_table, zl3vni_cleanup_all, NULL); } stream_failure: @@ -4475,3 +6598,28 @@ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); hash_free(zvrf->vni_table); } + +/* init the l3vni table */ +void zebra_vxlan_ns_init(struct zebra_ns *zns) +{ + zns->l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp, + "Zebra VRF L3 VNI table"); +} + +/* free l3vni table */ +void zebra_vxlan_ns_disable(struct zebra_ns *zns) +{ + hash_free(zns->l3vni_table); +} + +/* get the l3vni svi ifindex */ +ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return 0; + + return zl3vni->svi_if->ifindex; +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 290d19bcf3..b7def6acf8 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -51,6 +51,13 @@ is_evpn_enabled() #define VNI_STR_LEN 32 +extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id); +extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf); +extern void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, + struct ipaddr *ip, u_char uj); +extern void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, + struct ethaddr *rmac, + u_char use_json); extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, u_char use_json); extern void zebra_vxlan_print_macs_all_vni(struct vty *vty, @@ -84,6 +91,16 @@ extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, u_char use_json); extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, u_char use_json); +extern void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t vni, u_char + use_json); +extern void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, u_char use_json); +extern void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t vni, u_char + use_json); +extern void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, u_char use_json); +extern void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, + u_char use_json); +extern void zebra_vxlan_print_l3vnis(struct vty *vty, + u_char use_json); extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, int add); @@ -129,7 +146,20 @@ extern int zebra_vxlan_advertise_gw_macip(struct zserv *client, extern int zebra_vxlan_advertise_all_vni(struct zserv *client, u_short length, struct zebra_vrf *zvrf); +extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, + char *err, + int err_str_sz, int add); extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf); extern void zebra_vxlan_close_tables(struct zebra_vrf *); +extern void zebra_vxlan_ns_init(struct zebra_ns *zns); +extern void zebra_vxlan_ns_disable(struct zebra_ns *zns); +extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *ip, + struct prefix *host_prefix); +extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix); #endif /* _ZEBRA_VXLAN_H */ diff --git a/zebra/zebra_vxlan_null.c b/zebra/zebra_vxlan_null.c index bbed5ddb05..db828c337e 100644 --- a/zebra/zebra_vxlan_null.c +++ b/zebra/zebra_vxlan_null.c @@ -83,6 +83,30 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf) { } +void zebra_vxlan_print_rmacs_l3vni(struct vty*, vni_t, u_char) +{ +} + +void zebra_vxlan_print_rmacs_all_l3vni(struct vty*, u_char) +{ +} + +void zebra_vxlan_print_nh_l3vni(struct vty*, vni_t, u_char) +{ +} + +void zebra_vxlan_print_nh_all_l3vni(struct vty*, u_char) +{ +} + +void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni) +{ +} + +void zebra_vxlan_print_l3vnis(struct vty *vty) +{ +} + int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { return 0; diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index fa7d0e9457..ef6f9b99cb 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -30,12 +30,16 @@ #include "if.h" #include "linklist.h" +#include "zebra_vxlan.h" + +#define ERR_STR_SZ 256 /* definitions */ typedef struct zebra_vni_t_ zebra_vni_t; typedef struct zebra_vtep_t_ zebra_vtep_t; typedef struct zebra_mac_t_ zebra_mac_t; typedef struct zebra_neigh_t_ zebra_neigh_t; +typedef struct zebra_l3vni_t_ zebra_l3vni_t; /* * VTEP info @@ -75,6 +79,9 @@ struct zebra_vni_t_ { /* Local IP */ struct in_addr local_vtep_ip; + /* tenant VRF, if any */ + vrf_id_t vrf_id; + /* List of local or remote MAC */ struct hash *mac_table; @@ -82,6 +89,128 @@ struct zebra_vni_t_ { struct hash *neigh_table; }; +/* L3 VNI hash table */ +struct zebra_l3vni_t_ { + + /* VNI key */ + vni_t vni; + + /* vrf_id */ + vrf_id_t vrf_id; + + /* Local IP */ + struct in_addr local_vtep_ip; + + /* kernel interface for l3vni */ + struct interface *vxlan_if; + + /* SVI interface corresponding to the l3vni */ + struct interface *svi_if; + + /* list of L2 VNIs associated with the L3 VNI */ + struct list *l2vnis; + + /* list of remote router-macs */ + struct hash *rmac_table; + + /* list of remote vtep-ip neigh */ + struct hash *nh_table; +}; + +/* get the vx-intf name for l3vni */ +static inline const char *zl3vni_vxlan_if_name(zebra_l3vni_t *zl3vni) +{ + return zl3vni->vxlan_if ? zl3vni->vxlan_if->name : "None"; +} + +/* get the svi intf name for l3vni */ +static inline const char *zl3vni_svi_if_name(zebra_l3vni_t *zl3vni) +{ + return zl3vni->svi_if ? zl3vni->svi_if->name : "None"; +} + +/* get the vrf name for l3vni */ +static inline const char *zl3vni_vrf_name(zebra_l3vni_t *zl3vni) +{ + return vrf_id_to_name(zl3vni->vrf_id); +} + +/* get the rmac string */ +static inline const char *zl3vni_rmac2str(zebra_l3vni_t *zl3vni, char *buf, + int size) +{ + char *ptr; + + if (!buf) + ptr = (char *)XMALLOC(MTYPE_TMP, + ETHER_ADDR_STRLEN * sizeof(char)); + else { + assert(size >= ETHER_ADDR_STRLEN); + ptr = buf; + } + + if (zl3vni->svi_if) + snprintf(ptr, (ETHER_ADDR_STRLEN), + "%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)zl3vni->svi_if->hw_addr[0], + (uint8_t)zl3vni->svi_if->hw_addr[1], + (uint8_t)zl3vni->svi_if->hw_addr[2], + (uint8_t)zl3vni->svi_if->hw_addr[3], + (uint8_t)zl3vni->svi_if->hw_addr[4], + (uint8_t)zl3vni->svi_if->hw_addr[5]); + else + snprintf(ptr, ETHER_ADDR_STRLEN, "None"); + + return ptr; +} + +/* + * l3-vni is oper up when: + * 0. if EVPN is enabled (advertise-all-vni cfged) + * 1. it is associated to a vxlan-intf + * 2. Associated vxlan-intf is oper up + * 3. it is associated to an SVI + * 4. associated SVI is oper up + */ +static inline int is_l3vni_oper_up(zebra_l3vni_t *zl3vni) +{ + return (is_evpn_enabled() && zl3vni && + (zl3vni->vrf_id != VRF_UNKNOWN) && + zl3vni->vxlan_if && if_is_operative(zl3vni->vxlan_if) && + zl3vni->svi_if && if_is_operative(zl3vni->svi_if)); +} + +static inline const char *zl3vni_state2str(zebra_l3vni_t *zl3vni) +{ + if (!zl3vni) + return NULL; + + if (is_l3vni_oper_up(zl3vni)) + return "Up"; + else + return "Down"; + + return NULL; +} + +static inline vrf_id_t zl3vni_vrf_id(zebra_l3vni_t *zl3vni) +{ + return zl3vni->vrf_id; +} + +static inline void zl3vni_get_rmac(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + if (!zl3vni) + return; + + if (!is_l3vni_oper_up(zl3vni)) + return; + + if (zl3vni->svi_if && if_is_operative(zl3vni->svi_if)) + memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN); +} + /* * MAC hash table. * @@ -103,6 +232,7 @@ struct zebra_mac_t_ { #define ZEBRA_MAC_REMOTE 0x02 #define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ #define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ +#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ /* Local or remote info. */ union { @@ -116,6 +246,9 @@ struct zebra_mac_t_ { /* List of neigh associated with this mac */ struct list *neigh_list; + + /* list of hosts pointing to this remote RMAC */ + struct list *host_list; }; /* @@ -141,6 +274,11 @@ struct mac_walk_ctx { struct json_object *json; /* Used for JSON Output */ }; +struct rmac_walk_ctx { + struct vty *vty; + struct json_object *json; +}; + enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 }; #define IS_ZEBRA_NEIGH_ACTIVE(n) n->state == ZEBRA_NEIGH_ACTIVE @@ -175,11 +313,15 @@ struct zebra_neigh_t_ { u_int32_t flags; #define ZEBRA_NEIGH_LOCAL 0x01 #define ZEBRA_NEIGH_REMOTE 0x02 +#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ enum zebra_neigh_state state; /* Remote VTEP IP - applicable only for remote neighbors. */ struct in_addr r_vtep_ip; + + /* list of hosts pointing to this remote NH entry */ + struct list *host_list; }; /* @@ -206,4 +348,18 @@ struct neigh_walk_ctx { struct json_object *json; /* Used for JSON Output */ }; +/* context for neigh hash walk - update l3vni and rmac */ +struct neigh_l3info_walk_ctx { + + zebra_vni_t *zvni; + zebra_l3vni_t *zl3vni; + int add; +}; + +struct nh_walk_ctx { + + struct vty *vty; + struct json_object *json; +}; + #endif /* _ZEBRA_VXLAN_PRIVATE_H */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 684ba49e7d..7eded89f6d 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1132,23 +1132,26 @@ static int zread_route_add(struct zserv *client, u_short length, struct route_entry *re; struct nexthop *nexthop = NULL; int i, ret; + vrf_id_t vrf_id = 0; s = client->ibuf; if (zapi_route_decode(s, &api) < 0) return -1; /* Allocate new route. */ + vrf_id = zvrf_id(zvrf); re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); re->type = api.type; re->instance = api.instance; re->flags = api.flags; re->uptime = time(NULL); - re->vrf_id = zvrf_id(zvrf); + re->vrf_id = vrf_id; re->table = zvrf->table_id; if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; + ifindex_t ifindex = 0; switch (api_nh->type) { case NEXTHOP_TYPE_IFINDEX: @@ -1159,11 +1162,42 @@ static int zread_route_add(struct zserv *client, u_short length, nexthop = route_entry_nexthop_ipv4_add( re, &api_nh->gate.ipv4, NULL); break; - case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFINDEX: { + + struct ipaddr vtep_ip; + + memset(&vtep_ip, 0, sizeof(struct ipaddr)); + if (CHECK_FLAG(api.flags, + ZEBRA_FLAG_EVPN_ROUTE)) { + ifindex = + get_l3vni_svi_ifindex(vrf_id); + } else { + ifindex = api_nh->ifindex; + } + nexthop = route_entry_nexthop_ipv4_ifindex_add( re, &api_nh->gate.ipv4, NULL, - api_nh->ifindex); + ifindex); + + /* if this an EVPN route entry, + program the nh as neigh + */ + if (CHECK_FLAG(api.flags, + ZEBRA_FLAG_EVPN_ROUTE)) { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_EVPN_RVTEP); + vtep_ip.ipa_type = IPADDR_V4; + memcpy(&(vtep_ip.ipaddr_v4), + &(api_nh->gate.ipv4), + sizeof(struct in_addr)); + zebra_vxlan_evpn_vrf_route_add( + vrf_id, + &api.rmac, + &vtep_ip, + &api.prefix); + } break; + } case NEXTHOP_TYPE_IPV6: nexthop = route_entry_nexthop_ipv6_add( re, &api_nh->gate.ipv6); @@ -1265,7 +1299,7 @@ static int zread_route_del(struct zserv *client, u_short length, rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance, api.flags, &api.prefix, src_p, NULL, zvrf->table_id, - api.metric, false); + api.metric, false, &api.rmac); /* Stats */ switch (api.prefix.family) { @@ -1466,7 +1500,7 @@ static int zread_ipv4_delete(struct zserv *client, u_short length, table_id = zvrf->table_id; rib_delete(AFI_IP, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, NULL, NULL, table_id, 0, false); + api.flags, &p, NULL, NULL, table_id, 0, false, NULL); client->v4_route_del_cnt++; stream_failure: @@ -1884,7 +1918,8 @@ static int zread_ipv6_delete(struct zserv *client, u_short length, src_pp = NULL; rib_delete(AFI_IP6, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, src_pp, NULL, client->rtm_table, 0, false); + api.flags, &p, src_pp, NULL, client->rtm_table, 0, false, + NULL); client->v6_route_del_cnt++; @@ -2979,6 +3014,8 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "Interface Down Notifications: %d\n", client->ifdown_cnt); vty_out(vty, "VNI add notifications: %d\n", client->vniadd_cnt); vty_out(vty, "VNI delete notifications: %d\n", client->vnidel_cnt); + vty_out(vty, "L3-VNI add notifications: %d\n", client->l3vniadd_cnt); + vty_out(vty, "L3-VNI delete notifications: %d\n", client->l3vnidel_cnt); vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt); vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt); diff --git a/zebra/zserv.h b/zebra/zserv.h index c4b4e20df2..63db9d9eb9 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -110,6 +110,8 @@ struct zserv { u_int32_t bfd_client_reg_cnt; u_int32_t vniadd_cnt; u_int32_t vnidel_cnt; + u_int32_t l3vniadd_cnt; + u_int32_t l3vnidel_cnt; u_int32_t macipadd_cnt; u_int32_t macipdel_cnt; |
