summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_evpn.c40
-rw-r--r--bgpd/bgp_evpn_mh.c536
-rw-r--r--bgpd/bgp_evpn_mh.h48
-rw-r--r--bgpd/bgp_evpn_private.h4
-rw-r--r--bgpd/bgp_evpn_vty.c109
-rw-r--r--bgpd/bgp_memory.c1
-rw-r--r--bgpd/bgp_memory.h1
-rw-r--r--doc/user/bgp.rst51
-rw-r--r--doc/user/pim.rst20
-rw-r--r--lib/prefix.c7
-rw-r--r--lib/prefix.h1
-rw-r--r--pimd/pim_cmd.c39
-rw-r--r--pimd/pim_igmpv2.c3
-rw-r--r--pimd/pim_mroute.c10
-rw-r--r--pimd/pim_nht.c6
-rw-r--r--pimd/pim_rp.c2
-rw-r--r--pimd/pim_vty.c5
-rw-r--r--pimd/pim_zlookup.c2
-rw-r--r--pimd/pimd.h6
-rw-r--r--tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py217
-rw-r--r--tests/topotests/lib/micronet_cli.py4
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py8
-rw-r--r--tools/frrcommon.sh.in6
-rw-r--r--zebra/rt_netlink.c2
-rw-r--r--zebra/rt_socket.c6
-rw-r--r--zebra/zebra_evpn_mh.c10
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();