From 12a844a5069bb3152cbb072560ed13c1d102f808 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 19 Mar 2018 15:41:17 -0400 Subject: [PATCH] bgpd: Implement vrf - vrf route leaking cli add the `import vrf XXXX` command router bgp 4 vrf DONNA ! router bgp 4 vrf EVA address-family ipv4 uni import vrf DONNA ! ! This command will allow for vrf EVA to specify that it would like to receive the routes from vrf DONNA into it's table. Signed-off-by: Donald Sharp --- bgpd/bgp_mplsvpn.c | 104 +++++++++++++++++++++++++++------------ bgpd/bgp_mplsvpn.h | 8 ++- bgpd/bgp_vty.c | 119 +++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgpd.c | 13 +++++ bgpd/bgpd.h | 6 +++ 5 files changed, 216 insertions(+), 34 deletions(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 01068d17f4..5a12e12c77 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -640,8 +640,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ char *s = ecommunity_ecom2str(info_vrf->attr->ecommunity, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - zlog_debug("%s: info_vrf->type=%d, EC{%s}", __func__, - info_vrf->type, s); + zlog_debug("%s: %s info_vrf->type=%d, EC{%s}", __func__, + bgp_vrf->name, info_vrf->type, s); XFREE(MTYPE_ECOMMUNITY_STR, s); } @@ -661,7 +661,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) { if (debug) - zlog_debug("%s: skipping: %s", __func__, debugmsg); + zlog_debug("%s: %s skipping: %s", __func__, + bgp_vrf->name, debugmsg); return; } @@ -737,9 +738,9 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ /* if policy nexthop not set, use 0 */ if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { - struct prefix *nexthop = &bgp_vrf->vpn_policy[afi].tovpn_nexthop; + switch (nexthop->family) { case AF_INET: /* prevent mp_nexthop_global_in <- self in bgp_route.c @@ -759,12 +760,31 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ assert(0); } } else { - if (afi == AFI_IP) { - /* For ipv4, copy to multiprotocol nexthop field */ - static_attr.mp_nexthop_global_in = static_attr.nexthop; - static_attr.mp_nexthop_len = 4; - /* XXX Leave static_attr.nexthop intact for NHT */ - static_attr.flag &= ~ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT)) { + if (afi == AFI_IP) { + /* For ipv4, copy to multiprotocol nexthop field */ + static_attr.mp_nexthop_global_in = static_attr.nexthop; + static_attr.mp_nexthop_len = 4; + /* XXX Leave static_attr.nexthop intact for NHT */ + static_attr.flag &= ~ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } + } else { + switch (afi) { + case AFI_IP: + static_attr.mp_nexthop_global_in.s_addr = + static_attr.nexthop.s_addr; + static_attr.mp_nexthop_len = 4; + static_attr.flag |= + ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + break; + case AFI_IP6: + break; + case AFI_L2VPN: + case AFI_MAX: + assert(!"Unexpected AFI to process"); + break; + } } nexthop_self_flag = 1; } @@ -1036,22 +1056,36 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ nexthop_orig.family = nhfamily; switch (nhfamily) { - case AF_INET: /* save */ nexthop_orig.u.prefix4 = info_vpn->attr->mp_nexthop_global_in; nexthop_orig.prefixlen = 32; + + if (CHECK_FLAG(bgp_vrf->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + static_attr.nexthop.s_addr = + nexthop_orig.u.prefix4.s_addr; + + static_attr.mp_nexthop_global_in = + info_vpn->attr->mp_nexthop_global_in; + static_attr.mp_nexthop_len = + info_vpn->attr->mp_nexthop_len; + } static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); break; - case AF_INET6: /* save */ nexthop_orig.u.prefix6 = info_vpn->attr->mp_nexthop_global; nexthop_orig.prefixlen = 128; + + if (CHECK_FLAG(bgp_vrf->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + static_attr.mp_nexthop_global = nexthop_orig.u.prefix6; + static_attr.mp_nexthop_len = 16; + } break; } - /* * route map handling */ @@ -1101,28 +1135,34 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ * labels for these routes enables the non-labeled nexthops * from the originating VRF to be considered valid for this route. */ + if (!CHECK_FLAG(bgp_vrf->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + /* work back to original route */ + for (bi_ultimate = info_vpn; + bi_ultimate->extra && bi_ultimate->extra->parent; + bi_ultimate = bi_ultimate->extra->parent) + ; - /* work back to original route */ - for (bi_ultimate = info_vpn; - bi_ultimate->extra && bi_ultimate->extra->parent; - bi_ultimate = bi_ultimate->extra->parent) - ; - - /* if original route was unicast, then it did not arrive over vpn */ - if (bi_ultimate->net) { - struct bgp_table *table; + /* + * if original route was unicast, + * then it did not arrive over vpn + */ + if (bi_ultimate->net) { + struct bgp_table *table; - table = bgp_node_table(bi_ultimate->net); - if (table && (table->safi == SAFI_UNICAST)) - origin_local = 1; - } + table = bgp_node_table(bi_ultimate->net); + if (table && (table->safi == SAFI_UNICAST)) + origin_local = 1; + } - /* copy labels */ - if (!origin_local && info_vpn->extra && info_vpn->extra->num_labels) { - num_labels = info_vpn->extra->num_labels; - if (num_labels > BGP_MAX_LABELS) - num_labels = BGP_MAX_LABELS; - pLabels = info_vpn->extra->label; + /* copy labels */ + if (!origin_local && + info_vpn->extra && info_vpn->extra->num_labels) { + num_labels = info_vpn->extra->num_labels; + if (num_labels > BGP_MAX_LABELS) + num_labels = BGP_MAX_LABELS; + pLabels = info_vpn->extra->label; + } } if (debug) { diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index c13030c6c8..2882e1990f 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -92,7 +92,9 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, /* Is vrf configured to export to vpn? */ if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) + && !CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT)) { if (pmsg) *pmsg = "export not set"; return 0; @@ -147,7 +149,9 @@ static inline int vpn_leak_from_vpn_active(struct bgp *bgp_vrf, afi_t afi, /* Is vrf configured to import from vpn? */ if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) { + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) + && !CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { if (pmsg) *pmsg = "import not set"; return 0; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 6bc50fb77e..361b5fb3c0 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6575,6 +6575,116 @@ ALIAS (af_route_map_vpn_imexport, "For routes leaked from vpn to current address-family\n" "For routes leaked from current address-family to vpn\n") +DEFPY (bgp_imexport_vrf, + bgp_imexport_vrf_cmd, + "[no] import vrf NAME$import_name", + NO_STR + "Import routes from another VRF\n" + "VRF to import from\n" + "The name of the VRF\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct listnode *node; + struct bgp *vrf_bgp; + bool remove = false; + int32_t idx = 0; + char *vname; + const char *export_name; + safi_t safi; + afi_t afi; + + if (argv_find(argv, argc, "no", &idx)) + remove = true; + + afi = bgp_node_afi(vty); + safi = bgp_node_safi(vty); + + vrf_bgp = bgp_lookup_by_name(import_name); + if (!vrf_bgp) { + vty_out(vty, "VRF %s is not configured as a bgp instance\n", + import_name); + return CMD_WARNING; + } + + export_name = bgp->name ? bgp->name : VRF_DEFAULT_NAME; + + if (remove) { + for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi].import_vrf, node, + vname)) { + if (strcmp(vname, import_name) == 0) + break; + } + + if (!vname) { + vty_out(vty, + "Specified VRF %s was not imported, ignoring", + import_name); + return CMD_WARNING; + } + + listnode_delete(bgp->vpn_policy[afi].import_vrf, vname); + XFREE(MTYPE_TMP, vname); + + if (bgp->vpn_policy[afi].import_vrf->count == 0) { + UNSET_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT); + + ecommunity_free( + &bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_FROMVPN]); + } + + for (ALL_LIST_ELEMENTS_RO(vrf_bgp->vpn_policy[afi].export_vrf, + node, vname)) { + if (strcmp(vname, export_name) == 0) + break; + } + + listnode_delete(vrf_bgp->vpn_policy[afi].export_vrf, vname); + XFREE(MTYPE_TMP, vname); + + UNSET_FLAG(vrf_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_EXPORT); + UNSET_FLAG(vrf_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET); + + ecommunity_free(&vrf_bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_TOVPN]); + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp, vrf_bgp); + } else { + char buf[1000]; + + for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi].import_vrf, node, + vname)) { + if (strcmp(vname, import_name) == 0) + return CMD_WARNING; + } + + vname = XSTRDUP(MTYPE_TMP, import_name); + listnode_add(bgp->vpn_policy[afi].import_vrf, vname); + + vname = XSTRDUP(MTYPE_TMP, export_name); + listnode_add(vrf_bgp->vpn_policy[afi].export_vrf, vname); + + prefix_rd2str(&vrf_bgp->vrf_prd, buf, sizeof(buf)); + vrf_bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN] = + ecommunity_str2com(buf, ECOMMUNITY_ROUTE_TARGET, 0); + bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN] = + ecommunity_str2com(buf, ECOMMUNITY_ROUTE_TARGET, 0); + + SET_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT); + SET_FLAG(vrf_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_EXPORT); + SET_FLAG(vrf_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET); + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp, + vrf_bgp); + } + + return CMD_SUCCESS; +} + /* This command is valid only in a bgp vrf instance or the default instance */ DEFPY (bgp_imexport_vpn, bgp_imexport_vpn_cmd, @@ -11744,6 +11854,12 @@ void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, { int indent = 2; + if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_IMPORT) + || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT)) + return; + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { @@ -13063,6 +13179,9 @@ void bgp_vty_init(void) install_element(BGP_IPV4_NODE, &bgp_imexport_vpn_cmd); install_element(BGP_IPV6_NODE, &bgp_imexport_vpn_cmd); + install_element(BGP_IPV4_NODE, &bgp_imexport_vrf_cmd); + install_element(BGP_IPV6_NODE, &bgp_imexport_vrf_cmd); + /* ttl_security commands */ install_element(BGP_NODE, &neighbor_ttl_security_cmd); install_element(BGP_NODE, &no_neighbor_ttl_security_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index df0f1bd19c..e181385847 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2956,6 +2956,9 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = MPLS_LABEL_NONE; + + bgp->vpn_policy[afi].import_vrf = list_new(); + bgp->vpn_policy[afi].export_vrf = list_new(); } if (name) { bgp->name = XSTRDUP(MTYPE_BGP, name); @@ -7194,6 +7197,16 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, vty_out(vty, " import vpn\n"); } + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + struct listnode *node; + char *name; + + for (ALL_LIST_ELEMENTS_RO( + bgp->vpn_policy[afi].import_vrf, node, + name)) + vty_out(vty, " import vrf %s\n", name); + } } vty_endframe(vty, " exit-address-family\n"); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 680bac0214..472c805632 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -188,6 +188,10 @@ struct vpn_policy { #define BGP_VPN_POLICY_TOVPN_LABEL_AUTO (1 << 0) #define BGP_VPN_POLICY_TOVPN_RD_SET (1 << 1) #define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET (1 << 2) + + /* If we are importing a vrf -> vrf keep a list of vrf names */ + struct list *import_vrf; + struct list *export_vrf; }; /* @@ -345,6 +349,8 @@ struct bgp { #define BGP_CONFIG_DAMPENING (1 << 0) #define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 1) #define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 2) +#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 3) +#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 4) /* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */ #define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1) -- 2.39.5