diff options
-rw-r--r-- | bgpd/bgp_ecommunity.c | 14 | ||||
-rw-r--r-- | bgpd/bgp_evpn.c | 1147 | ||||
-rw-r--r-- | bgpd/bgp_evpn.h | 4 | ||||
-rw-r--r-- | bgpd/bgp_evpn_private.h | 83 | ||||
-rw-r--r-- | bgpd/bgp_evpn_vty.c | 402 | ||||
-rw-r--r-- | bgpd/bgp_memory.c | 2 | ||||
-rw-r--r-- | bgpd/bgp_memory.h | 3 | ||||
-rw-r--r-- | bgpd/bgp_route.c | 6 | ||||
-rw-r--r-- | bgpd/bgp_zebra.c | 36 | ||||
-rw-r--r-- | bgpd/bgpd.h | 3 | ||||
-rw-r--r-- | lib/log.c | 2 | ||||
-rw-r--r-- | lib/prefix.c | 68 | ||||
-rw-r--r-- | lib/prefix.h | 15 | ||||
-rw-r--r-- | lib/zclient.c | 10 | ||||
-rw-r--r-- | lib/zclient.h | 4 |
15 files changed, 1693 insertions, 106 deletions
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 20f5e15d6..d00a8dc2e 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -801,6 +801,20 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) len = sprintf( str_buf + str_pnt, "FS:marking %u", *(pnt+5)); + } else if (*pnt + == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) { + struct ethaddr mac; + pnt++; + memcpy(&mac, pnt, ETH_ALEN); + len = sprintf( + str_buf + str_pnt, + "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)mac.octet[0], + (uint8_t)mac.octet[1], + (uint8_t)mac.octet[2], + (uint8_t)mac.octet[3], + (uint8_t)mac.octet[4], + (uint8_t)mac.octet[5]); } else unk_ecom = 1; } else { diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 9f3fb3498..f448df13e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -53,13 +53,15 @@ extern struct zclient *zclient; DEFINE_QOBJ_TYPE(bgpevpn) +DEFINE_QOBJ_TYPE(evpnes) /* * Static function declarations */ -static void delete_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, - afi_t afi, safi_t safi, struct bgp_node *rn, +static void delete_evpn_route_entry(struct bgp *bgp, + afi_t afi, safi_t safi, + struct bgp_node *rn, struct bgp_info **ri); static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); @@ -67,6 +69,47 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); * Private functions. */ +/* compare two IPV4 VTEP IPs */ +static int evpn_vtep_ip_cmp(const void *p1, const void *p2) +{ + const struct in_addr *ip1 = p1; + const struct in_addr *ip2 = p2; + + if (!ip1 && !ip2) + return 1; + if (!ip1 || !ip2) + return 0; + return (ip1->s_addr == ip2->s_addr); +} + +/* + * Make hash key for ESI. + */ +static unsigned int esi_hash_keymake(void *p) +{ + struct evpnes *pes = p; + const void *pnt = (void *)pes->esi.val; + + return jhash(pnt, ESI_BYTES, 0xa5a5a55a); +} + +/* + * Compare two ESIs. + */ +static int esi_cmp(const void *p1, const void *p2) +{ + const struct evpnes *pes1 = p1; + const struct evpnes *pes2 = p2; + + if (pes1 == NULL && pes2 == NULL) + return 1; + + if (pes1 == NULL || pes2 == NULL) + return 0; + + return (memcmp(pes1->esi.val, pes2->esi.val, ESI_BYTES) == 0); +} + /* * Make vni hash key. */ @@ -593,6 +636,40 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, } /* + * Build extended community for EVPN ES (type-4) route + */ +static void build_evpn_type4_route_extcomm(struct evpnes *es, + struct attr *attr) +{ + struct ecommunity ecom_encap; + struct ecommunity ecom_es_rt; + struct ecommunity_val eval; + struct ecommunity_val eval_es_rt; + bgp_encap_types tnl_type; + struct ethaddr mac; + + /* 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 = (uint8_t *)eval.val; + attr->ecommunity = ecommunity_dup(&ecom_encap); + + /* ES import RT */ + memset(&mac, 0, sizeof(struct ethaddr)); + memset(&ecom_es_rt, 0, sizeof(ecom_es_rt)); + es_get_system_mac(&es->esi, &mac); + encode_es_rt_extcomm(&eval_es_rt, &mac); + ecom_es_rt.size = 1; + ecom_es_rt.val = (uint8_t *)eval_es_rt.val; + attr->ecommunity = + ecommunity_merge(attr->ecommunity, &ecom_es_rt); + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); +} + +/* * Build extended communities for EVPN prefix route. */ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, @@ -833,7 +910,7 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn, (struct prefix *)&rn->p, &vpn->prd); if (global_rn) { /* Delete route entry in the global EVPN table. */ - delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri); + delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri); /* Schedule for processing - withdraws to peers happen from * this table. @@ -847,6 +924,158 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn, bgp_info_delete(rn, old_local); } +static struct in_addr *es_vtep_new(struct in_addr vtep) +{ + struct in_addr *ip; + + ip = XCALLOC(MTYPE_BGP_EVPN_ES_VTEP, sizeof(struct in_addr)); + if (!ip) + return NULL; + + ip->s_addr = vtep.s_addr; + return ip; +} + +static void es_vtep_free(struct in_addr *ip) +{ + XFREE(MTYPE_BGP_EVPN_ES_VTEP, ip); +} + +/* check if VTEP is already part of the list */ +static int is_vtep_present_in_list(struct list *list, + struct in_addr vtep) +{ + struct listnode *node = NULL; + struct in_addr *tmp; + + for (ALL_LIST_ELEMENTS_RO(list, node, tmp)) { + if (tmp->s_addr == vtep.s_addr) + return 1; + } + return 0; +} + +/* Best path for ES route was changed, + update the list of VTEPs for this ES */ +static int evpn_es_install_vtep(struct bgp *bgp, + struct evpnes *es, + struct prefix_evpn *p, + struct in_addr rvtep) +{ + struct in_addr *vtep_ip; + + if (is_vtep_present_in_list(es->vtep_list, rvtep)) + return 0; + + + vtep_ip = es_vtep_new(rvtep); + if (vtep_ip) + listnode_add_sort(es->vtep_list, vtep_ip); + return 0; +} + +/* Best path for ES route was changed, + update the list of VTEPs for this ES */ +static int evpn_es_uninstall_vtep(struct bgp *bgp, + struct evpnes *es, + struct prefix_evpn *p, + struct in_addr rvtep) +{ + struct listnode *node, *nnode, *node_to_del = NULL; + struct in_addr *tmp; + + for (ALL_LIST_ELEMENTS(es->vtep_list, node, nnode, tmp)) { + if (tmp->s_addr == rvtep.s_addr) { + es_vtep_free(tmp); + node_to_del = node; + } + } + + if (node_to_del) + list_delete_node(es->vtep_list, node_to_del); + + return 0; +} + +/* + * Calculate the best path for a ES(type-4) route. + */ +static int evpn_es_route_select_install(struct bgp *bgp, + struct evpnes *es, + struct bgp_node *rn) +{ + int ret = 0; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp_info *old_select; /* old best */ + struct bgp_info *new_select; /* new best */ + struct bgp_info_pair old_and_new; + + /* Compute the best path. */ + bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], + &old_and_new, afi, safi); + old_select = old_and_new.old; + new_select = old_and_new.new; + + /* If the best path hasn't changed - see if something needs to be + * updated + */ + if (old_select && old_select == new_select + && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_IMPORTED + && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) + && !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED) + && !bgp->addpath_tx_used[afi][safi]) { + if (bgp_zebra_has_route_changed(rn, old_select)) { + ret = evpn_es_install_vtep(bgp, es, + (struct prefix_evpn *)&rn->p, + old_select->attr->nexthop); + } + UNSET_FLAG(old_select->flags, BGP_INFO_MULTIPATH_CHG); + bgp_zebra_clear_route_change_flags(rn); + return ret; + } + + /* If the user did a "clear" this flag will be set */ + UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR); + + /* bestpath has changed; update relevant fields and install or uninstall + * into the zebra RIB. + */ + if (old_select || new_select) + bgp_bump_version(rn); + + if (old_select) + bgp_info_unset_flag(rn, old_select, BGP_INFO_SELECTED); + if (new_select) { + bgp_info_set_flag(rn, new_select, BGP_INFO_SELECTED); + bgp_info_unset_flag(rn, new_select, BGP_INFO_ATTR_CHANGED); + UNSET_FLAG(new_select->flags, BGP_INFO_MULTIPATH_CHG); + } + + if (new_select && new_select->type == ZEBRA_ROUTE_BGP + && new_select->sub_type == BGP_ROUTE_IMPORTED) { + ret = evpn_es_install_vtep(bgp, es, + (struct prefix_evpn *)&rn->p, + new_select->attr->nexthop); + } else { + if (old_select && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_IMPORTED) + ret = evpn_es_uninstall_vtep(bgp, es, + (struct prefix_evpn *)&rn->p, + old_select->attr->nexthop); + } + + /* Clear any route change flags. */ + bgp_zebra_clear_route_change_flags(rn); + + /* Reap old select bgp_info, if it has been removed */ + if (old_select && CHECK_FLAG(old_select->flags, BGP_INFO_REMOVED)) + bgp_info_reap(rn, old_select); + + return ret; +} + /* * Calculate the best path for an EVPN route. Install/update best path in zebra, * if appropriate. @@ -991,6 +1220,177 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn) return local_ri->attr->sticky; } +/* + * create or update EVPN type4 route entry. + * This could be in the ES table or the global table. + * TODO: handle remote ES (type4) routes as well + */ +static int update_evpn_type4_route_entry(struct bgp *bgp, + struct evpnes *es, + afi_t afi, safi_t safi, + struct bgp_node *rn, + struct attr *attr, + int add, + struct bgp_info **ri, + int *route_changed) +{ + char buf[ESI_STR_LEN]; + char buf1[INET6_ADDRSTRLEN]; + struct bgp_info *tmp_ri = NULL; + struct bgp_info *local_ri = NULL; /* local route entry if any */ + struct bgp_info *remote_ri = NULL; /* remote route entry if any */ + struct attr *attr_new = NULL; + struct prefix_evpn *evp = NULL; + + *ri = NULL; + *route_changed = 1; + evp = (struct prefix_evpn *)&rn->p; + + /* locate the local and remote entries if any */ + for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) { + if (tmp_ri->peer == bgp->peer_self && + tmp_ri->type == ZEBRA_ROUTE_BGP && + tmp_ri->sub_type == BGP_ROUTE_STATIC) + local_ri = tmp_ri; + if (tmp_ri->type == ZEBRA_ROUTE_BGP && + tmp_ri->sub_type == BGP_ROUTE_IMPORTED && + CHECK_FLAG(tmp_ri->flags, BGP_INFO_VALID)) + remote_ri = tmp_ri; + } + + /* we don't expect to see a remote_ri at this point. + * An ES route has esi + vtep_ip as the key, + * We shouldn't see the same route from any other vtep. + */ + if (remote_ri) { + zlog_err( + "%u ERROR: local es route for ESI: %s Vtep %s also learnt from remote", + bgp->vrf_id, + esi_to_str(&evp->prefix.es_addr.esi, buf, sizeof(buf)), + ipaddr2str(&es->originator_ip, buf1, sizeof(buf1))); + return -1; + } + + if (!local_ri && !add) + return 0; + + /* create or update the entry */ + if (!local_ri) { + + /* Add or update attribute to hash */ + attr_new = bgp_attr_intern(attr); + + /* Create new route with its attribute. */ + tmp_ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, + 0, bgp->peer_self, attr_new, rn); + SET_FLAG(tmp_ri->flags, BGP_INFO_VALID); + + /* add the newly created path to the route-node */ + bgp_info_add(rn, tmp_ri); + } else { + tmp_ri = local_ri; + if (attrhash_cmp(tmp_ri->attr, attr) + && !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED)) + *route_changed = 0; + else { + /* 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 back the route entry. */ + *ri = tmp_ri; + return 0; +} + +/* update evpn es (type-4) route */ +static int update_evpn_type4_route(struct bgp *bgp, + struct evpnes *es, + struct prefix_evpn *p) +{ + int ret = 0; + int route_changed = 0; + char buf[ESI_STR_LEN]; + char buf1[INET6_ADDRSTRLEN]; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct attr attr; + struct attr *attr_new = NULL; + struct bgp_node *rn = NULL; + struct bgp_info *ri = NULL; + + memset(&attr, 0, sizeof(struct attr)); + + /* Build path-attribute for this route. */ + bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + attr.nexthop = es->originator_ip.ipaddr_v4; + attr.mp_nexthop_global_in = es->originator_ip.ipaddr_v4; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + + /* Set up extended community. */ + build_evpn_type4_route_extcomm(es, &attr); + + /* First, create (or fetch) route node within the ESI. */ + /* NOTE: There is no RD here. */ + rn = bgp_node_get(es->route_table, (struct prefix *)p); + + /* Create or update route entry. */ + ret = update_evpn_type4_route_entry(bgp, es, afi, safi, rn, + &attr, 1, &ri, + &route_changed); + if (ret != 0) { + zlog_err("%u ERROR: Failed to updated ES route ESI: %s VTEP %s", + bgp->vrf_id, + esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)), + ipaddr2str(&es->originator_ip, buf1, sizeof(buf1))); + } + + assert(ri); + attr_new = ri->attr; + + /* Perform route selection; + * this is just to set the flags correctly + * as local route in the ES always wins. + */ + evpn_es_route_select_install(bgp, es, rn); + bgp_unlock_node(rn); + + /* If this is a new route or some attribute has changed, export the + * route to the global table. The route will be advertised to peers + * from there. Note that this table is a 2-level tree (RD-level + + * Prefix-level) similar to L3VPN routes. + */ + if (route_changed) { + struct bgp_info *global_ri; + + rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, + (struct prefix *)p, &es->prd); + update_evpn_type4_route_entry(bgp, es, afi, safi, + rn, attr_new, + 1, &global_ri, + &route_changed); + + /* Schedule for processing and unlock node. */ + bgp_process(bgp, rn, afi, safi); + bgp_unlock_node(rn); + } + + /* Unintern temporary. */ + aspath_unintern(&attr.aspath); + return 0; +} + 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, @@ -1171,8 +1571,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!local_ri) { /* When learnt locally for the first time but already known from * remote, we have to initiate appropriate MAC mobility steps. - * This - * is applicable when updating the VNI routing table. + * This is applicable when updating the VNI routing table. * We need to skip mobility steps for g/w macs (local mac on g/w * SVI) advertised in EVPN. * This will ensure that local routes are preferred for g/w macs @@ -1354,19 +1753,22 @@ 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) +/* + * Delete EVPN route entry. + * The entry can be in ESI/VNI table or the global table. + */ +static void delete_evpn_route_entry(struct bgp *bgp, + afi_t afi, safi_t safi, + struct bgp_node *rn, + struct bgp_info **ri) { - struct bgp_info *tmp_ri = NULL; + struct bgp_info *tmp_ri; *ri = NULL; - /* find the matching route entry */ + /* Now, find matching route. */ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) - if (tmp_ri->peer == bgp_def->peer_self + if (tmp_ri->peer == bgp->peer_self && tmp_ri->type == ZEBRA_ROUTE_BGP && tmp_ri->sub_type == BGP_ROUTE_STATIC) break; @@ -1378,6 +1780,57 @@ static void delete_evpn_type5_route_entry(struct bgp *bgp_def, bgp_info_delete(rn, tmp_ri); } + + +/* Delete EVPN ES (type-4) route */ +static int delete_evpn_type4_route(struct bgp *bgp, + struct evpnes *es, + struct prefix_evpn *p) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp_info *ri; + struct bgp_node *rn = NULL; /* rn in esi table */ + struct bgp_node *global_rn = NULL; /* rn in global table */ + + /* First, locate the route node within the ESI. + * If it doesn't exist, ther is nothing to do. + * Note: there is no RD here. + */ + rn = bgp_node_lookup(es->route_table, (struct prefix *)p); + if (!rn ) + return 0; + + /* Next, locate route node in the global EVPN routing table. + * Note that this table is a 2-level tree (RD-level + Prefix-level) + */ + global_rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, + (struct prefix *)p, &es->prd); + if (global_rn) { + + /* Delete route entry in the global EVPN table. */ + delete_evpn_route_entry(bgp, afi, safi, + global_rn, &ri); + + /* Schedule for processing - withdraws to peers happen from + * this table. + */ + if (ri) + bgp_process(bgp, global_rn, afi, safi); + bgp_unlock_node(global_rn); + } + + /* + * Delete route entry in the ESI route table. + * This can just be removed. + */ + delete_evpn_route_entry(bgp, afi, safi, rn, &ri); + if (ri) + bgp_info_reap(rn, ri); + bgp_unlock_node(rn); + return 0; +} + /* Delete EVPN type5 route */ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) { @@ -1397,7 +1850,7 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) if (!rn) return 0; - delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri); + delete_evpn_route_entry(bgp_def, afi, safi, rn, &ri); if (ri) bgp_process(bgp_def, rn, afi, safi); bgp_unlock_node(rn); @@ -1405,32 +1858,6 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) } /* - * Delete EVPN route entry. This could be in the VNI route table - * or the global route table. - */ -static void delete_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, - afi_t afi, safi_t safi, struct bgp_node *rn, - struct bgp_info **ri) -{ - struct bgp_info *tmp_ri; - - *ri = NULL; - - /* Now, find matching route. */ - for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) - if (tmp_ri->peer == bgp->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 route (of type based on prefix) for specified VNI and * schedule for processing. */ @@ -1459,7 +1886,7 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, (struct prefix *)p, &vpn->prd); if (global_rn) { /* Delete route entry in the global EVPN table. */ - delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri); + delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri); /* Schedule for processing - withdraws to peers happen from * this table. @@ -1471,7 +1898,7 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, /* Delete route entry in the VNI route table. This can just be removed. */ - delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri); + delete_evpn_route_entry(bgp, afi, safi, rn, &ri); if (ri) bgp_info_reap(rn, ri); bgp_unlock_node(rn); @@ -1629,7 +2056,7 @@ static int delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; - delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri); + delete_evpn_route_entry(bgp, afi, safi, rn, &ri); if (ri) bgp_process(bgp, rn, afi, safi); } @@ -1670,7 +2097,7 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; - delete_evpn_route_entry(bgp, vpn, afi, safi, rn, &ri); + delete_evpn_route_entry(bgp, afi, safi, rn, &ri); /* Route entry in local table gets deleted immediately. */ if (ri) @@ -1681,6 +2108,27 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) } /* + * Delete all routes in per ES route-table + */ +static int delete_all_es_routes(struct bgp *bgp, struct evpnes *es) +{ + struct bgp_node *rn; + struct bgp_info *ri, *nextri; + + /* Walk this ES's route table and delete all routes. */ + for (rn = bgp_table_top(es->route_table); rn; + rn = bgp_route_next(rn)) { + for (ri = rn->info; (ri != NULL) && (nextri = ri->next, 1); + ri = nextri) { + bgp_info_delete(rn, ri); + bgp_info_reap(rn, ri); + } + } + + return 0; +} + +/* * Delete all routes in the per-VNI route table. */ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) @@ -1723,6 +2171,30 @@ static int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) return update_all_type2_routes(bgp, vpn); } +/* Delete (and withdraw) local routes for specified ES from global and ES table. + * Also remove all other routes from the per ES table. + * Invoked when ES is deleted. + */ +static int delete_routes_for_es(struct bgp *bgp, struct evpnes *es) +{ + int ret; + char buf[ESI_STR_LEN]; + struct prefix_evpn p; + + /* Delete and withdraw locally learnt ES route */ + build_evpn_type4_prefix(&p, &es->esi, es->originator_ip.ipaddr_v4); + ret = delete_evpn_type4_route(bgp, es, &p); + if (ret) { + zlog_err( + "%u failed to delete type-4 route for ESI %s", + bgp->vrf_id, + esi_to_str(&es->esi, buf, sizeof(buf))); + } + + /* Delete all routes from per ES table */ + return delete_all_es_routes(bgp, es); +} + /* * Delete (and withdraw) local routes for specified VNI from the global * table and per-VNI table. After this, remove all other routes from @@ -1785,6 +2257,68 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +/* Install EVPN route entry in ES */ +static int install_evpn_route_entry_in_es(struct bgp *bgp, + struct evpnes *es, + struct prefix_evpn *p, + struct bgp_info *parent_ri) +{ + int ret = 0; + struct bgp_node *rn = NULL; + struct bgp_info *ri = NULL; + struct attr *attr_new = NULL; + + /* Create (or fetch) route within the VNI. + * NOTE: There is no RD here. + */ + rn = bgp_node_get(es->route_table, (struct prefix *)p); + + /* 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, BGP_ROUTE_IMPORTED, 0, + parent_ri->peer, attr_new, rn); + SET_FLAG(ri->flags, BGP_INFO_VALID); + bgp_info_extra_get(ri); + ri->extra->parent = parent_ri; + 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. */ + ret = evpn_es_route_select_install(bgp, es, rn); + return ret; +} + /* * Install route entry into the VRF routing table and invoke route selection. */ @@ -1966,6 +2500,47 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, return ret; } +/* Uninstall EVPN route entry from ES route table */ +static int uninstall_evpn_route_entry_in_es(struct bgp *bgp, + struct evpnes *es, + struct prefix_evpn *p, + struct bgp_info *parent_ri) +{ + int ret; + struct bgp_node *rn; + struct bgp_info *ri; + + if (!es->route_table) + return 0; + + /* Locate route within the ESI. + * NOTE: There is no RD here. + */ + rn = bgp_node_lookup(es->route_table, (struct prefix *)p); + 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. */ + ret = evpn_es_route_select_install(bgp, es, rn); + + /* Unlock route node. */ + bgp_unlock_node(rn); + + return ret; +} + /* * Uninstall route entry from the VRF routing table and send message * to zebra, if appropriate. @@ -2071,6 +2646,22 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, } /* + * Given a prefix, see if it belongs to ES. + */ +static int is_prefix_matching_for_es(struct prefix_evpn *p, + struct evpnes *es) +{ + /* if not an ES route return false */ + if (p->prefix.route_type != BGP_EVPN_ES_ROUTE) + return 0; + + if (memcmp(&p->prefix.es_addr.esi, &es->esi, sizeof(esi_t)) == 0) + return 1; + + return 0; +} + +/* * Given a route entry and a VRF, see if this route entry should be * imported into the VRF i.e., RTs match. */ @@ -2203,6 +2794,74 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +static int install_uninstall_routes_for_es(struct bgp *bgp, + struct evpnes *es, + int install) +{ + int ret; + afi_t afi; + safi_t safi; + char buf[PREFIX_STRLEN]; + char buf1[ESI_STR_LEN]; + struct bgp_node *rd_rn, *rn; + struct bgp_table *table; + struct bgp_info *ri; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + + /* Walk entire global routing table and evaluate routes which could be + * imported into this 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->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; + + for (ri = rn->info; ri; ri = ri->next) { + + /* Consider "valid" remote routes applicable for + * this ES. + */ + if (!(CHECK_FLAG(ri->flags, BGP_INFO_VALID) + && ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (is_prefix_matching_for_es(evp, es)) { + if (install) + ret = + install_evpn_route_entry_in_es( + bgp, es, evp, ri); + else + ret = + uninstall_evpn_route_entry_in_es( + bgp, es, evp, ri); + + if (ret) { + zlog_err( + "Failed to %s EVPN %s route in ESI %s", + install ? "install" + : "uninstall", + prefix2str(evp, buf, + sizeof(buf)), + esi_to_str(&es->esi, + buf1, + sizeof(buf1))); + return ret; + } + } + } + } + } + return 0; +} + /* * Install or uninstall mac-ip routes are appropriate for this * particular VRF. @@ -2358,6 +3017,15 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, return 0; } +/* Install any existing remote ES routes applicable for this ES into its routing + * table. This is invoked when ES comes up. + */ +static int install_routes_for_es(struct bgp *bgp, struct evpnes *es) +{ + return install_uninstall_routes_for_es(bgp, es, 1); +} + + /* Install any existing remote routes applicable for this VRF into VRF RIB. This * is invoked upon l3vni-add or l3vni import rt change */ @@ -2416,6 +3084,32 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) 0); } +/* Install or unistall route in ES */ +static int install_uninstall_route_in_es(struct bgp *bgp, struct evpnes *es, + afi_t afi, safi_t safi, + struct prefix_evpn *evp, + struct bgp_info *ri, + int install) +{ + int ret = 0; + char buf[ESI_STR_LEN]; + + if (install) + ret = install_evpn_route_entry_in_es(bgp, es, evp, ri); + else + ret = uninstall_evpn_route_entry_in_es(bgp, es, evp, ri); + + if (ret) { + zlog_err("%u: Failed to %s EVPN %s route in ESI %s", + bgp->vrf_id, install ? "install" : "uninstall", + "ES", + esi_to_str(&evp->prefix.es_addr.esi, buf, + sizeof(buf))); + return ret; + } + return 0; +} + /* * Install or uninstall route in matching VRFs (list). */ @@ -2498,7 +3192,7 @@ static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi, } /* - * Install or uninstall route for appropriate VNIs. + * Install or uninstall route for appropriate VNIs/ESIs. */ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, struct prefix *p, struct bgp_info *ri, @@ -2511,9 +3205,10 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, assert(attr); - /* Only type-2 and type-3 and type-5 are supported currently */ + /* Only type-2, type-3, type-4 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_ES_ROUTE || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) return 0; @@ -2525,9 +3220,8 @@ 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/VRFs match and import - * the route into matching VNIs/VRFs. - */ + /* An EVPN route belongs to a VNI or a VRF or an ESI based on the RTs + * attached to the route */ for (i = 0; i < ecom->size; i++) { uint8_t *pnt; uint8_t type, sub_type; @@ -2535,6 +3229,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, struct ecommunity_val eval_tmp; struct irt_node *irt; /* import rt for l2vni */ struct vrf_irt_node *vrf_irt; /* import rt for l3vni */ + struct evpnes *es; /* Only deal with RTs */ pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); @@ -2545,44 +3240,70 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (sub_type != ECOMMUNITY_ROUTE_TARGET) continue; - /* Import route into matching l2-vnis (type-2/type-3 routes go - * into l2vni table) + /* + * macip routes (type-2) are imported into VNI and VRF tables. + * IMET route is imported into VNI table. + * prefix routes are imported into VRF table. */ - irt = lookup_import_rt(bgp, eval); - if (irt) - install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, - irt->vnis, import); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || + evp->prefix.route_type == BGP_EVPN_IMET_ROUTE || + evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { - /* 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) - 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); + irt = lookup_import_rt(bgp, eval); + if (irt) + install_uninstall_route_in_vnis(bgp, afi, safi, + evp, ri, + irt->vnis, + import); + + vrf_irt = lookup_vrf_import_rt(eval); + if (vrf_irt) + 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) + install_uninstall_route_in_vnis(bgp, afi, safi, + evp, ri, + irt->vnis, + import); + if (vrf_irt) + install_uninstall_route_in_vrfs(bgp, afi, safi, + evp, ri, + vrf_irt->vrfs, + import); + } + + /* es route is imported into the es table */ + if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE) { + + /* we will match based on the entire esi to avoid + * imoort of an es route for esi2 into esi1 + */ + es = bgp_evpn_lookup_es(bgp, &evp->prefix.es_addr.esi); + if (es && is_es_local(es)) + install_uninstall_route_in_es(bgp, es, + afi, safi, + evp, ri, import); } - if (irt) - install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, - irt->vnis, import); - if (vrf_irt) - install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri, - vrf_irt->vrfs, import); } return 0; @@ -2750,7 +3471,7 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) (struct prefix *)&p, &vpn->prd); if (global_rn) { /* Delete route entry in the global EVPN table. */ - delete_evpn_route_entry(bgp, vpn, afi, safi, global_rn, &ri); + delete_evpn_route_entry(bgp, afi, safi, global_rn, &ri); /* Schedule for processing - withdraws to peers happen from * this table. @@ -2853,7 +3574,7 @@ static int process_type2_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_2_ROUTE_PREFIXLEN; + p.prefixlen = EVPN_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE; /* Copy Ethernet Seg Identifier */ @@ -2972,7 +3693,7 @@ static int process_type3_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_3_ROUTE_PREFIXLEN; + p.prefixlen = EVPN_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_IMET_ROUTE; /* Copy Ethernet Tag */ @@ -3005,6 +3726,67 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, } /* + * Process received EVPN type-4 route (advertise or withdraw). + */ +static int process_type4_route(struct peer *peer, afi_t afi, safi_t safi, + struct attr *attr, uint8_t *pfx, int psize, + uint32_t addpath_id) +{ + int ret; + esi_t esi; + uint8_t ipaddr_len; + struct in_addr vtep_ip; + struct prefix_rd prd; + struct prefix_evpn p; + + /* Type-4 route should be either 23 or 35 bytes + * RD (8), ESI (10), ip-len (1), ip (4 or 16) + */ + if (psize != 23 && psize != 35) { + zlog_err("%u:%s - Rx EVPN Type-4 NLRI with invalid length %d", + peer->bgp->vrf_id, peer->host, psize); + return -1; + } + + /* Make prefix_rd */ + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(&prd.val, pfx, 8); + pfx += 8; + + /* get the ESI */ + memcpy(&esi, pfx, ESI_BYTES); + pfx += ESI_BYTES; + + + /* Get the IP. */ + ipaddr_len = *pfx++; + if (ipaddr_len == IPV4_MAX_BITLEN) { + memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN); + } else { + zlog_err( + "%u:%s - Rx EVPN Type-4 NLRI with unsupported IP address length %d", + peer->bgp->vrf_id, peer->host, ipaddr_len); + return -1; + } + + build_evpn_type4_prefix(&p, &esi, vtep_ip); + /* Process the route. */ + if (attr) { + ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, + afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + &prd, NULL, 0, 0, NULL); + } + else { + ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, + afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + &prd, NULL, 0, NULL); + } + return ret; +} + + +/* * Process received EVPN type-5 route (advertise or withdraw). */ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, @@ -3039,7 +3821,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.prefixlen = EVPN_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; /* Additional information outside of prefix - ESI and GW IP */ @@ -3695,6 +4477,7 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) { char buf1[ETHER_ADDR_STRLEN]; char buf2[PREFIX2STR_BUFFER]; + char buf3[ESI_STR_LEN]; if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) { snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type, @@ -3736,6 +4519,13 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) is_evpn_prefix_ipaddr_v4(p) ? inet_ntoa(p->prefix.prefix_addr.ip.ipaddr_v4) : inet6_ntoa(p->prefix.prefix_addr.ip.ipaddr_v6)); + } else if (p->prefix.route_type == BGP_EVPN_ES_ROUTE) { + snprintf(buf, len, "[%d]:[%s]:[%d]:[%s]", + p->prefix.route_type, + esi_to_str(&p->prefix.es_addr.esi, buf3, sizeof(buf3)), + is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN + : IPV6_MAX_BITLEN, + inet_ntoa(p->prefix.es_addr.ip.ipaddr_v4)); } else { /* For EVPN route types not supported yet. */ snprintf(buf, len, "(unsupported route type %d)", @@ -3801,6 +4591,14 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, stream_put_in_addr(s, &evp->prefix.imet_addr.ip.ipaddr_v4); break; + case BGP_EVPN_ES_ROUTE: + stream_putc(s, 23); /* TODO: length: assumes ipv4 VTEP */ + stream_put(s, prd->val, 8); /* RD */ + stream_put(s, evp->prefix.es_addr.esi.val, 10); /* ESI */ + stream_putc(s, IPV4_MAX_BITLEN); /* IP address Length - bits */ + stream_put_in_addr(s, &evp->prefix.es_addr.ip.ipaddr_v4); /* VTEP IP*/ + break; + case BGP_EVPN_IP_PREFIX_ROUTE: /* TODO: AddPath support. */ evpn_mpattr_encode_type5(s, p, prd, label, num_labels, attr); @@ -3885,6 +4683,17 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, } break; + case BGP_EVPN_ES_ROUTE: + if (process_type4_route(peer, afi, safi, + withdraw ? NULL : attr, pnt, + psize, addpath_id)) { + zlog_err( + "%u:%s - Error in processing EVPN type-4 NLRI size %d", + peer->bgp->vrf_id, peer->host, psize); + return -1; + } + break; + case BGP_EVPN_IP_PREFIX_ROUTE: if (process_type5_route(peer, afi, safi, attr, pnt, psize, addpath_id, withdraw)) { @@ -4148,7 +4957,81 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) } /* - * Import route into matching VNI(s). + * Lookup local ES. + */ +struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi) +{ + struct evpnes *es; + struct evpnes tmp; + + memset(&tmp, 0, sizeof(struct evpnes)); + memcpy(&tmp.esi, esi, sizeof(esi_t)); + es = hash_lookup(bgp->esihash, &tmp); + return es; +} + +/* + * Create a new local es - invoked upon zebra notification. + */ +struct evpnes *bgp_evpn_es_new(struct bgp *bgp, + esi_t *esi, + struct ipaddr *originator_ip) +{ + char buf[100]; + struct evpnes *es; + + if (!bgp) + return NULL; + + es = XCALLOC(MTYPE_BGP_EVPN_ES, sizeof(struct evpnes)); + if (!es) + return NULL; + + /* set the ESI and originator_ip */ + memcpy(&es->esi, esi, sizeof(esi_t)); + memcpy(&es->originator_ip, originator_ip, sizeof(struct ipaddr)); + + /* Initialise the VTEP list */ + es->vtep_list = list_new(); + es->vtep_list->cmp = (int (*)(void *, void *))evpn_vtep_ip_cmp; + + /* auto derive RD for this es */ + bf_assign_index(bm->rd_idspace, es->rd_id); + es->prd.family = AF_UNSPEC; + es->prd.prefixlen = 64; + sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), es->rd_id); + (void)str2prefix_rd(buf, &es->prd); + + /* Initialize the ES route table */ + es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN); + + /* Add to hash */ + if (!hash_get(bgp->esihash, es, hash_alloc_intern)) { + XFREE(MTYPE_BGP_EVPN_ES, es); + return NULL; + } + + QOBJ_REG(es, evpnes); + return es; +} + +/* + * Free a given ES - + * This just frees appropriate memory, caller should have taken other + * needed actions. + */ +void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es) +{ + list_delete_and_null(&es->vtep_list); + bgp_table_unlock(es->route_table); + bf_release_index(bm->rd_idspace, es->rd_id); + hash_release(bgp->esihash, es); + QOBJ_UNREG(es); + XFREE(MTYPE_BGP_EVPN_ES, es); +} + +/* + * Import evpn route from global table to VNI/VRF/ESI. */ int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, struct prefix *p, struct bgp_info *ri) @@ -4157,7 +5040,7 @@ int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, } /* - * Unimport route from matching VNI(s). + * Unimport evpn route from VNI/VRF/ESI. */ int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, struct prefix *p, struct bgp_info *ri) @@ -4602,6 +5485,84 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, } /* + * bgp_evpn_local_es_del + */ +int bgp_evpn_local_es_del(struct bgp *bgp, + esi_t *esi, + struct ipaddr *originator_ip) +{ + char buf[ESI_STR_LEN]; + struct evpnes *es = NULL; + + if (!bgp->esihash) { + zlog_err("%u: ESI hash not yet created", bgp->vrf_id); + return -1; + } + + /* Lookup ESI hash - should exist. */ + es = bgp_evpn_lookup_es(bgp, esi); + if (!es) { + zlog_warn("%u: ESI hash entry for ESI %s at Local ES DEL", + bgp->vrf_id, + esi_to_str(esi, buf, sizeof(buf))); + return -1; + } + + /* Delete all local EVPN ES routes from ESI table + * and schedule for processing (to withdraw from peers)) + */ + delete_routes_for_es(bgp, es); + + /* free the hash entry */ + bgp_evpn_es_free(bgp, es); + + return 0; +} + +/* + * bgp_evpn_local_es_add + */ +int bgp_evpn_local_es_add(struct bgp *bgp, + esi_t *esi, + struct ipaddr *originator_ip) +{ + char buf[ESI_STR_LEN]; + struct evpnes *es = NULL; + struct prefix_evpn p; + + if (!bgp->esihash) { + zlog_err("%u: ESI hash not yet created", bgp->vrf_id); + return -1; + } + + /* create the new es */ + es = bgp_evpn_lookup_es(bgp,esi); + if (!es) { + es = bgp_evpn_es_new(bgp, esi, originator_ip); + if (!es) { + zlog_err( + "%u: Failed to allocate ES entry for ESI %s - at Local ES Add", + bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf))); + return -1; + } + } + UNSET_FLAG(es->flags, EVPNES_REMOTE); + SET_FLAG(es->flags, EVPNES_LOCAL); + + build_evpn_type4_prefix(&p, esi, originator_ip->ipaddr_v4); + if (update_evpn_type4_route(bgp, es, &p)) { + zlog_err("%u: Type4 route creation failure for ESI %s", + bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf))); + return -1; + } + + /* import all remote ES routes in th ES table */ + install_routes_for_es(bgp, es); + + return 0; +} + +/* * Cleanup EVPN information on disable - Need to delete and withdraw * EVPN routes from peers. */ @@ -4631,6 +5592,9 @@ void bgp_evpn_cleanup(struct bgp *bgp) if (bgp->vnihash) hash_free(bgp->vnihash); bgp->vnihash = NULL; + if (bgp->esihash) + hash_free(bgp->esihash); + bgp->esihash = NULL; if (bgp->vrf_import_rtl) list_delete_and_null(&bgp->vrf_import_rtl); if (bgp->vrf_export_rtl) @@ -4649,6 +5613,9 @@ void bgp_evpn_init(struct bgp *bgp) { bgp->vnihash = hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash"); + bgp->esihash = + hash_create(esi_hash_keymake, esi_cmp, + "BGP EVPN Local ESI Hash"); bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 8e954159c..91d4c9fac 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -141,6 +141,10 @@ 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, vrf_id_t tenant_vrf_id); +extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, + struct ipaddr *originator_ip); +extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi, + struct ipaddr *originator_ip); 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 39a08e703..bafe81b25 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -31,9 +31,7 @@ #define RT_ADDRSTRLEN 28 /* 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 +#define EVPN_ROUTE_PREFIXLEN 224 /* EVPN route types. */ typedef enum { @@ -98,6 +96,38 @@ struct bgpevpn { DECLARE_QOBJ_TYPE(bgpevpn) +struct evpnes { + + /* Ethernet Segment Identifier */ + esi_t esi; + + /* es flags */ + uint16_t flags; +#define EVPNES_LOCAL 0x01 +#define EVPNES_REMOTE 0x02 + + /* Id for deriving the RD + * automatically for this ESI */ + uint16_t rd_id; + + /* RD for this VNI. */ + struct prefix_rd prd; + + /* originator ip address */ + struct ipaddr originator_ip; + + /* list of VTEPs in the same site */ + struct list *vtep_list; + + /* Route table for EVPN routes for + * this ESI. - type4 routes */ + struct bgp_table *route_table; + + QOBJ_FIELDS +}; + +DECLARE_QOBJ_TYPE(evpnes) + /* Mapping of Import RT to VNIs. * The Import RTs of all VNIs are maintained in a hash table with each * RT linking to all VNIs that will import routes matching this RT. @@ -238,6 +268,15 @@ static inline int is_vni_param_configured(struct bgpevpn *vpn) || is_export_rt_configured(vpn)); } +static inline void encode_es_rt_extcomm(struct ecommunity_val *eval, + struct ethaddr *mac) +{ + memset(eval, 0, sizeof(struct ecommunity_val)); + eval->val[0] = ECOMMUNITY_ENCODE_EVPN; + eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT; + memcpy(&eval->val[2], mac, ETH_ALEN); +} + static inline void encode_rmac_extcomm(struct ecommunity_val *eval, struct ethaddr *rmac) { @@ -326,7 +365,7 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p, { memset(p, 0, sizeof(struct prefix_evpn)); p->family = AF_EVPN; - p->prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN; + p->prefixlen = EVPN_ROUTE_PREFIXLEN; p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE; memcpy(&p->prefix.macip_addr.mac.octet, mac->octet, ETH_ALEN); p->prefix.macip_addr.ip.ipa_type = IPADDR_NONE; @@ -352,7 +391,7 @@ static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp, memset(evp, 0, sizeof(struct prefix_evpn)); evp->family = AF_EVPN; - evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN; + evp->prefixlen = EVPN_ROUTE_PREFIXLEN; evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; evp->prefix.prefix_addr.ip_prefix_length = ip_prefix->prefixlen; evp->prefix.prefix_addr.ip.ipa_type = ip.ipa_type; @@ -364,12 +403,26 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p, { memset(p, 0, sizeof(struct prefix_evpn)); p->family = AF_EVPN; - p->prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN; + p->prefixlen = EVPN_ROUTE_PREFIXLEN; p->prefix.route_type = BGP_EVPN_IMET_ROUTE; p->prefix.imet_addr.ip.ipa_type = IPADDR_V4; p->prefix.imet_addr.ip.ipaddr_v4 = originator_ip; } +static inline void build_evpn_type4_prefix(struct prefix_evpn *p, + esi_t *esi, + struct in_addr originator_ip) +{ + memset(p, 0, sizeof(struct prefix_evpn)); + p->family = AF_EVPN; + p->prefixlen = EVPN_ROUTE_PREFIXLEN; + p->prefix.route_type = BGP_EVPN_ES_ROUTE; + p->prefix.es_addr.ip_prefix_length = IPV4_MAX_BITLEN; + p->prefix.es_addr.ip.ipa_type = IPADDR_V4; + p->prefix.es_addr.ip.ipaddr_v4 = originator_ip; + memcpy(&p->prefix.es_addr.esi, esi, sizeof(esi_t)); +} + static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi, safi_t safi) { @@ -384,6 +437,20 @@ static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi, return 0; } +static inline void es_get_system_mac(esi_t *esi, + struct ethaddr *mac) +{ + /* for type-1 and type-3 ESIs, + the system mac starts at val[1] + */ + memcpy(mac, &esi->val[1], ETH_ALEN); +} + +static inline int is_es_local(struct evpnes *es) +{ + return CHECK_FLAG(es->flags, EVPNES_LOCAL) ? 1 : 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); @@ -417,4 +484,8 @@ extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id); extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn); +extern struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi); +extern struct evpnes *bgp_evpn_es_new(struct bgp *bgp, esi_t *esi, + struct ipaddr *originator_ip); +extern void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 12f18016b..2f4349b32 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -328,6 +328,7 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n"); vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); + vty_out(vty, "EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]\n"); vty_out(vty, "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n"); vty_out(vty, "%s", ri_header); } @@ -409,6 +410,45 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, json_object_object_add(json, "exportRts", json_export_rtl); } +static void display_es(struct vty *vty, struct evpnes *es, json_object *json) +{ + struct in_addr *vtep; + char buf[ESI_STR_LEN]; + char buf1[RD_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct listnode *node = NULL; + json_object *json_vteps = NULL; + + if (json) { + json_vteps = json_object_new_array(); + json_object_string_add(json, "esi", + esi_to_str(&es->esi, buf, sizeof(buf))); + json_object_string_add(json, "rd", + prefix_rd2str(&es->prd, buf1, + sizeof(buf1))); + json_object_string_add(json,"originatorIp", + ipaddr2str(&es->originator_ip, buf2, sizeof(buf2))); + if (es->vtep_list) { + for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep)) + json_object_array_add(json_vteps, + json_object_new_string(inet_ntoa(*vtep))); + } + json_object_object_add(json, "vteps", json_vteps); + } else { + vty_out(vty, "ESI: %s\n", + esi_to_str(&es->esi, buf, sizeof(buf))); + vty_out(vty, " RD: %s\n", prefix_rd2str(&es->prd, buf1, + sizeof(buf1))); + vty_out(vty, " Originator-IP: %s\n", + ipaddr2str(&es->originator_ip, buf2, sizeof(buf2))); + if (es->vtep_list) { + vty_out(vty, " VTEP List:\n"); + for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep)) + vty_out(vty," %s\n", inet_ntoa(*vtep)); + } + } +} + static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) { char buf1[RD_ADDRSTRLEN]; @@ -487,6 +527,88 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object_object_add(json, "exportRts", json_export_rtl); } +static void show_esi_routes(struct bgp *bgp, + struct evpnes *es, + struct vty *vty, + json_object *json) +{ + int header = 1; + struct bgp_node *rn; + struct bgp_info *ri; + uint32_t prefix_cnt, path_cnt; + uint64_t tbl_ver; + + prefix_cnt = path_cnt = 0; + + tbl_ver = es->route_table->version; + for (rn = bgp_table_top(es->route_table); rn; + rn = bgp_route_next(rn)) { + int add_prefix_to_json = 0; + char prefix_str[BUFSIZ]; + json_object *json_paths = NULL; + json_object *json_prefix = NULL; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + sizeof(prefix_str)); + + if (json) + json_prefix = json_object_new_object(); + + if (rn->info) { + /* Overall header/legend displayed once. */ + if (header) { + bgp_evpn_show_route_header(vty, bgp, + tbl_ver, json); + header = 0; + } + + prefix_cnt++; + } + + if (json) + json_paths = json_object_new_array(); + + /* For EVPN, the prefix is displayed for each path (to fit in + * with code that already exists). + */ + for (ri = rn->info; ri; ri = ri->next) { + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + + route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, json_path); + + if (json) + json_object_array_add(json_paths, json_path); + + path_cnt++; + add_prefix_to_json = 1; + } + + if (json && add_prefix_to_json) { + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + rn->p.prefixlen); + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json, prefix_str, json_prefix); + } + } + + if (json) { + json_object_int_add(json, "numPrefix", prefix_cnt); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (prefix_cnt == 0) + vty_out(vty, "No EVPN prefixes exist for this ESI"); + else + vty_out(vty, "\nDisplayed %u prefixes (%u paths)\n", + 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) @@ -575,7 +697,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, vty_out(vty, "No EVPN prefixes %sexist for this VNI", type ? "(of requested type) " : ""); else - vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s", + vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n", prefix_cnt, path_cnt, type ? " (of requested type)" : ""); } @@ -707,6 +829,47 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, } } +static void show_es_entry(struct hash_backet *backet, void *args[]) +{ + char buf[ESI_STR_LEN]; + char buf1[RD_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct in_addr *vtep = NULL; + struct vty *vty = args[0]; + json_object *json = args[1]; + json_object *json_vteps = NULL; + struct listnode *node = NULL; + struct evpnes *es = (struct evpnes *)backet->data; + + if (json) { + json_vteps = json_object_new_array(); + json_object_string_add(json, "esi", + esi_to_str(&es->esi, buf, sizeof(buf))); + json_object_string_add(json, "type", + is_es_local(es) ? "Local" : "Remote"); + json_object_string_add(json, "rd", + prefix_rd2str(&es->prd, buf1, + sizeof(buf1))); + json_object_string_add(json,"originatorIp", + ipaddr2str(&es->originator_ip, buf2, sizeof(buf2))); + if (es->vtep_list) { + for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep)) + json_object_array_add(json_vteps, + json_object_new_string( + inet_ntoa(*vtep))); + } + json_object_object_add(json, "vteps", json_vteps); + } else { + vty_out(vty, "%-30s %-6s %-21s %-15s %-6d\n", + esi_to_str(&es->esi, buf, sizeof(buf)), + is_es_local(es) ? "Local" : "Remote", + prefix_rd2str(&es->prd, buf1, sizeof(buf1)), + ipaddr2str(&es->originator_ip, buf2, + sizeof(buf2)), + es->vtep_list ? listcount(es->vtep_list) : 0); + } +} + static void show_vni_entry(struct hash_backet *backet, void *args[]) { struct vty *vty; @@ -1969,6 +2132,23 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, } } +/* Disaplay EVPN routes for a ESI - VTY handler */ +static void evpn_show_routes_esi(struct vty *vty, struct bgp *bgp, + esi_t *esi, json_object *json) +{ + struct evpnes *es = NULL; + + /* locate the ES */ + es = bgp_evpn_lookup_es(bgp, esi); + if (!es) { + if (!json) + vty_out(vty, "ESI not found\n"); + return; + } + + show_esi_routes(bgp, es, vty, json); +} + /* * Display EVPN routes for a VNI - vty handler. * If 'type' is non-zero, only routes matching that type are shown. @@ -2318,6 +2498,42 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, } } +/* Display specific ES */ +static void evpn_show_es(struct vty *vty, struct bgp *bgp, esi_t *esi, + json_object *json) +{ + struct evpnes *es = NULL; + + es = bgp_evpn_lookup_es(bgp, esi); + if (es) { + display_es(vty, es, json); + } else { + if (json) { + vty_out(vty, "{}\n"); + } else { + vty_out(vty, "ESI not found\n"); + return; + } + } +} + +/* Display all ESs */ +static void evpn_show_all_es(struct vty *vty, struct bgp *bgp, json_object *json) +{ + void *args[2]; + + if (!json) + vty_out(vty, "%-30s %-6s %-21s %-15s %-6s\n", + "ESI", "Type", "RD", "Originator-IP", "#VTEPs"); + + /* print all ESs */ + args[0] = vty; + args[1] = json; + hash_iterate(bgp->esihash, + (void (*)(struct hash_backet *, void *))show_es_entry, + args); +} + /* * Display specified VNI (vty handler) */ @@ -3027,6 +3243,58 @@ DEFUN(show_bgp_l2vpn_evpn_vni, return CMD_SUCCESS; } +/* Disaply ES */ +DEFUN(show_bgp_l2vpn_evpn_es, + show_bgp_l2vpn_evpn_es_cmd, + "show bgp l2vpn evpn es [ESI] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "ethernet-Segment\n" + "Ethernet-Segment Identifier\n") +{ + int idx = 0; + uint8_t uj = 0; + esi_t esi = {0}; + json_object *json = NULL; + struct bgp *bgp = NULL; + + uj = use_json(argc, argv); + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + if ((uj && argc == ((idx + 1) + 2)) || + (!uj && argc == (idx + 1) + 1)) { + + /* show all ESs */ + evpn_show_all_es(vty, bgp, json); + } else { + + /* show a specific ES */ + + /* get the ESI - ESI-ID is at argv[5] */ + if (!str_to_esi(argv[idx + 2]->arg, &esi)) { + vty_out(vty, "%% Malformed ESI\n"); + return CMD_WARNING; + } + evpn_show_es(vty, bgp, &esi, 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 neighbor summary. */ @@ -3056,7 +3324,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary, */ DEFUN(show_bgp_l2vpn_evpn_route, show_bgp_l2vpn_evpn_route_cmd, - "show bgp l2vpn evpn route [type <macip|multicast|prefix>] [json]", + "show bgp l2vpn evpn route [type <macip|multicast|es|prefix>] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR @@ -3065,7 +3333,8 @@ DEFUN(show_bgp_l2vpn_evpn_route, "Specify Route type\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n" - "Prefix route\n" + "Ethernet Segment (type-4) route \n" + "Prefix (type-5 )route\n" JSON_STR) { struct bgp *bgp; @@ -3090,6 +3359,8 @@ DEFUN(show_bgp_l2vpn_evpn_route, type = BGP_EVPN_MAC_IP_ROUTE; else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; + else if (strncmp(argv[type_idx + 1]->arg, "es", 2) == 0) + type = BGP_EVPN_ES_ROUTE; else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0) type = BGP_EVPN_IP_PREFIX_ROUTE; else @@ -3111,7 +3382,7 @@ DEFUN(show_bgp_l2vpn_evpn_route, */ DEFUN(show_bgp_l2vpn_evpn_route_rd, show_bgp_l2vpn_evpn_route_rd_cmd, - "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast|prefix>] [json]", + "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast|es|prefix>] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR @@ -3122,6 +3393,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, "Specify Route type\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n" + "Ethernet Segment route\n" "Prefix route\n" JSON_STR) { @@ -3255,6 +3527,50 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, return CMD_SUCCESS; } +/* Display per ESI routing table */ +DEFUN(show_bgp_l2vpn_evpn_route_esi, + show_bgp_l2vpn_evpn_route_esi_cmd, + "show bgp l2vpn evpn route esi ESI [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "Ethernet Segment Identifier\n" + "ESI ID\n" + JSON_STR) +{ + int uj = 0; + esi_t esi = {0}; + struct bgp *bgp = NULL; + json_object *json = NULL; + + bgp = bgp_get_default(); + if (!bgp) + return CMD_WARNING; + + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + + /* get the ESI - ESI-ID is at argv[6] */ + if (!str_to_esi(argv[6]->arg, &esi)) { + vty_out(vty, "%% Malformed ESI\n"); + return CMD_WARNING; + } + + evpn_show_routes_esi(vty, bgp, &esi, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + + /* * Display per-VNI EVPN routing table. */ @@ -3583,6 +3899,78 @@ DEFUN(show_bgp_l2vpn_evpn_import_rt, } #if defined(HAVE_CUMULUS) +DEFUN(test_adv_evpn_type4_route, + test_adv_evpn_type4_route_cmd, + "advertise es ESI", + "Advertise EVPN ES route\n" + "Ethernet-segment\n" + "Ethernet-Segment Identifier\n") +{ + int ret = 0; + esi_t esi; + struct bgp *bgp; + struct ipaddr vtep_ip; + + bgp = bgp_get_default(); + if (!bgp) { + vty_out(vty, "%%Default BGP instance not yet created\n"); + return CMD_WARNING; + } + + if(!str_to_esi(argv[2]->arg, &esi)) { + vty_out(vty, "%%Malformed ESI\n"); + return CMD_WARNING; + } + + vtep_ip.ipa_type = IPADDR_V4; + vtep_ip.ipaddr_v4 = bgp->router_id; + + ret = bgp_evpn_local_es_add(bgp, &esi, &vtep_ip); + if (ret == -1) { + vty_out(vty, "%%Failed to EVPN advertise type-4 route\n"); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN(test_withdraw_evpn_type4_route, + test_withdraw_evpn_type4_route_cmd, + "withdraw es ESI", + "Advertise EVPN ES route\n" + "Ethernet-segment\n" + "Ethernet-Segment Identifier\n") +{ + int ret = 0; + esi_t esi; + struct bgp *bgp; + struct ipaddr vtep_ip; + + bgp = bgp_get_default(); + if (!bgp) { + vty_out(vty, "%%Default BGP instance not yet created\n"); + return CMD_WARNING; + } + + if (!bgp->peer_self) { + vty_out(vty, "%%BGP instance doesnt have self peer\n"); + return CMD_WARNING; + } + + if(!str_to_esi(argv[2]->arg, &esi)) { + vty_out(vty, "%%Malformed ESI\n"); + return CMD_WARNING; + } + + vtep_ip.ipa_type = IPADDR_V4; + vtep_ip.ipaddr_v4 = bgp->router_id; + ret = bgp_evpn_local_es_del(bgp, &esi, &vtep_ip); + if (ret == -1) { + vty_out(vty, "%%Failed to withdraw EVPN type-4 route\n"); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + ALIAS_HIDDEN(show_bgp_l2vpn_evpn_vni, show_bgp_evpn_vni_cmd, "show bgp evpn vni [(1-16777215)]", SHOW_STR BGP_STR EVPN_HELP_STR "Show VNI\n" @@ -4543,12 +4931,18 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &bgp_evpn_default_originate_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_default_originate_cmd); + /* test commands */ + install_element(BGP_EVPN_NODE, &test_adv_evpn_type4_route_cmd); + install_element(BGP_EVPN_NODE, &test_withdraw_evpn_type4_route_cmd); + /* "show bgp l2vpn evpn" commands. */ + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); 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_esi_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 4669fad3b..3e4dfb11a 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -116,6 +116,8 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string") DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value") DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information") +DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP Ip") +DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI 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 6fa3040a1..03715f562 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -111,6 +111,9 @@ DECLARE_MTYPE(LCOMMUNITY) DECLARE_MTYPE(LCOMMUNITY_STR) DECLARE_MTYPE(LCOMMUNITY_VAL) +DECLARE_MTYPE(BGP_EVPN_ES) +DECLARE_MTYPE(BGP_EVPN_ES_VTEP) + DECLARE_MTYPE(BGP_EVPN) DECLARE_MTYPE(BGP_EVPN_IMPORT_RT) DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 132fa5fc9..ff103bb77 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2107,12 +2107,10 @@ int bgp_zebra_has_route_changed(struct bgp_node *rn, struct bgp_info *selected) struct bgp_info *mpinfo; /* If this is multipath, check all selected paths for any nexthop change - * or - * attribute change. Some attribute changes (e.g., community) aren't of + * or attribute change. Some attribute changes (e.g., community) aren't of * relevance to the RIB, but we'll update zebra to ensure we handle the * case of BGP nexthop change. This is the behavior when the best path - * has - * an attribute change anyway. + * has an attribute change anyway. */ if (CHECK_FLAG(selected->flags, BGP_INFO_IGP_CHANGED) || CHECK_FLAG(selected->flags, BGP_INFO_MULTIPATH_CHG)) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 7a6b80f3a..7b6f3b2e4 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2249,6 +2249,40 @@ static void bgp_zebra_connected(struct zclient *zclient) */ } +static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + esi_t esi; + struct bgp *bgp = NULL; + struct stream *s = NULL; + char buf[ESI_STR_LEN]; + char buf1[INET6_ADDRSTRLEN]; + struct ipaddr originator_ip; + + memset(&esi, 0, sizeof(esi_t)); + memset(&originator_ip, 0, sizeof(struct ipaddr)); + + bgp = bgp_lookup_by_vrf_id(vrf_id); + if (!bgp) + return 0; + + s = zclient->ibuf; + stream_get(&esi, s, sizeof(esi_t)); + stream_get(&originator_ip, s, sizeof(struct ipaddr)); + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Rx %s ESI %s originator-ip %s", + (cmd == ZEBRA_LOCAL_ES_ADD) ? "add" : "del", + esi_to_str(&esi, buf, sizeof(buf)), + ipaddr2str(&originator_ip, buf1, sizeof(buf1))); + + if (cmd == ZEBRA_LOCAL_ES_ADD) + bgp_evpn_local_es_add(bgp, &esi, &originator_ip); + else + bgp_evpn_local_es_del(bgp, &esi, &originator_ip); + return 0; +} + static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { @@ -2484,6 +2518,8 @@ void bgp_zebra_init(struct thread_master *master) zclient->nexthop_update = bgp_read_nexthop_update; zclient->import_check_update = bgp_read_import_check_update; zclient->fec_update = bgp_read_fec_update; + zclient->local_es_add = bgp_zebra_process_local_es; + zclient->local_es_del = bgp_zebra_process_local_es; zclient->local_vni_add = bgp_zebra_process_local_vni; zclient->local_vni_del = bgp_zebra_process_local_vni; zclient->local_macip_add = bgp_zebra_process_local_macip; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index f663df162..4c87ac0b6 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -534,6 +534,9 @@ struct bgp { struct bgp_pbr_config *bgp_pbr_cfg; + /* local esi hash table */ + struct hash *esihash; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp) @@ -945,6 +945,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_ADVERTISE_ALL_VNI), DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW), DESC_ENTRY(ZEBRA_ADVERTISE_SUBNET), + DESC_ENTRY(ZEBRA_LOCAL_ES_ADD), + DESC_ENTRY(ZEBRA_LOCAL_ES_DEL), DESC_ENTRY(ZEBRA_VNI_ADD), DESC_ENTRY(ZEBRA_VNI_DEL), DESC_ENTRY(ZEBRA_L3VNI_ADD), diff --git a/lib/prefix.c b/lib/prefix.c index 05af190e9..303b12e3a 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -429,6 +429,14 @@ static const struct in6_addr maskbytes6[] = { #define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) +void prefix_hexdump(const struct prefix *p) +{ + char buf[PREFIX_STRLEN]; + zlog_debug("prefix: %s", + prefix2str(p, buf, sizeof(buf))); + zlog_hexdump(p, sizeof(struct prefix)); +} + int is_zero_mac(struct ethaddr *mac) { int i = 0; @@ -1262,7 +1270,12 @@ static const char *prefixevpn_imet2str(const struct prefix_evpn *p, char *str, static const char *prefixevpn_es2str(const struct prefix_evpn *p, char *str, int size) { - snprintf(str, size, "Unsupported EVPN prefix"); + char buf[ESI_STR_LEN]; + + snprintf(str, size, "[%d]:[%s]:[%s]/%d", p->prefix.route_type, + esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)), + inet_ntoa(p->prefix.es_addr.ip.ipaddr_v4), + p->prefixlen); return str; } @@ -1540,3 +1553,56 @@ unsigned prefix_hash_key(void *pp) offsetof(struct prefix, u.prefix) + PSIZE(copy.prefixlen), 0x55aa5a5a); } + +/* converts to internal representation of esi + * returns 1 on success, 0 otherwise + * format accepted: aa:aa:aa:aa:aa:aa:aa:aa:aa:aa + * if esi parameter is null, then check only + */ +int str_to_esi(const char *str, esi_t *esi) +{ + int i; + unsigned int a[ESI_BYTES]; + + if (!str) + return 0; + + if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x", + a + 0, a + 1, a + 2, a + 3, + a + 4, a + 5, a + 6, a + 7, + a + 8, a + 9) + != ESI_BYTES) { + /* error in incoming str length */ + return 0; + } + + /* valid ESI */ + if (!esi) + return 1; + for (i = 0; i < ESI_BYTES; ++i) + esi->val[i] = a[i] & 0xff; + return 1; +} + +char *esi_to_str(const esi_t *esi, char *buf, int size) +{ + char *ptr; + + if (!esi) + return NULL; + if (!buf) + ptr = (char *)XMALLOC(MTYPE_TMP, + ESI_STR_LEN * sizeof(char)); + else { + assert(size >= ESI_STR_LEN); + ptr = buf; + } + + snprintf(ptr, ESI_STR_LEN, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + esi->val[0], esi->val[1], esi->val[2], + esi->val[3], esi->val[4], esi->val[5], + esi->val[6], esi->val[7], esi->val[8], + esi->val[9]); + return ptr; +} diff --git a/lib/prefix.h b/lib/prefix.h index ab3c05ae7..fadaab185 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -39,6 +39,9 @@ #define ETH_ALEN 6 #endif +#define ESI_BYTES 10 +#define ESI_STR_LEN (3* ESI_BYTES) + #define ETHER_ADDR_STRLEN (3*ETH_ALEN) /* * there isn't a portable ethernet address type. We define our @@ -213,6 +216,8 @@ static inline int is_evpn_prefix_ipaddr_none(const struct prefix_evpn *evp) return IS_IPADDR_NONE(&(evp)->prefix.macip_addr.ip); if (evp->prefix.route_type == 3) return IS_IPADDR_NONE(&(evp)->prefix.imet_addr.ip); + if (evp->prefix.route_type == 4) + return IS_IPADDR_NONE(&(evp)->prefix.es_addr.ip); if (evp->prefix.route_type == 5) return IS_IPADDR_NONE(&(evp)->prefix.prefix_addr.ip); return 0; @@ -224,6 +229,8 @@ static inline int is_evpn_prefix_ipaddr_v4(const struct prefix_evpn *evp) return IS_IPADDR_V4(&(evp)->prefix.macip_addr.ip); if (evp->prefix.route_type == 3) return IS_IPADDR_V4(&(evp)->prefix.imet_addr.ip); + if (evp->prefix.route_type == 4) + return IS_IPADDR_V4(&(evp)->prefix.es_addr.ip); if (evp->prefix.route_type == 5) return IS_IPADDR_V4(&(evp)->prefix.prefix_addr.ip); return 0; @@ -235,6 +242,8 @@ static inline int is_evpn_prefix_ipaddr_v6(const struct prefix_evpn *evp) return IS_IPADDR_V6(&(evp)->prefix.macip_addr.ip); if (evp->prefix.route_type == 3) return IS_IPADDR_V6(&(evp)->prefix.imet_addr.ip); + if (evp->prefix.route_type == 4) + return IS_IPADDR_V6(&(evp)->prefix.es_addr.ip); if (evp->prefix.route_type == 5) return IS_IPADDR_V6(&(evp)->prefix.prefix_addr.ip); return 0; @@ -432,6 +441,11 @@ extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); extern unsigned prefix_hash_key(void *pp); +extern int str_to_esi(const char *str, esi_t *esi); +extern char *esi_to_str(const esi_t *esi, char *buf, int size); +extern void prefix_hexdump(const struct prefix *p); +extern void prefix_evpn_hexdump(const struct prefix_evpn *p); + static inline int ipv6_martian(struct in6_addr *addr) { struct in6_addr localhost_addr; @@ -482,5 +496,4 @@ static inline int is_host_route(struct prefix *p) return (p->prefixlen == IPV6_MAX_BITLEN); return 0; } - #endif /* _ZEBRA_PREFIX_H */ diff --git a/lib/zclient.c b/lib/zclient.c index 0f7cf350d..38a9e6c78 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2760,6 +2760,16 @@ static int zclient_read(struct thread *thread) if (zclient->fec_update) (*zclient->fec_update)(command, zclient, length); break; + case ZEBRA_LOCAL_ES_ADD: + if (zclient->local_es_add) + (*zclient->local_es_add)(command, zclient, length, + vrf_id); + break; + case ZEBRA_LOCAL_ES_DEL: + if (zclient->local_es_del) + (*zclient->local_es_del)(command, zclient, length, + vrf_id); + break; case ZEBRA_VNI_ADD: if (zclient->local_vni_add) (*zclient->local_vni_add)(command, zclient, length, diff --git a/lib/zclient.h b/lib/zclient.h index 2ec03acc4..ceb4dc74f 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -124,6 +124,8 @@ typedef enum { ZEBRA_ADVERTISE_DEFAULT_GW, ZEBRA_ADVERTISE_SUBNET, ZEBRA_ADVERTISE_ALL_VNI, + ZEBRA_LOCAL_ES_ADD, + ZEBRA_LOCAL_ES_DEL, ZEBRA_VNI_ADD, ZEBRA_VNI_DEL, ZEBRA_L3VNI_ADD, @@ -237,6 +239,8 @@ struct zclient { int (*redistribute_route_del)(int, struct zclient *, uint16_t, vrf_id_t); int (*fec_update)(int, struct zclient *, uint16_t); + int (*local_es_add)(int, struct zclient *, uint16_t, vrf_id_t); + int (*local_es_del)(int, struct zclient *, uint16_t, vrf_id_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); |