From: mitesh Date: Wed, 11 Oct 2017 08:32:54 +0000 (-0700) Subject: bgpd: program mac-ip routes in matching vrfs X-Git-Tag: frr-4.0-dev~58^2~63 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=d3135ba31d46262e4bd9deaa65e62366cc65a033;p=matthieu%2Ffrr.git bgpd: program mac-ip routes in matching vrfs Signed-off-by: Mitesh Kanjariya --- diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 326748bb47..4ddbbceb51 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -782,6 +782,95 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn, bgp_info_delete(rn, old_local); } +/* + * Calculate the best path for an EVPN route. Install/update best path in zebra, + * if appropriate. + */ +static int evpn_vrf_route_select_install(struct bgp *bgp_vrf, + struct bgp_node *rn, + struct prefix *p, + afi_t afi, + safi_t safi) +{ + struct bgp_info *old_select, *new_select; + struct bgp_info_pair old_and_new; + int ret = 0; + + /* Compute the best path. */ + bgp_best_selection(bgp_vrf, rn, &bgp_vrf->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 there is still something to + * update + * to zebra RIB. + */ + if (old_select && old_select == new_select + && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_NORMAL + && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) + && !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED) + && !bgp_vrf->addpath_tx_used[afi][safi]) { + if (bgp_zebra_has_route_changed(rn, old_select)) + bgp_zebra_announce(rn, p, old_select, bgp_vrf, + afi, safi); + UNSET_FLAG(old_select->flags, BGP_INFO_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_info_unset_flag(rn, old_select, BGP_INFO_SELECTED); + if (new_select) { + bgp_info_set_flag(rn, new_select, BGP_INFO_SELECTED); + bgp_info_unset_flag(rn, new_select, BGP_INFO_ATTR_CHANGED); + UNSET_FLAG(new_select->flags, BGP_INFO_MULTIPATH_CHG); + } + + if (new_select && new_select->type == ZEBRA_ROUTE_BGP + && new_select->sub_type == BGP_ROUTE_NORMAL) { + bgp_zebra_announce(rn, p, new_select, bgp_vrf, + afi, safi); + + /* If an old best existed and it was a "local" route, the only + * reason + * it would be supplanted is due to MAC mobility procedures. So, + * we + * need to do an implicit delete and withdraw that route from + * peers. + */ + /*if (old_select && old_select->peer == bgp_vrf->peer_self + && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_STATIC) { + //evpn_delete_old_local_route(bgp, vpn, rn, old_select); + }*/ //TODO_MITESH: probably not needed for vrf routes, think!! + } else { + if (old_select && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_NORMAL) { + bgp_zebra_withdraw(p, old_select, safi); + } + } + + /* Clear any route change flags. */ + bgp_zebra_clear_route_change_flags(rn); + + /* Reap old select bgp_info, if it has been removed */ + if (old_select && CHECK_FLAG(old_select->flags, BGP_INFO_REMOVED)) + bgp_info_reap(rn, old_select); + + return ret; +} + /* * Calculate the best path for an EVPN route. Install/update best path in zebra, * if appropriate. @@ -1418,6 +1507,88 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, return update_routes_for_vni(bgp, vpn); } +/* + * Install route entry into the VRF routing table and invoke route selection. + */ +static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, + struct prefix_evpn *evp, + struct bgp_info *parent_ri) +{ + struct bgp_node *rn; + struct bgp_info *ri; + struct attr *attr_new; + int ret; + struct prefix p; + struct prefix *pp = &p; + afi_t afi = 0; + safi_t safi = 0; + + memset(pp, 0, sizeof(struct prefix)); + ip_prefix_from_type2_prefix(evp, pp); + + /* Create (or fetch) route within the VRF. */ + /* NOTE: There is no RD here. */ + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + afi = AFI_IP; + safi = SAFI_UNICAST; + rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); + } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + afi = AFI_IP6; + safi = SAFI_UNICAST; + rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); + } else + return 0; + + /* Check if route entry is already present. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->extra + && (struct bgp_info *)ri->extra->parent == parent_ri) + break; + + if (!ri) { + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_ri->attr); + + /* Create new route with its attribute. */ + ri = info_make(parent_ri->type, parent_ri->sub_type, 0, + parent_ri->peer, attr_new, rn); + SET_FLAG(ri->flags, BGP_INFO_VALID); + bgp_info_extra_get(ri); + ri->extra->parent = parent_ri; + if (parent_ri->extra) + memcpy(&ri->extra->label, &parent_ri->extra->label, + BGP_LABEL_BYTES); + bgp_info_add(rn, ri); + } else { + if (attrhash_cmp(ri->attr, parent_ri->attr) + && !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) { + bgp_unlock_node(rn); + return 0; + } + /* The attribute has changed. */ + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_ri->attr); + + /* Restore route, if needed. */ + if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) + bgp_info_restore(rn, ri); + + /* Mark if nexthop has changed. */ + if (!IPV4_ADDR_SAME(&ri->attr->nexthop, &attr_new->nexthop)) + SET_FLAG(ri->flags, BGP_INFO_IGP_CHANGED); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&ri->attr); + ri->attr = attr_new; + ri->uptime = bgp_clock(); + } + + /* Perform route selection and update zebra, if required. */ + ret = evpn_vrf_route_select_install(bgp_vrf, rn, pp, afi, safi); + + return ret; +} + /* * Install route entry into the VNI routing table and invoke route selection. */ @@ -1484,6 +1655,61 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, return ret; } +/* + * Uninstall route entry from the VRF routing table and send message + * to zebra, if appropriate. + */ +static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, + struct prefix_evpn *evp, + struct bgp_info *parent_ri) +{ + struct bgp_node *rn; + struct bgp_info *ri; + int ret; + struct prefix p; + struct prefix *pp = &p; + afi_t afi = 0; + safi_t safi = 0; + + memset(pp, 0, sizeof(struct prefix)); + ip_prefix_from_type2_prefix(evp, pp); + + /* Locate route within the VRF. */ + /* NOTE: There is no RD here. */ + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + afi = AFI_IP; + safi = SAFI_UNICAST; + rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); + } else { + afi = AFI_IP6; + safi = SAFI_UNICAST; + rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); + } + + if (!rn) + return 0; + + /* Find matching route entry. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->extra + && (struct bgp_info *)ri->extra->parent == parent_ri) + break; + + if (!ri) + return 0; + + /* Mark entry for deletion */ + bgp_info_delete(rn, ri); + + /* Perform route selection and update zebra, if required. */ + ret = evpn_vrf_route_select_install(bgp_vrf, rn, pp, afi, safi); + + /* Unlock route node. */ + bgp_unlock_node(rn); + + return ret; +} + /* * Uninstall route entry from the VNI routing table and send message * to zebra, if appropriate. @@ -1706,6 +1932,45 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) 0); } +/* + * Install or uninstall route in matching VRFs (list). + */ +static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi, + safi_t safi, struct prefix_evpn *evp, + struct bgp_info *ri, + struct list *vrfs, int install) +{ + char buf[PREFIX2STR_BUFFER]; + struct bgp *bgp_vrf; + struct listnode *node, *nnode; + + /* Only type-2 routes go into a VRF */ + if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)) + return 0; + + for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) { + int ret; + + if (install) + ret = install_evpn_route_entry_in_vrf(bgp_vrf, + evp, ri); + else + ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, + evp, ri); + + if (ret) { + zlog_err("%u: Failed to %s prefix %s in VRF %s", + bgp_def->vrf_id, + install ? "install" : "uninstall", + prefix2str(evp, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + return ret; + } + } + + return 0; +} + /* * Install or uninstall route in matching VNIs (list). */ @@ -1777,7 +2042,8 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, u_char type, sub_type; struct ecommunity_val *eval; struct ecommunity_val eval_tmp; - struct irt_node *irt; + struct irt_node *irt; /* import rt for l2vni */ + struct vrf_irt_node *vrf_irt; /* import rt for l3vni */ /* Only deal with RTs */ pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); @@ -1788,12 +2054,18 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (sub_type != ECOMMUNITY_ROUTE_TARGET) continue; - /* Are we interested in this RT? */ + /* Import route into matching l2-vnis */ irt = lookup_import_rt(bgp, eval); if (irt && irt->vnis) install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, irt->vnis, import); + /* Import route into matching l3-vnis (vrfs) */ + vrf_irt = lookup_vrf_import_rt(eval); + if (vrf_irt && vrf_irt->vrfs) + install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri, + vrf_irt->vrfs, import); + /* Also check for non-exact match. In this, we mask out the AS * and * only check on the local-admin sub-field. This is to @@ -1801,16 +2073,22 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, * VNI as the RT for EBGP peering too. */ irt = NULL; + vrf_irt = NULL; if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); + vrf_irt = lookup_vrf_import_rt(&eval_tmp); } if (irt && irt->vnis) install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, irt->vnis, import); + if (vrf_irt && vrf_irt->vrfs) + install_uninstall_route_in_vrfs(bgp, afi, safi, evp, + ri, vrf_irt->vrfs, + import); } return 0; diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index f8594394ef..1b7e4d719e 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -258,6 +258,25 @@ static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq, eval->val[7] = seq & 0xff; } +static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp, + struct prefix *ip) +{ + memset(ip, 0, sizeof(struct prefix)); + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + ip->family = AF_INET; + ip->prefixlen = IPV4_MAX_BITLEN; + memcpy(&(ip->u.prefix4), + &(evp->prefix.ip.ip), + IPV4_MAX_BYTELEN); + } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + ip->family = AF_INET6; + ip->prefixlen = IPV6_MAX_BITLEN; + memcpy(&(ip->u.prefix6), + &(evp->prefix.ip.ip), + IPV6_MAX_BYTELEN); + } +} + static inline void build_evpn_type2_prefix(struct prefix_evpn *p, struct ethaddr *mac, struct ipaddr *ip) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 86b50db581..c3f77aba27 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -411,6 +411,52 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object_object_add(json, "exportRts", json_export_rtl); } +static void evpn_show_vrf_routes(struct vty *vty, + struct bgp *bgp_vrf) +{ + struct bgp *bgp_def = NULL; + struct bgp_node *rn; + struct bgp_info *ri; + int header = 1; + u_int32_t prefix_cnt, path_cnt; + + prefix_cnt = path_cnt = 0; + bgp_def = bgp_get_default(); + if (!bgp_def) + return; + + for (rn = bgp_table_top(bgp_vrf->rib[AFI_L2VPN][SAFI_EVPN]); rn; + rn = bgp_route_next(rn)) { + char prefix_str[BUFSIZ]; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + sizeof(prefix_str)); + + if (rn->info) { + /* Overall header/legend displayed once. */ + if (header) { + bgp_evpn_show_route_header(vty, bgp_def, NULL); + header = 0; + } + prefix_cnt++; + } + + /* For EVPN, the prefix is displayed for each path (to fit in + * with code that already exists). + */ + for (ri = rn->info; ri; ri = ri->next) { + route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL); + path_cnt++; + } + } + + if (prefix_cnt == 0) + vty_out(vty, "No EVPN prefixes exist for this VRF"); + else + vty_out(vty, "\nDisplayed %u prefixes (%u paths)", + prefix_cnt, path_cnt); +} + static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, struct vty *vty, struct in_addr vtep_ip, json_object *json) @@ -2640,6 +2686,33 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, return CMD_SUCCESS; } +/* + * Display per-VRF EVPN routing table. + */ +DEFUN(show_bgp_l2vpn_evpn_route_vrf, show_bgp_l2vpn_evpn_route_vrf_cmd, + "show bgp l2vpn evpn route vrf VRFNAME", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VRF\n" + "VRF Name\n") +{ + int vrf_idx = 6; + char *vrf_name = NULL; + struct bgp *bgp_vrf = NULL; + + vrf_name = argv[vrf_idx]->arg; + bgp_vrf = bgp_lookup_by_name(vrf_name); + if (!bgp_vrf) + return CMD_WARNING; + + evpn_show_vrf_routes(vty, bgp_vrf); + + return CMD_SUCCESS; +} + /* * Display per-VNI EVPN routing table. */ @@ -3697,6 +3770,7 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index a660139a24..78defa85dc 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1015,6 +1015,12 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (info->sub_type == BGP_ROUTE_AGGREGATE) zapi_route_set_blackhole(&api, BLACKHOLE_NULL); + /* If it is an EVPN route mark as such. + * Currently presence of rmac in attr denotes + * this is an EVPN type-2 route */ + if (!is_zero_mac(&(info->attr->rmac))) + SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_TYPE2_ROUTE); + if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED || info->sub_type == BGP_ROUTE_AGGREGATE) { SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); @@ -1072,7 +1078,13 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, api_nh = &api.nexthops[valid_nh_count]; api_nh->gate.ipv4 = *nexthop; - api_nh->type = NEXTHOP_TYPE_IPV4; + + /* EVPN type-2 routes are + programmed as onlink on l3-vni SVI */ + if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_TYPE2_ROUTE)) + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + else + api_nh->type = NEXTHOP_TYPE_IPV4; } else { ifindex_t ifindex; struct in6_addr *nexthop; diff --git a/lib/zebra.h b/lib/zebra.h index fa5fa89f77..73b5f7058e 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -409,6 +409,7 @@ extern const char *zserv_command_string(unsigned int command); #define ZEBRA_FLAG_STATIC 0x40 #define ZEBRA_FLAG_SCOPE_LINK 0x100 #define ZEBRA_FLAG_FIB_OVERRIDE 0x200 +#define ZEBRA_FLAG_EVPN_TYPE2_ROUTE 0x400 /* ZEBRA_FLAG_BLACKHOLE was 0x04 */ /* ZEBRA_FLAG_REJECT was 0x80 */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 58b6965995..82f087cebf 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -259,7 +259,8 @@ struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, /*Pending: need to think if null ifp here is ok during bootup? There was a crash because ifp here was coming to be NULL */ if (ifp) - if (connected_is_unnumbered(ifp)) { + if (connected_is_unnumbered(ifp) || + CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_TYPE2_ROUTE)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 962e66068d..03e9fd3185 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -6084,3 +6084,15 @@ void zebra_vxlan_ns_disable(struct zebra_ns *zns) { hash_free(zns->l3vni_table); } + +/* get the l3vni svi ifindex */ +ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return 0; + + return zl3vni->svi_if->ifindex; +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 33e962c484..9e1ca746a5 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -51,6 +51,7 @@ is_evpn_enabled() #define VNI_STR_LEN 32 +extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id); extern int zebra_vxlan_vrf_delete(struct zebra_vrf *); extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, u_char use_json); diff --git a/zebra/zserv.c b/zebra/zserv.c index 4352b78e83..d82ccb93d7 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1149,6 +1149,7 @@ static int zread_route_add(struct zserv *client, u_short length, if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; + ifindex_t ifindex = 0; switch (api_nh->type) { case NEXTHOP_TYPE_IFINDEX: @@ -1160,9 +1161,16 @@ static int zread_route_add(struct zserv *client, u_short length, re, &api_nh->gate.ipv4, NULL); break; case NEXTHOP_TYPE_IPV4_IFINDEX: + if (CHECK_FLAG(api.flags, + ZEBRA_FLAG_EVPN_TYPE2_ROUTE)) { + ifindex = + get_l3vni_svi_ifindex(zvrf_id(zvrf)); + } else { + ifindex = api_nh->ifindex; + } nexthop = route_entry_nexthop_ipv4_ifindex_add( re, &api_nh->gate.ipv4, NULL, - api_nh->ifindex); + ifindex); break; case NEXTHOP_TYPE_IPV6: nexthop = route_entry_nexthop_ipv6_add(