diff options
89 files changed, 2988 insertions, 698 deletions
diff --git a/Makefile.am b/Makefile.am index 8c7bde9d4c..44d2ab8e72 100644 --- a/Makefile.am +++ b/Makefile.am @@ -231,7 +231,7 @@ EXTRA_DIST += \ \ python/xrefstructs.json \ \ - redhat/frr.logrotate \ + tools/etc/logrotate.d/frr \ redhat/frr.pam \ redhat/frr.spec \ \ diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index 51986de2dd..3aad9549b5 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -15,8 +15,8 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl libcap-dev libcurl libedit libffi libgcc libgomp libisoburn libisofs libltdl libressl libssh2 libstdc++ libtool libuuid linux-headers lzip lzo m4 make mkinitfs mpc1 mpfr4 mtools musl-dev - ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre - perl pkgconf python3 python3-dev readline readline-dev sqlite-libs + ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre2 + perl pkgconf python3 python3-dev readline readline-dev sqlite-libs pcre2-dev squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev py3-sphinx elfutils elfutils-dev libyang-dev" checkdepends="pytest py-setuptools" @@ -46,8 +46,9 @@ build() { --enable-multipath=64 \ --enable-vty-group=frrvty \ --enable-user=$_user \ - --enable-group=$_user - make + --enable-group=$_user \ + --enable-pcre2posix + make -j $(nproc) } check() { diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b2d7bdcef4..b8010364a7 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -4506,7 +4506,9 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, stream_put(s, &attr->srv6_l3vpn->sid, sizeof(attr->srv6_l3vpn->sid)); /* sid */ stream_putc(s, 0); /* sid_flags */ - stream_putw(s, 0xffff); /* endpoint */ + stream_putw(s, + attr->srv6_l3vpn + ->endpoint_behavior); /* endpoint */ stream_putc(s, 0); /* reserved */ stream_putc( s, diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 0a97a8b7bc..eab70bfdaf 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -67,7 +67,6 @@ DEFINE_MTYPE_STATIC(BGPD, VRF_ROUTE_TARGET, "L3 Route Target"); /* * Static function declarations */ -static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *evpn); static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *evpn); static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, @@ -645,10 +644,14 @@ static void evpn_convert_nexthop_to_ipv6(struct attr *attr) attr->mp_nexthop_len = IPV6_MAX_BYTELEN; } -struct bgp_dest *bgp_global_evpn_node_get(struct bgp_table *table, afi_t afi, +/* + * Wrapper for node get in global table. + */ +struct bgp_dest *bgp_evpn_global_node_get(struct bgp_table *table, afi_t afi, safi_t safi, const struct prefix_evpn *evp, - struct prefix_rd *prd) + struct prefix_rd *prd, + const struct bgp_path_info *local_pi) { struct prefix_evpn global_p; @@ -658,14 +661,38 @@ struct bgp_dest *bgp_global_evpn_node_get(struct bgp_table *table, afi_t afi, */ evpn_type1_prefix_global_copy(&global_p, evp); evp = &global_p; + } else if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && + local_pi) { + /* + * prefix in the global table needs MAC/IP, ensure they are + * present, using one's from local table's path_info. + */ + if (is_evpn_prefix_ipaddr_none(evp)) { + /* VNI MAC -> Global */ + evpn_type2_prefix_global_copy( + &global_p, evp, NULL /* mac */, + evpn_type2_path_info_get_ip(local_pi)); + } else { + /* VNI IP -> Global */ + evpn_type2_prefix_global_copy( + &global_p, evp, + evpn_type2_path_info_get_mac(local_pi), + NULL /* ip */); + } + + evp = &global_p; } return bgp_afi_node_get(table, afi, safi, (struct prefix *)evp, prd); } -struct bgp_dest *bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, - safi_t safi, - const struct prefix_evpn *evp, - struct prefix_rd *prd) +/* + * Wrapper for node lookup in global table. + */ +struct bgp_dest * +bgp_evpn_global_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, + const struct prefix_evpn *evp, + struct prefix_rd *prd, + const struct bgp_path_info *local_pi) { struct prefix_evpn global_p; @@ -675,21 +702,177 @@ struct bgp_dest *bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, */ evpn_type1_prefix_global_copy(&global_p, evp); evp = &global_p; + } else if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && + local_pi) { + /* + * prefix in the global table needs MAC/IP, ensure they are + * present, using one's from local table's path_info. + */ + if (is_evpn_prefix_ipaddr_none(evp)) { + /* VNI MAC -> Global */ + evpn_type2_prefix_global_copy( + &global_p, evp, NULL /* mac */, + evpn_type2_path_info_get_ip(local_pi)); + } else { + /* VNI IP -> Global */ + evpn_type2_prefix_global_copy( + &global_p, evp, + evpn_type2_path_info_get_mac(local_pi), + NULL /* ip */); + } + + evp = &global_p; } return bgp_afi_node_lookup(table, afi, safi, (struct prefix *)evp, prd); } /* + * Wrapper for node get in VNI IP table. + */ +struct bgp_dest *bgp_evpn_vni_ip_node_get(struct bgp_table *const table, + const struct prefix_evpn *evp, + const struct bgp_path_info *parent_pi) +{ + struct prefix_evpn vni_p; + + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE && parent_pi) { + /* prefix in the global table doesn't include the VTEP-IP so + * we need to create a different copy for the VNI + */ + evpn_type1_prefix_vni_ip_copy(&vni_p, evp, + parent_pi->attr->nexthop); + evp = &vni_p; + } else if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + /* Only MAC-IP should go into this table, not mac-only */ + assert(is_evpn_prefix_ipaddr_none(evp) == false); + + /* + * prefix in the vni IP table doesn't include MAC so + * we need to create a different copy of the prefix. + */ + evpn_type2_prefix_vni_ip_copy(&vni_p, evp); + evp = &vni_p; + } + return bgp_node_get(table, (struct prefix *)evp); +} + +/* + * Wrapper for node lookup in VNI IP table. + */ +struct bgp_dest * +bgp_evpn_vni_ip_node_lookup(const struct bgp_table *const table, + const struct prefix_evpn *evp, + const struct bgp_path_info *parent_pi) +{ + struct prefix_evpn vni_p; + + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE && parent_pi) { + /* prefix in the global table doesn't include the VTEP-IP so + * we need to create a different copy for the VNI + */ + evpn_type1_prefix_vni_ip_copy(&vni_p, evp, + parent_pi->attr->nexthop); + evp = &vni_p; + } else if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + /* Only MAC-IP should go into this table, not mac-only */ + assert(is_evpn_prefix_ipaddr_none(evp) == false); + + /* + * prefix in the vni IP table doesn't include MAC so + * we need to create a different copy of the prefix. + */ + evpn_type2_prefix_vni_ip_copy(&vni_p, evp); + evp = &vni_p; + } + return bgp_node_lookup(table, (struct prefix *)evp); +} + +/* + * Wrapper for node get in VNI MAC table. + */ +struct bgp_dest * +bgp_evpn_vni_mac_node_get(struct bgp_table *const table, + const struct prefix_evpn *evp, + const struct bgp_path_info *parent_pi) +{ + struct prefix_evpn vni_p; + + /* Only type-2 should ever go into this table */ + assert(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE); + + /* + * prefix in the vni MAC table doesn't include IP so + * we need to create a different copy of the prefix. + */ + evpn_type2_prefix_vni_mac_copy(&vni_p, evp); + evp = &vni_p; + return bgp_node_get(table, (struct prefix *)evp); +} + +/* + * Wrapper for node lookup in VNI MAC table. + */ +struct bgp_dest * +bgp_evpn_vni_mac_node_lookup(const struct bgp_table *const table, + const struct prefix_evpn *evp, + const struct bgp_path_info *parent_pi) +{ + struct prefix_evpn vni_p; + + /* Only type-2 should ever go into this table */ + assert(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE); + + /* + * prefix in the vni MAC table doesn't include IP so + * we need to create a different copy of the prefix. + */ + evpn_type2_prefix_vni_mac_copy(&vni_p, evp); + evp = &vni_p; + return bgp_node_lookup(table, (struct prefix *)evp); +} + +/* + * Wrapper for node get in both VNI tables. + */ +struct bgp_dest *bgp_evpn_vni_node_get(struct bgpevpn *vpn, + const struct prefix_evpn *p, + const struct bgp_path_info *parent_pi) +{ + if ((p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) && + (is_evpn_prefix_ipaddr_none(p) == true)) + return bgp_evpn_vni_mac_node_get(vpn->mac_table, p, parent_pi); + + return bgp_evpn_vni_ip_node_get(vpn->ip_table, p, parent_pi); +} + +/* + * Wrapper for node lookup in both VNI tables. + */ +struct bgp_dest *bgp_evpn_vni_node_lookup(const struct bgpevpn *vpn, + const struct prefix_evpn *p, + const struct bgp_path_info *parent_pi) +{ + if ((p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) && + (is_evpn_prefix_ipaddr_none(p) == true)) + return bgp_evpn_vni_mac_node_lookup(vpn->mac_table, p, + parent_pi); + + return bgp_evpn_vni_ip_node_lookup(vpn->ip_table, p, parent_pi); +} + +/* * Add (update) or delete MACIP from zebra. */ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, const struct prefix_evpn *p, + const struct ethaddr *mac, struct in_addr remote_vtep_ip, int add, uint8_t flags, uint32_t seq, esi_t *esi) { struct stream *s; uint16_t ipa_len; static struct in_addr zero_remote_vtep_ip; + bool esi_valid; /* Check socket. */ if (!zclient || zclient->sock < 0) @@ -713,7 +896,12 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, s, add ? ZEBRA_REMOTE_MACIP_ADD : ZEBRA_REMOTE_MACIP_DEL, bgp->vrf_id); stream_putl(s, vpn->vni); - stream_put(s, &p->prefix.macip_addr.mac.octet, ETH_ALEN); /* Mac Addr */ + + if (mac) /* Mac Addr */ + stream_put(s, &mac->octet, ETH_ALEN); + else + stream_put(s, &p->prefix.macip_addr.mac.octet, ETH_ALEN); + /* IP address length and IP address, if any. */ if (is_evpn_prefix_ipaddr_none(p)) stream_putw(s, 0); @@ -726,10 +914,13 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, /* If the ESI is valid that becomes the nexthop; tape out the * VTEP-IP for that case */ - if (bgp_evpn_is_esi_valid(esi)) + if (bgp_evpn_is_esi_valid(esi)) { + esi_valid = true; stream_put_in_addr(s, &zero_remote_vtep_ip); - else + } else { + esi_valid = false; stream_put_in_addr(s, &remote_vtep_ip); + } /* TX flags - MAC sticky status and/or gateway mac */ /* Also TX the sequence number of the best route. */ @@ -741,12 +932,20 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, stream_putw_at(s, 0, stream_get_endp(s)); - if (bgp_debug_zebra(NULL)) + if (bgp_debug_zebra(NULL)) { + char esi_buf[ESI_STR_LEN]; + + if (esi_valid) + esi_to_str(esi, esi_buf, sizeof(esi_buf)); + else + snprintf(esi_buf, sizeof(esi_buf), "-"); zlog_debug( - "Tx %s MACIP, VNI %u MAC %pEA IP %pIA flags 0x%x seq %u remote VTEP %pI4", + "Tx %s MACIP, VNI %u MAC %pEA IP %pIA flags 0x%x seq %u remote VTEP %pI4 esi %s", add ? "ADD" : "DEL", vpn->vni, - &p->prefix.macip_addr.mac, &p->prefix.macip_addr.ip, - flags, seq, &remote_vtep_ip); + (mac ? mac : &p->prefix.macip_addr.mac), + &p->prefix.macip_addr.ip, flags, seq, &remote_vtep_ip, + esi_buf); + } frrtrace(5, frr_bgp, evpn_mac_ip_zsend, add, vpn, p, remote_vtep_ip, esi); @@ -1080,8 +1279,13 @@ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn, } ret = bgp_zebra_send_remote_macip( - bgp, vpn, p, pi->attr->nexthop, 1, flags, - seq, bgp_evpn_attr_get_esi(pi->attr)); + bgp, vpn, p, + (is_evpn_prefix_ipaddr_none(p) + ? NULL /* MAC update */ + : evpn_type2_path_info_get_mac( + pi) /* MAC-IP update */), + pi->attr->nexthop, 1, flags, seq, + bgp_evpn_attr_get_esi(pi->attr)); } else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) { ret = bgp_evpn_remote_es_evi_add(bgp, vpn, p); } else { @@ -1107,13 +1311,19 @@ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn, /* Uninstall EVPN route from zebra. */ static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn, const struct prefix_evpn *p, - struct in_addr remote_vtep_ip) + struct bgp_path_info *pi, bool is_sync) { int ret; if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) - ret = bgp_zebra_send_remote_macip(bgp, vpn, p, remote_vtep_ip, - 0, 0, 0, NULL); + ret = bgp_zebra_send_remote_macip( + bgp, vpn, p, + (is_evpn_prefix_ipaddr_none(p) + ? NULL /* MAC update */ + : evpn_type2_path_info_get_mac( + pi) /* MAC-IP update */), + (is_sync ? zero_vtep_ip : pi->attr->nexthop), 0, 0, 0, + NULL); else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) ret = bgp_evpn_remote_es_evi_del(bgp, vpn, p); else @@ -1156,9 +1366,10 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn, * this table is a 2-level tree (RD-level + Prefix-level) similar to * L3VPN routes. */ - global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi, - (const struct prefix_evpn *)bgp_dest_get_prefix(dest), - &vpn->prd); + global_dest = bgp_evpn_global_node_lookup( + bgp->rib[afi][safi], afi, safi, + (const struct prefix_evpn *)bgp_dest_get_prefix(dest), + &vpn->prd, old_local); if (global_dest) { /* Delete route entry in the global EVPN table. */ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); @@ -1271,7 +1482,7 @@ int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, bgp, vpn, (const struct prefix_evpn *)bgp_dest_get_prefix( dest), - old_select->attr->nexthop); + old_select, false); } /* Clear any route change flags. */ @@ -1457,9 +1668,8 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, build_evpn_type5_route_extcomm(bgp_vrf, &attr); /* get the route node in global table */ - dest = bgp_global_evpn_node_get(bgp_evpn->rib[afi][safi], afi, safi, - (const struct prefix_evpn *)evp, - &bgp_vrf->vrf_prd); + dest = bgp_evpn_global_node_get(bgp_evpn->rib[afi][safi], afi, safi, + evp, &bgp_vrf->vrf_prd, NULL); assert(dest); /* create or update the route entry within the route node */ @@ -1481,13 +1691,22 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, static void bgp_evpn_get_sync_info(struct bgp *bgp, esi_t *esi, struct bgp_dest *dest, uint32_t loc_seq, uint32_t *max_sync_seq, bool *active_on_peer, - bool *peer_router, bool *proxy_from_peer) + bool *peer_router, bool *proxy_from_peer, + const struct ethaddr *mac) { struct bgp_path_info *tmp_pi; struct bgp_path_info *second_best_path = NULL; uint32_t tmp_mm_seq = 0; esi_t *tmp_esi; int paths_eq; + struct ethaddr *tmp_mac; + bool mac_cmp = false; + struct prefix_evpn *evp = (struct prefix_evpn *)&dest->p; + + + /* mac comparison is not needed for MAC-only routes */ + if (mac && !is_evpn_prefix_ipaddr_none(evp)) + mac_cmp = true; /* find the best non-local path. a local path can only be present * as best path @@ -1498,6 +1717,13 @@ static void bgp_evpn_get_sync_info(struct bgp *bgp, esi_t *esi, !CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID)) continue; + /* ignore paths that have a different mac */ + if (mac_cmp) { + tmp_mac = evpn_type2_path_info_get_mac(tmp_pi); + if (memcmp(mac, tmp_mac, sizeof(*mac))) + continue; + } + if (bgp_evpn_path_info_cmp(bgp, tmp_pi, second_best_path, &paths_eq)) second_best_path = tmp_pi; @@ -1542,7 +1768,8 @@ static void bgp_evpn_get_sync_info(struct bgp *bgp, esi_t *esi, static void update_evpn_route_entry_sync_info(struct bgp *bgp, struct bgp_dest *dest, struct attr *attr, - uint32_t loc_seq, bool setup_sync) + uint32_t loc_seq, bool setup_sync, + const struct ethaddr *mac) { esi_t *esi; struct prefix_evpn *evp = @@ -1561,7 +1788,8 @@ static void update_evpn_route_entry_sync_info(struct bgp *bgp, bgp_evpn_get_sync_info(bgp, esi, dest, loc_seq, &max_sync_seq, &active_on_peer, - &peer_router, &proxy_from_peer); + &peer_router, &proxy_from_peer, + mac); attr->mm_sync_seqnum = max_sync_seq; if (active_on_peer) attr->es_flags |= ATTR_ES_PEER_ACTIVE; @@ -1604,15 +1832,16 @@ static void update_evpn_route_entry_sync_info(struct bgp *bgp, } /* - * Create or update EVPN route entry. This could be in the VNI route table + * Create or update EVPN route entry. This could be in the VNI route tables * or the global route table. */ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, afi_t afi, safi_t safi, struct bgp_dest *dest, struct attr *attr, - int add, struct bgp_path_info **pi, - uint8_t flags, uint32_t seq, bool vpn_rt, - bool *old_is_sync) + const struct ethaddr *mac, + const struct ipaddr *ip, int add, + struct bgp_path_info **pi, uint8_t flags, + uint32_t seq, bool vpn_rt, bool *old_is_sync) { struct bgp_path_info *tmp_pi; struct bgp_path_info *local_pi; @@ -1643,7 +1872,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* if a local path is being added with a non-zero esi look * for SYNC paths from ES peers and bubble up the sync-info */ - update_evpn_route_entry_sync_info(bgp, dest, attr, seq, vpn_rt); + update_evpn_route_entry_sync_info(bgp, dest, attr, seq, vpn_rt, mac); /* For non-GW MACs, update MAC mobility seq number, if needed. */ if (seq && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) @@ -1684,6 +1913,14 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, memcpy(&tmp_pi->extra->label, label, sizeof(label)); tmp_pi->extra->num_labels = num_labels; + + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + if (mac) + evpn_type2_path_info_set_mac(tmp_pi, *mac); + else if (ip) + evpn_type2_path_info_set_ip(tmp_pi, *ip); + } + /* Mark route as self type-2 route */ if (flags && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP)) tmp_pi->extra->af_flags = BGP_EVPN_MACIP_TYPE_SVI_IP; @@ -1713,6 +1950,15 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, memcpy(&tmp_pi->extra->label, label, sizeof(label)); tmp_pi->extra->num_labels = num_labels; + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + if (mac) + evpn_type2_path_info_set_mac(tmp_pi, + *mac); + else if (ip) + evpn_type2_path_info_set_ip(tmp_pi, + *ip); + } + /* The attribute has changed. */ /* Add (or update) attribute to hash. */ attr_new = bgp_attr_intern(attr); @@ -1832,6 +2078,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, safi_t safi = SAFI_EVPN; int route_change; bool old_is_sync = false; + bool mac_only = false; memset(&attr, 0, sizeof(attr)); @@ -1891,14 +2138,20 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, /* Set up extended community. */ build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); - /* First, create (or fetch) route node within the VNI. */ - /* NOTE: There is no RD here. */ - dest = bgp_node_get(vpn->route_table, (struct prefix *)p); + /* First, create (or fetch) route node within the VNI. + * NOTE: There is no RD here. + */ + dest = bgp_evpn_vni_node_get(vpn, p, NULL); + + if ((p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) && + (is_evpn_prefix_ipaddr_none(p) == true)) + mac_only = true; /* Create or update route entry. */ - route_change = update_evpn_route_entry(bgp, vpn, afi, safi, dest, &attr, - 1, &pi, flags, seq, - true /* setup_sync */, &old_is_sync); + route_change = update_evpn_route_entry( + bgp, vpn, afi, safi, dest, &attr, + (mac_only ? NULL : &p->prefix.macip_addr.mac), NULL /* ip */, 1, + &pi, flags, seq, true /* setup_sync */, &old_is_sync); assert(pi); attr_new = pi->attr; @@ -1936,7 +2189,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, */ new_is_sync = bgp_evpn_attr_is_sync(pi->attr); if (!new_is_sync && old_is_sync) - evpn_zebra_uninstall(bgp, vpn, p, zero_vtep_ip); + evpn_zebra_uninstall(bgp, vpn, p, pi, true); } } bgp_path_info_unlock(pi); @@ -1951,12 +2204,12 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, if (route_change) { struct bgp_path_info *global_pi; - dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, - (const struct prefix_evpn *)p, - &vpn->prd); - update_evpn_route_entry(bgp, vpn, afi, safi, dest, attr_new, 1, - &global_pi, flags, seq, - false /* setup_sync */, NULL /* old_is_sync */); + dest = bgp_evpn_global_node_get(bgp->rib[afi][safi], afi, safi, + p, &vpn->prd, NULL); + update_evpn_route_entry( + bgp, vpn, afi, safi, dest, attr_new, NULL /* mac */, + NULL /* ip */, 1, &global_pi, flags, seq, + false /* setup_sync */, NULL /* old_is_sync */); /* Schedule for processing and unlock node. */ bgp_process(bgp, dest, afi, safi); @@ -2010,8 +2263,8 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) return 0; /* locate the global route entry for this type-5 prefix */ - dest = bgp_global_evpn_node_lookup(bgp_evpn->rib[afi][safi], afi, safi, - (const struct prefix_evpn *)evp, &bgp_vrf->vrf_prd); + dest = bgp_evpn_global_node_lookup(bgp_evpn->rib[afi][safi], afi, safi, + evp, &bgp_vrf->vrf_prd, NULL); if (!dest) return 0; @@ -2037,9 +2290,9 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, /* First, locate the route node within the VNI. If it doesn't exist, * there * is nothing further to do. + * NOTE: There is no RD here. */ - /* NOTE: There is no RD here. */ - dest = bgp_node_lookup(vpn->route_table, (struct prefix *)p); + dest = bgp_evpn_vni_node_lookup(vpn, p, NULL); if (!dest) return 0; @@ -2047,8 +2300,8 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, * this table is a 2-level tree (RD-level + Prefix-level) similar to * L3VPN routes. */ - global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi, - (const struct prefix_evpn *)p, &vpn->prd); + global_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], afi, + safi, p, &vpn->prd, NULL); if (global_dest) { /* Delete route entry in the global EVPN table. */ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); @@ -2087,8 +2340,7 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, int add_l3_ecomm = 0; struct bgp_dest *global_dest; struct bgp_path_info *global_pi; - struct prefix_evpn *evp = - (struct prefix_evpn *)bgp_dest_get_prefix(dest); + struct prefix_evpn evp; int route_change; bool old_is_sync = false; @@ -2096,6 +2348,22 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, return; /* + * VNI table MAC-IP prefixes don't have MAC so make sure it's set from + * path info here. + */ + if (is_evpn_prefix_ipaddr_none((struct prefix_evpn *)&dest->p)) { + /* VNI MAC -> Global */ + evpn_type2_prefix_global_copy( + &evp, (struct prefix_evpn *)&dest->p, NULL /* mac */, + evpn_type2_path_info_get_ip(local_pi)); + } else { + /* VNI IP -> Global */ + evpn_type2_prefix_global_copy( + &evp, (struct prefix_evpn *)&dest->p, + evpn_type2_path_info_get_mac(local_pi), NULL /* ip */); + } + + /* * Build attribute per local route as the MAC mobility and * some other values could differ for different routes. The * attributes will be shared in the hash table. @@ -2109,18 +2377,17 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, attr.es_flags = local_pi->attr->es_flags; if (local_pi->attr->default_gw) { attr.default_gw = 1; - if (is_evpn_prefix_ipaddr_v6(evp)) + if (is_evpn_prefix_ipaddr_v6(&evp)) attr.router_flag = 1; } memcpy(&attr.esi, &local_pi->attr->esi, sizeof(esi_t)); - bgp_evpn_get_rmac_nexthop(vpn, evp, &attr, - local_pi->extra->af_flags); + bgp_evpn_get_rmac_nexthop(vpn, &evp, &attr, local_pi->extra->af_flags); vni2label(vpn->vni, &(attr.label)); /* Add L3 VNI RTs and RMAC for non IPv6 link-local if * using L3 VNI for type-2 routes also. */ add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok( - vpn, evp, + vpn, &evp, (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL); /* Set up extended community. */ @@ -2134,15 +2401,15 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, "VRF %s vni %u evp %pFX RMAC %pEA nexthop %pI4 esi %s esf 0x%x from %s", vpn->bgp_vrf ? vrf_id_to_name(vpn->bgp_vrf->vrf_id) : " ", - vpn->vni, evp, &attr.rmac, &attr.mp_nexthop_global_in, + vpn->vni, &evp, &attr.rmac, &attr.mp_nexthop_global_in, esi_to_str(&attr.esi, buf3, sizeof(buf3)), attr.es_flags, caller); } /* Update the route entry. */ route_change = update_evpn_route_entry( - bgp, vpn, afi, safi, dest, &attr, 0, &pi, 0, seq, - true /* setup_sync */, &old_is_sync); + bgp, vpn, afi, safi, dest, &attr, NULL /* mac */, NULL /* ip */, + 0, &pi, 0, seq, true /* setup_sync */, &old_is_sync); assert(pi); attr_new = pi->attr; @@ -2177,8 +2444,7 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, */ new_is_sync = bgp_evpn_attr_is_sync(pi->attr); if (!new_is_sync && old_is_sync) - evpn_zebra_uninstall(bgp, vpn, - evp, zero_vtep_ip); + evpn_zebra_uninstall(bgp, vpn, &evp, pi, true); } } @@ -2188,13 +2454,14 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (route_change) { /* Update route in global routing table. */ - global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, - safi, evp, &vpn->prd); + global_dest = bgp_evpn_global_node_get( + bgp->rib[afi][safi], afi, safi, &evp, &vpn->prd, NULL); assert(global_dest); update_evpn_route_entry( - bgp, vpn, afi, safi, global_dest, attr_new, 0, - &global_pi, 0, mac_mobility_seqnum(attr_new), - false /* setup_sync */, NULL /* old_is_sync */); + bgp, vpn, afi, safi, global_dest, attr_new, + NULL /* mac */, NULL /* ip */, 0, &global_pi, 0, + mac_mobility_seqnum(attr_new), false /* setup_sync */, + NULL /* old_is_sync */); /* Schedule for processing and unlock node. */ bgp_process(bgp, global_dest, afi, safi); @@ -2205,43 +2472,51 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, aspath_unintern(&attr.aspath); } +static void update_type2_route(struct bgp *bgp, struct bgpevpn *vpn, + struct bgp_dest *dest) +{ + struct bgp_path_info *tmp_pi; + + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); + + if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) + return; + + /* Identify local route. */ + for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi; + tmp_pi = tmp_pi->next) { + if (tmp_pi->peer == bgp->peer_self && + tmp_pi->type == ZEBRA_ROUTE_BGP && + tmp_pi->sub_type == BGP_ROUTE_STATIC) + break; + } + + if (!tmp_pi) + return; + + bgp_evpn_update_type2_route_entry(bgp, vpn, dest, tmp_pi, __func__); +} + /* * Update all type-2 (MACIP) local routes for this VNI - these should also * be scheduled for advertise to peers. */ -static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) +static void update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) { struct bgp_dest *dest; - struct bgp_path_info *tmp_pi; - /* Walk this VNI's route table and update local type-2 routes. For any - * routes updated, update corresponding entry in the global table too. + /* Walk this VNI's route MAC & IP table and update local type-2 + * routes. For any routes updated, update corresponding entry in the + * global table too. */ - for (dest = bgp_table_top(vpn->route_table); dest; - dest = bgp_route_next(dest)) { - const struct prefix_evpn *evp = - (const struct prefix_evpn *)bgp_dest_get_prefix(dest); - - if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) - continue; - - /* Identify local route. */ - for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi; - tmp_pi = tmp_pi->next) { - if (tmp_pi->peer == bgp->peer_self - && tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_STATIC) - break; - } - - if (!tmp_pi) - continue; - - bgp_evpn_update_type2_route_entry(bgp, vpn, dest, tmp_pi, - __func__); - } + for (dest = bgp_table_top(vpn->mac_table); dest; + dest = bgp_route_next(dest)) + update_type2_route(bgp, vpn, dest); - return 0; + for (dest = bgp_table_top(vpn->ip_table); dest; + dest = bgp_route_next(dest)) + update_type2_route(bgp, vpn, dest); } /* @@ -2282,55 +2557,65 @@ static void delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) } } +static void delete_vni_type2_route(struct bgp *bgp, struct bgp_dest *dest) +{ + struct bgp_path_info *pi; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); + + if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) + return; + + delete_evpn_route_entry(bgp, afi, safi, dest, &pi); + + /* Route entry in local table gets deleted immediately. */ + if (pi) + bgp_path_info_reap(dest, pi); +} + +static void delete_vni_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) +{ + struct bgp_dest *dest; + + /* Next, walk this VNI's MAC & IP route table and delete local type-2 + * routes. + */ + for (dest = bgp_table_top(vpn->mac_table); dest; + dest = bgp_route_next(dest)) + delete_vni_type2_route(bgp, dest); + + for (dest = bgp_table_top(vpn->ip_table); dest; + dest = bgp_route_next(dest)) + delete_vni_type2_route(bgp, dest); +} + /* * Delete all type-2 (MACIP) local routes for this VNI - from the global * table as well as the per-VNI route table. */ -static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) +static void delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) { - afi_t afi; - safi_t safi; - struct bgp_dest *dest; - struct bgp_path_info *pi; - - afi = AFI_L2VPN; - safi = SAFI_EVPN; - /* First, walk the global route table for this VNI's type-2 local * routes. * EVPN routes are a 2-level table, first get the RD table. */ delete_global_type2_routes(bgp, vpn); - - /* Next, walk this VNI's route table and delete local type-2 routes. */ - for (dest = bgp_table_top(vpn->route_table); dest; - dest = bgp_route_next(dest)) { - const struct prefix_evpn *evp = - (const struct prefix_evpn *)bgp_dest_get_prefix(dest); - - if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) - continue; - - delete_evpn_route_entry(bgp, afi, safi, dest, &pi); - - /* Route entry in local table gets deleted immediately. */ - if (pi) - bgp_path_info_reap(dest, pi); - } - - return 0; + delete_vni_type2_routes(bgp, vpn); } /* * Delete all routes in the per-VNI route table. */ -static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) +static void delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) { struct bgp_dest *dest; struct bgp_path_info *pi, *nextpi; - /* Walk this VNI's route table and delete all routes. */ - for (dest = bgp_table_top(vpn->route_table); dest; + /* Walk this VNI's MAC & IP route table and delete all routes. */ + for (dest = bgp_table_top(vpn->mac_table); dest; dest = bgp_route_next(dest)) { for (pi = bgp_dest_get_bgp_path_info(dest); (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { @@ -2340,7 +2625,14 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) } } - return 0; + for (dest = bgp_table_top(vpn->ip_table); dest; + dest = bgp_route_next(dest)) { + for (pi = bgp_dest_get_bgp_path_info(dest); + (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { + bgp_path_info_delete(dest, pi); + bgp_path_info_reap(dest, pi); + } + } } /* BUM traffic flood mode per-l2-vni */ @@ -2387,7 +2679,8 @@ int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) return ret; } - return update_all_type2_routes(bgp, vpn); + update_all_type2_routes(bgp, vpn); + return 0; } /* @@ -2404,9 +2697,7 @@ static int delete_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) /* Delete and withdraw locally learnt type-2 routes (MACIP) * followed by type-3 routes (only one) - for this VNI. */ - ret = delete_all_type2_routes(bgp, vpn); - if (ret) - return ret; + delete_all_type2_routes(bgp, vpn); build_evpn_type3_prefix(&p, vpn->originator_ip); ret = delete_evpn_route(bgp, vpn, &p); @@ -2414,7 +2705,8 @@ static int delete_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) return ret; /* Delete all routes from the per-VNI table. */ - return delete_all_vni_routes(bgp, vpn); + delete_all_vni_routes(bgp, vpn); + return 0; } /* @@ -2495,6 +2787,7 @@ bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi, pi->extra->num_labels = parent_pi->extra->num_labels; pi->extra->igpmetric = parent_pi->extra->igpmetric; } + bgp_path_info_add(dest, pi); return pi; @@ -2673,32 +2966,19 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, } /* - * Install route entry into the VNI routing table and invoke route selection. + * Common handling for vni route tables install/selection. */ -static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, - const struct prefix_evpn *p, - struct bgp_path_info *parent_pi) +static int install_evpn_route_entry_in_vni_common( + struct bgp *bgp, struct bgpevpn *vpn, const struct prefix_evpn *p, + struct bgp_dest *dest, struct bgp_path_info *parent_pi) { - struct bgp_dest *dest; struct bgp_path_info *pi; struct bgp_path_info *local_pi; 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 - */ - if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) - p = evpn_type1_prefix_vni_copy(&ad_evp, p, - parent_pi->attr->nexthop); - - /* Create (or fetch) route within the VNI. */ - /* NOTE: There is no RD here. */ - dest = bgp_node_get(vpn->route_table, (struct prefix *)p); - /* Check if route entry is already present. */ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->extra @@ -2709,13 +2989,27 @@ 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); + + if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + if (is_evpn_type2_dest_ipaddr_none(dest)) + evpn_type2_path_info_set_ip( + pi, p->prefix.macip_addr.ip); + else + evpn_type2_path_info_set_mac( + pi, p->prefix.macip_addr.mac); + } + 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)) { - bgp_dest_unlock_node(dest); + /* Return early if attributes haven't changed + * and dest isn't flagged for removal. + * dest will be unlocked by either + * install_evpn_route_entry_in_vni_mac() or + * install_evpn_route_entry_in_vni_ip() + */ + if (attrhash_cmp(pi->attr, parent_pi->attr) && + !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) return 0; - } /* The attribute has changed. */ /* Add (or update) attribute to hash. */ attr_new = bgp_attr_intern(parent_pi->attr); @@ -2766,12 +3060,160 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (local_pi && (old_local_es || new_local_es)) bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi, __func__); + + return ret; +} + +/* + * Common handling for vni route tables uninstall/selection. + */ +static int uninstall_evpn_route_entry_in_vni_common( + struct bgp *bgp, struct bgpevpn *vpn, const struct prefix_evpn *p, + struct bgp_dest *dest, struct bgp_path_info *parent_pi) +{ + struct bgp_path_info *pi; + struct bgp_path_info *local_pi; + int ret; + + /* Find matching route entry. */ + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) + if (pi->extra && + (struct bgp_path_info *)pi->extra->parent == parent_pi) + break; + + if (!pi) + return 0; + + bgp_evpn_remote_ip_hash_del(vpn, pi); + + /* Mark entry for deletion */ + bgp_path_info_delete(dest, pi); + + /* Perform route selection and update zebra, if required. */ + ret = evpn_route_select_install(bgp, vpn, dest); + + /* if the best path is a local path with a non-zero ES + * sync info against the local path may need to be updated + * when a remote path is deleted + */ + local_pi = bgp_evpn_route_get_local_path(bgp, dest); + if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr)) + bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi, + __func__); + + return ret; +} + +/* + * Install route entry into VNI IP table and invoke route selection. + */ +static int install_evpn_route_entry_in_vni_ip(struct bgp *bgp, + struct bgpevpn *vpn, + const struct prefix_evpn *p, + struct bgp_path_info *parent_pi) +{ + int ret; + struct bgp_dest *dest; + + /* Ignore MAC Only Type-2 */ + if ((p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) && + (is_evpn_prefix_ipaddr_none(p) == true)) + return 0; + + /* Create (or fetch) route within the VNI IP table. */ + dest = bgp_evpn_vni_ip_node_get(vpn->ip_table, p, parent_pi); + + ret = install_evpn_route_entry_in_vni_common(bgp, vpn, p, dest, + parent_pi); + + bgp_dest_unlock_node(dest); + + return ret; +} + +/* + * Install route entry into VNI MAC table and invoke route selection. + */ +static int install_evpn_route_entry_in_vni_mac(struct bgp *bgp, + struct bgpevpn *vpn, + const struct prefix_evpn *p, + struct bgp_path_info *parent_pi) +{ + int ret; + struct bgp_dest *dest; + + /* Only type-2 routes go into this table */ + if (p->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) + return 0; + + /* Create (or fetch) route within the VNI MAC table. */ + dest = bgp_evpn_vni_mac_node_get(vpn->mac_table, p, parent_pi); + + ret = install_evpn_route_entry_in_vni_common(bgp, vpn, p, dest, + parent_pi); + + bgp_dest_unlock_node(dest); + + return ret; +} + +/* + * Uninstall route entry from VNI IP table and invoke route selection. + */ +static int uninstall_evpn_route_entry_in_vni_ip(struct bgp *bgp, + struct bgpevpn *vpn, + const struct prefix_evpn *p, + struct bgp_path_info *parent_pi) +{ + int ret; + struct bgp_dest *dest; + + /* Ignore MAC Only Type-2 */ + if ((p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) && + (is_evpn_prefix_ipaddr_none(p) == true)) + return 0; + + /* Locate route within the VNI IP table. */ + dest = bgp_evpn_vni_ip_node_lookup(vpn->ip_table, p, parent_pi); + if (!dest) + return 0; + + ret = uninstall_evpn_route_entry_in_vni_common(bgp, vpn, p, dest, + parent_pi); + bgp_dest_unlock_node(dest); return ret; } /* + * Uninstall route entry from VNI IP table and invoke route selection. + */ +static int +uninstall_evpn_route_entry_in_vni_mac(struct bgp *bgp, struct bgpevpn *vpn, + const struct prefix_evpn *p, + struct bgp_path_info *parent_pi) +{ + int ret; + struct bgp_dest *dest; + + /* Only type-2 routes go into this table */ + if (p->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) + return 0; + + /* Locate route within the VNI MAC table. */ + dest = bgp_evpn_vni_mac_node_lookup(vpn->mac_table, p, parent_pi); + if (!dest) + return 0; + + ret = uninstall_evpn_route_entry_in_vni_common(bgp, vpn, p, dest, + parent_pi); + + bgp_dest_unlock_node(dest); + + return ret; +} +/* * Uninstall route entry from the VRF routing table and send message * to zebra, if appropriate. */ @@ -2849,62 +3291,79 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, } /* - * Uninstall route entry from the VNI routing table and send message - * to zebra, if appropriate. + * Install route entry into the VNI routing tables. + */ +static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, + const struct prefix_evpn *p, + struct bgp_path_info *parent_pi) +{ + int ret = 0; + + if (bgp_debug_update(parent_pi->peer, NULL, NULL, 1)) + zlog_debug( + "%s (%u): Installing EVPN %pFX route in VNI %u IP/MAC table", + vrf_id_to_name(bgp->vrf_id), bgp->vrf_id, p, vpn->vni); + + ret = install_evpn_route_entry_in_vni_mac(bgp, vpn, p, parent_pi); + + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "%s (%u): Failed to install EVPN %pFX route in VNI %u MAC table", + vrf_id_to_name(bgp->vrf_id), bgp->vrf_id, p, vpn->vni); + + return ret; + } + + ret = install_evpn_route_entry_in_vni_ip(bgp, vpn, p, parent_pi); + + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "%s (%u): Failed to install EVPN %pFX route in VNI %u IP table", + vrf_id_to_name(bgp->vrf_id), bgp->vrf_id, p, vpn->vni); + + return ret; + } + + return ret; +} + +/* + * Uninstall route entry from the VNI routing tables. */ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, const struct prefix_evpn *p, struct bgp_path_info *parent_pi) { - struct bgp_dest *dest; - struct bgp_path_info *pi; - struct bgp_path_info *local_pi; - int ret; - struct prefix_evpn ad_evp; + int ret = 0; - /* EAD prefix in the global table doesn't include the VTEP-IP so - * we need to create a different copy for the VNI - */ - if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) - p = evpn_type1_prefix_vni_copy(&ad_evp, p, - parent_pi->attr->nexthop); + if (bgp_debug_update(parent_pi->peer, NULL, NULL, 1)) + zlog_debug( + "%s (%u): Uninstalling EVPN %pFX route from VNI %u IP/MAC table", + vrf_id_to_name(bgp->vrf_id), bgp->vrf_id, p, vpn->vni); - /* Locate route within the VNI. */ - /* NOTE: There is no RD here. */ - dest = bgp_node_lookup(vpn->route_table, (struct prefix *)p); - if (!dest) - return 0; + ret = uninstall_evpn_route_entry_in_vni_ip(bgp, vpn, p, parent_pi); - /* Find matching route entry. */ - for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) - if (pi->extra - && (struct bgp_path_info *)pi->extra->parent == parent_pi) - break; + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "%s (%u): Failed to uninstall EVPN %pFX route from VNI %u IP table", + vrf_id_to_name(bgp->vrf_id), bgp->vrf_id, p, vpn->vni); - if (!pi) { - bgp_dest_unlock_node(dest); - return 0; + return ret; } - bgp_evpn_remote_ip_hash_del(vpn, pi); - - /* Mark entry for deletion */ - bgp_path_info_delete(dest, pi); - - /* Perform route selection and update zebra, if required. */ - ret = evpn_route_select_install(bgp, vpn, dest); + ret = uninstall_evpn_route_entry_in_vni_mac(bgp, vpn, p, parent_pi); - /* if the best path is a local path with a non-zero ES - * sync info against the local path may need to be updated - * when a remote path is deleted - */ - local_pi = bgp_evpn_route_get_local_path(bgp, dest); - if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr)) - bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi, - __func__); + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "%s (%u): Failed to uninstall EVPN %pFX route from VNI %u MAC table", + vrf_id_to_name(bgp->vrf_id), bgp->vrf_id, p, vpn->vni); - /* Unlock route node. */ - bgp_dest_unlock_node(dest); + return ret; + } return ret; } @@ -3480,7 +3939,8 @@ static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi, * we need to create a different copy for the VNI */ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) - evp = evpn_type1_prefix_vni_copy(&ad_evp, evp, attr->nexthop); + evp = evpn_type1_prefix_vni_ip_copy(&ad_evp, evp, + attr->nexthop); ecom = bgp_attr_get_ecommunity(attr); if (!ecom || !ecom->size) @@ -3695,16 +4155,94 @@ static void withdraw_router_id_vrf(struct bgp *bgp_vrf) delete_withdraw_vrf_routes(bgp_vrf); } +static void update_advertise_vni_route(struct bgp *bgp, struct bgpevpn *vpn, + struct bgp_dest *dest) +{ + struct bgp_dest *global_dest; + struct bgp_path_info *pi, *global_pi; + struct attr *attr; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + + struct prefix_evpn tmp_evp; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); + + /* + * We have already processed type-3 routes. + * Process only type-1 and type-2 routes here. + */ + if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE && + evp->prefix.route_type != BGP_EVPN_AD_ROUTE) + return; + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) + if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP && + pi->sub_type == BGP_ROUTE_STATIC) + break; + if (!pi) + return; + + /* + * VNI table MAC-IP prefixes don't have MAC so make sure it's + * set from path info here. + */ + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + if (is_evpn_prefix_ipaddr_none(evp)) { + /* VNI MAC -> Global */ + evpn_type2_prefix_global_copy( + &tmp_evp, evp, NULL /* mac */, + evpn_type2_path_info_get_ip(pi)); + } else { + /* VNI IP -> Global */ + evpn_type2_prefix_global_copy( + &tmp_evp, evp, evpn_type2_path_info_get_mac(pi), + NULL /* ip */); + } + } else { + memcpy(&tmp_evp, evp, sizeof(tmp_evp)); + } + + /* Create route in global routing table using this route entry's + * attribute. + */ + attr = pi->attr; + global_dest = bgp_evpn_global_node_get(bgp->rib[afi][safi], afi, safi, + &tmp_evp, &vpn->prd, NULL); + assert(global_dest); + + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + /* Type-2 route */ + update_evpn_route_entry( + bgp, vpn, afi, safi, global_dest, attr, NULL /* mac */, + NULL /* ip */, 1, &global_pi, 0, + mac_mobility_seqnum(attr), false /* setup_sync */, + NULL /* old_is_sync */); + } else { + /* Type-1 route */ + struct bgp_evpn_es *es; + int route_changed = 0; + + es = bgp_evpn_es_find(&evp->prefix.ead_addr.esi); + bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, global_dest, + attr, &global_pi, &route_changed); + } + + /* Schedule for processing and unlock node. */ + bgp_process(bgp, global_dest, afi, safi); + bgp_dest_unlock_node(global_dest); +} + /* * Update and advertise local routes for a VNI. Invoked upon router-id * change. Note that the processing is done only on the global route table * using routes that already exist in the per-VNI table. */ -static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) +static void update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) { struct prefix_evpn p; struct bgp_dest *dest, *global_dest; - struct bgp_path_info *pi, *global_pi; + struct bgp_path_info *pi; struct attr *attr; afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -3718,9 +4256,9 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) if (bgp_evpn_vni_flood_mode_get(bgp, vpn) == VXLAN_FLOOD_HEAD_END_REPL) { build_evpn_type3_prefix(&p, vpn->originator_ip); - dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); + dest = bgp_evpn_vni_node_lookup(vpn, &p, NULL); if (!dest) /* unexpected */ - return 0; + return; for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP @@ -3728,76 +4266,33 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) break; if (!pi) { bgp_dest_unlock_node(dest); - return 0; + return; } + attr = pi->attr; - global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], - afi, safi, &p, &vpn->prd); - update_evpn_route_entry(bgp, vpn, afi, safi, global_dest, attr, - 1, &pi, 0, mac_mobility_seqnum(attr), - false /* setup_sync */, NULL /* old_is_sync */); + global_dest = bgp_evpn_global_node_get( + bgp->rib[afi][safi], afi, safi, &p, &vpn->prd, NULL); + update_evpn_route_entry( + bgp, vpn, afi, safi, global_dest, attr, NULL /* mac */, + NULL /* ip */, 1, &pi, 0, mac_mobility_seqnum(attr), + false /* setup_sync */, NULL /* old_is_sync */); /* Schedule for processing and unlock node. */ bgp_process(bgp, global_dest, afi, safi); bgp_dest_unlock_node(global_dest); } - /* Now, walk this VNI's route table and use the route and its attribute - * to create and schedule route in global table. + /* Now, walk this VNI's MAC & IP route table and use the route and its + * attribute to create and schedule route in global table. */ - for (dest = bgp_table_top(vpn->route_table); dest; - dest = bgp_route_next(dest)) { - const struct prefix_evpn *evp = - (const struct prefix_evpn *)bgp_dest_get_prefix(dest); - - /* - * We have already processed type-3 routes. - * Process only type-1 and type-2 routes here. - */ - if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE - && evp->prefix.route_type != BGP_EVPN_AD_ROUTE) - continue; - - for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) - if (pi->peer == bgp->peer_self - && pi->type == ZEBRA_ROUTE_BGP - && pi->sub_type == BGP_ROUTE_STATIC) - break; - if (!pi) - continue; - - /* Create route in global routing table using this route entry's - * attribute. - */ - attr = pi->attr; - global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, - evp, &vpn->prd); - assert(global_dest); - - if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { - /* Type-2 route */ - update_evpn_route_entry( - bgp, vpn, afi, safi, global_dest, attr, 1, - &global_pi, 0, mac_mobility_seqnum(attr), - false /* setup_sync */, NULL /* old_is_sync */); - } else { - /* Type-1 route */ - struct bgp_evpn_es *es; - int route_changed = 0; - - es = bgp_evpn_es_find(&evp->prefix.ead_addr.esi); - bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, - global_dest, attr, &global_pi, - &route_changed); - } - - /* Schedule for processing and unlock node. */ - bgp_process(bgp, global_dest, afi, safi); - bgp_dest_unlock_node(global_dest); - } + for (dest = bgp_table_top(vpn->mac_table); dest; + dest = bgp_route_next(dest)) + update_advertise_vni_route(bgp, vpn, dest); - return 0; + for (dest = bgp_table_top(vpn->ip_table); dest; + dest = bgp_route_next(dest)) + update_advertise_vni_route(bgp, vpn, dest); } /* @@ -3819,8 +4314,8 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) /* Remove type-3 route for this VNI from global table. */ build_evpn_type3_prefix(&p, vpn->originator_ip); - global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi, - (const struct prefix_evpn *)&p, &vpn->prd); + global_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], afi, + safi, &p, &vpn->prd, NULL); if (global_dest) { /* Delete route entry in the global EVPN table. */ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); @@ -5498,8 +5993,9 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, bf_assign_index(bm->rd_idspace, vpn->rd_id); derive_rd_rt_for_vni(bgp, vpn); - /* Initialize EVPN route table. */ - vpn->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN); + /* Initialize EVPN route tables. */ + vpn->ip_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN); + vpn->mac_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN); /* Add to hash */ (void)hash_get(bgp->vnihash, vpn, hash_alloc_intern); @@ -5527,7 +6023,8 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) bgp_evpn_remote_ip_hash_destroy(vpn); bgp_evpn_vni_es_cleanup(vpn); bgpevpn_unlink_from_l3vni(vpn); - bgp_table_unlock(vpn->route_table); + bgp_table_unlock(vpn->ip_table); + bgp_table_unlock(vpn->mac_table); bgp_evpn_unmap_vni_from_its_rts(bgp, vpn); list_delete(&vpn->import_rtl); list_delete(&vpn->export_rtl); @@ -5653,7 +6150,7 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, delete_evpn_route(bgp, vpn, &p); } else { /* Re-instate the current remote best path if any */ - dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); + dest = bgp_evpn_vni_node_lookup(vpn, &p, NULL); if (dest) { evpn_zebra_reinstall_best_route(bgp, vpn, dest); bgp_dest_unlock_node(dest); @@ -6661,7 +7158,7 @@ void bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket, bgp_evpn_remote_ip_hash_init(vpn); - for (dest = bgp_table_top(vpn->route_table); dest; + for (dest = bgp_table_top(vpn->ip_table); dest; dest = bgp_route_next(dest)) for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) bgp_evpn_remote_ip_hash_add(vpn, pi); diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 1d40664aeb..2a5c5d7ec4 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -471,7 +471,7 @@ static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, struct prefix_rd *prd; if (vpn) { - rt_table = vpn->route_table; + rt_table = vpn->ip_table; prd = &vpn->prd; } else { rt_table = es->route_table; @@ -498,9 +498,8 @@ static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, /* Next, locate route node in the global EVPN routing table. * Note that this table is a 2-level tree (RD-level + Prefix-level) */ - global_dest = - bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi, - (const struct prefix_evpn *)p, prd); + global_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], afi, + safi, p, prd, NULL); if (global_dest) { /* Delete route entry in the global EVPN table. */ @@ -675,8 +674,9 @@ static int bgp_evpn_type4_route_update(struct bgp *bgp, if (route_changed) { struct bgp_path_info *global_pi; - dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, - p, &es->es_base_frag->prd); + dest = bgp_evpn_global_node_get(bgp->rib[afi][safi], afi, safi, + p, &es->es_base_frag->prd, + NULL); bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, dest, attr_new, &global_pi, &route_changed); @@ -960,7 +960,7 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es, bgp_evpn_type1_evi_route_extcomm_build(es, vpn, &attr); /* First, create (or fetch) route node within the VNI. */ - dest = bgp_node_get(vpn->route_table, (struct prefix *)p); + dest = bgp_node_get(vpn->ip_table, (struct prefix *)p); /* Create or update route entry. */ ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, dest, @@ -1015,8 +1015,8 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es, if (route_changed) { struct bgp_path_info *global_pi; - dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, - p, global_rd); + dest = bgp_evpn_global_node_get(bgp->rib[afi][safi], afi, safi, + p, global_rd, NULL); bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, dest, attr_new, &global_pi, &route_changed); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index fdbffa95dd..3f18e4e9f1 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -112,9 +112,10 @@ struct bgpevpn { */ struct hash *remote_ip_hash; - /* Route table for EVPN routes for + /* Route tables for EVPN routes for * this VNI. */ - struct bgp_table *route_table; + struct bgp_table *ip_table; + struct bgp_table *mac_table; /* RB tree of ES-EVIs */ struct bgp_es_evi_rb_head es_evi_rb_tree; @@ -543,10 +544,10 @@ static inline void evpn_type1_prefix_global_copy(struct prefix_evpn *global_p, /* EAD prefix in the global table doesn't include the VTEP-IP so * we need to create a different copy for the VNI */ -static inline struct prefix_evpn *evpn_type1_prefix_vni_copy( - struct prefix_evpn *vni_p, - const struct prefix_evpn *global_p, - struct in_addr originator_ip) +static inline struct prefix_evpn * +evpn_type1_prefix_vni_ip_copy(struct prefix_evpn *vni_p, + const struct prefix_evpn *global_p, + struct in_addr originator_ip) { memcpy(vni_p, global_p, sizeof(*vni_p)); vni_p->prefix.ead_addr.ip.ipa_type = IPADDR_V4; @@ -555,6 +556,77 @@ static inline struct prefix_evpn *evpn_type1_prefix_vni_copy( return vni_p; } +static inline void evpn_type2_prefix_global_copy( + struct prefix_evpn *global_p, const struct prefix_evpn *vni_p, + const struct ethaddr *mac, const struct ipaddr *ip) +{ + memcpy(global_p, vni_p, sizeof(*global_p)); + + if (mac) + global_p->prefix.macip_addr.mac = *mac; + + if (ip) + global_p->prefix.macip_addr.ip = *ip; +} + +static inline void +evpn_type2_prefix_vni_ip_copy(struct prefix_evpn *vni_p, + const struct prefix_evpn *global_p) +{ + memcpy(vni_p, global_p, sizeof(*vni_p)); + memset(&vni_p->prefix.macip_addr.mac, 0, sizeof(struct ethaddr)); +} + +static inline void +evpn_type2_prefix_vni_mac_copy(struct prefix_evpn *vni_p, + const struct prefix_evpn *global_p) +{ + memcpy(vni_p, global_p, sizeof(*vni_p)); + memset(&vni_p->prefix.macip_addr.ip, 0, sizeof(struct ipaddr)); +} + +/* Get MAC of path_info prefix */ +static inline struct ethaddr * +evpn_type2_path_info_get_mac(const struct bgp_path_info *local_pi) +{ + assert(local_pi->extra); + return &local_pi->extra->vni_info.mac; +} + +/* Get IP of path_info prefix */ +static inline struct ipaddr * +evpn_type2_path_info_get_ip(const struct bgp_path_info *local_pi) +{ + assert(local_pi->extra); + return &local_pi->extra->vni_info.ip; +} + +/* Set MAC of path_info prefix */ +static inline void evpn_type2_path_info_set_mac(struct bgp_path_info *local_pi, + const struct ethaddr mac) +{ + assert(local_pi->extra); + local_pi->extra->vni_info.mac = mac; +} + +/* Set IP of path_info prefix */ +static inline void evpn_type2_path_info_set_ip(struct bgp_path_info *local_pi, + const struct ipaddr ip) +{ + assert(local_pi->extra); + local_pi->extra->vni_info.ip = ip; +} + +/* Is the IP empty for the RT's dest? */ +static inline bool is_evpn_type2_dest_ipaddr_none(const struct bgp_dest *dest) +{ + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); + + assert(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE); + return is_evpn_prefix_ipaddr_none(evp); +} + static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi, safi_t safi) { @@ -651,14 +723,39 @@ extern void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, int vni_list_cmp(void *p1, void *p2); extern int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, struct bgp_dest *dest); -extern struct bgp_dest *bgp_global_evpn_node_get(struct bgp_table *table, - afi_t afi, safi_t safi, - const struct prefix_evpn *evp, - struct prefix_rd *prd); extern struct bgp_dest * -bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, +bgp_evpn_global_node_get(struct bgp_table *table, afi_t afi, safi_t safi, + const struct prefix_evpn *evp, struct prefix_rd *prd, + const struct bgp_path_info *local_pi); +extern struct bgp_dest * +bgp_evpn_global_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, + const struct prefix_evpn *evp, + struct prefix_rd *prd, + const struct bgp_path_info *local_pi); +extern struct bgp_dest * +bgp_evpn_vni_ip_node_get(struct bgp_table *const table, + const struct prefix_evpn *evp, + const struct bgp_path_info *parent_pi); +extern struct bgp_dest * +bgp_evpn_vni_ip_node_lookup(const struct bgp_table *const table, const struct prefix_evpn *evp, - struct prefix_rd *prd); + const struct bgp_path_info *parent_pi); +extern struct bgp_dest * +bgp_evpn_vni_mac_node_get(struct bgp_table *const table, + const struct prefix_evpn *evp, + const struct bgp_path_info *parent_pi); +extern struct bgp_dest * +bgp_evpn_vni_mac_node_lookup(const struct bgp_table *const table, + const struct prefix_evpn *evp, + const struct bgp_path_info *parent_pi); +extern struct bgp_dest * +bgp_evpn_vni_node_get(struct bgpevpn *vpn, const struct prefix_evpn *p, + const struct bgp_path_info *parent_pi); +extern struct bgp_dest * +bgp_evpn_vni_node_lookup(const struct bgpevpn *vpn, const struct prefix_evpn *p, + const struct bgp_path_info *parent_pi); + +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, diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 0f2ade8737..88c1329f48 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -57,6 +57,8 @@ struct vni_walk_ctx { struct in_addr vtep_ip; json_object *json; int detail; + int type; + bool mac_table; }; int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx, @@ -517,7 +519,7 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json, "sviInterface", ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id)); } else { - vty_out(vty, "VNI: %d", vpn->vni); + vty_out(vty, "VNI: %u", vpn->vni); if (is_vni_live(vpn)) vty_out(vty, " (known to the kernel)"); vty_out(vty, "\n"); @@ -735,7 +737,8 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, if (detail) route_vty_out_detail( - vty, bgp, rn, pi, AFI_L2VPN, SAFI_EVPN, + vty, bgp, rn, bgp_dest_get_prefix(rn), + pi, AFI_L2VPN, SAFI_EVPN, RPKI_NOT_BEING_USED, json_path); else route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, @@ -770,9 +773,10 @@ static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi, 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) +static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, + struct vty *vty, int type, bool mac_table, + struct in_addr vtep_ip, json_object *json, + int detail) { struct bgp_dest *dest; struct bgp_path_info *pi; @@ -783,7 +787,11 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, prefix_cnt = path_cnt = 0; - table = vpn->route_table; + if (mac_table) + table = vpn->mac_table; + else + table = vpn->ip_table; + tbl_ver = table->version; for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { const struct prefix_evpn *evp = @@ -818,6 +826,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, * with code that already exists). */ for (; pi; pi = pi->next) { + struct prefix tmp_p; json_object *json_path = NULL; if (vtep_ip.s_addr != INADDR_ANY @@ -825,16 +834,43 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, &(pi->attr->nexthop))) continue; + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + /* + * VNI IP/MAC table prefixes don't have MAC/IP + * respectively so make sure it's set from path + * info here. + */ + if (is_evpn_prefix_ipaddr_none(evp)) { + /* VNI MAC -> Global */ + evpn_type2_prefix_global_copy( + (struct prefix_evpn *)&tmp_p, + evp, NULL /* mac */, + evpn_type2_path_info_get_ip( + pi)); + } else { + /* VNI IP -> Global */ + evpn_type2_prefix_global_copy( + (struct prefix_evpn *)&tmp_p, + evp, + evpn_type2_path_info_get_mac( + pi), + NULL /* ip */); + } + } else + memcpy(&tmp_p, p, sizeof(tmp_p)); + + if (json) json_path = json_object_new_array(); if (detail) - route_vty_out_detail(vty, bgp, dest, pi, + route_vty_out_detail(vty, bgp, dest, &tmp_p, pi, AFI_L2VPN, SAFI_EVPN, RPKI_NOT_BEING_USED, json_path); + else - route_vty_out(vty, p, pi, 0, SAFI_EVPN, + route_vty_out(vty, &tmp_p, pi, 0, SAFI_EVPN, json_path, false); if (json) @@ -887,19 +923,55 @@ static void show_vni_routes_hash(struct hash_bucket *bucket, void *arg) json_object *json_vni = NULL; char vni_str[VNI_STR_LEN]; - snprintf(vni_str, sizeof(vni_str), "%d", vpn->vni); + snprintf(vni_str, sizeof(vni_str), "%u", vpn->vni); + if (json) { + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "vni", vpn->vni); + } else { + vty_out(vty, "\nVNI: %u\n\n", vpn->vni); + } + + show_vni_routes(wctx->bgp, vpn, wctx->vty, wctx->type, wctx->mac_table, + wctx->vtep_ip, json_vni, wctx->detail); + + if (json) + json_object_object_add(json, vni_str, json_vni); +} + +static void show_vni_routes_all_hash(struct hash_bucket *bucket, void *arg) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct vni_walk_ctx *wctx = arg; + struct vty *vty = wctx->vty; + json_object *json = wctx->json; + json_object *json_vni = NULL; + json_object *json_vni_mac = NULL; + char vni_str[VNI_STR_LEN]; + + snprintf(vni_str, sizeof(vni_str), "%u", vpn->vni); if (json) { json_vni = json_object_new_object(); json_object_int_add(json_vni, "vni", vpn->vni); } else { - vty_out(vty, "\nVNI: %d\n\n", vpn->vni); + vty_out(vty, "\nVNI: %u\n\n", vpn->vni); } - show_vni_routes(wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip, json_vni, - wctx->detail); + show_vni_routes(wctx->bgp, vpn, wctx->vty, 0, false, wctx->vtep_ip, + json_vni, wctx->detail); if (json) json_object_object_add(json, vni_str, json_vni); + + if (json) + json_vni_mac = json_object_new_object(); + else + vty_out(vty, "\nVNI: %u MAC Table\n\n", vpn->vni); + + show_vni_routes(wctx->bgp, vpn, wctx->vty, 0, true, wctx->vtep_ip, + json_vni_mac, wctx->detail); + + if (json) + json_object_object_add(json_vni, "macTable", json_vni_mac); } static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, @@ -2322,9 +2394,9 @@ static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp, /* * Display EVPN routes for all VNIs - vty handler. */ -static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, - struct in_addr vtep_ip, json_object *json, - int detail) +static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, int type, + bool mac_table, struct in_addr vtep_ip, + json_object *json, int detail) { uint32_t num_vnis; struct vni_walk_ctx wctx; @@ -2335,6 +2407,8 @@ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, memset(&wctx, 0, sizeof(wctx)); wctx.bgp = bgp; wctx.vty = vty; + wctx.type = type; + wctx.mac_table = mac_table; wctx.vtep_ip = vtep_ip; wctx.json = json; wctx.detail = detail; @@ -2344,6 +2418,32 @@ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, } /* + * Display EVPN routes for all VNIs & all types - vty handler. + */ +static void evpn_show_routes_vni_all_type_all(struct vty *vty, struct bgp *bgp, + struct in_addr vtep_ip, + json_object *json, int detail) +{ + uint32_t num_vnis; + struct vni_walk_ctx wctx; + + num_vnis = hashcount(bgp->vnihash); + if (!num_vnis) + return; + + memset(&wctx, 0, sizeof(struct vni_walk_ctx)); + wctx.bgp = bgp; + wctx.vty = vty; + wctx.vtep_ip = vtep_ip; + wctx.json = json; + wctx.detail = detail; + hash_iterate(bgp->vnihash, + (void (*)(struct hash_bucket *, + void *))show_vni_routes_all_hash, + &wctx); +} + +/* * Display EVPN routes for a VNI -- for specific type-3 route (vty handler). */ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, @@ -2371,7 +2471,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, /* See if route exists. */ build_evpn_type3_prefix(&p, orig_ip); - dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); + dest = bgp_evpn_vni_node_lookup(vpn, &p, NULL); if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) { if (!json) vty_out(vty, "%% Network not in table\n"); @@ -2386,7 +2486,8 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, json_paths = json_object_new_array(); /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, dest, NULL, afi, safi, json); + route_vty_out_detail_header(vty, bgp, dest, bgp_dest_get_prefix(dest), + NULL, afi, safi, json); /* Display each path for this prefix. */ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { @@ -2395,8 +2496,9 @@ 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, - RPKI_NOT_BEING_USED, json_path); + route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest), + pi, afi, safi, RPKI_NOT_BEING_USED, + json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2427,12 +2529,15 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, { struct bgpevpn *vpn; struct prefix_evpn p; + struct prefix_evpn tmp_p; struct bgp_dest *dest; struct bgp_path_info *pi; uint32_t path_cnt = 0; afi_t afi; safi_t safi; json_object *json_paths = NULL; + struct ethaddr empty_mac = {}; + const struct prefix_evpn *evp; afi = AFI_L2VPN; safi = SAFI_EVPN; @@ -2445,9 +2550,10 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, return; } + build_evpn_type2_prefix(&p, mac ? mac : &empty_mac, ip); + /* See if route exists. Look for both non-sticky and sticky. */ - build_evpn_type2_prefix(&p, mac, ip); - dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); + dest = bgp_evpn_vni_node_lookup(vpn, &p, NULL); if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) { if (!json) vty_out(vty, "%% Network not in table\n"); @@ -2458,21 +2564,68 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, return; } + /* + * MAC is per-path, we have to walk the path_info's and look for it + * first here. + */ + if (ip && mac) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { + if (memcmp(mac, evpn_type2_path_info_get_mac(pi), + sizeof(*mac)) == 0) + break; + } + + if (!pi) { + if (!json) + vty_out(vty, "%% Network not in table\n"); + return; + } + } + if (json) json_paths = json_object_new_array(); /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, dest, NULL, afi, safi, json); + route_vty_out_detail_header(vty, bgp, dest, (struct prefix *)&p, NULL, + afi, safi, json); + + evp = (const struct prefix_evpn *)bgp_dest_get_prefix(dest); /* Display each path for this prefix. */ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { json_object *json_path = NULL; + /* skip non-matching MACs */ + if (ip && mac && + memcmp(mac, evpn_type2_path_info_get_mac(pi), + sizeof(*mac)) != 0) + continue; + if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, afi, safi, - RPKI_NOT_BEING_USED, json_path); + /* + * VNI table MAC-IP prefixes don't have MAC so + * make sure it's set from path info + * here. + */ + if (is_evpn_prefix_ipaddr_none(evp)) { + /* VNI MAC -> Global */ + evpn_type2_prefix_global_copy( + (struct prefix_evpn *)&tmp_p, evp, + NULL /* mac */, + evpn_type2_path_info_get_ip(pi)); + } else { + /* VNI IP -> Global */ + evpn_type2_prefix_global_copy( + (struct prefix_evpn *)&tmp_p, evp, + evpn_type2_path_info_get_mac(pi), + NULL /* ip */); + } + + route_vty_out_detail(vty, bgp, dest, (struct prefix *)&tmp_p, + pi, afi, safi, RPKI_NOT_BEING_USED, + json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2516,8 +2669,8 @@ static void evpn_show_routes_esi(struct vty *vty, struct bgp *bgp, * If the vtep_ip is non zero, only routes behind that vtep are shown */ static void evpn_show_routes_vni(struct vty *vty, struct bgp *bgp, vni_t vni, - int type, struct in_addr vtep_ip, - json_object *json) + int type, bool mac_table, + struct in_addr vtep_ip, json_object *json) { struct bgpevpn *vpn; @@ -2530,7 +2683,7 @@ static void evpn_show_routes_vni(struct vty *vty, struct bgp *bgp, vni_t vni, } /* Walk this VNI's route table and display appropriate routes. */ - show_vni_routes(bgp, vpn, type, vty, vtep_ip, json, 0); + show_vni_routes(bgp, vpn, vty, type, mac_table, vtep_ip, json, 0); } /* @@ -2568,7 +2721,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, } /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, dest, prd, afi, safi, json); + route_vty_out_detail_header(vty, bgp, dest, bgp_dest_get_prefix(dest), + prd, afi, safi, json); if (json) json_paths = json_object_new_array(); @@ -2580,8 +2734,9 @@ 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, - RPKI_NOT_BEING_USED, json_path); + route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest), + pi, afi, safi, RPKI_NOT_BEING_USED, + json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2673,8 +2828,9 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, } /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, dest, prd, afi, - safi, json_prefix); + route_vty_out_detail_header( + vty, bgp, dest, bgp_dest_get_prefix(dest), prd, + afi, safi, json_prefix); prefix_cnt++; } @@ -2689,8 +2845,9 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, afi, safi, - RPKI_NOT_BEING_USED, json_path); + route_vty_out_detail( + vty, bgp, dest, bgp_dest_get_prefix(dest), pi, + afi, safi, RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2807,7 +2964,7 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp, } else /* Prefix and num paths displayed once per prefix. */ route_vty_out_detail_header( - vty, bgp, dest, (struct prefix_rd *)rd_destp, + vty, bgp, dest, p, (struct prefix_rd *)rd_destp, AFI_L2VPN, SAFI_EVPN, json_prefix); /* For EVPN, the prefix is displayed for each path (to @@ -2822,7 +2979,7 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, AFI_L2VPN, + route_vty_out_detail(vty, bgp, dest, p, pi, AFI_L2VPN, SAFI_EVPN, RPKI_NOT_BEING_USED, json_path); @@ -2960,6 +3117,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, if (detail) route_vty_out_detail_header( vty, bgp, dest, + bgp_dest_get_prefix(dest), (struct prefix_rd *)rd_destp, AFI_L2VPN, SAFI_EVPN, json_prefix); @@ -2979,9 +3137,10 @@ 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, RPKI_NOT_BEING_USED, - json_path); + vty, bgp, dest, + bgp_dest_get_prefix(dest), pi, + AFI_L2VPN, SAFI_EVPN, + RPKI_NOT_BEING_USED, json_path); } else route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path, false); @@ -3297,7 +3456,7 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) struct ecommunity *ecom; if (is_vni_configured(vpn)) { - vty_out(vty, " vni %d\n", vpn->vni); + vty_out(vty, " vni %u\n", vpn->vni); if (is_rd_configured(vpn)) vty_out(vty, " rd %pRD\n", &vpn->prd); @@ -4562,28 +4721,32 @@ DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, as_type, as, show_flags); } +static int bgp_evpn_cli_parse_type_cmp(int *type, const char *type_str) +{ + if ((strncmp(type_str, "ma", 2) == 0) || (strmatch(type_str, "2"))) + *type = BGP_EVPN_MAC_IP_ROUTE; + else if ((strncmp(type_str, "mu", 2) == 0) || (strmatch(type_str, "3"))) + *type = BGP_EVPN_IMET_ROUTE; + else if ((strncmp(type_str, "es", 2) == 0) || (strmatch(type_str, "4"))) + *type = BGP_EVPN_ES_ROUTE; + else if ((strncmp(type_str, "ea", 2) == 0) || (strmatch(type_str, "1"))) + *type = BGP_EVPN_AD_ROUTE; + else if ((strncmp(type_str, "p", 1) == 0) || (strmatch(type_str, "5"))) + *type = BGP_EVPN_IP_PREFIX_ROUTE; + else + return -1; + + return 0; +} + int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc) { int type_idx = 0; if (argv_find(argv, argc, "type", &type_idx)) { /* Specific type is requested */ - if ((strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0) - || (strmatch(argv[type_idx + 1]->arg, "2"))) - *type = BGP_EVPN_MAC_IP_ROUTE; - else if ((strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) - || (strmatch(argv[type_idx + 1]->arg, "3"))) - *type = BGP_EVPN_IMET_ROUTE; - else if ((strncmp(argv[type_idx + 1]->arg, "es", 2) == 0) - || (strmatch(argv[type_idx + 1]->arg, "4"))) - *type = BGP_EVPN_ES_ROUTE; - else if ((strncmp(argv[type_idx + 1]->arg, "ea", 2) == 0) - || (strmatch(argv[type_idx + 1]->arg, "1"))) - *type = BGP_EVPN_AD_ROUTE; - else if ((strncmp(argv[type_idx + 1]->arg, "p", 1) == 0) - || (strmatch(argv[type_idx + 1]->arg, "5"))) - *type = BGP_EVPN_IP_PREFIX_ROUTE; - else + if (bgp_evpn_cli_parse_type_cmp(type, + argv[type_idx + 1]->arg) != 0) return -1; } @@ -4881,7 +5044,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd, } } - evpn_show_routes_vni(vty, bgp, vni, type, vtep_ip, json); + evpn_show_routes_vni(vty, bgp, vni, type, false, vtep_ip, json); if (uj) vty_json(vty, json); @@ -5063,7 +5226,464 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all, } } - evpn_show_routes_vni_all(vty, bgp, vtep_ip, json, da); + evpn_show_routes_vni_all(vty, bgp, 0, false, vtep_ip, json, da); + + if (uj) { + vty_json(vty, json); + json_object_free(json); + } + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN ALL routing tables - for all VNIs. + */ +DEFPY(show_bgp_vni_all, + show_bgp_vni_all_cmd, + "show bgp vni all [vtep A.B.C.D$addr] [detail$detail] [json$uj]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_ALL_HELP_STR + VTEP_HELP_STR + VTEP_IP_HELP_STR + DETAIL_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni_all_type_all(vty, bgp, addr, json, !!detail); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN EAD routing table - for all VNIs. + */ +DEFPY(show_bgp_vni_all_ead, + show_bgp_vni_all_ead_cmd, + "show bgp vni all type <1|ead> [vtep A.B.C.D$addr] [<detail$detail|json$uj>]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_ALL_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_1_HELP_STR + EVPN_TYPE_1_HELP_STR + VTEP_HELP_STR + VTEP_IP_HELP_STR + DETAIL_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni_all(vty, bgp, BGP_EVPN_AD_ROUTE, false, addr, json, + !!detail); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN MAC routing table - for all VNIs. + */ +DEFPY(show_bgp_vni_all_macip_mac, + show_bgp_vni_all_macip_mac_cmd, + "show bgp vni all type <2|macip> mac [vtep A.B.C.D$addr] [<detail$detail|json$uj>]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_ALL_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_2_HELP_STR + "MAC Table\n" + VTEP_HELP_STR + VTEP_IP_HELP_STR + DETAIL_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni_all(vty, bgp, BGP_EVPN_MAC_IP_ROUTE, true, addr, + json, !!detail); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN IP routing table - for all VNIs. + */ +DEFPY(show_bgp_vni_all_macip_ip, + show_bgp_vni_all_macip_ip_cmd, + "show bgp vni all type <2|macip> ip [vtep A.B.C.D$addr] [<detail$detail|json$uj>]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_ALL_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_2_HELP_STR + "IP Table\n" + VTEP_HELP_STR + VTEP_IP_HELP_STR + DETAIL_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni_all(vty, bgp, BGP_EVPN_MAC_IP_ROUTE, false, addr, + json, !!detail); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN Multicast routing table - for all VNIs. + */ +DEFPY(show_bgp_vni_all_imet, + show_bgp_vni_all_imet_cmd, + "show bgp vni all type <3|multicast> [vtep A.B.C.D$addr] [<detail$detail|json$uj>]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_ALL_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_3_HELP_STR + EVPN_TYPE_3_HELP_STR + VTEP_HELP_STR + VTEP_IP_HELP_STR + DETAIL_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni_all(vty, bgp, BGP_EVPN_IMET_ROUTE, false, addr, + json, !!detail); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN ALL routing tables - for select VNI + */ +DEFPY(show_bgp_vni, + show_bgp_vni_cmd, + "show bgp vni "CMD_VNI_RANGE"$vni [vtep A.B.C.D$addr] [json$uj]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_NUM_HELP_STR + VTEP_HELP_STR + VTEP_IP_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + json_object *json_mac = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) { + json = json_object_new_object(); + json_mac = json_object_new_object(); + } + + evpn_show_routes_vni(vty, bgp, vni, 0, false, addr, json); + + if (!uj) + vty_out(vty, "\n\nMAC Table:\n\n"); + + evpn_show_routes_vni(vty, bgp, vni, 0, true, addr, json_mac); + + if (uj) { + json_object_object_add(json, "macTable", json_mac); + vty_json(vty, json); + } + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN EAD routing table - for select VNI + */ +DEFPY(show_bgp_vni_ead, + show_bgp_vni_ead_cmd, + "show bgp vni "CMD_VNI_RANGE"$vni type <1|ead> [vtep A.B.C.D$addr] [json$uj]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_NUM_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_1_HELP_STR + EVPN_TYPE_1_HELP_STR + VTEP_HELP_STR + VTEP_IP_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni(vty, bgp, vni, BGP_EVPN_AD_ROUTE, false, addr, + json); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN MAC-IP MAC routing table - for select VNI + */ +DEFPY(show_bgp_vni_macip_mac, + show_bgp_vni_macip_mac_cmd, + "show bgp vni "CMD_VNI_RANGE"$vni type <2|macip> mac [vtep A.B.C.D$addr] [json$uj]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_NUM_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_2_HELP_STR + "MAC Table\n" + VTEP_HELP_STR + VTEP_IP_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni(vty, bgp, vni, BGP_EVPN_MAC_IP_ROUTE, true, addr, + json); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN MAC-IP IP routing table - for select VNI + */ +DEFPY(show_bgp_vni_macip_ip, + show_bgp_vni_macip_ip_cmd, + "show bgp vni "CMD_VNI_RANGE"$vni type <2|macip> ip [vtep A.B.C.D$addr] [json$uj]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_NUM_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_2_HELP_STR + "IP Table\n" + VTEP_HELP_STR + VTEP_IP_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni(vty, bgp, vni, BGP_EVPN_MAC_IP_ROUTE, false, addr, + json); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN Multicast routing table - for select VNI + */ +DEFPY(show_bgp_vni_imet, + show_bgp_vni_imet_cmd, + "show bgp vni "CMD_VNI_RANGE"$vni type <3|multicast> [vtep A.B.C.D$addr] [json$uj]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_NUM_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_3_HELP_STR + EVPN_TYPE_3_HELP_STR + VTEP_HELP_STR + VTEP_IP_HELP_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_routes_vni(vty, bgp, vni, BGP_EVPN_IMET_ROUTE, false, addr, + json); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN MACIP MAC routing table - for select VNI & MAC + */ +DEFPY(show_bgp_vni_macip_mac_addr, + show_bgp_vni_macip_mac_addr_cmd, + "show bgp vni "CMD_VNI_RANGE"$vni type <2|macip> mac X:X:X:X:X:X [json$uj]", + SHOW_STR + BGP_STR + VNI_HELP_STR + VNI_NUM_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_2_HELP_STR + "MAC Table\n" + MAC_STR + JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + evpn_show_route_vni_macip(vty, bgp, vni, &mac->eth_addr, NULL, json); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* + * Display per-VNI EVPN MACIP IP routing table - for select VNI & IP + */ +DEFPY(show_bgp_vni_macip_ip_addr, show_bgp_vni_macip_ip_addr_cmd, + "show bgp vni " CMD_VNI_RANGE + "$vni type <2|macip> ip <A.B.C.D|X:X::X:X> [json$uj]", + SHOW_STR BGP_STR VNI_HELP_STR VNI_NUM_HELP_STR EVPN_TYPE_HELP_STR + EVPN_TYPE_2_HELP_STR EVPN_TYPE_2_HELP_STR + "IP Table\n" IP_ADDR_STR IP6_ADDR_STR JSON_STR) +{ + struct bgp *bgp; + json_object *json = NULL; + struct ipaddr ip_addr = {.ipa_type = IPADDR_NONE}; + + bgp = bgp_get_evpn(); + if (!bgp) + return CMD_WARNING; + + /* check if we need json output */ + if (uj) + json = json_object_new_object(); + + if (sockunion_family(ip) == AF_INET) { + ip_addr.ipa_type = IPADDR_V4; + ip_addr.ipaddr_v4.s_addr = sockunion2ip(ip); + } else { + ip_addr.ipa_type = IPADDR_V6; + memcpy(&ip_addr.ipaddr_v6, &ip->sin6.sin6_addr, + sizeof(struct in6_addr)); + } + evpn_show_route_vni_macip(vty, bgp, vni, NULL, &ip_addr, json); if (uj) vty_json(vty, json); @@ -6755,6 +7375,20 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd); + /* "show bgp vni" commands. */ + install_element(VIEW_NODE, &show_bgp_vni_all_cmd); + install_element(VIEW_NODE, &show_bgp_vni_all_ead_cmd); + install_element(VIEW_NODE, &show_bgp_vni_all_macip_mac_cmd); + install_element(VIEW_NODE, &show_bgp_vni_all_macip_ip_cmd); + install_element(VIEW_NODE, &show_bgp_vni_all_imet_cmd); + install_element(VIEW_NODE, &show_bgp_vni_cmd); + install_element(VIEW_NODE, &show_bgp_vni_ead_cmd); + install_element(VIEW_NODE, &show_bgp_vni_macip_mac_cmd); + install_element(VIEW_NODE, &show_bgp_vni_macip_ip_cmd); + install_element(VIEW_NODE, &show_bgp_vni_imet_cmd); + install_element(VIEW_NODE, &show_bgp_vni_macip_mac_addr_cmd); + install_element(VIEW_NODE, &show_bgp_vni_macip_ip_addr_cmd); + /* "show bgp evpn" commands. */ install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd); install_element(VIEW_NODE, &show_bgp_evpn_summary_cmd); diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h index 137365ddbf..6b17a83b74 100644 --- a/bgpd/bgp_evpn_vty.h +++ b/bgpd/bgp_evpn_vty.h @@ -27,6 +27,12 @@ extern void bgp_ethernetvpn_init(void); #define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n" #define EVPN_HELP_STR "Ethernet Virtual Private Network\n" +#define VNI_HELP_STR "VXLAN Network Identifier\n" +#define VNI_NUM_HELP_STR "VNI number\n" +#define VNI_ALL_HELP_STR "All VNIs\n" +#define DETAIL_HELP_STR "Print Detailed Output\n" +#define VTEP_HELP_STR "Remote VTEP\n" +#define VTEP_IP_HELP_STR "Remote VTEP IP address\n" extern int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx, diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 01c61615a3..f110005e49 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -634,7 +634,8 @@ const char *const peer_down_str[] = {"", "AS Set config change", "Waiting for peer OPEN", "Reached received prefix count", - "Socket Error"}; + "Socket Error", + "Admin. shutdown (RTT)"}; static void bgp_graceful_restart_timer_off(struct peer *peer) { @@ -1832,7 +1833,9 @@ int bgp_start(struct peer *peer) flog_err(EC_BGP_FSM, "%s [FSM] Trying to start suppressed peer - this is never supposed to happen!", peer->host); - if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(peer->sflags, PEER_STATUS_RTT_SHUTDOWN)) + peer->last_reset = PEER_DOWN_RTT_SHUTDOWN; + else if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) peer->last_reset = PEER_DOWN_USER_SHUTDOWN; else if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) peer->last_reset = PEER_DOWN_USER_SHUTDOWN; diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index ce8ce96a0d..49ae9816a3 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -280,10 +280,11 @@ static void bgp_process_reads(struct thread *thread) case -ENOMEM: ibuf_full = true; if (!ibuf_full_logged) { - flog_warn( - EC_BGP_UPDATE_RCV, - "%s [Warning] Peer Input-Queue is full: limit (%u)", - peer->host, bm->inq_limit); + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s [Event] Peer Input-Queue is full: limit (%u)", + peer->host, bm->inq_limit); + ibuf_full_logged = true; } break; diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 9a25450afa..18cb90763c 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1554,13 +1554,22 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ /* Set SID for SRv6 VPN */ if (from_bgp->vpn_policy[afi].tovpn_sid_locator) { + struct srv6_locator_chunk *locator = + from_bgp->vpn_policy[afi].tovpn_sid_locator; encode_label( from_bgp->vpn_policy[afi].tovpn_sid_transpose_label, &label); static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, sizeof(struct bgp_attr_srv6_l3vpn)); static_attr.srv6_l3vpn->sid_flags = 0x00; - static_attr.srv6_l3vpn->endpoint_behavior = 0xffff; + static_attr.srv6_l3vpn->endpoint_behavior = + afi == AFI_IP + ? (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT4) + : (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT6); static_attr.srv6_l3vpn->loc_block_len = from_bgp->vpn_policy[afi] .tovpn_sid_locator->block_bits_length; @@ -1587,12 +1596,17 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ .tovpn_sid_locator->prefix.prefix, sizeof(struct in6_addr)); } else if (from_bgp->tovpn_sid_locator) { + struct srv6_locator_chunk *locator = + from_bgp->tovpn_sid_locator; encode_label(from_bgp->tovpn_sid_transpose_label, &label); static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, sizeof(struct bgp_attr_srv6_l3vpn)); static_attr.srv6_l3vpn->sid_flags = 0x00; - static_attr.srv6_l3vpn->endpoint_behavior = 0xffff; + static_attr.srv6_l3vpn->endpoint_behavior = + CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT46; static_attr.srv6_l3vpn->loc_block_len = from_bgp->tovpn_sid_locator->block_bits_length; static_attr.srv6_l3vpn->loc_node_len = diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 72d6a92317..f3ca3bba0a 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1011,9 +1011,12 @@ static void bgp_notify_send_internal(struct peer *peer, uint8_t code, if (code == BGP_NOTIFY_CEASE) { if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET) peer->last_reset = PEER_DOWN_USER_RESET; - else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN) - peer->last_reset = PEER_DOWN_USER_SHUTDOWN; - else + else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN) { + if (CHECK_FLAG(peer->sflags, PEER_STATUS_RTT_SHUTDOWN)) + peer->last_reset = PEER_DOWN_RTT_SHUTDOWN; + else + peer->last_reset = PEER_DOWN_USER_SHUTDOWN; + } else peer->last_reset = PEER_DOWN_NOTIFY_SEND; } else peer->last_reset = PEER_DOWN_NOTIFY_SEND; @@ -1749,15 +1752,24 @@ static int bgp_keepalive_receive(struct peer *peer, bgp_size_t size) /* If the peer's RTT is higher than expected, shutdown * the peer automatically. */ - if (CHECK_FLAG(peer->flags, PEER_FLAG_RTT_SHUTDOWN) - && peer->rtt > peer->rtt_expected) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_RTT_SHUTDOWN)) + return Receive_KEEPALIVE_message; + if (peer->rtt > peer->rtt_expected) { peer->rtt_keepalive_rcv++; if (peer->rtt_keepalive_rcv > peer->rtt_keepalive_conf) { - zlog_warn( - "%s shutdown due to high round-trip-time (%dms > %dms)", - peer->host, peer->rtt, peer->rtt_expected); + char rtt_shutdown_reason[BUFSIZ] = {}; + + snprintfrr( + rtt_shutdown_reason, + sizeof(rtt_shutdown_reason), + "shutdown due to high round-trip-time (%dms > %dms, hit %u times)", + peer->rtt, peer->rtt_expected, + peer->rtt_keepalive_rcv); + zlog_warn("%s %s", peer->host, rtt_shutdown_reason); + SET_FLAG(peer->sflags, PEER_STATUS_RTT_SHUTDOWN); + peer_tx_shutdown_message_set(peer, rtt_shutdown_reason); peer_flag_set(peer, PEER_FLAG_SHUTDOWN); } } else { diff --git a/bgpd/bgp_regex.h b/bgpd/bgp_regex.h index 43ebb9ac91..e07b7f911b 100644 --- a/bgpd/bgp_regex.h +++ b/bgpd/bgp_regex.h @@ -18,19 +18,24 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _QUAGGA_BGP_REGEX_H -#define _QUAGGA_BGP_REGEX_H +#ifndef _FRR_BGP_REGEX_H +#define _FRR_BGP_REGEX_H #include <zebra.h> -#ifdef HAVE_LIBPCREPOSIX +#ifdef HAVE_LIBPCRE2_POSIX +#ifndef _FRR_PCRE2_POSIX +#define _FRR_PCRE2_POSIX +#include <pcre2posix.h> +#endif /* _FRR_PCRE2_POSIX */ +#elif defined(HAVE_LIBPCREPOSIX) #include <pcreposix.h> #else #include <regex.h> -#endif /* HAVE_LIBPCREPOSIX */ +#endif /* HAVE_LIBPCRE2_POSIX */ extern void bgp_regex_free(regex_t *regex); extern regex_t *bgp_regcomp(const char *str); extern int bgp_regexec(regex_t *regex, struct aspath *aspath); -#endif /* _QUAGGA_BGP_REGEX_H */ +#endif /* _FRR_BGP_REGEX_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 130a0b4abd..6eb1a556b1 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4044,6 +4044,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, int vnc_implicit_withdraw = 0; #endif int same_attr = 0; + const struct prefix *bgp_nht_param_prefix; /* Special case for BGP-LU - map LU safi to ordinary unicast safi */ if (orig_safi == SAFI_LABELED_UNICAST) @@ -4111,6 +4112,11 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (aspath_get_last_as(attr->aspath) == bgp->as) do_loop_check = 0; + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) + bgp_nht_param_prefix = NULL; + else + bgp_nht_param_prefix = p; + /* AS path loop check. */ if (do_loop_check) { if (aspath_loop_check(attr->aspath, bgp->as) > allowas_in || @@ -4621,8 +4627,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, nh_afi, safi, pi, NULL, connected, - p) - || CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) + bgp_nht_param_prefix) || + CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); else { @@ -4784,8 +4790,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, nh_afi = BGP_ATTR_NH_AFI(afi, new->attr); if (bgp_find_or_add_nexthop(bgp, bgp, nh_afi, safi, new, NULL, - connected, p) - || CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) + connected, bgp_nht_param_prefix) || + CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); else { if (BGP_DEBUG(nht, NHT)) { @@ -10209,12 +10215,14 @@ 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, + const struct prefix *p, 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]; + char tag_buf[30]; struct attr *attr = path->attr; time_t tbuf; json_object *json_bestpath = NULL; @@ -10245,6 +10253,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, uint32_t bos = 0; uint32_t exp = 0; mpls_label_t label = MPLS_INVALID_LABEL; + tag_buf[0] = '\0'; if (json_paths) { json_path = json_object_new_object(); @@ -10254,13 +10263,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, if (safi == SAFI_EVPN) { if (!json_paths) - vty_out(vty, " Route %pRN", bn); + vty_out(vty, " Route %pFX", p); } if (path->extra) { - char tag_buf[30]; - - tag_buf[0] = '\0'; if (path->extra && path->extra->num_labels) { bgp_evpn_label2str(path->extra->label, path->extra->num_labels, tag_buf, @@ -10279,44 +10285,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } } } - - if (path->extra && path->extra->parent && !json_paths) { - struct bgp_path_info *parent_ri; - struct bgp_dest *dest, *pdest; - - parent_ri = (struct bgp_path_info *)path->extra->parent; - dest = parent_ri->net; - if (dest && dest->pdest) { - pdest = dest->pdest; - if (is_pi_family_evpn(parent_ri)) { - vty_out(vty, - " Imported from %pRD:%pFX, VNI %s", - (struct prefix_rd *) - bgp_dest_get_prefix( - pdest), - (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 %pRD:%pFX\n", - (struct prefix_rd *) - bgp_dest_get_prefix( - pdest), - (struct prefix_evpn *) - bgp_dest_get_prefix( - dest)); - } - } } if (safi == SAFI_EVPN @@ -10336,6 +10304,41 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, if (safi == SAFI_EVPN && !json_path) vty_out(vty, "\n"); + + if (path->extra && path->extra->parent && !json_paths) { + struct bgp_path_info *parent_ri; + struct bgp_dest *dest, *pdest; + + parent_ri = (struct bgp_path_info *)path->extra->parent; + dest = parent_ri->net; + if (dest && dest->pdest) { + pdest = dest->pdest; + if (is_pi_family_evpn(parent_ri)) { + vty_out(vty, + " Imported from %pRD:%pFX, VNI %s", + (struct prefix_rd *)bgp_dest_get_prefix( + pdest), + (struct prefix_evpn *) + bgp_dest_get_prefix(dest), + tag_buf); + if (CHECK_FLAG(attr->es_flags, ATTR_ES_L3_NHG)) + vty_out(vty, ", L3NHG %s", + CHECK_FLAG( + attr->es_flags, + ATTR_ES_L3_NHG_ACTIVE) + ? "active" + : "inactive"); + vty_out(vty, "\n"); + + } else + vty_out(vty, " Imported from %pRD:%pFX\n", + (struct prefix_rd *)bgp_dest_get_prefix( + pdest), + (struct prefix_evpn *) + bgp_dest_get_prefix(dest)); + } + } + /* Line1 display AS-path, Aggregator */ if (attr->aspath) { if (json_paths) { @@ -10415,10 +10418,9 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, /* Line2 display Next-hop, Neighbor, Router-id */ /* Display the nexthop */ - const struct prefix *bn_p = bgp_dest_get_prefix(bn); - if ((bn_p->family == AF_INET || bn_p->family == AF_ETHERNET || - bn_p->family == AF_EVPN) && + if ((p->family == AF_INET || p->family == AF_ETHERNET || + p->family == AF_EVPN) && (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN || !BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))) { if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP @@ -10531,7 +10533,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, /* This path was originated locally */ if (path->peer == bgp->peer_self) { - if (safi == SAFI_EVPN || (bn_p->family == AF_INET && + if (safi == SAFI_EVPN || (p->family == AF_INET && !BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))) { if (json_paths) json_object_string_add(json_peer, "peerId", @@ -11555,8 +11557,9 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, prd = bgp_rd_from_dest(dest, safi); route_vty_out_detail_header( - vty, bgp, dest, prd, table->afi, - safi, jtemp); + vty, bgp, dest, + bgp_dest_get_prefix(dest), prd, + table->afi, safi, jtemp); json_object_array_add(json_paths, jtemp); @@ -11582,7 +11585,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, else { if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_DETAIL)) route_vty_out_detail( - vty, bgp, dest, pi, + vty, bgp, dest, + bgp_dest_get_prefix(dest), pi, family2afi(dest_p->family), safi, RPKI_NOT_BEING_USED, json_paths); @@ -11803,12 +11807,11 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, /* Header of detailed BGP route information */ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, - struct bgp_dest *dest, - const struct prefix_rd *prd, - afi_t afi, safi_t safi, json_object *json) + struct bgp_dest *dest, const struct prefix *p, + const struct prefix_rd *prd, afi_t afi, + safi_t safi, json_object *json) { struct bgp_path_info *pi; - const struct prefix *p; struct peer *peer; struct listnode *node, *nnode; char buf1[RD_ADDRSTRLEN]; @@ -11838,7 +11841,6 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, mpls_lse_decode(dest->local_label, &label, &ttl, &exp, &bos); - p = bgp_dest_get_prefix(dest); has_valid_label = bgp_is_valid_label(&label); if (safi == SAFI_EVPN) { @@ -12046,8 +12048,9 @@ static void bgp_show_path_info(const struct prefix_rd *pfx_rd, if (header) { route_vty_out_detail_header( - vty, bgp, bgp_node, pfx_rd, - AFI_IP, safi, json_header); + vty, bgp, bgp_node, + bgp_dest_get_prefix(bgp_node), pfx_rd, AFI_IP, + safi, json_header); header = 0; } (*display)++; @@ -12058,8 +12061,10 @@ static void bgp_show_path_info(const 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, rpki_curr_state, json_paths); + route_vty_out_detail(vty, bgp, bgp_node, + bgp_dest_get_prefix(bgp_node), pi, + AFI_IP, safi, rpki_curr_state, + json_paths); } if (json && json_paths) { diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index c85551634a..3fa58c0dfb 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -214,9 +214,14 @@ struct bgp_path_info_extra { } vnc; #endif - /* For imported routes into a VNI (or VRF), this points to the parent. + /* + * For imported routes into a VNI (or VRF) */ - void *parent; + void *parent; /* parent from global table */ + union { + struct ethaddr mac; /* MAC set here for VNI IP table */ + struct ipaddr ip; /* IP set here for VNI MAC table */ + } vni_info; /* * Some tunnelish parameters follow. Maybe consolidate into an @@ -849,10 +854,11 @@ extern bool bgp_zebra_has_route_changed(struct bgp_path_info *selected); extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, struct bgp_dest *dest, + const struct prefix *p, const struct prefix_rd *prd, afi_t afi, safi_t safi, json_object *json); extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp, - struct bgp_dest *bn, + struct bgp_dest *bn, const struct prefix *p, struct bgp_path_info *path, afi_t afi, safi_t safi, enum rpki_states, json_object *json_paths); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index aff09206e4..b736e6c38a 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -30,11 +30,16 @@ #include "log.h" #include "frrlua.h" #include "frrscript.h" -#ifdef HAVE_LIBPCREPOSIX +#ifdef HAVE_LIBPCRE2_POSIX +#ifndef _FRR_PCRE2_POSIX +#define _FRR_PCRE2_POSIX +#include <pcre2posix.h> +#endif /* _FRR_PCRE2_POSIX */ +#elif defined(HAVE_LIBPCREPOSIX) #include <pcreposix.h> #else #include <regex.h> -#endif /* HAVE_LIBPCREPOSIX */ +#endif /* HAVE_LIBPCRE2_POSIX */ #include "buffer.h" #include "sockunion.h" #include "hash.h" diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 1f66080e93..6f0bd5eded 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -5272,8 +5272,10 @@ static int peer_flag_modify_vty(struct vty *vty, const char *ip_str, return CMD_WARNING_CONFIG_FAILED; } - if (!set && flag == PEER_FLAG_SHUTDOWN) + if (!set && flag == PEER_FLAG_SHUTDOWN) { peer_tx_shutdown_message_unset(peer); + UNSET_FLAG(peer->sflags, PEER_STATUS_RTT_SHUTDOWN); + } if (set) ret = peer_flag_set(peer, flag); @@ -14565,9 +14567,18 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, if (use_json) { json_object_int_add(json_neigh, "connectRetryTimer", p->v_connect); - if (peer_established(p) && p->rtt) + if (peer_established(p)) { json_object_int_add(json_neigh, "estimatedRttInMsecs", p->rtt); + if (CHECK_FLAG(p->flags, PEER_FLAG_RTT_SHUTDOWN)) { + json_object_int_add(json_neigh, + "shutdownRttInMsecs", + p->rtt_expected); + json_object_int_add(json_neigh, + "shutdownRttAfterCount", + p->rtt_keepalive_rcv); + } + } if (p->t_start) json_object_int_add( json_neigh, "nextStartTimerDueInMsecs", @@ -14602,9 +14613,14 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } else { vty_out(vty, "BGP Connect Retry Timer in Seconds: %d\n", p->v_connect); - if (peer_established(p) && p->rtt) + if (peer_established(p)) { vty_out(vty, "Estimated round trip time: %d ms\n", p->rtt); + if (CHECK_FLAG(p->flags, PEER_FLAG_RTT_SHUTDOWN)) + vty_out(vty, + "Shutdown when RTT > %dms, count > %u\n", + p->rtt_expected, p->rtt_keepalive_rcv); + } if (p->t_start) vty_out(vty, "Next start timer due in %ld seconds\n", thread_timer_remain_second(p->t_start)); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b3fce07344..8a0ec5ad2d 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1524,6 +1524,7 @@ struct peer { /* LLGR aware peer */ #define PEER_STATUS_LLGR_WAIT (1U << 11) #define PEER_STATUS_REFRESH_PENDING (1U << 12) /* refresh request from peer */ +#define PEER_STATUS_RTT_SHUTDOWN (1U << 13) /* In shutdown state due to RTT */ /* Configured timer values. */ _Atomic uint32_t holdtime; @@ -1737,6 +1738,7 @@ struct peer { #define PEER_DOWN_WAITING_OPEN 32U /* Waiting for open to succeed */ #define PEER_DOWN_PFX_COUNT 33U /* Reached received prefix count */ #define PEER_DOWN_SOCKET_ERROR 34U /* Some socket error happened */ +#define PEER_DOWN_RTT_SHUTDOWN 35U /* Automatically shutdown due to RTT */ /* * Remember to update peer_down_str in bgp_fsm.c when you add * a new value to the last_reset reason diff --git a/configure.ac b/configure.ac index 1a481ecd79..97c8ca451b 100644 --- a/configure.ac +++ b/configure.ac @@ -712,6 +712,8 @@ AC_ARG_ENABLE([cpu-time], AS_HELP_STRING([--disable-cpu-time], [disable cpu usage data gathering])) AC_ARG_ENABLE([pcreposix], AS_HELP_STRING([--enable-pcreposix], [enable using PCRE Posix libs for regex functions])) +AC_ARG_ENABLE([pcre2posix], + AS_HELP_STRING([--enable-pcre2posix], [enable using PCRE2 Posix libs for regex functions])) AC_ARG_ENABLE([fpm], AS_HELP_STRING([--enable-fpm], [enable Forwarding Plane Manager support])) AC_ARG_ENABLE([werror], @@ -1659,6 +1661,16 @@ if test "$enable_pcreposix" = "yes"; then fi AC_SUBST([HAVE_LIBPCREPOSIX]) +dnl --------------------------- +dnl check system has PCRE2 regexp +dnl --------------------------- +if test "$enable_pcre2posix" = "yes"; then + AC_CHECK_LIB([pcre2-posix], [regexec], [], [ + AC_MSG_ERROR([--enable-pcre2posix given but unable to find libpcre2-posix]) + ]) +fi +AC_SUBST([HAVE_LIBPCRE2_POSIX]) + dnl ########################################################################## dnl test "$enable_clippy_only" != "yes" fi diff --git a/debian/frr.install b/debian/frr.install index 48263222f8..044b484984 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -1,6 +1,7 @@ debian/frr.conf usr/lib/tmpfiles.d etc/ tools/etc/frr/frr.conf etc/frr/ +tools/etc/logrotate.d/frr etc/logrotate.d/ tools/frr-reload usr/lib/frr/ usr/bin/mtracebis usr/bin/vtysh diff --git a/debian/frr.pam b/debian/frr.pam index 2b106d43bc..737b88953b 100644 --- a/debian/frr.pam +++ b/debian/frr.pam @@ -1,3 +1,4 @@ # Any user may call vtysh but only those belonging to the group frrvty can # actually connect to the socket and use the program. auth sufficient pam_permit.so +account sufficient pam_rootok.so diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 7083b19b90..44349976f6 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -4135,6 +4135,10 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. EVPN prefixes can also be filtered by EVPN route type. +.. clicmd:: show bgp vni <all|VNI> [vtep VTEP] [type <ead|1|macip|2|multicast|3>] [<detail|json>] + + Display per-VNI EVPN routing table in bgp. Filter route-type, vtep, or VNI. + .. clicmd:: show bgp [afi] [safi] [all] summary [json] Show a bgp peer summary for the specified address family, and subsequent diff --git a/doc/user/installation.rst b/doc/user/installation.rst index ba35facf2a..8f89c6c4f8 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -368,6 +368,13 @@ options from the list below. Turn on the usage of PCRE Posix libs for regex functionality. +.. option:: --enable-pcre2posix + + Turn on the usage of PCRE2 Posix libs for regex functionality. + + PCRE2 versions <= 10.31 work a bit differently. We suggest using at least + >= 10.36. + .. option:: --enable-rpath Set hardcoded rpaths in the executable [default=yes]. diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst index f0b76f10b7..ec107fbe47 100644 --- a/doc/user/pathd.rst +++ b/doc/user/pathd.rst @@ -175,7 +175,7 @@ controller and obtain those by means of the PCEP protocol. .. image:: images/pathd_initiated_multi.png Starting -============= +======== Default configuration file for *pathd* is :file:`pathd.conf`. The typical location of :file:`pathd.conf` is |INSTALL_PREFIX_ETC|/pathd.conf. @@ -480,6 +480,12 @@ Configuration Commands Specify a peer and its precedence in a PCC definition. +Debugging +--------- + +.. clicmd:: debug pathd policy + + Enable or disable Pathd policy information. Introspection Commands ---------------------- diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 0a843b9283..db43266d68 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -810,6 +810,36 @@ and this section also helps that case. ! ... +.. clicmd:: behavior usid + + Specify the SRv6 locator as a Micro-segment (uSID) locator. When a locator is + specified as a uSID locator, all the SRv6 SIDs allocated from the locator by the routing + protocols are bound to the SRv6 uSID behaviors. For example, if you configure BGP to use + a locator specified as a uSID locator, BGP instantiates and advertises SRv6 uSID behaviors + (e.g., ``uDT4`` / ``uDT6`` / ``uDT46``) instead of classic SRv6 behaviors + (e.g., ``End.DT4`` / ``End.DT6`` / ``End.DT46``). + +:: + + router# configure terminal + router(config)# segment-routinig + router(config-sr)# srv6 + router(config-srv6)# locators + router(config-srv6-locators)# locator loc1 + router(config-srv6-locator)# prefix fc00:0:1::/48 block-len 32 node-len 16 func-bits 16 + router(config-srv6-locator)# behavior usid + + router(config-srv6-locator)# show run + ... + segment-routing + srv6 + locators + locator loc1 + prefix fc00:0:1::/48 + behavior usid + ! + ... + .. _multicast-rib-commands: Multicast RIB Commands diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index b9278dbb88..238a7fc409 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Create a basic stage set up to build APKs -FROM alpine:3.15 as alpine-builder +FROM alpine:3.16 as alpine-builder RUN apk add \ --update-cache \ abuild \ @@ -13,7 +13,7 @@ RUN apk add \ RUN adduser -D -G abuild builder && su builder -c 'abuild-keygen -a -n' # This stage builds a dist tarball from the source -FROM alpine:3.15 as source-builder +FROM alpine:3.16 as source-builder RUN mkdir -p /src/alpine COPY alpine/APKBUILD.in /src/alpine @@ -48,7 +48,7 @@ RUN cd /dist \ && abuild -r -P /pkgs/apk # This stage installs frr from the apk -FROM alpine:3.15 +FROM alpine:3.16 RUN mkdir -p /pkgs/apk COPY --from=alpine-apk-builder /pkgs/apk/ /pkgs/apk/ RUN apk add \ diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 4fdb11b211..c7dc9b7c44 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -485,6 +485,9 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix, struct prefix_ipv6 *src_p, struct isis_route_info *route_info) { + if (area == NULL) + return; + if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) { if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) return; @@ -492,9 +495,8 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix, isis_route_remove_previous_sid(area, prefix, route_info); /* Install route. */ - if (area) - isis_zebra_route_add_route(area->isis, prefix, src_p, - route_info); + isis_zebra_route_add_route(area->isis, prefix, src_p, + route_info); /* Install/reinstall Prefix-SID label. */ if (route_info->sr.present) isis_zebra_prefix_sid_install(area, prefix, route_info, @@ -509,9 +511,8 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix, isis_zebra_prefix_sid_uninstall( area, prefix, route_info, &route_info->sr); /* Uninstall route. */ - if (area) - isis_zebra_route_del_route(area->isis, prefix, src_p, - route_info); + isis_zebra_route_del_route(area->isis, prefix, src_p, + route_info); hook_call(isis_route_update_hook, area, prefix, route_info); UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); diff --git a/lib/command.h b/lib/command.h index 31e5cad23f..0f9715e81c 100644 --- a/lib/command.h +++ b/lib/command.h @@ -383,6 +383,8 @@ struct cmd_node { #define SHOW_STR "Show running system information\n" #define IP_STR "IP information\n" #define IPV6_STR "IPv6 information\n" +#define IP_ADDR_STR "IPv4 Address\n" +#define IP6_ADDR_STR "IPv6 Address\n" #define SRTE_STR "SR-TE information\n" #define SRTE_COLOR_STR "SR-TE Color information\n" #define NO_STR "Negate a command or set its defaults\n" diff --git a/lib/frrstr.c b/lib/frrstr.c index 1b98b224cc..d66c6f8c16 100644 --- a/lib/frrstr.c +++ b/lib/frrstr.c @@ -23,11 +23,16 @@ #include <string.h> #include <ctype.h> #include <sys/types.h> -#ifdef HAVE_LIBPCREPOSIX +#ifdef HAVE_LIBPCRE2_POSIX +#ifndef _FRR_PCRE2_POSIX +#define _FRR_PCRE2_POSIX +#include <pcre2posix.h> +#endif /* _FRR_PCRE2_POSIX */ +#elif defined(HAVE_LIBPCREPOSIX) #include <pcreposix.h> #else #include <regex.h> -#endif /* HAVE_LIBPCREPOSIX */ +#endif /* HAVE_LIBPCRE2_POSIX */ #include "frrstr.h" #include "memory.h" diff --git a/lib/frrstr.h b/lib/frrstr.h index d52d6a4482..f0066d0fc5 100644 --- a/lib/frrstr.h +++ b/lib/frrstr.h @@ -23,11 +23,16 @@ #include <sys/types.h> #include <sys/types.h> -#ifdef HAVE_LIBPCREPOSIX +#ifdef HAVE_LIBPCRE2_POSIX +#ifndef _FRR_PCRE2_POSIX +#define _FRR_PCRE2_POSIX +#include <pcre2posix.h> +#endif /* _FRR_PCRE2_POSIX */ +#elif defined(HAVE_LIBPCREPOSIX) #include <pcreposix.h> #else #include <regex.h> -#endif /* HAVE_LIBPCREPOSIX */ +#endif /* HAVE_LIBPCRE2_POSIX */ #include <stdbool.h> #include "vector.h" diff --git a/lib/ipaddr.h b/lib/ipaddr.h index d7ab358afe..43b3028200 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -61,6 +61,8 @@ struct ipaddr { #define IPADDRSZ(p) \ (IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr)) +#define IPADDR_STRING_SIZE 46 + static inline int ipaddr_family(const struct ipaddr *ip) { switch (ip->ipa_type) { diff --git a/lib/srv6.c b/lib/srv6.c index 1c2c8913d5..5cd82080f5 100644 --- a/lib/srv6.c +++ b/lib/srv6.c @@ -241,6 +241,10 @@ json_object *srv6_locator_json(const struct srv6_locator *loc) json_object_int_add(jo_root, "argumentBitsLength", loc->argument_bits_length); + /* set true if the locator is a Micro-segment (uSID) locator */ + if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID)) + json_object_string_add(jo_root, "behavior", "usid"); + /* set status_up */ json_object_boolean_add(jo_root, "statusUp", loc->status_up); @@ -286,6 +290,10 @@ json_object *srv6_locator_detailed_json(const struct srv6_locator *loc) json_object_int_add(jo_root, "argumentBitsLength", loc->argument_bits_length); + /* set true if the locator is a Micro-segment (uSID) locator */ + if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID)) + json_object_string_add(jo_root, "behavior", "usid"); + /* set algonum */ json_object_int_add(jo_root, "algoNum", loc->algonum); diff --git a/lib/srv6.h b/lib/srv6.h index 18d5bdebc2..acfb0631cc 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -92,6 +92,9 @@ struct srv6_locator { bool status_up; struct list *chunks; + uint8_t flags; +#define SRV6_LOCATOR_USID (1 << 0) /* The SRv6 Locator is a uSID Locator */ + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(srv6_locator); @@ -116,6 +119,23 @@ struct srv6_locator_chunk { uint8_t proto; uint16_t instance; uint32_t session_id; + + uint8_t flags; +}; + +/* + * SRv6 Endpoint Behavior codepoints, as defined by IANA in + * https://www.iana.org/assignments/segment-routing/segment-routing.xhtml + */ +enum srv6_endpoint_behavior_codepoint { + SRV6_ENDPOINT_BEHAVIOR_RESERVED = 0x0000, + SRV6_ENDPOINT_BEHAVIOR_END_DT6 = 0x0012, + SRV6_ENDPOINT_BEHAVIOR_END_DT4 = 0x0013, + SRV6_ENDPOINT_BEHAVIOR_END_DT46 = 0x0014, + SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID = 0x003E, + SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID = 0x003F, + SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID = 0x0040, + SRV6_ENDPOINT_BEHAVIOR_OPAQUE = 0xFFFF, }; struct nexthop_srv6 { @@ -24,11 +24,16 @@ #include <lib/version.h> #include <sys/types.h> #include <sys/types.h> -#ifdef HAVE_LIBPCREPOSIX +#ifdef HAVE_LIBPCRE2_POSIX +#ifndef _FRR_PCRE2_POSIX +#define _FRR_PCRE2_POSIX +#include <pcre2posix.h> +#endif /* _FRR_PCRE2_POSIX */ +#elif defined(HAVE_LIBPCREPOSIX) #include <pcreposix.h> #else #include <regex.h> -#endif /* HAVE_LIBPCREPOSIX */ +#endif /* HAVE_LIBPCRE2_POSIX */ #include <stdio.h> #include "linklist.h" @@ -22,11 +22,16 @@ #define _ZEBRA_VTY_H #include <sys/types.h> -#ifdef HAVE_LIBPCREPOSIX +#ifdef HAVE_LIBPCRE2_POSIX +#ifndef _FRR_PCRE2_POSIX +#define _FRR_PCRE2_POSIX +#include <pcre2posix.h> +#endif /* _FRR_PCRE2_POSIX */ +#elif defined(HAVE_LIBPCREPOSIX) #include <pcreposix.h> #else #include <regex.h> -#endif /* HAVE_LIBPCREPOSIX */ +#endif /* HAVE_LIBPCRE2_POSIX */ #include "thread.h" #include "log.h" diff --git a/lib/zclient.c b/lib/zclient.c index 2517773dc4..fd6eb7db0d 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1088,6 +1088,7 @@ int zapi_srv6_locator_chunk_encode(struct stream *s, stream_putc(s, c->node_bits_length); stream_putc(s, c->function_bits_length); stream_putc(s, c->argument_bits_length); + stream_putc(s, c->flags); return 0; } @@ -1109,6 +1110,7 @@ int zapi_srv6_locator_chunk_decode(struct stream *s, STREAM_GETC(s, c->node_bits_length); STREAM_GETC(s, c->function_bits_length); STREAM_GETC(s, c->argument_bits_length); + STREAM_GETC(s, c->flags); return 0; stream_failure: diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 4fb3a33568..a67b6c6c19 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2205,7 +2205,7 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, */ if (ospf->router_id.s_addr == INADDR_ANY) { - if (IS_DEBUG_OSPF_EVENT) + if (ei && IS_DEBUG_OSPF_EVENT) zlog_debug( "LSA[Type5:%pI4]: deferring AS-external-LSA origination, router ID is zero", &ei->p.prefix); @@ -2214,7 +2214,7 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, /* Create new AS-external-LSA instance. */ if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) { - if (IS_DEBUG_OSPF_EVENT) + if (ei && IS_DEBUG_OSPF_EVENT) zlog_debug( "LSA[Type5:%pI4]: Could not originate AS-external-LSA", &ei->p.prefix); diff --git a/pathd/path_cli.c b/pathd/path_cli.c index a6540cc84a..b88453c68f 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -126,7 +126,7 @@ DEFPY(show_srte_policy, ttable_rowseps(tt, 0, BOTTOM, true, '-'); RB_FOREACH (policy, srte_policy_head, &srte_policies) { - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; char binding_sid[16] = "-"; ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); @@ -173,7 +173,7 @@ DEFPY(show_srte_policy_detail, vty_out(vty, "\n"); RB_FOREACH (policy, srte_policy_head, &srte_policies) { struct srte_candidate *candidate; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; char binding_sid[16] = "-"; char *segment_list_info; static char undefined_info[] = "(undefined)"; @@ -1091,8 +1091,25 @@ DEFPY_NOSH(show_debugging_pathd, show_debugging_pathd_cmd, "pathd module debugging\n") { + vty_out(vty, "Path debugging status:\n"); + cmd_show_lib_debugs(vty); /* nothing to do here */ + path_ted_show_debugging(vty); + path_policy_show_debugging(vty); + return CMD_SUCCESS; +} + +DEFPY(debug_path_policy, debug_path_policy_cmd, "[no] debug pathd policy", + NO_STR DEBUG_STR + "path debugging\n" + "policy debugging\n") +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + bool no_debug = no; + + DEBUG_MODE_SET(&path_policy_debug, mode, !no); + DEBUG_FLAGS_SET(&path_policy_debug, PATH_POLICY_DEBUG_BASIC, !no_debug); return CMD_SUCCESS; } @@ -1291,8 +1308,34 @@ int config_write_segment_routing(struct vty *vty) return 1; } +static int path_policy_cli_debug_config_write(struct vty *vty) +{ + if (DEBUG_MODE_CHECK(&path_policy_debug, DEBUG_MODE_CONF)) { + if (DEBUG_FLAGS_CHECK(&path_policy_debug, + PATH_POLICY_DEBUG_BASIC)) + vty_out(vty, "debug pathd policy\n"); + return 1; + } + return 0; +} + +static int path_policy_cli_debug_set_all(uint32_t flags, bool set) +{ + DEBUG_FLAGS_SET(&path_policy_debug, flags, set); + + /* If all modes have been turned off, don't preserve options. */ + if (!DEBUG_MODE_CHECK(&path_policy_debug, DEBUG_MODE_ALL)) + DEBUG_CLEAR(&path_policy_debug); + + return 0; +} + void path_cli_init(void) { + hook_register(nb_client_debug_config_write, + path_policy_cli_debug_config_write); + hook_register(nb_client_debug_set_all, path_policy_cli_debug_set_all); + install_node(&segment_routing_node); install_node(&sr_traffic_eng_node); install_node(&srte_segment_list_node); @@ -1308,6 +1351,9 @@ void path_cli_init(void) install_element(ENABLE_NODE, &show_srte_policy_cmd); install_element(ENABLE_NODE, &show_srte_policy_detail_cmd); + install_element(ENABLE_NODE, &debug_path_policy_cmd); + install_element(CONFIG_NODE, &debug_path_policy_cmd); + install_element(CONFIG_NODE, &segment_routing_cmd); install_element(SEGMENT_ROUTING_NODE, &sr_traffic_eng_cmd); install_element(SR_TRAFFIC_ENG_NODE, &srte_segment_list_cmd); diff --git a/pathd/path_ted.c b/pathd/path_ted.c index 68748af53e..5fc8a1f032 100644 --- a/pathd/path_ted.c +++ b/pathd/path_ted.c @@ -488,6 +488,12 @@ int path_ted_cli_debug_config_write(struct vty *vty) return 0; } +void path_ted_show_debugging(struct vty *vty) +{ + if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) + vty_out(vty, " Path TED debugging is on\n"); +} + int path_ted_cli_debug_set_all(uint32_t flags, bool set) { DEBUG_FLAGS_SET(&ted_state_g.dbg, flags, set); diff --git a/pathd/path_ted.h b/pathd/path_ted.h index c6897b1520..5a0c8eecd3 100644 --- a/pathd/path_ted.h +++ b/pathd/path_ted.h @@ -101,6 +101,7 @@ int path_ted_segment_list_refresh(void); /* TED configuration functions */ uint32_t path_ted_config_write(struct vty *vty); +void path_ted_show_debugging(struct vty *vty); /* TED util functions */ /* clang-format off */ diff --git a/pathd/pathd.c b/pathd/pathd.c index e9d7cc6fc7..167c88aeab 100644 --- a/pathd/pathd.c +++ b/pathd/pathd.c @@ -23,6 +23,8 @@ #include "lib_errors.h" #include "network.h" #include "libfrr.h" +#include <debug.h> +#include <hook.h> #include "pathd/pathd.h" #include "pathd/path_zebra.h" @@ -44,6 +46,17 @@ DEFINE_HOOK(pathd_candidate_updated, (struct srte_candidate * candidate), DEFINE_HOOK(pathd_candidate_removed, (struct srte_candidate * candidate), (candidate)); +struct debug path_policy_debug; + +#define PATH_POLICY_DEBUG(fmt, ...) \ + do { \ + if (DEBUG_FLAGS_CHECK(&path_policy_debug, \ + PATH_POLICY_DEBUG_BASIC)) \ + DEBUGD(&path_policy_debug, "policy: " fmt, \ + ##__VA_ARGS__); \ + } while (0) + + static void trigger_pathd_candidate_created(struct srte_candidate *candidate); static void trigger_pathd_candidate_created_timer(struct thread *thread); static void trigger_pathd_candidate_updated(struct srte_candidate *candidate); @@ -97,6 +110,20 @@ RB_GENERATE(srte_policy_head, srte_policy, entry, srte_policy_compare) struct srte_policy_head srte_policies = RB_INITIALIZER(&srte_policies); +static void srte_policy_status_log(struct srte_policy *policy) +{ + char endpoint[ENDPOINT_STR_LENGTH]; + + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + if (policy->status == SRTE_POLICY_STATUS_DOWN) { + PATH_POLICY_DEBUG("SR-TE(%s, %u): policy is DOWN", endpoint, + policy->color); + } else if (policy->status == SRTE_POLICY_STATUS_UP) { + PATH_POLICY_DEBUG("SR-TE(%s, %u): policy is UP", endpoint, + policy->color); + } +} + /** * Adds a segment list to pathd. * @@ -531,6 +558,10 @@ void srte_apply_changes(void) RB_FOREACH_SAFE (policy, srte_policy_head, &srte_policies, safe_pol) { if (CHECK_FLAG(policy->flags, F_POLICY_DELETED)) { + if (policy->status != SRTE_POLICY_STATUS_DOWN) { + policy->status = SRTE_POLICY_STATUS_DOWN; + srte_policy_status_log(policy); + } srte_policy_del(policy); continue; } @@ -565,7 +596,7 @@ void srte_policy_apply_changes(struct srte_policy *policy) struct srte_candidate *candidate, *safe; struct srte_candidate *old_best_candidate; struct srte_candidate *new_best_candidate; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); @@ -574,8 +605,7 @@ void srte_policy_apply_changes(struct srte_policy *policy) new_best_candidate = srte_policy_best_candidate(policy); if (new_best_candidate != old_best_candidate) { - /* TODO: add debug guard. */ - zlog_debug( + PATH_POLICY_DEBUG( "SR-TE(%s, %u): best candidate changed from %s to %s", endpoint, policy->color, old_best_candidate ? old_best_candidate->name : "none", @@ -617,10 +647,10 @@ void srte_policy_apply_changes(struct srte_policy *policy) F_SEGMENT_LIST_MODIFIED); if (candidate_changed || segment_list_changed) { - /* TODO: add debug guard. */ - zlog_debug("SR-TE(%s, %u): best candidate %s changed", - endpoint, policy->color, - new_best_candidate->name); + PATH_POLICY_DEBUG( + "SR-TE(%s, %u): best candidate %s changed", + endpoint, policy->color, + new_best_candidate->name); path_zebra_add_sr_policy( policy, new_best_candidate->lsp->segment_list); @@ -722,10 +752,10 @@ void srte_candidate_set_bandwidth(struct srte_candidate *candidate, float bandwidth, bool required) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug( + PATH_POLICY_DEBUG( "SR-TE(%s, %u): candidate %s %sconfig bandwidth set to %f B/s", endpoint, policy->color, candidate->name, required ? "required " : "", bandwidth); @@ -750,11 +780,13 @@ void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth, { struct srte_candidate *candidate = lsp->candidate; struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug("SR-TE(%s, %u): candidate %s %slsp bandwidth set to %f B/s", - endpoint, policy->color, candidate->name, - required ? "required" : "", bandwidth); + PATH_POLICY_DEBUG( + "SR-TE(%s, %u): candidate %s %slsp bandwidth set to %f B/s", + endpoint, policy->color, candidate->name, + required ? "required" : "", bandwidth); SET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH); COND_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required); lsp->bandwidth = bandwidth; @@ -770,10 +802,11 @@ void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth, void srte_candidate_unset_bandwidth(struct srte_candidate *candidate) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug("SR-TE(%s, %u): candidate %s config bandwidth unset", - endpoint, policy->color, candidate->name); + PATH_POLICY_DEBUG("SR-TE(%s, %u): candidate %s config bandwidth unset", + endpoint, policy->color, candidate->name); UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH); UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH); candidate->bandwidth = 0; @@ -792,10 +825,11 @@ void srte_lsp_unset_bandwidth(struct srte_lsp *lsp) { struct srte_candidate *candidate = lsp->candidate; struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug("SR-TE(%s, %u): candidate %s lsp bandwidth unset", endpoint, - policy->color, candidate->name); + PATH_POLICY_DEBUG("SR-TE(%s, %u): candidate %s lsp bandwidth unset", + endpoint, policy->color, candidate->name); UNSET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH); UNSET_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH); SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); @@ -820,9 +854,10 @@ void srte_candidate_set_metric(struct srte_candidate *candidate, bool is_computed) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug( + PATH_POLICY_DEBUG( "SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f (is-bound: %s; is_computed: %s)", endpoint, policy->color, candidate->name, required ? "required " : "", srte_candidate_metric_name(type), @@ -854,9 +889,10 @@ void srte_lsp_set_metric(struct srte_lsp *lsp, { struct srte_candidate *candidate = lsp->candidate; struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug( + PATH_POLICY_DEBUG( "SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f (is-bound: %s; is_computed: %s)", endpoint, policy->color, candidate->name, required ? "required " : "", srte_candidate_metric_name(type), @@ -889,11 +925,13 @@ void srte_candidate_unset_metric(struct srte_candidate *candidate, enum srte_candidate_metric_type type) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug("SR-TE(%s, %u): candidate %s config metric %s (%u) unset", - endpoint, policy->color, candidate->name, - srte_candidate_metric_name(type), type); + PATH_POLICY_DEBUG( + "SR-TE(%s, %u): candidate %s config metric %s (%u) unset", + endpoint, policy->color, candidate->name, + srte_candidate_metric_name(type), type); assert((type > 0) && (type <= MAX_METRIC_TYPE)); srte_unset_metric(&candidate->metrics[type - 1]); srte_lsp_unset_metric(candidate->lsp, type); @@ -913,11 +951,13 @@ void srte_lsp_unset_metric(struct srte_lsp *lsp, { struct srte_candidate *candidate = lsp->candidate; struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug("SR-TE(%s, %u): candidate %s lsp metric %s (%u) unset", - endpoint, policy->color, candidate->name, - srte_candidate_metric_name(type), type); + PATH_POLICY_DEBUG( + "SR-TE(%s, %u): candidate %s lsp metric %s (%u) unset", + endpoint, policy->color, candidate->name, + srte_candidate_metric_name(type), type); assert((type > 0) && (type <= MAX_METRIC_TYPE)); srte_unset_metric(&lsp->metrics[type - 1]); } @@ -941,16 +981,18 @@ void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required, enum objfun_type type) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); candidate->objfun = type; SET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN); COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN, required); SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); - zlog_debug("SR-TE(%s, %u): candidate %s %sobjective function set to %s", - endpoint, policy->color, candidate->name, - required ? "required " : "", objfun_type_name(type)); + PATH_POLICY_DEBUG( + "SR-TE(%s, %u): candidate %s %sobjective function set to %s", + endpoint, policy->color, candidate->name, + required ? "required " : "", objfun_type_name(type)); } /** @@ -961,14 +1003,15 @@ void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required, void srte_candidate_unset_objfun(struct srte_candidate *candidate) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN); UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN); SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); candidate->objfun = OBJFUN_UNDEFINED; - zlog_debug( + PATH_POLICY_DEBUG( "SR-TE(%s, %u): candidate %s objective functions preferences unset", endpoint, policy->color, candidate->name); } @@ -1013,7 +1056,8 @@ void srte_candidate_set_affinity_filter(struct srte_candidate *candidate, uint32_t filter) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); assert(type > AFFINITY_FILTER_UNDEFINED); @@ -1021,7 +1065,7 @@ void srte_candidate_set_affinity_filter(struct srte_candidate *candidate, SET_FLAG(candidate->flags, filter_type_to_flag(type)); SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); candidate->affinity_filters[type - 1] = filter; - zlog_debug( + PATH_POLICY_DEBUG( "SR-TE(%s, %u): candidate %s affinity filter %s set to 0x%08x", endpoint, policy->color, candidate->name, filter_type_name(type), filter); @@ -1038,7 +1082,8 @@ void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate, enum affinity_filter_type type) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); assert(type > AFFINITY_FILTER_UNDEFINED); @@ -1046,9 +1091,10 @@ void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate, UNSET_FLAG(candidate->flags, filter_type_to_flag(type)); SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); candidate->affinity_filters[type - 1] = 0; - zlog_debug("SR-TE(%s, %u): candidate %s affinity filter %s unset", - endpoint, policy->color, candidate->name, - filter_type_name(type)); + PATH_POLICY_DEBUG( + "SR-TE(%s, %u): candidate %s affinity filter %s unset", + endpoint, policy->color, candidate->name, + filter_type_name(type)); } /** @@ -1093,10 +1139,11 @@ srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index) void srte_candidate_status_update(struct srte_candidate *candidate, int status) { struct srte_policy *policy = candidate->policy; - char endpoint[46]; + char endpoint[ENDPOINT_STR_LENGTH]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); - zlog_debug("SR-TE(%s, %u): zebra updated status to %d", endpoint, - policy->color, status); + PATH_POLICY_DEBUG("SR-TE(%s, %u): zebra updated status to %d", endpoint, + policy->color, status); switch (status) { case ZEBRA_SR_POLICY_DOWN: switch (policy->status) { @@ -1109,9 +1156,8 @@ void srte_candidate_status_update(struct srte_candidate *candidate, int status) case SRTE_POLICY_STATUS_DOWN: return; default: - zlog_debug("SR-TE(%s, %u): policy is DOWN", endpoint, - policy->color); policy->status = SRTE_POLICY_STATUS_DOWN; + srte_policy_status_log(policy); break; } break; @@ -1120,9 +1166,8 @@ void srte_candidate_status_update(struct srte_candidate *candidate, int status) case SRTE_POLICY_STATUS_UP: return; default: - zlog_debug("SR-TE(%s, %u): policy is UP", endpoint, - policy->color); policy->status = SRTE_POLICY_STATUS_UP; + srte_policy_status_log(policy); break; } break; @@ -1148,19 +1193,20 @@ void srte_candidate_unset_segment_list(const char *originator, bool force) return; } - zlog_debug("Unset segment lists for originator %s", originator); + PATH_POLICY_DEBUG("Unset segment lists for originator %s", originator); /* Iterate the policies, then iterate each policy's candidate path * to check the candidate path's segment list originator */ struct srte_policy *policy; RB_FOREACH (policy, srte_policy_head, &srte_policies) { - zlog_debug("Unset segment lists checking policy %s", - policy->name); + PATH_POLICY_DEBUG("Unset segment lists checking policy %s", + policy->name); struct srte_candidate *candidate; RB_FOREACH (candidate, srte_candidate_head, &policy->candidate_paths) { - zlog_debug("Unset segment lists checking candidate %s", - candidate->name); + PATH_POLICY_DEBUG( + "Unset segment lists checking candidate %s", + candidate->name); if (candidate->lsp == NULL) { continue; } @@ -1190,8 +1236,8 @@ void srte_candidate_unset_segment_list(const char *originator, bool force) sizeof(segment_list->originator)) == 0 || force) { - zlog_debug("Unset segment list %s", - segment_list->name); + PATH_POLICY_DEBUG("Unset segment list %s", + segment_list->name); SET_FLAG(segment_list->flags, F_SEGMENT_LIST_DELETED); SET_FLAG(candidate->flags, @@ -1222,6 +1268,12 @@ const char *srte_origin2str(enum srte_protocol_origin origin) } } +void path_policy_show_debugging(struct vty *vty) +{ + if (DEBUG_FLAGS_CHECK(&path_policy_debug, PATH_POLICY_DEBUG_BASIC)) + vty_out(vty, " Path policy debugging is on\n"); +} + void pathd_shutdown(void) { path_ted_teardown(); @@ -1347,8 +1399,9 @@ int32_t srte_ted_do_query_type_c(struct srte_segment_entry *entry, zlog_warn(" %s: PATHD-TED: SL: ERROR query C : ted-sid (%d)", __func__, ted_sid); } else { - zlog_debug("%s: PATHD-TED: SL: Success query C : ted-sid (%d)", - __func__, ted_sid); + PATH_TED_DEBUG( + "%s: PATHD-TED: SL: Success query C : ted-sid (%d)", + __func__, ted_sid); } if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, entry->sid_value)) { @@ -1377,8 +1430,9 @@ int32_t srte_ted_do_query_type_e(struct srte_segment_entry *entry, zlog_warn(" %s: PATHD-TED: SL: ERROR query E : ted-sid (%d)", __func__, ted_sid); } else { - zlog_debug("%s: PATHD-TED: SL: Success query E : ted-sid (%d)", - __func__, ted_sid); + PATH_TED_DEBUG( + "%s: PATHD-TED: SL: Success query E : ted-sid (%d)", + __func__, ted_sid); } if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, entry->sid_value)) { @@ -1406,8 +1460,8 @@ int32_t srte_ted_do_query_type_f(struct srte_segment_entry *entry, zlog_warn("%s:SL: ERROR query F : ted-sid (%d)", __func__, ted_sid); } else { - zlog_debug("%s:SL: Success query F : ted-sid (%d)", __func__, - ted_sid); + PATH_TED_DEBUG("%s:SL: Success query F : ted-sid (%d)", + __func__, ted_sid); } if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, entry->sid_value)) { diff --git a/pathd/pathd.h b/pathd/pathd.h index 81d7aa9105..bb2e63c04b 100644 --- a/pathd/pathd.h +++ b/pathd/pathd.h @@ -43,6 +43,10 @@ enum srte_protocol_origin { SRTE_ORIGIN_LOCAL = 3, }; +extern struct debug path_policy_debug; + +#define PATH_POLICY_DEBUG_BASIC 0x01 + enum srte_policy_status { SRTE_POLICY_STATUS_UNKNOWN = 0, SRTE_POLICY_STATUS_DOWN = 1, @@ -326,6 +330,8 @@ struct srte_candidate { RB_HEAD(srte_candidate_head, srte_candidate); RB_PROTOTYPE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare) +#define ENDPOINT_STR_LENGTH IPADDR_STRING_SIZE + struct srte_policy { RB_ENTRY(srte_policy) entry; @@ -444,6 +450,7 @@ void srte_candidate_status_update(struct srte_candidate *candidate, int status); void srte_candidate_unset_segment_list(const char *originator, bool force); const char *srte_origin2str(enum srte_protocol_origin origin); void pathd_shutdown(void); +void path_policy_show_debugging(struct vty *vty); /* path_cli.c */ void path_cli_init(void); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 6f272f0085..db9156b04b 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -689,7 +689,7 @@ static void pim_if_addr_del_pim(struct connected *ifc) { struct pim_interface *pim_ifp = ifc->ifp->info; - if (ifc->address->family != AF_INET) { + if (ifc->address->family != PIM_AF) { /* non-IPv4 address */ return; } @@ -843,7 +843,7 @@ void pim_if_addr_del_all(struct interface *ifp) for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { struct prefix *p = ifc->address; - if (p->family != AF_INET) + if (p->family != PIM_AF) continue; pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */); diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 1a90b46dec..259c34c819 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -365,19 +365,9 @@ static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr, if (ip_hdr->ip_ttl-- <= 1) return -1; - ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4); - - fd = pim_socket_raw(IPPROTO_RAW); - - if (fd < 0) - return -1; - - pim_socket_ip_hdr(fd); - if (interface == NULL) { memset(&nexthop, 0, sizeof(nexthop)); if (!pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0)) { - close(fd); if (PIM_DEBUG_MTRACE) zlog_debug( "Dropping mtrace packet, no route to destination"); @@ -389,6 +379,15 @@ static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr, if_out = interface; } + ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4); + + fd = pim_socket_raw(IPPROTO_RAW); + + if (fd < 0) + return -1; + + pim_socket_ip_hdr(fd); + ret = pim_socket_bind(fd, if_out); if (ret < 0) { diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 12f8ffedfe..c4ff912cde 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -24,6 +24,7 @@ #include "lib/northbound_cli.h" #include "pim_igmpv3.h" #include "pim_neighbor.h" +#include "pim_nht.h" #include "pim_pim.h" #include "pim_mlag.h" #include "pim_bfd.h" @@ -146,6 +147,7 @@ static int pim_cmd_interface_add(struct interface *ifp) pim_ifp->pim_enable = true; pim_if_addr_add_all(ifp); + pim_upstream_nh_if_update(pim_ifp->pim, ifp); pim_if_membership_refresh(ifp); pim_if_create_pimreg(pim_ifp->pim); @@ -171,6 +173,7 @@ static int pim_cmd_interface_delete(struct interface *ifp) if (!pim_ifp->gm_enable) { pim_if_addr_del_all(ifp); + pim_upstream_nh_if_update(pim_ifp->pim, ifp); pim_if_delete(ifp); } diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index f9a9aeb1b0..a33da64568 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -469,6 +469,40 @@ static int pim_update_upstream_nh(struct pim_instance *pim, return 0; } +static int pim_upstream_nh_if_update_helper(struct hash_bucket *bucket, + void *arg) +{ + struct pim_nexthop_cache *pnc = bucket->data; + struct pnc_hash_walk_data *pwd = arg; + struct pim_instance *pim = pwd->pim; + struct interface *ifp = pwd->ifp; + struct nexthop *nh_node = NULL; + ifindex_t first_ifindex; + + for (nh_node = pnc->nexthop; nh_node; nh_node = nh_node->next) { + first_ifindex = nh_node->ifindex; + if (ifp != if_lookup_by_index(first_ifindex, pim->vrf->vrf_id)) + continue; + + if (pnc->upstream_hash->count) { + pim_update_upstream_nh(pim, pnc); + break; + } + } + + return HASHWALK_CONTINUE; +} + +void pim_upstream_nh_if_update(struct pim_instance *pim, struct interface *ifp) +{ + struct pnc_hash_walk_data pwd; + + pwd.pim = pim; + pwd.ifp = ifp; + + hash_walk(pim->rpf_hash, pim_upstream_nh_if_update_helper, &pwd); +} + uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp) { uint32_t hash_val; @@ -495,6 +529,7 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, uint32_t hash_val = 0, mod_val = 0; uint8_t nh_iter = 0, found = 0; uint32_t i, num_nbrs = 0; + struct pim_interface *pim_ifp; if (!pnc || !pnc->nexthop_num || !nexthop) return 0; @@ -611,10 +646,13 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, nh_iter++; continue; } - if (!ifp->info) { + + pim_ifp = ifp->info; + + if (!pim_ifp || !pim_ifp->pim_enable) { if (PIM_DEBUG_PIM_NHT) zlog_debug( - "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)", + "%s: pim not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)", __func__, ifp->name, pim->vrf->name, first_ifindex, &src); if (nh_iter == mod_val) @@ -882,6 +920,7 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, uint8_t i = 0; uint32_t hash_val = 0, mod_val = 0; uint32_t num_nbrs = 0; + struct pim_interface *pim_ifp; if (PIM_DEBUG_PIM_NHT_DETAIL) zlog_debug("%s: Looking up: %pPA(%s), last lookup time: %lld", @@ -964,10 +1003,12 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, continue; } - if (!ifp->info) { + pim_ifp = ifp->info; + + if (!pim_ifp || !pim_ifp->pim_enable) { if (PIM_DEBUG_PIM_NHT) zlog_debug( - "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)", + "%s: pim not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)", __func__, ifp->name, pim->vrf->name, first_ifindex, &src); if (i == mod_val) diff --git a/pimd/pim_nht.h b/pimd/pim_nht.h index 240e61d98f..f487a21ba4 100644 --- a/pimd/pim_nht.h +++ b/pimd/pim_nht.h @@ -53,6 +53,11 @@ struct pim_nexthop_cache { uint32_t bsr_count; }; +struct pnc_hash_walk_data { + struct pim_instance *pim; + struct interface *ifp; +}; + int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS); int pim_find_or_track_nexthop(struct pim_instance *pim, pim_addr addr, struct pim_upstream *up, struct rp_info *rp, @@ -77,5 +82,5 @@ void pim_nht_bsr_del(struct pim_instance *pim, pim_addr bsr_addr); /* RPF(bsr_addr) == src_ip%src_ifp? */ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, struct interface *src_ifp, pim_addr src_ip); - +void pim_upstream_nh_if_update(struct pim_instance *pim, struct interface *ifp); #endif diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index f5834029a5..d237a73126 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -61,6 +61,7 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, ifindex_t first_ifindex = 0; int found = 0; int i = 0; + struct pim_interface *pim_ifp; #if PIM_IPV == 4 /* @@ -118,15 +119,16 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, continue; } - if (!ifp->info) { + pim_ifp = ifp->info; + if (!pim_ifp || !pim_ifp->pim_enable) { if (PIM_DEBUG_ZEBRA) zlog_debug( - "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)", + "%s: pim not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)", __func__, ifp->name, first_ifindex, &addr); i++; - } else if (neighbor_needed - && !pim_if_connected_to_source(ifp, addr)) { + } else if (neighbor_needed && + !pim_if_connected_to_source(ifp, addr)) { nbr = pim_neighbor_find(ifp, nexthop_tab[i].nexthop_addr); if (PIM_DEBUG_PIM_TRACE_DETAIL) diff --git a/redhat/frr.pam b/redhat/frr.pam index 5cef5d9d74..17a62f1999 100644 --- a/redhat/frr.pam +++ b/redhat/frr.pam @@ -5,6 +5,7 @@ # Only allow root (and possibly wheel) to use this because enable access # is unrestricted. auth sufficient pam_rootok.so +account sufficient pam_rootok.so # Uncomment the following line to implicitly trust users in the "wheel" group. #auth sufficient pam_wheel.so trust use_uid diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 8f469d2a07..afe75e1f26 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -469,7 +469,7 @@ ln -s %{_sbindir}/frrinit.sh %{buildroot}%{_initddir}/frr install %{zeb_src}/tools/etc/frr/daemons %{buildroot}%{_sysconfdir}/frr install %{zeb_src}/tools/etc/frr/frr.conf %{buildroot}%{_sysconfdir}/frr/frr.conf.template install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr -install -m644 %{zeb_rh_src}/frr.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/frr +install -m644 %{zeb_src}/tools/etc/logrotate.d/frr %{buildroot}%{_sysconfdir}/logrotate.d/frr install -d -m750 %{buildroot}%{rundir} %if 0%{?rhel} > 7 || 0%{?fedora} > 29 diff --git a/tests/topotests/srv6_locator_usid/__init__.py b/tests/topotests/srv6_locator_usid/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/__init__.py diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_1.json b/tests/topotests/srv6_locator_usid/expected_chunks_1.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_1.json @@ -0,0 +1 @@ +[] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_2.json b/tests/topotests/srv6_locator_usid/expected_chunks_2.json new file mode 100644 index 0000000000..304d73807c --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_2.json @@ -0,0 +1,8 @@ +[ + { + "name": "loc1", + "chunks": [ + "fc00:0:1::/48" + ] + } +] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_3.json b/tests/topotests/srv6_locator_usid/expected_chunks_3.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_3.json @@ -0,0 +1 @@ +[] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_4.json b/tests/topotests/srv6_locator_usid/expected_chunks_4.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_4.json @@ -0,0 +1 @@ +[] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_5.json b/tests/topotests/srv6_locator_usid/expected_chunks_5.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_5.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_6.json b/tests/topotests/srv6_locator_usid/expected_chunks_6.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_6.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_7.json b/tests/topotests/srv6_locator_usid/expected_chunks_7.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_7.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_8.json b/tests/topotests/srv6_locator_usid/expected_chunks_8.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_8.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator_usid/expected_locators_1.json b/tests/topotests/srv6_locator_usid/expected_locators_1.json new file mode 100644 index 0000000000..c0eeacc09a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_1.json @@ -0,0 +1,20 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_2.json b/tests/topotests/srv6_locator_usid/expected_locators_2.json new file mode 100644 index 0000000000..38a6739d64 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_2.json @@ -0,0 +1,20 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "sharp" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_3.json b/tests/topotests/srv6_locator_usid/expected_locators_3.json new file mode 100644 index 0000000000..c0eeacc09a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_3.json @@ -0,0 +1,20 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_4.json b/tests/topotests/srv6_locator_usid/expected_locators_4.json new file mode 100644 index 0000000000..b1528ff111 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_4.json @@ -0,0 +1,35 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + }, + { + "name": "loc2", + "prefix": "fc00:0:2::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:2::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_5.json b/tests/topotests/srv6_locator_usid/expected_locators_5.json new file mode 100644 index 0000000000..b6acc238a7 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_5.json @@ -0,0 +1,36 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + }, + { + "name": "loc2", + "prefix": "fc00:0:2::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:2::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_6.json b/tests/topotests/srv6_locator_usid/expected_locators_6.json new file mode 100644 index 0000000000..b1528ff111 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_6.json @@ -0,0 +1,35 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + }, + { + "name": "loc2", + "prefix": "fc00:0:2::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:2::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_7.json b/tests/topotests/srv6_locator_usid/expected_locators_7.json new file mode 100644 index 0000000000..e965e02170 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_7.json @@ -0,0 +1,19 @@ +{ + "locators":[ + { + "name": "loc2", + "prefix": "fc00:0:2::/48", + "statusUp": true, + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "chunks":[ + { + "prefix": "fc00:0:2::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_8.json b/tests/topotests/srv6_locator_usid/expected_locators_8.json new file mode 100644 index 0000000000..6e1b993ca8 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_8.json @@ -0,0 +1,4 @@ +{ + "locators":[ + ] +} diff --git a/tests/topotests/srv6_locator_usid/r1/setup.sh b/tests/topotests/srv6_locator_usid/r1/setup.sh new file mode 100644 index 0000000000..36ed713f24 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/r1/setup.sh @@ -0,0 +1,2 @@ +ip link add dummy0 type dummy +ip link set dummy0 up diff --git a/tests/topotests/srv6_locator_usid/r1/sharpd.conf b/tests/topotests/srv6_locator_usid/r1/sharpd.conf new file mode 100644 index 0000000000..d46085935c --- /dev/null +++ b/tests/topotests/srv6_locator_usid/r1/sharpd.conf @@ -0,0 +1,7 @@ +hostname r1 +! +log stdout notifications +log monitor notifications +log commands +log file sharpd.log debugging +! diff --git a/tests/topotests/srv6_locator_usid/r1/zebra.conf b/tests/topotests/srv6_locator_usid/r1/zebra.conf new file mode 100644 index 0000000000..78ef1e9d40 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/r1/zebra.conf @@ -0,0 +1,20 @@ +hostname r1 +! +! debug zebra events +! debug zebra rib detailed +! +log stdout notifications +log monitor notifications +log commands +log file zebra.log debugging +! +segment-routing + srv6 + locators + locator loc1 + prefix fc00:0:1::/48 func-bits 16 block-len 32 node-len 16 + behavior usid + ! + ! + ! +! diff --git a/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py b/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py new file mode 100755 index 0000000000..37fd736d2b --- /dev/null +++ b/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python + +# Copyright (c) 2022, University of Rome Tor Vergata +# Authored by Carmine Scarpitta <carmine.scarpitta@uniroma2.it> +# +# 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_srv6_locator_usid.py: +Test for SRv6 Locator uSID on zebra +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd] + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join( + CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_SHARP, os.path.join( + CWD, "{}/sharpd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def _check_srv6_locator(router, expected_locator_file): + logger.info("checking zebra locator status") + output = json.loads( + router.vtysh_cmd("show segment-routing srv6 locator json") + ) + expected = open_json_file("{}/{}".format(CWD, expected_locator_file)) + return topotest.json_cmp(output, expected) + + +def _check_sharpd_chunk(router, expected_chunk_file): + logger.info("checking sharpd locator chunk status") + output = json.loads( + router.vtysh_cmd("show sharp segment-routing srv6 json") + ) + expected = open_json_file("{}/{}".format(CWD, expected_chunk_file)) + return topotest.json_cmp(output, expected) + + +def check_srv6_locator(router, expected_file): + func = functools.partial(_check_srv6_locator, router, expected_file) + success, result = topotest.run_and_expect(func, None, count=5, wait=3) + assert result is None, "Failed" + + +def check_sharpd_chunk(router, expected_file): + func = functools.partial(_check_sharpd_chunk, router, expected_file) + success, result = topotest.run_and_expect(func, None, count=5, wait=3) + assert result is None, "Failed" + + +def test_srv6_usid_locator_configuration(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Verify SRv6 Locators instantiated from config file") + check_srv6_locator(router, "expected_locators_1.json") + check_sharpd_chunk(router, "expected_chunks_1.json") + + +def test_srv6_usid_locator_get_chunk(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Get chunk for the locator loc1") + router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc1") + check_srv6_locator(router, "expected_locators_2.json") + check_sharpd_chunk(router, "expected_chunks_2.json") + + +def test_srv6_usid_locator_release_chunk(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Release chunk for the locator loc1") + router.vtysh_cmd("sharp srv6-manager release-locator-chunk loc1") + check_srv6_locator(router, "expected_locators_3.json") + check_sharpd_chunk(router, "expected_chunks_3.json") + + +def test_srv6_usid_locator_create_locator(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Create an additional SRv6 Locator") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc2 + prefix fc00:0:2::/48 func-bits 16 block-len 32 node-len 16 + """ + ) + check_srv6_locator(router, "expected_locators_4.json") + check_sharpd_chunk(router, "expected_chunks_4.json") + + +def test_srv6_usid_locator_set_behavior_usid(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info( + "Specify the SRv6 Locator loc2 as a Micro-segment (uSID) Locator" + ) + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc2 + behavior usid + """ + ) + check_srv6_locator(router, "expected_locators_5.json") + check_sharpd_chunk(router, "expected_chunks_5.json") + + +def test_srv6_usid_locator_unset_behavior_usid(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Clear Micro-segment (uSID) Locator flag for loc2") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc2 + no behavior usid + """ + ) + check_srv6_locator(router, "expected_locators_6.json") + check_sharpd_chunk(router, "expected_chunks_6.json") + + +def test_srv6_usid_locator_delete(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info( + "Delete locator loc1 and verify that the chunk is released automatically" + ) + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) + check_srv6_locator(router, "expected_locators_7.json") + check_sharpd_chunk(router, "expected_chunks_7.json") + + +def test_srv6_usid_locator_delete_all(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Delete all the SRv6 configuration") + router.vtysh_cmd( + """ + configure terminal + segment-routing + no srv6 + """ + ) + check_srv6_locator(router, "expected_locators_8.json") + check_sharpd_chunk(router, "expected_chunks_8.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/debian/frr.logrotate b/tools/etc/logrotate.d/frr index 735af6539b..735af6539b 100644 --- a/debian/frr.logrotate +++ b/tools/etc/logrotate.d/frr diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 61f1abb378..3c16c27c6d 100755 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -335,7 +335,7 @@ if [ -z "$FRR_PATHSPACE" ]; then load_old_config "/etc/sysconfig/frr" fi -if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a'; then +if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare -a'; then log_warning_msg "watchfrr_options contains a bash array value." \ "The configured value is intentionally ignored since it is likely wrong." \ "Please remove or fix the setting." diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index d07c4c6332..c5e1c113cb 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -98,6 +98,7 @@ struct fpm_nl_ctx { struct thread *t_read; struct thread *t_write; struct thread *t_event; + struct thread *t_nhg; struct thread *t_dequeue; /* zebra events. */ @@ -271,7 +272,7 @@ DEFUN(fpm_use_nhg, fpm_use_nhg_cmd, return CMD_SUCCESS; thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, - FNE_TOGGLE_NHG, &gfnc->t_event); + FNE_TOGGLE_NHG, &gfnc->t_nhg); return CMD_SUCCESS; } @@ -287,7 +288,7 @@ DEFUN(no_fpm_use_nhg, no_fpm_use_nhg_cmd, return CMD_SUCCESS; thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, - FNE_TOGGLE_NHG, &gfnc->t_event); + FNE_TOGGLE_NHG, &gfnc->t_nhg); return CMD_SUCCESS; } @@ -1275,7 +1276,7 @@ static void fpm_process_queue(struct thread *t) static void fpm_process_event(struct thread *t) { struct fpm_nl_ctx *fnc = THREAD_ARG(t); - int event = THREAD_VAL(t); + enum fpm_nl_events event = THREAD_VAL(t); switch (event) { case FNE_DISABLE: @@ -1328,11 +1329,6 @@ static void fpm_process_event(struct thread *t) if (IS_ZEBRA_DEBUG_FPM) zlog_debug("%s: LSP walk finished", __func__); break; - - default: - if (IS_ZEBRA_DEBUG_FPM) - zlog_debug("%s: unhandled event %d", __func__, event); - break; } } @@ -1372,6 +1368,8 @@ static int fpm_nl_finish_early(struct fpm_nl_ctx *fnc) THREAD_OFF(fnc->t_ribwalk); THREAD_OFF(fnc->t_rmacreset); THREAD_OFF(fnc->t_rmacwalk); + THREAD_OFF(fnc->t_event); + THREAD_OFF(fnc->t_nhg); thread_cancel_async(fnc->fthread->master, &fnc->t_read, NULL); thread_cancel_async(fnc->fthread->master, &fnc->t_write, NULL); thread_cancel_async(fnc->fthread->master, &fnc->t_connect, NULL); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index a3db53f296..130fb972db 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2606,8 +2606,10 @@ static void zread_sr_policy_set(ZAPI_HANDLER_ARGS) return; policy = zebra_sr_policy_find(zp.color, &zp.endpoint); - if (!policy) + if (!policy) { policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name); + policy->sock = client->sock; + } /* TODO: per-VRF list of SR-TE policies. */ policy->zvrf = zvrf; @@ -2710,6 +2712,7 @@ int zsend_srv6_manager_get_locator_chunk_response(struct zserv *client, chunk.keep = 0; chunk.proto = client->proto; chunk.instance = client->instance; + chunk.flags = loc->flags; zclient_create_header(s, ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK, vrf_id); zapi_srv6_locator_chunk_encode(s, &chunk); diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 168f0b2ce6..f207477445 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -1327,11 +1327,11 @@ static void zebra_evpn_process_sync_macip_add(struct zebra_evpn *zevpn, uint8_t flags, uint32_t seq, const esi_t *esi) { - struct sync_mac_ip_ctx ctx; char ipbuf[INET6_ADDRSTRLEN]; bool sticky; bool remote_gw; struct zebra_neigh *n = NULL; + struct zebra_mac *mac = NULL; sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); @@ -1352,22 +1352,30 @@ static void zebra_evpn_process_sync_macip_add(struct zebra_evpn *zevpn, return; } - if (ipa_len) { + if (!ipa_len) { + /* MAC update */ + (void)zebra_evpn_proc_sync_mac_update(zevpn, macaddr, ipa_len, + ipaddr, flags, seq, esi); + } else { + /* MAC-IP update */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + mac = zebra_evpn_proc_sync_mac_update(zevpn, macaddr, + ipa_len, ipaddr, + flags, seq, esi); + } + if (!mac) + return; + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); if (n && !zebra_evpn_neigh_is_bgp_seq_ok(zevpn, n, macaddr, seq, true)) return; - } - - memset(&ctx, 0, sizeof(ctx)); - ctx.mac = zebra_evpn_proc_sync_mac_update( - zevpn, macaddr, ipa_len, ipaddr, flags, seq, esi, &ctx); - if (ctx.ignore_macip || !ctx.mac || !ipa_len) - return; - zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr, flags, seq, - esi, &ctx); + zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr, + flags, seq, esi, mac); + } } /************************** remote mac-ip handling **************************/ @@ -1452,14 +1460,30 @@ void zebra_evpn_rem_macip_add(vni_t vni, const struct ethaddr *macaddr, } zvrf = zebra_vrf_get_evpn(); - if (zebra_evpn_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len, - ipaddr, &mac, vtep_ip, flags, seq, - esi) - != 0) + if (!zvrf) return; - zebra_evpn_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip, - flags, seq); + if (!ipa_len) { + /* MAC update */ + zebra_evpn_mac_remote_macip_add(zevpn, zvrf, macaddr, vtep_ip, + flags, seq, esi); + } else { + /* MAC-IP update + * Add auto MAC if it doesn't exist. + */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + mac = zebra_evpn_mac_add_auto(zevpn, macaddr); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Neigh %pIA: MAC %pEA not found, Auto MAC created", + ipaddr, macaddr); + } + + zebra_evpn_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, + vtep_ip, flags, seq); + } } /* Process a remote MACIP delete from BGP. */ @@ -1504,7 +1528,7 @@ void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr, if (n && !mac) { zlog_warn( - "Failed to locate MAC %pEA for neigh %pIA VNI %u upon remote MACIP DEL", + "Failed to locate MAC %pEA for Neigh %pIA VNI %u upon remote MACIP DEL", macaddr, ipaddr, vni); return; } @@ -1512,8 +1536,13 @@ void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr, /* If the remote mac or neighbor doesn't exist there is nothing * more to do. Otherwise, uninstall the entry and then remove it. */ - if (!mac && !n) + if (!mac && !n) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Failed to locate MAC %pEA & Neigh %pIA VNI %u upon remote MACIP DEL", + macaddr, ipaddr, vni); return; + } zvrf = zevpn->vxlan_if->vrf->info; diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index cbdc17653b..218184ad0d 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -1179,6 +1179,25 @@ int zebra_evpn_mac_del(struct zebra_evpn *zevpn, struct zebra_mac *mac) return 0; } +/* + * Add Auto MAC entry. + */ +struct zebra_mac *zebra_evpn_mac_add_auto(struct zebra_evpn *zevpn, + const struct ethaddr *macaddr) +{ + struct zebra_mac *mac; + + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) + return NULL; + + zebra_evpn_mac_clear_fwd_info(mac); + memset(&mac->flags, 0, sizeof(uint32_t)); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + + return mac; +} + static bool zebra_evpn_check_mac_del_from_db(struct mac_walk_ctx *wctx, struct zebra_mac *mac) { @@ -1592,43 +1611,44 @@ void zebra_evpn_sync_mac_del(struct zebra_mac *mac) static inline bool zebra_evpn_mac_is_bgp_seq_ok(struct zebra_evpn *zevpn, struct zebra_mac *mac, - uint32_t seq, uint16_t ipa_len, - const struct ipaddr *ipaddr, - bool sync) + uint32_t seq, bool sync) { - char ipbuf[INET6_ADDRSTRLEN]; + char mac_buf[MAC_BUF_SIZE]; uint32_t tmp_seq; const char *n_type; + bool is_local = false; if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { tmp_seq = mac->loc_seq; n_type = "local"; + is_local = true; } else { tmp_seq = mac->rem_seq; n_type = "remote"; } if (seq < tmp_seq) { + + if (is_local && !zebra_evpn_mac_is_ready_for_bgp(mac->flags)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC || IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s-macip not ready vni %u %s-mac %pEA lower seq %u f 0x%x", + sync ? "sync" : "rem", zevpn->vni, + n_type, &mac->macaddr, tmp_seq, + mac->flags); + return true; + } + /* if the mac was never advertised to bgp we must accept * whatever sequence number bgp sends - * XXX - check with Vivek */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) - && !zebra_evpn_mac_is_ready_for_bgp(mac->flags)) { - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC - || IS_ZEBRA_DEBUG_VXLAN) { - char mac_buf[MAC_BUF_SIZE]; - + if (!is_local && zebra_vxlan_get_accept_bgp_seq()) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC || + IS_ZEBRA_DEBUG_VXLAN) { zlog_debug( - "%s-macip accept vni %u %s-mac %pEA%s%s lower seq %u f %s", + "%s-macip accept vni %u %s-mac %pEA lower seq %u f %s", sync ? "sync" : "rem", zevpn->vni, - n_type, - &mac->macaddr, - ipa_len ? " IP " : "", - ipa_len ? ipaddr2str(ipaddr, ipbuf, - sizeof(ipbuf)) - : "", - tmp_seq, + n_type, &mac->macaddr, tmp_seq, zebra_evpn_zebra_mac_flag_dump( mac, mac_buf, sizeof(mac_buf))); } @@ -1637,30 +1657,26 @@ static inline bool zebra_evpn_mac_is_bgp_seq_ok(struct zebra_evpn *zevpn, } if (IS_ZEBRA_DEBUG_EVPN_MH_MAC || IS_ZEBRA_DEBUG_VXLAN) { - char mac_buf[MAC_BUF_SIZE]; - zlog_debug( - "%s-macip ignore vni %u %s-mac %pEA%s%s as existing has higher seq %u f %s", + "%s-macip ignore vni %u %s-mac %pEA as existing has higher seq %u f %s", sync ? "sync" : "rem", zevpn->vni, n_type, - &mac->macaddr, - ipa_len ? " IP " : "", - ipa_len ? ipaddr2str(ipaddr, ipbuf, - sizeof(ipbuf)) - : "", - tmp_seq, + &mac->macaddr, tmp_seq, zebra_evpn_zebra_mac_flag_dump( mac, mac_buf, sizeof(mac_buf))); } + return false; } return true; } -struct zebra_mac *zebra_evpn_proc_sync_mac_update( - struct zebra_evpn *zevpn, const struct ethaddr *macaddr, - uint16_t ipa_len, const struct ipaddr *ipaddr, uint8_t flags, - uint32_t seq, const esi_t *esi, struct sync_mac_ip_ctx *ctx) +struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, + const struct ethaddr *macaddr, + uint16_t ipa_len, + const struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, + const esi_t *esi) { struct zebra_mac *mac; bool inform_bgp = false; @@ -1672,6 +1688,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update( bool old_local = false; bool old_bgp_ready; bool new_bgp_ready; + bool created = false; mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { @@ -1680,8 +1697,6 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update( */ inform_bgp = true; inform_dataplane = true; - ctx->mac_created = true; - ctx->mac_inactive = true; /* create the MAC and associate it with the dest ES */ mac = zebra_evpn_mac_add(zevpn, macaddr); @@ -1699,6 +1714,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update( SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); old_bgp_ready = false; new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + created = true; } else { uint32_t old_flags; uint32_t new_flags; @@ -1723,14 +1739,10 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update( : "", sticky ? " sticky" : "", remote_gw ? " remote_gw" : ""); - ctx->ignore_macip = true; return NULL; } - if (!zebra_evpn_mac_is_bgp_seq_ok(zevpn, mac, seq, ipa_len, - ipaddr, true)) { - ctx->ignore_macip = true; + if (!zebra_evpn_mac_is_bgp_seq_ok(zevpn, mac, seq, true)) return NULL; - } old_local = !!CHECK_FLAG(old_flags, ZEBRA_MAC_LOCAL); old_static = zebra_evpn_mac_is_static(mac); @@ -1739,12 +1751,11 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update( new_flags = 0; SET_FLAG(new_flags, ZEBRA_MAC_LOCAL); /* retain old local activity flag */ - if (old_flags & ZEBRA_MAC_LOCAL) { + if (old_flags & ZEBRA_MAC_LOCAL) new_flags |= (old_flags & ZEBRA_MAC_LOCAL_INACTIVE); - } else { + else new_flags |= ZEBRA_MAC_LOCAL_INACTIVE; - ctx->mac_inactive = true; - } + if (ipa_len) { /* if mac-ip route do NOT update the peer flags * i.e. retain only flags as is @@ -1797,7 +1808,6 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update( if (es_change) { inform_bgp = true; inform_dataplane = true; - ctx->mac_inactive = true; } /* if peer-flag is being set notify dataplane that the @@ -1828,8 +1838,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update( char mac_buf[MAC_BUF_SIZE]; zlog_debug("sync-mac %s vni %u mac %pEA es %s seq %d f %s%s%s", - ctx->mac_created ? "created" : "updated", - zevpn->vni, macaddr, + created ? "created" : "updated", zevpn->vni, macaddr, mac->es ? mac->es->esi_str : "-", mac->loc_seq, zebra_evpn_zebra_mac_flag_dump(mac, mac_buf, sizeof(mac_buf)), @@ -1848,22 +1857,15 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update( zebra_evpn_process_neigh_on_local_mac_change( zevpn, mac, seq_change, es_change); - if (inform_dataplane) { - if (ipa_len) - /* if the mac is being created as a part of MAC-IP - * route wait for the neigh to be updated or - * created before programming the mac - */ - ctx->mac_dp_update_deferred = true; - else - /* program the local mac in the kernel. when the ES - * change we need to force the dataplane to reset - * the activity as we are yet to establish activity - * locally - */ - zebra_evpn_sync_mac_dp_install( - mac, ctx->mac_inactive, - false /* force_clear_static */, __func__); + if (inform_dataplane && !ipa_len) { + /* program the local mac in the kernel. when the ES + * change we need to force the dataplane to reset + * the activity as we are yet to establish activity + * locally + */ + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); } return mac; @@ -1987,13 +1989,12 @@ void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket, zebra_evpn_print_mac_hash_detail(bucket, ctxt); } -int zebra_evpn_mac_remote_macip_add( - struct zebra_evpn *zevpn, struct zebra_vrf *zvrf, - const struct ethaddr *macaddr, uint16_t ipa_len, - const struct ipaddr *ipaddr, struct zebra_mac **macp, - struct in_addr vtep_ip, uint8_t flags, uint32_t seq, const esi_t *esi) +int zebra_evpn_mac_remote_macip_add(struct zebra_evpn *zevpn, + struct zebra_vrf *zvrf, + const struct ethaddr *macaddr, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq, const esi_t *esi) { - char buf1[INET6_ADDRSTRLEN]; bool sticky; bool remote_gw; int update_mac = 0; @@ -2015,11 +2016,8 @@ int zebra_evpn_mac_remote_macip_add( && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Ignore remote MACIP ADD VNI %u MAC %pEA%s%s as MAC is already configured as gateway MAC", - zevpn->vni, macaddr, - ipa_len ? " IP " : "", - ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) - : ""); + "Ignore remote MACIP ADD VNI %u MAC %pEA as MAC is already configured as gateway MAC", + zevpn->vni, macaddr); return -1; } @@ -2040,10 +2038,6 @@ int zebra_evpn_mac_remote_macip_add( if (!mac) { mac = zebra_evpn_mac_add(zevpn, macaddr); zebra_evpn_es_mac_ref(mac, esi); - - /* Is this MAC created for a MACIP? */ - if (ipa_len) - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); } else { /* When host moves but changes its (MAC,IP) * binding, BGP may install a MACIP entry that @@ -2053,8 +2047,8 @@ int zebra_evpn_mac_remote_macip_add( * the sequence number and ignore this update * if appropriate. */ - if (!zebra_evpn_mac_is_bgp_seq_ok( - zevpn, mac, seq, ipa_len, ipaddr, false)) + if (!zebra_evpn_mac_is_bgp_seq_ok(zevpn, mac, seq, + false)) return -1; old_es_present = !!mac->es; @@ -2138,12 +2132,7 @@ int zebra_evpn_mac_remote_macip_add( /* Update seq number. */ mac->rem_seq = seq; - /* If there is no IP, return after clearing AUTO flag of MAC. */ - if (!ipa_len) { - UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - return -1; - } - *macp = mac; + UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); return 0; } diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index b727ac1f98..9b4ea2b79e 100644 --- a/zebra/zebra_evpn_mac.h +++ b/zebra/zebra_evpn_mac.h @@ -176,17 +176,6 @@ struct rmac_walk_ctx { struct json_object *json; }; -/* temporary datastruct to pass info between the mac-update and - * neigh-update while handling mac-ip routes - */ -struct sync_mac_ip_ctx { - bool ignore_macip; - bool mac_created; - bool mac_inactive; - bool mac_dp_update_deferred; - struct zebra_mac *mac; -}; - /**************************** SYNC MAC handling *****************************/ /* if the mac has been added of a mac-route from the peer * or if it is being referenced by a neigh added by the @@ -232,6 +221,8 @@ struct zebra_mac *zebra_evpn_mac_lookup(struct zebra_evpn *zevi, const struct ethaddr *mac); struct zebra_mac *zebra_evpn_mac_add(struct zebra_evpn *zevi, const struct ethaddr *macaddr); +struct zebra_mac *zebra_evpn_mac_add_auto(struct zebra_evpn *zevi, + const struct ethaddr *macaddr); int zebra_evpn_mac_del(struct zebra_evpn *zevi, struct zebra_mac *mac); int zebra_evpn_macip_send_msg_to_client(uint32_t id, const struct ethaddr *macaddr, @@ -255,20 +246,22 @@ int zebra_evpn_mac_send_add_to_client(vni_t vni, const struct ethaddr *macaddr, int zebra_evpn_mac_send_del_to_client(vni_t vni, const struct ethaddr *macaddr, uint32_t flags, bool force); void zebra_evpn_send_mac_list_to_client(struct zebra_evpn *zevi); -struct zebra_mac *zebra_evpn_proc_sync_mac_update( - struct zebra_evpn *zevi, const struct ethaddr *macaddr, - uint16_t ipa_len, const struct ipaddr *ipaddr, uint8_t flags, - uint32_t seq, const esi_t *esi, struct sync_mac_ip_ctx *ctx); +struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevi, + const struct ethaddr *macaddr, + uint16_t ipa_len, + const struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, + const esi_t *esi); void zebra_evpn_sync_mac_del(struct zebra_mac *mac); void zebra_evpn_rem_mac_del(struct zebra_evpn *zevi, struct zebra_mac *mac); void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt); void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket, void *ctxt); -int zebra_evpn_mac_remote_macip_add( - struct zebra_evpn *zevpn, struct zebra_vrf *zvrf, - const struct ethaddr *macaddr, uint16_t ipa_len, - const struct ipaddr *ipaddr, struct zebra_mac **macp, - struct in_addr vtep_ip, uint8_t flags, uint32_t seq, const esi_t *esi); +int zebra_evpn_mac_remote_macip_add(struct zebra_evpn *zevpn, + struct zebra_vrf *zvrf, + const struct ethaddr *macaddr, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq, const esi_t *esi); int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, struct zebra_evpn *zevpn, diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 98120accfd..01ea9c5b9c 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -2759,6 +2759,12 @@ bool zebra_evpn_is_if_es_capable(struct zebra_if *zif) if (zif->zif_type == ZEBRA_IF_BOND) return true; + /* relax the checks to allow config to be applied in zebra + * before interface is rxed from the kernel + */ + if (zif->ifp->ifindex == IFINDEX_INTERNAL) + return true; + /* XXX: allow swpX i.e. a regular ethernet port to be an ES link too */ return false; } diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index 6d90a603f7..684720bb4d 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -501,22 +501,33 @@ bool zebra_evpn_neigh_is_bgp_seq_ok(struct zebra_evpn *zevpn, { uint32_t tmp_seq; const char *n_type; + bool is_local = false; if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { tmp_seq = n->loc_seq; n_type = "local"; + is_local = true; } else { tmp_seq = n->rem_seq; n_type = "remote"; } if (seq < tmp_seq) { + if (is_local && !zebra_evpn_neigh_is_ready_for_bgp(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH || + IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s-macip not ready vni %u %s mac %pEA IP %pIA lower seq %u f 0x%x", + sync ? "sync" : "remote", zevpn->vni, + n_type, macaddr, &n->ip, tmp_seq, + n->flags); + return true; + } + /* if the neigh was never advertised to bgp we must accept * whatever sequence number bgp sends - * XXX - check with Vivek */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) - && !zebra_evpn_neigh_is_ready_for_bgp(n)) { + if (!is_local && zebra_vxlan_get_accept_bgp_seq()) { if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH || IS_ZEBRA_DEBUG_VXLAN) zlog_debug( @@ -615,11 +626,10 @@ void zebra_evpn_sync_neigh_del(struct zebra_neigh *n) struct zebra_neigh *zebra_evpn_proc_sync_neigh_update( struct zebra_evpn *zevpn, struct zebra_neigh *n, uint16_t ipa_len, const struct ipaddr *ipaddr, uint8_t flags, uint32_t seq, - const esi_t *esi, struct sync_mac_ip_ctx *ctx) + const esi_t *esi, struct zebra_mac *mac) { struct interface *ifp = NULL; bool is_router; - struct zebra_mac *mac = ctx->mac; uint32_t tmp_seq; bool old_router = false; bool old_bgp_ready = false; @@ -780,8 +790,8 @@ struct zebra_neigh *zebra_evpn_proc_sync_neigh_update( inform_bgp = true; new_mac_static = zebra_evpn_mac_is_static(mac); - if ((old_mac_static != new_mac_static) || ctx->mac_dp_update_deferred) - zebra_evpn_sync_mac_dp_install(mac, ctx->mac_inactive, + if (old_mac_static != new_mac_static) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, false /* force_clear_static */, __func__); @@ -1275,10 +1285,12 @@ int zebra_evpn_local_neigh_update(struct zebra_evpn *zevpn, zlog_debug("AUTO MAC %pEA created for neigh %pIA on VNI %u", macaddr, ip, zevpn->vni); - zmac = zebra_evpn_mac_add(zevpn, macaddr); - zebra_evpn_mac_clear_fwd_info(zmac); - memset(&zmac->flags, 0, sizeof(uint32_t)); - SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); + zmac = zebra_evpn_mac_add_auto(zevpn, macaddr); + if (!zmac) { + zlog_debug("Failed to add MAC %pEA VNI %u", macaddr, + zevpn->vni); + return -1; + } } else { if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { /* @@ -2233,6 +2245,12 @@ void zebra_evpn_neigh_remote_uninstall(struct zebra_evpn *zevpn, zebra_evpn_neigh_del(zevpn, n); zebra_evpn_deref_ip2mac(zevpn, mac); } + } else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: IP %pIA MAC %pEA (flags 0x%x) found doesn't match MAC %pEA, ignoring Neigh DEL", + __func__, ipaddr, &n->emac, n->flags, + &mac->macaddr); } } diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h index c779109e0a..9271817440 100644 --- a/zebra/zebra_evpn_neigh.h +++ b/zebra/zebra_evpn_neigh.h @@ -231,7 +231,7 @@ void zebra_evpn_sync_neigh_del(struct zebra_neigh *n); struct zebra_neigh *zebra_evpn_proc_sync_neigh_update( struct zebra_evpn *zevpn, struct zebra_neigh *n, uint16_t ipa_len, const struct ipaddr *ipaddr, uint8_t flags, uint32_t seq, - const esi_t *esi, struct sync_mac_ip_ctx *ctx); + const esi_t *esi, struct zebra_mac *mac); void zebra_evpn_neigh_del_all(struct zebra_evpn *zevpn, int uninstall, int upd_client, uint32_t flags); struct zebra_neigh *zebra_evpn_neigh_lookup(struct zebra_evpn *zevpn, diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c index c0f18dd091..7d95607fcf 100644 --- a/zebra/zebra_srte.c +++ b/zebra/zebra_srte.c @@ -384,6 +384,23 @@ int zebra_sr_policy_label_update(mpls_label_t label, return 0; } +static int zebra_srte_client_close_cleanup(struct zserv *client) +{ + int sock = client->sock; + struct zebra_sr_policy *policy; + + if (!sock) + return 0; + + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + if (policy->sock == sock) + zebra_sr_policy_del(policy); + } + return 1; +} + void zebra_srte_init(void) { + hook_register(zserv_client_close, zebra_srte_client_close_cleanup); } diff --git a/zebra/zebra_srte.h b/zebra/zebra_srte.h index fe77809446..dff2f595fd 100644 --- a/zebra/zebra_srte.h +++ b/zebra/zebra_srte.h @@ -45,6 +45,7 @@ struct zebra_sr_policy { struct zapi_srte_tunnel segment_list; struct zebra_lsp *lsp; struct zebra_vrf *zvrf; + int sock; }; RB_HEAD(zebra_sr_policy_instance_head, zebra_sr_policy); RB_PROTOTYPE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 36506cacc7..d61e4f8045 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -177,6 +177,58 @@ struct srv6_locator *zebra_srv6_locator_lookup(const char *name) return NULL; } +void zebra_notify_srv6_locator_add(struct srv6_locator *locator) +{ + struct listnode *node; + struct zserv *client; + + /* + * Notify new locator info to zclients. + * + * The srv6 locators and their prefixes are managed by zserv(zebra). + * And an actual configuration the srv6 sid in the srv6 locator is done + * by zclient(bgpd, isisd, etc). The configuration of each locator + * allocation and specify it by zserv and zclient should be + * asynchronous. For that, zclient should be received the event via + * ZAPI when a srv6 locator is added on zebra. + * Basically, in SRv6, adding/removing SRv6 locators is performed less + * frequently than adding rib entries, so a broad to all zclients will + * not degrade the overall performance of FRRouting. + */ + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) + zsend_zebra_srv6_locator_add(client, locator); +} + +void zebra_notify_srv6_locator_delete(struct srv6_locator *locator) +{ + struct listnode *n; + struct srv6_locator_chunk *c; + struct zserv *client; + + /* + * Notify deleted locator info to zclients if needed. + * + * zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and + * uses it for its own purpose. For example, in the case of BGP L3VPN, + * the SID assigned to vpn unicast rib will be given. + * And when the locator is deleted by zserv(zebra), those SIDs need to + * be withdrawn. The zclient must initiate the withdrawal of the SIDs + * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the + * owner of each chunk. + */ + for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) { + if (c->proto == ZEBRA_ROUTE_SYSTEM) + continue; + client = zserv_find_client(c->proto, c->instance); + if (!client) { + zlog_warn("Not found zclient(proto=%u, instance=%u).", + c->proto, c->instance); + continue; + } + zsend_zebra_srv6_locator_delete(client, locator); + } +} + struct zebra_srv6 *zebra_srv6_get_default(void) { static struct zebra_srv6 srv6; diff --git a/zebra/zebra_srv6.h b/zebra/zebra_srv6.h index 84fcc305bc..f320b9ca0f 100644 --- a/zebra/zebra_srv6.h +++ b/zebra/zebra_srv6.h @@ -61,6 +61,9 @@ extern void zebra_srv6_locator_add(struct srv6_locator *locator); extern void zebra_srv6_locator_delete(struct srv6_locator *locator); extern struct srv6_locator *zebra_srv6_locator_lookup(const char *name); +void zebra_notify_srv6_locator_add(struct srv6_locator *locator); +void zebra_notify_srv6_locator_delete(struct srv6_locator *locator); + extern void zebra_srv6_init(void); extern struct zebra_srv6 *zebra_srv6_get_default(void); extern bool zebra_srv6_is_enable(void); diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index e6810bdc56..1221365d4d 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -172,6 +172,9 @@ DEFUN (show_srv6_locator_detail, vty_out(vty, "Argument-Bit-Len: %u\n", locator->argument_bits_length); + if (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) + vty_out(vty, "Behavior: uSID\n"); + vty_out(vty, "Chunks:\n"); for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, node, chunk)) { @@ -369,6 +372,38 @@ DEFPY (locator_prefix, return CMD_SUCCESS; } +DEFPY (locator_behavior, + locator_behavior_cmd, + "[no] behavior usid", + NO_STR + "Configure SRv6 behavior\n" + "Specify SRv6 behavior uSID\n") +{ + VTY_DECLVAR_CONTEXT(srv6_locator, locator); + + if (no && !CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) + /* SRv6 locator uSID flag already unset, nothing to do */ + return CMD_SUCCESS; + + if (!no && CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) + /* SRv6 locator uSID flag already set, nothing to do */ + return CMD_SUCCESS; + + /* Remove old locator from zclients */ + zebra_notify_srv6_locator_delete(locator); + + /* Set/Unset the SRV6_LOCATOR_USID */ + if (no) + UNSET_FLAG(locator->flags, SRV6_LOCATOR_USID); + else + SET_FLAG(locator->flags, SRV6_LOCATOR_USID); + + /* Notify the new locator to zclients */ + zebra_notify_srv6_locator_add(locator); + + return CMD_SUCCESS; +} + static int zebra_sr_config(struct vty *vty) { struct zebra_srv6 *srv6 = zebra_srv6_get_default(); @@ -399,6 +434,8 @@ static int zebra_sr_config(struct vty *vty) if (locator->argument_bits_length) vty_out(vty, " arg-len %u", locator->argument_bits_length); + if (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) + vty_out(vty, " behavior usid"); vty_out(vty, "\n"); vty_out(vty, " exit\n"); vty_out(vty, " !\n"); @@ -435,6 +472,7 @@ void zebra_srv6_vty_init(void) /* Command for configuration */ install_element(SRV6_LOC_NODE, &locator_prefix_cmd); + install_element(SRV6_LOC_NODE, &locator_behavior_cmd); /* Command for operation */ install_element(VIEW_NODE, &show_srv6_locator_cmd); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 40b400f104..6561ac95fa 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -3747,6 +3747,27 @@ DEFPY (clear_evpn_dup_addr, return ret; } +DEFPY_HIDDEN (evpn_accept_bgp_seq, + evpn_accept_bgp_seq_cmd, + "evpn accept-bgp-seq", + "EVPN\n" + "Accept all sequence numbers from BGP\n") +{ + zebra_vxlan_set_accept_bgp_seq(true); + return CMD_SUCCESS; +} + +DEFPY_HIDDEN (no_evpn_accept_bgp_seq, + no_evpn_accept_bgp_seq_cmd, + "no evpn accept-bgp-seq", + NO_STR + "EVPN\n" + "Accept all sequence numbers from BGP\n") +{ + zebra_vxlan_set_accept_bgp_seq(false); + return CMD_SUCCESS; +} + /* Static ip route configuration write function. */ static int zebra_ip_config(struct vty *vty) { @@ -3952,6 +3973,9 @@ static int config_write_protocol(struct vty *vty) zebra_pbr_config_write(vty); + if (!zebra_vxlan_get_accept_bgp_seq()) + vty_out(vty, "no evpn accept-bgp-seq\n"); + /* Include nexthop-group config */ if (!zebra_nhg_kernel_nexthops_enabled()) vty_out(vty, "no zebra nexthop kernel enable\n"); @@ -4589,6 +4613,8 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_neigh_vni_dad_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_all_dad_cmd); install_element(ENABLE_NODE, &clear_evpn_dup_addr_cmd); + install_element(CONFIG_NODE, &evpn_accept_bgp_seq_cmd); + install_element(CONFIG_NODE, &no_evpn_accept_bgp_seq_cmd); install_element(VIEW_NODE, &show_neigh_cmd); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 34cce71cd7..f1c7debe11 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -69,6 +69,9 @@ DEFINE_HOOK(zebra_rmac_update, const char *reason), (rmac, zl3vni, delete, reason)); +/* config knobs */ +static bool accept_bgp_seq = true; + /* static function declarations */ static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket, void **args); @@ -6284,6 +6287,17 @@ extern void zebra_vxlan_handle_result(struct zebra_dplane_ctx *ctx) return; } +/* Config knob for accepting lower sequence numbers */ +void zebra_vxlan_set_accept_bgp_seq(bool set) +{ + accept_bgp_seq = set; +} + +bool zebra_vxlan_get_accept_bgp_seq(void) +{ + return accept_bgp_seq; +} + /* Cleanup BGP EVPN configuration upon client disconnect */ extern void zebra_evpn_init(void) { diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 757c65d185..121e5633d6 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -225,6 +225,9 @@ extern int zebra_vxlan_dp_network_mac_del(struct interface *ifp, struct ethaddr *macaddr, vlanid_t vid); +extern void zebra_vxlan_set_accept_bgp_seq(bool set); +extern bool zebra_vxlan_get_accept_bgp_seq(void); + #ifdef __cplusplus } #endif |
