From 185fb14a4138c0cd2ce98711d31205c657893c2f Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Fri, 27 Mar 2020 06:39:51 -0700 Subject: [PATCH] bgpd: pull the multihoming code out to a separate file Re-org only; no other code changes. This is being done to make maintanence of MH functionality (which will have more code added to it) easy. The code moved here was originally committed via - 'commit 50f74cf13105 ("*: support for evpn type-4 route")' Signed-off-by: Anuradha Karuppiah --- bgpd/bgp_evpn.c | 988 +--------------------------------------- bgpd/bgp_evpn.h | 4 - bgpd/bgp_evpn_mh.c | 970 +++++++++++++++++++++++++++++++++++++++ bgpd/bgp_evpn_mh.h | 40 ++ bgpd/bgp_evpn_private.h | 6 +- bgpd/bgp_evpn_vty.c | 1 + bgpd/bgp_zebra.c | 1 + bgpd/subdir.am | 3 + 8 files changed, 1042 insertions(+), 971 deletions(-) create mode 100644 bgpd/bgp_evpn_mh.c create mode 100644 bgpd/bgp_evpn_mh.h diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 8c5d6421f1..7909a6feee 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -40,6 +40,7 @@ #include "bgpd/bgp_label.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_encap_types.h" #include "bgpd/bgp_debug.h" @@ -63,52 +64,12 @@ DEFINE_QOBJ_TYPE(evpnes) /* * Static function declarations */ -static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, - struct bgp_dest *dest, - struct bgp_path_info **pi); 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(void *p1, void *p2) -{ - const struct in_addr *ip1 = p1; - const struct in_addr *ip2 = p2; - - return ip1->s_addr - ip2->s_addr; -} - -/* - * Make hash key for ESI. - */ -static unsigned int esi_hash_keymake(const void *p) -{ - const struct evpnes *pes = p; - const void *pnt = (void *)pes->esi.val; - - return jhash(pnt, ESI_BYTES, 0xa5a5a55a); -} - -/* - * Compare two ESIs. - */ -static bool esi_cmp(const void *p1, const void *p2) -{ - const struct evpnes *pes1 = p1; - const struct evpnes *pes2 = p2; - - if (pes1 == NULL && pes2 == NULL) - return true; - - if (pes1 == NULL || pes2 == NULL) - return false; - - return (memcmp(pes1->esi.val, pes2->esi.val, ESI_BYTES) == 0); -} - /* * Make vni hash key. */ @@ -697,40 +658,6 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, return zclient_send_message(zclient); } -/* - * 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. */ @@ -1020,167 +947,6 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn, bgp_path_info_delete(dest, 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)); - - 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, - const 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_dest *dest) -{ - int ret = 0; - afi_t afi = AFI_L2VPN; - safi_t safi = SAFI_EVPN; - struct bgp_path_info *old_select; /* old best */ - struct bgp_path_info *new_select; /* new best */ - struct bgp_path_info_pair old_and_new; - - /* Compute the best path. */ - bgp_best_selection(bgp, dest, &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(dest->flags, BGP_NODE_USER_CLEAR) - && !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) - && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { - if (bgp_zebra_has_route_changed(old_select)) { - ret = evpn_es_install_vtep( - bgp, es, - (const struct prefix_evpn *)bgp_dest_get_prefix( - dest), - old_select->attr->nexthop); - } - UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); - UNSET_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG); - bgp_zebra_clear_route_change_flags(dest); - return ret; - } - - /* If the user did a "clear" this flag will be set */ - UNSET_FLAG(dest->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(dest); - - if (old_select) - bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED); - if (new_select) { - bgp_path_info_set_flag(dest, new_select, BGP_PATH_SELECTED); - bgp_path_info_unset_flag(dest, new_select, - BGP_PATH_ATTR_CHANGED); - UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG); - UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG); - } - - if (new_select && new_select->type == ZEBRA_ROUTE_BGP - && new_select->sub_type == BGP_ROUTE_IMPORTED) { - ret = evpn_es_install_vtep( - bgp, es, - (const struct prefix_evpn *)bgp_dest_get_prefix(dest), - 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 *)bgp_dest_get_prefix(dest), - old_select->attr->nexthop); - } - - /* Clear any route change flags. */ - bgp_zebra_clear_route_change_flags(dest); - - /* Reap old select bgp_path_info, if it has been removed */ - if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED)) - bgp_path_info_reap(dest, old_select); - - return ret; -} - /* * Calculate the best path for an EVPN route. Install/update best path in zebra, * if appropriate. @@ -1326,177 +1092,6 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_dest *dest) return local_pi->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_dest *dest, - struct attr *attr, int add, - struct bgp_path_info **ri, - int *route_changed) -{ - char buf[ESI_STR_LEN]; - char buf1[INET6_ADDRSTRLEN]; - struct bgp_path_info *tmp_pi = NULL; - struct bgp_path_info *local_pi = NULL; /* local route entry if any */ - struct bgp_path_info *remote_pi = NULL; /* remote route entry if any */ - struct attr *attr_new = NULL; - const struct prefix_evpn *evp = NULL; - - *ri = NULL; - *route_changed = 1; - evp = (const struct prefix_evpn *)bgp_dest_get_prefix(dest); - - /* locate the local and remote entries if any */ - for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi; - tmp_pi = tmp_pi->next) { - if (tmp_pi->peer == bgp->peer_self - && tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_STATIC) - local_pi = tmp_pi; - if (tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_IMPORTED - && CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID)) - remote_pi = tmp_pi; - } - - /* 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_pi) { - flog_err( - EC_BGP_ES_INVALID, - "%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_pi && !add) - return 0; - - /* create or update the entry */ - if (!local_pi) { - - /* Add or update attribute to hash */ - attr_new = bgp_attr_intern(attr); - - /* Create new route with its attribute. */ - tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, - bgp->peer_self, attr_new, dest); - SET_FLAG(tmp_pi->flags, BGP_PATH_VALID); - - /* add the newly created path to the route-node */ - bgp_path_info_add(dest, tmp_pi); - } else { - tmp_pi = local_pi; - if (attrhash_cmp(tmp_pi->attr, attr) - && !CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) - *route_changed = 0; - else { - /* The attribute has changed. - * Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(attr); - bgp_path_info_set_flag(dest, tmp_pi, - BGP_PATH_ATTR_CHANGED); - - /* Restore route, if needed. */ - if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(dest, tmp_pi); - - /* Unintern existing, set to new. */ - bgp_attr_unintern(&tmp_pi->attr); - tmp_pi->attr = attr_new; - tmp_pi->uptime = bgp_clock(); - } - } - - /* Return back the route entry. */ - *ri = tmp_pi; - 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_dest *dest = NULL; - struct bgp_path_info *pi = 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. */ - dest = bgp_node_get(es->route_table, (struct prefix *)p); - - /* Create or update route entry. */ - ret = update_evpn_type4_route_entry(bgp, es, afi, safi, dest, &attr, 1, - &pi, &route_changed); - if (ret != 0) { - flog_err(EC_BGP_ES_INVALID, - "%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(pi); - attr_new = pi->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, dest); - bgp_dest_unlock_node(dest); - - /* 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_path_info *global_pi; - - dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, - (struct prefix *)p, &es->prd); - update_evpn_type4_route_entry(bgp, es, afi, safi, dest, - attr_new, 1, &global_pi, - &route_changed); - - /* Schedule for processing and unlock node. */ - bgp_process(bgp, dest, afi, safi); - bgp_dest_unlock_node(dest); - } - - /* Unintern temporary. */ - aspath_unintern(&attr.aspath); - return 0; -} - static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, struct bgp *bgp_vrf, afi_t afi, safi_t safi, struct bgp_dest *dest, @@ -1987,7 +1582,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, * 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, +void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_dest *dest, struct bgp_path_info **pi) { @@ -2010,56 +1605,6 @@ static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, bgp_path_info_delete(dest, tmp_pi); } - - -/* 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_path_info *pi; - struct bgp_dest *dest = NULL; /* dest in esi table */ - struct bgp_dest *global_dest = NULL; /* dest 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. - */ - dest = bgp_node_lookup(es->route_table, (struct prefix *)p); - if (!dest) - 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_dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)p, &es->prd); - if (global_dest) { - - /* Delete route entry in the global EVPN table. */ - delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); - - /* Schedule for processing - withdraws to peers happen from - * this table. - */ - if (pi) - bgp_process(bgp, global_dest, afi, safi); - bgp_dest_unlock_node(global_dest); - } - - /* - * Delete route entry in the ESI route table. - * This can just be removed. - */ - delete_evpn_route_entry(bgp, afi, safi, dest, &pi); - if (pi) - bgp_path_info_reap(dest, pi); - bgp_dest_unlock_node(dest); - return 0; -} - /* Delete EVPN type5 route */ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) { @@ -2355,27 +1900,6 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) return 0; } -/* - * Delete all routes in per ES route-table - */ -static int delete_all_es_routes(struct bgp *bgp, struct evpnes *es) -{ - struct bgp_dest *dest; - struct bgp_path_info *pi, *nextpi; - - /* Walk this ES's route table and delete all routes. */ - for (dest = bgp_table_top(es->route_table); dest; - dest = bgp_route_next(dest)) { - for (pi = bgp_dest_get_bgp_path_info(dest); - (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { - bgp_path_info_delete(dest, pi); - bgp_path_info_reap(dest, pi); - } - } - - return 0; -} - /* * Delete all routes in the per-VNI route table. */ @@ -2442,29 +1966,6 @@ 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) { - flog_err(EC_BGP_EVPN_ROUTE_DELETE, - "%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 @@ -2545,95 +2046,33 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, /* Update the tunnel IP and re-advertise all routes for this VNI. */ vpn->originator_ip = originator_ip; return 0; -} - -static struct bgp_path_info * -bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi, - struct bgp_dest *dest, struct attr *attr) -{ - struct attr *attr_new; - struct bgp_path_info *pi; - - /* Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(attr); - - /* Create new route with its attribute. */ - pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, parent_pi->peer, - attr_new, dest); - SET_FLAG(pi->flags, BGP_PATH_VALID); - bgp_path_info_extra_get(pi); - pi->extra->parent = bgp_path_info_lock(parent_pi); - bgp_dest_lock_node((struct bgp_dest *)parent_pi->net); - if (parent_pi->extra) { - memcpy(&pi->extra->label, &parent_pi->extra->label, - sizeof(pi->extra->label)); - pi->extra->num_labels = parent_pi->extra->num_labels; - } - bgp_path_info_add(dest, pi); - - return pi; -} - -/* Install EVPN route entry in ES */ -static int install_evpn_route_entry_in_es(struct bgp *bgp, struct evpnes *es, - const struct prefix_evpn *p, - struct bgp_path_info *parent_pi) -{ - int ret = 0; - struct bgp_dest *dest = NULL; - struct bgp_path_info *pi = NULL; - struct attr *attr_new = NULL; - - /* Create (or fetch) route within the VNI. - * NOTE: There is no RD here. - */ - dest = bgp_node_get(es->route_table, (struct prefix *)p); - - /* Check if route entry is already present. */ - for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) - if (pi->extra - && (struct bgp_path_info *)pi->extra->parent == parent_pi) - break; - - if (!pi) { - /* Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(parent_pi->attr); - - /* Create new route with its attribute. */ - pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, - parent_pi->peer, attr_new, dest); - SET_FLAG(pi->flags, BGP_PATH_VALID); - bgp_path_info_extra_get(pi); - pi->extra->parent = bgp_path_info_lock(parent_pi); - bgp_dest_lock_node((struct bgp_dest *)parent_pi->net); - bgp_path_info_add(dest, pi); - } else { - if (attrhash_cmp(pi->attr, parent_pi->attr) - && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { - bgp_dest_unlock_node(dest); - return 0; - } - /* The attribute has changed. */ - /* Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(parent_pi->attr); +} - /* Restore route, if needed. */ - if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(dest, pi); +static struct bgp_path_info * +bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi, + struct bgp_dest *dest, struct attr *attr) +{ + struct attr *attr_new; + struct bgp_path_info *pi; - /* Mark if nexthop has changed. */ - if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop)) - SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(attr); - /* Unintern existing, set to new. */ - bgp_attr_unintern(&pi->attr); - pi->attr = attr_new; - pi->uptime = bgp_clock(); + /* Create new route with its attribute. */ + pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, parent_pi->peer, + attr_new, dest); + SET_FLAG(pi->flags, BGP_PATH_VALID); + bgp_path_info_extra_get(pi); + pi->extra->parent = bgp_path_info_lock(parent_pi); + bgp_dest_lock_node((struct bgp_dest *)parent_pi->net); + if (parent_pi->extra) { + memcpy(&pi->extra->label, &parent_pi->extra->label, + sizeof(pi->extra->label)); + pi->extra->num_labels = parent_pi->extra->num_labels; } + bgp_path_info_add(dest, pi); - /* Perform route selection and update zebra, if required. */ - ret = evpn_es_route_select_install(bgp, es, dest); - return ret; + return pi; } /* @@ -2810,46 +2249,6 @@ 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, - const struct prefix_evpn *p, - struct bgp_path_info *parent_pi) -{ - int ret; - struct bgp_dest *dest; - struct bgp_path_info *pi; - - if (!es->route_table) - return 0; - - /* Locate route within the ESI. - * NOTE: There is no RD here. - */ - dest = bgp_node_lookup(es->route_table, (struct prefix *)p); - if (!dest) - return 0; - - /* Find matching route entry. */ - for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) - if (pi->extra - && (struct bgp_path_info *)pi->extra->parent == parent_pi) - break; - - if (!pi) - return 0; - - /* Mark entry for deletion */ - bgp_path_info_delete(dest, pi); - - /* Perform route selection and update zebra, if required. */ - ret = evpn_es_route_select_install(bgp, es, dest); - - /* Unlock route node. */ - bgp_dest_unlock_node(dest); - - return ret; -} - /* * Uninstall route entry from the VRF routing table and send message * to zebra, if appropriate. @@ -2965,22 +2364,6 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, return ret; } -/* - * Given a prefix, see if it belongs to ES. - */ -static int is_prefix_matching_for_es(const 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. @@ -3115,78 +2498,6 @@ 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_dest *rd_dest, *dest; - struct bgp_table *table; - struct bgp_path_info *pi; - - 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_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest; - rd_dest = bgp_route_next(rd_dest)) { - table = bgp_dest_get_bgp_table_info(rd_dest); - if (!table) - continue; - - for (dest = bgp_table_top(table); dest; - dest = bgp_route_next(dest)) { - const struct prefix_evpn *evp = - (const struct prefix_evpn *)bgp_dest_get_prefix( - dest); - - for (pi = bgp_dest_get_bgp_path_info(dest); pi; - pi = pi->next) { - /* - * Consider "valid" remote routes applicable for - * this ES. - */ - if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) - && pi->type == ZEBRA_ROUTE_BGP - && pi->sub_type == BGP_ROUTE_NORMAL)) - continue; - - if (!is_prefix_matching_for_es(evp, es)) - continue; - - if (install) - ret = install_evpn_route_entry_in_es( - bgp, es, evp, pi); - else - ret = uninstall_evpn_route_entry_in_es( - bgp, es, evp, pi); - - if (ret) { - flog_err( - EC_BGP_EVPN_FAIL, - "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; -} - /* This API will scan evpn routes for checking attribute's rmac * macthes with bgp instance router mac. It avoid installing * route into bgp vrf table and remote rmac in bridge table. @@ -3390,15 +2701,6 @@ 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 */ @@ -3457,31 +2759,6 @@ 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_path_info *pi, int install) -{ - int ret = 0; - char buf[ESI_STR_LEN]; - - if (install) - ret = install_evpn_route_entry_in_es(bgp, es, evp, pi); - else - ret = uninstall_evpn_route_entry_in_es(bgp, es, evp, pi); - - if (ret) { - flog_err( - EC_BGP_EVPN_FAIL, - "%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). */ @@ -4164,68 +3441,6 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, return ret; } -/* - * 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) { - flog_err(EC_BGP_EVPN_ROUTE_INVALID, - "%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 { - flog_err( - EC_BGP_EVPN_ROUTE_INVALID, - "%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). */ @@ -5530,79 +4745,6 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) XFREE(MTYPE_BGP_EVPN, vpn); } -/* - * 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)); - - /* 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 = 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; - snprintf(buf, sizeof(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(&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. */ @@ -6136,88 +5278,6 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, return 0; } -/* - * 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) { - flog_err(EC_BGP_ES_CREATE, "%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) { - flog_warn(EC_BGP_EVPN_ESI, - "%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) { - flog_err(EC_BGP_ES_CREATE, "%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) { - flog_err( - EC_BGP_ES_CREATE, - "%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)) { - flog_err(EC_BGP_EVPN_ROUTE_CREATE, - "%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; -} - /* * Handle change in setting for BUM handling. The supported values * are head-end replication and dropping all BUM packets. Any change diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 267c87ee54..cb87f89420 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -188,10 +188,6 @@ extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, struct in_addr mcast_grp); -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_flood_control_change(struct bgp *bgp); extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); extern void bgp_evpn_cleanup(struct bgp *bgp); diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c new file mode 100644 index 0000000000..341b6e8e63 --- /dev/null +++ b/bgpd/bgp_evpn_mh.c @@ -0,0 +1,970 @@ +/* EVPN Multihoming procedures + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRRouting is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRRouting is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include + +#include "command.h" +#include "filter.h" +#include "prefix.h" +#include "log.h" +#include "memory.h" +#include "stream.h" +#include "hash.h" +#include "jhash.h" +#include "zclient.h" + +#include "bgpd/bgp_attr_evpn.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_mh.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_encap_types.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_errors.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_addpath.h" + +/* compare two IPV4 VTEP IPs */ +static int evpn_vtep_ip_cmp(void *p1, void *p2) +{ + const struct in_addr *ip1 = p1; + const struct in_addr *ip2 = p2; + + return ip1->s_addr - ip2->s_addr; +} + +/* + * Make hash key for ESI. + */ +unsigned int esi_hash_keymake(const void *p) +{ + const struct evpnes *pes = p; + const void *pnt = (void *)pes->esi.val; + + return jhash(pnt, ESI_BYTES, 0xa5a5a55a); +} + +/* + * Compare two ESIs. + */ +bool esi_cmp(const void *p1, const void *p2) +{ + const struct evpnes *pes1 = p1; + const struct evpnes *pes2 = p2; + + if (pes1 == NULL && pes2 == NULL) + return true; + + if (pes1 == NULL || pes2 == NULL) + return false; + + return (memcmp(pes1->esi.val, pes2->esi.val, ESI_BYTES) == 0); +} + +/* + * 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); +} + +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)); + + 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_path_info *old_select; /* old best */ + struct bgp_path_info *new_select; /* new best */ + struct bgp_path_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_PATH_ATTR_CHANGED) + && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { + if (bgp_zebra_has_route_changed(old_select)) { + ret = evpn_es_install_vtep(bgp, es, + (struct prefix_evpn *)&rn->p, + old_select->attr->nexthop); + } + UNSET_FLAG(old_select->flags, BGP_PATH_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_path_info_unset_flag(rn, old_select, BGP_PATH_SELECTED); + if (new_select) { + bgp_path_info_set_flag(rn, new_select, BGP_PATH_SELECTED); + bgp_path_info_unset_flag(rn, new_select, BGP_PATH_ATTR_CHANGED); + UNSET_FLAG(new_select->flags, BGP_PATH_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_path_info, if it has been removed */ + if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED)) + bgp_path_info_reap(rn, old_select); + + return ret; +} + +/* + * 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_path_info **ri, + int *route_changed) +{ + char buf[ESI_STR_LEN]; + char buf1[INET6_ADDRSTRLEN]; + struct bgp_path_info *tmp_pi = NULL; + struct bgp_path_info *local_pi = NULL; /* local route entry if any */ + struct bgp_path_info *remote_pi = 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_pi = bgp_dest_get_bgp_path_info(rn); tmp_pi; + tmp_pi = tmp_pi->next) { + if (tmp_pi->peer == bgp->peer_self + && tmp_pi->type == ZEBRA_ROUTE_BGP + && tmp_pi->sub_type == BGP_ROUTE_STATIC) + local_pi = tmp_pi; + if (tmp_pi->type == ZEBRA_ROUTE_BGP + && tmp_pi->sub_type == BGP_ROUTE_IMPORTED + && CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID)) + remote_pi = tmp_pi; + } + + /* 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_pi) { + flog_err( + EC_BGP_ES_INVALID, + "%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_pi && !add) + return 0; + + /* create or update the entry */ + if (!local_pi) { + + /* Add or update attribute to hash */ + attr_new = bgp_attr_intern(attr); + + /* Create new route with its attribute. */ + tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, + bgp->peer_self, attr_new, rn); + SET_FLAG(tmp_pi->flags, BGP_PATH_VALID); + + /* add the newly created path to the route-node */ + bgp_path_info_add(rn, tmp_pi); + } else { + tmp_pi = local_pi; + if (attrhash_cmp(tmp_pi->attr, attr) + && !CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) + *route_changed = 0; + else { + /* The attribute has changed. + * Add (or update) attribute to hash. + */ + attr_new = bgp_attr_intern(attr); + bgp_path_info_set_flag(rn, tmp_pi, + BGP_PATH_ATTR_CHANGED); + + /* Restore route, if needed. */ + if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) + bgp_path_info_restore(rn, tmp_pi); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&tmp_pi->attr); + tmp_pi->attr = attr_new; + tmp_pi->uptime = bgp_clock(); + } + } + + /* Return back the route entry. */ + *ri = tmp_pi; + 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_path_info *pi = 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, + &pi, &route_changed); + if (ret != 0) { + flog_err(EC_BGP_ES_INVALID, + "%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(pi); + attr_new = pi->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_dest_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_path_info *global_pi; + + 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_pi, &route_changed); + + /* Schedule for processing and unlock node. */ + bgp_process(bgp, rn, afi, safi); + bgp_dest_unlock_node(rn); + } + + /* Unintern temporary. */ + aspath_unintern(&attr.aspath); + return 0; +} + +/* 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_path_info *pi; + 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, &pi); + + /* Schedule for processing - withdraws to peers happen from + * this table. + */ + if (pi) + bgp_process(bgp, global_rn, afi, safi); + bgp_dest_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, &pi); + if (pi) + bgp_path_info_reap(rn, pi); + bgp_dest_unlock_node(rn); + return 0; +} + +/* + * 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_path_info *pi, *nextpi; + + /* 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 (pi = bgp_dest_get_bgp_path_info(rn); + (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { + bgp_path_info_delete(rn, pi); + bgp_path_info_reap(rn, pi); + } + } + + return 0; +} + +/* 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) { + flog_err(EC_BGP_EVPN_ROUTE_DELETE, + "%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); +} + +/* 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_path_info *parent_pi) +{ + int ret = 0; + struct bgp_node *rn = NULL; + struct bgp_path_info *pi = 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 (pi = bgp_dest_get_bgp_path_info(rn); pi; pi = pi->next) + if (pi->extra + && (struct bgp_path_info *)pi->extra->parent == parent_pi) + break; + + if (!pi) { + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_pi->attr); + + /* Create new route with its attribute. */ + pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, + parent_pi->peer, attr_new, rn); + SET_FLAG(pi->flags, BGP_PATH_VALID); + bgp_path_info_extra_get(pi); + pi->extra->parent = parent_pi; + bgp_path_info_add(rn, pi); + } else { + if (attrhash_cmp(pi->attr, parent_pi->attr) + && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { + bgp_dest_unlock_node(rn); + return 0; + } + /* The attribute has changed. */ + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_pi->attr); + + /* Restore route, if needed. */ + if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) + bgp_path_info_restore(rn, pi); + + /* Mark if nexthop has changed. */ + if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop)) + SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&pi->attr); + pi->attr = attr_new; + pi->uptime = bgp_clock(); + } + + /* Perform route selection and update zebra, if required. */ + ret = evpn_es_route_select_install(bgp, es, rn); + 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_path_info *parent_pi) +{ + int ret; + struct bgp_node *rn; + struct bgp_path_info *pi; + + 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 (pi = bgp_dest_get_bgp_path_info(rn); pi; pi = pi->next) + if (pi->extra + && (struct bgp_path_info *)pi->extra->parent == parent_pi) + break; + + if (!pi) + return 0; + + /* Mark entry for deletion */ + bgp_path_info_delete(rn, pi); + + /* Perform route selection and update zebra, if required. */ + ret = evpn_es_route_select_install(bgp, es, rn); + + /* Unlock route node. */ + bgp_dest_unlock_node(rn); + + return ret; +} + +/* + * 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; +} + +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_path_info *pi; + + 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 = bgp_dest_get_bgp_table_info(rd_rn); + 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 (pi = bgp_dest_get_bgp_path_info(rn); pi; + pi = pi->next) { + /* + * Consider "valid" remote routes applicable for + * this ES. + */ + if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (!is_prefix_matching_for_es(evp, es)) + continue; + + if (install) + ret = install_evpn_route_entry_in_es( + bgp, es, evp, pi); + else + ret = uninstall_evpn_route_entry_in_es( + bgp, es, evp, pi); + + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "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 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 or unistall route in ES */ +int install_uninstall_route_in_es(struct bgp *bgp, struct evpnes *es, + afi_t afi, safi_t safi, + struct prefix_evpn *evp, + struct bgp_path_info *pi, int install) +{ + int ret = 0; + char buf[ESI_STR_LEN]; + + if (install) + ret = install_evpn_route_entry_in_es(bgp, es, evp, pi); + else + ret = uninstall_evpn_route_entry_in_es(bgp, es, evp, pi); + + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "%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; +} + +/* + * Process received EVPN type-4 route (advertise or withdraw). + */ +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) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%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 { + flog_err( + EC_BGP_EVPN_ROUTE_INVALID, + "%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; +} + +/* + * 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. + */ +static 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)); + + /* 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 = 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. + */ +static void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es) +{ + list_delete(&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); +} + +/* + * 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) { + flog_err(EC_BGP_ES_CREATE, "%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) { + flog_warn(EC_BGP_EVPN_ESI, + "%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) { + flog_err(EC_BGP_ES_CREATE, "%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) { + flog_err( + EC_BGP_ES_CREATE, + "%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)) { + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%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; +} + diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h new file mode 100644 index 0000000000..868fe749f4 --- /dev/null +++ b/bgpd/bgp_evpn_mh.h @@ -0,0 +1,40 @@ +/* EVPN header for multihoming procedures + * + * Copyright (C) 2019 Cumulus Networks + * + * This file is part of FRRouting. + * + * FRRouting is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRRouting is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef _FRR_BGP_EVPN_MH_H +#define _FRR_BGP_EVPN_MH_H + +#include "vxlan.h" +#include "bgpd.h" +#include "bgp_evpn.h" +#include "bgp_evpn_private.h" + +extern unsigned int esi_hash_keymake(const void *p); +extern bool esi_cmp(const void *p1, const void *p2); +extern int install_uninstall_route_in_es(struct bgp *bgp, struct evpnes *es, + afi_t afi, safi_t safi, + struct prefix_evpn *evp, + struct bgp_path_info *pi, int install); +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); +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); +#endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index c7ccf69f05..839785d3a0 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -564,9 +564,9 @@ extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, struct in_addr mcast_grp); 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); extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni); extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn); +extern void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, + struct bgp_dest *dest, + struct bgp_path_info **pi); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 2584939378..3e8c03783a 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -33,6 +33,7 @@ #include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_errors.h" diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index a469fb507f..80645b0310 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -56,6 +56,7 @@ #include "bgpd/rfapi/vnc_export_bgp.h" #endif #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_labelpool.h" #include "bgpd/bgp_pbr.h" diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 6b5c0fe719..a5393e25ac 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -15,6 +15,7 @@ vtysh_scan += \ bgpd/bgp_bfd.c \ bgpd/bgp_debug.c \ bgpd/bgp_dump.c \ + bgpd/bgp_evpn_mh.c \ bgpd/bgp_evpn_vty.c \ bgpd/bgp_filter.c \ bgpd/bgp_mplsvpn.c \ @@ -65,6 +66,7 @@ bgpd_libbgp_a_SOURCES = \ bgpd/bgp_encap_tlv.c \ bgpd/bgp_errors.c \ bgpd/bgp_evpn.c \ + bgpd/bgp_evpn_mh.c \ bgpd/bgp_evpn_vty.c \ bgpd/bgp_filter.c \ bgpd/bgp_flowspec.c \ @@ -139,6 +141,7 @@ noinst_HEADERS += \ bgpd/bgp_encap_types.h \ bgpd/bgp_errors.h \ bgpd/bgp_evpn.h \ + bgpd/bgp_evpn_mh.h \ bgpd/bgp_evpn_private.h \ bgpd/bgp_evpn_vty.h \ bgpd/bgp_filter.h \ -- 2.39.5