From 342dd0c6230a7d637a8dec08a5ca9c7b6eb148be Mon Sep 17 00:00:00 2001 From: mitesh Date: Fri, 27 Oct 2017 14:15:45 -0700 Subject: [PATCH] bgpd: advertise/withdraw type-5 routes upon user config CLI config for enabling/disabling type-5 routes router bgp vrf address-family l2vpn evpn [no] advertise loop through all the routes in VRF instance and advertise/withdraw all ip routes as type-5 routes in default instance. Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn.c | 263 ++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_evpn.h | 2 + bgpd/bgp_evpn_private.h | 35 +++++- bgpd/bgp_evpn_vty.c | 109 ++++++++++++++++- bgpd/bgp_route.h | 5 +- bgpd/bgpd.h | 9 +- lib/prefix.c | 3 +- 7 files changed, 413 insertions(+), 13 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index e5d7d369c2..cef8735684 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -601,6 +601,52 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, return zclient_send_message(zclient); } +/* + * Build extended communities for EVPN prefix route. + */ +static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, + struct attr *attr) +{ + struct ecommunity ecom_encap; + struct ecommunity ecom_rmac; + struct ecommunity_val eval; + struct ecommunity_val eval_rmac; + bgp_encap_types tnl_type; + struct listnode *node, *nnode; + struct ecommunity *ecom; + struct list *vrf_export_rtl = NULL; + + /* 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 = (u_int8_t *)eval.val; + + /* Add Encap */ + attr->ecommunity = ecommunity_dup(&ecom_encap); + + /* Add the export RTs for L3VNI/VRF */ + vrf_export_rtl = bgp_vrf->vrf_export_rtl; + if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) { + for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom)) + attr->ecommunity = ecommunity_merge(attr->ecommunity, + ecom); + } + + /* add the router mac extended community */ + if (!is_zero_mac(&attr->rmac)) { + memset(&ecom_rmac, 0, sizeof(ecom_rmac)); + encode_rmac_extcomm(&eval_rmac, &attr->rmac); + ecom_rmac.size = 1; + ecom_rmac.val = (uint8_t *)eval_rmac.val; + attr->ecommunity = ecommunity_merge(attr->ecommunity, + &ecom_rmac); + } + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); +} + /* * Build extended communities for EVPN route. RT and ENCAP are * applicable to all routes. @@ -907,6 +953,112 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn) return local_ri->attr->sticky; } +static int update_evpn_type5_route_entry(struct bgp *bgp_def, + struct bgp *bgp_vrf, afi_t afi, + safi_t safi, struct bgp_node *rn, + struct attr *attr) +{ + struct attr *attr_new = NULL; + struct bgp_info *ri = NULL; + mpls_label_t label = MPLS_INVALID_LABEL; + struct bgp_info *local_ri = NULL; + struct bgp_info *tmp_ri = NULL; + + + /* locate the local route entry if any */ + for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) { + if (tmp_ri->peer == bgp_def->peer_self + && tmp_ri->type == ZEBRA_ROUTE_BGP + && tmp_ri->sub_type == BGP_ROUTE_STATIC) + local_ri = tmp_ri; + } + + /* create a new route entry if one doesnt exist. + Otherwise see if route attr has changed */ + if (!local_ri) { + + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(attr); + + /* create the route info from attribute */ + ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, + bgp_def->peer_self, attr_new, rn); + SET_FLAG(ri->flags, BGP_INFO_VALID); + + /* L3-VNI goes in the label2 field */ + bgp_info_extra_get(ri); + vni2label(bgp_vrf->l3vni, &label); + memcpy(&ri->extra->label2, &label, BGP_LABEL_BYTES); + + /* add the route entry to route node*/ + bgp_info_add(rn, ri); + } else { + + tmp_ri = local_ri; + if (!attrhash_cmp(tmp_ri->attr, attr)) { + /* The attribute has changed. */ + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(attr); + bgp_info_set_flag(rn, tmp_ri, BGP_INFO_ATTR_CHANGED); + + /* Restore route, if needed. */ + if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED)) + bgp_info_restore(rn, tmp_ri); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&tmp_ri->attr); + tmp_ri->attr = attr_new; + tmp_ri->uptime = bgp_clock(); + } + } + return 0; +} + +/* update evpn type-5 route entry */ +static int update_evpn_type5_route(struct bgp *bgp_vrf, + struct prefix_evpn *evp) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct attr attr; + struct bgp_node *rn = NULL; + struct bgp *bgp_def = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) + return -1; + + /* build path attribute for this route */ + memset(&attr, 0, sizeof(struct attr)); + bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + attr.nexthop = bgp_vrf->originator_ip; + attr.mp_nexthop_global_in = bgp_vrf->originator_ip; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + memcpy(&attr.rmac, &bgp_vrf->rmac, sizeof(struct ethaddr)); + + /* Setup RT and encap extended community */ + build_evpn_type5_route_extcomm(bgp_vrf, &attr); + + /* get the route node in global table */ + rn = bgp_afi_node_get(bgp_def->rib[afi][safi], afi, safi, + (struct prefix *)evp, + &bgp_vrf->vrf_prd); + assert(rn); + + /* create or update the route entry within the route node */ + update_evpn_type5_route_entry(bgp_def, bgp_vrf, + afi, safi, + rn, &attr); + + /* schedule for processing and unlock node */ + bgp_process(bgp_def, rn, afi, safi); + bgp_unlock_node(rn); + + /* uninten temporary */ + aspath_unintern(&attr.aspath); + return 0; +} + /* * Create or update EVPN route entry. This could be in the VNI route table * or the global route table. @@ -1089,6 +1241,58 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +/* Delete EVPN type5 route entry from global table */ +static void delete_evpn_type5_route_entry(struct bgp *bgp_def, + struct bgp *bgp_vrf, + afi_t afi, safi_t safi, + struct bgp_node *rn, + struct bgp_info **ri) +{ + struct bgp_info *tmp_ri = NULL; + + *ri = NULL; + + /* find the matching route entry */ + for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) + if (tmp_ri->peer == bgp_def->peer_self + && tmp_ri->type == ZEBRA_ROUTE_BGP + && tmp_ri->sub_type == BGP_ROUTE_STATIC) + break; + + *ri = tmp_ri; + + /* Mark route for delete. */ + if (tmp_ri) + bgp_info_delete(rn, tmp_ri); +} + +/* Delete EVPN type5 route */ +static int delete_evpn_type5_route(struct bgp *bgp_vrf, + struct prefix_evpn *evp) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp_node *rn = NULL; + struct bgp_info *ri = NULL; + struct bgp *bgp_def = NULL; /* default bgp instance */ + + bgp_def = bgp_get_default(); + if (!bgp_def) + return -1; + + /* locate the global route entry for this type-5 prefix */ + rn = bgp_afi_node_lookup(bgp_def->rib[afi][safi], afi, safi, + (struct prefix *)evp, &bgp_vrf->vrf_prd); + if (!rn) + return 0; + + delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri); + if (ri) + bgp_process(bgp_def, rn, afi, safi); + bgp_unlock_node(rn); + return 0; +} + /* * Delete EVPN route entry. This could be in the VNI route table * or the global route table. @@ -2804,6 +3008,58 @@ static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf) * Public functions. */ +/* withdraw all type-5 routes for an address family */ +void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, + afi_t afi) +{ + struct bgp_table *table = NULL; + struct bgp_node *rn = NULL; + + table = bgp_vrf->rib[afi][SAFI_UNICAST]; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + + int ret = 0; + struct prefix_evpn evp; + char buf[PREFIX_STRLEN]; + + build_type5_prefix_from_ip_prefix(&evp, &rn->p); + ret = delete_evpn_type5_route(bgp_vrf, &evp); + if (ret) { + zlog_err( + "%u failed to delete type-5 route for prefix %s in vrf %s", + bgp_vrf->vrf_id, + prefix2str(&rn->p, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } + } +} + +/* advertise all type-5 routes for an address family */ +void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, + afi_t afi) +{ + struct bgp_table *table = NULL; + struct bgp_node *rn = NULL; + + table = bgp_vrf->rib[afi][SAFI_UNICAST]; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + + int ret = 0; + struct prefix_evpn evp; + char buf[PREFIX_STRLEN]; + + build_type5_prefix_from_ip_prefix(&evp, &rn->p); + ret = update_evpn_type5_route(bgp_vrf, &evp); + if (ret) { + zlog_err( + "%u failed to create type-5 route for prefix %s in vrf %s", + bgp_vrf->vrf_id, + prefix2str(&rn->p, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } + } +} + void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl) { @@ -3128,6 +3384,13 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) inet_ntop(family, &p->prefix.ip.ip.addr, buf2, PREFIX2STR_BUFFER)); } + } else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { + snprintf(buf, len, "[%d]:[0]:[%d]:[%s]", + p->prefix.route_type, + p->prefix.ip_prefix_length, + IS_EVPN_PREFIX_IPADDR_V4(p) ? + inet_ntoa(p->prefix.ip.ipaddr_v4) : + inet6_ntoa(p->prefix.ip.ipaddr_v6)); } else { /* For EVPN route types not supported yet. */ snprintf(buf, len, "(unsupported route type %d)", diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index e43fab9a7e..488ebf686a 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -25,6 +25,8 @@ #define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */ +extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi); +extern void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi); 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); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 26a0260794..1cf3f2215f 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -33,6 +33,7 @@ /* EVPN prefix lengths. */ #define EVPN_TYPE_2_ROUTE_PREFIXLEN 224 #define EVPN_TYPE_3_ROUTE_PREFIXLEN 224 +#define EVPN_TYPE_5_ROUTE_PREFIXLEN 168 /* EVPN route types. */ typedef enum { @@ -128,11 +129,14 @@ static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf, static inline int is_evpn_prefix_routes_adv_enabled(struct bgp *bgp_vrf) { - if (!bgp_vrf->l3vni || - !CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_EVPN_PREFIX_ROUTE)) + if (!bgp_vrf->l3vni) return 0; - return 1; + if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN) || + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) + return 1; + + return 0; } static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn) @@ -312,6 +316,31 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p, memcpy(&p->prefix.ip, ip, sizeof(*ip)); } +static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp, + struct prefix *ip_prefix) +{ + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (ip_prefix->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&ip.ipaddr_v4, &ip_prefix->u.prefix4, + sizeof(struct in_addr)); + } else { + ip.ipa_type = IPADDR_V6; + memcpy(&ip.ipaddr_v6, &ip_prefix->u.prefix6, + sizeof(struct in6_addr)); + } + + memset(evp, 0, sizeof(struct prefix_evpn)); + evp->family = AF_EVPN; + evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN; + evp->prefix.ip_prefix_length = ip_prefix->prefixlen; + evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; + evp->prefix.ip.ipa_type = ip.ipa_type; + memcpy(&evp->prefix.ip, &ip, sizeof(struct ipaddr)); +} + static inline void build_evpn_type3_prefix(struct prefix_evpn *p, struct in_addr originator_ip) { diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index cb3c5eeb4d..b2f4737b25 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -1978,11 +1978,11 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* RD header and legend - once overall. */ if (rd_header && !json) { vty_out(vty, - "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:" - "[MAC]\n"); + "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]\n"); vty_out(vty, - "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:" - "[OrigIP]\n\n"); + "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); + vty_out(vty, + "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n"); rd_header = 0; } @@ -2440,6 +2440,105 @@ DEFUN (no_bgp_evpn_advertise_all_vni, return CMD_SUCCESS; } +DEFUN (bgp_evpn_advertise_type5, + bgp_evpn_advertise_type5_cmd, + "advertise ", + "Advertise prefix routes\n" + "advertise ipv4 prefix only\n" + "advertise ipv6 prefix only\n" + "advertise both ipv4/ipv6 prefix\n") +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ + uint32_t type = 0; + + if (strcmp(argv[1]->text, "ipv4")) { + type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN; + } else if (strcmp(argv[1]->text, "ipv6")) { + type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN; + } else if (strcmp(argv[1]->text, "both")) { + type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN | + BGP_VRF_ADVERTISE_IPV6_IN_EVPN; + } else { + vty_out(vty, "%%invalid command"); + return CMD_WARNING; + } + + if (CHECK_FLAG(type, BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { + + /* if we are already advertising ipv4 prefix as type-5 + * nothing to do */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { + SET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN); + bgp_evpn_advertise_type5_routes(bgp_vrf, + AFI_IP); + } + } else { + + /* if we are already advertising ipv6 prefix as type-5 + * nothing to do */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) { + SET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN); + bgp_evpn_advertise_type5_routes(bgp_vrf, + AFI_IP6); + } + } + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_advertise_type5, + no_bgp_evpn_advertise_type5_cmd, + "no advertise ", + NO_STR + "Advertise prefix routes\n" + "advertise ipv4 prefix only\n" + "advertise ipv6 prefix only\n" + "advertise both ipv4/ipv6 prefix\n") +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ + uint32_t type = 0; + + if (strcmp(argv[1]->text, "ipv4")) { + type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN; + } else if (strcmp(argv[1]->text, "ipv6")) { + type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN; + } else if (strcmp(argv[1]->text, "both")) { + type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN | + BGP_VRF_ADVERTISE_IPV6_IN_EVPN; + } else { + vty_out(vty, "%%invalid command"); + return CMD_WARNING; + } + + if (CHECK_FLAG(type, BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { + + /* if we are already advertising ipv4 prefix as type-5 + * nothing to do */ + if (CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { + bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP); + UNSET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN); + } + } else { + + /* if we are already advertising ipv6 prefix as type-5 + * nothing to do */ + if (CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) { + bgp_evpn_withdraw_type5_routes(bgp_vrf, + AFI_IP6); + UNSET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN); + } + } + return CMD_SUCCESS; +} + /* * Display VNI information - for all or a specific VNI */ @@ -3955,6 +4054,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd); /* "show bgp l2vpn evpn" commands. */ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 085de3fabb..ae4759aad0 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -73,9 +73,12 @@ struct bgp_info_extra { /* Nexthop reachability check. */ u_int32_t igpmetric; - /* MPLS label. */ + /* MPLS label - L2VNI */ mpls_label_t label; + /* MPLS label - L3-VNI */ + mpls_label_t label2; + #if ENABLE_BGP_VNC union { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 449326ece5..bcf30adb3d 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -424,10 +424,11 @@ struct bgp { /* vrf flags */ uint32_t vrf_flags; #define BGP_VRF_AUTO (1 << 0) -#define BGP_VRF_ADVERTISE_EVPN_PREFIX_ROUTE (1 << 1) -#define BGP_VRF_IMPORT_RT_CFGD (1 << 2) -#define BGP_VRF_EXPORT_RT_CFGD (1 << 3) -#define BGP_VRF_RD_CFGD (1 << 4) +#define BGP_VRF_ADVERTISE_IPV4_IN_EVPN (1 << 1) +#define BGP_VRF_ADVERTISE_IPV6_IN_EVPN (1 << 2) +#define BGP_VRF_IMPORT_RT_CFGD (1 << 3) +#define BGP_VRF_EXPORT_RT_CFGD (1 << 4) +#define BGP_VRF_RD_CFGD (1 << 5) /* unique ID for auto derivation of RD for this vrf */ uint16_t vrf_rd_id; diff --git a/lib/prefix.c b/lib/prefix.c index f1c25d24d9..9f13cb8bb1 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1043,10 +1043,11 @@ static const char *prefixevpn2str(const struct prefix *p, char *str, int size) family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p) ? AF_INET : AF_INET6; - snprintf(str, size, "[%d]:[%u][%s]/%d", + snprintf(str, size, "[%d]:[%u][%s/%d]/%d", p->u.prefix_evpn.route_type, p->u.prefix_evpn.eth_tag, inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf, PREFIX2STR_BUFFER), + p->u.prefix_evpn.ip_prefix_length, p->prefixlen); } else { sprintf(str, "Unsupported EVPN route type %d", -- 2.39.5