diff options
| -rw-r--r-- | bgpd/bgp_evpn.c | 433 | ||||
| -rw-r--r-- | bgpd/bgp_evpn.h | 5 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_private.h | 15 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_vty.c | 57 | ||||
| -rw-r--r-- | bgpd/bgp_memory.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_memory.h | 2 | ||||
| -rw-r--r-- | bgpd/bgp_nexthop.c | 14 | ||||
| -rw-r--r-- | bgpd/bgp_nexthop.h | 27 | ||||
| -rw-r--r-- | bgpd/bgp_nht.c | 56 | ||||
| -rw-r--r-- | bgpd/bgp_nht.h | 1 | ||||
| -rw-r--r-- | bgpd/bgpd.h | 8 |
11 files changed, 608 insertions, 11 deletions
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 6fb7045296..1db552f11d 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -66,6 +66,24 @@ DEFINE_QOBJ_TYPE(bgp_evpn_es); * Static function declarations */ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *evpn); +static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *evpn); +static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, + struct bgp_path_info *pi); +static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, + struct bgp_path_info *pi); +static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, + struct bgpevpn *vpn); +static unsigned int vni_svi_hash_key_make(const void *p); +static bool vni_svi_hash_cmp(const void *p1, const void *p2); +static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, + struct ipaddr *addr, + bool resolve); +static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket, + void *args); +static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket, + void *args); static struct in_addr zero_vtep_ip; /* @@ -2220,6 +2238,7 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) dest = bgp_route_next(dest)) { for (pi = bgp_dest_get_bgp_path_info(dest); (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { + bgp_evpn_remote_ip_hash_del(vpn, pi); bgp_path_info_delete(dest, pi); bgp_path_info_reap(dest, pi); } @@ -2633,6 +2652,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, pi->uptime = bgp_clock(); } + /* Add this route to remote IP hashtable */ + bgp_evpn_remote_ip_hash_add(vpn, pi); + /* Perform route selection and update zebra, if required. */ ret = evpn_route_select_install(bgp, vpn, dest); @@ -2761,6 +2783,8 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!pi) return 0; + bgp_evpn_remote_ip_hash_del(vpn, pi); + /* Mark entry for deletion */ bgp_path_info_delete(dest, pi); @@ -5266,6 +5290,9 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, return NULL; } + bgp_evpn_remote_ip_hash_init(vpn); + bgp_evpn_link_to_vni_svi_hash(bgp, vpn); + /* add to l2vni list on corresponding vrf */ bgpevpn_link_to_l3vni(vpn); @@ -5283,6 +5310,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, */ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) { + bgp_evpn_remote_ip_hash_destroy(vpn); bgp_evpn_vni_es_cleanup(vpn); bgpevpn_unlink_from_l3vni(vpn); bgp_table_unlock(vpn->route_table); @@ -5290,6 +5318,7 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) list_delete(&vpn->import_rtl); list_delete(&vpn->export_rtl); bf_release_index(bm->rd_idspace, vpn->rd_id); + hash_release(bgp->vni_svi_hash, vpn); hash_release(bgp->vnihash, vpn); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); @@ -5701,6 +5730,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) */ delete_routes_for_vni(bgp, vpn); + bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); + vpn->svi_ifindex = 0; /* * tunnel is no longer active, del tunnel ip address from tip_hash @@ -5743,13 +5774,53 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp); - vpn->svi_ifindex = svi_ifindex; + if (vpn->svi_ifindex != svi_ifindex) { + + /* + * Unresolve all the gateway IP nexthops for this VNI + * for old SVI + */ + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_remote_ip_hash_unlink_nexthop, + vpn); + bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); + vpn->svi_ifindex = svi_ifindex; + bgp_evpn_link_to_vni_svi_hash(bgp, vpn); + + /* + * Resolve all the gateway IP nexthops for this VNI + * for new SVI + */ + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_remote_ip_hash_link_nexthop, + vpn); + } /* Update tenant_vrf_id if it has changed. */ if (vpn->tenant_vrf_id != tenant_vrf_id) { + + /* + * Unresolve all the gateway IP nexthops for this VNI + * in old tenant vrf + */ + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_remote_ip_hash_unlink_nexthop, + vpn); bgpevpn_unlink_from_l3vni(vpn); vpn->tenant_vrf_id = tenant_vrf_id; bgpevpn_link_to_l3vni(vpn); + + /* + * Resolve all the gateway IP nexthops for this VNI + * in new tenant vrf + */ + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_remote_ip_hash_link_nexthop, + vpn); } /* If tunnel endpoint IP has changed, update (and delete prior @@ -5870,6 +5941,8 @@ void bgp_evpn_cleanup(struct bgp *bgp) hash_free(bgp->vrf_import_rt_hash); bgp->vrf_import_rt_hash = NULL; + hash_free(bgp->vni_svi_hash); + bgp->vni_svi_hash = NULL; hash_free(bgp->vnihash); bgp->vnihash = NULL; @@ -5888,6 +5961,9 @@ void bgp_evpn_init(struct bgp *bgp) { bgp->vnihash = hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash"); + bgp->vni_svi_hash = + hash_create(vni_svi_hash_key_make, vni_svi_hash_cmp, + "BGP VNI hash based on SVI ifindex"); bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); @@ -5980,3 +6056,358 @@ bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx) return false; } + +static void *bgp_evpn_remote_ip_hash_alloc(void *p) +{ + const struct evpn_remote_ip *key = (const struct evpn_remote_ip *)p; + struct evpn_remote_ip *ip; + + ip = XMALLOC(MTYPE_EVPN_REMOTE_IP, sizeof(struct evpn_remote_ip)); + *ip = *key; + ip->macip_path_list = list_new(); + + return ip; +} + +static unsigned int bgp_evpn_remote_ip_hash_key_make(const void *p) +{ + const struct evpn_remote_ip *ip = p; + const struct ipaddr *addr = &ip->addr; + + if (IS_IPADDR_V4(addr)) + return jhash_1word(addr->ipaddr_v4.s_addr, 0); + + return jhash2(addr->ipaddr_v6.s6_addr32, + array_size(addr->ipaddr_v6.s6_addr32), 0); +} + +static bool bgp_evpn_remote_ip_hash_cmp(const void *p1, const void *p2) +{ + const struct evpn_remote_ip *ip1 = p1; + const struct evpn_remote_ip *ip2 = p2; + + return (memcmp(&ip1->addr, &ip2->addr, sizeof(struct ipaddr)) == 0); +} + +static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *vpn) +{ + vpn->remote_ip_hash = hash_create(bgp_evpn_remote_ip_hash_key_make, + bgp_evpn_remote_ip_hash_cmp, + "BGP EVPN remote IP hash"); +} + +static void bgp_evpn_remote_ip_hash_free(struct hash_bucket *bucket, void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); + + list_delete(&ip->macip_path_list); + + hash_release(vpn->remote_ip_hash, ip); + XFREE(MTYPE_EVPN_REMOTE_IP, ip); +} + +static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn) +{ + if (vpn->remote_ip_hash == NULL) + return; + + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, void *))bgp_evpn_remote_ip_hash_free, + vpn); + + hash_free(vpn->remote_ip_hash); + vpn->remote_ip_hash = NULL; +} + +/* Add a remote MAC/IP route to hash table */ +static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, + struct bgp_path_info *pi) +{ + struct evpn_remote_ip tmp; + struct evpn_remote_ip *ip; + struct prefix_evpn *evp; + + evp = (struct prefix_evpn *)&pi->net->p; + + if (evp->family != AF_EVPN + || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE + || is_evpn_prefix_ipaddr_none(evp)) + return; + + tmp.addr = evp->prefix.macip_addr.ip; + ip = hash_lookup(vpn->remote_ip_hash, &tmp); + if (ip) { + if (listnode_lookup(ip->macip_path_list, pi) != NULL) + return; + (void)listnode_add(ip->macip_path_list, pi); + return; + } + + ip = hash_get(vpn->remote_ip_hash, &tmp, bgp_evpn_remote_ip_hash_alloc); + if (!ip) + return; + + (void)listnode_add(ip->macip_path_list, pi); + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true); +} + +/* Delete a remote MAC/IP route from hash table */ +static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, + struct bgp_path_info *pi) +{ + struct evpn_remote_ip tmp; + struct evpn_remote_ip *ip; + struct prefix_evpn *evp; + + evp = (struct prefix_evpn *)&pi->net->p; + + if (evp->family != AF_EVPN + || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE + || is_evpn_prefix_ipaddr_none(evp)) + return; + + tmp.addr = evp->prefix.macip_addr.ip; + ip = hash_lookup(vpn->remote_ip_hash, &tmp); + if (ip == NULL) + return; + + listnode_delete(ip->macip_path_list, pi); + + if (ip->macip_path_list->count == 0) { + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); + hash_release(vpn->remote_ip_hash, ip); + XFREE(MTYPE_EVPN_REMOTE_IP, ip); + } +} + +static void show_remote_ip_entry(struct hash_bucket *bucket, void *args) +{ + char buf[INET6_ADDRSTRLEN]; + char buf2[EVPN_ROUTE_STRLEN]; + struct prefix_evpn *evp; + + struct listnode *node = NULL; + struct bgp_path_info *pi = NULL; + struct vty *vty = (struct vty *)args; + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + + vty_out(vty, " Remote IP: %s\n", + ipaddr2str(&ip->addr, buf, sizeof(buf))); + vty_out(vty, " Linked MAC/IP routes:\n"); + for (ALL_LIST_ELEMENTS_RO(ip->macip_path_list, node, pi)) { + evp = (struct prefix_evpn *)&pi->net->p; + prefix2str(evp, buf2, sizeof(buf2)); + vty_out(vty, " %s\n", buf2); + } +} + +void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct vty *vty = (struct vty *)args; + + vty_out(vty, "VNI: %u\n", vpn->vni); + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, void *))show_remote_ip_entry, + vty); + vty_out(vty, "\n"); +} + +static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket, + void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true); +} + +static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket, + void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); +} + +static unsigned int vni_svi_hash_key_make(const void *p) +{ + const struct bgpevpn *vpn = p; + + return jhash_1word(vpn->svi_ifindex, 0); +} + +static bool vni_svi_hash_cmp(const void *p1, const void *p2) +{ + const struct bgpevpn *vpn1 = p1; + const struct bgpevpn *vpn2 = p2; + + return (vpn1->svi_ifindex == vpn2->svi_ifindex); +} + +static struct bgpevpn *bgp_evpn_vni_svi_hash_lookup(struct bgp *bgp, + ifindex_t svi) +{ + struct bgpevpn *vpn; + struct bgpevpn tmp; + + memset(&tmp, 0, sizeof(struct bgpevpn)); + tmp.svi_ifindex = svi; + vpn = hash_lookup(bgp->vni_svi_hash, &tmp); + return vpn; +} + +static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn) +{ + if (vpn->svi_ifindex == 0) + return; + + hash_get(bgp->vni_svi_hash, vpn, hash_alloc_intern); +} + +static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, + struct bgpevpn *vpn) +{ + if (vpn->svi_ifindex == 0) + return; + + hash_release(bgp->vni_svi_hash, vpn); +} + +void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args) +{ + struct bgpevpn *evpn = (struct bgpevpn *)bucket->data; + struct vty *vty = (struct vty *)args; + + vty_out(vty, "SVI: %u VNI: %u\n", evpn->svi_ifindex, evpn->vni); +} + +/* + * This function is called for a bgp_nexthop_cache entry when the nexthop is + * gateway IP overlay index. + * This function returns true if there is a remote MAC/IP route for the gateway + * IP in the EVI of the nexthop SVI. + */ +bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc) +{ + struct bgp *bgp_evpn = NULL; + struct bgpevpn *vpn = NULL; + struct evpn_remote_ip tmp; + struct prefix *p; + + if (!bnc->nexthop || bnc->nexthop->ifindex == 0) + return false; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return false; + + /* + * Gateway IP is resolved by nht over SVI interface. + * Use this SVI to find corresponding EVI(L2 context) + */ + vpn = bgp_evpn_vni_svi_hash_lookup(bgp_evpn, bnc->nexthop->ifindex); + if (!vpn) + return false; + + if (vpn->bgp_vrf != bnc->bgp) + return false; + + /* + * Check if the gateway IP is present in the EVI remote_ip_hash table + * which stores all the remote IP addresses received via MAC/IP routes + * in this EVI + */ + memset(&tmp, 0, sizeof(struct evpn_remote_ip)); + + p = &bnc->prefix; + if (p->family == AF_INET) { + tmp.addr.ipa_type = IPADDR_V4; + memcpy(&(tmp.addr.ipaddr_v4), &(p->u.prefix4), + sizeof(struct in_addr)); + } else if (p->family == AF_INET6) { + tmp.addr.ipa_type = IPADDR_V6; + memcpy(&(tmp.addr.ipaddr_v6), &(p->u.prefix6), + sizeof(struct in6_addr)); + } else + return false; + + if (hash_lookup(vpn->remote_ip_hash, &tmp) == NULL) + return false; + + return true; +} + +/* Resolve/Unresolve nexthops when a MAC/IP route is added/deleted */ +static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, + struct ipaddr *addr, + bool resolve) +{ + afi_t afi; + struct prefix p; + struct bgp_nexthop_cache *bnc; + struct bgp_nexthop_cache_head *tree = NULL; + + if (!vpn->bgp_vrf || vpn->svi_ifindex == 0) + return; + + memset(&p, 0, sizeof(struct prefix)); + + if (addr->ipa_type == IPADDR_V4) { + afi = AFI_IP; + p.family = AF_INET; + memcpy(&(p.u.prefix4), &(addr->ipaddr_v4), + sizeof(struct in_addr)); + p.prefixlen = IPV4_MAX_BITLEN; + } else if (addr->ipa_type == IPADDR_V6) { + afi = AFI_IP6; + p.family = AF_INET6; + memcpy(&(p.u.prefix6), &(addr->ipaddr_v6), + sizeof(struct in6_addr)); + p.prefixlen = IPV6_MAX_BITLEN; + } else + return; + + tree = &vpn->bgp_vrf->nexthop_cache_table[afi]; + bnc = bnc_find(tree, &p, 0); + + if (!bnc || !bnc->is_evpn_gwip_nexthop) + return; + + if (!bnc->nexthop || bnc->nexthop->ifindex != vpn->svi_ifindex) + return; + + if (BGP_DEBUG(nht, NHT)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&bnc->prefix, buf, sizeof(buf)); + zlog_debug("%s(%u): vni %u mac/ip %s for NH %s", + vpn->bgp_vrf->name_pretty, vpn->tenant_vrf_id, + vpn->vni, (resolve ? "add" : "delete"), buf); + } + + /* + * MAC/IP route or SVI or tenant vrf being added to EVI. + * Set nexthop as valid only if it is already L3 reachable + */ + if (resolve && bnc->flags & BGP_NEXTHOP_EVPN_INCOMPLETE) { + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + evaluate_paths(bnc); + } + + /* MAC/IP route or SVI or tenant vrf being deleted from EVI */ + if (!resolve && bnc->flags & BGP_NEXTHOP_VALID) { + bnc->flags &= ~BGP_NEXTHOP_VALID; + bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + evaluate_paths(bnc); + } +} + diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 617ed31be1..f91b81f10a 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -211,4 +211,9 @@ extern void bgp_evpn_init(struct bgp *bgp); extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); +extern void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, + void *args); +extern void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args); +extern bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc); + #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 3983f35e50..c46f34bf74 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -103,6 +103,15 @@ struct bgpevpn { struct list *import_rtl; struct list *export_rtl; + /* + * EVPN route that uses gateway IP overlay index as its nexthop + * needs to do a recursive lookup. + * A remote MAC/IP entry should be present for the gateway IP. + * Maintain a hash of the addresses received via remote MAC/IP routes + * for efficient gateway IP recursive lookup in this EVI + */ + struct hash *remote_ip_hash; + /* Route table for EVPN routes for * this VNI. */ struct bgp_table *route_table; @@ -179,6 +188,12 @@ struct bgp_evpn_info { bool is_anycast_mac; }; +/* This structure defines an entry in remote_ip_hash */ +struct evpn_remote_ip { + struct ipaddr addr; + struct list *macip_path_list; +}; + static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) { return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD)); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 4c6225be94..c334992d9a 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4360,6 +4360,61 @@ DEFUN(show_bgp_l2vpn_evpn_vni, return CMD_SUCCESS; } +DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_remote_ip_hash, + show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd, + "show bgp l2vpn evpn vni remote-ip-hash", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show VNI\n" + "Remote IP hash\n") +{ + struct bgp *bgp_evpn; + int idx = 0; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + hash_iterate(bgp_evpn->vnihash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_show_remote_ip_hash, + vty); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_svi_hash, + show_bgp_l2vpn_evpn_vni_svi_hash_cmd, + "show bgp l2vpn evpn vni-svi-hash", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show vni-svi-hash\n") +{ + struct bgp *bgp_evpn; + int idx = 0; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + hash_iterate(bgp_evpn->vni_svi_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_show_vni_svi_hash, + vty); + + return CMD_SUCCESS; +} + DEFPY(show_bgp_l2vpn_evpn_es_evi, show_bgp_l2vpn_evpn_es_evi_cmd, "show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]", @@ -6396,6 +6451,8 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_svi_hash_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index eb85936f0f..196e56a937 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -144,3 +144,4 @@ DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie"); DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service"); DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id"); DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function"); +DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index c5ba371498..0021fa9c3d 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -142,4 +142,6 @@ DECLARE_MTYPE(BGP_SRV6_VPN); DECLARE_MTYPE(BGP_SRV6_SID); DECLARE_MTYPE(BGP_SRV6_FUNCTION); +DECLARE_MTYPE(EVPN_REMOTE_IP); + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index c417dda398..e7bad42f97 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -835,6 +835,18 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bnc->metric, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); + vty_out(vty, "\n"); + bgp_show_nexthops_detail(vty, bgp, bnc); + } else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) { + vty_out(vty, + " %s overlay index unresolved [IGP metric %d], #paths %d", + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, + buf, sizeof(buf)), + bnc->metric, bnc->path_count); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); vty_out(vty, "\n"); bgp_show_nexthops_detail(vty, bgp, bnc); } else { @@ -844,6 +856,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); vty_out(vty, "\n"); if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) vty_out(vty, " Must be Connected\n"); diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index fe0a9646a0..16c2b6c65a 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -56,6 +56,10 @@ struct bgp_nexthop_cache { time_t last_update; uint16_t flags; +/* + * If the nexthop is EVPN gateway IP NH, VALID flag is set only if the nexthop + * is RIB reachable as well as MAC/IP is present + */ #define BGP_NEXTHOP_VALID (1 << 0) #define BGP_NEXTHOP_REGISTERED (1 << 1) #define BGP_NEXTHOP_CONNECTED (1 << 2) @@ -64,11 +68,29 @@ struct bgp_nexthop_cache { #define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5) #define BGP_NEXTHOP_LABELED_VALID (1 << 6) +/* + * This flag is added for EVPN gateway IP nexthops. + * If the nexthop is RIB reachable, but a MAC/IP is not yet + * resolved, this flag is set. + * Following table explains the combination of L3 and L2 reachability w.r.t. + * VALID and INCOMPLETE flags + * + * | MACIP resolved | MACIP unresolved + *----------------|----------------|------------------ + * L3 reachable | VALID = 1 | VALID = 0 + * | INCOMPLETE = 0 | INCOMPLETE = 1 + * ---------------|----------------|-------------------- + * L3 unreachable | VALID = 0 | VALID = 0 + * | INCOMPLETE = 0 | INCOMPLETE = 0 + */ +#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7) + uint16_t change_flags; #define BGP_NEXTHOP_CHANGED (1 << 0) #define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) #define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) +#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3) /* Back pointer to the cache tree this entry belongs to. */ struct bgp_nexthop_cache_head *tree; @@ -79,6 +101,11 @@ struct bgp_nexthop_cache { LIST_HEAD(path_list, bgp_path_info) paths; unsigned int path_count; struct bgp *bgp; + + /* This flag is set to TRUE for a bnc that is gateway IP overlay index + * nexthop. + */ + bool is_evpn_gwip_nexthop; }; extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 638c72ae67..742ef217d9 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -53,7 +53,6 @@ static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, int is_bgp_static_route); static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, int is_bgp_static_route); -static void evaluate_paths(struct bgp_nexthop_cache *bnc); static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p); static int bgp_nht_ifp_initial(struct thread *thread); @@ -244,6 +243,9 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, } } + if (pi && is_route_parent_evpn(pi)) + bnc->is_evpn_gwip_nexthop = true; + if (is_bgp_static_route) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); @@ -331,7 +333,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, return 1; else if (safi == SAFI_UNICAST && pi && pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra - && pi->extra->num_labels) { + && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) { return bgp_isvalid_labeled_nexthop(bnc); } else return (bgp_isvalid_nexthop(bnc)); @@ -387,6 +389,7 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, struct nexthop *nhlist_head = NULL; struct nexthop *nhlist_tail = NULL; int i; + bool evpn_resolved = false; bnc->last_update = bgp_clock(); bnc->change_flags = 0; @@ -417,7 +420,8 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, if (!bnc->nexthop_num) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); - bnc->flags |= BGP_NEXTHOP_VALID; + if (!bnc->is_evpn_gwip_nexthop) + bnc->flags |= BGP_NEXTHOP_VALID; bnc->metric = nhr->metric; bnc->nexthop_num = nhr->nexthop_num; @@ -488,7 +492,40 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, } bnc_nexthop_free(bnc); bnc->nexthop = nhlist_head; + + /* + * Gateway IP nexthop is L3 reachable. Mark it as + * BGP_NEXTHOP_VALID only if it is recursively resolved with a + * remote EVPN RT-2. + * Else, mark it as BGP_NEXTHOP_EVPN_INCOMPLETE. + * When its mapping with EVPN RT-2 is established, unset + * BGP_NEXTHOP_EVPN_INCOMPLETE and set BGP_NEXTHOP_VALID. + */ + if (bnc->is_evpn_gwip_nexthop) { + evpn_resolved = bgp_evpn_is_gateway_ip_resolved(bnc); + + if (BGP_DEBUG(nht, NHT)) { + char buf2[PREFIX2STR_BUFFER]; + + prefix2str(&bnc->prefix, buf2, sizeof(buf2)); + zlog_debug( + "EVPN gateway IP %s recursive MAC/IP lookup %s", + buf2, + (evpn_resolved ? "successful" + : "failed")); + } + + if (evpn_resolved) { + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + } else { + bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->flags &= ~BGP_NEXTHOP_VALID; + } + } } else { + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; bnc->flags &= ~BGP_NEXTHOP_VALID; bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; bnc->nexthop_num = nhr->nexthop_num; @@ -694,6 +731,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE); } } } @@ -888,7 +926,7 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, * RETURNS: * void. */ -static void evaluate_paths(struct bgp_nexthop_cache *bnc) +void evaluate_paths(struct bgp_nexthop_cache *bnc) { struct bgp_dest *dest; struct bgp_path_info *path; @@ -950,12 +988,10 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) bool bnc_is_valid_nexthop = false; bool path_valid = false; - if (safi == SAFI_UNICAST && - path->sub_type == BGP_ROUTE_IMPORTED && - path->extra && - path->extra->num_labels && - path->attr->evpn_overlay.type != - OVERLAY_INDEX_GATEWAY_IP) { + if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED + && path->extra && path->extra->num_labels + && (path->attr->evpn_overlay.type + != OVERLAY_INDEX_GATEWAY_IP)) { bnc_is_valid_nexthop = bgp_isvalid_labeled_nexthop(bnc) ? true : false; } else { diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index 9268b225ca..e006aa4469 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -90,6 +90,7 @@ extern void bgp_nht_register_nexthops(struct bgp *bgp); */ extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer); extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer); +extern void evaluate_paths(struct bgp_nexthop_cache *bnc); /* APIs for setting up and allocating L3 nexthop group ids */ extern uint32_t bgp_l3nhg_id_alloc(void); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index f2c8ece024..a43a21e167 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -640,6 +640,14 @@ struct bgp { /* EVI hash table */ struct hash *vnihash; + /* + * VNI hash table based on SVI ifindex as its key. + * We use SVI ifindex as key to lookup a VNI table for gateway IP + * overlay index recursive lookup. + * For this purpose, a hashtable is added which optimizes this lookup. + */ + struct hash *vni_svi_hash; + /* EVPN enable - advertise gateway macip routes */ int advertise_gw_macip; |
