diff options
71 files changed, 2525 insertions, 588 deletions
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 1b176f8716..a583581030 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -243,6 +243,15 @@ struct attr { */ #define ATTR_ES_PEER_ROUTER (1 << 4) + /* These two flags are only set on L3 routes installed in a + * VRF as a result of EVPN MAC-IP route + * XXX - while splitting up per-family attrs these need to be + * classified as non-EVPN + */ +#define ATTR_ES_L3_NHG_USE (1 << 5) +#define ATTR_ES_L3_NHG_ACTIVE (1 << 6) +#define ATTR_ES_L3_NHG (ATTR_ES_L3_NHG_USE | ATTR_ES_L3_NHG_ACTIVE) + /* route tag */ route_tag_t tag; diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index cebc1c4d22..2d4fea413a 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -65,11 +65,6 @@ DEFINE_QOBJ_TYPE(bgp_evpn_es); * Static function declarations */ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); -static void bgp_evpn_update_type2_route_entry(struct bgp *bgp, - struct bgpevpn *vpn, - struct bgp_dest *dest, - struct bgp_path_info *local_pi, - const char *caller); static struct in_addr zero_vtep_ip; /* @@ -1602,8 +1597,8 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, } } - /* MAC-IP routes in the VNI route table are linked to the - * destination ES + /* local MAC-IP routes in the VNI table are linked to + * the destination ES */ if (route_change && vpn_rt && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)) @@ -1669,6 +1664,18 @@ static void evpn_cleanup_local_non_best_route(struct bgp *bgp, evpn_zebra_reinstall_best_route(bgp, vpn, dest); } +static inline bool bgp_evpn_route_add_l3_ecomm_ok(struct bgpevpn *vpn, + const struct prefix_evpn *p, + esi_t *esi) +{ + return p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE + && (is_evpn_prefix_ipaddr_v4(p) + || !IN6_IS_ADDR_LINKLOCAL( + &p->prefix.macip_addr.ip.ipaddr_v6)) + && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) + && bgpevpn_get_l3vni(vpn) && bgp_evpn_es_add_l3_ecomm_ok(esi); +} + /* * Create or update EVPN route (of type based on prefix) for specified VNI * and schedule for processing. @@ -1738,12 +1745,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, * IPv4 or IPv6 global addresses and we're advertising L3VNI with * these routes. */ - if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && - (is_evpn_prefix_ipaddr_v4(p) || - !IN6_IS_ADDR_LINKLOCAL(&p->prefix.macip_addr.ip.ipaddr_v6)) && - CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) && - bgpevpn_get_l3vni(vpn)) - add_l3_ecomm = 1; + add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok( + vpn, p, (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL); /* Set up extended community. */ build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); @@ -1930,11 +1933,10 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, return 0; } -static void bgp_evpn_update_type2_route_entry(struct bgp *bgp, - struct bgpevpn *vpn, - struct bgp_dest *dest, - struct bgp_path_info *local_pi, - const char *caller) +void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, + struct bgp_dest *dest, + struct bgp_path_info *local_pi, + const char *caller) { afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -1977,12 +1979,9 @@ static void bgp_evpn_update_type2_route_entry(struct bgp *bgp, /* Add L3 VNI RTs and RMAC for non IPv6 link-local if * using L3 VNI for type-2 routes also. */ - if ((is_evpn_prefix_ipaddr_v4(evp) || - !IN6_IS_ADDR_LINKLOCAL( - &evp->prefix.macip_addr.ip.ipaddr_v6)) && - CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) && - bgpevpn_get_l3vni(vpn)) - add_l3_ecomm = 1; + add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok( + vpn, evp, + (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL); /* Set up extended community. */ build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); @@ -2379,6 +2378,8 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, afi_t afi = 0; safi_t safi = 0; bool new_pi = false; + bool use_l3nhg = false; + bool is_l3nhg_active = false; memset(pp, 0, sizeof(struct prefix)); ip_prefix_from_evpn_prefix(evp, pp); @@ -2414,6 +2415,13 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, else attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg, + &is_l3nhg_active, NULL); + if (use_l3nhg) + attr.es_flags |= ATTR_ES_L3_NHG_USE; + if (is_l3nhg_active) + attr.es_flags |= ATTR_ES_L3_NHG_ACTIVE; + /* Check if route entry is already present. */ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->extra @@ -2454,6 +2462,9 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* as it is an importation, change nexthop */ bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); + /* Link path to evpn nexthop */ + bgp_evpn_path_nh_add(bgp_vrf, pi); + bgp_aggregate_increment(bgp_vrf, bgp_dest_get_prefix(dest), pi, afi, safi); @@ -2487,6 +2498,8 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, struct attr *attr_new; int ret; struct prefix_evpn ad_evp; + bool old_local_es = false; + bool new_local_es; /* EAD prefix in the global table doesn't include the VTEP-IP so * we need to create a different copy for the VNI @@ -2509,6 +2522,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* Create an info */ pi = bgp_create_evpn_bgp_path_info(parent_pi, dest, parent_pi->attr); + new_local_es = bgp_evpn_attr_is_local_es(pi->attr); } else { if (attrhash_cmp(pi->attr, parent_pi->attr) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { @@ -2527,17 +2541,29 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop)) SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); + old_local_es = bgp_evpn_attr_is_local_es(pi->attr); + new_local_es = bgp_evpn_attr_is_local_es(attr_new); + /* If ESI is different or if its type has changed we + * need to reinstall the path in zebra + */ + if ((old_local_es != new_local_es) + || memcmp(&pi->attr->esi, &attr_new->esi, + sizeof(attr_new->esi))) { + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("VNI %d path %pFX chg to %s es", + vpn->vni, &pi->net->p, + new_local_es ? "local" + : "non-local"); + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); + } + /* Unintern existing, set to new. */ bgp_attr_unintern(&pi->attr); pi->attr = attr_new; pi->uptime = bgp_clock(); } - /* MAC-IP routes in the VNI table are linked to the destination ES */ - if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) - bgp_evpn_path_es_link(pi, vpn->vni, - bgp_evpn_attr_get_esi(pi->attr)); - /* Perform route selection and update zebra, if required. */ ret = evpn_route_select_install(bgp, vpn, dest); @@ -2547,10 +2573,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, * from sync-path to remote-path) */ local_pi = bgp_evpn_route_get_local_path(bgp, dest); - if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr)) + if (local_pi && (old_local_es || new_local_es)) bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi, - __func__); - + __func__); bgp_dest_unlock_node(dest); return ret; @@ -2619,6 +2644,9 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* Mark entry for deletion */ bgp_path_info_delete(dest, pi); + /* Unlink path to evpn nexthop */ + bgp_evpn_path_nh_del(bgp_vrf, pi); + /* Perform route selection and update zebra, if required. */ bgp_process(bgp_vrf, dest, afi, safi); @@ -2853,11 +2881,11 @@ static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf, /* don't import hosts that are locally attached */ static inline bool -bgp_evpn_skip_vrf_import_of_local_es(const struct prefix_evpn *evp, +bgp_evpn_skip_vrf_import_of_local_es(struct bgp *bgp_vrf, + const struct prefix_evpn *evp, struct bgp_path_info *pi, int install) { esi_t *esi; - struct in_addr nh; if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { esi = bgp_evpn_attr_get_esi(pi->attr); @@ -2875,29 +2903,51 @@ bgp_evpn_skip_vrf_import_of_local_es(const struct prefix_evpn *evp, } return true; } + } + return false; +} - /* Don't import routes with ES as destination if the nexthop - * has not been advertised via the EAD-ES - */ - if (pi->attr) - nh = pi->attr->nexthop; +/* + * Install or uninstall a mac-ip route in the provided vrf if + * there is a rt match + */ +int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf, + struct bgp_path_info *pi, + int install) +{ + int ret = 0; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(pi->net); + + /* Consider "valid" remote routes applicable for + * this VRF. + */ + if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_NORMAL)) + return 0; + + if (is_route_matching_for_vrf(bgp_vrf, pi)) { + if (bgp_evpn_route_rmac_self_check(bgp_vrf, evp, pi)) + 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)) + ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi); else - nh.s_addr = INADDR_ANY; - if (install && !bgp_evpn_es_is_vtep_active(esi, nh)) { - if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) { - char esi_buf[ESI_STR_LEN]; + ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp, + pi); - zlog_debug( - "vrf %s of evpn prefix %pFX skipped, nh %pI4 inactive in es %s", - install ? "import" : "unimport", evp, - &nh, - esi_to_str(esi, esi_buf, - sizeof(esi_buf))); - } - return true; - } + if (ret) + flog_err(EC_BGP_EVPN_FAIL, + "Failed to %s EVPN %pFX route in VRF %s", + install ? "install" : "uninstall", evp, + vrf_id_to_name(bgp_vrf->vrf_id)); } - return false; + + return ret; } /* @@ -2949,46 +2999,10 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { - /* Consider "valid" remote routes applicable for - * this VRF. - */ - if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) - && pi->type == ZEBRA_ROUTE_BGP - && pi->sub_type == BGP_ROUTE_NORMAL)) - continue; - - /* don't import hosts that are locally attached - */ - if (bgp_evpn_skip_vrf_import_of_local_es( - evp, pi, install)) - continue; - - if (is_route_matching_for_vrf(bgp_vrf, pi)) { - if (bgp_evpn_route_rmac_self_check( - bgp_vrf, evp, pi)) - continue; - - if (install) - ret = install_evpn_route_entry_in_vrf( - bgp_vrf, evp, pi); - else - ret = uninstall_evpn_route_entry_in_vrf( - bgp_vrf, evp, pi); - - if (ret) { - flog_err( - EC_BGP_EVPN_FAIL, - "Failed to %s EVPN %pFX route in VRF %s", - install ? "install" - : "uninstall", - evp, - vrf_id_to_name( - bgp_vrf->vrf_id)); - bgp_dest_unlock_node(rd_dest); - bgp_dest_unlock_node(dest); - return ret; - } - } + ret = bgp_evpn_route_entry_install_if_vrf_match( + bgp_vrf, pi, install); + if (ret) + return ret; } } } @@ -3169,14 +3183,13 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi, || is_evpn_prefix_ipaddr_v6(evp))) return 0; - /* don't import hosts that are locally attached */ - if (bgp_evpn_skip_vrf_import_of_local_es(evp, pi, install)) - return 0; - for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) { int ret; - if (install) + /* don't import hosts that are locally attached */ + if (install + && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, + install)) ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi); else ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp, @@ -3291,6 +3304,13 @@ static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi, if (sub_type != ECOMMUNITY_ROUTE_TARGET) continue; + /* non-local MAC-IP routes in the global route table are linked + * to the destination ES + */ + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + bgp_evpn_path_es_link(pi, 0, + bgp_evpn_attr_get_esi(pi->attr)); + /* * macip routes (type-2) are imported into VNI and VRF tables. * IMET route is imported into VNI table. @@ -3370,6 +3390,18 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, true, true); } +void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import) +{ + struct bgp *bgp_evpn; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return; + + install_uninstall_evpn_route(bgp_evpn, AFI_L2VPN, SAFI_EVPN, + &pi->net->p, pi, import); +} + /* Import the pi into vrf routing tables */ void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import) { @@ -3723,7 +3755,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, if (attr) { STREAM_GET(&attr->esi, pkt, sizeof(esi_t)); - if (bgp_evpn_is_esi_local(&attr->esi)) + if (bgp_evpn_is_esi_local_and_non_bypass(&attr->esi)) attr->es_flags |= ATTR_ES_IS_LOCAL; else attr->es_flags &= ~ATTR_ES_IS_LOCAL; @@ -5776,11 +5808,14 @@ void bgp_evpn_init(struct bgp *bgp) /* Default BUM handling is to do head-end replication. */ bgp->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL; + + bgp_evpn_nh_init(bgp); } void bgp_evpn_vrf_delete(struct bgp *bgp_vrf) { bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + bgp_evpn_nh_finish(bgp_vrf); } /* diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 29d3d2c62f..83a6dd84c8 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -206,5 +206,4 @@ extern void bgp_evpn_init(struct bgp *bgp); extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); - #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 826de21b9d..868238ebdd 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -64,17 +64,20 @@ static void bgp_evpn_es_vtep_del(struct bgp *bgp, struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr); static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es); static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es); -static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi); +static struct bgp_evpn_es_evi * +bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi); static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es); static void bgp_evpn_l3nhg_update_on_vtep_chg(struct bgp_evpn_es *es); static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi); static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller); -static void -bgp_evpn_es_path_update_on_vtep_chg(struct bgp_evpn_es_vtep *es_vtep, - bool active); +static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info); +static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es, + bool is_local); esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; static int bgp_evpn_run_consistency_checks(struct thread *t); +static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info); +static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info); /****************************************************************************** * per-ES (Ethernet Segment) routing table @@ -1291,8 +1294,6 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp, * removed. */ bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es); - bgp_evpn_es_path_update_on_vtep_chg(es_vtep, new_active); - /* queue up the es for background consistency checks */ bgp_evpn_es_cons_checks_pend_add(es_vtep->es); } @@ -1368,59 +1369,60 @@ static void bgp_evpn_es_vtep_del(struct bgp *bgp, bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr); } -bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh) -{ - struct bgp_evpn_es *es; - struct bgp_evpn_es_vtep *es_vtep; - struct listnode *node = NULL; - bool rc = false; - - if (!memcmp(esi, zero_esi, sizeof(*esi)) || !nh.s_addr) - return true; - - es = bgp_evpn_es_find(esi); - if (!es) - return false; - - for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { - if (es_vtep->vtep_ip.s_addr == nh.s_addr) { - if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE)) - rc = true; - break; - } - } - return rc; -} - /********************** ES MAC-IP paths ************************************* - * MAC-IP routes in the VNI routing table are linked to the destination - * ES for efficient updates on ES changes (such as VTEP add/del). + * 1. Local MAC-IP routes in the VNI routing table are linked to the + * destination ES (macip_evi_path_list) for efficient updates on ES oper + * state changes. + * 2. Non-local MAC-IP routes in the global routing table are linked to + * the detination for efficient updates on - + * a. VTEP add/del - this results in a L3NHG update. + * b. ES-VRF add/del - this may result in the host route being migrated to + * L3NHG or vice versa (flat multipath list). ****************************************************************************/ -void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info) +static void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info) { bgp_evpn_path_es_unlink(es_info); XFREE(MTYPE_BGP_EVPN_PATH_ES_INFO, es_info); } +void bgp_evpn_path_mh_info_free(struct bgp_path_mh_info *mh_info) +{ + if (mh_info->es_info) + bgp_evpn_path_es_info_free(mh_info->es_info); + if (mh_info->nh_info) + bgp_evpn_path_nh_info_free(mh_info->nh_info); + XFREE(MTYPE_BGP_EVPN_PATH_MH_INFO, mh_info); +} + static struct bgp_path_es_info * bgp_evpn_path_es_info_new(struct bgp_path_info *pi, vni_t vni) { struct bgp_path_info_extra *e; + struct bgp_path_mh_info *mh_info; + struct bgp_path_es_info *es_info; e = bgp_path_info_extra_get(pi); + /* If mh_info doesn't exist allocate it */ + mh_info = e->mh_info; + if (!mh_info) + e->mh_info = mh_info = XCALLOC(MTYPE_BGP_EVPN_PATH_MH_INFO, + sizeof(struct bgp_path_mh_info)); + /* If es_info doesn't exist allocate it */ - if (!e->es_info) { - e->es_info = XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO, - sizeof(struct bgp_path_es_info)); - e->es_info->pi = pi; - e->es_info->vni = vni; + es_info = mh_info->es_info; + if (!es_info) { + mh_info->es_info = es_info = + XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO, + sizeof(struct bgp_path_es_info)); + es_info->vni = vni; + es_info->pi = pi; } - return e->es_info; + return es_info; } -void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) +static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) { struct bgp_evpn_es *es = es_info->es; struct bgp_path_info *pi; @@ -1433,7 +1435,13 @@ void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) zlog_debug("vni %u path %pFX unlinked from es %s", es_info->vni, &pi->net->p, es->esi_str); - list_delete_node(es->macip_path_list, &es_info->es_listnode); + if (es_info->vni) + list_delete_node(es->macip_evi_path_list, + &es_info->es_listnode); + else + list_delete_node(es->macip_global_path_list, + &es_info->es_listnode); + es_info->es = NULL; /* if there are no other references against the ES it @@ -1450,9 +1458,11 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) { struct bgp_path_es_info *es_info; struct bgp_evpn_es *es; - struct bgp *bgp_evpn = bgp_get_evpn(); + struct bgp *bgp_evpn; - es_info = pi->extra ? pi->extra->es_info : NULL; + es_info = (pi->extra && pi->extra->mh_info) + ? pi->extra->mh_info->es_info + : NULL; /* if the esi is zero just unlink the path from the old es */ if (!esi || !memcmp(esi, zero_esi, sizeof(*esi))) { if (es_info) @@ -1460,6 +1470,7 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) return; } + bgp_evpn = bgp_get_evpn(); if (!bgp_evpn) return; @@ -1486,43 +1497,59 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) /* link mac-ip path to the new destination ES */ es_info->es = es; listnode_init(&es_info->es_listnode, es_info); - listnode_add(es->macip_path_list, &es_info->es_listnode); + if (es_info->vni) + listnode_add(es->macip_evi_path_list, &es_info->es_listnode); + else + listnode_add(es->macip_global_path_list, &es_info->es_listnode); } +static bool bgp_evpn_is_macip_path(struct bgp_path_info *pi) +{ + struct prefix_evpn *evp; + + /* Only MAC-IP routes need to be linked (MAC-only routes can be + * skipped) as these lists are maintained for managing + * host routes in the tenant VRF + */ + evp = (struct prefix_evpn *)&pi->net->p; + return is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp); +} + +/* When a remote ES is added to a VRF, routes using that as + * a destination need to be migrated to a L3NHG or viceversa. + * This is done indirectly by re-attempting an install of the + * route in the associated VRFs. As a part of the VRF install use + * of l3 NHG is evaluated and this results in the + * attr.es_flag ATTR_ES_USE_L3_NHG being set or cleared. + */ static void -bgp_evpn_es_path_update_on_vtep_chg(struct bgp_evpn_es_vtep *es_vtep, - bool active) +bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf, + const char *reason) { struct listnode *node; struct bgp_path_es_info *es_info; struct bgp_path_info *pi; - struct bgp_path_info *parent_pi; - struct bgp_evpn_es *es = es_vtep->es; + struct bgp_evpn_es *es = es_vrf->es; + + if (!bgp_mh_info->host_routes_use_l3nhg) + return; if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) - zlog_debug("update paths linked to es %s on vtep chg", - es->esi_str); + zlog_debug("update paths linked to es %s on es-vrf %s %s", + es->esi_str, es_vrf->bgp_vrf->name, reason); - for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) { + for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) { pi = es_info->pi; - if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) - continue; - - if (pi->sub_type != BGP_ROUTE_IMPORTED) - continue; - parent_pi = pi->extra ? pi->extra->parent : NULL; - if (!parent_pi || !parent_pi->attr) - continue; - - if (es_vtep->vtep_ip.s_addr != parent_pi->attr->nexthop.s_addr) + if (!bgp_evpn_is_macip_path(pi)) continue; if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) zlog_debug( - "update path %pFX linked to es %s on vtep chg", - &parent_pi->net->p, es->esi_str); - bgp_evpn_import_route_in_vrfs(parent_pi, active ? 1 : 0); + "update path %pFX linked to es %s on vrf chg", + &pi->net->p, es->esi_str); + bgp_evpn_route_entry_install_if_vrf_match(es_vrf->bgp_vrf, pi, + 1); } } @@ -1579,8 +1606,10 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi) listset_app_node_mem(es->es_vrf_list); /* Initialise the route list used for efficient event handling */ - es->macip_path_list = list_new(); - listset_app_node_mem(es->macip_path_list); + es->macip_evi_path_list = list_new(); + 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); QOBJ_REG(es, bgp_evpn_es); @@ -1594,7 +1623,8 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi) static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) { if ((es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) - || listcount(es->macip_path_list)) + || listcount(es->macip_evi_path_list) + || listcount(es->macip_global_path_list)) return; if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) @@ -1604,7 +1634,8 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) list_delete(&es->es_evi_list); list_delete(&es->es_vrf_list); list_delete(&es->es_vtep_list); - list_delete(&es->macip_path_list); + list_delete(&es->macip_evi_path_list); + list_delete(&es->macip_global_path_list); bgp_table_unlock(es->route_table); /* remove the entry from various databases */ @@ -1615,15 +1646,25 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) XFREE(MTYPE_BGP_EVPN_ES, es); } +static inline bool bgp_evpn_is_es_local_and_non_bypass(struct bgp_evpn_es *es) +{ + return (es->flags & BGP_EVPNES_LOCAL) + && !(es->flags & BGP_EVPNES_BYPASS); +} + /* 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; if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) return; + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); SET_FLAG(es->flags, BGP_EVPNES_LOCAL); + listnode_init(&es->es_listnode, es); listnode_add(bgp_mh_info->local_es_list, &es->es_listnode); @@ -1633,16 +1674,28 @@ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es) es->prd.prefixlen = 64; snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, es->rd_id); (void)str2prefix_rd(buf, &es->prd); + + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (old_is_local != is_local) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); } /* clear any local info associated with the ES */ -static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es) +static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es, bool finish) { + bool old_is_local; + bool is_local; + if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) return; + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL); + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (!finish && (old_is_local != is_local)) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); + /* remove from the ES local list */ list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode); @@ -1664,10 +1717,127 @@ static void bgp_evpn_es_remote_info_re_eval(struct bgp_evpn_es *es) } } -static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es) +/* If ES is present and local it needs to be active/oper-up for + * including L3 EC + */ +bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi) { - return (es->flags & BGP_EVPNES_OPER_UP) - && !(es->flags & BGP_EVPNES_BYPASS); + struct bgp_evpn_es *es; + + if (!esi || !bgp_mh_info->suppress_l3_ecomm_on_inactive_es) + return true; + + es = bgp_evpn_es_find(esi); + + return (!es || !(es->flags & BGP_EVPNES_LOCAL) + || bgp_evpn_local_es_is_active(es)); +} + +static bool bgp_evpn_is_valid_local_path(struct bgp_path_info *pi) +{ + return (CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_STATIC); +} + +/* Update all local MAC-IP routes in the VNI routing table associated + * with the ES. When the ES is down the routes are advertised without + * the L3 extcomm + */ +static void bgp_evpn_mac_update_on_es_oper_chg(struct bgp_evpn_es *es) +{ + struct listnode *node; + struct bgp_path_es_info *es_info; + struct bgp_path_info *pi; + struct bgp *bgp; + struct bgpevpn *vpn; + + if (!bgp_mh_info->suppress_l3_ecomm_on_inactive_es) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("update paths linked to es %s on oper chg", + es->esi_str); + + bgp = bgp_get_evpn(); + for (ALL_LIST_ELEMENTS_RO(es->macip_evi_path_list, node, es_info)) { + pi = es_info->pi; + + if (!bgp_evpn_is_valid_local_path(pi)) + continue; + + if (!bgp_evpn_is_macip_path(pi)) + continue; + + vpn = bgp_evpn_lookup_vni(bgp, es_info->vni); + if (!vpn) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "update path %d %pFX linked to es %s on oper chg", + es_info->vni, &pi->net->p, es->esi_str); + + bgp_evpn_update_type2_route_entry(bgp, vpn, pi->net, pi, + __func__); + } +} + +static bool bgp_evpn_is_valid_bgp_path(struct bgp_path_info *pi) +{ + return (CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_NORMAL); +} + +/* If an ES is no longer local (or becomes local) we need to re-install + * paths using that ES as destination. This is needed as the criteria + * for best path selection has changed. + */ +static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es, + bool is_local) +{ + struct listnode *node; + struct bgp_path_es_info *es_info; + struct bgp_path_info *pi; + bool tmp_local; + struct attr *attr_new; + struct attr attr_tmp; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("update paths linked to es %s on chg to %s", + es->esi_str, is_local ? "local" : "non-local"); + + for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) { + pi = es_info->pi; + + /* Consider "valid" remote routes */ + if (!bgp_evpn_is_valid_bgp_path(pi)) + continue; + + if (!pi->attr) + continue; + + tmp_local = !!(pi->attr->es_flags & ATTR_ES_IS_LOCAL); + if (tmp_local == is_local) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "update path %pFX linked to es %s on chg to %s", + &pi->net->p, es->esi_str, + is_local ? "local" : "non-local"); + + attr_tmp = *pi->attr; + if (is_local) + attr_tmp.es_flags |= ATTR_ES_IS_LOCAL; + else + attr_tmp.es_flags &= ~ATTR_ES_IS_LOCAL; + attr_new = bgp_attr_intern(&attr_tmp); + bgp_attr_unintern(&pi->attr); + pi->attr = attr_new; + bgp_evpn_import_type2_route(pi, 1); + } } static void bgp_evpn_local_es_deactivate(struct bgp *bgp, @@ -1699,6 +1869,8 @@ static void bgp_evpn_local_es_deactivate(struct bgp *bgp, "%u failed to delete type-1 route for ESI %s", bgp->vrf_id, es->esi_str); } + + bgp_evpn_mac_update_on_es_oper_chg(es); } /* Process ES link oper-down by withdrawing ES-EAD and ESR */ @@ -1746,6 +1918,8 @@ static void bgp_evpn_local_es_activate(struct bgp *bgp, struct bgp_evpn_es *es, es->originator_ip); (void)bgp_evpn_type1_route_update(bgp, es, NULL, &p); } + + bgp_evpn_mac_update_on_es_oper_chg(es); } /* Process ES link oper-up by generating ES-EAD and ESR */ @@ -1780,11 +1954,14 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp, bool old_bypass = !!(es->flags & BGP_EVPNES_BYPASS); bool old_active; bool new_active; + bool old_is_local; + bool is_local; if (bypass == old_bypass) return; old_active = bgp_evpn_local_es_is_active(es); + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); if (bypass) SET_FLAG(es->flags, BGP_EVPNES_BYPASS); else @@ -1801,6 +1978,10 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp, else bgp_evpn_local_es_deactivate(bgp, es); } + + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (old_is_local != is_local) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); } static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) @@ -1825,16 +2006,17 @@ static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) /* Clear local info associated with the ES and free it up if there is * no remote reference */ - bgp_evpn_es_local_info_clear(es); + bgp_evpn_es_local_info_clear(es, false); } -bool bgp_evpn_is_esi_local(esi_t *esi) +bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi) { struct bgp_evpn_es *es = NULL; /* Lookup ESI hash - should exist. */ es = bgp_evpn_es_find(esi); - return es ? !!(es->flags & BGP_EVPNES_LOCAL) : false; + + return es && bgp_evpn_is_es_local_and_non_bypass(es); } int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi) @@ -2114,7 +2296,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, json_object_int_add(json, "vrfCount", listcount(es->es_vrf_list)); json_object_int_add(json, "macipPathCount", - listcount(es->macip_path_list)); + listcount(es->macip_evi_path_list)); + json_object_int_add(json, "macipGlobalPathCount", + listcount(es->macip_global_path_list)); json_object_int_add(json, "inconsistentVniVtepCount", es->incons_evi_vtep_cnt); if (listcount(es->es_vtep_list)) { @@ -2162,8 +2346,10 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, vty_out(vty, " Remote VNI Count: %d\n", es->remote_es_evi_cnt); vty_out(vty, " VRF Count: %d\n", listcount(es->es_vrf_list)); - vty_out(vty, " MACIP Path Count: %d\n", - listcount(es->macip_path_list)); + vty_out(vty, " MACIP EVI Path Count: %d\n", + listcount(es->macip_evi_path_list)); + vty_out(vty, " MACIP Global Path Count: %d\n", + listcount(es->macip_global_path_list)); vty_out(vty, " Inconsistent VNI VTEP Count: %d\n", es->incons_evi_vtep_cnt); if (es->inconsistencies) { @@ -2394,6 +2580,8 @@ static void bgp_evpn_l3nhg_deactivate(struct bgp_evpn_es_vrf *es_vrf) es_vrf->nhg_id); bgp_evpn_l3nhg_zebra_del(es_vrf); es_vrf->flags &= ~BGP_EVPNES_VRF_NHG_ACTIVE; + /* MAC-IPs can now be installed via the L3NHG */ + bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg-deactivate"); } static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update) @@ -2412,6 +2600,8 @@ static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update) es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id, es_vrf->nhg_id); es_vrf->flags |= BGP_EVPNES_VRF_NHG_ACTIVE; + /* MAC-IPs can now be installed via the L3NHG */ + bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg_activate"); } bgp_evpn_l3nhg_zebra_add(es_vrf); @@ -2488,6 +2678,11 @@ static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_create(struct bgp_evpn_es *es, bgp_vrf->vrf_id, es_vrf->nhg_id, es_vrf->v6_nhg_id); bgp_evpn_l3nhg_activate(es_vrf, false /* update */); + /* update paths in the VRF that may already be associated with + * this destination ES + */ + bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-create"); + return es_vrf; } @@ -2516,6 +2711,11 @@ static void bgp_evpn_es_vrf_delete(struct bgp_evpn_es_vrf *es_vrf) /* remove from the VRF-ESI rb tree */ RB_REMOVE(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf); + /* update paths in the VRF that may already be associated with + * this destination ES + */ + bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-delete"); + XFREE(MTYPE_BGP_EVPN_ES_VRF, es_vrf); } @@ -2598,22 +2798,56 @@ void bgp_evpn_es_evi_vrf_ref(struct bgpevpn *vpn) bgp_evpn_es_vrf_ref(es_evi, vpn->bgp_vrf); } +/* 1. If ES-VRF is not present install the host route with the exploded/flat + * multi-path list. + * 2. If ES-VRF is present - + * - if L3NHG has not been activated for the ES-VRF (this could be because + * all the PEs attached to the VRF are down) do not install the route + * in zebra. + * - if L3NHG has been activated install the route via that L3NHG + */ +void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi, bool *use_l3nhg, + bool *is_l3nhg_active, + struct bgp_evpn_es_vrf **es_vrf_p) +{ + struct bgp_evpn_es *es; + struct bgp_evpn_es_vrf *es_vrf; + + if (!bgp_mh_info->host_routes_use_l3nhg) + return; + + es = bgp_evpn_es_find(esi); + if (!es) + return; + + es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf); + if (!es_vrf) + return; + + *use_l3nhg = true; + if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE) + *is_l3nhg_active = true; + if (es_vrf_p) + *es_vrf_p = es_vrf; +} + /* returns false if legacy-exploded mp needs to be used for route install */ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, uint32_t *nhg_p) { esi_t *esi; - struct bgp_evpn_es *es; - struct bgp_evpn_es_vrf *es_vrf; + struct bgp_evpn_es_vrf *es_vrf = NULL; struct bgp_path_info *parent_pi; struct bgp_node *rn; struct prefix_evpn *evp; struct bgp_path_info *mpinfo; + bool use_l3nhg = false; + bool is_l3nhg_active = false; *nhg_p = 0; - /* L3NHG support is disabled, use legacy-exploded multipath */ - if (!bgp_mh_info->host_routes_use_l3nhg) + /* we don't support NHG for routes leaked from another VRF yet */ + if (pi->extra && pi->extra->bgp_orig) return false; parent_pi = get_route_parent_evpn(pi); @@ -2633,15 +2867,17 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, if (!memcmp(esi, zero_esi, sizeof(*esi))) return false; - /* if the ES-VRF is not setup or if the NHG has not been installed - * we cannot install the route yet, return a 0-NHG to indicate - * that + bgp_evpn_es_vrf_use_nhg(bgp_vrf, esi, &use_l3nhg, &is_l3nhg_active, + &es_vrf); + + /* L3NHG support is disabled, use legacy-exploded multipath */ + if (!use_l3nhg) + return false; + + /* if the NHG has not been installed we cannot install the route yet, + * return a 0-NHG to indicate that */ - es = bgp_evpn_es_find(esi); - if (!es) - return true; - es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf); - if (!es_vrf || !(es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE)) + if (!is_l3nhg_active) return true; /* this needs to be set the v6NHG if v6route */ @@ -2652,7 +2888,7 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, for (mpinfo = bgp_path_info_mpath_next(pi); mpinfo; mpinfo = bgp_path_info_mpath_next(mpinfo)) { - /* if any of the paths of have a different ESI we can't use + /* if any of the paths have a different ESI we can't use * the NHG associated with the ES. fallback to legacy-exploded * multipath */ @@ -2988,7 +3224,8 @@ static struct bgp_evpn_es_evi *bgp_evpn_es_evi_new(struct bgp_evpn_es *es, /* remove the ES-EVI from the per-L2-VNI and per-ES tables and free * up the memory. */ -static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) +static struct bgp_evpn_es_evi * +bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) { struct bgp_evpn_es *es = es_evi->es; struct bgpevpn *vpn = es_evi->vpn; @@ -2997,7 +3234,7 @@ static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) * reference */ if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE)) - return; + return es_evi; bgp_evpn_es_vrf_deref(es_evi); @@ -3012,6 +3249,8 @@ static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) /* remove from the VNI-ESI rb tree */ XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi); + + return NULL; } /* init local info associated with the ES-EVI */ @@ -3028,17 +3267,18 @@ static void bgp_evpn_es_evi_local_info_set(struct bgp_evpn_es_evi *es_evi) } /* clear any local info associated with the ES-EVI */ -static void bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi) +static struct bgp_evpn_es_evi * +bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi) { struct bgpevpn *vpn = es_evi->vpn; if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) - return; + return es_evi; UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL); list_delete_node(vpn->local_es_evi_list, &es_evi->l2vni_listnode); - bgp_evpn_es_evi_free(es_evi); + return bgp_evpn_es_evi_free(es_evi); } /* eval remote info associated with the ES */ @@ -3068,14 +3308,15 @@ static void bgp_evpn_es_evi_remote_info_re_eval(struct bgp_evpn_es_evi *es_evi) } } -static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) +static struct bgp_evpn_es_evi * +bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) { struct prefix_evpn p; struct bgp_evpn_es *es = es_evi->es; struct bgp *bgp; if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) - return; + return es_evi; if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) zlog_debug("del local es %s evi %u", @@ -3109,8 +3350,7 @@ static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) } } - bgp_evpn_es_evi_local_info_clear(es_evi); - + return bgp_evpn_es_evi_local_info_clear(es_evi); } int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni) @@ -3326,6 +3566,30 @@ int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +/* If a VNI is being deleted we need to force del all remote VTEPs */ +static void bgp_evpn_remote_es_evi_flush(struct bgp_evpn_es_evi *es_evi) +{ + struct listnode *node = NULL; + struct listnode *nnode = NULL; + struct bgp_evpn_es_evi_vtep *evi_vtep; + struct bgp *bgp; + + bgp = bgp_get_evpn(); + if (!bgp) + return; + + /* delete all VTEPs */ + for (ALL_LIST_ELEMENTS(es_evi->es_evi_vtep_list, node, nnode, + evi_vtep)) { + evi_vtep->flags &= ~(BGP_EVPN_EVI_VTEP_EAD_PER_ES + | BGP_EVPN_EVI_VTEP_EAD_PER_EVI); + bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep); + bgp_evpn_es_evi_vtep_free(evi_vtep); + } + /* delete the EVI */ + bgp_evpn_es_evi_remote_info_re_eval(es_evi); +} + /* Initialize the ES tables maintained per-L2_VNI */ void bgp_evpn_vni_es_init(struct bgpevpn *vpn) { @@ -3345,7 +3609,9 @@ void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn) RB_FOREACH_SAFE(es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi_next) { - bgp_evpn_local_es_evi_do_del(es_evi); + es_evi = bgp_evpn_local_es_evi_do_del(es_evi); + if (es_evi) + bgp_evpn_remote_es_evi_flush(es_evi); } list_delete(&vpn->local_es_evi_list); @@ -3844,6 +4110,507 @@ static int bgp_evpn_run_consistency_checks(struct thread *t) return 0; } +/***************************************************************************** + * EVPN-Nexthop and RMAC management: nexthops associated with Type-2 routes + * that have an ES as destination are consolidated by BGP into a per-VRF + * nh->rmac mapping which is sent to zebra. Zebra installs the nexthop + * as a remote neigh/fdb entry with a dummy (type-1) prefix referencing it. + * + * This handling is needed because Type-2 routes with ES as dest use NHG + * that is setup using EAD routes (i.e. such NHGs do not include the + * RMAC info). + ****************************************************************************/ +static void bgp_evpn_nh_zebra_update_send(struct bgp_evpn_nh *nh, bool add) +{ + struct stream *s; + struct bgp *bgp_vrf = nh->bgp_vrf; + + /* Check socket. */ + if (!zclient || zclient->sock < 0) + return; + + /* Don't try to register if Zebra doesn't know of this instance. */ + if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp_vrf)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("No zebra instance, not %s remote nh %s", + add ? "adding" : "deleting", nh->nh_str); + return; + } + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header( + s, add ? ZEBRA_EVPN_REMOTE_NH_ADD : ZEBRA_EVPN_REMOTE_NH_DEL, + bgp_vrf->vrf_id); + stream_putl(s, bgp_vrf->vrf_id); + stream_put(s, &nh->ip, sizeof(nh->ip)); + if (add) + stream_put(s, &nh->rmac, sizeof(nh->rmac)); + + stream_putw_at(s, 0, stream_get_endp(s)); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) { + if (add) + zlog_debug("evpn vrf %s nh %s rmac %pEA add to zebra", + nh->bgp_vrf->name, nh->nh_str, &nh->rmac); + else if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s del to zebra", + nh->bgp_vrf->name, nh->nh_str); + } + + zclient_send_message(zclient); +} + +static void bgp_evpn_nh_zebra_update(struct bgp_evpn_nh *nh, bool add) +{ + if (add && !is_zero_mac(&nh->rmac)) { + nh->flags |= BGP_EVPN_NH_READY_FOR_ZEBRA; + bgp_evpn_nh_zebra_update_send(nh, true); + } else { + if (!(nh->flags & BGP_EVPN_NH_READY_FOR_ZEBRA)) + return; + nh->flags &= ~BGP_EVPN_NH_READY_FOR_ZEBRA; + bgp_evpn_nh_zebra_update_send(nh, false); + } +} + +static void *bgp_evpn_nh_alloc(void *p) +{ + struct bgp_evpn_nh *tmp_n = p; + struct bgp_evpn_nh *n; + + n = XCALLOC(MTYPE_BGP_EVPN_NH, sizeof(struct bgp_evpn_nh)); + *n = *tmp_n; + + return ((void *)n); +} + +static struct bgp_evpn_nh *bgp_evpn_nh_find(struct bgp *bgp_vrf, + struct ipaddr *ip) +{ + struct bgp_evpn_nh tmp; + struct bgp_evpn_nh *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(bgp_vrf->evpn_nh_table, &tmp); + + return n; +} + +/* Add nexthop entry - implicitly created on first path reference */ +static struct bgp_evpn_nh *bgp_evpn_nh_add(struct bgp *bgp_vrf, + struct ipaddr *ip, + struct bgp_path_info *pi) +{ + struct bgp_evpn_nh tmp_n; + struct bgp_evpn_nh *n = NULL; + + memset(&tmp_n, 0, sizeof(struct bgp_evpn_nh)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(bgp_vrf->evpn_nh_table, &tmp_n, bgp_evpn_nh_alloc); + ipaddr2str(ip, n->nh_str, sizeof(n->nh_str)); + n->bgp_vrf = bgp_vrf; + + n->pi_list = list_new(); + listset_app_node_mem(n->pi_list); + + /* Setup ref_pi when the nh is created */ + if (CHECK_FLAG(pi->flags, BGP_PATH_VALID) && pi->attr) { + n->ref_pi = pi; + memcpy(&n->rmac, &pi->attr->rmac, ETH_ALEN); + } + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s rmac %pEA add", n->bgp_vrf->name, + n->nh_str, &n->rmac); + bgp_evpn_nh_zebra_update(n, true); + return n; +} + +/* Delete nexthop entry if there are no paths referencing it */ +static void bgp_evpn_nh_del(struct bgp_evpn_nh *n) +{ + struct bgp_evpn_nh *tmp_n; + struct bgp *bgp_vrf = n->bgp_vrf; + + if (listcount(n->pi_list)) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s del to zebra", bgp_vrf->name, + n->nh_str); + + bgp_evpn_nh_zebra_update(n, false); + list_delete(&n->pi_list); + tmp_n = hash_release(bgp_vrf->evpn_nh_table, n); + XFREE(MTYPE_BGP_EVPN_NH, tmp_n); +} + +static unsigned int bgp_evpn_nh_hash_keymake(const void *p) +{ + const struct bgp_evpn_nh *n = p; + const struct ipaddr *ip = &n->ip; + + if (IS_IPADDR_V4(ip)) + return jhash_1word(ip->ipaddr_v4.s_addr, 0); + + return jhash2(ip->ipaddr_v6.s6_addr32, + array_size(ip->ipaddr_v6.s6_addr32), 0); +} + +static bool bgp_evpn_nh_cmp(const void *p1, const void *p2) +{ + const struct bgp_evpn_nh *n1 = p1; + const struct bgp_evpn_nh *n2 = p2; + + if (n1 == NULL && n2 == NULL) + return true; + + if (n1 == NULL || n2 == NULL) + return false; + + return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0); +} + +void bgp_evpn_nh_init(struct bgp *bgp_vrf) +{ + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh init", bgp_vrf->name); + bgp_vrf->evpn_nh_table = hash_create( + bgp_evpn_nh_hash_keymake, bgp_evpn_nh_cmp, "BGP EVPN NH table"); +} + +static void bgp_evpn_nh_flush_entry(struct bgp_evpn_nh *nh) +{ + struct listnode *node; + struct listnode *nnode; + struct bgp_path_evpn_nh_info *nh_info; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s flush", nh->bgp_vrf->name, + nh->nh_str); + + /* force flush paths */ + for (ALL_LIST_ELEMENTS(nh->pi_list, node, nnode, nh_info)) + bgp_evpn_path_nh_del(nh->bgp_vrf, nh_info->pi); +} + +static void bgp_evpn_nh_flush_cb(struct hash_bucket *bucket, void *ctxt) +{ + struct bgp_evpn_nh *nh = (struct bgp_evpn_nh *)bucket->data; + + bgp_evpn_nh_flush_entry(nh); +} + +void bgp_evpn_nh_finish(struct bgp *bgp_vrf) +{ + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh finish", bgp_vrf->name); + hash_iterate( + bgp_vrf->evpn_nh_table, + (void (*)(struct hash_bucket *, void *))bgp_evpn_nh_flush_cb, + NULL); + hash_free(bgp_vrf->evpn_nh_table); + bgp_vrf->evpn_nh_table = NULL; +} + +static void bgp_evpn_nh_update_ref_pi(struct bgp_evpn_nh *nh) +{ + struct listnode *node; + struct bgp_path_info *pi; + struct bgp_path_evpn_nh_info *nh_info; + + if (nh->ref_pi) + return; + + for (ALL_LIST_ELEMENTS_RO(nh->pi_list, node, nh_info)) { + pi = nh_info->pi; + if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID) || !pi->attr) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s ref_pi update", + nh->bgp_vrf->name, nh->nh_str); + nh->ref_pi = pi; + /* If we have a new pi copy rmac from it and update + * zebra if the new rmac is different + */ + if (memcmp(&nh->rmac, &nh->ref_pi->attr->rmac, ETH_ALEN)) { + memcpy(&nh->rmac, &nh->ref_pi->attr->rmac, ETH_ALEN); + bgp_evpn_nh_zebra_update(nh, true); + } + break; + } +} + +static void bgp_evpn_nh_clear_ref_pi(struct bgp_evpn_nh *nh, + struct bgp_path_info *pi) +{ + if (nh->ref_pi != pi) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s ref_pi clear", nh->bgp_vrf->name, + nh->nh_str); + nh->ref_pi = NULL; + /* try to find another ref_pi */ + bgp_evpn_nh_update_ref_pi(nh); + /* couldn't find one - clear the old rmac and notify zebra */ + if (!nh->ref_pi) { + memset(&nh->rmac, 0, ETH_ALEN); + bgp_evpn_nh_zebra_update(nh, true); + } +} + +static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info) +{ + bgp_evpn_path_nh_unlink(nh_info); + XFREE(MTYPE_BGP_EVPN_PATH_NH_INFO, nh_info); +} + +static struct bgp_path_evpn_nh_info * +bgp_evpn_path_nh_info_new(struct bgp_path_info *pi) +{ + struct bgp_path_info_extra *e; + struct bgp_path_mh_info *mh_info; + struct bgp_path_evpn_nh_info *nh_info; + + e = bgp_path_info_extra_get(pi); + + /* If mh_info doesn't exist allocate it */ + mh_info = e->mh_info; + if (!mh_info) + e->mh_info = mh_info = XCALLOC(MTYPE_BGP_EVPN_PATH_MH_INFO, + sizeof(struct bgp_path_mh_info)); + + /* If nh_info doesn't exist allocate it */ + nh_info = mh_info->nh_info; + if (!nh_info) { + mh_info->nh_info = nh_info = + XCALLOC(MTYPE_BGP_EVPN_PATH_NH_INFO, + sizeof(struct bgp_path_evpn_nh_info)); + nh_info->pi = pi; + } + + return nh_info; +} + +static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info) +{ + struct bgp_evpn_nh *nh = nh_info->nh; + struct bgp_path_info *pi; + char prefix_buf[PREFIX_STRLEN]; + + if (!nh) + return; + + pi = nh_info->pi; + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("path %s unlinked from nh %s %s", + pi->net ? prefix2str(&pi->net->p, prefix_buf, + sizeof(prefix_buf)) + : "", + nh->bgp_vrf->name, nh->nh_str); + + list_delete_node(nh->pi_list, &nh_info->nh_listnode); + + nh_info->nh = NULL; + + /* check if the ref_pi need to be updated */ + bgp_evpn_nh_clear_ref_pi(nh, pi); + + /* if there are no other references against the nh it + * needs to be freed + */ + bgp_evpn_nh_del(nh); + + /* Note we don't free the path nh_info on unlink; it will be freed up + * along with the path. + */ +} + +static void bgp_evpn_path_nh_link(struct bgp *bgp_vrf, struct bgp_path_info *pi) +{ + struct bgp_path_evpn_nh_info *nh_info; + struct bgp_evpn_nh *nh; + struct ipaddr ip; + + /* EVPN nexthop setup in bgp has been turned off */ + if (!bgp_mh_info->bgp_evpn_nh_setup) + return; + + if (!bgp_vrf->evpn_nh_table) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("path %pFX linked to vrf %s failed", + &pi->net->p, bgp_vrf->name); + return; + } + + nh_info = (pi->extra && pi->extra->mh_info) + ? pi->extra->mh_info->nh_info + : NULL; + + /* if NHG is not being used for this path we don't need to manage the + * nexthops in bgp (they are managed by zebra instead) + */ + if (!(pi->attr->es_flags & ATTR_ES_L3_NHG_USE)) { + if (nh_info) + bgp_evpn_path_nh_unlink(nh_info); + return; + } + + /* setup nh_info against the path if it doesn't aleady exist */ + if (!nh_info) + nh_info = bgp_evpn_path_nh_info_new(pi); + + /* find-create nh */ + memset(&ip, 0, sizeof(ip)); + if (pi->net->p.family == AF_INET6) { + SET_IPADDR_V6(&ip); + memcpy(&ip.ipaddr_v6, &pi->attr->mp_nexthop_global, + sizeof(ip.ipaddr_v6)); + } else { + SET_IPADDR_V4(&ip); + memcpy(&ip.ipaddr_v4, &pi->attr->nexthop, sizeof(ip.ipaddr_v4)); + } + + nh = bgp_evpn_nh_find(bgp_vrf, &ip); + if (!nh) + nh = bgp_evpn_nh_add(bgp_vrf, &ip, pi); + + /* dup check */ + if (nh_info->nh == nh) { + /* Check if any of the paths are now valid */ + bgp_evpn_nh_update_ref_pi(nh); + return; + } + + /* unlink old nh if any */ + bgp_evpn_path_nh_unlink(nh_info); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("path %pFX linked to nh %s %s", &pi->net->p, + nh->bgp_vrf->name, nh->nh_str); + + /* link mac-ip path to the new nh */ + nh_info->nh = nh; + listnode_init(&nh_info->nh_listnode, nh_info); + listnode_add(nh->pi_list, &nh_info->nh_listnode); + /* If a new valid path got linked to the nh see if can get the rmac + * from it + */ + bgp_evpn_nh_update_ref_pi(nh); + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) { + if (!nh->ref_pi) + zlog_debug( + "path %pFX linked to nh %s %s with no valid pi", + &pi->net->p, nh->bgp_vrf->name, nh->nh_str); + } +} + +void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi) +{ + struct bgp_path_evpn_nh_info *nh_info; + + nh_info = (pi->extra && pi->extra->mh_info) + ? pi->extra->mh_info->nh_info + : NULL; + + if (!nh_info) + return; + + bgp_evpn_path_nh_unlink(nh_info); +} + +void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi) +{ + bgp_evpn_path_nh_link(bgp_vrf, pi); +} + +static void bgp_evpn_nh_show_entry(struct bgp_evpn_nh *nh, struct vty *vty, + json_object *json_array) +{ + json_object *json = NULL; + char mac_buf[ETHER_ADDR_STRLEN]; + char prefix_buf[PREFIX_STRLEN]; + + if (json_array) + /* create a separate json object for each ES */ + json = json_object_new_object(); + + prefix_mac2str(&nh->rmac, mac_buf, sizeof(mac_buf)); + if (nh->ref_pi && nh->ref_pi->net) + prefix2str(&nh->ref_pi->net->p, prefix_buf, sizeof(prefix_buf)); + else + prefix_buf[0] = '\0'; + if (json) { + json_object_string_add(json, "vrf", nh->bgp_vrf->name); + json_object_string_add(json, "ip", nh->nh_str); + json_object_string_add(json, "rmac", mac_buf); + json_object_string_add(json, "basePath", prefix_buf); + json_object_int_add(json, "pathCount", listcount(nh->pi_list)); + } else { + vty_out(vty, "%-15s %-15s %-17s %-10d %s\n", nh->bgp_vrf->name, + nh->nh_str, mac_buf, listcount(nh->pi_list), + prefix_buf); + } + + /* add ES to the json array */ + if (json_array) + json_object_array_add(json_array, json); +} + +struct nh_show_ctx { + struct vty *vty; + json_object *json; +}; + +static void bgp_evpn_nh_show_hash_cb(struct hash_bucket *bucket, void *ctxt) +{ + struct bgp_evpn_nh *nh = (struct bgp_evpn_nh *)bucket->data; + struct nh_show_ctx *wctx = (struct nh_show_ctx *)ctxt; + + bgp_evpn_nh_show_entry(nh, wctx->vty, wctx->json); +} + +/* Display all evpn nexthops */ +void bgp_evpn_nh_show(struct vty *vty, bool uj) +{ + json_object *json_array = NULL; + struct bgp *bgp_vrf; + struct listnode *node; + struct nh_show_ctx wctx; + + if (uj) { + /* create an array of nexthops */ + json_array = json_object_new_array(); + } else { + vty_out(vty, "%-15s %-15s %-17s %-10s %s\n", "VRF", "IP", + "RMAC", "#Paths", "Base Path"); + } + + wctx.vty = vty; + wctx.json = json_array; + + /* walk through all vrfs */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + hash_iterate(bgp_vrf->evpn_nh_table, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_nh_show_hash_cb, + &wctx); + } + + /* print the array of json-ESs */ + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_array, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_array); + } +} + /*****************************************************************************/ void bgp_evpn_mh_init(void) { @@ -3866,6 +4633,8 @@ void bgp_evpn_mh_init(void) bgp_mh_info->consistency_checking = true; bgp_mh_info->install_l3nhg = false; 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; memset(&zero_esi_buf, 0, sizeof(esi_t)); } @@ -3880,7 +4649,7 @@ void bgp_evpn_mh_finish(void) RB_FOREACH_SAFE (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es_next) { - bgp_evpn_es_local_info_clear(es); + bgp_evpn_es_local_info_clear(es, true); } if (bgp_mh_info->t_cons_check) thread_cancel(&bgp_mh_info->t_cons_check); diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index 8c66e391b6..c96de86871 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -105,8 +105,17 @@ struct bgp_evpn_es { /* List of MAC-IP VNI paths using this ES as destination - * element is bgp_path_info_extra->es_info + * Note: Only local/zebra-added MACIP paths in the VNI + * routing table are linked to this list */ - struct list *macip_path_list; + struct list *macip_evi_path_list; + + /* List of MAC-IP paths in the global routing table using this + * ES as destination - data is bgp_path_info_extra->es_info + * Note: Only non-local/imported MACIP paths in the global + * routing table are linked to this list + */ + struct list *macip_global_path_list; /* Number of remote VNIs referencing this ES */ uint32_t remote_es_evi_cnt; @@ -241,6 +250,26 @@ struct bgp_evpn_es_evi_vtep { struct bgp_evpn_es_vtep *es_vtep; }; +/* A nexthop is created when a path (imported from an EVPN type-2 route) + * is added to the VRF route table using that nexthop. + * It is added on first pi reference and removed on last pi deref. + */ +struct bgp_evpn_nh { + /* backpointer to the VRF */ + struct bgp *bgp_vrf; + /* nexthop/VTEP IP */ + struct ipaddr ip; + /* description for easy logging */ + char nh_str[INET6_ADDRSTRLEN]; + struct ethaddr rmac; + /* pi from which we are pulling the nh RMAC */ + struct bgp_path_info *ref_pi; + /* List of VRF paths using this nexthop */ + struct list *pi_list; + uint8_t flags; +#define BGP_EVPN_NH_READY_FOR_ZEBRA (1 << 0) +}; + /* multihoming information stored in bgp_master */ #define bgp_mh_info (bm->mh_info) struct bgp_evpn_mh_info { @@ -273,6 +302,12 @@ struct bgp_evpn_mh_info { /* Skip EAD-EVI advertisements by turning off this knob */ bool ead_evi_tx; #define BGP_EVPN_MH_EAD_EVI_TX_DEF true + /* If the Local ES is inactive we advertise the MAC-IP without the + * L3 ecomm + */ + bool suppress_l3_ecomm_on_inactive_es; + /* Setup EVPN PE nexthops and their RMAC in bgpd */ + bool bgp_evpn_nh_setup; }; /****************************************************************************/ @@ -330,6 +365,12 @@ static inline uint32_t bgp_evpn_attr_get_df_pref(struct attr *attr) return (attr) ? attr->df_pref : 0; } +static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es) +{ + return (es->flags & BGP_EVPNES_OPER_UP) + && !(es->flags & BGP_EVPNES_BYPASS); +} + /****************************************************************************/ extern int bgp_evpn_es_route_install_uninstall(struct bgp *bgp, struct bgp_evpn_es *es, afi_t afi, safi_t safi, @@ -362,21 +403,28 @@ void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni, bool uj, bool detail); void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail); struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi); -extern bool bgp_evpn_is_esi_local(esi_t *esi); extern void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf); +extern bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi); extern void bgp_evpn_es_vrf_deref(struct bgp_evpn_es_evi *es_evi); extern void bgp_evpn_es_vrf_ref(struct bgp_evpn_es_evi *es_evi, struct bgp *bgp_vrf); -extern void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info); -extern void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info); +extern void bgp_evpn_path_mh_info_free(struct bgp_path_mh_info *mh_info); extern void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi); -extern bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh); extern bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, uint32_t *nhg_p); extern void bgp_evpn_es_vrf_show(struct vty *vty, bool uj, struct bgp_evpn_es *es); extern void bgp_evpn_es_vrf_show_esi(struct vty *vty, esi_t *esi, bool uj); extern void bgp_evpn_switch_ead_evi_rx(void); +extern bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi); +extern void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi, + bool *use_l3nhg, bool *is_l3nhg_active, + struct bgp_evpn_es_vrf **es_vrf_p); +extern void bgp_evpn_nh_init(struct bgp *bgp_vrf); +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); #endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index ff4970af41..debed9f68b 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -631,4 +631,13 @@ bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, const struct prefix_evpn *evp, struct prefix_rd *prd); extern void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import); +extern void bgp_evpn_update_type2_route_entry(struct bgp *bgp, + struct bgpevpn *vpn, + struct bgp_node *rn, + struct bgp_path_info *local_pi, + const char *caller); +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); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index b101589a79..5a0258f3bf 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -688,7 +688,8 @@ static void show_esi_routes(struct bgp *bgp, /* Display all MAC-IP VNI routes linked to an ES */ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, - json_object *json, int detail) + json_object *json, int detail, + bool global_table) { struct bgp_node *rn; struct bgp_path_info *pi; @@ -709,11 +710,17 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, json_paths = json_object_new_array(); RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) { + struct list *es_list; if (esi && memcmp(esi, &es->esi, sizeof(*esi))) continue; - for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) { + if (global_table) + es_list = es->macip_global_path_list; + else + es_list = es->macip_evi_path_list; + + for (ALL_LIST_ELEMENTS_RO(es_list, node, es_info)) { json_object *json_path = NULL; pi = es_info->pi; @@ -734,9 +741,9 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, json_path = json_object_new_array(); if (detail) - route_vty_out_detail(vty, bgp, rn, pi, - AFI_L2VPN, SAFI_EVPN, - json_path); + route_vty_out_detail( + vty, bgp, rn, pi, AFI_L2VPN, SAFI_EVPN, + RPKI_NOT_BEING_USED, json_path); else route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, json_path, false); @@ -758,6 +765,18 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, } } +static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi, + json_object *json, int detail) +{ + return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false); +} + +static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi, + json_object *json, int detail) +{ + return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true); +} + static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, struct vty *vty, struct in_addr vtep_ip, json_object *json, int detail) @@ -823,6 +842,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, if (detail) route_vty_out_detail(vty, bgp, dest, pi, AFI_L2VPN, SAFI_EVPN, + RPKI_NOT_BEING_USED, json_path); else route_vty_out(vty, p, pi, 0, SAFI_EVPN, @@ -2367,7 +2387,8 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, + RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2436,7 +2457,8 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, + RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2541,7 +2563,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, + RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2651,7 +2674,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, json_path = json_object_new_array(); route_vty_out_detail(vty, bgp, dest, pi, afi, safi, - json_path); + RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2820,7 +2843,8 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, if (detail) { route_vty_out_detail( vty, bgp, dest, pi, AFI_L2VPN, - SAFI_EVPN, json_path); + SAFI_EVPN, RPKI_NOT_BEING_USED, + json_path); } else route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path, false); @@ -4100,6 +4124,21 @@ DEFPY(show_bgp_l2vpn_evpn_es_vrf, show_bgp_l2vpn_evpn_es_vrf_cmd, return CMD_SUCCESS; } +DEFPY(show_bgp_l2vpn_evpn_nh, + show_bgp_l2vpn_evpn_nh_cmd, + "show bgp l2vpn evpn next-hops [json$uj]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Nexthops\n" + JSON_STR) +{ + bgp_evpn_nh_show(vty, uj); + + return CMD_SUCCESS; +} + /* * Display EVPN neighbor summary. */ @@ -4658,12 +4697,49 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all, } DEFPY_HIDDEN( - show_bgp_l2vpn_evpn_route_mac_ip_es, - show_bgp_l2vpn_evpn_route_mac_ip_es_cmd, - "show bgp l2vpn evpn route mac-ip-es [NAME$esi_str|detail$detail] [json$uj]", + show_bgp_l2vpn_evpn_route_mac_ip_evi_es, + show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd, + "show bgp l2vpn evpn route mac-ip-evi-es [NAME$esi_str|detail$detail] [json$uj]", + SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR + "EVPN route information\n" + "MAC IP routes in the EVI tables linked to the ES\n" + "ES ID\n" + "Detailed information\n" JSON_STR) +{ + esi_t esi; + esi_t *esi_p; + json_object *json = NULL; + + if (esi_str) { + if (!str_to_esi(esi_str, &esi)) { + vty_out(vty, "%%Malformed ESI\n"); + return CMD_WARNING; + } + esi_p = &esi; + } else { + esi_p = NULL; + } + + if (uj) + json = json_object_new_object(); + bgp_evpn_show_routes_mac_ip_evi_es(vty, esi_p, json, !!detail); + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN( + show_bgp_l2vpn_evpn_route_mac_ip_global_es, + show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd, + "show bgp l2vpn evpn route mac-ip-global-es [NAME$esi_str|detail$detail] [json$uj]", SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR "EVPN route information\n" - "MAC IP routes linked to the ES\n" + "MAC IP routes in the global table linked to the ES\n" "ES ID\n" "Detailed information\n" JSON_STR) { @@ -4683,7 +4759,7 @@ DEFPY_HIDDEN( if (uj) json = json_object_new_object(); - bgp_evpn_show_routes_mac_ip_es(vty, esi_p, json, !!detail); + bgp_evpn_show_routes_mac_ip_global_es(vty, esi_p, json, !!detail); if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -5957,6 +6033,7 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_evi_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); @@ -5968,7 +6045,10 @@ void bgp_ethernetvpn_init(void) &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd); - install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_mac_ip_es_cmd); + install_element(VIEW_NODE, + &show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd); + install_element(VIEW_NODE, + &show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 9a4eb5d3bd..36bdc05eb7 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -119,7 +119,10 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value"); DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_MH_INFO, "BGP EVPN MH Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP"); +DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_MH_INFO, "BGP EVPN PATH MH Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_ES_INFO, "BGP EVPN PATH ES Information"); +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_EVI, "BGP EVPN ES-per-EVI Information"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 7b839f1d4c..29923424e3 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -118,6 +118,9 @@ DECLARE_MTYPE(BGP_EVPN_ES_EVI); DECLARE_MTYPE(BGP_EVPN_ES_VRF); DECLARE_MTYPE(BGP_EVPN_ES_VTEP); DECLARE_MTYPE(BGP_EVPN_PATH_ES_INFO); +DECLARE_MTYPE(BGP_EVPN_PATH_MH_INFO); +DECLARE_MTYPE(BGP_EVPN_PATH_NH_INFO); +DECLARE_MTYPE(BGP_EVPN_NH); DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP); DECLARE_MTYPE(BGP_EVPN); diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c index b74cf37ac7..6f75856d54 100644 --- a/bgpd/bgp_mplsvpn_snmp.c +++ b/bgpd/bgp_mplsvpn_snmp.c @@ -1478,10 +1478,16 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[], oid_copy_str(&name[namelen], (*l3vpn_bgp)->name, vrf_name_len); oid_index = namelen + vrf_name_len; - name[oid_index++] = - v4 ? INETADDRESSTYPEIPV4 : INETADDRESSTYPEIPV6; - oid_copy_addr(&name[oid_index], &p->u.prefix4, - addr_len); + if (v4) { + name[oid_index++] = INETADDRESSTYPEIPV4; + oid_copy_in_addr(&name[oid_index], + &p->u.prefix4); + } else { + name[oid_index++] = INETADDRESSTYPEIPV6; + oid_copy_in6_addr(&name[oid_index], + &p->u.prefix6); + } + oid_index += addr_len; name[oid_index++] = p->prefixlen; name[oid_index++] = *policy >> 8; @@ -1493,9 +1499,8 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[], INETADDRESSTYPEUNKNOWN; else { name[oid_index++] = INETADDRESSTYPEIPV4; - oid_copy_addr(&name[oid_index], - &attr->nexthop, - sizeof(struct in_addr)); + oid_copy_in_addr(&name[oid_index], + &attr->nexthop); oid_index += sizeof(struct in_addr); } } else { @@ -1505,11 +1510,9 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[], INETADDRESSTYPEUNKNOWN; else { name[oid_index++] = INETADDRESSTYPEIPV6; - oid_copy_addr( + oid_copy_in6_addr( &name[oid_index], - (struct in_addr *)&attr - ->mp_nexthop_global, - sizeof(struct in6_addr)); + &attr->mp_nexthop_global); oid_index += sizeof(struct in6_addr); } } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 124a477248..a6bfb519d8 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -71,6 +71,7 @@ #include "bgpd/bgp_mac.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_trace.h" +#include "bgpd/bgp_rpki.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -250,8 +251,8 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) if (e->aggr_suppressors) list_delete(&e->aggr_suppressors); - if (e->es_info) - bgp_evpn_path_es_info_free(e->es_info); + if (e->mh_info) + bgp_evpn_path_mh_info_free(e->mh_info); if ((*extra)->bgp_fs_iprule) list_delete(&((*extra)->bgp_fs_iprule)); @@ -7551,18 +7552,20 @@ static const char *bgp_origin2str(uint8_t origin) return "n/a"; } -static const char *bgp_rpki_validation2str(int v_state) +static const char *bgp_rpki_validation2str(enum rpki_states v_state) { switch (v_state) { - case 1: + case RPKI_NOT_BEING_USED: + return "not used"; + case RPKI_VALID: return "valid"; - case 2: + case RPKI_NOTFOUND: return "not found"; - case 3: + case RPKI_INVALID: return "invalid"; - default: - break; } + + assert(!"We should never get here this is a dev escape"); return "ERROR"; } @@ -8856,15 +8859,17 @@ void route_vty_out(struct vty *vty, const struct prefix *p, if (safi == SAFI_EVPN) { struct bgp_path_es_info *path_es_info = NULL; - if (path->extra) - path_es_info = path->extra->es_info; - if (bgp_evpn_is_esi_valid(&attr->esi)) { /* XXX - add these params to the json out */ vty_out(vty, "%*s", 20, " "); vty_out(vty, "ESI:%s", esi_to_str(&attr->esi, esi_buf, sizeof(esi_buf))); + + if (path->extra && path->extra->mh_info) + path_es_info = + path->extra->mh_info->es_info; + if (path_es_info && path_es_info->es) vty_out(vty, " VNI: %u", path_es_info->vni); @@ -9547,9 +9552,10 @@ static void route_vty_out_detail_es_info(struct vty *vty, } } -void route_vty_out_detail(struct vty *vty, struct bgp *bgp, - struct bgp_dest *bn, struct bgp_path_info *path, - afi_t afi, safi_t safi, json_object *json_paths) +void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, + struct bgp_path_info *path, afi_t afi, safi_t safi, + enum rpki_states rpki_curr_state, + json_object *json_paths) { char buf[INET6_ADDRSTRLEN]; char buf1[BUFSIZ]; @@ -9580,7 +9586,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, int i; char *nexthop_hostname = bgp_nexthop_hostname(path->peer, path->nexthop); - int rpki_validation_state = 0; if (json_paths) { json_path = json_object_new_object(); @@ -9626,12 +9631,20 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, buf1, sizeof(buf1)); if (is_pi_family_evpn(parent_ri)) { vty_out(vty, - " Imported from %s:%pFX, VNI %s\n", + " Imported from %s:%pFX, VNI %s", buf1, (struct prefix_evpn *) bgp_dest_get_prefix( dest), tag_buf); + if (attr->es_flags & ATTR_ES_L3_NHG) + vty_out(vty, ", L3NHG %s", + (attr->es_flags + & ATTR_ES_L3_NHG_ACTIVE) + ? "active" + : "inactive"); + vty_out(vty, "\n"); + } else vty_out(vty, " Imported from %s:%pFX\n", @@ -10179,18 +10192,14 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, } } - const struct prefix *p = bgp_dest_get_prefix(bn); - if (p->family == AF_INET || p->family == AF_INET6) - rpki_validation_state = hook_call(bgp_rpki_prefix_status, - path->peer, path->attr, p); - if (rpki_validation_state) { + if (rpki_curr_state != RPKI_NOT_BEING_USED) { if (json_paths) json_object_string_add( json_path, "rpkiValidationState", - bgp_rpki_validation2str(rpki_validation_state)); + bgp_rpki_validation2str(rpki_curr_state)); else - vty_out(vty, ", validation-state: %s", - bgp_rpki_validation2str(rpki_validation_state)); + vty_out(vty, ", rpki validation-state: %s", + bgp_rpki_validation2str(rpki_curr_state)); } if (json_bestpath) @@ -10510,7 +10519,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, enum bgp_show_type type, void *output_arg, char *rd, int is_last, unsigned long *output_cum, unsigned long *total_cum, - unsigned long *json_header_depth, uint8_t show_flags) + unsigned long *json_header_depth, uint8_t show_flags, + enum rpki_states rpki_target_state) { struct bgp_path_info *pi; struct bgp_dest *dest; @@ -10559,6 +10569,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, /* Start processing of routes. */ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { const struct prefix *dest_p = bgp_dest_get_prefix(dest); + enum rpki_states rpki_curr_state = RPKI_NOT_BEING_USED; pi = bgp_dest_get_bgp_path_info(dest); if (pi == NULL) @@ -10572,6 +10583,18 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, for (; pi; pi = pi->next) { total_count++; + + if (type == bgp_show_type_rpki) { + if (dest_p->family == AF_INET + || dest_p->family == AF_INET6) + rpki_curr_state = hook_call( + bgp_rpki_prefix_status, + pi->peer, pi->attr, dest_p); + if (rpki_target_state != RPKI_NOT_BEING_USED + && rpki_curr_state != rpki_target_state) + continue; + } + if (type == bgp_show_type_flap_statistics || type == bgp_show_type_flap_neighbor || type == bgp_show_type_dampend_paths @@ -10881,7 +10904,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, bgp_show_table(vty, bgp, safi, itable, type, output_arg, rd, next == NULL, &output_cum, &total_cum, &json_header_depth, - show_flags); + show_flags, RPKI_NOT_BEING_USED); if (next == NULL) show_msg = false; } @@ -10899,7 +10922,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, } static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, enum bgp_show_type type, void *output_arg, - uint8_t show_flags) + uint8_t show_flags, enum rpki_states rpki_target_state) { struct bgp_table *table; unsigned long json_header_depth = 0; @@ -10934,7 +10957,8 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, safi = SAFI_UNICAST; return bgp_show_table(vty, bgp, safi, table, type, output_arg, NULL, 1, - NULL, NULL, &json_header_depth, show_flags); + NULL, NULL, &json_header_depth, show_flags, + rpki_target_state); } static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, @@ -10968,7 +10992,7 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, : bgp->name); } bgp_show(vty, bgp, afi, safi, bgp_show_type_normal, NULL, - show_flags); + show_flags, RPKI_NOT_BEING_USED); } if (use_json) @@ -11187,15 +11211,25 @@ static void bgp_show_path_info(struct prefix_rd *pfx_rd, struct bgp_dest *bgp_node, struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, json_object *json, enum bgp_path_type pathtype, - int *display) + int *display, enum rpki_states rpki_target_state) { struct bgp_path_info *pi; int header = 1; char rdbuf[RD_ADDRSTRLEN]; json_object *json_header = NULL; json_object *json_paths = NULL; + const struct prefix *p = bgp_dest_get_prefix(bgp_node); for (pi = bgp_dest_get_bgp_path_info(bgp_node); pi; pi = pi->next) { + enum rpki_states rpki_curr_state = RPKI_NOT_BEING_USED; + + if (p->family == AF_INET || p->family == AF_INET6) + rpki_curr_state = hook_call(bgp_rpki_prefix_status, + pi->peer, pi->attr, p); + + if (rpki_target_state != RPKI_NOT_BEING_USED + && rpki_curr_state != rpki_target_state) + continue; if (json && !json_paths) { /* Instantiate json_paths only if path is valid */ @@ -11221,9 +11255,8 @@ static void bgp_show_path_info(struct prefix_rd *pfx_rd, || (pathtype == BGP_PATH_SHOW_MULTIPATH && (CHECK_FLAG(pi->flags, BGP_PATH_MULTIPATH) || CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)))) - route_vty_out_detail(vty, bgp, bgp_node, - pi, AFI_IP, safi, - json_paths); + route_vty_out_detail(vty, bgp, bgp_node, pi, AFI_IP, + safi, rpki_curr_state, json_paths); } if (json && json_paths) { @@ -11238,6 +11271,7 @@ static void bgp_show_path_info(struct prefix_rd *pfx_rd, static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, struct bgp_table *rib, const char *ip_str, afi_t afi, safi_t safi, + enum rpki_states rpki_target_state, struct prefix_rd *prd, int prefix_check, enum bgp_path_type pathtype, bool use_json) { @@ -11285,7 +11319,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi, json, pathtype, - &display); + &display, rpki_target_state); bgp_dest_unlock_node(rm); } @@ -11344,7 +11378,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi, json, pathtype, - &display); + &display, rpki_target_state); bgp_dest_unlock_node(rm); } @@ -11371,7 +11405,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, || dest_p->prefixlen == match.prefixlen) { bgp_show_path_info(NULL, dest, vty, bgp, afi, safi, json, pathtype, - &display); + &display, rpki_target_state); } bgp_dest_unlock_node(dest); @@ -11397,7 +11431,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str, afi_t afi, safi_t safi, struct prefix_rd *prd, int prefix_check, enum bgp_path_type pathtype, - bool use_json) + enum rpki_states rpki_target_state, bool use_json) { if (!bgp) { bgp = bgp_get_default(); @@ -11415,8 +11449,8 @@ static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str, safi = SAFI_UNICAST; return bgp_show_route_in_table(vty, bgp, bgp->rib[afi][safi], ip_str, - afi, safi, prd, prefix_check, pathtype, - use_json); + afi, safi, rpki_target_state, prd, + prefix_check, pathtype, use_json); } static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc, @@ -11458,9 +11492,9 @@ static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc, } ret = bgp_show(vty, bgp, afi, safi, - (exact ? bgp_show_type_lcommunity_exact - : bgp_show_type_lcommunity), - lcom, show_flags); + (exact ? bgp_show_type_lcommunity_exact + : bgp_show_type_lcommunity), + lcom, show_flags, RPKI_NOT_BEING_USED); lcommunity_free(&lcom); return ret; @@ -11488,7 +11522,7 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp, return bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_lcommunity_list_exact : bgp_show_type_lcommunity_list), - list, show_flags); + list, show_flags, RPKI_NOT_BEING_USED); } DEFUN (show_ip_bgp_large_community_list, @@ -11570,7 +11604,8 @@ DEFUN (show_ip_bgp_large_community, exact_match, afi, safi, uj); } else return bgp_show(vty, bgp, afi, safi, - bgp_show_type_lcommunity_all, NULL, show_flags); + bgp_show_type_lcommunity_all, NULL, show_flags, + RPKI_NOT_BEING_USED); } static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi, @@ -11818,6 +11853,7 @@ DEFPY (show_ip_bgp_json, |accept-own|accept-own-nexthop|route-filter-v6\ |route-filter-v4|route-filter-translated-v6\ |route-filter-translated-v4] [exact-match]\ + |rpki <invalid|valid|notfound>\ ] [json$uj | wide$wide]", SHOW_STR IP_STR @@ -11847,6 +11883,10 @@ DEFPY (show_ip_bgp_json, "RT translated VPNv6 route filtering (well-known community)\n" "RT translated VPNv4 route filtering (well-known community)\n" "Exact match of the communities\n" + "RPKI route types\n" + "A valid path as determined by rpki\n" + "A invalid path as determined by rpki\n" + "A path that has no rpki data\n" JSON_STR "Increase table width for longer prefixes\n") { @@ -11859,7 +11899,7 @@ DEFPY (show_ip_bgp_json, char *community = NULL; bool first = true; uint8_t show_flags = 0; - + enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED; if (uj) { argc--; @@ -11916,6 +11956,14 @@ DEFPY (show_ip_bgp_json, sh_type = bgp_show_type_community_all; } + if (argv_find(argv, argc, "rpki", &idx)) { + sh_type = bgp_show_type_rpki; + if (argv_find(argv, argc, "valid", &idx)) + rpki_target_state = RPKI_VALID; + else if (argv_find(argv, argc, "invalid", &idx)) + rpki_target_state = RPKI_INVALID; + } + if (!all) { /* show bgp: AFI_IP6, show ip bgp: AFI_IP */ if (community) @@ -11924,7 +11972,7 @@ DEFPY (show_ip_bgp_json, show_flags); else return bgp_show(vty, bgp, afi, safi, sh_type, NULL, - show_flags); + show_flags, rpki_target_state); } else { /* show <ip> bgp ipv4 all: AFI_IP, show <ip> bgp ipv6 all: * AFI_IP6 */ @@ -11961,7 +12009,8 @@ DEFPY (show_ip_bgp_json, safi, show_flags); else bgp_show(vty, bgp, afi, safi, sh_type, - NULL, show_flags); + NULL, show_flags, + rpki_target_state); if (uj) vty_out(vty, "}\n"); } @@ -11992,7 +12041,8 @@ DEFPY (show_ip_bgp_json, safi, show_flags); else bgp_show(vty, bgp, afi, safi, sh_type, - NULL, show_flags); + NULL, show_flags, + rpki_target_state); if (uj) vty_out(vty, "}\n"); } @@ -12005,7 +12055,7 @@ DEFPY (show_ip_bgp_json, DEFUN (show_ip_bgp_route, show_ip_bgp_route_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [json]", + "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]", SHOW_STR IP_STR BGP_STR @@ -12018,6 +12068,10 @@ DEFUN (show_ip_bgp_route, "IPv6 prefix\n" "Display only the bestpath\n" "Display only multipaths\n" + "Display only paths that match the specified rpki state\n" + "A valid path as determined by rpki\n" + "A invalid path as determined by rpki\n" + "A path that has no rpki data\n" JSON_STR) { int prefix_check = 0; @@ -12074,7 +12128,7 @@ DEFUN (show_ip_bgp_route, path_type = BGP_PATH_SHOW_ALL; return bgp_show_route(vty, bgp, prefix, afi, safi, NULL, prefix_check, - path_type, uj); + path_type, RPKI_NOT_BEING_USED, uj); } DEFUN (show_ip_bgp_regexp, @@ -12169,7 +12223,8 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, return CMD_WARNING; } - rc = bgp_show(vty, bgp, afi, safi, type, regex, show_flags); + rc = bgp_show(vty, bgp, afi, safi, type, regex, show_flags, + RPKI_NOT_BEING_USED); bgp_regex_free(regex); return rc; } @@ -12188,7 +12243,8 @@ static int bgp_show_prefix_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, plist, show_flags); + return bgp_show(vty, bgp, afi, safi, type, plist, show_flags, + RPKI_NOT_BEING_USED); } static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, @@ -12205,7 +12261,8 @@ static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, as_list, show_flags); + return bgp_show(vty, bgp, afi, safi, type, as_list, show_flags, + RPKI_NOT_BEING_USED); } static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, @@ -12221,7 +12278,8 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, rmap, show_flags); + return bgp_show(vty, bgp, afi, safi, type, rmap, show_flags, + RPKI_NOT_BEING_USED); } static int bgp_show_community(struct vty *vty, struct bgp *bgp, @@ -12240,7 +12298,7 @@ static int bgp_show_community(struct vty *vty, struct bgp *bgp, ret = bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_community_exact : bgp_show_type_community), - com, show_flags); + com, show_flags, RPKI_NOT_BEING_USED); community_free(&com); return ret; @@ -12262,7 +12320,7 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp, return bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_community_list_exact : bgp_show_type_community_list), - list, show_flags); + list, show_flags, RPKI_NOT_BEING_USED); } static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, @@ -12281,7 +12339,8 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - ret = bgp_show(vty, bgp, afi, safi, type, p, show_flags); + ret = bgp_show(vty, bgp, afi, safi, type, p, show_flags, + RPKI_NOT_BEING_USED); prefix_free(&p); return ret; } @@ -13003,7 +13062,7 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix, } return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL, prefix_check, BGP_PATH_SHOW_ALL, - use_json(argc, argv)); + RPKI_NOT_BEING_USED, use_json(argc, argv)); } static void show_adj_route_header(struct vty *vty, struct bgp *bgp, @@ -13723,7 +13782,8 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, if (safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; - return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, show_flags); + return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, show_flags, + RPKI_NOT_BEING_USED); } DEFUN (show_ip_bgp_flowspec_routes_detailed, @@ -13756,7 +13816,7 @@ DEFUN (show_ip_bgp_flowspec_routes_detailed, return CMD_WARNING; return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, - show_flags); + show_flags, RPKI_NOT_BEING_USED); } DEFUN (show_ip_bgp_neighbor_routes, @@ -13852,7 +13912,8 @@ DEFUN (show_bgp_afi_vpn_rd_route, } return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd, - 0, BGP_PATH_SHOW_ALL, use_json(argc, argv)); + 0, BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, + use_json(argc, argv)); } static struct bgp_distance *bgp_distance_new(void) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 1dec99f085..0a4fd026e4 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -28,6 +28,7 @@ #include "nexthop.h" #include "bgp_table.h" #include "bgp_addpath_types.h" +#include "bgp_rpki.h" struct bgp_nexthop_cache; struct bgp_route_evpn; @@ -56,6 +57,7 @@ enum bgp_show_type { bgp_show_type_dampend_paths, bgp_show_type_damp_neighbor, bgp_show_type_detail, + bgp_show_type_rpki, }; enum bgp_show_adj_route_type { @@ -102,7 +104,9 @@ enum bgp_show_adj_route_type { #define BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE -15 #define BGP_NLRI_PARSE_ERROR -32 -/* MAC-IP/type-2 path_info in the VNI routing table is linked to the +/* 1. local MAC-IP/type-2 paths in the VNI routing table are linked to the + * destination ES + * 2. remote MAC-IP paths in the global routing table are linked to the * destination ES */ struct bgp_path_es_info { @@ -113,6 +117,27 @@ struct bgp_path_es_info { struct bgp_evpn_es *es; /* memory used for linking the path to the destination ES */ struct listnode es_listnode; + uint8_t flags; +/* Path is linked to the VNI list */ +#define BGP_EVPN_PATH_ES_INFO_VNI_LIST (1 << 0) +/* Path is linked to the global list */ +#define BGP_EVPN_PATH_ES_INFO_GLOBAL_LIST (1 << 1) +}; + +/* IP paths imported into the VRF from an EVPN route source + * are linked to the nexthop/VTEP IP + */ +struct bgp_path_evpn_nh_info { + /* back pointer to the route */ + struct bgp_path_info *pi; + struct bgp_evpn_nh *nh; + /* memory used for linking the path to the nexthop */ + struct listnode nh_listnode; +}; + +struct bgp_path_mh_info { + struct bgp_path_es_info *es_info; + struct bgp_path_evpn_nh_info *nh_info; }; /* Ancillary information to struct bgp_path_info, @@ -202,7 +227,7 @@ struct bgp_path_info_extra { /* presence of FS pbr iprule based entry */ struct list *bgp_fs_iprule; /* Destination Ethernet Segment links for EVPN MH */ - struct bgp_path_es_info *es_info; + struct bgp_path_mh_info *mh_info; }; struct bgp_path_info { @@ -740,7 +765,8 @@ extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, struct bgp_path_info *path, afi_t afi, - safi_t safi, json_object *json_paths); + safi_t safi, enum rpki_states, + json_object *json_paths); extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, struct prefix_rd *prd, enum bgp_show_type type, void *output_arg, diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 9344384956..3ef0137ba6 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -47,6 +47,8 @@ #include "bgpd/bgp_attr.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_rpki.h" + #include "lib/network.h" #include "lib/thread.h" #ifndef VTYSH_EXTRACT_PL @@ -63,10 +65,6 @@ DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group"); -#define RPKI_VALID 1 -#define RPKI_NOTFOUND 2 -#define RPKI_INVALID 3 - #define POLLING_PERIOD_DEFAULT 3600 #define EXPIRE_INTERVAL_DEFAULT 7200 #define RETRY_INTERVAL_DEFAULT 600 diff --git a/bgpd/bgp_rpki.h b/bgpd/bgp_rpki.h new file mode 100644 index 0000000000..4dd4b4a2b2 --- /dev/null +++ b/bgpd/bgp_rpki.h @@ -0,0 +1,33 @@ +/* + * bgp_rpki code + * Copyright (C) 2021 NVIDIA Corporation and Mellanox Technologies, LTD + * All Rights Reserved + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __BGP_RPKI_H__ +#define __BGP_RPKI_H__ + +enum rpki_states { + RPKI_NOT_BEING_USED, + RPKI_VALID, + RPKI_NOTFOUND, + RPKI_INVALID +}; + +#endif diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index bc26314b50..3afdbea908 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -435,7 +435,7 @@ static struct peer *bgpPeerTable_lookup(struct variable *v, oid name[], if (peer == NULL) return NULL; - oid_copy_addr(name + namelen, addr, sizeof(struct in_addr)); + oid_copy_in_addr(name + namelen, addr); *length = sizeof(struct in_addr) + namelen; return peer; @@ -767,14 +767,12 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], v->namelen + BGP_PATHATTR_ENTRY_OFFSET; offset = name + v->namelen; - oid_copy_addr(offset, &rn_p->u.prefix4, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, &rn_p->u.prefix4); offset += IN_ADDR_SIZE; *offset = rn_p->prefixlen; offset++; - oid_copy_addr(offset, - &min->peer->su.sin.sin_addr, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, + &min->peer->su.sin.sin_addr); addr->prefix = rn_p->u.prefix4; addr->prefixlen = rn_p->prefixlen; @@ -868,7 +866,7 @@ static int bgpTrapEstablished(struct peer *peer) if (ret == 0) return 0; - oid_copy_addr(index, &addr, IN_ADDR_SIZE); + oid_copy_in_addr(index, &addr); smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid, array_size(bgp_trap_oid), bgp_oid, @@ -887,7 +885,7 @@ static int bgpTrapBackwardTransition(struct peer *peer) if (ret == 0) return 0; - oid_copy_addr(index, &addr, IN_ADDR_SIZE); + oid_copy_in_addr(index, &addr); smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid, array_size(bgp_trap_oid), bgp_oid, diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 6270542178..43d0a3b2d2 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -664,6 +664,9 @@ struct bgp { /* RB tree of ES-VRFs */ struct bgp_es_vrf_rb_head es_vrf_rb_tree; + /* Hash table of EVPN nexthops maintained per-tenant-VRF */ + struct hash *evpn_nh_table; + /* vrf flags */ uint32_t vrf_flags; #define BGP_VRF_AUTO (1 << 0) diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 3991f7d1ed..0ca43fd308 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -176,6 +176,7 @@ noinst_HEADERS += \ bgpd/bgp_pbr.h \ bgpd/bgp_rd.h \ bgpd/bgp_regex.h \ + bgpd/bgp_rpki.h \ bgpd/bgp_route.h \ bgpd/bgp_script.h \ bgpd/bgp_table.h \ diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 4f58ee335b..2a6d2dda34 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -233,6 +233,85 @@ for ``master`` branch: and create ``frr`` user and ``frrvty`` group as shown above. +Debugging Topotest Failures +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For the below debugging options which launch programs, if the topotest is run +within screen_ or tmux_, ``gdb``, the shell or ``vtysh`` will be launched using +that windowing program, otherwise mininet's ``xterm`` functionality will be used +to launch the given program. + +If you wish to force the use of ``xterm`` rather than ``tmux`` or ``screen``, or +wish to use ``gnome-terminal`` instead of ``xterm``, set the environment +variable ``FRR_TOPO_TERMINAL`` to either ``xterm`` or ``gnome-terminal``. + +.. _screen: https://www.gnu.org/software/screen/ +.. _tmux: https://github.com/tmux/tmux/wiki + +Spawning ``vtysh`` or Shells on Routers +""""""""""""""""""""""""""""""""""""""" + +Topotest can automatically launch a shell or ``vtysh`` for any or all routers in +a test. This is enabled by specifying 1 of 2 CLI arguments ``--shell`` or +``--vtysh``. Both of these options can be set to a single router value, multiple +comma-seperated values, or ``all``. + +When either of these options are specified topotest will pause after each test +to allow for inspection of the router state. + +Here's an example of launching ``vtysh`` on routers ``rt1`` and ``rt2``. + +.. code:: shell + + pytest --vtysh=rt1,rt2 all-protocol-startup + +Spawning Mininet CLI, ``vtysh`` or Shells on Routers on Test Failure +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Similar to the previous section one can have ``vtysh`` or a shell launched on +routers, but in this case only when a test fails. To launch the given process on +each router after a test failure specify one of ``--shell-on-error`` or +``--vtysh-on-error``. + + +Here's an example of having ``vtysh`` launched on test failure. + +.. code:: shell + + pytest --vtysh-on-error all-protocol-startup + + +Additionally, one can have the mininet CLI invoked on test failures by +specifying the ``--mininet-on-error`` CLI option as shown in the example below. + +.. code:: shell + + pytest --mininet-on-error all-protocol-startup + +Debugging with GDB +"""""""""""""""""" + +Topotest can automatically launch any daemon with ``gdb``, possibly setting +breakpoints for any test run. This is enabled by specifying 1 or 2 CLI arguments +``--gdb-routers`` and ``--gdb-daemons``. Additionally ``--gdb-breakpoints`` can +be used to automatically set breakpoints in the launched ``gdb`` processes. + +Each of these options can be set to a single value, multiple comma-seperated +values, or ``all``. If ``--gdb-routers`` is empty but ``--gdb_daemons`` is set +then the given daemons will be launched in ``gdb`` on all routers in the test. +Likewise if ``--gdb_routers`` is set, but ``--gdb_daemons`` is empty then all +daemons on the given routers will be launched in ``gdb``. + +Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router +``r1`` with a breakpoint set on ``nb_config_diff`` + +.. code:: shell + + pytest --gdb-routers=r1 \ + --gdb-daemons=bgpd,zebra \ + --gdb-breakpoints=nb_config_diff \ + all-protocol-startup + .. _topotests_docker: Running Tests with Docker diff --git a/doc/developer/tracing.rst b/doc/developer/tracing.rst index ae4d621a8e..63b04585f1 100644 --- a/doc/developer/tracing.rst +++ b/doc/developer/tracing.rst @@ -396,7 +396,7 @@ modifying ``frr.service`` like so: --- a/frr.service +++ b/frr.service @@ -7,6 +7,7 @@ Before=network.target - OnFailure=heartbeat-failed@%n.service + OnFailure=heartbeat-failed@%n [Service] +Environment="LD_PRELOAD=liblttng-ust-fork.so" diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 4433dc9e21..61ed4d3e09 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1420,6 +1420,12 @@ Configuring Peers This command is deprecated and may be removed in a future release. Its use should be avoided. +.. clicmd:: neighbor PEER interface remote-as <internal|external|ASN> + + Configure an unnumbered BGP peer. ``PEER`` should be an interface name. The + session will be established via IPv6 link locals. Use ``internal`` for iBGP + and ``external`` for eBGP sessions, or specify an ASN if you wish. + .. clicmd:: neighbor PEER next-hop-self [all] This command specifies an announced route's nexthop as being equivalent to diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 64ce85503e..af9a7844a2 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -390,6 +390,27 @@ Areas Prevents an *ospfd* ABR from injecting inter-area summaries into the specified stub area. +.. clicmd:: area A.B.C.D nssa + +.. clicmd:: area (0-4294967295) nssa + + Configure the area to be a NSSA (Not-So-Stubby Area). This is an area that + allows OSPF to import external routes into a stub area via a new LSA type + (type 7). An NSSA autonomous system boundary router (ASBR) will generate this + type of LSA. The area border router (ABR) translates the LSA type 7 into LSA + type 5, which is propagated into the OSPF domain. NSSA areas are defined in + RFC 3101. + +.. clicmd:: area A.B.C.D nssa suppress-fa + +.. clicmd:: area (0-4294967295) nssa suppress-fa + + Configure the router to set the forwarding address to 0.0.0.0 in all LSA type 5 + translated from LSA type 7. The router needs to be elected the translator of the + area for this command to take effect. This feature causes routers that are + configured not to advertise forwarding addresses into the backbone to direct + forwarded traffic to the NSSA ABR translator. + .. clicmd:: area A.B.C.D default-cost (0-16777215) diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 01705f607c..d496d437d3 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -206,6 +206,14 @@ Displaying RPKI Display all configured cache servers, whether active or not. +.. clicmd:: show bgp [afi] [safi] <A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> rpki <valid|invalid|notfound> + + Display for the specified prefix or address the bgp paths that match the given rpki state. + +.. clicmd:: show bgp [afi] [safi] rpki <valid|invalid|notfound> + + Display all prefixes that match the given rpki state. + RPKI Configuration Example -------------------------- diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c index 3b232be386..9ada292feb 100644 --- a/eigrpd/eigrp_snmp.c +++ b/eigrpd/eigrp_snmp.c @@ -588,8 +588,7 @@ static struct eigrp_neighbor *eigrpNbrLookup(struct variable *v, oid *name, if (nbr) { *length = v->namelen + IN_ADDR_SIZE + 1; - oid_copy_addr(name + v->namelen, nbr_addr, - IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, nbr_addr); name[v->namelen + IN_ADDR_SIZE] = *ifindex; return nbr; } diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c index 522026dde4..fe6a2f4052 100644 --- a/isisd/isis_snmp.c +++ b/isisd/isis_snmp.c @@ -1037,6 +1037,8 @@ static int isis_snmp_circuit_level_lookup_next( break; } + assert(oid_idx != NULL); + /* We have to check level specified by index */ if (oid_idx[1] < IS_LEVEL_1) { level = IS_LEVEL_1; diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 47fd684eb3..2bac8e7fd5 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -51,7 +51,8 @@ DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists"); typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type, uint8_t tlv_len, struct stream *s, struct sbuf *log, void *dest, int indent); -typedef int (*pack_item_func)(struct isis_item *item, struct stream *s); +typedef int (*pack_item_func)(struct isis_item *item, struct stream *s, + size_t *min_length); typedef void (*free_item_func)(struct isis_item *i); typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s, struct sbuf *log, void *dest, int indent); @@ -367,12 +368,14 @@ static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) } static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, - struct stream *s) + struct stream *s, size_t *min_len) { uint8_t size; - if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) + if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) { + *min_len = ISIS_SUBTLV_MAX_SIZE; return 1; + } if (IS_SUBTLV(exts, EXT_ADM_GRP)) { stream_putc(s, ISIS_SUBTLV_ADMIN_GRP); @@ -828,14 +831,17 @@ static void free_item_prefix_sid(struct isis_item *i) XFREE(MTYPE_ISIS_SUBTLV, i); } -static int pack_item_prefix_sid(struct isis_item *i, struct stream *s) +static int pack_item_prefix_sid(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; uint8_t size = (sid->flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6; - if (STREAM_WRITEABLE(s) < size) + if (STREAM_WRITEABLE(s) < size) { + *min_len = size; return 1; + } stream_putc(s, sid->flags); stream_putc(s, sid->algorithm); @@ -1120,12 +1126,15 @@ static void free_item_area_address(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_area_address(struct isis_item *i, struct stream *s) +static int pack_item_area_address(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_area_address *addr = (struct isis_area_address *)i; - if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len) + if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len) { + *min_len = (unsigned)1 + addr->len; return 1; + } stream_putc(s, addr->len); stream_put(s, addr->addr, addr->len); return 0; @@ -1199,12 +1208,15 @@ static void free_item_oldstyle_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s) +static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; - if (STREAM_WRITEABLE(s) < 11) + if (STREAM_WRITEABLE(s) < 11) { + *min_len = 11; return 1; + } stream_putc(s, r->metric); stream_putc(s, 0x80); /* delay metric - unsupported */ @@ -1268,12 +1280,15 @@ static void free_item_lan_neighbor(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s) +static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; - if (STREAM_WRITEABLE(s) < 6) + if (STREAM_WRITEABLE(s) < 6) { + *min_len = 6; return 1; + } stream_put(s, n->mac, 6); @@ -1333,12 +1348,15 @@ static void free_item_lsp_entry(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_lsp_entry(struct isis_item *i, struct stream *s) +static int pack_item_lsp_entry(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; - if (STREAM_WRITEABLE(s) < 16) + if (STREAM_WRITEABLE(s) < 16) { + *min_len = 16; return 1; + } stream_putw(s, e->rem_lifetime); stream_put(s, e->id, 8); @@ -1413,14 +1431,17 @@ static void free_item_extended_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, item); } -static int pack_item_extended_reach(struct isis_item *i, struct stream *s) +static int pack_item_extended_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_extended_reach *r = (struct isis_extended_reach *)i; size_t len; size_t len_pos; - if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE) + if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE) { + *min_len = 11 + ISIS_SUBTLV_MAX_SIZE; return 1; + } stream_put(s, r->id, sizeof(r->id)); stream_put3(s, r->metric); @@ -1428,7 +1449,7 @@ static int pack_item_extended_reach(struct isis_item *i, struct stream *s) /* Real length will be adjust after adding subTLVs */ stream_putc(s, 11); if (r->subtlvs) - pack_item_ext_subtlvs(r->subtlvs, s); + pack_item_ext_subtlvs(r->subtlvs, s, min_len); /* Adjust length */ len = stream_get_endp(s) - len_pos - 1; stream_putc_at(s, len_pos, len); @@ -1521,12 +1542,15 @@ static void free_item_oldstyle_ip_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s) +static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i; - if (STREAM_WRITEABLE(s) < 12) + if (STREAM_WRITEABLE(s) < 12) { + *min_len = 12; return 1; + } stream_putc(s, r->metric); stream_putc(s, 0x80); /* delay metric - unsupported */ @@ -1676,12 +1700,15 @@ static void free_item_ipv4_address(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_ipv4_address(struct isis_item *i, struct stream *s) +static int pack_item_ipv4_address(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_ipv4_address *a = (struct isis_ipv4_address *)i; - if (STREAM_WRITEABLE(s) < 4) + if (STREAM_WRITEABLE(s) < 4) { + *min_len = 4; return 1; + } stream_put(s, &a->addr, 4); @@ -1737,12 +1764,15 @@ static void free_item_ipv6_address(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_ipv6_address(struct isis_item *i, struct stream *s) +static int pack_item_ipv6_address(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; - if (STREAM_WRITEABLE(s) < 16) + if (STREAM_WRITEABLE(s) < 16) { + *min_len = 16; return 1; + } stream_put(s, &a->addr, 16); @@ -1801,12 +1831,15 @@ static void free_item_mt_router_info(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_mt_router_info(struct isis_item *i, struct stream *s) +static int pack_item_mt_router_info(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_mt_router_info *info = (struct isis_mt_router_info *)i; - if (STREAM_WRITEABLE(s) < 2) + if (STREAM_WRITEABLE(s) < 2) { + *min_len = 2; return 1; + } uint16_t entry = info->mtid; @@ -1961,13 +1994,16 @@ static void free_item_extended_ip_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, item); } -static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s) +static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i; uint8_t control; - if (STREAM_WRITEABLE(s) < 5) + if (STREAM_WRITEABLE(s) < 5) { + *min_len = 5; return 1; + } stream_putl(s, r->metric); control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0; @@ -1976,8 +2012,10 @@ static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s) stream_putc(s, control); - if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) + if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) { + *min_len = 5 + (unsigned)PSIZE(r->prefix.prefixlen); return 1; + } stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen)); if (r->subtlvs) @@ -2443,13 +2481,16 @@ static void free_item_ipv6_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, item); } -static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s) +static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i; uint8_t control; - if (STREAM_WRITEABLE(s) < 6) + if (STREAM_WRITEABLE(s) < 6 + (unsigned)PSIZE(r->prefix.prefixlen)) { + *min_len = 6 + (unsigned)PSIZE(r->prefix.prefixlen); return 1; + } stream_putl(s, r->metric); control = r->down ? ISIS_IPV6_REACH_DOWN : 0; @@ -2459,8 +2500,6 @@ static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s) stream_putc(s, control); stream_putc(s, r->prefix.prefixlen); - if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) - return 1; stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen)); if (r->subtlvs) @@ -2908,23 +2947,30 @@ static void free_item_auth(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_auth(struct isis_item *i, struct stream *s) +static int pack_item_auth(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_auth *auth = (struct isis_auth *)i; - if (STREAM_WRITEABLE(s) < 1) + if (STREAM_WRITEABLE(s) < 1) { + *min_len = 1; return 1; + } stream_putc(s, auth->type); switch (auth->type) { case ISIS_PASSWD_TYPE_CLEARTXT: - if (STREAM_WRITEABLE(s) < auth->length) + if (STREAM_WRITEABLE(s) < auth->length) { + *min_len = 1 + auth->length; return 1; + } stream_put(s, auth->passwd, auth->length); break; case ISIS_PASSWD_TYPE_HMAC_MD5: - if (STREAM_WRITEABLE(s) < 16) + if (STREAM_WRITEABLE(s) < 16) { + *min_len = 1 + 16; return 1; + } auth->offset = stream_get_endp(s); stream_put(s, NULL, 16); break; @@ -3159,14 +3205,14 @@ static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, } static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type, - struct isis_item *i, struct stream *s, + struct isis_item *i, struct stream *s, size_t *min_len, struct isis_tlvs **fragment_tlvs, const struct pack_order_entry *pe, uint16_t mtid) { const struct tlv_ops *ops = tlv_table[context][type]; if (ops && ops->pack_item) { - return ops->pack_item(i, s); + return ops->pack_item(i, s, min_len); } assert(!"Unknown item tlv type!"); @@ -3200,6 +3246,7 @@ static int pack_items_(uint16_t mtid, enum isis_tlv_context context, size_t len_pos, last_len, len; struct isis_item *item = NULL; int rv; + size_t min_len = 0; if (!items->head) return 0; @@ -3227,7 +3274,8 @@ top: last_len = len = 0; for (item = item ? item : items->head; item; item = item->next) { - rv = pack_item(context, type, item, s, fragment_tlvs, pe, mtid); + rv = pack_item(context, type, item, s, &min_len, fragment_tlvs, + pe, mtid); if (rv) goto too_long; @@ -3271,6 +3319,8 @@ too_long: if (!fragment_tlvs) return 1; stream_reset(s); + if (STREAM_WRITEABLE(s) < min_len) + return 1; *fragment_tlvs = new_fragment(new_fragment_arg); goto top; } diff --git a/ldpd/ldp_snmp.c b/ldpd/ldp_snmp.c index 3f59d18aa8..9fb4e46515 100644 --- a/ldpd/ldp_snmp.c +++ b/ldpd/ldp_snmp.c @@ -301,8 +301,7 @@ static uint8_t *ldpEntityTable(struct variable *v, oid name[], size_t *length, /* Append index */ *length = LDP_ENTITY_TOTAL_LEN; - oid_copy_addr(name + v->namelen, &entityLdpId, - IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX; @@ -402,8 +401,7 @@ static uint8_t *ldpEntityStatsTable(struct variable *v, oid name[], /* Append index */ *length = LDP_ENTITY_TOTAL_LEN; - oid_copy_addr(name + v->namelen, &entityLdpId, - IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX; @@ -640,13 +638,11 @@ static uint8_t *ldpHelloAdjacencyTable(struct variable *v, oid name[], size_t *l struct in_addr peerLdpId = ctl_adj->id; - oid_copy_addr(name + v->namelen, &entityLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX; - oid_copy_addr(name + v->namelen + 7, &peerLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen + 7, &peerLdpId); name[v->namelen + 11] = 0; name[v->namelen + 12] = 0; name[v->namelen + 13] = adjacencyIndex; @@ -804,14 +800,12 @@ static uint8_t *ldpPeerTable(struct variable *v, oid name[], size_t *length, memcpy(name, v->name, v->namelen * sizeof(oid)); /* Append index */ - oid_copy_addr(name + v->namelen, &entityLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = entityIndex; - oid_copy_addr(name + v->namelen + 7, &peerLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen + 7, &peerLdpId); name[v->namelen + 11] = 0; name[v->namelen + 12] = 0; @@ -875,14 +869,12 @@ static uint8_t *ldpSessionTable(struct variable *v, oid name[], size_t *length, memcpy(name, v->name, v->namelen * sizeof(oid)); /* Append index */ - oid_copy_addr(name + v->namelen, &entityLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = entityIndex; - oid_copy_addr(name + v->namelen + 7, &peerLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen + 7, &peerLdpId); name[v->namelen + 11] = 0; name[v->namelen + 12] = 0; @@ -961,13 +953,11 @@ static uint8_t *ldpSessionStatsTable(struct variable *v, oid name[], memcpy(name, v->name, v->namelen * sizeof(oid)); /* Append index */ - oid_copy_addr(name + v->namelen, &entityLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = entityIndex; - oid_copy_addr(name + v->namelen + 7, &peerLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen + 7, &peerLdpId); name[v->namelen + 11] = 0; name[v->namelen + 12] = 0; @@ -1147,15 +1137,15 @@ ldpTrapSession(struct nbr * nbr, unsigned int sptrap) entityIndex = LDP_DEFAULT_ENTITY_INDEX; peerLdpId = ctl_nbr->id; - oid_copy_addr(index, &entityLdpId, sizeof(struct in_addr)); - index[4] = 0; - index[5] = 0; - index[6] = entityIndex; - oid_copy_addr(&index[7], &peerLdpId, sizeof(struct in_addr)); - index[11] = 0; - index[12] = 0; + oid_copy_in_addr(index, &entityLdpId); + index[4] = 0; + index[5] = 0; + index[6] = entityIndex; + oid_copy_in_addr(&index[7], &peerLdpId); + index[11] = 0; + index[12] = 0; - index[LDP_PEER_ENTRY_MAX_IDX_LEN] = 0; + index[LDP_PEER_ENTRY_MAX_IDX_LEN] = 0; smux_trap(ldpe_variables, array_size(ldpe_variables), ldp_trap_oid, array_size(ldp_trap_oid), ldp_oid, @@ -462,7 +462,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_NHG_DEL), DESC_ENTRY(ZEBRA_NHG_NOTIFY_OWNER), DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST), - DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY)}; + DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY), + DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_ADD), + DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_DEL)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c index a0634cde4b..8c7a8a58c4 100644 --- a/lib/printf/vfprintf.c +++ b/lib/printf/vfprintf.c @@ -138,7 +138,7 @@ __wcsconv(wchar_t *wcsarg, int prec) * write a uintmax_t in octal (plus one byte). */ #if UINTMAX_MAX <= UINT64_MAX -#define BUF 64 +#define BUF 80 #else #error "BUF must be large enough to format a uintmax_t" #endif diff --git a/lib/smux.h b/lib/smux.h index c063833e41..74447341d8 100644 --- a/lib/smux.h +++ b/lib/smux.h @@ -152,7 +152,8 @@ extern void oid2in_addr(oid[], int, struct in_addr *); extern void oid2in6_addr(oid oid[], struct in6_addr *addr); extern void oid2int(oid oid[], int *dest); extern void *oid_copy(void *, const void *, size_t); -extern void oid_copy_addr(oid[], const struct in_addr *, int); +extern void oid_copy_in_addr(oid[], const struct in_addr *); +extern void oid_copy_in6_addr(oid[], const struct in6_addr *); extern void oid_copy_int(oid oid[], int *val); extern void oid2string(oid oid[], int len, char *string); extern void oid_copy_str(oid oid[], const char *string, int len); diff --git a/lib/snmp.c b/lib/snmp.c index 17a4ed4a1d..23d3f38b31 100644 --- a/lib/snmp.c +++ b/lib/snmp.c @@ -88,13 +88,24 @@ void oid2int(oid oid[], int *dest) *dest = ntohl(network_dest); } -void oid_copy_addr(oid oid[], const struct in_addr *addr, int len) +void oid_copy_in_addr(oid oid[], const struct in_addr *addr) { int i; const uint8_t *pnt; + int len = sizeof(struct in_addr); - if (len == 0) - return; + pnt = (uint8_t *)addr; + + for (i = 0; i < len; i++) + oid[i] = *pnt++; +} + + +void oid_copy_in6_addr(oid oid[], const struct in6_addr *addr) +{ + int i; + const uint8_t *pnt; + int len = sizeof(struct in6_addr); pnt = (uint8_t *)addr; diff --git a/lib/zclient.h b/lib/zclient.h index 43197534a8..5b2298c42d 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -213,6 +213,8 @@ typedef enum { ZEBRA_NHG_ADD, ZEBRA_NHG_DEL, ZEBRA_NHG_NOTIFY_OWNER, + ZEBRA_EVPN_REMOTE_NH_ADD, + ZEBRA_EVPN_REMOTE_NH_DEL, ZEBRA_ERROR, ZEBRA_CLIENT_CAPABILITIES, ZEBRA_OPAQUE_MESSAGE, diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index f6a246500b..2393cd6713 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -1209,7 +1209,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) */ if (old_entry_updated == false) { if ((old == NULL) || (old->type != route->type) - || (old->path.type != route->path.type)) + || (old->path.type != route->path.type) + || (old->path.cost != route->path.cost)) add_route = true; } diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 3497b26656..7894924a8e 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -1096,8 +1096,10 @@ void ospf6_asbr_send_externals_to_area(struct ospf6_area *oa) for (ALL_LSDB(oa->ospf6->lsdb, lsa, lsanext)) { if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) { - zlog_debug("%s: Flooding AS-External LSA %s", - __func__, lsa->name); + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("%s: Flooding AS-External LSA %s", + __func__, lsa->name); + ospf6_flood_area(NULL, lsa, oa); } } diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 2d896546fa..5f4815fec1 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -463,6 +463,19 @@ static void ospf6_flood_process(struct ospf6_neighbor *from, struct ospf6_area *oa; for (ALL_LIST_ELEMENTS(process->area_list, node, nnode, oa)) { + + /* If unknown LSA and U-bit clear, treat as link local + * flooding scope + */ + if (!OSPF6_LSA_IS_KNOWN(lsa->header->type) + && !(ntohs(lsa->header->type) & OSPF6_LSTYPE_UBIT_MASK) + && (oa != OSPF6_INTERFACE(lsa->lsdb->data)->area)) { + + if (IS_OSPF6_DEBUG_FLOODING) + zlog_debug("Unknown LSA, do not flood"); + continue; + } + if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_AREA && oa != OSPF6_AREA(lsa->lsdb->data)) continue; diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index f2a933d878..b4f0c30f12 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -853,11 +853,12 @@ int ospf6_lsa_refresh(struct thread *thread) void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6) { - struct listnode *node; + struct listnode *node, *nnode; struct ospf6_area *oa; struct ospf6_lsa *lsa; const struct route_node *end = NULL; uint32_t type, adv_router; + struct ospf6_interface *oi; ospf6->inst_shutdown = 1; @@ -872,6 +873,19 @@ void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6) lsa = ospf6_lsdb_next(end, lsa); } + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) { + end = ospf6_lsdb_head(oi->lsdb_self, 0, 0, + ospf6->router_id, &lsa); + while (lsa) { + /* RFC 2328 (14.1): Set MAXAGE */ + lsa->header->age = htons(OSPF_LSA_MAXAGE); + /* Flood MAXAGE LSA*/ + ospf6_flood(NULL, lsa); + + lsa = ospf6_lsdb_next(end, lsa); + } + } } type = htons(OSPF6_LSTYPE_AS_EXTERNAL); diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index f3c4798906..b5c97eda3c 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -675,7 +675,8 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) * originate translated LSA */ - if (ospf_translated_nssa_originate(area->ospf, lsa) == NULL) { + if (ospf_translated_nssa_originate(area->ospf, lsa, old) + == NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( "ospf_abr_translate_nssa(): Could not translate Type-7 for %pI4 to Type-5", diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index d494f0fbce..6829c4a347 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -1272,12 +1272,27 @@ void ospf_if_interface(struct interface *ifp) hook_call(ospf_if_update, ifp); } -static int ospf_ifp_create(struct interface *ifp) +uint32_t ospf_if_count_area_params(struct interface *ifp) { - struct ospf *ospf = NULL; struct ospf_if_params *params; struct route_node *rn; uint32_t count = 0; + + params = IF_DEF_PARAMS(ifp); + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) + count++; + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) + if ((params = rn->info) + && OSPF_IF_PARAM_CONFIGURED(params, if_area)) + count++; + + return count; +} + +static int ospf_ifp_create(struct interface *ifp) +{ + struct ospf *ospf = NULL; struct ospf_if_info *oii; if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) @@ -1303,18 +1318,8 @@ static int ospf_ifp_create(struct interface *ifp) if (!ospf) return 0; - params = IF_DEF_PARAMS(ifp); - if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) - if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - if (count > 0) { - ospf->if_ospf_cli_count += count; + if (ospf_if_count_area_params(ifp) > 0) ospf_interface_area_set(ospf, ifp); - } ospf_if_recalculate_output_cost(ifp); @@ -1382,9 +1387,7 @@ static int ospf_ifp_down(struct interface *ifp) static int ospf_ifp_destroy(struct interface *ifp) { struct ospf *ospf; - struct ospf_if_params *params; struct route_node *rn; - uint32_t count = 0; if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) zlog_debug( @@ -1397,18 +1400,8 @@ static int ospf_ifp_destroy(struct interface *ifp) ospf = ospf_lookup_by_vrf_id(ifp->vrf_id); if (ospf) { - params = IF_DEF_PARAMS(ifp); - if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) - if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - if (count > 0) { - ospf->if_ospf_cli_count -= count; + if (ospf_if_count_area_params(ifp) > 0) ospf_interface_area_unset(ospf, ifp); - } } for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index a9534f543d..e2d7327381 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -338,6 +338,8 @@ extern void ospf_if_set_multicast(struct ospf_interface *); extern void ospf_if_interface(struct interface *ifp); +extern uint32_t ospf_if_count_area_params(struct interface *ifp); + DECLARE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd)); DECLARE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd)); diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 6bde5467b2..cb1c565d37 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -1765,7 +1765,14 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, /* copy over Type-7 data to new */ extnew->e[0].tos = ext->e[0].tos; extnew->e[0].route_tag = ext->e[0].route_tag; - extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr; + if (type7->area->suppress_fa) { + extnew->e[0].fwd_addr.s_addr = 0; + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "ospf_lsa_translated_nssa_new(): Suppress forwarding address for %pI4", + &ei.p.prefix); + } else + extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr; new->data->ls_seqnum = type7->data->ls_seqnum; /* add translated flag, checksum and lock new lsa */ @@ -1777,7 +1784,8 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, /* Originate Translated Type-5 for supplied Type-7 NSSA LSA */ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, - struct ospf_lsa *type7) + struct ospf_lsa *type7, + struct ospf_lsa *type5) { struct ospf_lsa *new; struct as_external_lsa *extnew; @@ -1796,6 +1804,10 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, extnew = (struct as_external_lsa *)new->data; + /* Update LSA sequence number from translated Type-5 LSA */ + if (type5) + new->data->ls_seqnum = lsa_seqnum_increment(type5); + if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: Could not install LSA id %pI4", __func__, @@ -1823,6 +1835,8 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, struct ospf_lsa *type5) { struct ospf_lsa *new = NULL; + struct as_external_lsa *extold = NULL; + uint32_t ls_seqnum = 0; /* Sanity checks. */ assert(type7 || type5); @@ -1887,6 +1901,12 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, return NULL; } + extold = (struct as_external_lsa *)type5->data; + if (type7->area->suppress_fa == 1) { + if (extold->e[0].fwd_addr.s_addr == 0) + ls_seqnum = ntohl(type5->data->ls_seqnum); + } + /* Delete LSA from neighbor retransmit-list. */ ospf_ls_retransmit_delete_nbr_as(ospf, type5); @@ -1899,6 +1919,11 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, return NULL; } + if (type7->area->suppress_fa == 1) { + if (extold->e[0].fwd_addr.s_addr == 0) + new->data->ls_seqnum = htonl(ls_seqnum + 1); + } + if (!(new = ospf_lsa_install(ospf, NULL, new))) { flog_warn( EC_OSPF_LSA_INSTALL_FAILURE, diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index f2a0d36e7e..3c1f94e628 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -341,11 +341,12 @@ extern char link_info_set(struct stream **s, struct in_addr id, extern struct in_addr ospf_get_nssa_ip(struct ospf_area *); extern int ospf_translated_nssa_compare(struct ospf_lsa *, struct ospf_lsa *); -extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *, - struct ospf_lsa *, - struct ospf_lsa *); -extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *, - struct ospf_lsa *); +extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, + struct ospf_lsa *type7, + struct ospf_lsa *type5); +extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, + struct ospf_lsa *type7, + struct ospf_lsa *type5); extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, int type); #endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index 8418bbf2b9..43b998ac5b 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -674,7 +674,7 @@ static struct ospf_area *ospfAreaLookup(struct variable *v, oid name[], if (area == NULL) return NULL; - oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, addr); *length = sizeof(struct in_addr) + v->namelen; return area; @@ -800,7 +800,7 @@ static struct ospf_area *ospfStubAreaLookup(struct variable *v, oid name[], if (area == NULL) return NULL; - oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, addr); /* Set TOS 0. */ name[v->namelen + sizeof(struct in_addr)] = 0; *length = v->namelen + sizeof(struct in_addr) + 1; @@ -1008,15 +1008,14 @@ static struct ospf_lsa *ospfLsdbLookup(struct variable *v, oid *name, /* Fill in value. */ offset = name + v->namelen; - oid_copy_addr(offset, area_id, IN_ADDR_SIZE); + oid_copy_in_addr(offset, area_id); offset += IN_ADDR_SIZE; *offset = lsa->data->type; offset++; - oid_copy_addr(offset, &lsa->data->id, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, &lsa->data->id); offset += IN_ADDR_SIZE; - oid_copy_addr(offset, &lsa->data->adv_router, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, + &lsa->data->adv_router); return lsa; } @@ -1170,9 +1169,9 @@ static struct ospf_area_range *ospfAreaRangeLookup(struct variable *v, /* Fill in value. */ offset = name + v->namelen; - oid_copy_addr(offset, area_id, IN_ADDR_SIZE); + oid_copy_in_addr(offset, area_id); offset += IN_ADDR_SIZE; - oid_copy_addr(offset, range_net, IN_ADDR_SIZE); + oid_copy_in_addr(offset, range_net); return range; } @@ -1573,7 +1572,7 @@ static struct ospf_interface *ospfIfLookup(struct variable *v, oid *name, if (oi) { *length = v->namelen + IN_ADDR_SIZE + 1; offset = name + v->namelen; - oid_copy_addr(offset, ifaddr, IN_ADDR_SIZE); + oid_copy_in_addr(offset, ifaddr); offset += IN_ADDR_SIZE; *offset = *ifindex; return oi; @@ -1717,7 +1716,7 @@ static struct ospf_interface *ospfIfMetricLookup(struct variable *v, oid *name, if (oi) { *length = v->namelen + IN_ADDR_SIZE + 1 + 1; offset = name + v->namelen; - oid_copy_addr(offset, ifaddr, IN_ADDR_SIZE); + oid_copy_in_addr(offset, ifaddr); offset += IN_ADDR_SIZE; *offset = *ifindex; offset++; @@ -1906,9 +1905,9 @@ ospfVirtIfLookup(struct variable *v, oid *name, size_t *length, if (vl_data) { *length = v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE; - oid_copy_addr(name + v->namelen, area_id, IN_ADDR_SIZE); - oid_copy_addr(name + v->namelen + IN_ADDR_SIZE, - neighbor, IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, area_id); + oid_copy_in_addr(name + v->namelen + IN_ADDR_SIZE, + neighbor); return vl_data; } } @@ -2083,8 +2082,7 @@ static struct ospf_neighbor *ospfNbrLookup(struct variable *v, oid *name, if (nbr) { *length = v->namelen + IN_ADDR_SIZE + 1; - oid_copy_addr(name + v->namelen, nbr_addr, - IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, nbr_addr); name[v->namelen + IN_ADDR_SIZE] = *ifindex; return nbr; } @@ -2307,10 +2305,9 @@ static struct ospf_lsa *ospfExtLsdbLookup(struct variable *v, oid *name, *offset = OSPF_AS_EXTERNAL_LSA; offset++; - oid_copy_addr(offset, &lsa->data->id, IN_ADDR_SIZE); + oid_copy_in_addr(offset, &lsa->data->id); offset += IN_ADDR_SIZE; - oid_copy_addr(offset, &lsa->data->adv_router, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, &lsa->data->adv_router); return lsa; } @@ -2440,7 +2437,7 @@ static void ospfTrapNbrStateChange(struct ospf_neighbor *on) zlog_info("%s: trap sent: %pI4 now %s", __func__, &on->address.u.prefix4, msgbuf); - oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE); + oid_copy_in_addr(index, &(on->address.u.prefix4)); index[IN_ADDR_SIZE] = 0; smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, @@ -2455,7 +2452,7 @@ static void ospfTrapVirtNbrStateChange(struct ospf_neighbor *on) zlog_info("ospfTrapVirtNbrStateChange trap sent"); - oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE); + oid_copy_in_addr(index, &(on->address.u.prefix4)); index[IN_ADDR_SIZE] = 0; smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, @@ -2499,7 +2496,7 @@ static void ospfTrapIfStateChange(struct ospf_interface *oi) &oi->address->u.prefix4, lookup_msg(ospf_ism_state_msg, oi->state, NULL)); - oid_copy_addr(index, &(oi->address->u.prefix4), IN_ADDR_SIZE); + oid_copy_in_addr(index, &(oi->address->u.prefix4)); index[IN_ADDR_SIZE] = 0; smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, @@ -2514,7 +2511,7 @@ static void ospfTrapVirtIfStateChange(struct ospf_interface *oi) zlog_info("ospfTrapVirtIfStateChange trap sent"); - oid_copy_addr(index, &(oi->address->u.prefix4), IN_ADDR_SIZE); + oid_copy_in_addr(index, &(oi->address->u.prefix4)); index[IN_ADDR_SIZE] = 0; smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index e6835ffc72..134d8b6d1d 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -577,6 +577,7 @@ DEFUN (ospf_network_area, struct prefix_ipv4 p; struct in_addr area_id; int ret, format; + uint32_t count; if (ospf->instance) { vty_out(vty, @@ -584,14 +585,15 @@ DEFUN (ospf_network_area, return CMD_WARNING_CONFIG_FAILED; } - if (ospf->if_ospf_cli_count > 0) { + count = ospf_count_area_params(ospf); + if (count > 0) { vty_out(vty, "Please remove all ip ospf area x.x.x.x commands first.\n"); if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s ospf vrf %s num of %u ip ospf area x config", __func__, ospf->name ? ospf->name : "NIL", - ospf->if_ospf_cli_count); + count); return CMD_WARNING_CONFIG_FAILED; } @@ -1574,6 +1576,58 @@ DEFUN (ospf_area_nssa, return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0); } +DEFUN(ospf_area_nssa_suppress_fa, ospf_area_nssa_suppress_fa_cmd, + "area <A.B.C.D|(0-4294967295)> nssa suppress-fa", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Suppress forwarding address\n") +{ + int idx_ipv4_number = 1; + struct in_addr area_id; + int format; + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, + argv[idx_ipv4_number]->arg); + + ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), + format); + ospf_area_nssa_suppress_fa_set(ospf, area_id); + + ospf_schedule_abr_task(ospf); + + return CMD_SUCCESS; +} + +DEFUN(no_ospf_area_nssa_suppress_fa, no_ospf_area_nssa_suppress_fa_cmd, + "no area <A.B.C.D|(0-4294967295)> nssa suppress-fa", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Suppress forwarding address\n") +{ + int idx_ipv4_number = 2; + struct in_addr area_id; + int format; + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format, + argv[idx_ipv4_number]->arg); + + ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), + format); + ospf_area_nssa_suppress_fa_unset(ospf, area_id); + + ospf_schedule_abr_task(ospf); + + return CMD_SUCCESS; +} + DEFUN (ospf_area_nssa_no_summary, ospf_area_nssa_no_summary_cmd, "area <A.B.C.D|(0-4294967295)> nssa no-summary", @@ -7291,7 +7345,7 @@ DEFUN (show_ip_ospf_database_type_adv_router, bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; - int idx = 0, idx_vrf = 0; + int idx_vrf = 0; uint8_t use_vrf = 0; bool uj = use_json(argc, argv); json_object *json = NULL; @@ -7336,7 +7390,7 @@ DEFUN (show_ip_ospf_database_type_adv_router, } ret = show_ip_ospf_database_type_adv_router_common( - vty, ospf, idx ? 1 : 0, argc, argv, use_vrf, json, uj); + vty, ospf, 0, argc, argv, use_vrf, json, uj); } if (uj) { @@ -8821,10 +8875,8 @@ DEFUN (ip_ospf_area, if (count > 0) { ospf = ospf_lookup_by_vrf_id(ifp->vrf_id); - if (ospf) { + if (ospf) ospf_interface_area_unset(ospf, ifp); - ospf->if_ospf_cli_count -= count; - } } return CMD_NOT_MY_INSTANCE; @@ -8882,10 +8934,8 @@ DEFUN (ip_ospf_area, params->if_area_id_fmt = format; } - if (ospf) { + if (ospf) ospf_interface_area_set(ospf, ifp); - ospf->if_ospf_cli_count++; - } return CMD_SUCCESS; } @@ -8951,7 +9001,6 @@ DEFUN (no_ip_ospf_area, if (ospf) { ospf_interface_area_unset(ospf, ifp); - ospf->if_ospf_cli_count--; ospf_area_check_free(ospf, area_id); } @@ -11823,6 +11872,10 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf) vty_out(vty, " area %s nssa no-summary\n", buf); + if (area->suppress_fa) + vty_out(vty, + " area %s nssa suppress-fa\n", + buf); } if (area->default_cost != 1) @@ -12684,6 +12737,8 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_area_nssa_translate_cmd); install_element(OSPF_NODE, &ospf_area_nssa_no_summary_cmd); install_element(OSPF_NODE, &no_ospf_area_nssa_no_summary_cmd); + install_element(OSPF_NODE, &ospf_area_nssa_suppress_fa_cmd); + install_element(OSPF_NODE, &no_ospf_area_nssa_suppress_fa_cmd); install_element(OSPF_NODE, &no_ospf_area_nssa_cmd); install_element(OSPF_NODE, &ospf_area_default_cost_cmd); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 9856e60130..b1b8324074 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -476,41 +476,11 @@ struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name) static void ospf_init(struct ospf *ospf) { - struct vrf *vrf; - struct interface *ifp; - ospf_opaque_type11_lsa_init(ospf); if (ospf->vrf_id != VRF_UNKNOWN) ospf->oi_running = 1; - /* Activate 'ip ospf area x' configured interfaces for given - * vrf. Activate area on vrf x aware interfaces. - * vrf_enable callback calls router_id_update which - * internally will call ospf_if_update to trigger - * network_run_state - */ - vrf = vrf_lookup_by_id(ospf->vrf_id); - - FOR_ALL_INTERFACES (vrf, ifp) { - struct ospf_if_params *params; - struct route_node *rn; - uint32_t count = 0; - - params = IF_DEF_PARAMS(ifp); - if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) - if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - if (count > 0) { - ospf_interface_area_set(ospf, ifp); - ospf->if_ospf_cli_count += count; - } - } - ospf_router_id_update(ospf); } @@ -554,6 +524,23 @@ struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id) return (vrf->info) ? (struct ospf *)vrf->info : NULL; } +uint32_t ospf_count_area_params(struct ospf *ospf) +{ + struct vrf *vrf; + struct interface *ifp; + uint32_t count = 0; + + if (ospf->vrf_id != VRF_UNKNOWN) { + vrf = vrf_lookup_by_id(ospf->vrf_id); + + FOR_ALL_INTERFACES (vrf, ifp) { + count += ospf_if_count_area_params(ifp); + } + } + + return count; +} + /* It should only be used when processing incoming info update from zebra. * Other situations, it is not sufficient to lookup the ospf instance by * vrf_name only without using the instance number. @@ -1672,6 +1659,7 @@ int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id) /* set NSSA area defaults */ area->no_summary = 0; + area->suppress_fa = 0; area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; area->NSSATranslatorStabilityInterval = @@ -1693,6 +1681,7 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) ospf->anyNSSA--; /* set NSSA area defaults */ area->no_summary = 0; + area->suppress_fa = 0; area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; area->NSSATranslatorStabilityInterval = @@ -1708,6 +1697,32 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) return 1; } +int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + area->suppress_fa = 1; + + return 1; +} + +int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + area->suppress_fa = 0; + + return 1; +} + int ospf_area_nssa_translator_role_set(struct ospf *ospf, struct in_addr area_id, int role) { diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 5d64ee9ba2..45e011fece 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -310,11 +310,6 @@ struct ospf { /* Statistics for LSA used for new instantiation. */ uint32_t rx_lsa_count; - /* Counter of "ip ospf area x.x.x.x" used - * for mutual exclusion of network command under - * router ospf or ip ospf area x under interface. */ - uint32_t if_ospf_cli_count; - struct route_table *distance_table; /* Used during ospf instance going down send LSDB @@ -476,7 +471,7 @@ struct ospf_area { int shortcut_capability; /* Other ABRs agree on S-bit */ uint32_t default_cost; /* StubDefaultCost. */ int auth_type; /* Authentication type. */ - + int suppress_fa; /* Suppress forwarding address in NSSA ABR */ uint8_t NSSATranslatorRole; /* NSSA configured role */ #define OSPF_NSSA_ROLE_NEVER 0 @@ -650,6 +645,7 @@ extern struct ospf *ospf_new_alloc(unsigned short instance, const char *name); extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name); extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id); +extern uint32_t ospf_count_area_params(struct ospf *ospf); extern void ospf_finish(struct ospf *); extern void ospf_process_refresh_data(struct ospf *ospf, bool reset); extern void ospf_router_id_update(struct ospf *ospf); @@ -668,6 +664,10 @@ extern int ospf_area_no_summary_set(struct ospf *, struct in_addr); extern int ospf_area_no_summary_unset(struct ospf *, struct in_addr); extern int ospf_area_nssa_set(struct ospf *, struct in_addr); extern int ospf_area_nssa_unset(struct ospf *, struct in_addr, int); +extern int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, + struct in_addr area_id); +extern int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, + struct in_addr area_id); extern int ospf_area_nssa_translator_role_set(struct ospf *, struct in_addr, int); extern int ospf_area_export_list_set(struct ospf *, struct ospf_area *, diff --git a/pceplib/pcep_timers.c b/pceplib/pcep_timers.c index e9d9d4b21d..d0a2349d05 100644 --- a/pceplib/pcep_timers.c +++ b/pceplib/pcep_timers.c @@ -457,10 +457,17 @@ void pceplib_external_timer_expire_handler(void *data) } pcep_timer *timer = (pcep_timer *)data; + pthread_mutex_lock(&timers_context_->timer_list_lock); ordered_list_node *timer_node = ordered_list_find2(timers_context_->timer_list, timer, timer_list_node_timer_ptr_compare); + + /* Remove timer from list */ + if (timer_node) + ordered_list_remove_node2(timers_context_->timer_list, + timer_node); + pthread_mutex_unlock(&timers_context_->timer_list_lock); /* Cannot continue if the timer does not exist */ @@ -474,9 +481,5 @@ void pceplib_external_timer_expire_handler(void *data) timers_context_->expire_handler(timer->data, timer->timer_id); - pthread_mutex_lock(&timers_context_->timer_list_lock); - ordered_list_remove_node2(timers_context_->timer_list, timer_node); - pthread_mutex_unlock(&timers_context_->timer_list_lock); - pceplib_free(PCEPLIB_INFRA, timer); } diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index ae5b7940e9..4bbe7d35f0 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1094,6 +1094,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim, json_object_int_add(json_row, "helloPeriod", pim_ifp->pim_hello_period); + json_object_int_add(json_row, "holdTime", + PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); json_object_string_add(json_row, "helloTimer", hello_timer); json_object_string_add(json_row, "helloStatStart", @@ -1243,6 +1245,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim, vty_out(vty, "------\n"); vty_out(vty, "Period : %d\n", pim_ifp->pim_hello_period); + vty_out(vty, "HoldTime : %d\n", + PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); vty_out(vty, "Timer : %s\n", hello_timer); vty_out(vty, "StatStart : %s\n", stat_uptime); vty_out(vty, "Receive : %d\n", @@ -8987,7 +8991,7 @@ DEFUN (interface_ip_pim_hello, DEFUN (interface_no_ip_pim_hello, interface_no_ip_pim_hello_cmd, - "no ip pim hello [(1-180) (1-180)]", + "no ip pim hello [(1-180) [(1-180)]]", NO_STR IP_STR PIM_STR diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index e852a87683..475e393cf0 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1830,6 +1830,7 @@ int lib_interface_pim_hello_interval_modify(struct nb_cb_modify_args *args) pim_ifp = ifp->info; pim_ifp->pim_hello_period = yang_dnode_get_uint8(args->dnode, NULL); + pim_ifp->pim_default_holdtime = -1; break; } diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c index 37bce7484c..b922d66912 100644 --- a/ripd/rip_snmp.c +++ b/ripd/rip_snmp.c @@ -271,7 +271,7 @@ static struct interface *rip2IfLookup(struct variable *v, oid name[], if (ifp == NULL) return NULL; - oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, addr); *length = v->namelen + sizeof(struct in_addr); @@ -320,8 +320,8 @@ static struct rip_peer *rip2PeerLookup(struct variable *v, oid name[], || (peer->domain > (int)name[v->namelen + sizeof(struct in_addr)])) { - oid_copy_addr(name + v->namelen, &peer->addr, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, + &peer->addr); name[v->namelen + sizeof(struct in_addr)] = peer->domain; *length = @@ -334,8 +334,7 @@ static struct rip_peer *rip2PeerLookup(struct variable *v, oid name[], if (!peer) return NULL; - oid_copy_addr(name + v->namelen, &peer->addr, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &peer->addr); name[v->namelen + sizeof(struct in_addr)] = peer->domain; *length = sizeof(struct in_addr) + v->namelen + 1; diff --git a/tests/lib/test_table.c b/tests/lib/test_table.c index 290657bd56..9b6539e3bc 100644 --- a/tests/lib/test_table.c +++ b/tests/lib/test_table.c @@ -20,7 +20,7 @@ */ #include <zebra.h> - +#include "printfrr.h" #include "prefix.h" #include "table.h" @@ -113,7 +113,7 @@ static void print_subtree(struct route_node *rn, const char *legend, printf(" "); } - printf("%s: %pFX", legend, &rn->p); + printfrr("%s: %pFX", legend, &rn->p); if (!rn->info) { printf(" (internal)"); } diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 04e9961f10..7ad5d8c9ab 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -2,13 +2,14 @@ Topotest conftest.py file. """ +import os +import pdb +import pytest + from lib.topogen import get_topogen, diagnose_env from lib.topotest import json_cmp_result +from lib.topotest import g_extra_config as topotest_extra_config from lib.topolog import logger -import pytest - -topology_only = False - def pytest_addoption(parser): """ @@ -16,20 +17,72 @@ def pytest_addoption(parser): only run the setup_module() to setup the topology without running any tests. """ parser.addoption( + "--gdb-breakpoints", + metavar="SYMBOL[,SYMBOL...]", + help="Comma-separated list of functions to set gdb breakpoints on", + ) + + parser.addoption( + "--gdb-daemons", + metavar="DAEMON[,DAEMON...]", + help="Comma-separated list of daemons to spawn gdb on, or 'all'", + ) + + parser.addoption( + "--gdb-routers", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn gdb on, or 'all'", + ) + + parser.addoption( + "--mininet-on-error", + action="store_true", + help="Mininet cli on test failure", + ) + + parser.addoption( + "--pause-after", + action="store_true", + help="Pause after each test", + ) + + parser.addoption( + "--shell", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn shell on, or 'all'", + ) + + parser.addoption( + "--shell-on-error", + action="store_true", + help="Spawn shell on all routers on test failure", + ) + + parser.addoption( "--topology-only", action="store_true", help="Only set up this topology, don't run tests", ) + parser.addoption( + "--vtysh", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn vtysh on, or 'all'", + ) + + parser.addoption( + "--vtysh-on-error", + action="store_true", + help="Spawn vtysh on all routers on test failure", + ) + def pytest_runtest_call(): """ This function must be run after setup_module(), it does standarized post setup routines. It is only being used for the 'topology-only' option. """ - global topology_only - - if topology_only: + if topotest_extra_config["topology_only"]: tgen = get_topogen() if tgen is not None: # Allow user to play with the setup. @@ -42,6 +95,8 @@ def pytest_assertrepr_compare(op, left, right): """ Show proper assertion error message for json_cmp results. """ + del op + json_result = left if not isinstance(json_result, json_cmp_result): json_result = right @@ -52,43 +107,105 @@ def pytest_assertrepr_compare(op, left, right): def pytest_configure(config): - "Assert that the environment is correctly configured." - - global topology_only + """ + Assert that the environment is correctly configured, and get extra config. + """ if not diagnose_env(): - pytest.exit("enviroment has errors, please read the logs") + pytest.exit("environment has errors, please read the logs") + + gdb_routers = config.getoption("--gdb-routers") + gdb_routers = gdb_routers.split(",") if gdb_routers else [] + topotest_extra_config["gdb_routers"] = gdb_routers + + gdb_daemons = config.getoption("--gdb-daemons") + gdb_daemons = gdb_daemons.split(",") if gdb_daemons else [] + topotest_extra_config["gdb_daemons"] = gdb_daemons + + gdb_breakpoints = config.getoption("--gdb-breakpoints") + gdb_breakpoints = gdb_breakpoints.split(",") if gdb_breakpoints else [] + topotest_extra_config["gdb_breakpoints"] = gdb_breakpoints + + mincli_on_error = config.getoption("--mininet-on-error") + topotest_extra_config["mininet_on_error"] = mincli_on_error - if config.getoption("--topology-only"): - topology_only = True + shell = config.getoption("--shell") + topotest_extra_config["shell"] = shell.split(",") if shell else [] + + pause_after = config.getoption("--pause-after") + + shell_on_error = config.getoption("--shell-on-error") + topotest_extra_config["shell_on_error"] = shell_on_error + + vtysh = config.getoption("--vtysh") + topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else [] + + vtysh_on_error = config.getoption("--vtysh-on-error") + topotest_extra_config["vtysh_on_error"] = vtysh_on_error + + topotest_extra_config["pause_after"] = ( + pause_after or shell or vtysh + ) + + topotest_extra_config["topology_only"] = config.getoption("--topology-only") def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" - # Nothing happened - if call.excinfo is None: - return - parent = item.parent - modname = parent.module.__name__ + # Nothing happened + if call.when == "call": + pause = topotest_extra_config["pause_after"] + else: + pause = False - # Treat skips as non errors - if call.excinfo.typename != "AssertionError": - logger.info( - 'assert skipped at "{}/{}": {}'.format( - modname, item.name, call.excinfo.value + if call.excinfo is None: + error = False + else: + parent = item.parent + modname = parent.module.__name__ + + # Treat skips as non errors, don't pause after + if call.excinfo.typename != "AssertionError": + pause = False + error = False + logger.info( + 'assert skipped at "{}/{}": {}'.format( + modname, item.name, call.excinfo.value + ) + ) + else: + error = True + # Handle assert failures + parent._previousfailed = item # pylint: disable=W0212 + logger.error( + 'assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value) ) - ) - return - - # Handle assert failures - parent._previousfailed = item - logger.error( - 'assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value) - ) - # (topogen) Set topology error to avoid advancing in the test. - tgen = get_topogen() - if tgen is not None: - # This will cause topogen to report error on `routers_have_failure`. - tgen.set_error("{}/{}".format(modname, item.name)) + # (topogen) Set topology error to avoid advancing in the test. + tgen = get_topogen() + if tgen is not None: + # This will cause topogen to report error on `routers_have_failure`. + tgen.set_error("{}/{}".format(modname, item.name)) + + + if error and topotest_extra_config["shell_on_error"]: + for router in tgen.routers(): + pause = True + tgen.net[router].runInWindow(os.getenv("SHELL", "bash")) + + if error and topotest_extra_config["vtysh_on_error"]: + for router in tgen.routers(): + pause = True + tgen.net[router].runInWindow("vtysh") + + if error and topotest_extra_config["mininet_on_error"]: + tgen.mininet_cli() + + if pause: + try: + user = raw_input('Testing paused, "pdb" to debug, "Enter" to continue: ') + except NameError: + user = input('Testing paused, "pdb" to debug, "Enter" to continue: ') + if user.strip() == "pdb": + pdb.set_trace() diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 70b2cfd648..104b215078 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -50,7 +50,9 @@ from mininet.node import Node, OVSSwitch, Host from mininet.log import setLogLevel, info from mininet.cli import CLI from mininet.link import Intf +from mininet.term import makeTerm +g_extra_config = {} def gdb_core(obj, daemon, corefiles): gdbcmds = """ @@ -1341,6 +1343,37 @@ class Router(Node): logger.info("No daemon {} known".format(daemon)) # print "Daemons after:", self.daemons + # Run a command in a new window (gnome-terminal, screen, tmux, xterm) + def runInWindow(self, cmd, title=None): + topo_terminal = os.getenv("FRR_TOPO_TERMINAL") + if topo_terminal or ( + "TMUX" not in os.environ and "STY" not in os.environ + ): + term = topo_terminal if topo_terminal else "xterm" + makeTerm( + self, + title=title if title else cmd, + term=term, + cmd=cmd) + else: + nscmd = "sudo nsenter -m -n -t {} {}".format(self.pid, cmd) + if "TMUX" in os.environ: + self.cmd("tmux select-layout main-horizontal") + wcmd = "tmux split-window -h" + cmd = "{} {}".format(wcmd, nscmd) + elif "STY" in os.environ: + if os.path.exists( + "/run/screen/S-{}/{}".format( + os.environ['USER'], os.environ['STY'] + ) + ): + wcmd = "screen" + else: + wcmd = "sudo -u {} screen".format(os.environ["SUDO_USER"]) + cmd = "{} {}".format(wcmd, nscmd) + self.cmd(cmd) + + def startRouter(self, tgen=None): # Disable integrated-vtysh-config self.cmd( @@ -1393,6 +1426,14 @@ class Router(Node): return "LDP/MPLS Tests need mpls kernel modules" self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels") + shell_routers = g_extra_config["shell"] + if "all" in shell_routers or self.name in shell_routers: + self.runInWindow(os.getenv("SHELL", "bash")) + + vtysh_routers = g_extra_config["vtysh"] + if "all" in vtysh_routers or self.name in vtysh_routers: + self.runInWindow("vtysh") + if self.daemons["eigrpd"] == 1: eigrpd_path = os.path.join(self.daemondir, "eigrpd") if not os.path.isfile(eigrpd_path): @@ -1419,6 +1460,10 @@ class Router(Node): def startRouterDaemons(self, daemons=None): "Starts all FRR daemons for this router." + gdb_breakpoints = g_extra_config["gdb_breakpoints"] + gdb_daemons = g_extra_config["gdb_daemons"] + gdb_routers = g_extra_config["gdb_routers"] + bundle_data = "" if os.path.exists("/etc/frr/support_bundle_commands.conf"): @@ -1448,7 +1493,7 @@ class Router(Node): # If `daemons` was specified then some upper API called us with # specific daemons, otherwise just use our own configuration. daemons_list = [] - if daemons != None: + if daemons is not None: daemons_list = daemons else: # Append all daemons configured. @@ -1456,47 +1501,67 @@ class Router(Node): if self.daemons[daemon] == 1: daemons_list.append(daemon) - # Start Zebra first - if "zebra" in daemons_list: - zebra_path = os.path.join(self.daemondir, "zebra") - zebra_option = self.daemons_options["zebra"] - self.cmd( - "ASAN_OPTIONS=log_path=zebra.asan {0} {1} --log file:zebra.log --log-level debug -s 90000000 -d > zebra.out 2> zebra.err".format( - zebra_path, zebra_option + def start_daemon(daemon, extra_opts=None): + daemon_opts = self.daemons_options.get(daemon, "") + rediropt = " > {0}.out 2> {0}.err".format(daemon) + if daemon == "snmpd": + binary = "/usr/sbin/snmpd" + cmdenv = "" + cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format( + daemon_opts + ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype) + else: + binary = os.path.join(self.daemondir, daemon) + cmdenv = "ASAN_OPTIONS=log_path={0}.asan".format(daemon) + cmdopt = "{} --log file:{}.log --log-level debug".format( + daemon_opts, daemon ) - ) - logger.debug("{}: {} zebra started".format(self, self.routertype)) + if extra_opts: + cmdopt += " " + extra_opts + + if ( + (gdb_routers or gdb_daemons) + and (not gdb_routers + or self.name in gdb_routers + or "all" in gdb_routers) + and (not gdb_daemons + or daemon in gdb_daemons + or "all" in gdb_daemons) + ): + if daemon == "snmpd": + cmdopt += " -f " + + cmdopt += rediropt + gdbcmd = "sudo -E gdb " + binary + if gdb_breakpoints: + gdbcmd += " -ex 'set breakpoint pending on'" + for bp in gdb_breakpoints: + gdbcmd += " -ex 'b {}'".format(bp) + gdbcmd += " -ex 'run {}'".format(cmdopt) + + self.runInWindow(gdbcmd, daemon) + else: + if daemon != "snmpd": + cmdopt += " -d " + cmdopt += rediropt + self.cmd(" ".join([cmdenv, binary, cmdopt])) + logger.info("{}: {} {} started".format(self, self.routertype, daemon)) - # Remove `zebra` so we don't attempt to start it again. + + # Start Zebra first + if "zebra" in daemons_list: + start_daemon("zebra", "-s 90000000") while "zebra" in daemons_list: daemons_list.remove("zebra") # Start staticd next if required if "staticd" in daemons_list: - staticd_path = os.path.join(self.daemondir, "staticd") - staticd_option = self.daemons_options["staticd"] - self.cmd( - "ASAN_OPTIONS=log_path=staticd.asan {0} {1} --log file:staticd.log --log-level debug -d > staticd.out 2> staticd.err".format( - staticd_path, staticd_option - ) - ) - logger.debug("{}: {} staticd started".format(self, self.routertype)) - - # Remove `staticd` so we don't attempt to start it again. + start_daemon("staticd") while "staticd" in daemons_list: daemons_list.remove("staticd") if "snmpd" in daemons_list: - snmpd_path = "/usr/sbin/snmpd" - snmpd_option = self.daemons_options["snmpd"] - self.cmd( - "{0} {1} -C -c /etc/frr/snmpd.conf -p /var/run/{2}/snmpd.pid -x /etc/frr/agentx > snmpd.out 2> snmpd.err".format( - snmpd_path, snmpd_option, self.routertype - ) - ) - logger.info("{}: {} snmpd started".format(self, self.routertype)) - - # Remove `snmpd` so we don't attempt to start it again. + start_daemon("snmpd") while "snmpd" in daemons_list: daemons_list.remove("snmpd") @@ -1508,17 +1573,9 @@ class Router(Node): # Now start all the other daemons for daemon in daemons_list: - # Skip disabled daemons and zebra if self.daemons[daemon] == 0: continue - - daemon_path = os.path.join(self.daemondir, daemon) - self.cmd( - "ASAN_OPTIONS=log_path={2}.asan {0} {1} --log file:{2}.log --log-level debug -d > {2}.out 2> {2}.err".format( - daemon_path, self.daemons_options.get(daemon, ""), daemon - ) - ) - logger.debug("{}: {} {} started".format(self, self.routertype, daemon)) + start_daemon(daemon) # Check if daemons are running. rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype) diff --git a/tests/topotests/ospf_suppress_fa/__init__.py b/tests/topotests/ospf_suppress_fa/__init__.py new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/__init__.py diff --git a/tests/topotests/ospf_suppress_fa/r1/ospfd.conf b/tests/topotests/ospf_suppress_fa/r1/ospfd.conf new file mode 100644 index 0000000000..c02be35b14 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/ospfd.conf @@ -0,0 +1,9 @@ +! +interface r1-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 10.0.12.0/24 area 0 +! diff --git a/tests/topotests/ospf_suppress_fa/r1/zebra.conf b/tests/topotests/ospf_suppress_fa/r1/zebra.conf new file mode 100644 index 0000000000..c1e31fb474 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/zebra.conf @@ -0,0 +1,4 @@ +! +interface r1-eth0 + ip address 10.0.12.1/24 +! diff --git a/tests/topotests/ospf_suppress_fa/r2/ospfd.conf b/tests/topotests/ospf_suppress_fa/r2/ospfd.conf new file mode 100644 index 0000000000..ebc7d252fd --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r2/ospfd.conf @@ -0,0 +1,16 @@ +! +interface r2-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r2-eth1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 10.0.12.0/24 area 0 + network 10.0.23.0/24 area 1 + area 1 nssa +! diff --git a/tests/topotests/ospf_suppress_fa/r2/zebra.conf b/tests/topotests/ospf_suppress_fa/r2/zebra.conf new file mode 100644 index 0000000000..9f1a26349e --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r2/zebra.conf @@ -0,0 +1,7 @@ +! +interface r2-eth0 + ip address 10.0.12.2/24 +! +interface r2-eth1 + ip address 10.0.23.2/24 +! diff --git a/tests/topotests/ospf_suppress_fa/r3/ospfd.conf b/tests/topotests/ospf_suppress_fa/r3/ospfd.conf new file mode 100644 index 0000000000..08be11a7b7 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r3/ospfd.conf @@ -0,0 +1,11 @@ +! +interface r3-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + redistribute static + network 10.0.23.0/24 area 1 + area 1 nssa +! diff --git a/tests/topotests/ospf_suppress_fa/r3/zebra.conf b/tests/topotests/ospf_suppress_fa/r3/zebra.conf new file mode 100644 index 0000000000..f76cbf74d2 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r3/zebra.conf @@ -0,0 +1,8 @@ +! +ip route 3.3.1.1/32 Null0 +ip route 3.3.2.2/32 Null0 +ip route 3.3.3.3/32 Null0 +! +interface r3-eth0 + ip address 10.0.23.3/24 +! diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot new file mode 100644 index 0000000000..1036658f1a --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot @@ -0,0 +1,66 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph ospf_topo1 { + label="ospf suppress-fa"; + + # Routers + r1 [ + label="r1\nrtr-id 10.0.12.1", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + r2 [ + label="r2 (ABR)\nrtr-id 10.0.23.2", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + r3 [ + label="r3 (ASBR)\nrtr-id 10.0.23.3", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + + # Switches + s1 [ + label="s1\n10.0.12.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + label="s2\n10.0.23.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + subgraph cluster0 { + label="area 0" + r1 -- s1 [label="eth1\n.1"]; + r2 -- s1 [label="eth1\n.2"]; + } + + subgraph cluster1 { + label="area 1\nNSSA" + r2 -- s2 [label="eth2\n.2"]; + r3 -- s2 [label="eth1\n.3"]; + } + + { rank=same; r1; r2; r3; } +} diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg Binary files differnew file mode 100644 index 0000000000..2907d799f5 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py new file mode 100644 index 0000000000..74d609c57e --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python + +# +# test_ospf_suppres_fa.py +# Carles Kishimoto +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ospf_suppres_fa.py: Test OSPF suppress-fa feature +- Topology: r1 --- R2 (ABR) --- R3 (redistribute static) + +test_ospf_set_suppress_fa() + 1) R1: Get a dict[LSA_ID] = fwd_addr for all type 5 LSA + 2) R2: Configure: area 1 nssa suppress-fa + 3) R1: Get a dict[LSA_ID] and compare fwd_address with 0.0.0.0 + +test_ospf_unset_suppress_fa() + 4) R2: Configure: no area 1 nssa suppress-fa + 5) R1: Get a dict[LSA_ID] = fwd_addr and compare it with the dict obtained in 1) +""" + +import os +import sys +import re +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class NetworkTopo(Topo): + "OSPF topology builder" + + def build(self, *_args, **_opts): + "Build function" + + tgen = get_topogen(self) + + # Create routers + for router in range(1, 4): + tgen.add_router("r{}".format(router)) + + # R1-R2 backbone area + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # R2-R3 NSSA area + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(NetworkTopo, mod.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registred routers, load the zebra and ospf configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + + tgen = get_topogen() + tgen.stop_topology() + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(10, "Waiting for OSPF convergence") + +def ospf_configure_suppress_fa(router_name, area): + "Configure OSPF suppress-fa in router_name" + + tgen = get_topogen() + router = tgen.gears[router_name] + router.vtysh_cmd("conf t\nrouter ospf\narea {} nssa suppress-fa\nexit\n".format(area)) + +def ospf_unconfigure_suppress_fa(router_name, area): + "Remove OSPF suppress-fa in router_name" + + tgen = get_topogen() + router = tgen.gears[router_name] + router.vtysh_cmd("conf t\nrouter ospf\nno area {} nssa suppress-fa\nexit\n".format(area)) + +def ospf_get_lsa_type5(router_name): + "Return a dict with link state id as key and forwarding addresses as value" + + result = dict() + tgen = get_topogen() + router = tgen.gears[router_name] + cmd = "show ip ospf database external\n" + output = topotest.normalize_text(router.vtysh_cmd(cmd)) + for line in output.splitlines(): + re0 = re.match(r"\s+Link State ID: (\S+) \(External Network Number\)", line) + if re0: + lsa = re0.group(1) + re1 = re.match(r"\s+Forward Address: (\S+)", line) + if re1: + result[lsa] = re1.group(1) + return result + +@pytest.fixture(scope='module', name='original') +def test_ospf_set_suppress_fa(): + "Test OSPF area [x] nssa suppress-fa" + + # Get current forwarding address for each LSA type-5 in r1 + initial = ospf_get_lsa_type5("r1") + + # Configure suppres-fa in r2 area 1 + ospf_configure_suppress_fa("r2", "1") + topotest.sleep(10, "Waiting for OSPF convergence") + + # Check forwarding address on r1 for all statics is 0.0.0.0 + assertmsg = "Forwarding address is not 0.0.0.0 after enabling OSPF suppress-fa" + suppress = ospf_get_lsa_type5("r1") + for prefix in suppress: + assert suppress[prefix] == "0.0.0.0", assertmsg + + # Return the original forwarding addresses so we can compare them + # in the test_ospf_unset_supress_fa + return initial + +def test_ospf_unset_supress_fa(original): + "Test OSPF no area [x] nssa suppress-fa" + + # Remove suppress-fa in r2 area 1 + ospf_unconfigure_suppress_fa("r2", "1") + topotest.sleep(10, "Waiting for OSPF convergence") + + # Check forwarding address is the original value on r1 for all statics + assertmsg = "Forwarding address is not correct after removing OSPF suppress-fa" + restore = ospf_get_lsa_type5("r1") + for prefix in restore: + assert restore[prefix] == original[prefix], assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index 0bc6547994..f902d3dd21 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -84,6 +84,25 @@ CMD_LIST_END # CMD_LIST_END # PIM Support Bundle Command List -# PROC_NAME:pim -# CMD_LIST_START -# CMD_LIST_END +PROC_NAME:pim +CMD_LIST_START +show ip multicast +show ip pim interface +show ip pim interface traffic +show ip pim nexthop +show ip pim neighbor +show ip pim bsr +show ip pim bsrp-info +show ip pim bsm-database +show ip pim rp-info +show ip igmp groups +show ip igmp interface +show ip igmp join +show ip igmp sources +show ip pim upstream +show ip mroute +show ip pim join +show ip pim state +show ip pim statistics +show ip pim rpf +CMD_LIST_END diff --git a/tools/frr.service.in b/tools/frr.service.in index 836ce06be7..f67bc41b09 100644 --- a/tools/frr.service.in +++ b/tools/frr.service.in @@ -4,7 +4,7 @@ Documentation=https://frrouting.readthedocs.io/en/latest/setup.html Wants=network.target After=network-pre.target systemd-sysctl.service Before=network.target -OnFailure=heartbeat-failed@%n.service +OnFailure=heartbeat-failed@%n [Service] Nice=-5 diff --git a/tools/frr@.service.in b/tools/frr@.service.in index 1e5d252325..7b94a11f5b 100644 --- a/tools/frr@.service.in +++ b/tools/frr@.service.in @@ -4,7 +4,7 @@ Documentation=https://frrouting.readthedocs.io/en/latest/setup.html Wants=network.target After=network-pre.target systemd-sysctl.service Before=network.target -OnFailure=heartbeat-failed@%n.service +OnFailure=heartbeat-failed@%n [Service] Nice=-5 diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 3dbc6a1b43..2d925dbac3 100644 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -149,6 +149,10 @@ daemon_prep() { daemon_start() { local dmninst daemon inst args instopt wrap bin + + all=false + [ "$1" = "--all" ] && { all=true; shift; } + daemon_inst "$1" ulimit -n $MAX_FDS > /dev/null 2> /dev/null @@ -165,7 +169,11 @@ daemon_start() { if eval "$all_wrap $wrap $bin $nsopt -d $frr_global_options $instopt $args"; then log_success_msg "Started $dmninst" - vtysh_b "$daemon" + if $all; then + debug "Skipping startup of vtysh until all have started" + else + vtysh_b "$daemon" + fi else log_failure_msg "Failed to start $dmninst!" fi @@ -237,8 +245,9 @@ print_status() { all_start() { daemon_list daemons for dmninst in $daemons; do - daemon_start "$dmninst" + daemon_start --all "$dmninst" done + vtysh_b } all_stop() { diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index f959ff8be5..2070649ec2 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -294,6 +294,9 @@ module frr-pim { type uint8 { range "1..180"; } + must ". > ./../hello-interval" { + error-message "HoldTime must be greater than Hello"; + } description "Hello holdtime"; } diff --git a/yang/subdir.am b/yang/subdir.am index 47fc508901..da4432b622 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -86,3 +86,9 @@ endif if PATHD dist_yangmodels_DATA += yang/frr-pathd.yang endif + +CLEANFILES += \ + yang/*.c \ + yang/ietf/*.c \ + yang/confd/*.c \ + # diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 63ba6cd8d9..b482914418 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -3350,6 +3350,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_NHG_ADD] = zread_nhg_add, [ZEBRA_NHG_DEL] = zread_nhg_del, [ZEBRA_ROUTE_NOTIFY_REQUEST] = zread_route_notify_request, + [ZEBRA_EVPN_REMOTE_NH_ADD] = zebra_evpn_proc_remote_nh, + [ZEBRA_EVPN_REMOTE_NH_DEL] = zebra_evpn_proc_remote_nh, }; /* diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 1c258a04f7..cabba707a0 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -3867,6 +3867,47 @@ static void zebra_evpn_mh_startup_delay_timer_start(const char *rc) } } +/***************************************************************************** + * Nexthop management: nexthops associated with Type-2 routes that have + * an ES as destination are consolidated by BGP into a per-VRF nh->rmac + * mapping which is the installed as a remote neigh/fdb entry with a + * dummy (type-1) prefix referencing it. + * This handling is needed because Type-2 routes with ES as dest use NHG + * that are setup using EAD routes (i.e. such NHGs do not include the + * RMAC info). + ****************************************************************************/ +void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + vrf_id_t vrf_id; + struct ipaddr nh; + struct ethaddr rmac; + struct prefix_evpn dummy_prefix; + + s = msg; + vrf_id = stream_getl(s); + stream_get(&nh, s, sizeof(nh)); + + memset(&dummy_prefix, 0, sizeof(dummy_prefix)); + dummy_prefix.family = AF_EVPN; + dummy_prefix.prefixlen = (sizeof(struct evpn_addr) * 8); + dummy_prefix.prefix.route_type = 1; /* XXX - fixup to type-1 def */ + + if (hdr->command == ZEBRA_EVPN_REMOTE_NH_ADD) { + stream_get(&rmac, s, sizeof(rmac)); + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("evpn remote nh %d %pIA rmac %pEA add", + vrf_id, &nh, &rmac); + zebra_vxlan_evpn_vrf_route_add(vrf_id, &rmac, &nh, + (struct prefix *)&dummy_prefix); + } else { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("evpn remote nh %d %pIA del", vrf_id, &nh); + zebra_vxlan_evpn_vrf_route_del(vrf_id, &nh, + (struct prefix *)&dummy_prefix); + } +} + /*****************************************************************************/ void zebra_evpn_mh_config_write(struct vty *vty) { diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 2361a70bff..8861e80cee 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -382,5 +382,6 @@ extern void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, extern void zebra_evpn_acc_bd_svi_mac_add(struct interface *vlan_if); extern void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es, struct interface *ifp, bool bypass); +extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS); #endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index bc2eac7a0b..4cd3b60a0f 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -149,6 +149,11 @@ static int host_rb_entry_compare(const struct host_rb_entry *hle1, } else if (hle1->p.family == AF_INET6) { return memcmp(&hle1->p.u.prefix6, &hle2->p.u.prefix6, IPV6_MAX_BYTELEN); + } else if (hle1->p.family == AF_EVPN) { + /* a single dummy prefix of route_type BGP_EVPN_AD_ROUTE is + * used for all nexthops associated with a non-zero ESI + */ + return 0; } else { zlog_debug("%s: Unexpected family type: %d", __func__, hle1->p.family); |
