diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 355bc93320..1156810510 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1614,11 +1614,13 @@ void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, { const char *export_name; vpn_policy_direction_t idir, edir; - char *vname; - char buf[1000]; + char *vname, *tmp_name; + char buf[RD_ADDRSTRLEN]; struct ecommunity *ecom; bool first_export = false; int debug; + struct listnode *node; + bool is_inst_match = false; export_name = to_bgp->name ? to_bgp->name : VRF_DEFAULT_NAME; idir = BGP_VPN_POLICY_DIR_FROMVPN; @@ -1634,13 +1636,41 @@ void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, vname = (from_bgp->name ? XSTRDUP(MTYPE_TMP, from_bgp->name) : XSTRDUP(MTYPE_TMP, VRF_DEFAULT_NAME)); - listnode_add(to_bgp->vpn_policy[afi].import_vrf, vname); + /* Check the import_vrf list of destination vrf for the source vrf name, + * insert otherwise. + */ + for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi].import_vrf, + node, tmp_name)) { + if (strcmp(vname, tmp_name) == 0) { + is_inst_match = true; + break; + } + } + if (!is_inst_match) + listnode_add(to_bgp->vpn_policy[afi].import_vrf, + vname); - if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) - first_export = true; + /* Check if the source vrf already exports to any vrf, + * first time export requires to setup auto derived RD/RT values. + * Add the destination vrf name to export vrf list if it is + * not present. + */ + is_inst_match = false; vname = XSTRDUP(MTYPE_TMP, export_name); - listnode_add(from_bgp->vpn_policy[afi].export_vrf, vname); - + if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) { + first_export = true; + } else { + for (ALL_LIST_ELEMENTS_RO(from_bgp->vpn_policy[afi].export_vrf, + node, tmp_name)) { + if (strcmp(vname, tmp_name) == 0) { + is_inst_match = true; + break; + } + } + } + if (!is_inst_match) + listnode_add(from_bgp->vpn_policy[afi].export_vrf, + vname); /* Update import RT for current VRF using export RT of the VRF we're * importing from. First though, make sure "import_vrf" has that * set. @@ -1702,7 +1732,7 @@ void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, const char *export_name, *tmp_name; vpn_policy_direction_t idir, edir; char *vname; - struct ecommunity *ecom; + struct ecommunity *ecom = NULL; struct listnode *node; int debug; @@ -1747,10 +1777,12 @@ void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, if (to_bgp->vpn_policy[afi].import_vrf->count == 0) { UNSET_FLAG(to_bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_VRF_IMPORT); - ecommunity_free(&to_bgp->vpn_policy[afi].rtlist[idir]); + if (to_bgp->vpn_policy[afi].rtlist[idir]) + ecommunity_free(&to_bgp->vpn_policy[afi].rtlist[idir]); } else { ecom = from_bgp->vpn_policy[afi].rtlist[edir]; - ecommunity_del_val(to_bgp->vpn_policy[afi].rtlist[idir], + if (ecom) + ecommunity_del_val(to_bgp->vpn_policy[afi].rtlist[idir], (struct ecommunity_val *)ecom->val); vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp); } @@ -1783,8 +1815,11 @@ void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, * * import_vrf and export_vrf must match in having * the in/out names as appropriate. + * export_vrf list could have been cleaned up + * as part of no router bgp source instnace. */ - assert(vname); + if (!vname) + return; listnode_delete(from_bgp->vpn_policy[afi].export_vrf, vname); XFREE(MTYPE_TMP, vname); @@ -2471,3 +2506,167 @@ void vpn_leak_postchange_all(void) bgp); } } + +/* When a bgp vrf instance is unconfigured, remove its routes + * from the VPN table and this vrf could be importing routes from other + * bgp vrf instnaces, unimport them. + * VRF X and VRF Y are exporting routes to each other. + * When VRF X is deleted, unimport its routes from all target vrfs, + * also VRF Y should unimport its routes from VRF X table. + * This will ensure VPN table is cleaned up appropriately. + */ +int bgp_vpn_leak_unimport(struct bgp *from_bgp, struct vty *vty) +{ + struct bgp *to_bgp; + const char *tmp_name; + char *vname; + struct listnode *node, *next; + safi_t safi = SAFI_UNICAST; + afi_t afi; + bool is_vrf_leak_bind; + int debug; + + if (from_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + return 0; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + tmp_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME; + + for (afi = 0; afi < AFI_MAX; ++afi) { + /* vrf leak is for IPv4 and IPv6 Unicast only */ + if (afi != AFI_IP && afi != AFI_IP6) + continue; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, to_bgp)) { + if (from_bgp == to_bgp) + continue; + + /* Unimport and remove source vrf from the + * other vrfs import list. + */ + struct vpn_policy *to_vpolicy; + + is_vrf_leak_bind = false; + to_vpolicy = &(to_bgp->vpn_policy[afi]); + for (ALL_LIST_ELEMENTS_RO(to_vpolicy->import_vrf, node, + vname)) { + if (strcmp(vname, tmp_name) == 0) { + is_vrf_leak_bind = true; + break; + } + } + /* skip this bgp instance as there is no leak to this + * vrf instance. + */ + if (!is_vrf_leak_bind) + continue; + + if (debug) + zlog_debug("%s: unimport routes from %s to_bgp %s afi %s import vrfs count %u", + __func__, from_bgp->name_pretty, + to_bgp->name_pretty, afi2str(afi), + to_vpolicy->import_vrf->count); + + vrf_unimport_from_vrf(to_bgp, from_bgp, afi, safi); + + /* readd vrf name as unimport removes import vrf name + * from the destination vrf's import list where the + * `import vrf` configuration still exist. + */ + vname = XSTRDUP(MTYPE_TMP, tmp_name); + listnode_add(to_bgp->vpn_policy[afi].import_vrf, + vname); + SET_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT); + + /* If to_bgp exports its routes to the bgp vrf + * which is being deleted, un-import the + * to_bgp routes from VPN. + */ + for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi] + .export_vrf, node, + vname)) { + if (strcmp(vname, tmp_name) == 0) { + vrf_unimport_from_vrf(from_bgp, to_bgp, + afi, safi); + break; + } + } + } + } + return 0; +} + +/* When a router bgp is configured, there could be a bgp vrf + * instance importing routes from this newly configured + * bgp vrf instance. Export routes from configured + * bgp vrf to VPN. + * VRF Y has import from bgp vrf x, + * when a bgp vrf x instance is created, export its routes + * to VRF Y instance. + */ +void bgp_vpn_leak_export(struct bgp *from_bgp) +{ + afi_t afi; + const char *export_name; + char *vname; + struct listnode *node, *next; + struct ecommunity *ecom; + vpn_policy_direction_t idir, edir; + safi_t safi = SAFI_UNICAST; + struct bgp *to_bgp; + int debug; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + idir = BGP_VPN_POLICY_DIR_FROMVPN; + edir = BGP_VPN_POLICY_DIR_TOVPN; + + export_name = (from_bgp->name ? XSTRDUP(MTYPE_TMP, from_bgp->name) + : XSTRDUP(MTYPE_TMP, VRF_DEFAULT_NAME)); + + for (afi = 0; afi < AFI_MAX; ++afi) { + /* vrf leak is for IPv4 and IPv6 Unicast only */ + if (afi != AFI_IP && afi != AFI_IP6) + continue; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, to_bgp)) { + if (from_bgp == to_bgp) + continue; + + /* bgp instance has import list, check to see if newly + * configured bgp instance is the list. + */ + struct vpn_policy *to_vpolicy; + + to_vpolicy = &(to_bgp->vpn_policy[afi]); + for (ALL_LIST_ELEMENTS_RO(to_vpolicy->import_vrf, + node, vname)) { + if (strcmp(vname, export_name) != 0) + continue; + + if (debug) + zlog_debug("%s: found from_bgp %s in to_bgp %s import list, import routes.", + __func__, + export_name, to_bgp->name_pretty); + + ecom = from_bgp->vpn_policy[afi].rtlist[edir]; + /* remove import rt, it will be readded + * as part of import from vrf. + */ + if (ecom) + ecommunity_del_val( + to_vpolicy->rtlist[idir], + (struct ecommunity_val *) + ecom->val); + vrf_import_from_vrf(to_bgp, from_bgp, + afi, safi); + break; + + } + } + } +} diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 2a6c0e1708..3234f7fc9d 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -266,5 +266,7 @@ extern vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey); extern void vpn_leak_postchange_all(void); extern void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, bool is_config); +extern int bgp_vpn_leak_unimport(struct bgp *from_bgp, struct vty *vty); +extern void bgp_vpn_leak_export(struct bgp *from_bgp); #endif /* _QUAGGA_BGP_MPLSVPN_H */ diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9c3bf84213..6e0e079cd8 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -999,6 +999,8 @@ DEFUN_NOSH (router_bgp, if (is_new_bgp && inst_type == BGP_INSTANCE_TYPE_DEFAULT) vpn_leak_postchange_all(); + if (inst_type == BGP_INSTANCE_TYPE_VRF) + bgp_vpn_leak_export(bgp); /* Pending: handle when user tries to change a view to vrf n vv. */ } @@ -1081,6 +1083,9 @@ DEFUN (no_router_bgp, } } + if (bgp_vpn_leak_unimport(bgp, vty)) + return CMD_WARNING_CONFIG_FAILED; + bgp_delete(bgp); return CMD_SUCCESS; @@ -11313,15 +11318,19 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, json_object_array_add(json_import_vrfs, json_object_new_string(vname)); + json_object_object_add(json, "importFromVrfs", + json_import_vrfs); dir = BGP_VPN_POLICY_DIR_FROMVPN; - ecom_str = ecommunity_ecom2str( + if (bgp->vpn_policy[afi].rtlist[dir]) { + ecom_str = ecommunity_ecom2str( bgp->vpn_policy[afi].rtlist[dir], ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - json_object_object_add(json, "importFromVrfs", - json_import_vrfs); - json_object_string_add(json, "importRts", ecom_str); - - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + json_object_string_add(json, "importRts", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } else + json_object_string_add(json, "importRts", + "none"); } if (!CHECK_FLAG(bgp->af_flags[afi][safi], @@ -11345,12 +11354,16 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, buf1, RD_ADDRSTRLEN)); dir = BGP_VPN_POLICY_DIR_TOVPN; - ecom_str = ecommunity_ecom2str( + if (bgp->vpn_policy[afi].rtlist[dir]) { + ecom_str = ecommunity_ecom2str( bgp->vpn_policy[afi].rtlist[dir], ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - json_object_string_add(json, "exportRts", ecom_str); - - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + json_object_string_add(json, "exportRts", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } else + json_object_string_add(json, "exportRts", + "none"); } if (use_json) { @@ -11383,12 +11396,16 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, vty_out(vty, " %s\n", vname); dir = BGP_VPN_POLICY_DIR_FROMVPN; - ecom_str = ecommunity_ecom2str( + ecom_str = NULL; + if (bgp->vpn_policy[afi].rtlist[dir]) { + ecom_str = ecommunity_ecom2str( bgp->vpn_policy[afi].rtlist[dir], ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, "Import RT(s): %s\n", ecom_str); + vty_out(vty, "Import RT(s): %s\n", ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } else + vty_out(vty, "Import RT(s):\n"); } if (!CHECK_FLAG(bgp->af_flags[afi][safi], @@ -11411,11 +11428,14 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, buf1, RD_ADDRSTRLEN)); dir = BGP_VPN_POLICY_DIR_TOVPN; - ecom_str = ecommunity_ecom2str( + if (bgp->vpn_policy[afi].rtlist[dir]) { + ecom_str = ecommunity_ecom2str( bgp->vpn_policy[afi].rtlist[dir], ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, "Export RT: %s\n", ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + vty_out(vty, "Export RT: %s\n", ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } else + vty_out(vty, "Import RT(s):\n"); } }