From 10ebe1ab5417af8760a05fb2edd3306847f7e4ba Mon Sep 17 00:00:00 2001 From: Mitesh Kanjariya Date: Mon, 9 Oct 2017 18:12:05 -0700 Subject: [PATCH] bgpd: import rt to vrf mapping Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn.c | 281 +++++++++++++++++++++++++++++++++++++++- bgpd/bgp_evpn.h | 1 + bgpd/bgp_evpn_private.h | 15 +++ bgpd/bgp_evpn_vty.c | 167 ++++++++++++++++++++++++ bgpd/bgp_memory.c | 1 + bgpd/bgp_memory.h | 1 + bgpd/bgpd.c | 3 + bgpd/bgpd.h | 3 + 8 files changed, 466 insertions(+), 6 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 64b2501705..326748bb47 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -92,6 +92,131 @@ static int vni_hash_cmp(const void *p1, const void *p2) return (vpn1->vni == vpn2->vni); } +/* + * Make vrf import route target hash key. + */ +static unsigned int vrf_import_rt_hash_key_make(void *p) +{ + struct vrf_irt_node *irt = p; + char *pnt = irt->rt.val; + unsigned int key = 0; + int c = 0; + + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + key += pnt[c + 4]; + key += pnt[c + 5]; + key += pnt[c + 6]; + key += pnt[c + 7]; + + return key; +} + +/* + * Comparison function for vrf import rt hash + */ +static int vrf_import_rt_hash_cmp(const void *p1, const void *p2) +{ + const struct vrf_irt_node *irt1 = p1; + const struct vrf_irt_node *irt2 = p2; + + if (irt1 == NULL && irt2 == NULL) + return 1; + + if (irt1 == NULL || irt2 == NULL) + return 0; + + return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0); +} + +/* + * Create a new vrf import_rt in default instance + */ +static struct vrf_irt_node *vrf_import_rt_new(struct ecommunity_val *rt) +{ + struct bgp *bgp_def = NULL; + struct vrf_irt_node *irt; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt new - def instance not created yet"); + return NULL; + } + + irt = XCALLOC(MTYPE_BGP_EVPN_VRF_IMPORT_RT, + sizeof(struct vrf_irt_node)); + if (!irt) + return NULL; + + irt->rt = *rt; + irt->vrfs = list_new(); + + /* Add to hash */ + if (!hash_get(bgp_def->vrf_import_rt_hash, irt, hash_alloc_intern)) { + XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt); + return NULL; + } + + return irt; +} + +/* + * Free the vrf import rt node + */ +static void vrf_import_rt_free(struct vrf_irt_node *irt) +{ + struct bgp *bgp_def = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt free - def instance not created yet"); + return; + } + + hash_release(bgp_def->vrf_import_rt_hash, irt); + XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt); +} + +/* + * Function to lookup Import RT node - used to map a RT to set of + * VNIs importing routes with that RT. + */ +static struct vrf_irt_node *lookup_vrf_import_rt(struct ecommunity_val *rt) +{ + struct bgp *bgp_def = NULL; + struct vrf_irt_node *irt; + struct vrf_irt_node tmp; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt lookup - def instance not created yet"); + return NULL; + } + + memset(&tmp, 0, sizeof(struct vrf_irt_node)); + memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE); + irt = hash_lookup(bgp_def->vrf_import_rt_hash, &tmp); + return irt; +} + +/* + * Is specified VRF present on the RT's list of "importing" VRFs? + */ +static int is_vrf_present_in_irt_vrfs(struct list *vrfs, + struct bgp *bgp_vrf) +{ + struct listnode *node = NULL, *nnode = NULL; + struct bgp *tmp_bgp_vrf = NULL; + + for (ALL_LIST_ELEMENTS(vrfs, node, nnode, tmp_bgp_vrf)) { + if (tmp_bgp_vrf == bgp_vrf) + return 1; + } + return 0; +} + /* * Make import route target hash key. */ @@ -246,6 +371,57 @@ static inline void mask_ecom_global_admin(struct ecommunity_val *dst, } } +/* + * Map one RT to specified VRF. + * bgp_vrf = BGP vrf instance + */ +static void map_vrf_to_rt(struct bgp *bgp_vrf, + struct ecommunity_val *eval) +{ + struct vrf_irt_node *irt = NULL; + struct ecommunity_val eval_tmp; + + /* If using "automatic" RT, + * we only care about the local-admin sub-field. + * This is to facilitate using L3VNI(VRF-VNI) + * as the RT for EBGP peering too. + */ + memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_IMPORT_RT_CFGD)) + mask_ecom_global_admin(&eval_tmp, eval); + + irt = lookup_vrf_import_rt(&eval_tmp); + if (irt && irt->vrfs) + if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) + /* Already mapped. */ + return; + + if (!irt) { + irt = vrf_import_rt_new(&eval_tmp); + assert(irt); + } + + /* Add VRF to the list for this RT. */ + listnode_add(irt->vrfs, bgp_vrf); +} + +/* + * Unmap specified VRF from specified RT. If there are no other + * VRFs for this RT, then the RT hash is deleted. + * bgp_vrf: BGP VRF specific instance + */ +static void unmap_vrf_from_rt(struct bgp *bgp_vrf, + struct vrf_irt_node *irt) +{ + /* Delete VRF from list for this RT. */ + listnode_delete(irt->vrfs, bgp_vrf); + if (!listnode_head(irt->vrfs)) { + list_free(irt->vrfs); + vrf_import_rt_free(irt); + } +} + /* * Map one RT to specified VNI. */ @@ -2135,8 +2311,16 @@ static void free_vni_entry(struct hash_backet *backet, struct bgp *bgp) */ static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf) { - UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + struct bgp *bgp_def = NULL; + form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl); + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + + /* Map RT to VRF */ + bgp_def = bgp_get_default(); + if (!bgp_def) + return; + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); } /* @@ -2209,8 +2393,13 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, } void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, - struct ecommunity *ecomadd) + struct ecommunity *ecomadd) { + //TODO_MITESH: uninstall routes from VRF + + /* Cleanup the RT to VRF mapping */ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + /* Remove auto generated RT */ evpn_auto_rt_import_delete_for_vrf(bgp_vrf); @@ -2218,8 +2407,10 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd); SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); - //TODO_MITESH: handle RT change (uninstall old routes and install new - //routes matching this RT) + /* map VRF to its RTs */ + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + + //TODO_MITESH: install routes matching the new VRF } void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, @@ -2228,6 +2419,11 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; struct ecommunity *ecom = NULL; + //TODO_MITESH: uninstall routes from VRF + + /* Cleanup the RT to VRF mapping */ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + /* remove the RT from the RT list */ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { if (ecommunity_match(ecom, ecomdel)) { @@ -2246,8 +2442,10 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, evpn_auto_rt_import_add_for_vrf(bgp_vrf); } - //TODO_MITESH: handle import RT change - //uninstall old routes, install new routes matching this RT + /* map VRFs to its RTs */ + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + + //TODO_MITESH: install routes matching this new RT } void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, @@ -2632,6 +2830,65 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, return 0; } +/* + * Map the RTs (configured or automatically derived) of a VRF to the VRF. + * The mapping will be used during route processing. + * bgp_def: default bgp instance + * bgp_vrf: specific bgp vrf instance on which RT is configured + */ +void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf) +{ + int i = 0; + struct ecommunity_val *eval = NULL; + struct listnode *node = NULL, *nnode = NULL; + struct ecommunity *ecom = NULL; + + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { + for (i = 0; i < ecom->size; i++) { + eval = (struct ecommunity_val *)(ecom->val + + (i + * ECOMMUNITY_SIZE)); + map_vrf_to_rt(bgp_vrf, eval); + } + } +} + +/* + * Unmap the RTs (configured or automatically derived) of a VRF from the VRF. + */ +void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf) +{ + int i; + struct ecommunity_val *eval; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { + for (i = 0; i < ecom->size; i++) { + struct vrf_irt_node *irt; + struct ecommunity_val eval_tmp; + + eval = (struct ecommunity_val *)(ecom->val + + (i + * ECOMMUNITY_SIZE)); + /* If using "automatic" RT, we only care about the + * local-admin sub-field. + * This is to facilitate using VNI as the RT for EBGP + * peering too. + */ + memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_IMPORT_RT_CFGD)) + mask_ecom_global_admin(&eval_tmp, eval); + + irt = lookup_vrf_import_rt(&eval_tmp); + if (irt) + unmap_vrf_from_rt(bgp_vrf, irt); + } + } +} + + /* * Map the RTs (configured or automatically derived) of a VNI to the VNI. @@ -3071,6 +3328,7 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, /* delete RD/RT */ if (bgp_vrf->vrf_import_rtl && !list_isempty(bgp_vrf->vrf_import_rtl)) { + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); list_delete(bgp_vrf->vrf_import_rtl); bgp_vrf->vrf_import_rtl = NULL; } @@ -3236,6 +3494,9 @@ void bgp_evpn_cleanup(struct bgp *bgp) if (bgp->import_rt_hash) hash_free(bgp->import_rt_hash); bgp->import_rt_hash = NULL; + if (bgp->vrf_import_rt_hash) + hash_free(bgp->vrf_import_rt_hash); + bgp->vrf_import_rt_hash = NULL; if (bgp->vnihash) hash_free(bgp->vnihash); bgp->vnihash = NULL; @@ -3265,6 +3526,9 @@ void bgp_evpn_init(struct bgp *bgp) bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); + bgp->vrf_import_rt_hash = + hash_create(vrf_import_rt_hash_key_make, vrf_import_rt_hash_cmp, + "BGP VRF Import RT Hash"); bgp->vrf_import_rtl = list_new(); bgp->vrf_import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; @@ -3279,3 +3543,8 @@ void bgp_evpn_init(struct bgp *bgp) /*assign 0th index in the bitfield, so that we start with id 1*/ bf_assign_zero_index(bgp->rd_idspace); } + +void bgp_evpn_vrf_delete(struct bgp *bgp_vrf) +{ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); +} diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 42b9ebc5c5..14de479007 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -25,6 +25,7 @@ #define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */ +extern void bgp_evpn_vrf_delete(struct bgp *); extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw); extern char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len); extern char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index b983a00a76..f8594394ef 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -97,6 +97,19 @@ struct irt_node { struct list *vnis; }; +/* Mapping of Import RT to VRFs. + * The Import RTs of all VRFss are maintained in a hash table with each + * RT linking to all VRFs that will import routes matching this RT. + */ +struct vrf_irt_node { + /* RT */ + struct ecommunity_val rt; + + /* List of VNIs importing routes matching this RT. */ + struct list *vrfs; +}; + + #define RT_TYPE_IMPORT 1 #define RT_TYPE_EXPORT 2 #define RT_TYPE_BOTH 3 @@ -285,6 +298,8 @@ extern void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn, int withdraw); extern int bgp_evpn_install_routes(struct bgp *bgp, struct bgpevpn *vpn); extern int bgp_evpn_uninstall_routes(struct bgp *bgp, struct bgpevpn *vpn); +extern void bgp_evpn_map_vrf_to_its_rts(struct bgp *); +extern void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *); extern void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index fce887df2e..86b50db581 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -53,6 +53,117 @@ struct vni_walk_ctx { }; #if defined(HAVE_CUMULUS) +static void display_vrf_import_rt(struct vty *vty, + struct vrf_irt_node *irt, + json_object *json) +{ + u_char *pnt; + u_char type, sub_type; + struct ecommunity_as eas; + struct ecommunity_ip eip; + struct listnode *node, *nnode; + struct bgp *tmp_bgp_vrf = NULL; + json_object *json_rt = NULL; + json_object *json_vrfs = NULL; + char rt_buf[RT_ADDRSTRLEN]; + + if (json) { + json_rt = json_object_new_object(); + json_vrfs = json_object_new_array(); + } + + pnt = (u_char *)&irt->rt.val; + type = *pnt++; + sub_type = *pnt++; + if (sub_type != ECOMMUNITY_ROUTE_TARGET) + return; + + memset(&eas, 0, sizeof(eas)); + switch (type) { + case ECOMMUNITY_ENCODE_AS: + eas.as = (*pnt++ << 8); + eas.as |= (*pnt++); + pnt = ptr_get_be32(pnt, &eas.val); + + snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + + break; + + case ECOMMUNITY_ENCODE_IP: + memcpy(&eip.ip, pnt, 4); + pnt += 4; + eip.val = (*pnt++ << 8); + eip.val |= (*pnt++); + + snprintf(rt_buf, RT_ADDRSTRLEN, "%s:%u", inet_ntoa(eip.ip), + eip.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + + break; + + case ECOMMUNITY_ENCODE_AS4: + pnt = ptr_get_be32(pnt, &eas.val); + eas.val = (*pnt++ << 8); + eas.val |= (*pnt++); + + snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + + break; + + default: + return; + } + + if (!json) { + vty_out(vty, + "\nList of VRFs importing routes with this route-target:\n"); + } + + for (ALL_LIST_ELEMENTS(irt->vrfs, node, nnode, tmp_bgp_vrf)) { + if (json) + json_object_array_add( + json_vrfs, + json_object_new_string( + vrf_id_to_name( + tmp_bgp_vrf->vrf_id))); + else + vty_out(vty, " %s\n", + vrf_id_to_name(tmp_bgp_vrf->vrf_id)); + } + + if (json) { + json_object_object_add(json_rt, "vrfs", json_vrfs); + json_object_object_add(json, rt_buf, json_rt); + } +} + +static void show_vrf_import_rt_entry(struct hash_backet *backet, + void *args[]) +{ + json_object *json = NULL; + struct vty *vty = NULL; + struct vrf_irt_node *irt = (struct vrf_irt_node *)backet->data; + + vty = args[0]; + json = args[1]; + + display_vrf_import_rt(vty, irt, json); +} + static void display_import_rt(struct vty *vty, struct irt_node *irt, json_object *json) { @@ -1432,6 +1543,25 @@ static int evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn) return 0; } +/* + * Display import RT mapping to VRFs (vty handler) + * bgp_def: default bgp instance + */ +static void evpn_show_vrf_import_rts(struct vty *vty, + struct bgp *bgp_def, + json_object *json) +{ + void *args[2]; + + args[0] = vty; + args[1] = json; + + hash_iterate(bgp_def->vrf_import_rt_hash, + (void (*)(struct hash_backet *, void *)) + show_vrf_import_rt_entry, + args); +} + /* * Display import RT mapping to VNIs (vty handler) */ @@ -2765,6 +2895,42 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all, return CMD_SUCCESS; } +/* + * Display EVPN import route-target hash table + */ +DEFUN(show_bgp_l2vpn_evpn_vrf_import_rt, + show_bgp_l2vpn_evpn_vrf_import_rt_cmd, + "show bgp l2vpn evpn vrf-import-rt [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show vrf import route target\n" + JSON_STR) +{ + u_char uj = 0; + struct bgp *bgp_def; + json_object *json = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) + return CMD_WARNING; + + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + + evpn_show_vrf_import_rts(vty, bgp_def, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + /* * Display EVPN import route-target hash table */ @@ -3536,6 +3702,7 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd); /* "show bgp evpn" commands. */ install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 37054ce425..64543ff019 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -117,4 +117,5 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value") DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information") DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT") +DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT") DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 35b83a0401..fae98329c6 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -113,5 +113,6 @@ DECLARE_MTYPE(LCOMMUNITY_VAL) DECLARE_MTYPE(BGP_EVPN) DECLARE_MTYPE(BGP_EVPN_IMPORT_RT) +DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT) DECLARE_MTYPE(BGP_EVPN_MACIP) #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 56e18db311..de1e3b8959 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3139,6 +3139,9 @@ int bgp_delete(struct bgp *bgp) bgp->name); } + /* unmap from RT list */ + bgp_evpn_vrf_delete(bgp); + /* Stop timers. */ if (bgp->t_rmap_def_originate_eval) { BGP_TIMER_OFF(bgp->t_rmap_def_originate_eval); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index c49e2b56b2..927056bd48 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -406,6 +406,9 @@ struct bgp { /* Hash table of Import RTs to EVIs */ struct hash *import_rt_hash; + /* Hash table of VRF import RTs to VRFs */ + struct hash *vrf_import_rt_hash; + /* Id space for automatic RD derivation for an EVI */ bitfield_t rd_idspace; -- 2.39.5