diff options
| -rw-r--r-- | bgpd/bgp_evpn.c | 40 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_mh.c | 536 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_mh.h | 48 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_private.h | 4 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_vty.c | 109 | ||||
| -rw-r--r-- | bgpd/bgp_memory.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_memory.h | 1 | ||||
| -rw-r--r-- | doc/user/bgp.rst | 51 | ||||
| -rw-r--r-- | doc/user/pim.rst | 20 | ||||
| -rw-r--r-- | lib/prefix.c | 7 | ||||
| -rw-r--r-- | lib/prefix.h | 1 | ||||
| -rw-r--r-- | pimd/pim_cmd.c | 39 | ||||
| -rw-r--r-- | pimd/pim_igmpv2.c | 3 | ||||
| -rw-r--r-- | pimd/pim_mroute.c | 10 | ||||
| -rw-r--r-- | pimd/pim_nht.c | 6 | ||||
| -rw-r--r-- | pimd/pim_rp.c | 2 | ||||
| -rw-r--r-- | pimd/pim_vty.c | 5 | ||||
| -rw-r--r-- | pimd/pim_zlookup.c | 2 | ||||
| -rw-r--r-- | pimd/pimd.h | 6 | ||||
| -rw-r--r-- | tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py | 217 | ||||
| -rw-r--r-- | tests/topotests/lib/micronet_cli.py | 4 | ||||
| -rw-r--r-- | tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py | 8 | ||||
| -rw-r--r-- | tools/frrcommon.sh.in | 6 | ||||
| -rw-r--r-- | zebra/rt_netlink.c | 2 | ||||
| -rw-r--r-- | zebra/rt_socket.c | 6 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mh.c | 10 |
26 files changed, 978 insertions, 166 deletions
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index e755ff7455..9f3f8389ad 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -325,8 +325,8 @@ static int is_vni_present_in_irt_vnis(struct list *vnis, struct bgpevpn *vpn) /* * Compare Route Targets. */ -static int evpn_route_target_cmp(struct ecommunity *ecom1, - struct ecommunity *ecom2) +int bgp_evpn_route_target_cmp(struct ecommunity *ecom1, + struct ecommunity *ecom2) { if (ecom1 && !ecom2) return -1; @@ -349,7 +349,7 @@ static int evpn_route_target_cmp(struct ecommunity *ecom1, return strcmp(ecom1->str, ecom2->str); } -static void evpn_xxport_delete_ecomm(void *val) +void bgp_evpn_xxport_delete_ecomm(void *val) { struct ecommunity *ecomm = val; ecommunity_free(&ecomm); @@ -3036,9 +3036,11 @@ int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf, return 0; /* don't import hosts that are locally attached */ - if (install - && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, - install)) + if (install && bgp_evpn_skip_vrf_import_of_local_es( + bgp_vrf, evp, pi, install)) + return 0; + + if (install) ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi); else ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp, @@ -3291,9 +3293,11 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi, int ret; /* don't import hosts that are locally attached */ - if (install - && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, - install)) + if (install && bgp_evpn_skip_vrf_import_of_local_es( + bgp_vrf, evp, pi, install)) + return 0; + + if (install) ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi); else ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp, @@ -5318,11 +5322,13 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); - vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; - vpn->import_rtl->del = evpn_xxport_delete_ecomm; + vpn->import_rtl->cmp = + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + vpn->import_rtl->del = bgp_evpn_xxport_delete_ecomm; vpn->export_rtl = list_new(); - vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; - vpn->export_rtl->del = evpn_xxport_delete_ecomm; + vpn->export_rtl->cmp = + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + vpn->export_rtl->del = bgp_evpn_xxport_delete_ecomm; bf_assign_index(bm->rd_idspace, vpn->rd_id); derive_rd_rt_for_vni(bgp, vpn); @@ -6023,12 +6029,12 @@ void bgp_evpn_init(struct bgp *bgp) "BGP VRF Import RT Hash"); bgp->vrf_import_rtl = list_new(); bgp->vrf_import_rtl->cmp = - (int (*)(void *, void *))evpn_route_target_cmp; - bgp->vrf_import_rtl->del = evpn_xxport_delete_ecomm; + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + bgp->vrf_import_rtl->del = bgp_evpn_xxport_delete_ecomm; bgp->vrf_export_rtl = list_new(); bgp->vrf_export_rtl->cmp = - (int (*)(void *, void *))evpn_route_target_cmp; - bgp->vrf_export_rtl->del = evpn_xxport_delete_ecomm; + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + bgp->vrf_export_rtl->del = bgp_evpn_xxport_delete_ecomm; bgp->l2vnis = list_new(); bgp->l2vnis->cmp = vni_list_cmp; /* By default Duplicate Address Dection is enabled. diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index ea179ec2b4..2296bd773c 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -462,7 +462,9 @@ int bgp_evpn_mh_route_update(struct bgp *bgp, struct bgp_evpn_es *es, * ESR). */ static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, - struct bgpevpn *vpn, struct prefix_evpn *p) + struct bgpevpn *vpn, + struct bgp_evpn_es_frag *es_frag, + struct prefix_evpn *p) { afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -477,7 +479,7 @@ static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, prd = &vpn->prd; } else { rt_table = es->route_table; - prd = &es->prd; + prd = &es_frag->prd; } /* First, locate the route node within the ESI or VNI. @@ -680,7 +682,7 @@ static int bgp_evpn_type4_route_update(struct bgp *bgp, struct bgp_path_info *global_pi; dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, - p, &es->prd); + p, &es->es_base_frag->prd); bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, dest, attr_new, 1, &global_pi, &route_changed); @@ -699,7 +701,11 @@ static int bgp_evpn_type4_route_update(struct bgp *bgp, static int bgp_evpn_type4_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, struct prefix_evpn *p) { - return bgp_evpn_mh_route_delete(bgp, es, NULL /* l2vni */, p); + if (!es->es_base_frag) + return -1; + + return bgp_evpn_mh_route_delete(bgp, es, NULL /* l2vni */, + es->es_base_frag, p); } /* Process remote/received EVPN type-4 route (advertise or withdraw) */ @@ -845,8 +851,9 @@ static int bgp_evpn_type4_remote_routes_import(struct bgp *bgp, */ /* Extended communities associated with EAD-per-ES */ -static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es, - struct attr *attr) +static void +bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es_frag *es_frag, + struct attr *attr) { struct ecommunity ecom_encap; struct ecommunity ecom_esi_label; @@ -880,16 +887,22 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es, /* XXX - suppress EAD-ES advertisment if there are no EVIs associated * with it. */ - for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, - evi_node, es_evi)) { - if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) - continue; - for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl, - rt_node, ecom)) + if (listcount(bgp_mh_info->ead_es_export_rtl)) { + for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl, + rt_node, ecom)) bgp_attr_set_ecommunity( - attr, - ecommunity_merge(bgp_attr_get_ecommunity(attr), - ecom)); + attr, ecommunity_merge(attr->ecommunity, ecom)); + } else { + for (ALL_LIST_ELEMENTS_RO(es_frag->es_evi_frag_list, evi_node, + es_evi)) { + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + continue; + for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl, + rt_node, ecom)) + bgp_attr_set_ecommunity( + attr, ecommunity_merge(attr->ecommunity, + ecom)); + } } attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); @@ -926,9 +939,10 @@ static void bgp_evpn_type1_evi_route_extcomm_build(struct bgp_evpn_es *es, /* Update EVPN EAD (type-1) route - * vpn - valid for EAD-EVI routes and NULL for EAD-ES routes */ -static int bgp_evpn_type1_route_update(struct bgp *bgp, - struct bgp_evpn_es *es, struct bgpevpn *vpn, - struct prefix_evpn *p) +static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es, + struct bgpevpn *vpn, + struct bgp_evpn_es_frag *es_frag, + struct prefix_evpn *p) { int ret = 0; afi_t afi = AFI_L2VPN; @@ -974,7 +988,7 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, /* MPLS label is 0 for EAD-ES route */ /* Set up extended community */ - bgp_evpn_type1_es_route_extcomm_build(es, &attr); + bgp_evpn_type1_es_route_extcomm_build(es_frag, &attr); /* First, create (or fetch) route node within the ES. */ /* NOTE: There is no RD here. */ @@ -990,7 +1004,7 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, "%u ERROR: Failed to updated EAD-EVI route ESI: %s VTEP %pI4", bgp->vrf_id, es->esi_str, &es->originator_ip); } - global_rd = &es->prd; + global_rd = &es_frag->prd; } @@ -1034,54 +1048,86 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, * table and advertise these routes to peers. */ +static void bgp_evpn_ead_es_route_update(struct bgp *bgp, + struct bgp_evpn_es *es) +{ + struct listnode *node; + struct bgp_evpn_es_frag *es_frag; + struct prefix_evpn p; + + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi, + es->originator_ip); + for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { + if (!listcount(es_frag->es_evi_frag_list)) + continue; + + p.prefix.ead_addr.frag_id = es_frag->rd_id; + if (bgp_evpn_type1_route_update(bgp, es, NULL, es_frag, &p)) + flog_err( + EC_BGP_EVPN_ROUTE_CREATE, + "EAD-ES route creation failure for ESI %s frag %u", + es->esi_str, es_frag->rd_id); + } +} + +static void bgp_evpn_ead_evi_route_update(struct bgp *bgp, + struct bgp_evpn_es *es, + struct bgpevpn *vpn, + struct prefix_evpn *p) +{ + if (bgp_evpn_type1_route_update(bgp, es, vpn, NULL, p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "EAD-EVI route creation failure for ESI %s VNI %u", + es->esi_str, vpn->vni); +} + void update_type1_routes_for_evi(struct bgp *bgp, struct bgpevpn *vpn) { struct prefix_evpn p; struct bgp_evpn_es *es; struct bgp_evpn_es_evi *es_evi; - struct bgp_evpn_es_evi *es_evi_next; - RB_FOREACH_SAFE(es_evi, bgp_es_evi_rb_head, - &vpn->es_evi_rb_tree, es_evi_next) { + + RB_FOREACH (es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree) { es = es_evi->es; + if (es_evi->vpn != vpn) + continue; + /* Update EAD-ES */ - if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { - build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, - &es->esi, es->originator_ip); - if (bgp_evpn_type1_route_update(bgp, es, NULL, &p)) - flog_err(EC_BGP_EVPN_ROUTE_CREATE, - "%u: EAD-ES route update failure for ESI %s VNI %u", - bgp->vrf_id, es->esi_str, - es_evi->vpn->vni); - } + bgp_evpn_ead_es_route_update(bgp, es); /* Update EAD-EVI */ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) { build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, &es->esi, es->originator_ip); - if (bgp_evpn_type1_route_update(bgp, es, es_evi->vpn, - &p)) - flog_err(EC_BGP_EVPN_ROUTE_DELETE, - "%u: EAD-EVI route update failure for ESI %s VNI %u", - bgp->vrf_id, es->esi_str, - es_evi->vpn->vni); + bgp_evpn_ead_evi_route_update(bgp, es, vpn, &p); } } } /* Delete local Type-1 route */ -static int bgp_evpn_type1_es_route_delete(struct bgp *bgp, - struct bgp_evpn_es *es, struct prefix_evpn *p) +static void bgp_evpn_ead_es_route_delete(struct bgp *bgp, + struct bgp_evpn_es *es) { - return bgp_evpn_mh_route_delete(bgp, es, NULL /* l2vni */, p); + struct listnode *node; + struct bgp_evpn_es_frag *es_frag; + struct prefix_evpn p; + + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi, + es->originator_ip); + for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { + p.prefix.ead_addr.frag_id = es_frag->rd_id; + bgp_evpn_mh_route_delete(bgp, es, NULL, es_frag, &p); + } } -static int bgp_evpn_type1_evi_route_delete(struct bgp *bgp, - struct bgp_evpn_es *es, struct bgpevpn *vpn, - struct prefix_evpn *p) +static int bgp_evpn_ead_evi_route_delete(struct bgp *bgp, + struct bgp_evpn_es *es, + struct bgpevpn *vpn, + struct prefix_evpn *p) { - return bgp_evpn_mh_route_delete(bgp, es, vpn, p); + return bgp_evpn_mh_route_delete(bgp, es, vpn, NULL, p); } /* Generate EAD-EVI for all VNIs */ @@ -1107,10 +1153,7 @@ static void bgp_evpn_local_type1_evi_route_add(struct bgp *bgp, for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) { if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) continue; - if (bgp_evpn_type1_route_update(bgp, es, es_evi->vpn, &p)) - flog_err(EC_BGP_EVPN_ROUTE_CREATE, - "%u: Type4 route creation failure for ESI %s", - bgp->vrf_id, es->esi_str); + bgp_evpn_ead_evi_route_update(bgp, es, es_evi->vpn, &p); } } @@ -1135,7 +1178,7 @@ static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp, for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) { if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) continue; - if (bgp_evpn_mh_route_delete(bgp, es, es_evi->vpn, &p)) + if (bgp_evpn_mh_route_delete(bgp, es, es_evi->vpn, NULL, &p)) flog_err(EC_BGP_EVPN_ROUTE_CREATE, "%u: Type4 route creation failure for ESI %s", bgp->vrf_id, es->esi_str); @@ -1199,6 +1242,72 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, return ret; } +void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp, + struct ecommunity *ecomcfg, bool del) +{ + struct listnode *node, *nnode, *node_to_del; + struct ecommunity *ecom; + struct bgp_evpn_es *es; + + if (del) { + if (ecomcfg == NULL) { + /* Reset to default and process all routes. */ + for (ALL_LIST_ELEMENTS(bgp_mh_info->ead_es_export_rtl, + node, nnode, ecom)) { + ecommunity_free(&ecom); + list_delete_node(bgp_mh_info->ead_es_export_rtl, + node); + } + } + + /* Delete a specific export RT */ + else { + node_to_del = NULL; + + for (ALL_LIST_ELEMENTS(bgp_mh_info->ead_es_export_rtl, + node, nnode, ecom)) { + if (ecommunity_match(ecom, ecomcfg)) { + ecommunity_free(&ecom); + node_to_del = node; + break; + } + } + + if (node_to_del) + list_delete_node(bgp_mh_info->ead_es_export_rtl, + node_to_del); + } + } else { + listnode_add_sort(bgp_mh_info->ead_es_export_rtl, ecomcfg); + } + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("local ES del/re-add EAD route on export RT change"); + /* + * walk through all active ESs withdraw the old EAD and + * generate a new one + */ + RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) { + if (!bgp_evpn_is_es_local(es) || + !bgp_evpn_local_es_is_active(es)) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "local ES %s del/re-add EAD route on export RT change", + es->esi_str); + + /* + * withdraw EAD-ES. XXX - this should technically not be + * needed; can be removed after testing + */ + bgp_evpn_ead_es_route_delete(bgp, es); + + /* generate EAD-ES */ + bgp_evpn_ead_es_route_update(bgp, es); + } +} + /*****************************************************************************/ /* Ethernet Segment Management * 1. Ethernet Segment is a collection of links attached to the same @@ -1612,6 +1721,167 @@ bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf, } } +static void bgp_evpn_es_frag_free(struct bgp_evpn_es_frag *es_frag) +{ + struct bgp_evpn_es *es = es_frag->es; + + if (es->es_base_frag == es_frag) + es->es_base_frag = NULL; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s frag %u free", es->esi_str, es_frag->rd_id); + list_delete_node(es->es_frag_list, &es_frag->es_listnode); + + /* EVIs that are advertised using the info in this fragment */ + list_delete(&es_frag->es_evi_frag_list); + + bf_release_index(bm->rd_idspace, es_frag->rd_id); + + + XFREE(MTYPE_BGP_EVPN_ES_FRAG, es_frag); +} + +static void bgp_evpn_es_frag_free_unused(struct bgp_evpn_es_frag *es_frag) +{ + if ((es_frag->es->es_base_frag == es_frag) || + listcount(es_frag->es_evi_frag_list)) + return; + + bgp_evpn_es_frag_free(es_frag); +} + +static void bgp_evpn_es_frag_free_all(struct bgp_evpn_es *es) +{ + struct listnode *node; + struct listnode *nnode; + struct bgp_evpn_es_frag *es_frag; + + for (ALL_LIST_ELEMENTS(es->es_frag_list, node, nnode, es_frag)) + bgp_evpn_es_frag_free(es_frag); +} + +static struct bgp_evpn_es_frag *bgp_evpn_es_frag_new(struct bgp_evpn_es *es) +{ + struct bgp_evpn_es_frag *es_frag; + char buf[BGP_EVPN_PREFIX_RD_LEN]; + struct bgp *bgp; + + es_frag = XCALLOC(MTYPE_BGP_EVPN_ES_FRAG, sizeof(*es_frag)); + bf_assign_index(bm->rd_idspace, es_frag->rd_id); + es_frag->prd.family = AF_UNSPEC; + es_frag->prd.prefixlen = 64; + bgp = bgp_get_evpn(); + snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, + es_frag->rd_id); + (void)str2prefix_rd(buf, &es_frag->prd); + + /* EVIs that are advertised using the info in this fragment */ + es_frag->es_evi_frag_list = list_new(); + listset_app_node_mem(es_frag->es_evi_frag_list); + + /* Link the fragment to the parent ES */ + es_frag->es = es; + listnode_init(&es_frag->es_listnode, es_frag); + listnode_add(es->es_frag_list, &es_frag->es_listnode); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s frag %u new", es->esi_str, es_frag->rd_id); + return es_frag; +} + +static struct bgp_evpn_es_frag * +bgp_evpn_es_find_frag_with_space(struct bgp_evpn_es *es) +{ + struct listnode *node; + struct bgp_evpn_es_frag *es_frag; + + for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { + if (listcount(es_frag->es_evi_frag_list) < + bgp_mh_info->evi_per_es_frag) + return es_frag; + } + + /* No frags where found with space; allocate a new one */ + return bgp_evpn_es_frag_new(es); +} + +/* Link the ES-EVI to one of the ES fragments */ +static void bgp_evpn_es_frag_evi_add(struct bgp_evpn_es_evi *es_evi) +{ + struct bgp_evpn_es_frag *es_frag; + struct bgp_evpn_es *es = es_evi->es; + + if (es_evi->es_frag || + !(CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))) + return; + + es_frag = bgp_evpn_es_find_frag_with_space(es); + + es_evi->es_frag = es_frag; + listnode_init(&es_evi->es_frag_listnode, es_evi); + listnode_add(es_frag->es_evi_frag_list, &es_evi->es_frag_listnode); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s vni %d linked to frag %u", es->esi_str, + es_evi->vpn->vni, es_frag->rd_id); +} + +/* UnLink the ES-EVI from the ES fragment */ +static void bgp_evpn_es_frag_evi_del(struct bgp_evpn_es_evi *es_evi, + bool send_ead_del_if_empty) +{ + struct bgp_evpn_es_frag *es_frag = es_evi->es_frag; + struct prefix_evpn p; + struct bgp_evpn_es *es; + struct bgp *bgp; + + if (!es_frag) + return; + + es = es_frag->es; + es_evi->es_frag = NULL; + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s vni %d unlinked from frag %u", es->esi_str, + es_evi->vpn->vni, es_frag->rd_id); + + list_delete_node(es_frag->es_evi_frag_list, &es_evi->es_frag_listnode); + + /* + * if there are no other EVIs on the fragment deleted the EAD-ES for + * the fragment + */ + if (send_ead_del_if_empty && !listcount(es_frag->es_evi_frag_list)) { + bgp = bgp_get_evpn(); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s frag %u ead-es route delete", + es->esi_str, es_frag->rd_id); + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi, + es->originator_ip); + p.prefix.ead_addr.frag_id = es_frag->rd_id; + bgp_evpn_mh_route_delete(bgp, es, NULL, es_frag, &p); + } + + /* We don't attempt to coalesce frags that may not be full. Instead we + * only free up the frag when it is completely empty. + */ + bgp_evpn_es_frag_free_unused(es_frag); +} + +/* Link the ES-EVIs to one of the ES fragments */ +static void bgp_evpn_es_frag_evi_update_all(struct bgp_evpn_es *es, bool add) +{ + struct listnode *node; + struct bgp_evpn_es_evi *es_evi; + + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, node, es_evi)) { + if (add) + bgp_evpn_es_frag_evi_add(es_evi); + else + bgp_evpn_es_frag_evi_del(es_evi, false); + } +} + /* compare ES-IDs for the global ES RB tree */ static int bgp_es_rb_cmp(const struct bgp_evpn_es *es1, const struct bgp_evpn_es *es2) @@ -1651,10 +1921,7 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi) es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN); /* Add to rb_tree */ - if (RB_INSERT(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es)) { - XFREE(MTYPE_BGP_EVPN_ES, es); - return NULL; - } + RB_INSERT(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es); /* Initialise the ES-EVI list */ es->es_evi_list = list_new(); @@ -1669,6 +1936,8 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi) listset_app_node_mem(es->macip_evi_path_list); es->macip_global_path_list = list_new(); listset_app_node_mem(es->macip_global_path_list); + es->es_frag_list = list_new(); + listset_app_node_mem(es->es_frag_list); QOBJ_REG(es, bgp_evpn_es); @@ -1695,6 +1964,7 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) list_delete(&es->es_vtep_list); list_delete(&es->macip_evi_path_list); list_delete(&es->macip_global_path_list); + list_delete(&es->es_frag_list); bgp_table_unlock(es->route_table); /* remove the entry from various databases */ @@ -1714,7 +1984,6 @@ static inline bool bgp_evpn_is_es_local_and_non_bypass(struct bgp_evpn_es *es) /* init local info associated with the ES */ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es) { - char buf[BGP_EVPN_PREFIX_RD_LEN]; bool old_is_local; bool is_local; @@ -1727,12 +1996,12 @@ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es) listnode_init(&es->es_listnode, es); listnode_add(bgp_mh_info->local_es_list, &es->es_listnode); - /* auto derive RD for this es */ - bf_assign_index(bm->rd_idspace, es->rd_id); - es->prd.family = AF_UNSPEC; - es->prd.prefixlen = 64; - snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, es->rd_id); - (void)str2prefix_rd(buf, &es->prd); + /* setup the first ES fragment; more fragments may be allocated based + * on the the number of EVI entries + */ + es->es_base_frag = bgp_evpn_es_frag_new(es); + /* distribute ES-EVIs to one or more ES fragments */ + bgp_evpn_es_frag_evi_update_all(es, true); is_local = bgp_evpn_is_es_local_and_non_bypass(es); if (old_is_local != is_local) @@ -1748,6 +2017,11 @@ static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es, bool finish) if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) return; + /* clear the es frag references and free them up */ + bgp_evpn_es_frag_evi_update_all(es, false); + es->es_base_frag = NULL; + bgp_evpn_es_frag_free_all(es); + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL); @@ -1758,8 +2032,6 @@ static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es, bool finish) /* remove from the ES local list */ list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode); - bf_release_index(bm->rd_idspace, es->rd_id); - bgp_evpn_es_free(es, __func__); } @@ -1920,14 +2192,7 @@ static void bgp_evpn_local_es_deactivate(struct bgp *bgp, bgp_evpn_local_type1_evi_route_del(bgp, es); /* withdraw EAD-ES */ - build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, - &es->esi, es->originator_ip); - ret = bgp_evpn_type1_es_route_delete(bgp, es, &p); - if (ret) { - flog_err(EC_BGP_EVPN_ROUTE_DELETE, - "%u failed to delete type-1 route for ESI %s", - bgp->vrf_id, es->esi_str); - } + bgp_evpn_ead_es_route_delete(bgp, es); bgp_evpn_mac_update_on_es_oper_chg(es); } @@ -1973,9 +2238,7 @@ static void bgp_evpn_local_es_activate(struct bgp *bgp, struct bgp_evpn_es *es, bgp_evpn_local_type1_evi_route_add(bgp, es); /* generate EAD-ES */ - build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi, - es->originator_ip); - (void)bgp_evpn_type1_route_update(bgp, es, NULL, &p); + bgp_evpn_ead_es_route_update(bgp, es); } bgp_evpn_mac_update_on_es_oper_chg(es); @@ -2158,6 +2421,41 @@ int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, return 0; } +static void bgp_evpn_es_json_frag_fill(json_object *json_frags, + struct bgp_evpn_es *es) +{ + json_object *json_frag; + char buf1[RD_ADDRSTRLEN]; + struct listnode *node; + struct bgp_evpn_es_frag *es_frag; + + for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { + json_frag = json_object_new_object(); + + json_object_string_add( + json_frag, "rd", + prefix_rd2str(&es_frag->prd, buf1, sizeof(buf1))); + json_object_int_add(json_frag, "eviCount", + listcount(es_frag->es_evi_frag_list)); + + json_object_array_add(json_frags, json_frag); + } +} + +static void bgp_evpn_es_frag_show_detail(struct vty *vty, + struct bgp_evpn_es *es) +{ + struct listnode *node; + char buf1[RD_ADDRSTRLEN]; + struct bgp_evpn_es_frag *es_frag; + + for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { + vty_out(vty, " %s EVIs: %d\n", + prefix_rd2str(&es_frag->prd, buf1, sizeof(buf1)), + listcount(es_frag->es_evi_frag_list)); + } +} + static char *bgp_evpn_es_vteps_str(char *vtep_str, struct bgp_evpn_es *es, uint8_t vtep_str_size) { @@ -2267,9 +2565,11 @@ static void bgp_evpn_es_show_entry(struct vty *vty, json_object *json_types; json_object_string_add(json, "esi", es->esi_str); - json_object_string_add(json, "rd", - prefix_rd2str(&es->prd, buf1, - sizeof(buf1))); + if (es->es_base_frag) + json_object_string_add( + json, "rd", + prefix_rd2str(&es->es_base_frag->prd, buf1, + sizeof(buf1))); if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) { json_types = json_object_new_array(); @@ -2306,8 +2606,9 @@ static void bgp_evpn_es_show_entry(struct vty *vty, bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str)); - if (es->flags & BGP_EVPNES_LOCAL) - prefix_rd2str(&es->prd, buf1, sizeof(buf1)); + if (es->es_base_frag) + prefix_rd2str(&es->es_base_frag->prd, buf1, + sizeof(buf1)); else strlcpy(buf1, "-", sizeof(buf1)); @@ -2324,6 +2625,7 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, json_object *json_flags; json_object *json_incons; json_object *json_vteps; + json_object *json_frags; struct listnode *node; struct bgp_evpn_es_vtep *es_vtep; @@ -2362,6 +2664,11 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, } json_object_object_add(json, "vteps", json_vteps); } + if (listcount(es->es_frag_list)) { + json_frags = json_object_new_array(); + bgp_evpn_es_json_frag_fill(json_frags, es); + json_object_object_add(json, "fragments", json_frags); + } if (es->inconsistencies) { json_incons = json_object_new_array(); if (es->inconsistencies & BGP_EVPNES_INCONS_VTEP_LIST) @@ -2381,8 +2688,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, if (es->flags & BGP_EVPNES_REMOTE) strlcat(type_str, "R", sizeof(type_str)); - if (es->flags & BGP_EVPNES_LOCAL) - prefix_rd2str(&es->prd, buf1, sizeof(buf1)); + if (es->es_base_frag) + prefix_rd2str(&es->es_base_frag->prd, buf1, + sizeof(buf1)); else strlcpy(buf1, "-", sizeof(buf1)); @@ -2415,6 +2723,10 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, } vty_out(vty, " Inconsistencies: %s\n", incons_str); + if (listcount(es->es_frag_list)) { + vty_out(vty, " Fragments:\n"); + bgp_evpn_es_frag_show_detail(vty, es); + } if (listcount(es->es_vtep_list)) { vty_out(vty, " VTEPs:\n"); bgp_evpn_es_vteps_show_detail(vty, es); @@ -2716,10 +3028,7 @@ static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_create(struct bgp_evpn_es *es, es_vrf->bgp_vrf = bgp_vrf; /* insert into the VRF-ESI rb tree */ - if (RB_INSERT(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf)) { - XFREE(MTYPE_BGP_EVPN_ES_VRF, es_vrf); - return NULL; - } + RB_INSERT(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf); /* add to the ES's VRF list */ listnode_init(&es_vrf->es_listnode, es_vrf); @@ -3259,10 +3568,7 @@ static struct bgp_evpn_es_evi *bgp_evpn_es_evi_new(struct bgp_evpn_es *es, es_evi->es_evi_vtep_list->cmp = bgp_evpn_es_evi_vtep_cmp; /* insert into the VNI-ESI rb tree */ - if (RB_INSERT(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi)) { - XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi); - return NULL; - } + RB_INSERT(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi); /* add to the ES's VNI list */ listnode_init(&es_evi->es_listnode, es_evi); @@ -3287,7 +3593,7 @@ bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) */ if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE)) return es_evi; - + bgp_evpn_es_frag_evi_del(es_evi, false); bgp_evpn_es_vrf_deref(es_evi); /* remove from the ES's VNI list */ @@ -3316,6 +3622,7 @@ static void bgp_evpn_es_evi_local_info_set(struct bgp_evpn_es_evi *es_evi) SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL); listnode_init(&es_evi->l2vni_listnode, es_evi); listnode_add(vpn->local_es_evi_list, &es_evi->l2vni_listnode); + bgp_evpn_es_frag_evi_add(es_evi); } /* clear any local info associated with the ES-EVI */ @@ -3374,24 +3681,19 @@ bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) bgp = bgp_get_evpn(); + /* remove the es_evi from the es_frag before sending the update */ + bgp_evpn_es_frag_evi_del(es_evi, true); if (bgp) { /* update EAD-ES with new list of VNIs */ - if (bgp_evpn_local_es_is_active(es)) { - build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, - &es->esi, es->originator_ip); - if (bgp_evpn_type1_route_update(bgp, es, NULL, &p)) - flog_err(EC_BGP_EVPN_ROUTE_CREATE, - "%u: EAD-ES route update failure for ESI %s VNI %u", - bgp->vrf_id, es->esi_str, - es_evi->vpn->vni); - } + if (bgp_evpn_local_es_is_active(es)) + bgp_evpn_ead_es_route_update(bgp, es); /* withdraw and delete EAD-EVI */ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) { build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, &es->esi, es->originator_ip); - if (bgp_evpn_type1_evi_route_delete(bgp, - es, es_evi->vpn, &p)) + if (bgp_evpn_ead_evi_route_delete(bgp, es, es_evi->vpn, + &p)) flog_err(EC_BGP_EVPN_ROUTE_DELETE, "%u: EAD-EVI route deletion failure for ESI %s VNI %u", bgp->vrf_id, es->esi_str, @@ -3491,21 +3793,12 @@ int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni) if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) { build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, &es->esi, es->originator_ip); - if (bgp_evpn_type1_route_update(bgp, es, vpn, &p)) - flog_err(EC_BGP_EVPN_ROUTE_CREATE, - "%u: EAD-EVI route creation failure for ESI %s VNI %u", - bgp->vrf_id, es->esi_str, vni); + bgp_evpn_ead_evi_route_update(bgp, es, vpn, &p); } /* update EAD-ES */ - build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, - &es->esi, es->originator_ip); - if (bgp_evpn_local_es_is_active(es)) { - if (bgp_evpn_type1_route_update(bgp, es, NULL, &p)) - flog_err(EC_BGP_EVPN_ROUTE_CREATE, - "%u: EAD-ES route creation failure for ESI %s VNI %u", - bgp->vrf_id, es->esi_str, vni); - } + if (bgp_evpn_local_es_is_active(es)) + bgp_evpn_ead_es_route_update(bgp, es); return 0; } @@ -3782,11 +4075,18 @@ static void bgp_evpn_es_evi_show_entry(struct vty *vty, static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty, struct bgp_evpn_es_evi *es_evi, json_object *json) { + char buf1[RD_ADDRSTRLEN]; + if (json) { json_object *json_flags; /* Add the "brief" info first */ bgp_evpn_es_evi_show_entry(vty, es_evi, json); + if (es_evi->es_frag) + json_object_string_add( + json, "esFragmentRd", + prefix_rd2str(&es_evi->es_frag->prd, buf1, + sizeof(buf1))); if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) { json_flags = json_object_new_array(); json_array_string_add(json_flags, "es-vtep-mismatch"); @@ -3809,6 +4109,10 @@ static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty, vty_out(vty, "VNI: %d ESI: %s\n", es_evi->vpn->vni, es_evi->es->esi_str); vty_out(vty, " Type: %s\n", type_str); + if (es_evi->es_frag) + vty_out(vty, " ES fragment RD: %s\n", + prefix_rd2str(&es_evi->es_frag->prd, buf1, + sizeof(buf1))); vty_out(vty, " Inconsistencies: %s\n", (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) ? "es-vtep-mismatch":"-"); @@ -4664,6 +4968,10 @@ void bgp_evpn_mh_init(void) bgp_mh_info->ead_evi_rx = BGP_EVPN_MH_EAD_EVI_RX_DEF; bgp_mh_info->ead_evi_tx = BGP_EVPN_MH_EAD_EVI_TX_DEF; + bgp_mh_info->ead_es_export_rtl = list_new(); + bgp_mh_info->ead_es_export_rtl->cmp = + (int (*)(void *, void *))bgp_evpn_route_target_cmp; + bgp_mh_info->ead_es_export_rtl->del = bgp_evpn_xxport_delete_ecomm; /* config knobs - XXX add cli to control it */ bgp_mh_info->ead_evi_adv_for_down_links = true; @@ -4672,6 +4980,7 @@ void bgp_evpn_mh_init(void) bgp_mh_info->host_routes_use_l3nhg = BGP_EVPN_MH_USE_ES_L3NHG_DEF; bgp_mh_info->suppress_l3_ecomm_on_inactive_es = true; bgp_mh_info->bgp_evpn_nh_setup = true; + bgp_mh_info->evi_per_es_frag = BGP_EVPN_MAX_EVI_PER_ES_FRAG; memset(&zero_esi_buf, 0, sizeof(esi_t)); } @@ -4692,6 +5001,7 @@ void bgp_evpn_mh_finish(void) thread_cancel(&bgp_mh_info->t_cons_check); list_delete(&bgp_mh_info->local_es_list); list_delete(&bgp_mh_info->pend_es_list); + list_delete(&bgp_mh_info->ead_es_export_rtl); XFREE(MTYPE_BGP_EVPN_MH_INFO, bgp_mh_info); } diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index 37a46c2f0e..d9e2e72e4f 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -35,6 +35,28 @@ #define BGP_EVPN_MH_USE_ES_L3NHG_DEF true +/* XXX - tune this */ +#define BGP_EVPN_MAX_EVI_PER_ES_FRAG 128 + +/* An ES can result in multiple EAD-per-ES route. Each EAD fragment is + * associated with an unique RD + */ +struct bgp_evpn_es_frag { + /* frag is associated with a parent ES */ + struct bgp_evpn_es *es; + + /* Id for deriving the RD automatically for this ES fragment */ + uint16_t rd_id; + /* RD for this ES fragment */ + struct prefix_rd prd; + + /* Memory used for linking bgp_evpn_es_rd to bgp_evpn_es->rd_list */ + struct listnode es_listnode; + + /* List of ES-EVIs associated with this fragment */ + struct list *es_evi_frag_list; +}; + /* Ethernet Segment entry - * - Local and remote ESs are maintained in a global RB tree, * bgp_mh_info->es_rb_tree using ESI as key @@ -79,11 +101,9 @@ struct bgp_evpn_es { */ struct listnode pend_es_listnode; - /* [EVPNES_LOCAL] Id for deriving the RD automatically for this ESI */ - uint16_t rd_id; - - /* [EVPNES_LOCAL] RD for this ES */ - struct prefix_rd prd; + /* [EVPNES_LOCAL] List of RDs for this ES (bgp_evpn_es_rd) */ + struct list *es_frag_list; + struct bgp_evpn_es_frag *es_base_frag; /* [EVPNES_LOCAL] originator ip address */ struct in_addr originator_ip; @@ -203,6 +223,8 @@ struct bgp_evpn_es_vrf { */ struct bgp_evpn_es_evi { struct bgp_evpn_es *es; + /* Only applicableif EVI_LOCAL */ + struct bgp_evpn_es_frag *es_frag; struct bgpevpn *vpn; /* ES-EVI flags */ @@ -224,6 +246,10 @@ struct bgp_evpn_es_evi { */ struct listnode es_listnode; + /* memory used for linking the es_evi to + * es_evi->es_frag->es_evi_frag_list + */ + struct listnode es_frag_listnode; /* list of PEs (bgp_evpn_es_evi_vtep) attached to the ES for this VNI */ struct list *es_evi_vtep_list; @@ -310,6 +336,16 @@ struct bgp_evpn_mh_info { bool suppress_l3_ecomm_on_inactive_es; /* Setup EVPN PE nexthops and their RMAC in bgpd */ bool bgp_evpn_nh_setup; + + /* If global export-rts are configured that is used for sending + * sending the ead-per-es route instead of the L2-VNI(s) RTs + */ + struct list *ead_es_export_rtl; + + /* Number of EVIs in an ES fragment - used of EAD-per-ES route + * construction + */ + uint32_t evi_per_es_frag; }; /****************************************************************************/ @@ -434,5 +470,7 @@ extern void bgp_evpn_nh_finish(struct bgp *bgp_vrf); extern void bgp_evpn_nh_show(struct vty *vty, bool uj); extern void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi); extern void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi); +extern void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp, + struct ecommunity *ecom, bool del); #endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index ce2191f99c..fd30cc2db3 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -532,6 +532,7 @@ static inline void evpn_type1_prefix_global_copy(struct prefix_evpn *global_p, memcpy(global_p, vni_p, sizeof(*global_p)); global_p->prefix.ead_addr.ip.ipa_type = 0; global_p->prefix.ead_addr.ip.ipaddr_v4.s_addr = INADDR_ANY; + global_p->prefix.ead_addr.frag_id = 0; } /* EAD prefix in the global table doesn't include the VTEP-IP so @@ -656,4 +657,7 @@ extern int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf, struct bgp_path_info *pi, int install); extern void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import); +extern void bgp_evpn_xxport_delete_ecomm(void *val); +extern int bgp_evpn_route_target_cmp(struct ecommunity *ecom1, + struct ecommunity *ecom2); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 7ddf159844..4da3fa8f3b 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -358,7 +358,7 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n"); vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n"); vty_out(vty, - "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]\n"); + "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]:[Frag-id]\n"); vty_out(vty, "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n"); vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); @@ -2712,7 +2712,7 @@ 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-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]\n"); + "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]:[Frag-id]\n"); vty_out(vty, "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]\n"); vty_out(vty, @@ -5992,6 +5992,87 @@ DEFUN (no_bgp_evpn_vrf_rt, return CMD_SUCCESS; } +DEFPY(bgp_evpn_ead_ess_frag_evi_limit, bgp_evpn_ead_es_frag_evi_limit_cmd, + "[no$no] ead-es-frag evi-limit (1-1000)$limit", + NO_STR + "EAD ES fragment config\n" + "EVIs per-fragment\n" + "limit\n") +{ + bgp_mh_info->evi_per_es_frag = + no ? BGP_EVPN_MAX_EVI_PER_ES_FRAG : limit; + + return CMD_SUCCESS; +} + +DEFUN(bgp_evpn_ead_es_rt, bgp_evpn_ead_es_rt_cmd, + "ead-es-route-target export RT", + "EAD ES Route Target\n" + "export\n" + "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + struct ecommunity *ecomadd = NULL; + + if (!bgp) + return CMD_WARNING; + + if (!EVPN_ENABLED(bgp)) { + vty_out(vty, "This command is only supported under EVPN VRF\n"); + return CMD_WARNING; + } + + /* Add/update the export route-target */ + ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomadd); + + /* Do nothing if we already have this export route-target */ + if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl, + ecomadd)) + bgp_evpn_mh_config_ead_export_rt(bgp, ecomadd, false); + + return CMD_SUCCESS; +} + +DEFUN(no_bgp_evpn_ead_es_rt, no_bgp_evpn_ead_es_rt_cmd, + "no ead-es-route-target export RT", + NO_STR + "EAD ES Route Target\n" + "export\n" EVPN_ASN_IP_HELP_STR) +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + struct ecommunity *ecomdel = NULL; + + if (!bgp) + return CMD_WARNING; + + if (!EVPN_ENABLED(bgp)) { + vty_out(vty, "This command is only supported under EVPN VRF\n"); + return CMD_WARNING; + } + + ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomdel) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomdel); + + if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl, + ecomdel)) { + vty_out(vty, + "%% RT specified does not match EAD-ES RT configuration\n"); + return CMD_WARNING; + } + bgp_evpn_mh_config_ead_export_rt(bgp, ecomdel, true); + + return CMD_SUCCESS; +} + DEFUN (bgp_evpn_vni_rt, bgp_evpn_vni_rt_cmd, "route-target <both|import|export> RT", @@ -6258,6 +6339,10 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->resolve_overlay_index) vty_out(vty, " enable-resolve-overlay-index\n"); + if (bgp_mh_info->evi_per_es_frag != BGP_EVPN_MAX_EVI_PER_ES_FRAG) + vty_out(vty, " ead-es-frag evi-limit %u\n", + bgp_mh_info->evi_per_es_frag); + if (bgp_mh_info->host_routes_use_l3nhg != BGP_EVPN_MH_USE_ES_L3NHG_DEF) { if (bgp_mh_info->host_routes_use_l3nhg) @@ -6321,6 +6406,23 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, vty_out(vty, " advertise ipv4 unicast gateway-ip\n"); } + /* EAD ES export route-target */ + if (listcount(bgp_mh_info->ead_es_export_rtl)) { + struct ecommunity *ecom; + char *ecom_str; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl, node, + ecom)) { + + ecom_str = ecommunity_ecom2str( + ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " ead-es-route-target export %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } + } + if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) { if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) @@ -6506,6 +6608,9 @@ void bgp_ethernetvpn_init(void) install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_rt_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_ead_es_rt_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_frag_evi_limit_cmd); install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_svi_ip_vni_cmd); install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_default_gw_vni_cmd); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index ffb1ec162b..a994b536c4 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -126,6 +126,7 @@ DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_NH_INFO, "BGP EVPN PATH NH Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_NH, "BGP EVPN Nexthop"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI_VTEP, "BGP EVPN ES-EVI VTEP"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information"); +DEFINE_MTYPE(BGPD, BGP_EVPN_ES_FRAG, "BGP EVPN ES Fragment Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI, "BGP EVPN ES-per-EVI Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VRF, "BGP EVPN ES-per-VRF Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 63e7b40ef7..76b2f9f56a 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -115,6 +115,7 @@ DECLARE_MTYPE(LCOMMUNITY_VAL); DECLARE_MTYPE(BGP_EVPN_MH_INFO); DECLARE_MTYPE(BGP_EVPN_ES); +DECLARE_MTYPE(BGP_EVPN_ES_FRAG); DECLARE_MTYPE(BGP_EVPN_ES_EVI); DECLARE_MTYPE(BGP_EVPN_ES_VRF); DECLARE_MTYPE(BGP_EVPN_ES_VTEP); diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 7d7335a23f..b9733cd522 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2935,6 +2935,8 @@ Example configuration: exit-address-family ! +.. _bgp-evpn-mh: + EVPN Multihoming ^^^^^^^^^^^^^^^^ @@ -3045,6 +3047,55 @@ following zebra command - .. clicmd:: evpn mh startup-delay (0-3600) +EAD-per-ES fragmentation +"""""""""""""""""""""""" +The EAD-per-ES route carries the EVI route targets for all the broadcast +domains associated with the ES. Depending on the EVI scale the EAD-per-ES +route maybe fragmented. + +The number of EVIs per-EAD route can be configured via the following +BGP command - + +.. index:: [no] ead-es-frag evi-limit(1-1000) +.. clicmd:: [no] ead-es-frag evi-limit(1-1000) + +Sample Configuration +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: frr + + ! + router bgp 5556 + ! + address-family l2vpn evpn + ead-es-frag evi-limit 200 + exit-address-family + ! + ! + +EAD-per-ES route-target +""""""""""""""""""""""" +The EAD-per-ES route by default carries all the EVI route targets. Depending +on EVI scale that can result in route fragmentation. In some cases it maybe +necessary to avoid this fragmentation and that can be done via the following +workaround - +1. Configure a single supplementary BD per-tenant VRF. This SBD needs to +be provisioned on all EVPN PEs associated with the tenant-VRF. +2. Config the SBD's RT as the EAD-per-ES route's export RT. + +Sample Configuration +^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: frr + + ! + router bgp 5556 + ! + address-family l2vpn evpn + ead-es-route-target export 5556:1001 + ead-es-route-target export 5556:1004 + ead-es-route-target export 5556:1008 + exit-address-family + ! + Support with VRF network namespace backend ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It is possible to separate overlay networks contained in VXLAN interfaces from diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 30363dfdf6..dcea709503 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -509,7 +509,20 @@ cause great confusion. Display information about RP's that are configured on this router. - You can filter the output by specifying an arbitrary group. + You can filter the output by specifying an arbitrary group range. + + .. code-block:: frr + + # show ip pim rp-info + RP address group/prefix-list OIF I am RP Source Group-Type + 192.168.10.123 225.0.0.0/24 eth2 yes Static ASM + 192.168.10.123 239.0.0.0/8 eth2 yes Static ASM + 192.168.10.123 239.4.0.0/24 eth2 yes Static SSM + + # show ip pim rp-info 239.4.0.0/25 + RP address group/prefix-list OIF I am RP Source Group-Type + 192.168.10.123 239.0.0.0/8 eth2 yes Static ASM + 192.168.10.123 239.4.0.0/24 eth2 yes Static SSM .. clicmd:: show ip pim rpf @@ -611,6 +624,11 @@ the config was written out. This turns on debugging for PIM nexthop tracking. It will display information about RPF lookups and information about when a nexthop changes. +.. clicmd:: debug pim nht detail + + This turns on debugging for PIM nexthop in detail. This is not enabled + by default. + .. clicmd:: debug pim packet-dump This turns on an extraordinary amount of data. Each pim packet sent and diff --git a/lib/prefix.c b/lib/prefix.c index 89c5be8f38..4db0c2478b 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -915,12 +915,13 @@ static const char *prefixevpn_ead2str(const struct prefix_evpn *p, char *str, char buf1[INET6_ADDRSTRLEN]; family = IS_IPADDR_V4(&p->prefix.ead_addr.ip) ? AF_INET : AF_INET6; - snprintf(str, size, "[%d]:[%u]:[%s]:[%d]:[%s]", p->prefix.route_type, - p->prefix.ead_addr.eth_tag, + snprintf(str, size, "[%d]:[%u]:[%s]:[%d]:[%s]:[%u]", + p->prefix.route_type, p->prefix.ead_addr.eth_tag, esi_to_str(&p->prefix.ead_addr.esi, buf, sizeof(buf)), (family == AF_INET) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN, inet_ntop(family, &p->prefix.ead_addr.ip.ipaddr_v4, buf1, - sizeof(buf1))); + sizeof(buf1)), + p->prefix.ead_addr.frag_id); return str; } diff --git a/lib/prefix.h b/lib/prefix.h index b3545a72b4..816a1517e1 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -95,6 +95,7 @@ struct evpn_ead_addr { esi_t esi; uint32_t eth_tag; struct ipaddr ip; + uint16_t frag_id; }; struct evpn_macip_addr { diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 2a6d03fb91..86d179fe39 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1655,6 +1655,10 @@ static void pim_show_interface_traffic_single(struct pim_instance *pim, pim_ifp->pim_ifstat_join_recv); json_object_int_add(json_row, "joinTx", pim_ifp->pim_ifstat_join_send); + json_object_int_add(json_row, "pruneRx", + pim_ifp->pim_ifstat_prune_recv); + json_object_int_add(json_row, "pruneTx", + pim_ifp->pim_ifstat_prune_send); json_object_int_add(json_row, "registerRx", pim_ifp->pim_ifstat_reg_recv); json_object_int_add(json_row, "registerTx", @@ -6056,6 +6060,10 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, /* Find the inbound interface nested under the source, * create it if it doesn't exist */ + json_object_string_add(json_source, "source", + src_str); + json_object_string_add(json_source, "group", + grp_str); json_object_int_add(json_source, "installed", c_oil->installed); json_object_int_add(json_source, "refCount", @@ -8380,6 +8388,7 @@ DEFUN (debug_pim, PIM_DO_DEBUG_MSDP_EVENTS; PIM_DO_DEBUG_MSDP_PACKETS; PIM_DO_DEBUG_BSM; + PIM_DO_DEBUG_VXLAN; return CMD_SUCCESS; } @@ -8399,6 +8408,7 @@ DEFUN (no_debug_pim, PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND; PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV; PIM_DONT_DEBUG_BSM; + PIM_DONT_DEBUG_VXLAN; return CMD_SUCCESS; } @@ -8426,6 +8436,31 @@ DEFUN (no_debug_pim_nht, return CMD_SUCCESS; } +DEFUN (debug_pim_nht_det, + debug_pim_nht_det_cmd, + "debug pim nht detail", + DEBUG_STR + DEBUG_PIM_STR + "Nexthop Tracking\n" + "Detailed Information\n") +{ + PIM_DO_DEBUG_PIM_NHT_DETAIL; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_nht_det, + no_debug_pim_nht_det_cmd, + "no debug pim nht detail", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + "Nexthop Tracking\n" + "Detailed Information\n") +{ + PIM_DONT_DEBUG_PIM_NHT_DETAIL; + return CMD_SUCCESS; +} + DEFUN (debug_pim_nht_rp, debug_pim_nht_rp_cmd, "debug pim nht rp", @@ -10692,6 +10727,8 @@ void pim_cmd_init(void) install_element(ENABLE_NODE, &no_debug_pim_cmd); install_element(ENABLE_NODE, &debug_pim_nht_cmd); install_element(ENABLE_NODE, &no_debug_pim_nht_cmd); + install_element(ENABLE_NODE, &debug_pim_nht_det_cmd); + install_element(ENABLE_NODE, &no_debug_pim_nht_det_cmd); install_element(ENABLE_NODE, &debug_pim_nht_rp_cmd); install_element(ENABLE_NODE, &no_debug_pim_nht_rp_cmd); install_element(ENABLE_NODE, &debug_pim_events_cmd); @@ -10743,6 +10780,8 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &no_debug_pim_cmd); install_element(CONFIG_NODE, &debug_pim_nht_cmd); install_element(CONFIG_NODE, &no_debug_pim_nht_cmd); + install_element(CONFIG_NODE, &debug_pim_nht_det_cmd); + install_element(CONFIG_NODE, &no_debug_pim_nht_det_cmd); install_element(CONFIG_NODE, &debug_pim_nht_rp_cmd); install_element(CONFIG_NODE, &no_debug_pim_nht_rp_cmd); install_element(CONFIG_NODE, &debug_pim_events_cmd); diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c index 34cda25963..a949d2b126 100644 --- a/pimd/pim_igmpv2.c +++ b/pimd/pim_igmpv2.c @@ -54,7 +54,8 @@ void igmp_v2_send_query(struct gm_group *group, int fd, const char *ifname, /* max_resp_code must be non-zero else this will look like an IGMP v1 * query */ - max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec); + /* RFC 2236: 2.2. , v2's is equal to it */ + max_resp_code = query_max_response_time_dsec; assert(max_resp_code > 0); query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY; diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index f381a764cc..bce319b3ad 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -609,7 +609,7 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src); - if (!connected_src) { + if (!connected_src && !pim_addr_is_any(ip_hdr->ip_src)) { if (PIM_DEBUG_IGMP_PACKETS) { zlog_debug("Recv IGMP packet on interface: %s from a non-connected source: %pI4", ifp->name, &ip_hdr->ip_src); @@ -618,7 +618,8 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, } pim_ifp = ifp->info; - ifaddr = connected_src->u.prefix4; + ifaddr = connected_src ? connected_src->u.prefix4 + : pim_ifp->primary_address; igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr); @@ -631,8 +632,9 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, if (igmp) pim_igmp_packet(igmp, (char *)buf, buf_size); else if (PIM_DEBUG_IGMP_PACKETS) { - zlog_debug("No IGMP socket on interface: %s with connected source: %pFX", - ifp->name, connected_src); + zlog_debug( + "No IGMP socket on interface: %s with connected source: %pI4", + ifp->name, &ifaddr); } #endif } else if (ip_hdr->ip_p) { diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 35c8469090..3babddd877 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -121,7 +121,7 @@ static struct pim_nexthop_cache *pim_nht_get(struct pim_instance *pim, pnc = pim_nexthop_cache_add(pim, &rpf); pim_sendmsg_zebra_rnh(pim, zclient, pnc, ZEBRA_NEXTHOP_REGISTER); - if (PIM_DEBUG_PIM_NHT) + if (PIM_DEBUG_PIM_NHT_DETAIL) zlog_debug( "%s: NHT cache and zebra notification added for %pFX(%s)", __func__, addr, pim->vrf->name); @@ -894,7 +894,7 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, uint32_t num_nbrs = 0; pim_addr src_addr = pim_addr_from_prefix(src); - if (PIM_DEBUG_PIM_NHT) + if (PIM_DEBUG_PIM_NHT_DETAIL) zlog_debug("%s: Looking up: %pPA(%s), last lookup time: %lld", __func__, &src_addr, pim->vrf->name, nexthop->last_lookup_time); @@ -1049,7 +1049,7 @@ int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim, ifindex_t ifindex; pim_addr src_addr; - if (PIM_DEBUG_PIM_NHT) { + if (PIM_DEBUG_PIM_NHT_DETAIL) { src_addr = pim_addr_from_prefix(src); } diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 99727cf837..a552e77823 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -1175,7 +1175,7 @@ void pim_rp_show_information(struct pim_instance *pim, struct prefix *range, const char *group_type = pim_is_grp_ssm(pim, group) ? "SSM" : "ASM"; - if (range && !prefix_same(&rp_info->group, range)) + if (range && !prefix_match(&rp_info->group, range)) continue; if (rp_info->rp_src == RP_SRC_STATIC) diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 79deceee15..a7d5986655 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -166,6 +166,11 @@ int pim_debug_config_write(struct vty *vty) ++writes; } + if (PIM_DEBUG_PIM_NHT_DETAIL) { + vty_out(vty, "debug pim nht detail\n"); + ++writes; + } + return writes; } diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index c33e6032bf..c487f995e7 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -412,7 +412,7 @@ int zclient_lookup_nexthop(struct pim_instance *pim, num_ifindex = zclient_lookup_nexthop_once(pim, nexthop_tab, tab_size, addr); if (num_ifindex < 1) { - if (PIM_DEBUG_PIM_NHT) + if (PIM_DEBUG_PIM_NHT_DETAIL) zlog_debug( "%s: lookup=%d/%d: could not find nexthop ifindex for address %pPA(%s)", __func__, lookup, max_lookup, &addr, diff --git a/pimd/pimd.h b/pimd/pimd.h index d4eac58a29..1f7919ac6c 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -182,8 +182,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DEBUG_MSDP_PACKETS (router->debugs & PIM_MASK_MSDP_PACKETS) #define PIM_DEBUG_MSDP_INTERNAL (router->debugs & PIM_MASK_MSDP_INTERNAL) #define PIM_DEBUG_PIM_NHT (router->debugs & PIM_MASK_PIM_NHT) -#define PIM_DEBUG_PIM_NHT_DETAIL \ - (router->debugs & (PIM_MASK_PIM_NHT_DETAIL | PIM_MASK_PIM_NHT)) +#define PIM_DEBUG_PIM_NHT_DETAIL (router->debugs & PIM_MASK_PIM_NHT_DETAIL) #define PIM_DEBUG_PIM_NHT_RP (router->debugs & PIM_MASK_PIM_NHT_RP) #define PIM_DEBUG_MTRACE (router->debugs & PIM_MASK_MTRACE) #define PIM_DEBUG_VXLAN (router->debugs & PIM_MASK_VXLAN) @@ -228,6 +227,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DO_DEBUG_MSDP_PACKETS (router->debugs |= PIM_MASK_MSDP_PACKETS) #define PIM_DO_DEBUG_MSDP_INTERNAL (router->debugs |= PIM_MASK_MSDP_INTERNAL) #define PIM_DO_DEBUG_PIM_NHT (router->debugs |= PIM_MASK_PIM_NHT) +#define PIM_DO_DEBUG_PIM_NHT_DETAIL (router->debugs |= PIM_MASK_PIM_NHT_DETAIL) #define PIM_DO_DEBUG_PIM_NHT_RP (router->debugs |= PIM_MASK_PIM_NHT_RP) #define PIM_DO_DEBUG_MTRACE (router->debugs |= PIM_MASK_MTRACE) #define PIM_DO_DEBUG_VXLAN (router->debugs |= PIM_MASK_VXLAN) @@ -259,6 +259,8 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DONT_DEBUG_MSDP_PACKETS (router->debugs &= ~PIM_MASK_MSDP_PACKETS) #define PIM_DONT_DEBUG_MSDP_INTERNAL (router->debugs &= ~PIM_MASK_MSDP_INTERNAL) #define PIM_DONT_DEBUG_PIM_NHT (router->debugs &= ~PIM_MASK_PIM_NHT) +#define PIM_DONT_DEBUG_PIM_NHT_DETAIL \ + (router->debugs &= ~PIM_MASK_PIM_NHT_DETAIL) #define PIM_DONT_DEBUG_PIM_NHT_RP (router->debugs &= ~PIM_MASK_PIM_NHT_RP) #define PIM_DONT_DEBUG_MTRACE (router->debugs &= ~PIM_MASK_MTRACE) #define PIM_DONT_DEBUG_VXLAN (router->debugs &= ~PIM_MASK_VXLAN) diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py index e7a72ef33d..b3ff9d79ca 100644 --- a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py @@ -70,6 +70,8 @@ from lib.common_config import ( configure_vxlan, configure_brctl, create_interface_in_kernel, + kill_router_daemons, + start_router_daemons ) from lib.topolog import logger @@ -1755,6 +1757,221 @@ def test_route_map_operations_for_evpn_address_family_p1(request, attribute): write_test_footer(tc_name) +def test_evpn_address_family_with_graceful_restart_p0(request): + """ + Verify Graceful-restart function for EVPN address-family. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [{ + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED" + }] + }, + "r4":{ + "static_routes": [{ + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE" + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN" + }] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + step("Redistribute static in (IPv4 and IPv6) address-family " + "on Edge-1 for all VRFs.") + + input_dict_2={} + for dut in ["r3", "r4"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + if dut == "r3": + VRFS = ["RED"] + AS_NUM = [3] + if dut == "r4": + VRFS = ["BLUE", "GREEN"] + AS_NUM = [4, 4] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify on router Edge-1 that EVPN routes corresponding to " + "all VRFs are received from both routers DCG-1 and DCG-2") + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [{ + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED" + }] + }, + "r4":{ + "static_routes": [{ + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE" + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN" + }] + } + } + + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Configure DCG-2 as GR restarting node for EVPN session between" + " DCG-2 and EDGE-1, following by a session reset using 'clear bgp *'" + " command.") + + input_dict_gr = { + "d2": { + "bgp": + [ + { + "local_as": "200", + "graceful-restart": { + "graceful-restart": True, + } + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_gr) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify that DCG-2 changes it's role to GR-restarting router " + "and EDGE-1 becomes the GR-helper.") + + step("Kill BGPd daemon on DCG-2.") + kill_router_daemons(tgen, "d2", ["bgpd"]) + + step("Verify that EDGE-1 keep stale entries for EVPN RT-5 routes " + "received from DCG-2 before the restart.") + + for addr_type in ADDR_TYPES: + input_routes = { + "r4":{ + "static_routes": [{ + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE" + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN" + }] + } + } + result = verify_evpn_routes(tgen, topo, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that DCG-2 keeps BGP routes in Zebra until BGPd " + "comes up or end of 'rib-stale-time'") + + step("Start BGPd daemon on DCG-2.") + start_router_daemons(tgen, "d2", ["bgpd"]) + + step("Verify that EDGE-1 removed all the stale entries.") + for addr_type in ADDR_TYPES: + input_routes = { + "r4":{ + "static_routes": [{ + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE" + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN" + }] + } + } + result = verify_evpn_routes(tgen, topo, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that DCG-2 refresh zebra with EVPN routes. " + "(no significance of 'rib-stale-time'") + + for addr_type in ADDR_TYPES: + input_routes = { + "r4":{ + "static_routes": [{ + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE" + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN" + }] + } + } + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + @pytest.mark.parametrize("attribute", ["locPrf", "weight", "path"]) def test_bgp_attributes_for_evpn_address_family_p1(request, attribute): """ diff --git a/tests/topotests/lib/micronet_cli.py b/tests/topotests/lib/micronet_cli.py index 4292f47ce0..ef804f6dc2 100644 --- a/tests/topotests/lib/micronet_cli.py +++ b/tests/topotests/lib/micronet_cli.py @@ -110,7 +110,7 @@ def doline(unet, line, writef): args = oargs.split() if not args or (len(args) == 1 and args[0] == "*"): args = sorted(unet.hosts.keys()) - hosts = [unet.hosts[x] for x in args] + hosts = [unet.hosts[x] for x in args if x in unet.hosts] for host in hosts: if cmd == "t" or cmd == "term": host.run_in_window("bash", title="sh-%s" % host) @@ -250,6 +250,8 @@ def cli( prompt=None, background=True, ): + logger = logging.getLogger("cli-client") + if prompt is None: prompt = "unet> " diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py index 10c51a6784..3ec31f3fa8 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py @@ -2113,6 +2113,14 @@ def test_ospfv3_type5_summary_tc46_p0(request): tc_name, result ) + output = tgen.gears["r0"].vtysh_cmd( + "show ipv6 ospf6 database as-external json", isjson=True + ) + + output = tgen.gears["r1"].vtysh_cmd( + "show ipv6 ospf6 database as-external json", isjson=True + ) + result = verify_rib( tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False ) diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 7cde7a119e..d95f2d4be7 100644 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -191,7 +191,7 @@ daemon_stop() { [ -z "$fail" -a -z "$pid" ] && fail="pid file is empty" [ -n "$fail" ] || kill -0 "$pid" 2>/dev/null || fail="pid $pid not running" - if [ -n "$fail" ]; then + if [ -n "$fail" ] && [ "$2" != "--quiet" ]; then log_failure_msg "Cannot stop $dmninst: $fail" return 1 fi @@ -262,7 +262,7 @@ all_stop() { done for dmninst in $reversed; do - daemon_stop "$dmninst" & + daemon_stop "$dmninst" "$1" & pids="$pids $!" done for pid in $pids; do @@ -350,7 +350,7 @@ frrcommon_main() { start) all_start;; stop) all_stop;; restart) - all_stop + all_stop --quiet all_start ;; *) $cmd "$@";; diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index c6423dce99..f2cf9122fa 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2733,7 +2733,7 @@ netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) */ if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) && !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) - netlink_batch_add_msg( + return netlink_batch_add_msg( bth, ctx, netlink_delroute_msg_encoder, true); } else { diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 006513ac9e..1f3f66a68e 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -282,6 +282,12 @@ static int kernel_rtm(int cmd, const struct prefix *p, continue; /* Note any unexpected status returns */ + case ZEBRA_ERR_RTNOEXIST: + if (cmd != RTM_DELETE) + flog_err(EC_LIB_SYSTEM_CALL, + "%s: rtm_write() returned %d for command %d", + __func__, error, cmd); + break; default: flog_err( EC_LIB_SYSTEM_CALL, diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 9099c066b1..02eda4a438 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -113,10 +113,7 @@ static struct zebra_evpn_es_evi *zebra_evpn_es_evi_new(struct zebra_evpn_es *es, es_evi->zevpn = zevpn; /* insert into the EVPN-ESI rb tree */ - if (RB_INSERT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi)) { - XFREE(MTYPE_ZES_EVI, es_evi); - return NULL; - } + RB_INSERT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi); /* add to the ES's VNI list */ listnode_init(&es_evi->es_listnode, es_evi); @@ -1776,10 +1773,7 @@ static struct zebra_evpn_es *zebra_evpn_es_new(const esi_t *esi) esi_to_str(&es->esi, es->esi_str, sizeof(es->esi_str)); /* Add to rb_tree */ - if (RB_INSERT(zebra_es_rb_head, &zmh_info->es_rb_tree, es)) { - XFREE(MTYPE_ZES, es); - return NULL; - } + RB_INSERT(zebra_es_rb_head, &zmh_info->es_rb_tree, es); /* Initialise the ES-EVI list */ es->es_evi_list = list_new(); |
