diff options
185 files changed, 10108 insertions, 1186 deletions
diff --git a/Makefile.am b/Makefile.am index a38029dcfa..8c9d7df77c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -109,8 +109,6 @@ CLEANFILES = DISTCLEANFILES = SUFFIXES = -examplesdir = $(exampledir) - bin_PROGRAMS = sbin_PROGRAMS = sbin_SCRIPTS = @@ -122,7 +120,6 @@ lib_LTLIBRARIES = module_LTLIBRARIES = pkginclude_HEADERS = nodist_pkginclude_HEADERS = -dist_examples_DATA = dist_yangmodels_DATA = man_MANS = vtysh_scan = diff --git a/babeld/babeld.c b/babeld/babeld.c index 4d4dd2e194..b9623b64b5 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -819,6 +819,8 @@ babeld_quagga_init(void) install_element(BABEL_NODE, &babel_ipv6_distribute_list_cmd); install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_cmd); + vrf_cmd_init(NULL, &babeld_privs); + babel_if_init(); /* Access list install. */ diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index 1df646c346..0e341a8c6b 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -315,3 +315,4 @@ extern bool is_zero_gw_ip(const union gw_addr *gw_ip, const afi_t afi) return false; } + diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h index 6fdf73fd1e..102509fdd7 100644 --- a/bgpd/bgp_attr_evpn.h +++ b/bgpd/bgp_attr_evpn.h @@ -30,7 +30,21 @@ union gw_addr { struct in6_addr ipv6; }; +enum overlay_index_type { + OVERLAY_INDEX_TYPE_NONE, + OVERLAY_INDEX_GATEWAY_IP, + OVERLAY_INDEX_ESI, + OVERLAY_INDEX_MAC, +}; + +/* + * Structure to store ovrelay index for EVPN type-5 route + * This structure stores ESI and Gateway IP overlay index. + * MAC overlay index is stored in the RMAC attribute. + */ struct bgp_route_evpn { + enum overlay_index_type type; + esi_t eth_s_id; union gw_addr gw_ip; }; diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 8f286e66df..856afb05f8 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -2680,10 +2680,14 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels, int addpath_valid, uint32_t addpath_id, + struct bgp_route_evpn *overlay_index, char *str, int size) { char rd_buf[RD_ADDRSTRLEN]; char tag_buf[30]; + char overlay_index_buf[INET6_ADDRSTRLEN + 14]; + const struct prefix_evpn *evp; + /* ' with addpath ID ' 17 * max strlen of uint32 + 10 * +/- (just in case) + 1 @@ -2701,6 +2705,23 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u", addpath_id); + overlay_index_buf[0] = '\0'; + if (overlay_index && overlay_index->type == OVERLAY_INDEX_GATEWAY_IP) { + char obuf[INET6_ADDRSTRLEN]; + + obuf[0] = '\0'; + evp = pu.evp; + if (is_evpn_prefix_ipaddr_v4(evp)) + inet_ntop(AF_INET, &overlay_index->gw_ip, obuf, + sizeof(obuf)); + else if (is_evpn_prefix_ipaddr_v6(evp)) + inet_ntop(AF_INET6, &overlay_index->gw_ip, obuf, + sizeof(obuf)); + + snprintf(overlay_index_buf, sizeof(overlay_index_buf), + " gateway IP %s", obuf); + } + tag_buf[0] = '\0'; if (bgp_labeled_safi(safi) && num_labels) { @@ -2720,9 +2741,10 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, } if (prd) - snprintfrr(str, size, "RD %s %pFX%s%s %s %s", + snprintfrr(str, size, "RD %s %pFX%s%s%s %s %s", prefix_rd2str(prd, rd_buf, sizeof(rd_buf)), pu.p, - tag_buf, pathid_buf, afi2str(afi), safi2str(safi)); + overlay_index_buf, tag_buf, pathid_buf, afi2str(afi), + safi2str(safi)); else if (safi == SAFI_FLOWSPEC) { char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; const struct prefix_fs *fs = pu.fs; diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index fa8da1c345..d847fb84e7 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -37,7 +37,8 @@ #define DUMP_DETAIL 32 /* RD + Prefix + Path-Id */ -#define BGP_PRD_PATH_STRLEN (PREFIX_STRLEN + RD_ADDRSTRLEN + 20) +#define BGP_PRD_PATH_STRLEN \ + (PREFIX_STRLEN + RD_ADDRSTRLEN + INET6_ADDRSTRLEN + 34) extern int dump_open; extern int dump_update; @@ -179,11 +180,11 @@ extern bool bgp_debug_update(struct peer *peer, const struct prefix *p, extern bool bgp_debug_bestpath(struct bgp_dest *dest); extern bool bgp_debug_zebra(const struct prefix *p); -extern const char * -bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, const struct prefix_rd *prd, - union prefixconstptr pu, mpls_label_t *label, - uint32_t num_labels, int addpath_valid, - uint32_t addpath_id, char *str, int size); +extern const char *bgp_debug_rdpfxpath2str( + afi_t afi, safi_t safi, const struct prefix_rd *prd, + union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels, + int addpath_valid, uint32_t addpath_id, + struct bgp_route_evpn *overlay_index, char *str, int size); const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data, size_t datalen); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index d8e57419ee..5ef593b9c0 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -53,6 +53,7 @@ #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_nht.h" /* * Definitions and external declarations. @@ -65,6 +66,28 @@ DEFINE_QOBJ_TYPE(bgp_evpn_es); * Static function declarations */ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *evpn); +static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *evpn); +static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, + struct bgp_path_info *pi); +static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, + struct bgp_path_info *pi); +static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn, + void (*func)(struct hash_bucket *, + void *), + void *arg); +static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, + struct bgpevpn *vpn); +static unsigned int vni_svi_hash_key_make(const void *p); +static bool vni_svi_hash_cmp(const void *p1, const void *p2); +static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, + struct ipaddr *addr, + bool resolve); +static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket, + void *args); +static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket, + void *args); static struct in_addr zero_vtep_ip; /* @@ -1261,7 +1284,8 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, /* update evpn type-5 route entry */ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, - struct attr *src_attr) + struct attr *src_attr, afi_t src_afi, + safi_t src_safi) { afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -1315,6 +1339,26 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + if (src_afi == AFI_IP6 && + CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) { + if (src_attr && + !IN6_IS_ADDR_UNSPECIFIED(&src_attr->mp_nexthop_global)) { + attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&attr.evpn_overlay.gw_ip.ipv6, + &src_attr->mp_nexthop_global, + sizeof(struct in6_addr)); + } + } else if (src_afi == AFI_IP && + CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) { + if (src_attr && src_attr->nexthop.s_addr != 0) { + attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&attr.evpn_overlay.gw_ip.ipv4, + &src_attr->nexthop, sizeof(struct in_addr)); + } + } + /* Setup RT and encap extended community */ build_evpn_type5_route_extcomm(bgp_vrf, &attr); @@ -2198,6 +2242,7 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) dest = bgp_route_next(dest)) { for (pi = bgp_dest_get_bgp_path_info(dest); (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { + bgp_evpn_remote_ip_hash_del(vpn, pi); bgp_path_info_delete(dest, pi); bgp_path_info_reap(dest, pi); } @@ -2381,6 +2426,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, bool new_pi = false; bool use_l3nhg = false; bool is_l3nhg_active = false; + char buf1[INET6_ADDRSTRLEN]; memset(pp, 0, sizeof(struct prefix)); ip_prefix_from_evpn_prefix(evp, pp); @@ -2411,10 +2457,36 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, * make sure to set the flag for next hop attribute. */ attr = *parent_pi->attr; - if (afi == AFI_IP6) - evpn_convert_nexthop_to_ipv6(&attr); - else - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + if (attr.evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP) { + if (afi == AFI_IP6) + evpn_convert_nexthop_to_ipv6(&attr); + else + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } else { + + /* + * If gateway IP overlay index is specified in the NLRI of + * EVPN RT-5, this gateway IP should be used as the nexthop + * for the prefix in the VRF + */ + if (bgp_debug_zebra(NULL)) { + zlog_debug( + "Install gateway IP %s as nexthop for prefix %pFX in vrf %s", + inet_ntop(pp->family, &attr.evpn_overlay.gw_ip, + buf1, sizeof(buf1)), pp, + vrf_id_to_name(bgp_vrf->vrf_id)); + } + + if (afi == AFI_IP6) { + memcpy(&attr.mp_nexthop_global, + &attr.evpn_overlay.gw_ip.ipv6, + sizeof(struct in6_addr)); + attr.mp_nexthop_len = IPV6_MAX_BYTELEN; + } else { + attr.nexthop = attr.evpn_overlay.gw_ip.ipv4; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + } + } bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg, &is_l3nhg_active, NULL); @@ -2460,8 +2532,27 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, pi->attr = attr_new; pi->uptime = bgp_clock(); } - /* as it is an importation, change nexthop */ - bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); + + /* Gateway IP nexthop should be resolved */ + if (attr.evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { + if (bgp_find_or_add_nexthop(bgp_vrf, bgp_vrf, afi, safi, pi, + NULL, 0)) + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); + else { + if (BGP_DEBUG(nht, NHT)) { + inet_ntop(pp->family, + &attr.evpn_overlay.gw_ip, + buf1, sizeof(buf1)); + zlog_debug("%s: gateway IP NH unresolved", + buf1); + } + bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID); + } + } else { + + /* as it is an importation, change nexthop */ + bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); + } /* Link path to evpn nexthop */ bgp_evpn_path_nh_add(bgp_vrf, pi); @@ -2565,6 +2656,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, pi->uptime = bgp_clock(); } + /* Add this route to remote IP hashtable */ + bgp_evpn_remote_ip_hash_add(vpn, pi); + /* Perform route selection and update zebra, if required. */ ret = evpn_route_select_install(bgp, vpn, dest); @@ -2628,8 +2722,10 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, && (struct bgp_path_info *)pi->extra->parent == parent_pi) break; - if (!pi) + if (!pi) { + bgp_dest_unlock_node(dest); return 0; + } if (bgp_debug_zebra(NULL)) zlog_debug("... delete dest %p (l %d) pi %p (l %d, f 0x%x)", @@ -2690,8 +2786,12 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, && (struct bgp_path_info *)pi->extra->parent == parent_pi) break; - if (!pi) + if (!pi) { + bgp_dest_unlock_node(dest); return 0; + } + + bgp_evpn_remote_ip_hash_del(vpn, pi); /* Mark entry for deletion */ bgp_path_info_delete(dest, pi); @@ -3951,7 +4051,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, mpls_label_t label; /* holds the VNI as in the packet */ int ret; afi_t gw_afi; - bool is_valid_update = false; + bool is_valid_update = true; /* Type-5 route should be 34 or 58 bytes: * RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16), @@ -3980,9 +4080,9 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, /* Additional information outside of prefix - ESI and GW IP */ memset(&evpn, 0, sizeof(evpn)); - /* Fetch ESI */ + /* Fetch ESI overlay index */ if (attr) - memcpy(&attr->esi, pfx, sizeof(esi_t)); + memcpy(&evpn.eth_s_id, pfx, sizeof(esi_t)); pfx += ESI_BYTES; /* Fetch Ethernet Tag. */ @@ -4031,25 +4131,53 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, * field */ + /* + * An update containing a non-zero gateway IP and a non-zero ESI + * at the same time is should be treated as withdraw + */ + if (bgp_evpn_is_esi_valid(&evpn.eth_s_id) + && !is_zero_gw_ip(&evpn.gw_ip, gw_afi)) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%s - Rx EVPN Type-5 ESI and gateway-IP both non-zero.", + peer->host); + is_valid_update = false; + } else if (bgp_evpn_is_esi_valid(&evpn.eth_s_id)) + evpn.type = OVERLAY_INDEX_ESI; + else if (!is_zero_gw_ip(&evpn.gw_ip, gw_afi)) + evpn.type = OVERLAY_INDEX_GATEWAY_IP; if (attr) { - is_valid_update = true; - if (is_zero_mac(&attr->rmac) && - is_zero_gw_ip(&evpn.gw_ip, gw_afi)) + if (is_zero_mac(&attr->rmac) + && !bgp_evpn_is_esi_valid(&evpn.eth_s_id) + && is_zero_gw_ip(&evpn.gw_ip, gw_afi) && label == 0) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%s - Rx EVPN Type-5 ESI, gateway-IP, RMAC and label all zero", + peer->host); is_valid_update = false; + } if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac)) is_valid_update = false; } /* Process the route. */ - if (is_valid_update) + if (attr && is_valid_update) ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, 0, &evpn); - else + else { + if (!is_valid_update) { + char attr_str[BUFSIZ] = {0}; + + bgp_dump_attr(attr, attr_str, BUFSIZ); + zlog_warn( + "Invalid update from peer %s vrf %u prefix %pFX attr %s - treat as withdraw", + peer->hostname, peer->bgp->vrf_id, &p, + attr_str); + } ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, &evpn); + } return ret; } @@ -4078,7 +4206,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p, /* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */ stream_putc(s, 8 + 10 + 4 + 1 + len + 3); stream_put(s, prd->val, 8); - if (attr) + if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_ESI) stream_put(s, &attr->esi, sizeof(esi_t)); else stream_put(s, 0, sizeof(esi_t)); @@ -4088,7 +4216,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p, stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr); else stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16); - if (attr) { + if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { const struct bgp_route_evpn *evpn_overlay = bgp_attr_get_evpn_overlay(attr); @@ -4301,7 +4429,7 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p, struct prefix_evpn evp; build_type5_prefix_from_ip_prefix(&evp, p); - ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr); + ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr, afi, safi); if (ret) flog_err(EC_BGP_EVPN_ROUTE_CREATE, "%u: Failed to create type-5 route for prefix %pFX", @@ -5134,7 +5262,8 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni) struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp) + struct in_addr mcast_grp, + ifindex_t svi_ifindex) { struct bgpevpn *vpn; @@ -5148,6 +5277,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, vpn->originator_ip = originator_ip; vpn->tenant_vrf_id = tenant_vrf_id; vpn->mcast_grp = mcast_grp; + vpn->svi_ifindex = svi_ifindex; /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); @@ -5168,6 +5298,9 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, return NULL; } + bgp_evpn_remote_ip_hash_init(vpn); + bgp_evpn_link_to_vni_svi_hash(bgp, vpn); + /* add to l2vni list on corresponding vrf */ bgpevpn_link_to_l3vni(vpn); @@ -5185,6 +5318,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, */ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) { + bgp_evpn_remote_ip_hash_destroy(vpn); bgp_evpn_vni_es_cleanup(vpn); bgpevpn_unlink_from_l3vni(vpn); bgp_table_unlock(vpn->route_table); @@ -5192,6 +5326,7 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) list_delete(&vpn->import_rtl); list_delete(&vpn->export_rtl); bf_release_index(bm->rd_idspace, vpn->rd_id); + hash_release(bgp->vni_svi_hash, vpn); hash_release(bgp->vnihash, vpn); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); @@ -5308,8 +5443,10 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, } else { /* Re-instate the current remote best path if any */ dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); - if (dest) + if (dest) { evpn_zebra_reinstall_best_route(bgp, vpn, dest); + bgp_dest_unlock_node(dest); + } } return 0; @@ -5603,6 +5740,9 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) */ delete_routes_for_vni(bgp, vpn); + bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); + + vpn->svi_ifindex = 0; /* * tunnel is no longer active, del tunnel ip address from tip_hash */ @@ -5623,8 +5763,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp) - + struct in_addr mcast_grp, + ifindex_t svi_ifindex) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -5636,18 +5776,65 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, if (is_vni_live(vpn) && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip) && IPV4_ADDR_SAME(&vpn->mcast_grp, &mcast_grp) - && vpn->tenant_vrf_id == tenant_vrf_id) + && vpn->tenant_vrf_id == tenant_vrf_id + && vpn->svi_ifindex == svi_ifindex) /* Probably some other param has changed that we don't * care about. */ return 0; bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp); + if (vpn->svi_ifindex != svi_ifindex) { + + /* + * Unresolve all the gateway IP nexthops for this VNI + * for old SVI + */ + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_unlink_nexthop, + vpn); + bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); + vpn->svi_ifindex = svi_ifindex; + bgp_evpn_link_to_vni_svi_hash(bgp, vpn); + + /* + * Resolve all the gateway IP nexthops for this VNI + * for new SVI + */ + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_link_nexthop, + vpn); + } + /* Update tenant_vrf_id if it has changed. */ if (vpn->tenant_vrf_id != tenant_vrf_id) { + + /* + * Unresolve all the gateway IP nexthops for this VNI + * in old tenant vrf + */ + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_unlink_nexthop, + vpn); bgpevpn_unlink_from_l3vni(vpn); vpn->tenant_vrf_id = tenant_vrf_id; bgpevpn_link_to_l3vni(vpn); + + /* + * Resolve all the gateway IP nexthops for this VNI + * in new tenant vrf + */ + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_remote_ip_hash_link_nexthop, + vpn); } /* If tunnel endpoint IP has changed, update (and delete prior @@ -5666,7 +5853,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Create or update as appropriate. */ if (!vpn) { vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id, - mcast_grp); + mcast_grp, svi_ifindex); if (!vpn) { flog_err( EC_BGP_VNI, @@ -5768,6 +5955,8 @@ void bgp_evpn_cleanup(struct bgp *bgp) hash_free(bgp->vrf_import_rt_hash); bgp->vrf_import_rt_hash = NULL; + hash_free(bgp->vni_svi_hash); + bgp->vni_svi_hash = NULL; hash_free(bgp->vnihash); bgp->vnihash = NULL; @@ -5786,6 +5975,9 @@ void bgp_evpn_init(struct bgp *bgp) { bgp->vnihash = hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash"); + bgp->vni_svi_hash = + hash_create(vni_svi_hash_key_make, vni_svi_hash_cmp, + "BGP VNI hash based on SVI ifindex"); bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); @@ -5878,3 +6070,408 @@ bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx) return false; } + +static void *bgp_evpn_remote_ip_hash_alloc(void *p) +{ + const struct evpn_remote_ip *key = (const struct evpn_remote_ip *)p; + struct evpn_remote_ip *ip; + + ip = XMALLOC(MTYPE_EVPN_REMOTE_IP, sizeof(struct evpn_remote_ip)); + *ip = *key; + ip->macip_path_list = list_new(); + + return ip; +} + +static unsigned int bgp_evpn_remote_ip_hash_key_make(const void *p) +{ + const struct evpn_remote_ip *ip = p; + const struct ipaddr *addr = &ip->addr; + + if (IS_IPADDR_V4(addr)) + return jhash_1word(addr->ipaddr_v4.s_addr, 0); + + return jhash2(addr->ipaddr_v6.s6_addr32, + array_size(addr->ipaddr_v6.s6_addr32), 0); +} + +static bool bgp_evpn_remote_ip_hash_cmp(const void *p1, const void *p2) +{ + const struct evpn_remote_ip *ip1 = p1; + const struct evpn_remote_ip *ip2 = p2; + + return (memcmp(&ip1->addr, &ip2->addr, sizeof(struct ipaddr)) == 0); +} + +static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *vpn) +{ + if (!evpn_resolve_overlay_index()) + return; + + vpn->remote_ip_hash = hash_create(bgp_evpn_remote_ip_hash_key_make, + bgp_evpn_remote_ip_hash_cmp, + "BGP EVPN remote IP hash"); +} + +static void bgp_evpn_remote_ip_hash_free(struct hash_bucket *bucket, void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); + + list_delete(&ip->macip_path_list); + + hash_release(vpn->remote_ip_hash, ip); + XFREE(MTYPE_EVPN_REMOTE_IP, ip); +} + +static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn) +{ + if (!evpn_resolve_overlay_index() || vpn->remote_ip_hash == NULL) + return; + + hash_iterate(vpn->remote_ip_hash, + (void (*)(struct hash_bucket *, void *))bgp_evpn_remote_ip_hash_free, + vpn); + + hash_free(vpn->remote_ip_hash); + vpn->remote_ip_hash = NULL; +} + +/* Add a remote MAC/IP route to hash table */ +static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn, + struct bgp_path_info *pi) +{ + struct evpn_remote_ip tmp; + struct evpn_remote_ip *ip; + struct prefix_evpn *evp; + + if (!evpn_resolve_overlay_index()) + return; + + if (pi->type != ZEBRA_ROUTE_BGP || pi->sub_type != BGP_ROUTE_IMPORTED + || !CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + return; + + evp = (struct prefix_evpn *)&pi->net->p; + + if (evp->family != AF_EVPN + || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE + || is_evpn_prefix_ipaddr_none(evp)) + return; + + tmp.addr = evp->prefix.macip_addr.ip; + ip = hash_lookup(vpn->remote_ip_hash, &tmp); + if (ip) { + if (listnode_lookup(ip->macip_path_list, pi) != NULL) + return; + (void)listnode_add(ip->macip_path_list, pi); + return; + } + + ip = hash_get(vpn->remote_ip_hash, &tmp, bgp_evpn_remote_ip_hash_alloc); + if (!ip) + return; + + (void)listnode_add(ip->macip_path_list, pi); + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true); +} + +/* Delete a remote MAC/IP route from hash table */ +static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn, + struct bgp_path_info *pi) +{ + struct evpn_remote_ip tmp; + struct evpn_remote_ip *ip; + struct prefix_evpn *evp; + + if (!evpn_resolve_overlay_index()) + return; + + evp = (struct prefix_evpn *)&pi->net->p; + + if (evp->family != AF_EVPN + || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE + || is_evpn_prefix_ipaddr_none(evp)) + return; + + tmp.addr = evp->prefix.macip_addr.ip; + ip = hash_lookup(vpn->remote_ip_hash, &tmp); + if (ip == NULL) + return; + + listnode_delete(ip->macip_path_list, pi); + + if (ip->macip_path_list->count == 0) { + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); + hash_release(vpn->remote_ip_hash, ip); + XFREE(MTYPE_EVPN_REMOTE_IP, ip); + } +} + +static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn, + void (*func)(struct hash_bucket *, + void *), + void *arg) +{ + if (!evpn_resolve_overlay_index()) + return; + + hash_iterate(vpn->remote_ip_hash, func, arg); +} + +static void show_remote_ip_entry(struct hash_bucket *bucket, void *args) +{ + char buf[INET6_ADDRSTRLEN]; + char buf2[EVPN_ROUTE_STRLEN]; + struct prefix_evpn *evp; + + struct listnode *node = NULL; + struct bgp_path_info *pi = NULL; + struct vty *vty = (struct vty *)args; + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + + vty_out(vty, " Remote IP: %s\n", + ipaddr2str(&ip->addr, buf, sizeof(buf))); + vty_out(vty, " Linked MAC/IP routes:\n"); + for (ALL_LIST_ELEMENTS_RO(ip->macip_path_list, node, pi)) { + evp = (struct prefix_evpn *)&pi->net->p; + prefix2str(evp, buf2, sizeof(buf2)); + vty_out(vty, " %s\n", buf2); + } +} + +void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct vty *vty = (struct vty *)args; + + vty_out(vty, "VNI: %u\n", vpn->vni); + bgp_evpn_remote_ip_hash_iterate( + vpn, + (void (*)(struct hash_bucket *, void *))show_remote_ip_entry, + vty); + vty_out(vty, "\n"); +} + +static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket, + void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true); +} + +static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket, + void *args) +{ + struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data; + struct bgpevpn *vpn = (struct bgpevpn *)args; + + bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false); +} + +static unsigned int vni_svi_hash_key_make(const void *p) +{ + const struct bgpevpn *vpn = p; + + return jhash_1word(vpn->svi_ifindex, 0); +} + +static bool vni_svi_hash_cmp(const void *p1, const void *p2) +{ + const struct bgpevpn *vpn1 = p1; + const struct bgpevpn *vpn2 = p2; + + return (vpn1->svi_ifindex == vpn2->svi_ifindex); +} + +static struct bgpevpn *bgp_evpn_vni_svi_hash_lookup(struct bgp *bgp, + ifindex_t svi) +{ + struct bgpevpn *vpn; + struct bgpevpn tmp; + + memset(&tmp, 0, sizeof(struct bgpevpn)); + tmp.svi_ifindex = svi; + vpn = hash_lookup(bgp->vni_svi_hash, &tmp); + return vpn; +} + +static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn) +{ + if (vpn->svi_ifindex == 0) + return; + + hash_get(bgp->vni_svi_hash, vpn, hash_alloc_intern); +} + +static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp, + struct bgpevpn *vpn) +{ + if (vpn->svi_ifindex == 0) + return; + + hash_release(bgp->vni_svi_hash, vpn); +} + +void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args) +{ + struct bgpevpn *evpn = (struct bgpevpn *)bucket->data; + struct vty *vty = (struct vty *)args; + + vty_out(vty, "SVI: %u VNI: %u\n", evpn->svi_ifindex, evpn->vni); +} + +/* + * This function is called for a bgp_nexthop_cache entry when the nexthop is + * gateway IP overlay index. + * This function returns true if there is a remote MAC/IP route for the gateway + * IP in the EVI of the nexthop SVI. + */ +bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc) +{ + struct bgp *bgp_evpn = NULL; + struct bgpevpn *vpn = NULL; + struct evpn_remote_ip tmp; + struct prefix *p; + + if (!evpn_resolve_overlay_index()) + return false; + + if (!bnc->nexthop || bnc->nexthop->ifindex == 0) + return false; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return false; + + /* + * Gateway IP is resolved by nht over SVI interface. + * Use this SVI to find corresponding EVI(L2 context) + */ + vpn = bgp_evpn_vni_svi_hash_lookup(bgp_evpn, bnc->nexthop->ifindex); + if (!vpn) + return false; + + if (vpn->bgp_vrf != bnc->bgp) + return false; + + /* + * Check if the gateway IP is present in the EVI remote_ip_hash table + * which stores all the remote IP addresses received via MAC/IP routes + * in this EVI + */ + memset(&tmp, 0, sizeof(struct evpn_remote_ip)); + + p = &bnc->prefix; + if (p->family == AF_INET) { + tmp.addr.ipa_type = IPADDR_V4; + memcpy(&(tmp.addr.ipaddr_v4), &(p->u.prefix4), + sizeof(struct in_addr)); + } else if (p->family == AF_INET6) { + tmp.addr.ipa_type = IPADDR_V6; + memcpy(&(tmp.addr.ipaddr_v6), &(p->u.prefix6), + sizeof(struct in6_addr)); + } else + return false; + + if (hash_lookup(vpn->remote_ip_hash, &tmp) == NULL) + return false; + + return true; +} + +/* Resolve/Unresolve nexthops when a MAC/IP route is added/deleted */ +static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn, + struct ipaddr *addr, + bool resolve) +{ + afi_t afi; + struct prefix p; + struct bgp_nexthop_cache *bnc; + struct bgp_nexthop_cache_head *tree = NULL; + + if (!vpn->bgp_vrf || vpn->svi_ifindex == 0) + return; + + memset(&p, 0, sizeof(struct prefix)); + + if (addr->ipa_type == IPADDR_V4) { + afi = AFI_IP; + p.family = AF_INET; + memcpy(&(p.u.prefix4), &(addr->ipaddr_v4), + sizeof(struct in_addr)); + p.prefixlen = IPV4_MAX_BITLEN; + } else if (addr->ipa_type == IPADDR_V6) { + afi = AFI_IP6; + p.family = AF_INET6; + memcpy(&(p.u.prefix6), &(addr->ipaddr_v6), + sizeof(struct in6_addr)); + p.prefixlen = IPV6_MAX_BITLEN; + } else + return; + + tree = &vpn->bgp_vrf->nexthop_cache_table[afi]; + bnc = bnc_find(tree, &p, 0); + + if (!bnc || !bnc->is_evpn_gwip_nexthop) + return; + + if (!bnc->nexthop || bnc->nexthop->ifindex != vpn->svi_ifindex) + return; + + if (BGP_DEBUG(nht, NHT)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&bnc->prefix, buf, sizeof(buf)); + zlog_debug("%s(%u): vni %u mac/ip %s for NH %s", + vpn->bgp_vrf->name_pretty, vpn->tenant_vrf_id, + vpn->vni, (resolve ? "add" : "delete"), buf); + } + + /* + * MAC/IP route or SVI or tenant vrf being added to EVI. + * Set nexthop as valid only if it is already L3 reachable + */ + if (resolve && bnc->flags & BGP_NEXTHOP_EVPN_INCOMPLETE) { + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + evaluate_paths(bnc); + } + + /* MAC/IP route or SVI or tenant vrf being deleted from EVI */ + if (!resolve && bnc->flags & BGP_NEXTHOP_VALID) { + bnc->flags &= ~BGP_NEXTHOP_VALID; + bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + evaluate_paths(bnc); + } +} + +void bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket, + void *arg) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct bgp_dest *dest; + struct bgp_path_info *pi; + + bgp_evpn_remote_ip_hash_init(vpn); + + for (dest = bgp_table_top(vpn->route_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); +} + +void bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket, + void *arg) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + + bgp_evpn_remote_ip_hash_destroy(vpn); +} diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 83a6dd84c8..eec746e3be 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -63,14 +63,18 @@ static inline int advertise_type5_routes(struct bgp *bgp_vrf, if (!bgp_vrf->l3vni) return 0; - if (afi == AFI_IP && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) + if ((afi == AFI_IP) + && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) + || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)))) return 1; - if (afi == AFI_IP6 && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) + if ((afi == AFI_IP6) + && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) + || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)))) return 1; return 0; @@ -152,6 +156,14 @@ static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi) return true; } +static inline bool evpn_resolve_overlay_index(void) +{ + struct bgp *bgp = NULL; + + bgp = bgp_get_evpn(); + return bgp ? bgp->resolve_overlay_index : false; +} + extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p, struct attr *src_attr, afi_t afi, @@ -198,7 +210,8 @@ extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni); extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp); + struct in_addr mcast_grp, + ifindex_t svi_ifindex); extern void bgp_evpn_flood_control_change(struct bgp *bgp); extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); extern void bgp_evpn_cleanup(struct bgp *bgp); @@ -206,4 +219,15 @@ extern void bgp_evpn_init(struct bgp *bgp); extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); +extern void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, + void *args); +extern void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args); +extern bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc); +extern void +bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket, + void *arg); +extern void +bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket, + void *arg); + #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index b191840f63..4b90937f62 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -269,8 +269,10 @@ static int bgp_evpn_es_route_uninstall(struct bgp *bgp, struct bgp_evpn_es *es, parent_pi) break; - if (!pi) + if (!pi) { + bgp_dest_unlock_node(dest); return 0; + } /* Mark entry for deletion */ bgp_path_info_delete(dest, pi); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index debed9f68b..c46f34bf74 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -62,6 +62,7 @@ RB_PROTOTYPE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node, struct bgpevpn { vni_t vni; vrf_id_t tenant_vrf_id; + ifindex_t svi_ifindex; uint32_t flags; #define VNI_FLAG_CFGD 0x1 /* VNI is user configured */ #define VNI_FLAG_LIVE 0x2 /* VNI is "live" */ @@ -102,6 +103,15 @@ struct bgpevpn { struct list *import_rtl; struct list *export_rtl; + /* + * EVPN route that uses gateway IP overlay index as its nexthop + * needs to do a recursive lookup. + * A remote MAC/IP entry should be present for the gateway IP. + * Maintain a hash of the addresses received via remote MAC/IP routes + * for efficient gateway IP recursive lookup in this EVI + */ + struct hash *remote_ip_hash; + /* Route table for EVPN routes for * this VNI. */ struct bgp_table *route_table; @@ -178,6 +188,12 @@ struct bgp_evpn_info { bool is_anycast_mac; }; +/* This structure defines an entry in remote_ip_hash */ +struct evpn_remote_ip { + struct ipaddr addr; + struct list *macip_path_list; +}; + static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) { return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD)); @@ -612,7 +628,8 @@ extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni); extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, - struct in_addr mcast_grp); + struct in_addr mcast_grp, + ifindex_t svi_ifindex); extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn); extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni); extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 2a7c2ec853..190323859f 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -59,6 +59,17 @@ struct vni_walk_ctx { int detail; }; +int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx, + enum overlay_index_type *oly) +{ + *oly = OVERLAY_INDEX_TYPE_NONE; + if (argv_find(argv, argc, "gateway-ip", oly_idx)) { + if (oly) + *oly = OVERLAY_INDEX_GATEWAY_IP; + } + return 1; +} + static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt, json_object *json) { @@ -520,6 +531,9 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) else json_object_string_add(json, "advertiseSviMacIp", "Disabled"); + json_object_string_add( + json, "sviInterface", + ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id)); } else { vty_out(vty, "VNI: %d", vpn->vni); if (is_vni_live(vpn)) @@ -553,6 +567,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) else vty_out(vty, " Advertise-svi-macip : %s\n", "Disabled"); + vty_out(vty, " SVI interface : %s\n", + ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id)); } if (!json) @@ -2279,7 +2295,7 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni) /* tenant vrf will be updated when we get local_vni_add from * zebra */ - vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp); + vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp, 0); if (!vpn) { flog_err( EC_BGP_VNI, @@ -2418,6 +2434,10 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) { if (!json) vty_out(vty, "%% Network not in table\n"); + + if (dest) + bgp_dest_unlock_node(dest); + return; } @@ -2452,6 +2472,8 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); } + + bgp_dest_unlock_node(dest); } /* @@ -2488,6 +2510,10 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) { if (!json) vty_out(vty, "%% Network not in table\n"); + + if (dest) + bgp_dest_unlock_node(dest); + return; } @@ -2522,6 +2548,8 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); } + + bgp_dest_unlock_node(dest); } /* Disaplay EVPN routes for a ESI - VTY handler */ @@ -2592,6 +2620,10 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) { if (!json) vty_out(vty, "%% Network not in table\n"); + + if (dest) + bgp_dest_unlock_node(dest); + return; } @@ -2627,6 +2659,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); } + + bgp_dest_unlock_node(dest); } /* @@ -2660,14 +2694,18 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, return; table = bgp_dest_get_bgp_table_info(rd_dest); - if (table == NULL) + if (table == NULL) { + bgp_dest_unlock_node(rd_dest); return; + } if (json) { json_rd = json_object_new_object(); json_object_string_add(json_rd, "rd", rd_str); } + bgp_dest_unlock_node(rd_dest); + /* Display all prefixes with this RD. */ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { const struct prefix_evpn *evp = @@ -2878,6 +2916,8 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp, json_rd = NULL; } } + + bgp_dest_unlock_node(dest); } if (json) { @@ -3286,6 +3326,28 @@ static void evpn_unset_advertise_all_vni(struct bgp *bgp) bgp_evpn_cleanup_on_disable(bgp); } +/* Set resolve overlay index flag */ +static void bgp_evpn_set_unset_resolve_overlay_index(struct bgp *bgp, bool set) +{ + if (set == bgp->resolve_overlay_index) + return; + + if (set) { + bgp->resolve_overlay_index = true; + hash_iterate(bgp->vnihash, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_handle_resolve_overlay_index_set, + NULL); + } else { + hash_iterate( + bgp->vnihash, + (void (*)(struct hash_bucket *, void *)) + bgp_evpn_handle_resolve_overlay_index_unset, + NULL); + bgp->resolve_overlay_index = false; + } +} + /* * EVPN - use RFC8365 to auto-derive RT */ @@ -3784,10 +3846,11 @@ DEFUN_HIDDEN (no_bgp_evpn_advertise_vni_subnet, DEFUN (bgp_evpn_advertise_type5, bgp_evpn_advertise_type5_cmd, - "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]", + "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [gateway-ip] [route-map WORD]", "Advertise prefix routes\n" BGP_AFI_HELP_STR BGP_SAFI_HELP_STR + "advertise gateway IP overlay index\n" "route-map for filtering specific routes\n" "Name of the route map\n") { @@ -3799,9 +3862,14 @@ DEFUN (bgp_evpn_advertise_type5, safi_t safi = 0; int ret = 0; int rmap_changed = 0; + enum overlay_index_type oly = OVERLAY_INDEX_TYPE_NONE; + int idx_oly = 0; + bool adv_flag_changed = false; argv_find_and_parse_afi(argv, argc, &idx_afi, &afi); argv_find_and_parse_safi(argv, argc, &idx_safi, &safi); + argv_find_and_parse_oly_idx(argv, argc, &idx_oly, &oly); + ret = argv_find(argv, argc, "route-map", &idx_rmap); if (ret) { if (!bgp_vrf->adv_cmd_rmap[afi][safi].name) @@ -3826,39 +3894,149 @@ DEFUN (bgp_evpn_advertise_type5, return CMD_WARNING; } + if ((oly != OVERLAY_INDEX_TYPE_NONE) + && (oly != OVERLAY_INDEX_GATEWAY_IP)) { + vty_out(vty, "%%Unknown overlay-index type specified"); + return CMD_WARNING; + } + if (afi == AFI_IP) { + if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) + && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { + + /* + * this is the case for first time ever configuration + * adv ipv4 unicast is enabled for the first time. + * So no need to reset any flag + */ + if (oly == OVERLAY_INDEX_TYPE_NONE) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + else if (oly == OVERLAY_INDEX_GATEWAY_IP) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + } else if ((oly == OVERLAY_INDEX_TYPE_NONE) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))) { + + /* + * This is modify case from gateway-ip + * to no overlay index + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + } else if ((oly == OVERLAY_INDEX_GATEWAY_IP) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { + + /* + * This is modify case from no overlay index + * to gateway-ip + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); + } else { - /* if we are already advertising ipv4 prefix as type-5 - * nothing to do - */ - if (!rmap_changed && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) - return CMD_WARNING; - SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST); + /* + * Command is issued with the same option + * (no overlay index or gateway-ip) which was + * already configured. So nothing to do. + * However, route-map may have been modified. + * check if route-map has been modified. + * If not, return an error + */ + if (!rmap_changed) + return CMD_WARNING; + } } else { + if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) + && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) { + + /* + * this is the case for first time ever configuration + * adv ipv6 unicast is enabled for the first time. + * So no need to reset any flag + */ + if (oly == OVERLAY_INDEX_TYPE_NONE) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + else if (oly == OVERLAY_INDEX_GATEWAY_IP) + SET_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + } else if ((oly == OVERLAY_INDEX_TYPE_NONE) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))) { + + /* + * This is modify case from gateway-ip + * to no overlay index + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + } else if ((oly == OVERLAY_INDEX_GATEWAY_IP) + && (!CHECK_FLAG( + bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) { + + /* + * This is modify case from no overlay index + * to gateway-ip + */ + adv_flag_changed = true; + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); + } else { - /* if we are already advertising ipv6 prefix as type-5 - * nothing to do - */ - if (!rmap_changed && - CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) - return CMD_WARNING; - SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST); + /* + * Command is issued with the same option + * (no overlay index or gateway-ip) which was + * already configured. So nothing to do. + * However, route-map may have been modified. + * check if route-map has been modified. + * If not, return an error + */ + if (!rmap_changed) + return CMD_WARNING; + } } - if (rmap_changed) { + if ((rmap_changed) || (adv_flag_changed)) { + + /* If either of these are changed, then FRR needs to + * withdraw already advertised type5 routes. + */ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); - if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { - XFREE(MTYPE_ROUTE_MAP_NAME, - bgp_vrf->adv_cmd_rmap[afi][safi].name); - route_map_counter_decrement( + if (rmap_changed) { + if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp_vrf->adv_cmd_rmap[afi][safi].name); + route_map_counter_decrement( bgp_vrf->adv_cmd_rmap[afi][safi].map); - bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL; - bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL; + bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL; + bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL; + } } } @@ -3912,22 +4090,30 @@ DEFUN (no_bgp_evpn_advertise_type5, /* if we are not advertising ipv4 prefix as type-5 * nothing to do */ - if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) { + if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) || + (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) { bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST); + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST); + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP); } } else { /* if we are not advertising ipv6 prefix as type-5 * nothing to do */ - if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) { + if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) || + (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))){ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST); + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST); + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP); } } @@ -3977,6 +4163,23 @@ DEFPY (bgp_evpn_ead_evi_tx_disable, return CMD_SUCCESS; } +DEFPY (bgp_evpn_enable_resolve_overlay_index, + bgp_evpn_enable_resolve_overlay_index_cmd, + "[no$no] enable-resolve-overlay-index", + NO_STR + "Enable Recursive Resolution of type-5 route overlay index\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + + if (bgp != bgp_get_evpn()) { + vty_out(vty, "This command is only supported under EVPN VRF\n"); + return CMD_WARNING; + } + + bgp_evpn_set_unset_resolve_overlay_index(bgp, no ? false : true); + return CMD_SUCCESS; +} + DEFPY (bgp_evpn_advertise_pip_ip_mac, bgp_evpn_advertise_pip_ip_mac_cmd, "[no$no] advertise-pip [ip <A.B.C.D> [mac <X:X:X:X:X:X|X:X:X:X:X:X/M>]]", @@ -4220,6 +4423,61 @@ DEFUN(show_bgp_l2vpn_evpn_vni, return CMD_SUCCESS; } +DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_remote_ip_hash, + show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd, + "show bgp l2vpn evpn vni remote-ip-hash", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show VNI\n" + "Remote IP hash\n") +{ + struct bgp *bgp_evpn; + int idx = 0; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + hash_iterate(bgp_evpn->vnihash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_show_remote_ip_hash, + vty); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_svi_hash, + show_bgp_l2vpn_evpn_vni_svi_hash_cmd, + "show bgp l2vpn evpn vni-svi-hash", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show vni-svi-hash\n") +{ + struct bgp *bgp_evpn; + int idx = 0; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return CMD_WARNING; + + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + hash_iterate(bgp_evpn->vni_svi_hash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_show_vni_svi_hash, + vty); + + return CMD_SUCCESS; +} + DEFPY(show_bgp_l2vpn_evpn_es_evi, show_bgp_l2vpn_evpn_es_evi_cmd, "show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]", @@ -6061,6 +6319,9 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->evpn_info->advertise_svi_macip) vty_out(vty, " advertise-svi-ip\n"); + if (bgp->resolve_overlay_index) + vty_out(vty, " enable-resolve-overlay-index\n"); + if (bgp_mh_info->host_routes_use_l3nhg != BGP_EVPN_MH_USE_ES_L3NHG_DEF) { if (bgp_mh_info->host_routes_use_l3nhg) @@ -6107,21 +6368,40 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, vty_out(vty, " flooding disable\n"); if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) { + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) { if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name) vty_out(vty, " advertise ipv4 unicast route-map %s\n", bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name); else - vty_out(vty, " advertise ipv4 unicast\n"); + vty_out(vty, + " advertise ipv4 unicast\n"); + } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) { + if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name) + vty_out(vty, + " advertise ipv4 unicast gateway-ip route-map %s\n", + bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name); + else + vty_out(vty, " advertise ipv4 unicast gateway-ip\n"); } if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) { + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) { if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) - vty_out(vty, " advertise ipv6 unicast route-map %s\n", + vty_out(vty, + " advertise ipv6 unicast route-map %s\n", + bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name); + else + vty_out(vty, + " advertise ipv6 unicast\n"); + } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) { + if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) + vty_out(vty, + " advertise ipv6 unicast gateway-ip route-map %s\n", bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name); else - vty_out(vty, " advertise ipv6 unicast\n"); + vty_out(vty, " advertise ipv6 unicast gateway-ip\n"); } if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], @@ -6230,6 +6510,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &bgp_evpn_use_es_l3nhg_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_rx_disable_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_tx_disable_cmd); + install_element(BGP_EVPN_NODE, + &bgp_evpn_enable_resolve_overlay_index_cmd); /* test commands */ install_element(BGP_EVPN_NODE, &test_es_add_cmd); @@ -6241,6 +6523,8 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_svi_hash_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h index 33f6e4f1b6..137365ddbf 100644 --- a/bgpd/bgp_evpn_vty.h +++ b/bgpd/bgp_evpn_vty.h @@ -28,6 +28,10 @@ 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" +extern int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, + int *oly_idx, + enum overlay_index_type *oly); + /* Parse type from "type <ead|1|...>", return -1 on failure */ extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc); diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index bdab2ec36a..73ca9f07e0 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -89,8 +89,8 @@ int bgp_parse_fec_update(void) bgp_set_valid_label(&dest->local_label); } SET_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED); - bgp_dest_unlock_node(dest); bgp_process(bgp, dest, afi, safi); + bgp_dest_unlock_node(dest); return 1; } diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c index 3d7bc08ac5..02b7e64869 100644 --- a/bgpd/bgp_mac.c +++ b/bgpd/bgp_mac.c @@ -200,8 +200,8 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, AFI_L2VPN, SAFI_EVPN, &prd, p, label_pnt, num_labels, pi->addpath_rx_id ? 1 : 0, - pi->addpath_rx_id, pfx_buf, - sizeof(pfx_buf)); + pi->addpath_rx_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s skip update of %s marked as removed", peer->host, pfx_buf); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index eb85936f0f..196e56a937 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -144,3 +144,4 @@ DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie"); DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service"); DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id"); DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function"); +DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index c5ba371498..0021fa9c3d 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -142,4 +142,6 @@ DECLARE_MTYPE(BGP_SRV6_VPN); DECLARE_MTYPE(BGP_SRV6_SID); DECLARE_MTYPE(BGP_SRV6_FUNCTION); +DECLARE_MTYPE(EVPN_REMOTE_IP); + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c index 5189d7ba8f..ab22aee1ca 100644 --- a/bgpd/bgp_nb_config.c +++ b/bgpd/bgp_nb_config.c @@ -182,24 +182,52 @@ int bgp_router_destroy(struct nb_cb_destroy_args *args) for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) { if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) continue; - if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT) || - (bgp == bgp_get_evpn() && - (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) || - (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) { + if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) + || CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) + || (bgp == bgp_get_evpn() + && (CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) + || CHECK_FLAG( + tmp_bgp->af_flags + [AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) + || (tmp_bgp->vnihash + && hashcount(tmp_bgp->vnihash))) { snprintf( args->errmsg, args->errmsg_len, "Cannot delete default BGP instance. Dependent VRF instances exist\n"); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index c417dda398..e7bad42f97 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -835,6 +835,18 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bnc->metric, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); + vty_out(vty, "\n"); + bgp_show_nexthops_detail(vty, bgp, bnc); + } else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) { + vty_out(vty, + " %s overlay index unresolved [IGP metric %d], #paths %d", + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, + buf, sizeof(buf)), + bnc->metric, bnc->path_count); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); vty_out(vty, "\n"); bgp_show_nexthops_detail(vty, bgp, bnc); } else { @@ -844,6 +856,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); vty_out(vty, "\n"); if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) vty_out(vty, " Must be Connected\n"); diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index fe0a9646a0..16c2b6c65a 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -56,6 +56,10 @@ struct bgp_nexthop_cache { time_t last_update; uint16_t flags; +/* + * If the nexthop is EVPN gateway IP NH, VALID flag is set only if the nexthop + * is RIB reachable as well as MAC/IP is present + */ #define BGP_NEXTHOP_VALID (1 << 0) #define BGP_NEXTHOP_REGISTERED (1 << 1) #define BGP_NEXTHOP_CONNECTED (1 << 2) @@ -64,11 +68,29 @@ struct bgp_nexthop_cache { #define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5) #define BGP_NEXTHOP_LABELED_VALID (1 << 6) +/* + * This flag is added for EVPN gateway IP nexthops. + * If the nexthop is RIB reachable, but a MAC/IP is not yet + * resolved, this flag is set. + * Following table explains the combination of L3 and L2 reachability w.r.t. + * VALID and INCOMPLETE flags + * + * | MACIP resolved | MACIP unresolved + *----------------|----------------|------------------ + * L3 reachable | VALID = 1 | VALID = 0 + * | INCOMPLETE = 0 | INCOMPLETE = 1 + * ---------------|----------------|-------------------- + * L3 unreachable | VALID = 0 | VALID = 0 + * | INCOMPLETE = 0 | INCOMPLETE = 0 + */ +#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7) + uint16_t change_flags; #define BGP_NEXTHOP_CHANGED (1 << 0) #define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) #define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) +#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3) /* Back pointer to the cache tree this entry belongs to. */ struct bgp_nexthop_cache_head *tree; @@ -79,6 +101,11 @@ struct bgp_nexthop_cache { LIST_HEAD(path_list, bgp_path_info) paths; unsigned int path_count; struct bgp *bgp; + + /* This flag is set to TRUE for a bnc that is gateway IP overlay index + * nexthop. + */ + bool is_evpn_gwip_nexthop; }; extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 4b4a3716e6..742ef217d9 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -53,7 +53,6 @@ static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, int is_bgp_static_route); static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, int is_bgp_static_route); -static void evaluate_paths(struct bgp_nexthop_cache *bnc); static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p); static int bgp_nht_ifp_initial(struct thread *thread); @@ -244,6 +243,9 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, } } + if (pi && is_route_parent_evpn(pi)) + bnc->is_evpn_gwip_nexthop = true; + if (is_bgp_static_route) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); @@ -331,7 +333,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, return 1; else if (safi == SAFI_UNICAST && pi && pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra - && pi->extra->num_labels) { + && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) { return bgp_isvalid_labeled_nexthop(bnc); } else return (bgp_isvalid_nexthop(bnc)); @@ -387,6 +389,7 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, struct nexthop *nhlist_head = NULL; struct nexthop *nhlist_tail = NULL; int i; + bool evpn_resolved = false; bnc->last_update = bgp_clock(); bnc->change_flags = 0; @@ -417,7 +420,8 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, if (!bnc->nexthop_num) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); - bnc->flags |= BGP_NEXTHOP_VALID; + if (!bnc->is_evpn_gwip_nexthop) + bnc->flags |= BGP_NEXTHOP_VALID; bnc->metric = nhr->metric; bnc->nexthop_num = nhr->nexthop_num; @@ -488,7 +492,40 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, } bnc_nexthop_free(bnc); bnc->nexthop = nhlist_head; + + /* + * Gateway IP nexthop is L3 reachable. Mark it as + * BGP_NEXTHOP_VALID only if it is recursively resolved with a + * remote EVPN RT-2. + * Else, mark it as BGP_NEXTHOP_EVPN_INCOMPLETE. + * When its mapping with EVPN RT-2 is established, unset + * BGP_NEXTHOP_EVPN_INCOMPLETE and set BGP_NEXTHOP_VALID. + */ + if (bnc->is_evpn_gwip_nexthop) { + evpn_resolved = bgp_evpn_is_gateway_ip_resolved(bnc); + + if (BGP_DEBUG(nht, NHT)) { + char buf2[PREFIX2STR_BUFFER]; + + prefix2str(&bnc->prefix, buf2, sizeof(buf2)); + zlog_debug( + "EVPN gateway IP %s recursive MAC/IP lookup %s", + buf2, + (evpn_resolved ? "successful" + : "failed")); + } + + if (evpn_resolved) { + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED; + } else { + bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE; + bnc->flags &= ~BGP_NEXTHOP_VALID; + } + } } else { + bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE; bnc->flags &= ~BGP_NEXTHOP_VALID; bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; bnc->nexthop_num = nhr->nexthop_num; @@ -694,6 +731,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE); } } } @@ -888,7 +926,7 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, * RETURNS: * void. */ -static void evaluate_paths(struct bgp_nexthop_cache *bnc) +void evaluate_paths(struct bgp_nexthop_cache *bnc) { struct bgp_dest *dest; struct bgp_path_info *path; @@ -942,16 +980,18 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) * In case of unicast routes that were imported from vpn * and that have labels, they are valid only if there are * nexthops with labels + * + * If the nexthop is EVPN gateway-IP, + * do not check for a valid label. */ bool bnc_is_valid_nexthop = false; bool path_valid = false; - if (safi == SAFI_UNICAST && - path->sub_type == BGP_ROUTE_IMPORTED && - path->extra && - path->extra->num_labels) { - + if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED + && path->extra && path->extra->num_labels + && (path->attr->evpn_overlay.type + != OVERLAY_INDEX_GATEWAY_IP)) { bnc_is_valid_nexthop = bgp_isvalid_labeled_nexthop(bnc) ? true : false; } else { diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index 9268b225ca..e006aa4469 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -90,6 +90,7 @@ extern void bgp_nht_register_nexthops(struct bgp *bgp); */ extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer); extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer); +extern void evaluate_paths(struct bgp_nexthop_cache *bnc); /* APIs for setting up and allocating L3 nexthop group ids */ extern uint32_t bgp_l3nhg_id_alloc(void); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 959a87d583..4637cef3eb 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3451,23 +3451,6 @@ struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance, return new; } -static void overlay_index_update(struct attr *attr, - union gw_addr *gw_ip) -{ - if (!attr) - return; - if (gw_ip == NULL) { - struct bgp_route_evpn eo; - - memset(&eo, 0, sizeof(eo)); - bgp_attr_set_evpn_overlay(attr, &eo); - } else { - struct bgp_route_evpn eo = {.gw_ip = *gw_ip}; - - bgp_attr_set_evpn_overlay(attr, &eo); - } -} - static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path, union gw_addr *gw_ip) { @@ -3650,6 +3633,11 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (has_valid_label) assert(label != NULL); + /* Update overlay index of the attribute */ + if (afi == AFI_L2VPN && evpn) + memcpy(&attr->evpn_overlay, evpn, + sizeof(struct bgp_route_evpn)); + /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ if (!soft_reconfig @@ -3825,12 +3813,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, goto filtered; } - /* Update Overlay Index */ - if (afi == AFI_L2VPN) { - overlay_index_update(&new_attr, - evpn == NULL ? NULL : &evpn->gw_ip); - } - /* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following * condition : * Suppress fib is enabled @@ -3865,10 +3847,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, && (!has_valid_label || memcmp(&(bgp_path_info_extra_get(pi))->label, label, num_labels * sizeof(mpls_label_t)) - == 0) - && (overlay_index_equal( - afi, pi, - evpn == NULL ? NULL : &evpn->gw_ip))) { + == 0)) { if (get_active_bdc_from_pi(pi, afi, safi) && peer->sort == BGP_PEER_EBGP && CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { @@ -3876,7 +3855,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); @@ -3902,7 +3881,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s rcvd %s...duplicate ignored", @@ -3929,8 +3908,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, evpn, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s rcvd %s, flapped quicker than processing", peer->host, pfx_buf); @@ -3943,7 +3922,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, - addpath_id, pfx_buf, + addpath_id, evpn, pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); } @@ -4216,8 +4195,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, evpn, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd %s", peer->host, pfx_buf); } @@ -4248,11 +4227,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } - /* Update Overlay Index */ - if (afi == AFI_L2VPN) { - overlay_index_update(new->attr, - evpn == NULL ? NULL : &evpn->gw_ip); - } /* Nexthop reachability check. */ if (((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) @@ -4362,8 +4336,8 @@ filtered: } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, evpn, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s", peer->host, pfx_buf, reason); } @@ -4447,8 +4421,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug( "%s withdrawing route %s not in adj-in", peer->host, pfx_buf); @@ -4467,8 +4441,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* Logging. */ if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s rcvd UPDATE about %s -- withdrawn", peer->host, pfx_buf); } @@ -4488,8 +4462,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } else if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, - addpath_id ? 1 : 0, addpath_id, pfx_buf, - sizeof(pfx_buf)); + addpath_id ? 1 : 0, addpath_id, NULL, + pfx_buf, sizeof(pfx_buf)); zlog_debug("%s Can't find the route %s", peer->host, pfx_buf); } @@ -6182,6 +6156,7 @@ void bgp_static_add(struct bgp *bgp) struct bgp_table *table; struct bgp_static *bgp_static; + SET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS); FOREACH_AFI_SAFI (afi, safi) for (dest = bgp_table_top(bgp->route[afi][safi]); dest; dest = bgp_route_next(dest)) { @@ -6208,6 +6183,7 @@ void bgp_static_add(struct bgp *bgp) safi); } } + UNSET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS); } /* Called from bgp_delete(). Delete all static routes from the BGP @@ -9865,6 +9841,11 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, json_nexthop_global = json_object_new_object(); } + if (safi == SAFI_EVPN) { + if (!json_paths) + vty_out(vty, " Route %pRN", bn); + } + if (path->extra) { char tag_buf[30]; @@ -9876,12 +9857,8 @@ 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 %pFX", - (struct prefix_evpn *) - bgp_dest_get_prefix(bn)); if (tag_buf[0] != '\0') vty_out(vty, " VNI %s", tag_buf); - vty_out(vty, "\n"); } else { if (tag_buf[0]) json_object_string_add(json_path, "VNI", @@ -9928,6 +9905,27 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } } + if (safi == SAFI_EVPN + && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) { + char gwip_buf[INET6_ADDRSTRLEN]; + + if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)&bn->p)) + inet_ntop(AF_INET, &attr->evpn_overlay.gw_ip.ipv4, + gwip_buf, sizeof(gwip_buf)); + else + inet_ntop(AF_INET6, &attr->evpn_overlay.gw_ip.ipv6, + gwip_buf, sizeof(gwip_buf)); + + if (json_paths) + json_object_string_add(json_path, "gatewayIP", + gwip_buf); + else + vty_out(vty, " Gateway IP %s", gwip_buf); + } + + if (safi == SAFI_EVPN) + vty_out(vty, "\n"); + /* Line1 display AS-path, Aggregator */ if (attr->aspath) { if (json_paths) { diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 806a771cfb..9a7b7b3cf2 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1084,6 +1084,71 @@ static const struct route_map_rule_cmd route_match_evpn_rd_cmd = { route_match_rd_free }; +static enum route_map_cmd_result_t +route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object) +{ + struct ipaddr *gw_ip = rule; + struct bgp_path_info *path; + struct prefix_evpn *evp; + + if (prefix->family != AF_EVPN) + return RMAP_OKAY; + + evp = (struct prefix_evpn *)prefix; + if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE) + return RMAP_OKAY; + + if ((is_evpn_prefix_ipaddr_v4(evp) && IPADDRSZ(gw_ip) != 4) + || (is_evpn_prefix_ipaddr_v6(evp) && IPADDRSZ(gw_ip) != 16)) + return RMAP_OKAY; + + path = object; + + /* Set gateway-ip value. */ + path->attr->evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP; + memcpy(&path->attr->evpn_overlay.gw_ip, &gw_ip->ip.addr, + IPADDRSZ(gw_ip)); + + return RMAP_OKAY; +} + +/* + * Route map `evpn gateway-ip' compile function. + * Given string is converted to struct ipaddr structure + */ +static void *route_set_evpn_gateway_ip_compile(const char *arg) +{ + struct ipaddr *gw_ip = NULL; + int ret; + + gw_ip = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct ipaddr)); + + ret = str2ipaddr(arg, gw_ip); + if (ret < 0) { + XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip); + return NULL; + } + return gw_ip; +} + +/* Free route map's compiled `evpn gateway_ip' value. */ +static void route_set_evpn_gateway_ip_free(void *rule) +{ + struct ipaddr *gw_ip = rule; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip); +} + +/* Route map commands for set evpn gateway-ip ipv4. */ +struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv4_cmd = { + "evpn gateway-ip ipv4", route_set_evpn_gateway_ip, + route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free}; + +/* Route map commands for set evpn gateway-ip ipv6. */ +struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv6_cmd = { + "evpn gateway-ip ipv6", route_set_evpn_gateway_ip, + route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free}; + /* Route map commands for VRF route leak with source vrf matching */ static enum route_map_cmd_result_t route_match_vrl_source_vrf(void *rule, const struct prefix *prefix, @@ -4067,6 +4132,148 @@ DEFUN_YANG (no_match_evpn_rd, return nb_cli_apply_changes(vty, NULL); } +DEFUN_YANG (set_evpn_gw_ip_ipv4, + set_evpn_gw_ip_ipv4_cmd, + "set evpn gateway-ip ipv4 A.B.C.D", + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']"; + char xpath_value[XPATH_MAXLEN]; + + ret = str2sockunion(argv[4]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (su.sin.sin_addr.s_addr == 0 + || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) { + vty_out(vty, + "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4", + xpath); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_evpn_gw_ip_ipv4, + no_set_evpn_gw_ip_ipv4_cmd, + "no set evpn gateway-ip ipv4 A.B.C.D", + NO_STR + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']"; + + ret = str2sockunion(argv[5]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (su.sin.sin_addr.s_addr == 0 + || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) { + vty_out(vty, + "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (set_evpn_gw_ip_ipv6, + set_evpn_gw_ip_ipv6_cmd, + "set evpn gateway-ip ipv6 X:X::X:X", + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv6 address\n" + "Gateway IP address in IPv6 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']"; + char xpath_value[XPATH_MAXLEN]; + + ret = str2sockunion(argv[4]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) + || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) { + vty_out(vty, + "%% Gateway IP cannot be a linklocal or multicast address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6", + xpath); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_evpn_gw_ip_ipv6, + no_set_evpn_gw_ip_ipv6_cmd, + "no set evpn gateway-ip ipv6 X:X::X:X", + NO_STR + SET_STR + EVPN_HELP_STR + "Set gateway IP for prefix advertisement route\n" + "IPv4 address\n" + "Gateway IP address in IPv4 format\n") +{ + int ret; + union sockunion su; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']"; + + ret = str2sockunion(argv[5]->arg, &su); + if (ret < 0) { + vty_out(vty, "%% Malformed gateway IP\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) + || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) { + vty_out(vty, + "%% Gateway IP cannot be a linklocal or multicast address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + DEFPY_YANG(match_vrl_source_vrf, match_vrl_source_vrf_cmd, "match source-vrf NAME$vrf_name", @@ -6124,6 +6331,8 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_evpn_default_route_cmd); route_map_install_match(&route_match_vrl_source_vrf_cmd); + route_map_install_set(&route_set_evpn_gateway_ip_ipv4_cmd); + route_map_install_set(&route_set_evpn_gateway_ip_ipv6_cmd); route_map_install_set(&route_set_table_id_cmd); route_map_install_set(&route_set_srte_color_cmd); route_map_install_set(&route_set_ip_nexthop_cmd); @@ -6167,6 +6376,10 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_match_evpn_rd_cmd); install_element(RMAP_NODE, &match_evpn_default_route_cmd); install_element(RMAP_NODE, &no_match_evpn_default_route_cmd); + install_element(RMAP_NODE, &set_evpn_gw_ip_ipv4_cmd); + install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv4_cmd); + install_element(RMAP_NODE, &set_evpn_gw_ip_ipv6_cmd); + install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv6_cmd); install_element(RMAP_NODE, &match_vrl_source_vrf_cmd); install_element(RMAP_NODE, &no_match_vrl_source_vrf_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index b165c5d0ee..1254591b87 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -372,6 +372,20 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { } }, { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy, + } + }, + { .xpath = NULL, }, } diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index a15f521513..f0e492eb61 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -130,6 +130,14 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_mod int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( + struct nb_cb_destroy_args *args); #ifdef __cplusplus } diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index ff08c16a82..e541d117be 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -2637,3 +2637,107 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_spec { return lib_route_map_entry_set_destroy(args); } + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4 + */ +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "evpn gateway-ip ipv4"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv4", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6 + */ +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "evpn gateway-ip ipv6"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv6", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index 61a6467ab6..4baa730c8d 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -689,12 +689,12 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], dest = bgp_node_lookup(bgp->rib[AFI_IP][SAFI_UNICAST], (struct prefix *)addr); if (dest) { - bgp_dest_unlock_node(dest); - for (path = bgp_dest_get_bgp_path_info(dest); path; path = path->next) if (sockunion_same(&path->peer->su, &su)) return path; + + bgp_dest_unlock_node(dest); } } else { offset = name + v->namelen; diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index bb0c95e32f..18829aa747 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -829,6 +829,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) struct bgp_path_info_extra tmp_pie; tmp_attr = *pi->attr; + tmp_attr.aspath = attr.aspath; prep_for_rmap_apply(&tmp_pi, &tmp_pie, dest, pi, pi->peer, &tmp_attr); @@ -838,11 +839,12 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) bgp_dest_get_prefix(dest), &tmp_pi); if (ret == RMAP_DENYMATCH) { + /* The aspath belongs to 'attr' */ + tmp_attr.aspath = NULL; bgp_attr_flush(&tmp_attr); continue; } else { new_attr = bgp_attr_intern(&tmp_attr); - new_attr->aspath = attr.aspath; subgroup_announce_reset_nhop( (peer_cap_enhe(peer, afi, safi) @@ -883,7 +885,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) /* If default route is present in the local RIB, advertise the * route */ - if (dest != NULL) { + if (dest) { for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) @@ -895,6 +897,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) dest, subgrp, &attr, pi); } + bgp_dest_unlock_node(dest); } } else { if (!CHECK_FLAG(subgrp->sflags, @@ -907,7 +910,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) * clear adj_out for the 0.0.0.0/0 prefix in the BGP * table. */ - if (dest != NULL) { + if (dest) { /* Remove the adjacency for the previously * advertised default route */ @@ -923,6 +926,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) /* Free allocated information. */ adj_free(adj); } + bgp_dest_unlock_node(dest); } /* Advertise the default route */ diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 6418decd16..038ef4f798 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -862,6 +862,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, label_pnt, num_labels, addpath_encode, addpath_tx_id, + &adv->baa->attr->evpn_overlay, pfx_buf, sizeof(pfx_buf)); zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s", subgrp->update_group->id, subgrp->id, @@ -1031,7 +1032,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0, addpath_encode, addpath_tx_id, - pfx_buf, sizeof(pfx_buf)); + NULL, pfx_buf, sizeof(pfx_buf)); zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable", subgrp->update_group->id, subgrp->id, pfx_buf); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c2c114d2c9..5d3176537b 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1058,9 +1058,19 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, * connected routes leaked into a VRF. */ if (is_evpn) { - api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); - api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + + /* + * If the nexthop is EVPN overlay index gateway IP, + * treat the nexthop as NEXTHOP_TYPE_IPV4 + * Else, mark the nexthop as onlink. + */ + if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) + api_nh->type = NEXTHOP_TYPE_IPV4; + else { + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + } } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) { api_nh->type = NEXTHOP_TYPE_IFINDEX; @@ -1085,9 +1095,19 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->vrf_id = nh_bgp->vrf_id; if (is_evpn) { - api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; - SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); - api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + + /* + * If the nexthop is EVPN overlay index gateway IP, + * treat the nexthop as NEXTHOP_TYPE_IPV4 + * Else, mark the nexthop as onlink. + */ + if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) + api_nh->type = NEXTHOP_TYPE_IPV6; + else { + api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; + } } else if (nh_othervrf) { if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) { api_nh->type = NEXTHOP_TYPE_IFINDEX; @@ -1392,8 +1412,13 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, api_nh->label_num = 1; api_nh->labels[0] = label; } - memcpy(&api_nh->rmac, &(mpinfo->attr->rmac), - sizeof(struct ethaddr)); + + if (is_evpn + && mpinfo->attr->evpn_overlay.type + != OVERLAY_INDEX_GATEWAY_IP) + memcpy(&api_nh->rmac, &(mpinfo->attr->rmac), + sizeof(struct ethaddr)); + api_nh->weight = nh_weight; if (mpinfo->extra @@ -2444,8 +2469,6 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, if (!dest) return -1; - bgp_dest_unlock_node(dest); - switch (note) { case ZAPI_ROUTE_INSTALLED: new_select = NULL; @@ -2474,6 +2497,8 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, flog_err(EC_BGP_INVALID_ROUTE, "selected route %pRN not found", dest); + + bgp_dest_unlock_node(dest); return -1; } } @@ -2504,6 +2529,8 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, __func__, dest); break; } + + bgp_dest_unlock_node(dest); return 0; } @@ -2805,6 +2832,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) struct in_addr vtep_ip = {INADDR_ANY}; vrf_id_t tenant_vrf_id = VRF_DEFAULT; struct in_addr mcast_grp = {INADDR_ANY}; + ifindex_t svi_ifindex = 0; s = zclient->ibuf; vni = stream_getl(s); @@ -2812,6 +2840,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) vtep_ip.s_addr = stream_get_ipv4(s); stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); mcast_grp.s_addr = stream_get_ipv4(s); + stream_get(&svi_ifindex, s, sizeof(ifindex_t)); } bgp = bgp_lookup_by_vrf_id(vrf_id); @@ -2819,16 +2848,17 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) return 0; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s", - (cmd == ZEBRA_VNI_ADD) ? "add" : "del", - vrf_id_to_name(vrf_id), vni, - vrf_id_to_name(tenant_vrf_id)); + zlog_debug( + "Rx VNI %s VRF %s VNI %u tenant-vrf %s SVI ifindex %u", + (cmd == ZEBRA_VNI_ADD) ? "add" : "del", + vrf_id_to_name(vrf_id), vni, + vrf_id_to_name(tenant_vrf_id), svi_ifindex); if (cmd == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( bgp, vni, vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id, - tenant_vrf_id, mcast_grp); + tenant_vrf_id, mcast_grp, svi_ifindex); else return bgp_evpn_local_vni_del(bgp, vni); } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b18cd4ca77..776f4b0a21 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -510,16 +510,18 @@ struct bgp { uint16_t af_flags[AFI_MAX][SAFI_MAX]; #define BGP_CONFIG_DAMPENING (1 << 0) /* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */ -#define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1) -#define BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST (1 << 2) -#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 3) -#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 4) +#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST (1 << 1) +#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP (1 << 2) +#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST (1 << 3) +#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP (1 << 4) +#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 5) +#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 6) /* import/export between address families */ -#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 5) -#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 6) +#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 7) +#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 8) /* vrf-route leaking flags */ -#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 7) -#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 8) +#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9) +#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10) /* BGP per AF peer count */ uint32_t af_peer_count[AFI_MAX][SAFI_MAX]; @@ -638,6 +640,14 @@ struct bgp { /* EVI hash table */ struct hash *vnihash; + /* + * VNI hash table based on SVI ifindex as its key. + * We use SVI ifindex as key to lookup a VNI table for gateway IP + * overlay index recursive lookup. + * For this purpose, a hashtable is added which optimizes this lookup. + */ + struct hash *vni_svi_hash; + /* EVPN enable - advertise gateway macip routes */ int advertise_gw_macip; @@ -683,6 +693,15 @@ struct bgp { /* Hash table of EVPN nexthops maintained per-tenant-VRF */ struct hash *evpn_nh_table; + /* + * Flag resolve_overlay_index is used for recursive resolution + * procedures for EVPN type-5 route's gateway IP overlay index. + * When this flag is set, we build remote-ip-hash for + * all L2VNIs and resolve overlay index nexthops using this hash. + * Overlay index nexthops remain unresolved if this flag is not set. + */ + bool resolve_overlay_index; + /* vrf flags */ uint32_t vrf_flags; #define BGP_VRF_AUTO (1 << 0) diff --git a/buildtest.sh b/buildtest.sh index b4b02d10bf..eeae82fc44 100755 --- a/buildtest.sh +++ b/buildtest.sh @@ -4,7 +4,7 @@ # builds some git commit of FRR in some different configurations # usage: buildtest.sh [commit [configurations...]] -basecfg="--prefix=/usr --enable-user=frr --enable-group=frr --enable-vty-group=frr --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/frr --enable-exampledir=/etc/frr/samples --localstatedir=/var/run/frr --libdir=/usr/lib64/frr --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-pimd --enable-werror" +basecfg="--prefix=/usr --enable-user=frr --enable-group=frr --enable-vty-group=frr --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/frr --localstatedir=/var/run/frr --libdir=/usr/lib64/frr --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-pimd --enable-werror" configs_base="gcc|$basecfg" diff --git a/configure.ac b/configure.ac index 96bacc9ed4..0ea209bbfa 100644 --- a/configure.ac +++ b/configure.ac @@ -114,15 +114,6 @@ AC_PATH_PROG([PERL], [perl]) PKG_PROG_PKG_CONFIG dnl default is to match previous behavior -exampledir=${sysconfdir} -AC_ARG_ENABLE([exampledir], - AS_HELP_STRING([--enable-exampledir], - [specify alternate directory for examples]), - exampledir="$enableval",) -dnl XXX add --exampledir to autoconf standard directory list somehow -AC_SUBST([exampledir]) - -dnl default is to match previous behavior pkgsrcrcdir="" AC_ARG_ENABLE([pkgsrcrcdir], AS_HELP_STRING([--enable-pkgsrcrcdir], @@ -663,8 +654,6 @@ AC_ARG_ENABLE([irdp], AS_HELP_STRING([--disable-irdp], [disable IRDP server support in zebra (enabled by default if supported)])) AC_ARG_ENABLE([capabilities], AS_HELP_STRING([--disable-capabilities], [disable using POSIX capabilities])) -AC_ARG_ENABLE([rusage], - AS_HELP_STRING([--disable-rusage], [disable using getrusage])) AC_ARG_ENABLE([gcc_ultra_verbose], AS_HELP_STRING([--enable-gcc-ultra-verbose], [enable ultra verbose GCC warnings])) AC_ARG_ENABLE([backtrace], @@ -794,7 +783,6 @@ fi if test "$enable_datacenter" = "yes" ; then AC_DEFINE([HAVE_DATACENTER], [1], [Compile extensions for a DataCenter]) - AC_MSG_WARN([The --enable-datacenter compile time option is deprecated. Please modify the init script to pass -F datacenter to the daemons instead.]) DFLT_NAME="datacenter" else DFLT_NAME="traditional" @@ -2241,6 +2229,10 @@ AC_CHECK_DECL([CLOCK_MONOTONIC], AC_DEFINE([HAVE_CLOCK_MONOTONIC], [1], [Have monotonic clock]) ], [AC_MSG_RESULT([no])], [FRR_INCLUDES]) +AC_CHECK_DECL([CLOCK_THREAD_CPUTIME_ID], [ + AC_DEFINE([HAVE_CLOCK_THREAD_CPUTIME_ID], [1], [Have cpu-time clock]) +], [AC_MSG_RESULT([no])], [FRR_INCLUDES]) + AC_SEARCH_LIBS([clock_nanosleep], [rt], [ AC_DEFINE([HAVE_CLOCK_NANOSLEEP], [1], [Have clock_nanosleep()]) ]) @@ -2693,7 +2685,6 @@ make : ${MAKE-make} linker flags : ${LDFLAGS} ${SAN_FLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} state file directory : ${frr_statedir} config file directory : `eval echo \`echo ${sysconfdir}\`` -example directory : `eval echo \`echo ${exampledir}\`` module directory : ${CFG_MODULE} script directory : ${CFG_SCRIPT} user to run as : ${enable_user} @@ -2707,6 +2698,16 @@ vici socket path : ${vici_socket} The above user and group must have read/write access to the state file directory and to the config files in the config file directory." +if test -n "$enable_datacenter"; then + AC_MSG_WARN([The --enable-datacenter compile time option is deprecated. Please modify the init script to pass -F datacenter to the daemons instead.]) +fi +if test -n "$enable_time_check"; then + AC_MSG_WARN([The --enable-time-check compile time option is deprecated. Please use the service cputime-stats configuration option instead.]) +fi +if test -n "$enable_cpu_time"; then + AC_MSG_WARN([The --enable-cpu-time compile time option is deprecated. Please use the service cputime-warning NNN configuration option instead.]) +fi + if test "$enable_doc" != "no" -a "$frr_py_mod_sphinx" = "false"; then AC_MSG_WARN([sphinx is missing but required to build documentation]) fi diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index 5d3be492de..7a7af42119 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -172,7 +172,6 @@ an example.) --enable-user=frr \ --enable-group=frr \ --enable-vty-group=frrvty \ - --disable-exampledir \ --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index 8d0aea943c..93b9993a38 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -67,7 +67,6 @@ an example.) --enable-group=frr \ --enable-vty-group=frrvty \ --enable-systemd=yes \ - --disable-exampledir \ --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst index 77fe489358..65c93286b7 100644 --- a/doc/developer/building-frr-for-centos8.rst +++ b/doc/developer/building-frr-for-centos8.rst @@ -60,7 +60,6 @@ an example.) --enable-group=frr \ --enable-vty-group=frrvty \ --enable-systemd=yes \ - --disable-exampledir \ --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst index 51dd07c42a..475e0303fc 100644 --- a/doc/developer/building-frr-for-debian8.rst +++ b/doc/developer/building-frr-for-debian8.rst @@ -57,7 +57,6 @@ an example.) cd frr ./bootstrap.sh ./configure \ - --enable-exampledir=/usr/share/doc/frr/examples/ \ --localstatedir=/var/run/frr \ --sbindir=/usr/lib/frr \ --sysconfdir=/etc/frr \ diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst index 919b010314..1981127b3d 100644 --- a/doc/developer/building-frr-for-debian9.rst +++ b/doc/developer/building-frr-for-debian9.rst @@ -44,7 +44,6 @@ an example.) cd frr ./bootstrap.sh ./configure \ - --enable-exampledir=/usr/share/doc/frr/examples/ \ --localstatedir=/var/opt/frr \ --sbindir=/usr/lib/frr \ --sysconfdir=/etc/frr \ diff --git a/doc/developer/building-frr-for-netbsd6.rst b/doc/developer/building-frr-for-netbsd6.rst index e50d11130a..a78f8b3c2f 100644 --- a/doc/developer/building-frr-for-netbsd6.rst +++ b/doc/developer/building-frr-for-netbsd6.rst @@ -64,7 +64,6 @@ an example) export CPPFLAGS="-I/usr/pkg/include" ./configure \ --sysconfdir=/usr/pkg/etc/frr \ - --enable-exampledir=/usr/pkg/share/examples/frr \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ --localstatedir=/var/run/frr \ --enable-multipath=64 \ diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst index 32d1145edc..a52ece19a1 100644 --- a/doc/developer/building-frr-for-netbsd7.rst +++ b/doc/developer/building-frr-for-netbsd7.rst @@ -55,7 +55,6 @@ an example) export CPPFLAGS="-I/usr/pkg/include" ./configure \ --sysconfdir=/usr/pkg/etc/frr \ - --enable-exampledir=/usr/pkg/share/examples/frr \ --enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \ --localstatedir=/var/run/frr \ --enable-multipath=64 \ diff --git a/doc/developer/include-compile.rst b/doc/developer/include-compile.rst index 0ff0ae3ffe..d9fa260221 100644 --- a/doc/developer/include-compile.rst +++ b/doc/developer/include-compile.rst @@ -15,7 +15,6 @@ obtained by running ``./configure -h``. The options shown below are examples. ./configure \ --prefix=/usr \ --includedir=\${prefix}/include \ - --enable-exampledir=\${prefix}/share/doc/frr/examples \ --bindir=\${prefix}/bin \ --sbindir=\${prefix}/lib/frr \ --libdir=\${prefix}/lib/frr \ diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index ba03aa9045..18317cd33c 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -221,7 +221,6 @@ for ``master`` branch: --prefix=/usr/lib/frr --sysconfdir=/etc/frr \ --localstatedir=/var/run/frr \ --sbindir=/usr/lib/frr --bindir=/usr/lib/frr \ - --enable-exampledir=/usr/lib/frr/examples \ --with-moduledir=/usr/lib/frr/modules \ --enable-multipath=0 --enable-rtadv \ --enable-tcp-zebra --enable-fpm --enable-pimd \ diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 0db2361296..92357bec83 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -58,7 +58,13 @@ Basic Config Commands .. clicmd:: hostname HOSTNAME - Set hostname of the router. + Set hostname of the router. It is only for current ``vtysh``, it will not be + saved to any configuration file even with ``write file``. + +.. clicmd:: domainname DOMAINNAME + + Set domainname of the router. It is only for current ``vtysh``, it will not + be saved to any configuration file even with ``write file``. .. clicmd:: domainname DOMAINNAME @@ -74,6 +80,39 @@ Basic Config Commands Set enable password. The ``no`` form of the command deletes the enable password. +.. clicmd:: service cputime-stats + + Collect CPU usage statistics for individual FRR event handlers and CLI + commands. This is enabled by default and can be disabled if the extra + overhead causes a noticeable slowdown on your system. + + Disabling these statistics will also make the + :clicmd:`service cputime-warning (1-4294967295)` limit non-functional. + +.. clicmd:: service cputime-warning (1-4294967295) + + Warn if the CPU usage of an event handler or CLI command exceeds the + specified limit (in milliseconds.) Such warnings are generally indicative + of some routine in FRR mistakenly blocking/hogging the processing loop and + should be reported as a FRR bug. + + The default limit is 5 seconds (i.e. 5000), but this can be changed by the + deprecated ``--enable-time-check=...`` compile-time option. + + This command has no effect if :clicmd:`service cputime-stats` is disabled. + +.. clicmd:: service walltime-warning (1-4294967295) + + Warn if the total wallclock time spent handling an event or executing a CLI + command exceeds the specified limit (in milliseconds.) This includes time + spent waiting for I/O or other tasks executing and may produce excessive + warnings if the system is overloaded. (This may still be useful to + provide an immediate sign that FRR is not operating correctly due to + externally caused starvation.) + + The default limit is 5 seconds as above, including the same deprecated + ``--enable-time-check=...`` compile-time option. + .. clicmd:: log trap LEVEL These commands are deprecated and are present only for historical @@ -174,11 +213,17 @@ Basic Config Commands is used to start the daemon then this command is turned on by default and cannot be turned off and the [no] form of the command is dissallowed. -.. clicmd:: log-filter WORD [DAEMON] +.. clicmd:: log filtered-file [FILENAME [LEVEL]] + + Configure a destination file for filtered logs with the + :clicmd:`log filter-text WORD` command. + +.. clicmd:: log filter-text WORD This command forces logs to be filtered on a specific string. A log message will only be printed if it matches on one of the filters in the log-filter - table. Can be daemon independent. + table. The filter only applies to file logging targets configured with + :clicmd:`log filtered-file [FILENAME [LEVEL]]`. .. note:: @@ -187,10 +232,15 @@ Basic Config Commands Log filters prevent this but you should still expect a small performance hit due to filtering each of all those logs. -.. clicmd:: log-filter clear [DAEMON] + .. note:: + + This setting is not saved to ``frr.conf`` and not shown in + :clicmd:`show running-config`. It is intended for ephemeral debugging + purposes only. + +.. clicmd:: clear log filter-text - This command clears all current filters in the log-filter table. Can be - daemon independent. + This command clears all current filters in the log-filter table. .. clicmd:: log immediate-mode diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 4e78900e8d..7f23f7a633 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2696,6 +2696,115 @@ remote VTEP. Note that you should not enable both the advertise-svi-ip and the advertise-default-gw at the same time. +.. _bgp-evpn-overlay-index-gateway-ip: + +EVPN Overlay Index Gateway IP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Draft https://tools.ietf.org/html/draft-ietf-bess-evpn-prefix-advertisement-11 +explains the use of overlay indexes for recursive route resolution for EVPN +type-5 route. + +We support gateway IP overlay index. +A gateway IP, advertised with EVPN prefix route, is used to find an EVPN MAC/IP +route with its IP field same as the gateway IP. This MAC/IP entry provides the +nexthop VTEP and the tunnel information required for the VxLAN encapsulation. + +Functionality: + +:: + + . +--------+ BGP +--------+ BGP +--------+ +--------+ + SN1 | | IPv4 | | EVPN | | | | + ======+ Host1 +------+ PE1 +------+ PE2 +------+ Host2 + + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + +Consider above topology where prefix SN1 is connected behind host1. Host1 +advertises SN1 to PE1 over BGP IPv4 session. PE1 advertises SN1 to PE2 using +EVPN type-5 route with host1 IP as the gateway IP. PE1 also advertises +Host1 MAC/IP as type-2 route which is used to resolve host1 gateway IP. + +PE2 receives this type-5 route and imports it into the vrf based on route +targets. BGP prefix imported into the vrf uses gateway IP as its BGP nexthop. +This route is installed into zebra if following conditions are satisfied: +1. Gateway IP nexthop is L3 reachable. +2. PE2 has received EVPN type-2 route with IP field set to gateway IP. + +Topology requirements: +1. This feature is supported for asymmetric routing model only. While + sending packets to SN1, ingress PE (PE2) performs routing and + egress PE (PE1) performs only bridging. +2. This feature supports only tratitional(non vlan-aware) bridge model. Bridge + interface associated with L2VNI is an L3 interface. i.e., this interface is + configured with an address in the L2VNI subnet. Note that the gateway IP + should also have an address in the same subnet. +3. As this feature works in asymmetric routing model, all L2VNIs and corresponding + VxLAN and bridge interfaces should be present at all the PEs. +4. L3VNI configuration is required to generate and import EVPN type-5 routes. + L3VNI VxLAN and bridge interfaces also should be present. + +A PE can use one of the following two mechanisms to advertise an EVPN type-5 +route with gateway IP. + +1. CLI to add gateway IP while generating EVPN type-5 route from a BGP IPv4/IPv6 +prefix: + +.. index:: advertise <ipv4|ipv6> unicast [gateway-ip] +.. clicmd:: [no] advertise <ipv4|ipv6> unicast [gateway-ip] + +When this CLI is configured for a BGP vrf under L2VPN EVPN address family, EVPN +type-5 routes are generated for BGP prefixes in the vrf. Nexthop of the BGP +prefix becomes the gateway IP of the corresponding type-5 route. + +If the above command is configured without the "gateway-ip" keyword, type-5 +routes are generated without overlay index. + +2. Add gateway IP to EVPN type-5 route using a route-map: + +.. index:: set evpn gateway-ip <ipv4|ipv6> <addr> +.. clicmd:: [no] set evpn gateway-ip <ipv4|ipv6> <addr> + +When route-map with above set clause is applied as outbound policy in BGP, it +will set the gateway-ip in EVPN type-5 NLRI. + +Example configuration: + +.. code-block:: frr + + router bgp 100 + neighbor 192.168.0.1 remote-as 101 + ! + address-family ipv4 l2vpn evpn + neighbor 192.168.0.1 route-map RMAP out + exit-address-family + ! + route-map RMAP permit 10 + set evpn gateway-ip 10.0.0.1 + set evpn gateway-ip 10::1 + +A PE that receives a type-5 route with gateway IP overlay index should have +"enable-resolve-overlay-index" configuration enabled to recursively resolve the +overlay index nexthop and install the prefix into zebra. + +.. index:: enable-resolve-overlay-index +.. clicmd:: [no] enable-resolve-overlay-index + +Example configuration: + +.. code-block:: frr + + router bgp 65001 + bgp router-id 192.168.100.1 + no bgp ebgp-requires-policy + neighbor 10.0.1.2 remote-as 65002 + ! + address-family l2vpn evpn + neighbor 10.0.1.2 activate + advertise-all-vni + enable-resolve-overlay-index + exit-address-family + ! + EVPN Multihoming ^^^^^^^^^^^^^^^^ diff --git a/doc/user/installation.rst b/doc/user/installation.rst index f7e45a6231..63254555d9 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -199,6 +199,10 @@ options from the list below. .. option:: --enable-datacenter + This option is deprecated as it is superseded by the `-F` (profile) command + line option which allows adjusting the setting at startup rather than + compile time. + Enable system defaults to work as if in a Data Center. See defaults.h for what is changed by this configure option. @@ -343,20 +347,17 @@ options from the list below. .. option:: --enable-time-check XXX - When this is enabled with a XXX value in microseconds, any thread that - runs for over this value will cause a warning to be issued to the log. - If you do not specify any value or don't include this option then - the default time is 5 seconds. If --disable-time-check is specified - then no warning is issued for any thread run length. + This option is deprecated as it was replaced by the + :clicmd:`service cputime-stats` CLI command, which may be adjusted at + runtime rather than being a compile-time setting. See there for further + detail. .. option:: --disable-cpu-time - Disable cpu process accounting, this command also disables the `show thread cpu` - command. If this option is disabled, --enable-time-check is ignored. This - disabling of cpu time effectively means that the getrusage call is skipped. - Since this is a process switch into the kernel, systems with high FRR - load might see improvement in behavior. Be aware that `show thread cpu` - is considered a good data gathering tool from the perspective of developers. + This option is deprecated as it was replaced by the + :clicmd:`service cputime-warning NNN` CLI command, which may be adjusted at + runtime rather than being a compile-time setting. See there for further + detail. .. option:: --enable-pcreposix @@ -561,7 +562,6 @@ the options you chose: ./configure \ --prefix=/usr \ - --enable-exampledir=/usr/share/doc/frr/examples/ \ --localstatedir=/var/run/frr \ --sbindir=/usr/lib/frr \ --sysconfdir=/etc/frr \ diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index d26062e2b9..1e8c66ab2e 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -70,6 +70,13 @@ OSPF6 router Use this command to control the maximum number of parallel routes that OSPFv3 can support. The default is 64. +.. clicmd:: write-multiplier (1-100) + + Use this command to tune the amount of work done in the packet read and + write threads before relinquishing control. The parameter is the number + of packets to process before returning. The default value of this parameter + is 20. + .. _ospf6-area: @@ -83,7 +90,7 @@ NSSA Support in OSPFv3 ======================= The configuration of NSSA areas in OSPFv3 is supported using the CLI command -area A.B.C.D nssa in ospf6 router configuration mode. +``area A.B.C.D nssa`` in ospf6 router configuration mode. The following functionalities are implemented as per RFC 3101: 1. Advertising Type-7 LSA into NSSA area when external route is redistributed @@ -91,7 +98,7 @@ The following functionalities are implemented as per RFC 3101: 2. Processing Type-7 LSA received from neighbor and installing route in the route table 3. Support for NSSA ABR functionality which is generating Type-5 LSA when - backbone area is configured. Currently translation od TYpe-7 LSA to Type-5 LSA + backbone area is configured. Currently translation of Type-7 LSA to Type-5 LSA is enabled by default. 4. Support for NSSA Translator functionality when there are multiple NSSA ABR in an area diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 80ab279596..692ce8c1b2 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -299,6 +299,13 @@ To start OSPF process you have to specify the OSPF router. a specific destination. The upper limit may differ if you change the value of MULTIPATH_NUM during compilation. The default is MULTIPATH_NUM (64). +.. clicmd:: write-multiplier (1-100) + + Use this command to tune the amount of work done in the packet read and + write threads before relinquishing control. The parameter is the number + of packets to process before returning. The defult value of this parameter + is 20. + .. _ospf-area: Areas diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c index 3a978cae33..47de929fc3 100644 --- a/eigrpd/eigrp_cli.c +++ b/eigrpd/eigrp_cli.c @@ -919,6 +919,8 @@ eigrp_cli_init(void) install_element(EIGRP_NODE, &eigrp_neighbor_cmd); install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd); + vrf_cmd_init(NULL, &eigrpd_privs); + install_node(&eigrp_interface_node); if_cmd_init(); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 2622c5b51b..4a01c728f0 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -111,6 +111,28 @@ int isis_instance_is_type_modify(struct nb_cb_modify_args *args) return NB_OK; } +struct sysid_iter { + struct area_addr *addr; + bool same; +}; + +static int sysid_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct sysid_iter *iter = arg; + struct area_addr addr; + const char *net; + + net = yang_dnode_get_string(dnode, NULL); + addr.addr_len = dotformat2buff(addr.area_addr, net); + + if (memcmp(GETSYSID(iter->addr), GETSYSID((&addr)), ISIS_SYS_ID_LEN)) { + iter->same = false; + return YANG_ITER_STOP; + } + + return YANG_ITER_CONTINUE; +} + /* * XPath: /frr-isisd:isis/instance/area-address */ @@ -119,14 +141,12 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) struct isis_area *area; struct area_addr addr, *addrr = NULL, *addrp = NULL; struct listnode *node; + struct sysid_iter iter; uint8_t buff[255]; const char *net_title = yang_dnode_get_string(args->dnode, NULL); switch (args->event) { case NB_EV_VALIDATE: - area = nb_running_get_entry(args->dnode, NULL, false); - if (area == NULL) - return NB_ERR_VALIDATION; addr.addr_len = dotformat2buff(buff, net_title); memcpy(addr.area_addr, buff, addr.addr_len); if (addr.area_addr[addr.addr_len - 1] != 0) { @@ -135,15 +155,18 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) "nsel byte (last byte) in area address must be 0"); return NB_ERR_VALIDATION; } - if (area->isis->sysid_set) { - /* Check that the SystemID portions match */ - if (memcmp(area->isis->sysid, GETSYSID((&addr)), - ISIS_SYS_ID_LEN)) { - snprintf( - args->errmsg, args->errmsg_len, - "System ID must not change when defining additional area addresses"); - return NB_ERR_VALIDATION; - } + + iter.addr = &addr; + iter.same = true; + + yang_dnode_iterate(sysid_iter_cb, &iter, args->dnode, + "../area-address"); + + if (!iter.same) { + snprintf( + args->errmsg, args->errmsg_len, + "System ID must not change when defining additional area addresses"); + return NB_ERR_VALIDATION; } break; case NB_EV_PREPARE: @@ -2360,14 +2383,14 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify( int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args) { struct isis_area *area; + const char *vrfname; switch (args->event) { case NB_EV_VALIDATE: - area = nb_running_get_entry(args->dnode, NULL, false); - if (area == NULL || area->isis == NULL) - return NB_ERR_VALIDATION; + vrfname = yang_dnode_get_string( + lyd_parent(lyd_parent(args->dnode)), "./vrf"); - if (area->isis->vrf_id != VRF_DEFAULT) { + if (strcmp(vrfname, VRF_DEFAULT_NAME)) { snprintf(args->errmsg, args->errmsg_len, "LDP-Sync only runs on Default VRF"); return NB_ERR_VALIDATION; @@ -2404,14 +2427,15 @@ int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args) { struct isis_area *area; uint16_t holddown; + const char *vrfname; switch (args->event) { case NB_EV_VALIDATE: - area = nb_running_get_entry(args->dnode, NULL, false); - if (area == NULL || area->isis == NULL) - return NB_ERR_VALIDATION; + vrfname = yang_dnode_get_string( + lyd_parent(lyd_parent(lyd_parent(args->dnode))), + "./vrf"); - if (area->isis->vrf_id != VRF_DEFAULT) { + if (strcmp(vrfname, VRF_DEFAULT_NAME)) { snprintf(args->errmsg, args->errmsg_len, "LDP-Sync only runs on Default VRF"); return NB_ERR_VALIDATION; @@ -3180,26 +3204,14 @@ int lib_interface_isis_mpls_ldp_sync_modify(struct nb_cb_modify_args *args) struct isis_circuit *circuit; struct ldp_sync_info *ldp_sync_info; bool ldp_sync_enable; - struct interface *ifp; + const char *vrfname; switch (args->event) { case NB_EV_VALIDATE: - ifp = nb_running_get_entry( - lyd_parent(lyd_parent(lyd_parent(args->dnode))), NULL, - false); - if (ifp == NULL) - return NB_ERR_VALIDATION; - if (if_is_loopback(ifp)) { - snprintf(args->errmsg, args->errmsg_len, - "LDP-Sync does not run on loopback interface"); - return NB_ERR_VALIDATION; - } - - circuit = nb_running_get_entry(args->dnode, NULL, false); - if (circuit == NULL || circuit->area == NULL) - break; - - if (circuit->isis->vrf_id != VRF_DEFAULT) { + vrfname = yang_dnode_get_string( + lyd_parent(lyd_parent(lyd_parent(args->dnode))), + "./vrf"); + if (strcmp(vrfname, VRF_DEFAULT_NAME)) { snprintf(args->errmsg, args->errmsg_len, "LDP-Sync only runs on Default VRF"); return NB_ERR_VALIDATION; @@ -3236,27 +3248,14 @@ int lib_interface_isis_mpls_holddown_modify(struct nb_cb_modify_args *args) struct isis_circuit *circuit; struct ldp_sync_info *ldp_sync_info; uint16_t holddown; - struct interface *ifp; + const char *vrfname; switch (args->event) { case NB_EV_VALIDATE: - - ifp = nb_running_get_entry( - lyd_parent(lyd_parent(lyd_parent(args->dnode))), NULL, - false); - if (ifp == NULL) - return NB_ERR_VALIDATION; - if (if_is_loopback(ifp)) { - snprintf(args->errmsg, args->errmsg_len, - "LDP-Sync does not run on loopback interface"); - return NB_ERR_VALIDATION; - } - - circuit = nb_running_get_entry(args->dnode, NULL, false); - if (circuit == NULL || circuit->area == NULL) - break; - - if (circuit->isis->vrf_id != VRF_DEFAULT) { + vrfname = yang_dnode_get_string( + lyd_parent(lyd_parent(lyd_parent(args->dnode))), + "./vrf"); + if (strcmp(vrfname, VRF_DEFAULT_NAME)) { snprintf(args->errmsg, args->errmsg_len, "LDP-Sync only runs on Default VRF"); return NB_ERR_VALIDATION; @@ -3282,26 +3281,14 @@ int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args) { struct isis_circuit *circuit; struct ldp_sync_info *ldp_sync_info; - struct interface *ifp; + const char *vrfname; switch (args->event) { case NB_EV_VALIDATE: - ifp = nb_running_get_entry( - lyd_parent(lyd_parent(lyd_parent(args->dnode))), NULL, - false); - if (ifp == NULL) - return NB_ERR_VALIDATION; - if (if_is_loopback(ifp)) { - snprintf(args->errmsg, args->errmsg_len, - "LDP-Sync does not run on loopback interface"); - return NB_ERR_VALIDATION; - } - - circuit = nb_running_get_entry(args->dnode, NULL, false); - if (circuit == NULL || circuit->area == NULL) - break; - - if (circuit->isis->vrf_id != VRF_DEFAULT) { + vrfname = yang_dnode_get_string( + lyd_parent(lyd_parent(lyd_parent(args->dnode))), + "./vrf"); + if (strcmp(vrfname, VRF_DEFAULT_NAME)) { snprintf(args->errmsg, args->errmsg_len, "LDP-Sync only runs on Default VRF"); return NB_ERR_VALIDATION; diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index a19fcc240f..2c1b6e932f 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -187,7 +187,7 @@ DEFUN (show_lsp_flooding, vty_out(vty, "Area %s:\n", area->area_tag ? area->area_tag : "null"); if (lspid) { - lsp = lsp_for_arg(head, lspid, isis); + lsp = lsp_for_sysid(head, lspid, isis); if (lsp) lsp_print_flooding(vty, lsp, isis); continue; diff --git a/isisd/isisd.c b/isisd/isisd.c index 7f56903fc9..6c1308af0a 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -716,6 +716,8 @@ void isis_vrf_init(void) { vrf_init(isis_vrf_new, isis_vrf_enable, isis_vrf_disable, isis_vrf_delete, isis_vrf_enable); + + vrf_cmd_init(NULL, &isisd_privs); } void isis_terminate() @@ -2168,21 +2170,21 @@ DEFUN(show_isis_summary, show_isis_summary_cmd, return CMD_SUCCESS; } -struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, - struct isis *isis) +struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str, + struct isis *isis) { char sysid[255] = {0}; - uint8_t number[3]; + uint8_t number[3] = {0}; const char *pos; uint8_t lspid[ISIS_SYS_ID_LEN + 2] = {0}; struct isis_dynhn *dynhn; struct isis_lsp *lsp = NULL; - if (!argv) + if (!sysid_str) return NULL; /* - * extract fragment and pseudo id from the string argv + * extract fragment and pseudo id from the string sysid_str * in the forms: * (a) <systemid/hostname>.<pseudo-id>-<framenent> or * (b) <systemid/hostname>.<pseudo-id> or @@ -2190,10 +2192,10 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, * Where systemid is in the form: * xxxx.xxxx.xxxx */ - if (argv) - strlcpy(sysid, argv, sizeof(sysid)); - if (argv && strlen(argv) > 3) { - pos = argv + strlen(argv) - 3; + strlcpy(sysid, sysid_str, sizeof(sysid)); + + if (strlen(sysid_str) > 3) { + pos = sysid_str + strlen(sysid_str) - 3; if (strncmp(pos, "-", 1) == 0) { memcpy(number, ++pos, 2); lspid[ISIS_SYS_ID_LEN + 1] = @@ -2206,14 +2208,13 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, memcpy(number, ++pos, 2); lspid[ISIS_SYS_ID_LEN] = (uint8_t)strtol((char *)number, NULL, 16); - sysid[pos - argv - 1] = '\0'; + sysid[pos - sysid_str - 1] = '\0'; } } /* - * Try to find the lsp-id if the argv - * string is in - * the form + * Try to find the lsp-id if the sysid_str + * is in the form * hostname.<pseudo-id>-<fragment> */ if (sysid2buff(lspid, sysid)) { @@ -2231,15 +2232,15 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, int level, struct lspdb_head *lspdb, - const char *argv, int ui_level) + const char *sysid_str, int ui_level) { struct isis_lsp *lsp; int lsp_count; if (lspdb_count(lspdb) > 0) { - lsp = lsp_for_arg(lspdb, argv, area->isis); + lsp = lsp_for_sysid(lspdb, sysid_str, area->isis); - if (lsp != NULL || argv == NULL) { + if (lsp != NULL || sysid_str == NULL) { vty_out(vty, "IS-IS Level-%d link-state database:\n", level + 1); @@ -2255,7 +2256,7 @@ void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, else lsp_print(lsp, vty, area->dynhostname, area->isis); - } else if (argv == NULL) { + } else if (sysid_str == NULL) { lsp_count = lsp_print_all(vty, lspdb, ui_level, area->dynhostname, area->isis); @@ -2265,7 +2266,7 @@ void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, } } -static void show_isis_database_common(struct vty *vty, const char *argv, +static void show_isis_database_common(struct vty *vty, const char *sysid_str, int ui_level, struct isis *isis) { struct listnode *node; @@ -2281,7 +2282,7 @@ static void show_isis_database_common(struct vty *vty, const char *argv, for (level = 0; level < ISIS_LEVELS; level++) show_isis_database_lspdb(vty, area, level, - &area->lspdb[level], argv, + &area->lspdb[level], sysid_str, ui_level); } } @@ -2301,8 +2302,8 @@ static void show_isis_database_common(struct vty *vty, const char *argv, * [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ] * [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ] */ -static int show_isis_database(struct vty *vty, const char *argv, int ui_level, - const char *vrf_name, bool all_vrf) +static int show_isis_database(struct vty *vty, const char *sysid_str, + int ui_level, const char *vrf_name, bool all_vrf) { struct listnode *node; struct isis *isis; @@ -2310,14 +2311,15 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level, if (vrf_name) { if (all_vrf) { for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - show_isis_database_common(vty, argv, ui_level, - isis); + show_isis_database_common(vty, sysid_str, + ui_level, isis); return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); if (isis) - show_isis_database_common(vty, argv, ui_level, isis); + show_isis_database_common(vty, sysid_str, ui_level, + isis); } return CMD_SUCCESS; diff --git a/isisd/isisd.h b/isisd/isisd.h index b2c9af55b7..60dcf066dd 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -274,8 +274,8 @@ void isis_area_destroy(struct isis_area *area); void isis_filter_update(struct access_list *access); void isis_prefix_list_update(struct prefix_list *plist); void print_debug(struct vty *, int, int); -struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, - struct isis *isis); +struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str, + struct isis *isis); void isis_area_invalidate_routes(struct isis_area *area, int levels); void isis_area_verify_routes(struct isis_area *area); diff --git a/lib/command.c b/lib/command.c index 7be54907ed..e00d84a051 100644 --- a/lib/command.c +++ b/lib/command.c @@ -434,6 +434,36 @@ static int config_write_host(struct vty *vty) } log_config_write(vty); + /* print disable always, but enable only if default is flipped + * => prep for future removal of compile-time knob + */ + if (!cputime_enabled) + vty_out(vty, "no service cputime-stats\n"); +#ifdef EXCLUDE_CPU_TIME + else + vty_out(vty, "service cputime-stats\n"); +#endif + + if (!cputime_threshold) + vty_out(vty, "no service cputime-warning\n"); +#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000 + else /* again, always print non-default */ +#else + else if (cputime_threshold != 5000000) +#endif + vty_out(vty, "service cputime-warning %lu\n", + cputime_threshold); + + if (!walltime_threshold) + vty_out(vty, "no service walltime-warning\n"); +#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000 + else /* again, always print non-default */ +#else + else if (walltime_threshold != 5000000) +#endif + vty_out(vty, "service walltime-warning %lu\n", + walltime_threshold); + if (host.advanced) vty_out(vty, "service advanced-vty\n"); diff --git a/lib/compiler.h b/lib/compiler.h index e805eb8be4..970ed297fc 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -347,7 +347,11 @@ extern "C" { #define PRIx64 "Lx" #else /* !_FRR_ATTRIBUTE_PRINTFRR */ +#ifdef __NetBSD__ +#define PRINTFRR(a, b) __attribute__((format(gnu_syslog, a, b))) +#else #define PRINTFRR(a, b) __attribute__((format(printf, a, b))) +#endif /* frr-format plugin is C-only for now, so no point in doing these shenanigans * for C++... (also they can break some C++ stuff...) diff --git a/lib/log_filter.c b/lib/log_filter.c index 721e57a628..f01497dead 100644 --- a/lib/log_filter.c +++ b/lib/log_filter.c @@ -111,13 +111,14 @@ int zlog_filter_dump(char *buf, size_t max_size) return len; } -static int search_buf(const char *buf) +static int search_buf(const char *buf, size_t len) { char *found = NULL; frr_with_mutex(&logfilterlock) { for (int i = 0; i < zlog_filter_count; i++) { - found = strstr(buf, zlog_filters[i]); + found = memmem(buf, len, zlog_filters[i], + strlen(zlog_filters[i])); if (found != NULL) return 0; } @@ -131,12 +132,15 @@ static void zlog_filterfile_fd(struct zlog_target *zt, struct zlog_msg *msgs[], { struct zlog_msg *msgfilt[nmsgs]; size_t i, o = 0; + const char *text; + size_t text_len; for (i = 0; i < nmsgs; i++) { - if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG - && search_buf(zlog_msg_text(msgs[i], NULL)) < 0) - continue; - + if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG) { + text = zlog_msg_text(msgs[i], &text_len); + if (search_buf(text, text_len) < 0) + continue; + } msgfilt[o++] = msgs[i]; } diff --git a/lib/log_vty.c b/lib/log_vty.c index 9dbf216d31..cbb8de8976 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -34,6 +34,7 @@ #define ZLOG_MAXLVL(a, b) MAX(a, b) DEFINE_HOOK(zlog_rotate, (), ()); +DEFINE_HOOK(zlog_cli_show, (struct vty * vty), (vty)); static const int log_default_lvl = LOG_DEBUG; @@ -57,7 +58,7 @@ static struct zlog_cfg_filterfile zt_filterfile = { }, }; -static const char *zlog_progname; +const char *zlog_progname; static const char *zlog_protoname; static const struct facility_map { @@ -94,7 +95,14 @@ static const char * const zlog_priority[] = { "notifications", "informational", "debugging", NULL, }; -static const char *facility_name(int facility) +const char *zlog_priority_str(int priority) +{ + if (priority > LOG_DEBUG) + return "???"; + return zlog_priority[priority]; +} + +const char *facility_name(int facility) { const struct facility_map *fm; @@ -104,7 +112,7 @@ static const char *facility_name(int facility) return ""; } -static int facility_match(const char *str) +int facility_match(const char *str) { const struct facility_map *fm; @@ -194,6 +202,8 @@ DEFUN_NOSH (show_logging, vty_out(vty, "Record priority: %s\n", (zt_file.record_priority ? "enabled" : "disabled")); vty_out(vty, "Timestamp precision: %d\n", zt_file.ts_subsec); + + hook_call(zlog_cli_show, vty); return CMD_SUCCESS; } @@ -588,8 +598,9 @@ DEFUN (no_config_log_filterfile, DEFPY (log_filter, log_filter_cmd, - "[no] log-filter WORD$filter", + "[no] log filter-text WORD$filter", NO_STR + "Logging control\n" FILTER_LOG_STR "String to filter by\n") { @@ -616,8 +627,9 @@ DEFPY (log_filter, /* Clear all log filters */ DEFPY (log_filter_clear, log_filter_clear_cmd, - "clear log-filter", + "clear log filter-text", CLEAR_STR + "Logging control\n" FILTER_LOG_STR) { zlog_filter_clear(); @@ -627,8 +639,9 @@ DEFPY (log_filter_clear, /* Show log filter */ DEFPY (show_log_filter, show_log_filter_cmd, - "show log-filter", + "show logging filter-text", SHOW_STR + "Show current logging configuration\n" FILTER_LOG_STR) { char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = ""; diff --git a/lib/log_vty.h b/lib/log_vty.h index f0fb7d3dba..db46b3cb5b 100644 --- a/lib/log_vty.h +++ b/lib/log_vty.h @@ -34,9 +34,14 @@ extern void log_config_write(struct vty *vty); extern int log_level_match(const char *s); extern void log_show_syslog(struct vty *vty); +extern int facility_match(const char *str); +extern const char *facility_name(int facility); + DECLARE_HOOK(zlog_rotate, (), ()); extern void zlog_rotate(void); +DECLARE_HOOK(zlog_cli_show, (struct vty * vty), (vty)); + #ifdef __cplusplus } #endif diff --git a/lib/routemap.h b/lib/routemap.h index 5b6b64eaeb..4d76ae1536 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -366,6 +366,10 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:ipv4-vpn-address")) #define IS_SET_BGP_IPV4_NH(A) \ (strmatch(A, "frr-bgp-route-map:set-ipv4-nexthop")) +#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(A) \ + (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv4")) +#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(A) \ + (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv6")) /* Prototypes. */ extern void route_map_init(void); diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index e11b9eea74..bf982cfa2b 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1248,6 +1248,16 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/frr-bgp-route-map:ipv4-nexthop")); + } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(action)) { + vty_out(vty, " set evpn gateway-ip ipv4 %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4")); + } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(action)) { + vty_out(vty, " set evpn gateway-ip ipv6 %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6")); } } diff --git a/lib/subdir.am b/lib/subdir.am index 4015c67ea8..90301d800a 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -168,6 +168,7 @@ clippy_scan += \ lib/northbound_cli.c \ lib/plist.c \ lib/routemap_cli.c \ + lib/thread.c \ lib/vty.c \ # end @@ -463,7 +464,7 @@ endif SUFFIXES += .xref %.xref: % $(CLIPPY) - $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py $(XRELFO_FLAGS) -o $@ $< + $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py $(WERROR) $(XRELFO_FLAGS) -o $@ $< # dependencies added in python/makefile.py frr.xref: diff --git a/lib/thread.c b/lib/thread.c index 3af89fad5a..835aa38115 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -90,7 +90,22 @@ static struct list *masters; static void thread_free(struct thread_master *master, struct thread *thread); +#ifndef EXCLUDE_CPU_TIME +#define EXCLUDE_CPU_TIME 0 +#endif +#ifndef CONSUMED_TIME_CHECK +#define CONSUMED_TIME_CHECK 0 +#endif + +bool cputime_enabled = !EXCLUDE_CPU_TIME; +unsigned long cputime_threshold = CONSUMED_TIME_CHECK; +unsigned long walltime_threshold = CONSUMED_TIME_CHECK; + /* CLI start ---------------------------------------------------------------- */ +#ifndef VTYSH_EXTRACT_PL +#include "lib/thread_clippy.c" +#endif + static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a) { int size = sizeof(a->func); @@ -120,7 +135,6 @@ static void cpu_record_hash_free(void *a) XFREE(MTYPE_THREAD_STATS, hist); } -#ifndef EXCLUDE_CPU_TIME static void vty_out_cpu_thread_history(struct vty *vty, struct cpu_thread_history *a) { @@ -187,6 +201,14 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) struct thread_master *m; struct listnode *ln; + if (!cputime_enabled) + vty_out(vty, + "\n" + "Collecting CPU time statistics is currently disabled. Following statistics\n" + "will be zero or may display data from when collection was enabled. Use the\n" + " \"service cputime-stats\" command to start collecting data.\n" + "\nCounters and wallclock times are always maintained and should be accurate.\n"); + memset(&tmp, 0, sizeof(tmp)); tmp.funcname = "TOTAL"; tmp.types = filter; @@ -236,7 +258,6 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) if (tmp.total_calls > 0) vty_out_cpu_thread_history(vty, &tmp); } -#endif static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[]) { @@ -306,7 +327,6 @@ static uint8_t parse_filter(const char *filterstr) return filter; } -#ifndef EXCLUDE_CPU_TIME DEFUN_NOSH (show_thread_cpu, show_thread_cpu_cmd, "show thread cpu [FILTER]", @@ -331,7 +351,61 @@ DEFUN_NOSH (show_thread_cpu, cpu_record_print(vty, filter); return CMD_SUCCESS; } -#endif + +DEFPY (service_cputime_stats, + service_cputime_stats_cmd, + "[no] service cputime-stats", + NO_STR + "Set up miscellaneous service\n" + "Collect CPU usage statistics\n") +{ + cputime_enabled = !no; + return CMD_SUCCESS; +} + +DEFPY (service_cputime_warning, + service_cputime_warning_cmd, + "[no] service cputime-warning (1-4294967295)", + NO_STR + "Set up miscellaneous service\n" + "Warn for tasks exceeding CPU usage threshold\n" + "Warning threshold in milliseconds\n") +{ + if (no) + cputime_threshold = 0; + else + cputime_threshold = cputime_warning * 1000; + return CMD_SUCCESS; +} + +ALIAS (service_cputime_warning, + no_service_cputime_warning_cmd, + "no service cputime-warning", + NO_STR + "Set up miscellaneous service\n" + "Warn for tasks exceeding CPU usage threshold\n") + +DEFPY (service_walltime_warning, + service_walltime_warning_cmd, + "[no] service walltime-warning (1-4294967295)", + NO_STR + "Set up miscellaneous service\n" + "Warn for tasks exceeding total wallclock threshold\n" + "Warning threshold in milliseconds\n") +{ + if (no) + walltime_threshold = 0; + else + walltime_threshold = walltime_warning * 1000; + return CMD_SUCCESS; +} + +ALIAS (service_walltime_warning, + no_service_walltime_warning_cmd, + "no service walltime-warning", + NO_STR + "Set up miscellaneous service\n" + "Warn for tasks exceeding total wallclock threshold\n") static void show_thread_poll_helper(struct vty *vty, struct thread_master *m) { @@ -421,11 +495,15 @@ DEFUN (clear_thread_cpu, void thread_cmd_init(void) { -#ifndef EXCLUDE_CPU_TIME install_element(VIEW_NODE, &show_thread_cpu_cmd); -#endif install_element(VIEW_NODE, &show_thread_poll_cmd); install_element(ENABLE_NODE, &clear_thread_cpu_cmd); + + install_element(CONFIG_NODE, &service_cputime_stats_cmd); + install_element(CONFIG_NODE, &service_cputime_warning_cmd); + install_element(CONFIG_NODE, &no_service_cputime_warning_cmd); + install_element(CONFIG_NODE, &service_walltime_warning_cmd); + install_element(CONFIG_NODE, &no_service_walltime_warning_cmd); } /* CLI end ------------------------------------------------------------------ */ @@ -1743,9 +1821,14 @@ static unsigned long timeval_elapsed(struct timeval a, struct timeval b) unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) { +#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID + *cputime = (now->cpu.tv_sec - start->cpu.tv_sec) * TIMER_SECOND_MICRO + + (now->cpu.tv_nsec - start->cpu.tv_nsec) / 1000; +#else /* This is 'user + sys' time. */ *cputime = timeval_elapsed(now->cpu.ru_utime, start->cpu.ru_utime) + timeval_elapsed(now->cpu.ru_stime, start->cpu.ru_stime); +#endif return timeval_elapsed(now->real, start->real); } @@ -1778,13 +1861,23 @@ void thread_set_yield_time(struct thread *thread, unsigned long yield_time) void thread_getrusage(RUSAGE_T *r) { + monotime(&r->real); + if (!cputime_enabled) { + memset(&r->cpu, 0, sizeof(r->cpu)); + return; + } + +#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID + /* not currently implemented in Linux's vDSO, but maybe at some point + * in the future? + */ + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &r->cpu); +#else /* !HAVE_CLOCK_THREAD_CPUTIME_ID */ #if defined RUSAGE_THREAD #define FRR_RUSAGE RUSAGE_THREAD #else #define FRR_RUSAGE RUSAGE_SELF #endif - monotime(&r->real); -#ifndef EXCLUDE_CPU_TIME getrusage(FRR_RUSAGE, &(r->cpu)); #endif } @@ -1802,13 +1895,14 @@ void thread_getrusage(RUSAGE_T *r) */ void thread_call(struct thread *thread) { -#ifndef EXCLUDE_CPU_TIME - _Atomic unsigned long realtime, cputime; - unsigned long exp; - unsigned long helper; -#endif RUSAGE_T before, after; + /* if the thread being called is the CLI, it may change cputime_enabled + * ("service cputime-stats" command), which can result in nonsensical + * and very confusing warnings + */ + bool cputime_enabled_here = cputime_enabled; + if (thread->master->ready_run_loop) before = thread->master->last_getrusage; else @@ -1828,43 +1922,45 @@ void thread_call(struct thread *thread) GETRUSAGE(&after); thread->master->last_getrusage = after; -#ifndef EXCLUDE_CPU_TIME - realtime = thread_consumed_time(&after, &before, &helper); - cputime = helper; + unsigned long walltime, cputime; + unsigned long exp; + + walltime = thread_consumed_time(&after, &before, &cputime); - /* update realtime */ - atomic_fetch_add_explicit(&thread->hist->real.total, realtime, + /* update walltime */ + atomic_fetch_add_explicit(&thread->hist->real.total, walltime, memory_order_seq_cst); exp = atomic_load_explicit(&thread->hist->real.max, memory_order_seq_cst); - while (exp < realtime + while (exp < walltime && !atomic_compare_exchange_weak_explicit( - &thread->hist->real.max, &exp, realtime, - memory_order_seq_cst, memory_order_seq_cst)) + &thread->hist->real.max, &exp, walltime, + memory_order_seq_cst, memory_order_seq_cst)) ; - /* update cputime */ - atomic_fetch_add_explicit(&thread->hist->cpu.total, cputime, - memory_order_seq_cst); - exp = atomic_load_explicit(&thread->hist->cpu.max, - memory_order_seq_cst); - while (exp < cputime - && !atomic_compare_exchange_weak_explicit( - &thread->hist->cpu.max, &exp, cputime, - memory_order_seq_cst, memory_order_seq_cst)) - ; + if (cputime_enabled_here && cputime_enabled) { + /* update cputime */ + atomic_fetch_add_explicit(&thread->hist->cpu.total, cputime, + memory_order_seq_cst); + exp = atomic_load_explicit(&thread->hist->cpu.max, + memory_order_seq_cst); + while (exp < cputime + && !atomic_compare_exchange_weak_explicit( + &thread->hist->cpu.max, &exp, cputime, + memory_order_seq_cst, memory_order_seq_cst)) + ; + } atomic_fetch_add_explicit(&thread->hist->total_calls, 1, memory_order_seq_cst); atomic_fetch_or_explicit(&thread->hist->types, 1 << thread->add_type, memory_order_seq_cst); -#ifdef CONSUMED_TIME_CHECK - if (cputime > CONSUMED_TIME_CHECK) { + if (cputime_enabled_here && cputime_enabled && cputime_threshold + && cputime > cputime_threshold) { /* - * We have a CPU Hog on our hands. The time FRR - * has spent doing actual work ( not sleeping ) - * is greater than 5 seconds. + * We have a CPU Hog on our hands. The time FRR has spent + * doing actual work (not sleeping) is greater than 5 seconds. * Whinge about it now, so we're aware this is yet another task * to fix. */ @@ -1874,13 +1970,13 @@ void thread_call(struct thread *thread) EC_LIB_SLOW_THREAD_CPU, "CPU HOG: task %s (%lx) ran for %lums (cpu time %lums)", thread->xref->funcname, (unsigned long)thread->func, - realtime / 1000, cputime / 1000); - } else if (realtime > CONSUMED_TIME_CHECK) { + walltime / 1000, cputime / 1000); + + } else if (walltime_threshold && walltime > walltime_threshold) { /* - * The runtime for a task is greater than 5 seconds, but - * the cpu time is under 5 seconds. Let's whine - * about this because this could imply some sort of - * scheduling issue. + * The runtime for a task is greater than 5 seconds, but the + * cpu time is under 5 seconds. Let's whine about this because + * this could imply some sort of scheduling issue. */ atomic_fetch_add_explicit(&thread->hist->total_wall_warn, 1, memory_order_seq_cst); @@ -1888,10 +1984,8 @@ void thread_call(struct thread *thread) EC_LIB_SLOW_THREAD_WALL, "STARVATION: task %s (%lx) ran for %lums (cpu time %lums)", thread->xref->funcname, (unsigned long)thread->func, - realtime / 1000, cputime / 1000); + walltime / 1000, cputime / 1000); } -#endif /* CONSUMED_TIME_CHECK */ -#endif /* Exclude CPU Time */ } /* Execute thread */ diff --git a/lib/thread.h b/lib/thread.h index fee728dbf9..abd94ff4f0 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -33,8 +33,19 @@ extern "C" { #endif +extern bool cputime_enabled; +extern unsigned long cputime_threshold; +/* capturing wallclock time is always enabled since it is fast (reading + * hardware TSC w/o syscalls) + */ +extern unsigned long walltime_threshold; + struct rusage_t { +#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID + struct timespec cpu; +#else struct rusage cpu; +#endif struct timeval real; }; #define RUSAGE_T struct rusage_t @@ -827,10 +827,24 @@ DEFUN_YANG (no_vrf, return CMD_WARNING_CONFIG_FAILED; } + if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) { + /* + * Remove the VRF interface config. Currently, we allow to + * remove only inactive VRFs, so we use VRF_DEFAULT_NAME here, + * because when the VRF is removed from kernel, the interface + * is moved to the default VRF. If we ever allow removing + * active VRFs, this code have to be updated accordingly. + */ + snprintf(xpath_list, sizeof(xpath_list), + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + vrfname, VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL); + } + snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, vrfname); nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, xpath_list); + return nb_cli_apply_changes(vty, NULL); } @@ -502,37 +502,37 @@ static int vty_command(struct vty *vty, char *buf) zlog_notice("%s%s", prompt_str, buf); } -#ifdef CONSUMED_TIME_CHECK - { - RUSAGE_T before; - RUSAGE_T after; - unsigned long realtime, cputime; + RUSAGE_T before; + RUSAGE_T after; + unsigned long walltime, cputime; - GETRUSAGE(&before); -#endif /* CONSUMED_TIME_CHECK */ + /* cmd_execute() may change cputime_enabled if we're executing the + * "service cputime-stats" command, which can result in nonsensical + * and very confusing warnings + */ + bool cputime_enabled_here = cputime_enabled; - ret = cmd_execute(vty, buf, NULL, 0); + GETRUSAGE(&before); - /* Get the name of the protocol if any */ - protocolname = frr_protoname; + ret = cmd_execute(vty, buf, NULL, 0); -#ifdef CONSUMED_TIME_CHECK - GETRUSAGE(&after); - realtime = thread_consumed_time(&after, &before, &cputime); - if (cputime > CONSUMED_TIME_CHECK) { - /* Warn about CPU hog that must be fixed. */ - flog_warn( - EC_LIB_SLOW_THREAD_CPU, - "CPU HOG: command took %lums (cpu time %lums): %s", - realtime / 1000, cputime / 1000, buf); - } else if (realtime > CONSUMED_TIME_CHECK) { - flog_warn( - EC_LIB_SLOW_THREAD_WALL, - "STARVATION: command took %lums (cpu time %lums): %s", - realtime / 1000, cputime / 1000, buf); - } - } -#endif /* CONSUMED_TIME_CHECK */ + GETRUSAGE(&after); + + walltime = thread_consumed_time(&after, &before, &cputime); + + if (cputime_enabled_here && cputime_enabled && cputime_threshold + && cputime > cputime_threshold) + /* Warn about CPU hog that must be fixed. */ + flog_warn(EC_LIB_SLOW_THREAD_CPU, + "CPU HOG: command took %lums (cpu time %lums): %s", + walltime / 1000, cputime / 1000, buf); + else if (walltime_threshold && walltime > walltime_threshold) + flog_warn(EC_LIB_SLOW_THREAD_WALL, + "STARVATION: command took %lums (cpu time %lums): %s", + walltime / 1000, cputime / 1000, buf); + + /* Get the name of the protocol if any */ + protocolname = frr_protoname; if (ret != CMD_SUCCESS) switch (ret) { diff --git a/lib/zlog.c b/lib/zlog.c index 89ab9265d1..6fd52cae62 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -67,6 +67,7 @@ DEFINE_HOOK(zlog_aux_init, (const char *prefix, int prio_min), char zlog_prefix[128]; size_t zlog_prefixsz; int zlog_tmpdirfd = -1; +int zlog_instance = -1; static atomic_bool zlog_ec = true, zlog_xid = true; @@ -107,6 +108,7 @@ struct zlog_msg { size_t stackbufsz; char *text; size_t textlen; + size_t hdrlen; /* This is always ISO8601 with sub-second precision 9 here, it's * converted for callers as needed. ts_dot points to the "." @@ -116,8 +118,23 @@ struct zlog_msg { * Valid if ZLOG_TS_ISO8601 is set. * (0 if timestamp has not been formatted yet) */ - uint32_t ts_flags; char ts_str[32], *ts_dot, ts_zonetail[8]; + uint32_t ts_flags; + + /* "mmm dd hh:mm:ss" for 3164 legacy syslog - too dissimilar from + * the above, so just kept separately here. + */ + uint32_t ts_3164_flags; + char ts_3164_str[16]; + + /* at the time of writing, 16 args was the actual maximum of arguments + * to a single zlog call. Particularly printing flag bitmasks seems + * to drive this. That said, the overhead of dynamically sizing this + * probably outweighs the value. If anything, a printfrr extension + * for printing flag bitmasks might be a good idea. + */ + struct fmt_outpos argpos[24]; + size_t n_argpos; }; /* thread-local log message buffering @@ -204,8 +221,15 @@ static inline void zlog_tls_set(struct zlog_tls *val) #endif #ifdef CAN_DO_TLS -static long zlog_gettid(void) +static intmax_t zlog_gettid(void) { +#ifndef __OpenBSD__ + /* accessing a TLS variable is much faster than a syscall */ + static thread_local intmax_t cached_tid = -1; + if (cached_tid != -1) + return cached_tid; +#endif + long rv = -1; #ifdef HAVE_PTHREAD_GETTHREADID_NP rv = pthread_getthreadid_np(); @@ -225,6 +249,10 @@ static long zlog_gettid(void) rv = mach_thread_self(); mach_port_deallocate(mach_task_self(), rv); #endif + +#ifndef __OpenBSD__ + cached_tid = rv; +#endif return rv; } @@ -244,7 +272,7 @@ void zlog_tls_buffer_init(void) for (i = 0; i < array_size(zlog_tls->msgp); i++) zlog_tls->msgp[i] = &zlog_tls->msgs[i]; - snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid()); + snprintfrr(mmpath, sizeof(mmpath), "logbuf.%jd", zlog_gettid()); mmfd = openat(zlog_tmpdirfd, mmpath, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600); @@ -311,7 +339,7 @@ void zlog_tls_buffer_fini(void) zlog_tls_free(zlog_tls); zlog_tls_set(NULL); - snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid()); + snprintfrr(mmpath, sizeof(mmpath), "logbuf.%jd", zlog_gettid()); if (do_unlink && unlinkat(zlog_tmpdirfd, mmpath, 0)) zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno); } @@ -326,6 +354,24 @@ void zlog_tls_buffer_fini(void) } #endif +void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid) +{ +#ifndef __OpenBSD__ + static thread_local intmax_t cached_pid = -1; + if (cached_pid != -1) + *pid = cached_pid; + else + cached_pid = *pid = (intmax_t)getpid(); +#else + *pid = (intmax_t)getpid(); +#endif +#ifdef CAN_DO_TLS + *tid = zlog_gettid(); +#else + *tid = *pid; +#endif +} + static inline void zlog_tls_free(void *arg) { struct zlog_tls *zlog_tls = arg; @@ -592,15 +638,19 @@ const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen) if (need) need += bputch(&fb, ' '); - hdrlen = need; + msg->hdrlen = hdrlen = need; assert(hdrlen < msg->stackbufsz); + fb.outpos = msg->argpos; + fb.outpos_n = array_size(msg->argpos); + fb.outpos_i = 0; + va_copy(args, msg->args); need += vbprintfrr(&fb, msg->fmt, args); va_end(args); msg->textlen = need; - need += bputch(&fb, '\0'); + need += bputch(&fb, '\n'); if (need <= msg->stackbufsz) msg->text = msg->stackbuf; @@ -612,25 +662,42 @@ const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen) fb.buf = msg->text; fb.len = need; fb.pos = msg->text + hdrlen; + fb.outpos_i = 0; va_copy(args, msg->args); vbprintfrr(&fb, msg->fmt, args); va_end(args); - bputch(&fb, '\0'); + bputch(&fb, '\n'); } + + msg->n_argpos = fb.outpos_i; } if (textlen) *textlen = msg->textlen; return msg->text; } +void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen, size_t *n_argpos, + const struct fmt_outpos **argpos) +{ + if (!msg->text) + zlog_msg_text(msg, NULL); + + if (hdrlen) + *hdrlen = msg->hdrlen; + if (n_argpos) + *n_argpos = msg->n_argpos; + if (argpos) + *argpos = msg->argpos; +} + #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY) #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC -size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz, - uint32_t flags) +size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out, uint32_t flags) { + size_t outsz = out ? (out->buf + out->len - out->pos) : 0; size_t len1; if (!(flags & ZLOG_TS_FORMAT)) @@ -672,36 +739,78 @@ size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz, len1 = strlen(msg->ts_str); if (flags & ZLOG_TS_LEGACY) { - if (len1 + 1 > outsz) - return 0; + if (!out) + return len1; + + if (len1 > outsz) { + memset(out->pos, 0, outsz); + out->pos += outsz; + return len1; + } /* just swap out the formatting, faster than redoing it */ for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) { switch (*p) { case '-': - *out++ = '/'; + *out->pos++ = '/'; break; case 'T': - *out++ = ' '; + *out->pos++ = ' '; break; default: - *out++ = *p; + *out->pos++ = *p; } } - *out = '\0'; return len1; } else { size_t len2 = strlen(msg->ts_zonetail); - if (len1 + len2 + 1 > outsz) - return 0; - memcpy(out, msg->ts_str, len1); - memcpy(out + len1, msg->ts_zonetail, len2); - out[len1 + len2] = '\0'; + if (!out) + return len1 + len2; + + if (len1 + len2 > outsz) { + memset(out->pos, 0, outsz); + out->pos += outsz; + return len1 + len2; + } + + memcpy(out->pos, msg->ts_str, len1); + out->pos += len1; + memcpy(out->pos, msg->ts_zonetail, len2); + out->pos += len2; return len1 + len2; } } +size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, uint32_t flags) +{ + flags &= ZLOG_TS_UTC; + + if (!msg->ts_3164_str[0] || flags != msg->ts_3164_flags) { + /* these are "hardcoded" in RFC3164, so they're here too... */ + static const char *const months[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + struct tm tm; + + /* RFC3164 explicitly asks for local time, but common usage + * also includes UTC. + */ + if (flags & ZLOG_TS_UTC) + gmtime_r(&msg->ts.tv_sec, &tm); + else + localtime_r(&msg->ts.tv_sec, &tm); + + snprintfrr(msg->ts_3164_str, sizeof(msg->ts_3164_str), + "%3s %2d %02d:%02d:%02d", months[tm.tm_mon], + tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + msg->ts_3164_flags = flags; + } + return bputs(out, msg->ts_3164_str); +} + void zlog_set_prefix_ec(bool enable) { atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed); @@ -777,6 +886,7 @@ void zlog_init(const char *progname, const char *protoname, { zlog_uid = uid; zlog_gid = gid; + zlog_instance = instance; if (instance) { snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir), diff --git a/lib/zlog.h b/lib/zlog.h index c421c16f38..d9c8952ac5 100644 --- a/lib/zlog.h +++ b/lib/zlog.h @@ -31,14 +31,19 @@ #include "frrcu.h" #include "memory.h" #include "hook.h" +#include "printfrr.h" #ifdef __cplusplus extern "C" { #endif +DECLARE_MGROUP(LOG); + extern char zlog_prefix[]; extern size_t zlog_prefixsz; extern int zlog_tmpdirfd; +extern int zlog_instance; +extern const char *zlog_progname; struct xref_logmsg { struct xref xref; @@ -143,9 +148,18 @@ struct zlog_msg; extern int zlog_msg_prio(struct zlog_msg *msg); extern const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg); -/* pass NULL as textlen if you don't need it. */ +/* text is NOT \0 terminated; instead there is a \n after textlen since the + * logging targets would jump extra hoops otherwise for a single byte. (the + * \n is not included in textlen) + * + * calling this with NULL textlen is likely wrong. + * use "%.*s", (int)textlen, text when passing to printf-like functions + */ extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen); +extern void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen, + size_t *n_argpos, const struct fmt_outpos **argpos); + /* timestamp formatting control flags */ /* sub-second digit count */ @@ -161,9 +175,18 @@ extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen); /* default is local time zone */ #define ZLOG_TS_UTC (1 << 10) -extern size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz, +extern size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out, uint32_t flags); +/* "mmm dd hh:mm:ss" for RFC3164 syslog. Only ZLOG_TS_UTC for flags. */ +extern size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, + uint32_t flags); + +/* currently just returns the current PID/TID since we never write another + * thread's messages + */ +extern void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid); + /* This list & struct implements the actual logging targets. It is accessed * lock-free from all threads, and thus MUST only be changed atomically, i.e. * RCU. @@ -254,6 +277,8 @@ extern void zlog_tls_buffer_fini(void); /* Enable or disable 'immediate' output - default is to buffer messages. */ extern void zlog_set_immediate(bool set_p); +extern const char *zlog_priority_str(int priority); + #ifdef __cplusplus } #endif diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c index 7799fbfda7..48785ad298 100644 --- a/lib/zlog_targets.c +++ b/lib/zlog_targets.c @@ -31,7 +31,6 @@ * absolute end. */ -DECLARE_MGROUP(LOG); DEFINE_MGROUP_ACTIVEATEXIT(LOG, "logging subsystem"); DEFINE_MTYPE_STATIC(LOG, LOG_FD, "log file target"); @@ -79,13 +78,17 @@ void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs) int prio = zlog_msg_prio(msg); if (prio <= zt->prio_min) { + struct fbuf fbuf = { + .buf = ts_buf, + .pos = ts_pos, + .len = sizeof(ts_buf), + }; + iov[iovpos].iov_base = ts_pos; - if (iovpos > 0) - *ts_pos++ = '\n'; - ts_pos += zlog_msg_ts(msg, ts_pos, - sizeof(ts_buf) - 1 - - (ts_pos - ts_buf), - ZLOG_TS_LEGACY | zte->ts_subsec); + zlog_msg_ts(msg, &fbuf, + ZLOG_TS_LEGACY | zte->ts_subsec); + ts_pos = fbuf.pos; + *ts_pos++ = ' '; iov[iovpos].iov_len = ts_pos - (char *)iov[iovpos].iov_base; @@ -107,7 +110,7 @@ void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs) iov[iovpos].iov_base = (char *)zlog_msg_text(msg, &textlen); - iov[iovpos].iov_len = textlen; + iov[iovpos].iov_len = textlen + 1; iovpos++; } @@ -120,11 +123,6 @@ void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs) if (iovpos > 0 && (ts_buf + sizeof(ts_buf) - ts_pos < TS_LEN || i + 1 == nmsgs || array_size(iov) - iovpos < 5)) { - iov[iovpos].iov_base = (char *)"\n"; - iov[iovpos].iov_len = 1; - - iovpos++; - writev(fd, iov, iovpos); iovpos = 0; @@ -439,13 +437,16 @@ static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[], { size_t i; struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt); + const char *text; + size_t text_len; for (i = 0; i < nmsgs; i++) { if (zlog_msg_prio(msgs[i]) > zt->prio_min) continue; - syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s", - zlog_msg_text(msgs[i], NULL)); + text = zlog_msg_text(msgs[i], &text_len); + syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%.*s", + (int)text_len, text); } } diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index c2111a7706..54b7850207 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -118,6 +118,7 @@ static struct quagga_signal_t sighandlers[] = { static const struct frr_yang_module_info *const nhrpd_yang_modules[] = { &frr_filter_info, &frr_interface_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(nhrpd, NHRP, .vty_port = NHRP_VTY_PORT, diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c index 420ea12ec1..963fa4d995 100644 --- a/nhrpd/nhrp_vty.c +++ b/nhrpd/nhrp_vty.c @@ -1260,6 +1260,8 @@ void nhrp_config_init(void) install_element(CONFIG_NODE, &nhrp_multicast_nflog_group_cmd); install_element(CONFIG_NODE, &no_nhrp_multicast_nflog_group_cmd); + vrf_cmd_init(NULL, &nhrpd_privs); + /* interface specific commands */ install_node(&nhrp_interface_node); diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index c17af758b0..ba355a347e 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -58,7 +58,7 @@ DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments"); -static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id); +static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type); static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6, struct ospf6_redist *red, int type); @@ -1111,8 +1111,7 @@ void ospf6_asbr_routemap_update(const char *mapname) type)); ospf6_asbr_redistribute_unset(ospf6, red, type); ospf6_asbr_routemap_set(red, mapname); - ospf6_asbr_redistribute_set( - type, ospf6->vrf_id); + ospf6_asbr_redistribute_set(ospf6, type); } } } @@ -1231,15 +1230,9 @@ void ospf6_asbr_status_update(struct ospf6 *ospf6, int status) OSPF6_ROUTER_LSA_SCHEDULE(oa); } -static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id) +static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type) { - struct ospf6 *ospf6 = NULL; - ospf6_zebra_redistribute(type, vrf_id); - - ospf6 = ospf6_lookup_by_vrf_id(vrf_id); - - if (!ospf6) - return; + ospf6_zebra_redistribute(type, ospf6->vrf_id); ospf6_asbr_status_update(ospf6, ++ospf6->redist_count); } @@ -1263,7 +1256,6 @@ static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6, } ospf6_asbr_routemap_unset(red); - zlog_debug("%s: redist_count %d", __func__, ospf6->redist_count); ospf6_asbr_status_update(ospf6, --ospf6->redist_count); } @@ -1585,12 +1577,13 @@ DEFUN (ospf6_redistribute, if (type < 0) return CMD_WARNING_CONFIG_FAILED; - red = ospf6_redist_add(ospf6, type, 0); + red = ospf6_redist_lookup(ospf6, type, 0); if (!red) - return CMD_SUCCESS; + ospf6_redist_add(ospf6, type, 0); + else + ospf6_asbr_redistribute_unset(ospf6, red, type); - ospf6_asbr_redistribute_unset(ospf6, red, type); - ospf6_asbr_redistribute_set(type, ospf6->vrf_id); + ospf6_asbr_redistribute_set(ospf6, type); return CMD_SUCCESS; } @@ -1615,13 +1608,14 @@ DEFUN (ospf6_redistribute_routemap, if (type < 0) return CMD_WARNING_CONFIG_FAILED; - red = ospf6_redist_add(ospf6, type, 0); + red = ospf6_redist_lookup(ospf6, type, 0); if (!red) - return CMD_SUCCESS; + red = ospf6_redist_add(ospf6, type, 0); + else + ospf6_asbr_redistribute_unset(ospf6, red, type); - ospf6_asbr_redistribute_unset(ospf6, red, type); ospf6_asbr_routemap_set(red, argv[idx_word]->arg); - ospf6_asbr_redistribute_set(type, ospf6->vrf_id); + ospf6_asbr_redistribute_set(ospf6, type); return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 6763e7457f..553967e2e3 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -185,6 +185,8 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp) oi = XCALLOC(MTYPE_OSPF6_IF, sizeof(struct ospf6_interface)); + oi->obuf = ospf6_fifo_new(); + oi->area = (struct ospf6_area *)NULL; oi->neighbor_list = list_new(); oi->neighbor_list->cmp = ospf6_neighbor_cmp; @@ -243,6 +245,8 @@ void ospf6_interface_delete(struct ospf6_interface *oi) QOBJ_UNREG(oi); + ospf6_fifo_free(oi->obuf); + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) ospf6_neighbor_delete(on); @@ -885,6 +889,15 @@ int interface_down(struct thread *thread) ospf6_sso(oi->interface->ifindex, &allspfrouters6, IPV6_LEAVE_GROUP, ospf6->fd); + /* deal with write fifo */ + ospf6_fifo_flush(oi->obuf); + if (oi->on_write_q) { + listnode_delete(ospf6->oi_write_q, oi); + if (list_isempty(ospf6->oi_write_q)) + thread_cancel(&ospf6->t_write); + oi->on_write_q = 0; + } + ospf6_interface_state_change(OSPF6_INTERFACE_DOWN, oi); return 0; @@ -1619,6 +1632,9 @@ void ospf6_interface_start(struct ospf6_interface *oi) if (oi->area_id_format == OSPF6_AREA_FMT_UNSET) return; + if (oi->area) + return; + ospf6 = ospf6_lookup_by_vrf_id(oi->interface->vrf_id); if (!ospf6) return; @@ -1969,6 +1985,38 @@ DEFUN (no_auto_cost_reference_bandwidth, } +DEFUN (ospf6_write_multiplier, + ospf6_write_multiplier_cmd, + "write-multiplier (1-100)", + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + uint32_t write_oi_count; + + write_oi_count = strtol(argv[1]->arg, NULL, 10); + if (write_oi_count < 1 || write_oi_count > 100) { + vty_out(vty, "write-multiplier value is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + o->write_oi_count = write_oi_count; + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_write_multiplier, + no_ospf6_write_multiplier_cmd, + "no write-multiplier (1-100)", + NO_STR + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + + o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT; + return CMD_SUCCESS; +} + DEFUN (ipv6_ospf6_hellointerval, ipv6_ospf6_hellointerval_cmd, "ipv6 ospf6 hello-interval (1-65535)", @@ -2641,6 +2689,9 @@ void ospf6_interface_init(void) /* reference bandwidth commands */ install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); install_element(OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd); + /* write-multiplier commands */ + install_element(OSPF6_NODE, &ospf6_write_multiplier_cmd); + install_element(OSPF6_NODE, &no_ospf6_write_multiplier_cmd); } /* Clear the specified interface structure */ diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 796d75e897..530efc3bd2 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -56,6 +56,9 @@ struct ospf6_interface { /* I/F transmission delay */ uint32_t transdelay; + /* Packet send buffer. */ + struct ospf6_fifo *obuf; /* Output queue */ + /* Network Type */ uint8_t type; bool type_cfg; @@ -118,6 +121,9 @@ struct ospf6_interface { struct ospf6_route_table *route_connected; + /* last hello sent */ + struct timeval last_hello; + /* prefix-list name to filter connected prefix */ char *plist_name; @@ -130,6 +136,8 @@ struct ospf6_interface { char *profile; } bfd_config; + int on_write_q; + /* Statistics Fields */ uint32_t hello_in; uint32_t hello_out; diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 1cf4585c95..5f23aab80a 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -50,6 +50,8 @@ #include <netinet/ip6.h> DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PACKET, "OSPF6 packet"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_FIFO, "OSPF6 FIFO queue"); unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0}; static const struct message ospf6_message_type_str[] = { @@ -252,6 +254,137 @@ void ospf6_lsack_print(struct ospf6_header *oh, int action) } } +static struct ospf6_packet *ospf6_packet_new(size_t size) +{ + struct ospf6_packet *new; + + new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet)); + new->s = stream_new(size); + + return new; +} + +static void ospf6_packet_free(struct ospf6_packet *op) +{ + if (op->s) + stream_free(op->s); + + XFREE(MTYPE_OSPF6_PACKET, op); +} + +struct ospf6_fifo *ospf6_fifo_new(void) +{ + struct ospf6_fifo *new; + + new = XCALLOC(MTYPE_OSPF6_FIFO, sizeof(struct ospf6_fifo)); + return new; +} + +/* Add new packet to fifo. */ +static void ospf6_fifo_push(struct ospf6_fifo *fifo, struct ospf6_packet *op) +{ + if (fifo->tail) + fifo->tail->next = op; + else + fifo->head = op; + + fifo->tail = op; + + fifo->count++; +} + +/* Add new packet to head of fifo. */ +static void ospf6_fifo_push_head(struct ospf6_fifo *fifo, + struct ospf6_packet *op) +{ + op->next = fifo->head; + + if (fifo->tail == NULL) + fifo->tail = op; + + fifo->head = op; + + fifo->count++; +} + +/* Delete first packet from fifo. */ +static struct ospf6_packet *ospf6_fifo_pop(struct ospf6_fifo *fifo) +{ + struct ospf6_packet *op; + + op = fifo->head; + + if (op) { + fifo->head = op->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + + return op; +} + +/* Return first fifo entry. */ +static struct ospf6_packet *ospf6_fifo_head(struct ospf6_fifo *fifo) +{ + return fifo->head; +} + +/* Flush ospf packet fifo. */ +void ospf6_fifo_flush(struct ospf6_fifo *fifo) +{ + struct ospf6_packet *op; + struct ospf6_packet *next; + + for (op = fifo->head; op; op = next) { + next = op->next; + ospf6_packet_free(op); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free ospf packet fifo. */ +void ospf6_fifo_free(struct ospf6_fifo *fifo) +{ + ospf6_fifo_flush(fifo); + + XFREE(MTYPE_OSPF6_FIFO, fifo); +} + +static void ospf6_packet_add(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + /* Add packet to end of queue. */ + ospf6_fifo_push(oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void ospf6_packet_add_top(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + /* Add packet to head of queue. */ + ospf6_fifo_push_head(oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void ospf6_packet_delete(struct ospf6_interface *oi) +{ + struct ospf6_packet *op; + + op = ospf6_fifo_pop(oi->obuf); + + if (op) + ospf6_packet_free(op); +} + + static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, struct ospf6_interface *oi, struct ospf6_header *oh) @@ -263,7 +396,10 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, int neighborchange = 0; int neighbor_ifindex_change = 0; int backupseen = 0; + int64_t latency = 0; + struct timeval timestamp; + monotime(×tamp); hello = (struct ospf6_hello *)((caddr_t)oh + sizeof(struct ospf6_header)); @@ -305,6 +441,17 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, on->priority = hello->priority; } + /* check latency against hello period */ + if (on->hello_in) + latency = monotime_since(&on->last_hello, NULL) + - (oi->hello_interval * 1000000); + /* log if latency exceeds the hello period */ + if (latency > (oi->hello_interval * 1000000)) + zlog_warn("%s RX %pI4 high latency %" PRId64 "us.", __func__, + &on->router_id, latency); + on->last_hello = timestamp; + on->hello_in++; + /* Always override neighbor's source address */ memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr)); @@ -1512,23 +1659,19 @@ void ospf6_message_terminate(void) iobuflen = 0; } -int ospf6_receive(struct thread *thread) +enum ospf6_read_return_enum { + OSPF6_READ_ERROR, + OSPF6_READ_CONTINUE, +}; + +static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6) { - int sockfd; - unsigned int len; + int len; struct in6_addr src, dst; ifindex_t ifindex; struct iovec iovector[2]; struct ospf6_interface *oi; struct ospf6_header *oh; - struct ospf6 *ospf6; - - /* add next read thread */ - ospf6 = THREAD_ARG(thread); - sockfd = THREAD_FD(thread); - - thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, - &ospf6->t_ospf6_receive); /* initialize */ memset(&src, 0, sizeof(src)); @@ -1542,9 +1685,12 @@ int ospf6_receive(struct thread *thread) /* receive message */ len = ospf6_recvmsg(&src, &dst, &ifindex, iovector, sockfd); - if (len > iobuflen) { + if (len < 0) + return OSPF6_READ_ERROR; + + if ((uint)len > iobuflen) { flog_err(EC_LIB_DEVELOPMENT, "Excess message read"); - return 0; + return OSPF6_READ_ERROR; } oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); @@ -1553,19 +1699,19 @@ int ospf6_receive(struct thread *thread) if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV_HDR)) zlog_debug("Message received on disabled interface"); - return 0; + return OSPF6_READ_CONTINUE; } if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV_HDR)) zlog_debug("%s: Ignore message on passive interface %s", __func__, oi->interface->name); - return 0; + return OSPF6_READ_CONTINUE; } oh = (struct ospf6_header *)recvbuf; if (ospf6_rxpacket_examin(oi, oh, len) != MSG_OK) - return 0; + return OSPF6_READ_CONTINUE; /* Being here means, that no sizing/alignment issues were detected in the input packet. This renders the additional checks performed below @@ -1626,91 +1772,243 @@ int ospf6_receive(struct thread *thread) assert(0); } + return OSPF6_READ_CONTINUE; +} + +int ospf6_receive(struct thread *thread) +{ + int sockfd; + struct ospf6 *ospf6; + int count = 0; + + /* add next read thread */ + ospf6 = THREAD_ARG(thread); + sockfd = THREAD_FD(thread); + + thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, + &ospf6->t_ospf6_receive); + + while (count < ospf6->write_oi_count) { + count++; + switch (ospf6_read_helper(sockfd, ospf6)) { + case OSPF6_READ_ERROR: + return 0; + case OSPF6_READ_CONTINUE: + break; + } + } + return 0; } -static void ospf6_send(struct in6_addr *src, struct in6_addr *dst, - struct ospf6_interface *oi, struct ospf6_header *oh) +static void ospf6_make_header(uint8_t type, struct ospf6_interface *oi, + struct stream *s) { - unsigned int len; - char srcname[64]; - struct iovec iovector[2]; + struct ospf6_header *oh; - /* initialize */ - iovector[0].iov_base = (caddr_t)oh; - iovector[0].iov_len = ntohs(oh->length); - iovector[1].iov_base = NULL; - iovector[1].iov_len = 0; + oh = (struct ospf6_header *)STREAM_DATA(s); + + oh->version = (uint8_t)OSPFV3_VERSION; + oh->type = type; - /* fill OSPF header */ - oh->version = OSPFV3_VERSION; - /* message type must be set before */ - /* message length must be set before */ oh->router_id = oi->area->ospf6->router_id; oh->area_id = oi->area->area_id; - /* checksum is calculated by kernel */ oh->instance_id = oi->instance_id; oh->reserved = 0; + stream_forward_endp(s, OSPF6_HEADER_SIZE); +} - /* Log */ - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) { - if (src) - inet_ntop(AF_INET6, src, srcname, sizeof(srcname)); - else - memset(srcname, 0, sizeof(srcname)); - zlog_debug("%s send on %s", - lookup_msg(ospf6_message_type_str, oh->type, NULL), - oi->interface->name); - zlog_debug(" src: %s", srcname); - zlog_debug(" dst: %pI6", dst); +static void ospf6_fill_header(struct ospf6_interface *oi, struct stream *s, + uint16_t length) +{ + struct ospf6_header *oh; + + oh = (struct ospf6_header *)STREAM_DATA(s); + + oh->length = htons(length); +} + +static void ospf6_fill_lsupdate_header(struct stream *s, uint32_t lsa_num) +{ + struct ospf6_header *oh; + struct ospf6_lsupdate *lsu; + + oh = (struct ospf6_header *)STREAM_DATA(s); + + lsu = (struct ospf6_lsupdate *)((caddr_t)oh + + sizeof(struct ospf6_header)); + lsu->lsa_number = htonl(lsa_num); +} + +static uint32_t ospf6_packet_max(struct ospf6_interface *oi) +{ + assert(oi->ifmtu > sizeof(struct ip6_hdr)); + return oi->ifmtu - (sizeof(struct ip6_hdr)); +} + +static uint16_t ospf6_make_hello(struct ospf6_interface *oi, struct stream *s) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + uint16_t length = OSPF6_HELLO_MIN_SIZE; + + stream_putl(s, oi->interface->ifindex); + stream_putc(s, oi->priority); + stream_putc(s, oi->area->options[0]); + stream_putc(s, oi->area->options[1]); + stream_putc(s, oi->area->options[2]); + stream_putw(s, oi->hello_interval); + stream_putw(s, oi->dead_interval); + stream_put_ipv4(s, oi->drouter); + stream_put_ipv4(s, oi->bdrouter); + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + if (on->state < OSPF6_NEIGHBOR_INIT) + continue; + + if ((length + sizeof(uint32_t) + OSPF6_HEADER_SIZE) + > ospf6_packet_max(oi)) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, + SEND)) + zlog_debug( + "sending Hello message: exceeds I/F MTU"); + break; + } + + stream_put_ipv4(s, on->router_id); + length += sizeof(uint32_t); + } + + return length; +} + +static int ospf6_write(struct thread *thread) +{ + struct ospf6 *ospf6 = THREAD_ARG(thread); + struct ospf6_interface *oi; + struct ospf6_interface *last_serviced_oi = NULL; + struct ospf6_header *oh; + struct ospf6_packet *op; + struct listnode *node; + char srcname[64], dstname[64]; + struct iovec iovector[2]; + int pkt_count = 0; + int len; + int64_t latency = 0; + struct timeval timestamp; + + if (ospf6->fd < 0) { + zlog_warn("ospf6_write failed to send, fd %d", ospf6->fd); + return -1; + } + + node = listhead(ospf6->oi_write_q); + assert(node); + oi = listgetdata(node); + + while ((pkt_count < ospf6->write_oi_count) && oi + && (last_serviced_oi != oi)) { + + op = ospf6_fifo_head(oi->obuf); + assert(op); + assert(op->length >= OSPF6_HEADER_SIZE); + + iovector[0].iov_base = (caddr_t)stream_pnt(op->s); + iovector[0].iov_len = op->length; + iovector[1].iov_base = NULL; + iovector[1].iov_len = 0; + + oh = (struct ospf6_header *)STREAM_DATA(op->s); + + len = ospf6_sendmsg(oi->linklocal_addr, &op->dst, + oi->interface->ifindex, iovector, + ospf6->fd); + if (len != op->length) + flog_err(EC_LIB_DEVELOPMENT, + "Could not send entire message"); + + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)) { + inet_ntop(AF_INET6, &op->dst, dstname, sizeof(dstname)); + inet_ntop(AF_INET6, oi->linklocal_addr, srcname, + sizeof(srcname)); + zlog_debug("%s send on %s", + lookup_msg(ospf6_message_type_str, oh->type, + NULL), + oi->interface->name); + zlog_debug(" src: %s", srcname); + zlog_debug(" dst: %s", dstname); + } switch (oh->type) { case OSPF6_MESSAGE_TYPE_HELLO: - ospf6_hello_print(oh, OSPF6_ACTION_RECV); + monotime(×tamp); + if (oi->hello_out) + latency = monotime_since(&oi->last_hello, NULL) + - (oi->hello_interval * 1000000); + + /* log if latency exceeds the hello period */ + if (latency > (oi->hello_interval * 1000000)) + zlog_warn("%s hello TX high latency %" PRId64 + "us.", + __func__, latency); + oi->last_hello = timestamp; + oi->hello_out++; + ospf6_hello_print(oh, OSPF6_ACTION_SEND); break; case OSPF6_MESSAGE_TYPE_DBDESC: - ospf6_dbdesc_print(oh, OSPF6_ACTION_RECV); + oi->db_desc_out++; + ospf6_dbdesc_print(oh, OSPF6_ACTION_SEND); break; case OSPF6_MESSAGE_TYPE_LSREQ: - ospf6_lsreq_print(oh, OSPF6_ACTION_RECV); + oi->ls_req_out++; + ospf6_lsreq_print(oh, OSPF6_ACTION_SEND); break; case OSPF6_MESSAGE_TYPE_LSUPDATE: - ospf6_lsupdate_print(oh, OSPF6_ACTION_RECV); + oi->ls_upd_out++; + ospf6_lsupdate_print(oh, OSPF6_ACTION_SEND); break; case OSPF6_MESSAGE_TYPE_LSACK: - ospf6_lsack_print(oh, OSPF6_ACTION_RECV); + oi->ls_ack_out++; + ospf6_lsack_print(oh, OSPF6_ACTION_SEND); break; default: zlog_debug("Unknown message"); assert(0); break; } - } + /* Now delete packet from queue. */ + ospf6_packet_delete(oi); + + /* Move this interface to the tail of write_q to + serve everyone in a round robin fashion */ + list_delete_node(ospf6->oi_write_q, node); + if (ospf6_fifo_head(oi->obuf) == NULL) { + oi->on_write_q = 0; + last_serviced_oi = NULL; + oi = NULL; + } else { + listnode_add(ospf6->oi_write_q, oi); + } - /* send message */ - if (oi->area->ospf6->fd != -1) { - len = ospf6_sendmsg(src, dst, oi->interface->ifindex, iovector, - oi->area->ospf6->fd); - if (len != ntohs(oh->length)) - flog_err(EC_LIB_DEVELOPMENT, - "Could not send entire message"); + /* Setup to service from the head of the queue again */ + if (!list_isempty(ospf6->oi_write_q)) { + node = listhead(ospf6->oi_write_q); + oi = listgetdata(node); + } } -} -static uint32_t ospf6_packet_max(struct ospf6_interface *oi) -{ - assert(oi->ifmtu > sizeof(struct ip6_hdr)); - return oi->ifmtu - (sizeof(struct ip6_hdr)); + /* If packets still remain in queue, call write thread. */ + if (!list_isempty(ospf6->oi_write_q)) + thread_add_write(master, ospf6_write, ospf6, ospf6->fd, + &ospf6->t_write); + + return 0; } int ospf6_hello_send(struct thread *thread) { struct ospf6_interface *oi; - struct ospf6_header *oh; - struct ospf6_hello *hello; - uint8_t *p; - struct listnode *node, *nnode; - struct ospf6_neighbor *on; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; oi = (struct ospf6_interface *)THREAD_ARG(thread); oi->thread_send_hello = (struct thread *)NULL; @@ -1722,88 +2020,44 @@ int ospf6_hello_send(struct thread *thread) return 0; } - if (iobuflen == 0) { - zlog_debug("Unable to send Hello on interface %s iobuflen is 0", - oi->interface->name); + op = ospf6_packet_new(oi->ifmtu); + + ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s); + + /* Prepare OSPF Hello body */ + length += ospf6_make_hello(oi, op->s); + if (length == OSPF6_HEADER_SIZE) { + /* Hello overshooting MTU */ + ospf6_packet_free(op); return 0; } - /* set next thread */ - thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, - &oi->thread_send_hello); + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - hello = (struct ospf6_hello *)((caddr_t)oh - + sizeof(struct ospf6_header)); + /* Set packet length. */ + op->length = length; - hello->interface_id = htonl(oi->interface->ifindex); - hello->priority = oi->priority; - hello->options[0] = oi->area->options[0]; - hello->options[1] = oi->area->options[1]; - hello->options[2] = oi->area->options[2]; - hello->hello_interval = htons(oi->hello_interval); - hello->dead_interval = htons(oi->dead_interval); - hello->drouter = oi->drouter; - hello->bdrouter = oi->bdrouter; + op->dst = allspfrouters6; - p = (uint8_t *)((caddr_t)hello + sizeof(struct ospf6_hello)); + /* Add packet to the top of the interface output queue, so that they + * can't get delayed by things like long queues of LS Update packets + */ + ospf6_packet_add_top(oi, op); - for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { - if (on->state < OSPF6_NEIGHBOR_INIT) - continue; - - if (p - sendbuf + sizeof(uint32_t) > ospf6_packet_max(oi)) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, - SEND_HDR)) - zlog_debug( - "sending Hello message: exceeds I/F MTU"); - break; - } - - memcpy(p, &on->router_id, sizeof(uint32_t)); - p += sizeof(uint32_t); - } - - oh->type = OSPF6_MESSAGE_TYPE_HELLO; - oh->length = htons(p - sendbuf); + /* set next thread */ + thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, + &oi->thread_send_hello); - oi->hello_out++; + OSPF6_MESSAGE_WRITE_ON(oi); - ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh); return 0; } -int ospf6_dbdesc_send(struct thread *thread) +static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) { - struct ospf6_neighbor *on; - struct ospf6_header *oh; - struct ospf6_dbdesc *dbdesc; - uint8_t *p; + uint16_t length = OSPF6_DB_DESC_MIN_SIZE; struct ospf6_lsa *lsa, *lsanext; - struct in6_addr *dst; - - on = (struct ospf6_neighbor *)THREAD_ARG(thread); - on->thread_send_dbdesc = (struct thread *)NULL; - - if (on->state < OSPF6_NEIGHBOR_EXSTART) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND_HDR)) - zlog_debug( - "Quit to send DbDesc to neighbor %s state %s", - on->name, ospf6_neighbor_state_str[on->state]); - return 0; - } - - /* set next thread if master */ - if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) - thread_add_timer(master, ospf6_dbdesc_send, on, - on->ospf6_if->rxmt_interval, - &on->thread_send_dbdesc); - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh - + sizeof(struct ospf6_header)); /* if this is initial one, initialize sequence number for DbDesc */ if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) @@ -1811,45 +2065,79 @@ int ospf6_dbdesc_send(struct thread *thread) on->dbdesc_seqnum = monotime(NULL); } - dbdesc->options[0] = on->ospf6_if->area->options[0]; - dbdesc->options[1] = on->ospf6_if->area->options[1]; - dbdesc->options[2] = on->ospf6_if->area->options[2]; - dbdesc->ifmtu = htons(on->ospf6_if->ifmtu); - dbdesc->bits = on->dbdesc_bits; - dbdesc->seqnum = htonl(on->dbdesc_seqnum); + /* reserved */ + stream_putc(s, 0); /* reserved 1 */ + stream_putc(s, on->ospf6_if->area->options[0]); + stream_putc(s, on->ospf6_if->area->options[1]); + stream_putc(s, on->ospf6_if->area->options[2]); + stream_putw(s, on->ospf6_if->ifmtu); + stream_putc(s, 0); /* reserved 2 */ + stream_putc(s, on->dbdesc_bits); + stream_putl(s, on->dbdesc_seqnum); /* if this is not initial one, set LSA headers in dbdesc */ - p = (uint8_t *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc)); if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)) { for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) { ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); /* MTU check */ - if (p - sendbuf + sizeof(struct ospf6_lsa_header) + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) > ospf6_packet_max(on->ospf6_if)) { ospf6_lsa_unlock(lsa); if (lsanext) ospf6_lsa_unlock(lsanext); break; } - memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header)); - p += sizeof(struct ospf6_lsa_header); + stream_put(s, lsa->header, + sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); } } + return length; +} + +int ospf6_dbdesc_send(struct thread *thread) +{ + struct ospf6_neighbor *on; + uint16_t length = OSPF6_HEADER_SIZE; + struct ospf6_packet *op; - oh->type = OSPF6_MESSAGE_TYPE_DBDESC; - oh->length = htons(p - sendbuf); + on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on->thread_send_dbdesc = (struct thread *)NULL; + if (on->state < OSPF6_NEIGHBOR_EXSTART) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND)) + zlog_debug( + "Quit to send DbDesc to neighbor %s state %s", + on->name, ospf6_neighbor_state_str[on->state]); + return 0; + } + + /* set next thread if master */ + if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) + thread_add_timer(master, ospf6_dbdesc_send, on, + on->ospf6_if->rxmt_interval, + &on->thread_send_dbdesc); + + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_DBDESC, on->ospf6_if, op->s); + + length += ospf6_make_dbdesc(on, op->s); + ospf6_fill_header(on->ospf6_if, op->s, length); + + /* Set packet length. */ + op->length = length; if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) - dst = &allspfrouters6; + op->dst = allspfrouters6; else - dst = &on->linklocal_addr; + op->dst = on->linklocal_addr; - on->ospf6_if->db_desc_out++; + ospf6_packet_add(on->ospf6_if, op); - ospf6_send(on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); return 0; } @@ -1902,13 +2190,84 @@ int ospf6_dbdesc_send_newone(struct thread *thread) return 0; } +static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext, *last_req = NULL; + + for (ALL_LSDB(on->request_list, lsa, lsanext)) { + if ((length + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); + break; + } + stream_putw(s, 0); /* reserved */ + stream_putw(s, ntohs(lsa->header->type)); + stream_putl(s, ntohl(lsa->header->id)); + stream_putl(s, ntohl(lsa->header->adv_router)); + length += sizeof(struct ospf6_lsreq_entry); + last_req = lsa; + } + + if (last_req != NULL) { + if (on->last_ls_req != NULL) + on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req); + + ospf6_lsa_lock(last_req); + on->last_ls_req = last_req; + } + + return length; +} + +static uint16_t ospf6_make_lsack_neighbor(struct ospf6_neighbor *on, + struct ospf6_packet **op) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext; + int lsa_cnt = 0; + + for (ALL_LSDB(on->lsack_list, lsa, lsanext)) { + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + /* if we run out of packet size/space here, + better to try again soon. */ + if (lsa_cnt) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + + (*op)->length = length + OSPF6_HEADER_SIZE; + (*op)->dst = on->linklocal_addr; + ospf6_packet_add(on->ospf6_if, *op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + /* new packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, + on->ospf6_if, (*op)->s); + length = 0; + lsa_cnt = 0; + } + } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, + sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, on->lsack_list); + lsa_cnt++; + } + return length; +} + int ospf6_lsreq_send(struct thread *thread) { struct ospf6_neighbor *on; - struct ospf6_header *oh; - struct ospf6_lsreq_entry *e; - uint8_t *p; - struct ospf6_lsa *lsa, *lsanext, *last_req; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; on = (struct ospf6_neighbor *)THREAD_ARG(thread); on->thread_send_lsreq = (struct thread *)NULL; @@ -1929,49 +2288,31 @@ int ospf6_lsreq_send(struct thread *thread) return 0; } - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - last_req = NULL; - - /* set Request entries in lsreq */ - p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header)); - for (ALL_LSDB(on->request_list, lsa, lsanext)) { - /* MTU check */ - if (p - sendbuf + sizeof(struct ospf6_lsreq_entry) - > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock(lsa); - if (lsanext) - ospf6_lsa_unlock(lsanext); - break; - } - - e = (struct ospf6_lsreq_entry *)p; - e->type = lsa->header->type; - e->id = lsa->header->id; - e->adv_router = lsa->header->adv_router; - p += sizeof(struct ospf6_lsreq_entry); - last_req = lsa; - } + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSREQ, on->ospf6_if, op->s); - if (last_req != NULL) { - if (on->last_ls_req != NULL) - on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req); + length += ospf6_make_lsreq(on, op->s); - ospf6_lsa_lock(last_req); - on->last_ls_req = last_req; + if (length == OSPF6_HEADER_SIZE) { + /* Hello overshooting MTU */ + ospf6_packet_free(op); + return 0; } - oh->type = OSPF6_MESSAGE_TYPE_LSREQ; - oh->length = htons(p - sendbuf); + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); - on->ospf6_if->ls_req_out++; + /* Set packet length */ + op->length = length; if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) - ospf6_send(on->ospf6_if->linklocal_addr, &allspfrouters6, - on->ospf6_if, oh); + op->dst = allspfrouters6; else - ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); + op->dst = on->linklocal_addr; + + ospf6_packet_add(on->ospf6_if, op); + + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); /* set next thread */ if (on->request_list->count != 0) { @@ -1986,43 +2327,117 @@ int ospf6_lsreq_send(struct thread *thread) static void ospf6_send_lsupdate(struct ospf6_neighbor *on, struct ospf6_interface *oi, - struct ospf6_header *oh) + struct ospf6_packet *op) { if (on) { - on->ospf6_if->ls_upd_out++; if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) || (on->ospf6_if->state == OSPF6_INTERFACE_DR) - || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) { - ospf6_send(on->ospf6_if->linklocal_addr, - &allspfrouters6, on->ospf6_if, oh); - } else { - ospf6_send(on->ospf6_if->linklocal_addr, - &on->linklocal_addr, on->ospf6_if, oh); - } + || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = on->linklocal_addr; + oi = on->ospf6_if; } else if (oi) { - - oi->ls_upd_out++; - if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) || (oi->state == OSPF6_INTERFACE_DR) - || (oi->state == OSPF6_INTERFACE_BDR)) { - ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh); - } else { - ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh); + || (oi->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = alldrouters6; + } + if (oi) { + ospf6_packet_add(oi, op); + OSPF6_MESSAGE_WRITE_ON(oi); + } +} + +static uint16_t ospf6_make_lsupdate_list(struct ospf6_neighbor *on, + struct ospf6_packet **op, int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) { + if ((length + (unsigned int)OSPF6_LSA_SIZE(lsa->header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + ospf6_send_lsupdate(on, NULL, *op); + + /* refresh packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, + on->ospf6_if, (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, on->lsupdate_list); + } + return length; +} + +static uint16_t ospf6_make_ls_retrans_list(struct ospf6_neighbor *on, + struct ospf6_packet **op, + int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(on->retrans_list, lsa, lsanext)) { + if ((length + (unsigned int)OSPF6_LSA_SIZE(lsa->header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + (*op)->dst = allspfrouters6; + else + (*op)->dst = on->linklocal_addr; + + ospf6_packet_add(on->ospf6_if, *op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + + /* refresh packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, + on->ospf6_if, (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); } + return length; } int ospf6_lsupdate_send_neighbor(struct thread *thread) { struct ospf6_neighbor *on; - struct ospf6_header *oh; - struct ospf6_lsupdate *lsupdate; - uint8_t *p; - int lsa_cnt; - struct ospf6_lsa *lsa, *lsanext; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + int lsa_cnt = 0; on = (struct ospf6_neighbor *)THREAD_ARG(thread); on->thread_send_lsupdate = (struct thread *)NULL; @@ -2038,119 +2453,41 @@ int ospf6_lsupdate_send_neighbor(struct thread *thread) return 0; } - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh - + sizeof(struct ospf6_header)); - - p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); - lsa_cnt = 0; - - /* lsupdate_list lists those LSA which doesn't need to be - retransmitted. remove those from the list */ - for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) { - /* MTU check */ - if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header)) - > ospf6_packet_max(on->ospf6_if)) { - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - - ospf6_send_lsupdate(on, NULL, oh); - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate - *)((caddr_t)oh - + sizeof(struct - ospf6_header)); - - p = (uint8_t *)((caddr_t)lsupdate - + sizeof(struct - ospf6_lsupdate)); - lsa_cnt = 0; - } - } - - ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); - memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header)); - p += OSPF6_LSA_SIZE(lsa->header); - lsa_cnt++; - - assert(lsa->lock == 2); - ospf6_lsdb_remove(lsa, on->lsupdate_list); - } - + /* first do lsupdate_list */ + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); + length += ospf6_make_lsupdate_list(on, &op, &lsa_cnt); if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - ospf6_send_lsupdate(on, NULL, oh); - } - - /* The addresses used for retransmissions are different from those sent - the - first time and so we need to separate them here. - */ - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh - + sizeof(struct ospf6_header)); - p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); - lsa_cnt = 0; - - for (ALL_LSDB(on->retrans_list, lsa, lsanext)) { - /* MTU check */ - if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header)) - > ospf6_packet_max(on->ospf6_if)) { - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - - if (on->ospf6_if->state - == OSPF6_INTERFACE_POINTTOPOINT) { - ospf6_send(on->ospf6_if->linklocal_addr, - &allspfrouters6, - on->ospf6_if, oh); - } else { - ospf6_send(on->ospf6_if->linklocal_addr, - &on->linklocal_addr, - on->ospf6_if, oh); - } - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate - *)((caddr_t)oh - + sizeof(struct - ospf6_header)); - p = (uint8_t *)((caddr_t)lsupdate - + sizeof(struct - ospf6_lsupdate)); - lsa_cnt = 0; - } - } - - ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); - memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header)); - p += OSPF6_LSA_SIZE(lsa->header); - lsa_cnt++; - } - + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; + ospf6_send_lsupdate(on, NULL, op); + + /* prepare new packet */ + op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_HEADER_SIZE; + lsa_cnt = 0; + } else { + stream_reset(op->s); + length = OSPF6_HEADER_SIZE; + } + + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); + /* now do retransmit list */ + length += ospf6_make_ls_retrans_list(on, &op, &lsa_cnt); if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) - ospf6_send(on->ospf6_if->linklocal_addr, - &allspfrouters6, on->ospf6_if, oh); + op->dst = allspfrouters6; else - ospf6_send(on->ospf6_if->linklocal_addr, - &on->linklocal_addr, on->ospf6_if, oh); - } + op->dst = on->linklocal_addr; + ospf6_packet_add(on->ospf6_if, op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + } else + ospf6_packet_free(op); if (on->lsupdate_list->count != 0) { on->thread_send_lsupdate = NULL; @@ -2168,44 +2505,78 @@ int ospf6_lsupdate_send_neighbor(struct thread *thread) int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on, struct ospf6_lsa *lsa) { - struct ospf6_header *oh; - struct ospf6_lsupdate *lsupdate; - uint8_t *p; - int lsa_cnt = 0; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh - + sizeof(struct ospf6_header)); + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); - p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); + /* skip over fixed header */ + stream_forward_endp(op->s, OSPF6_LS_UPD_MIN_SIZE); ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); - memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header)); - p += OSPF6_LSA_SIZE(lsa->header); - lsa_cnt++; - - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); + stream_put(op->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + length = OSPF6_HEADER_SIZE + OSPF6_LS_UPD_MIN_SIZE + + OSPF6_LSA_SIZE(lsa->header); + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, 1); + op->length = length; if (IS_OSPF6_DEBUG_FLOODING || IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR)) zlog_debug("%s: Send lsupdate with lsa %s (age %u)", __func__, lsa->name, ntohs(lsa->header->age)); - ospf6_send_lsupdate(on, NULL, oh); + ospf6_send_lsupdate(on, NULL, op); return 0; } +static uint16_t ospf6_make_lsupdate_interface(struct ospf6_interface *oi, + struct ospf6_packet **op, + int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) { + if (length + (unsigned int)OSPF6_LSA_SIZE(lsa->header) + + OSPF6_HEADER_SIZE + > ospf6_packet_max(oi)) { + ospf6_fill_header(oi, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + ospf6_send_lsupdate(NULL, oi, *op); + + /* refresh packet */ + *op = ospf6_packet_new(oi->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, + (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + } + + ospf6_lsa_age_update_to_send(lsa, oi->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, oi->lsupdate_list); + } + return length; +} + int ospf6_lsupdate_send_interface(struct thread *thread) { struct ospf6_interface *oi; - struct ospf6_header *oh; - struct ospf6_lsupdate *lsupdate; - uint8_t *p; - int lsa_cnt; - struct ospf6_lsa *lsa, *lsanext; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + int lsa_cnt = 0; oi = (struct ospf6_interface *)THREAD_ARG(thread); oi->thread_send_lsupdate = (struct thread *)NULL; @@ -2224,59 +2595,17 @@ int ospf6_lsupdate_send_interface(struct thread *thread) if (oi->lsupdate_list->count == 0) return 0; - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh - + sizeof(struct ospf6_header)); - - p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); - lsa_cnt = 0; - - for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) { - /* MTU check */ - if ((p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE(lsa->header))) - > ospf6_packet_max(oi)) { - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - - ospf6_send_lsupdate(NULL, oi, oh); - if (IS_OSPF6_DEBUG_MESSAGE( - OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) - zlog_debug("%s: LSUpdate length %d", - __func__, ntohs(oh->length)); - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate - *)((caddr_t)oh - + sizeof(struct - ospf6_header)); - - p = (uint8_t *)((caddr_t)lsupdate - + sizeof(struct - ospf6_lsupdate)); - lsa_cnt = 0; - } - } - - ospf6_lsa_age_update_to_send(lsa, oi->transdelay); - memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header)); - p += OSPF6_LSA_SIZE(lsa->header); - lsa_cnt++; - - assert(lsa->lock == 2); - ospf6_lsdb_remove(lsa, oi->lsupdate_list); - } - + op = ospf6_packet_new(oi->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, op->s); + length += ospf6_make_lsupdate_interface(oi, &op, &lsa_cnt); if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - - ospf6_send_lsupdate(NULL, oi, oh); - } + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; + ospf6_send_lsupdate(NULL, oi, op); + } else + ospf6_packet_free(op); if (oi->lsupdate_list->count > 0) { oi->thread_send_lsupdate = NULL; @@ -2290,10 +2619,8 @@ int ospf6_lsupdate_send_interface(struct thread *thread) int ospf6_lsack_send_neighbor(struct thread *thread) { struct ospf6_neighbor *on; - struct ospf6_header *oh; - uint8_t *p; - struct ospf6_lsa *lsa, *lsanext; - int lsa_cnt = 0; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; on = (struct ospf6_neighbor *)THREAD_ARG(thread); on->thread_send_lsack = (struct thread *)NULL; @@ -2310,53 +2637,24 @@ int ospf6_lsack_send_neighbor(struct thread *thread) if (on->lsack_list->count == 0) return 0; - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - - p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header)); - - for (ALL_LSDB(on->lsack_list, lsa, lsanext)) { - /* MTU check */ - if (p - sendbuf + sizeof(struct ospf6_lsa_header) - > ospf6_packet_max(on->ospf6_if)) { - /* if we run out of packet size/space here, - better to try again soon. */ - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons(p - sendbuf); - - on->ospf6_if->ls_ack_out++; + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, on->ospf6_if, op->s); - ospf6_send(on->ospf6_if->linklocal_addr, - &on->linklocal_addr, on->ospf6_if, - oh); - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - p = (uint8_t *)((caddr_t)oh - + sizeof(struct ospf6_header)); - lsa_cnt = 0; - } - } + length += ospf6_make_lsack_neighbor(on, &op); - ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); - memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header)); - p += sizeof(struct ospf6_lsa_header); - - assert(lsa->lock == 2); - ospf6_lsdb_remove(lsa, on->lsack_list); - lsa_cnt++; + if (length == OSPF6_HEADER_SIZE) { + ospf6_packet_free(op); + return 0; } - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons(p - sendbuf); - - on->ospf6_if->ls_ack_out++; + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); - ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); - } + /* Set packet length, dst and queue to FIFO. */ + op->length = length; + op->dst = on->linklocal_addr; + ospf6_packet_add(on->ospf6_if, op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); if (on->lsack_list->count > 0) thread_add_event(master, ospf6_lsack_send_neighbor, on, 0, @@ -2365,13 +2663,42 @@ int ospf6_lsack_send_neighbor(struct thread *thread) return 0; } +static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext; + + for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) { + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(oi)) { + /* if we run out of packet size/space here, + better to try again soon. */ + THREAD_OFF(oi->thread_send_lsack); + thread_add_event(master, ospf6_lsack_send_interface, oi, + 0, &oi->thread_send_lsack); + + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); + break; + } + ospf6_lsa_age_update_to_send(lsa, oi->transdelay); + stream_put(op->s, lsa->header, sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, oi->lsack_list); + } + return length; +} + int ospf6_lsack_send_interface(struct thread *thread) { struct ospf6_interface *oi; - struct ospf6_header *oh; - uint8_t *p; - struct ospf6_lsa *lsa, *lsanext; - int lsa_cnt = 0; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; oi = (struct ospf6_interface *)THREAD_ARG(thread); oi->thread_send_lsack = (struct thread *)NULL; @@ -2389,47 +2716,29 @@ int ospf6_lsack_send_interface(struct thread *thread) if (oi->lsack_list->count == 0) return 0; - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - - p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header)); + op = ospf6_packet_new(oi->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, oi, op->s); - for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) { - /* MTU check */ - if (p - sendbuf + sizeof(struct ospf6_lsa_header) - > ospf6_packet_max(oi)) { - /* if we run out of packet size/space here, - better to try again soon. */ - THREAD_OFF(oi->thread_send_lsack); - thread_add_event(master, ospf6_lsack_send_interface, oi, - 0, &oi->thread_send_lsack); + length += ospf6_make_lsack_interface(oi, op); - ospf6_lsa_unlock(lsa); - if (lsanext) - ospf6_lsa_unlock(lsanext); - break; - } - - ospf6_lsa_age_update_to_send(lsa, oi->transdelay); - memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header)); - p += sizeof(struct ospf6_lsa_header); - - assert(lsa->lock == 2); - ospf6_lsdb_remove(lsa, oi->lsack_list); - lsa_cnt++; + if (length == OSPF6_HEADER_SIZE) { + ospf6_packet_free(op); + return 0; } + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons(p - sendbuf); + /* Set packet length, dst and queue to FIFO. */ + op->length = length; + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) + || (oi->state == OSPF6_INTERFACE_DR) + || (oi->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = alldrouters6; - if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) - || (oi->state == OSPF6_INTERFACE_DR) - || (oi->state == OSPF6_INTERFACE_BDR)) - ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh); - else - ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh); - } + ospf6_packet_add(oi, op); + OSPF6_MESSAGE_WRITE_ON(oi); if (oi->lsack_list->count > 0) thread_add_event(master, ospf6_lsack_send_interface, oi, 0, @@ -2438,7 +2747,6 @@ int ospf6_lsack_send_interface(struct thread *thread) return 0; } - /* Commands */ DEFUN(debug_ospf6_message, debug_ospf6_message_cmd, "debug ospf6 message <unknown|hello|dbdesc|lsreq|lsupdate|lsack|all> [<send|recv|send-hdr|recv-hdr>]", diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index 7c108bd452..0cd10ef825 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -63,6 +63,27 @@ extern unsigned char conf_debug_ospf6_message[]; #define OSPF6_MESSAGE_TYPE_LSACK 0x5 /* Flooding acknowledgment */ #define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */ +struct ospf6_packet { + struct ospf6_packet *next; + + /* Pointer to data stream. */ + struct stream *s; + + /* IP destination address. */ + struct in6_addr dst; + + /* OSPF6 packet length. */ + uint16_t length; +}; + +/* OSPF packet queue structure. */ +struct ospf6_fifo { + unsigned long count; + + struct ospf6_packet *head; + struct ospf6_packet *tail; +}; + /* OSPFv3 packet header */ #define OSPF6_HEADER_SIZE 16U struct ospf6_header { @@ -136,6 +157,10 @@ extern void ospf6_lsreq_print(struct ospf6_header *, int action); extern void ospf6_lsupdate_print(struct ospf6_header *, int action); extern void ospf6_lsack_print(struct ospf6_header *, int action); +extern struct ospf6_fifo *ospf6_fifo_new(void); +extern void ospf6_fifo_flush(struct ospf6_fifo *fifo); +extern void ospf6_fifo_free(struct ospf6_fifo *fifo); + extern int ospf6_iobuf_size(unsigned int size); extern void ospf6_message_terminate(void); extern int ospf6_receive(struct thread *thread); diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 9323da8be3..8cf05183e1 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -145,6 +145,8 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on) THREAD_OFF(on->inactivity_timer); + THREAD_OFF(on->last_dbdesc_release_timer); + THREAD_OFF(on->thread_send_dbdesc); THREAD_OFF(on->thread_send_lsreq); THREAD_OFF(on->thread_send_lsupdate); @@ -350,6 +352,16 @@ int negotiation_done(struct thread *thread) return 0; } +static int ospf6_neighbor_last_dbdesc_release(struct thread *thread) +{ + struct ospf6_neighbor *on = THREAD_ARG(thread); + + assert(on); + memset(&on->dbdesc_last, 0, sizeof(struct ospf6_dbdesc)); + + return 0; +} + int exchange_done(struct thread *thread) { struct ospf6_neighbor *on; @@ -366,10 +378,13 @@ int exchange_done(struct thread *thread) THREAD_OFF(on->thread_send_dbdesc); ospf6_lsdb_remove_all(on->dbdesc_list); - /* XXX - thread_add_timer (master, ospf6_neighbor_last_dbdesc_release, on, - on->ospf6_if->dead_interval); - */ + /* RFC 2328 (10.8): Release the last dbdesc after dead_interval */ + if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) { + THREAD_OFF(on->last_dbdesc_release_timer); + thread_add_timer(master, ospf6_neighbor_last_dbdesc_release, on, + on->ospf6_if->dead_interval, + &on->last_dbdesc_release_timer); + } if (on->request_list->count == 0) ospf6_neighbor_state_change(OSPF6_NEIGHBOR_FULL, on, @@ -969,10 +984,9 @@ static void ospf6_neighbor_show_detail(struct vty *vty, } } -static void ospf6_neighbor_show_detail_common(struct vty *vty, int argc, - struct cmd_token **argv, - struct ospf6 *ospf6, int idx_type, - int detail_idx, int json_idx) +static void ospf6_neighbor_show_detail_common(struct vty *vty, + struct ospf6 *ospf6, bool uj, + bool detail, bool drchoice) { struct ospf6_neighbor *on; struct ospf6_interface *oi; @@ -980,18 +994,15 @@ static void ospf6_neighbor_show_detail_common(struct vty *vty, int argc, struct listnode *i, *j, *k; json_object *json = NULL; json_object *json_array = NULL; - bool uj = use_json(argc, argv); void (*showfunc)(struct vty *, struct ospf6_neighbor *, json_object *json, bool use_json); - showfunc = ospf6_neighbor_show; - - if ((uj && argc == detail_idx) || (!uj && argc == json_idx)) { - if (!strncmp(argv[idx_type]->arg, "de", 2)) - showfunc = ospf6_neighbor_show_detail; - else if (!strncmp(argv[idx_type]->arg, "dr", 2)) - showfunc = ospf6_neighbor_show_drchoice; - } + if (detail) + showfunc = ospf6_neighbor_show_detail; + else if (drchoice) + showfunc = ospf6_neighbor_show_drchoice; + else + showfunc = ospf6_neighbor_show; if (uj) { json = json_object_new_object(); @@ -1036,28 +1047,28 @@ DEFUN(show_ipv6_ospf6_neighbor, show_ipv6_ospf6_neighbor_cmd, "Display details\n" "Display DR choices\n" JSON_STR) { - int idx_type = 4; - int detail_idx = 5; - int json_idx = 6; struct ospf6 *ospf6; struct listnode *node; const char *vrf_name = NULL; bool all_vrf = false; int idx_vrf = 0; + int idx_type = 4; + bool uj = use_json(argc, argv); + bool detail = false; + bool drchoice = false; OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - if (idx_vrf > 0) { - idx_type += 2; - detail_idx += 2; - json_idx += 2; - } + + if (argv_find(argv, argc, "detail", &idx_type)) + detail = true; + else if (argv_find(argv, argc, "drchoice", &idx_type)) + drchoice = true; for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { - ospf6_neighbor_show_detail_common(vty, argc, argv, - ospf6, idx_type, - detail_idx, json_idx); + ospf6_neighbor_show_detail_common(vty, ospf6, uj, + detail, drchoice); if (!all_vrf) break; } diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 47f8c834e2..729b1d2e85 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -47,6 +47,10 @@ struct ospf6_neighbor { uint32_t state_change; struct timeval last_changed; + /* last received hello */ + struct timeval last_hello; + uint32_t hello_in; + /* Neighbor Router ID */ in_addr_t router_id; @@ -89,6 +93,9 @@ struct ospf6_neighbor { /* Inactivity timer */ struct thread *inactivity_timer; + /* Timer to release the last dbdesc packet */ + struct thread *last_dbdesc_release_timer; + /* Thread for sending message */ struct thread *thread_send_dbdesc; struct thread *thread_send_lsreq; diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index 76f98fecdd..5961cfe66a 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -257,10 +257,13 @@ int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst, rmsghdr.msg_control = (caddr_t)cmsgbuf; rmsghdr.msg_controllen = sizeof(cmsgbuf); - retval = recvmsg(ospf6_sock, &rmsghdr, 0); - if (retval < 0) - zlog_warn("recvmsg failed: %s", safe_strerror(errno)); - else if (retval == iov_totallen(message)) + retval = recvmsg(ospf6_sock, &rmsghdr, MSG_DONTWAIT); + if (retval < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + zlog_warn("stream_recvmsg failed: %s", + safe_strerror(errno)); + return retval; + } else if (retval == iov_totallen(message)) zlog_warn("recvmsg read full buffer size: %d", retval); /* source address */ diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h index 08d8be4445..3886a0d263 100644 --- a/ospf6d/ospf6_network.h +++ b/ospf6d/ospf6_network.h @@ -36,4 +36,19 @@ extern int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst, ifindex_t *ifindex, struct iovec *message, int ospf6_sock); +#define OSPF6_MESSAGE_WRITE_ON(oi) \ + do { \ + bool list_was_empty = \ + list_isempty(oi->area->ospf6->oi_write_q); \ + if ((oi)->on_write_q == 0) { \ + listnode_add(oi->area->ospf6->oi_write_q, (oi)); \ + (oi)->on_write_q = 1; \ + } \ + if (list_was_empty \ + && !list_isempty(oi->area->ospf6->oi_write_q)) \ + thread_add_write(master, ospf6_write, oi->area->ospf6, \ + oi->area->ospf6->fd, \ + &oi->area->ospf6->t_write); \ + } while (0) + #endif /* OSPF6_NETWORK_H */ diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c index dfae51cec1..9f8cdf8fb7 100644 --- a/ospf6d/ospf6_nssa.c +++ b/ospf6d/ospf6_nssa.c @@ -139,6 +139,7 @@ void ospf6_abr_nssa_check_status(struct ospf6 *ospf6) struct listnode *lnode, *nnode; for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, nnode, area)) { + uint8_t old_state = area->NSSATranslatorState; if (IS_OSPF6_DEBUG_NSSA) zlog_debug("%s: checking area %s flag %x", __func__, @@ -177,16 +178,21 @@ void ospf6_abr_nssa_check_status(struct ospf6 *ospf6) } } } + + /* RFC3101, 3.1: + * All NSSA border routers must set the E-bit in the Type-1 + * router-LSAs of their directly attached non-stub areas, even + * when they are not translating. + */ + if (old_state != area->NSSATranslatorState) { + if (old_state == OSPF6_NSSA_TRANSLATE_DISABLED) + ospf6_asbr_status_update(ospf6, + ++ospf6->redist_count); + else + ospf6_asbr_status_update(ospf6, + --ospf6->redist_count); + } } - /* RFC3101, 3.1: - * All NSSA border routers must set the E-bit in the Type-1 - * router-LSAs of their directly attached non-stub areas, even - * when they are not translating. - */ - if (CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR) && (ospf6->anyNSSA)) - ospf6_asbr_status_update(ospf6, ++ospf6->redist_count); - else - ospf6_asbr_status_update(ospf6, --ospf6->redist_count); } /* Mark the summary LSA's as unapproved, when ABR status changes.*/ diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index cd1b5b99f8..6cea5032bd 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -232,6 +232,8 @@ void ospf6_vrf_init(void) { vrf_init(ospf6_vrf_new, ospf6_vrf_enable, ospf6_vrf_disable, ospf6_vrf_delete, ospf6_vrf_enable); + + vrf_cmd_init(NULL, &ospf6d_privs); } static void ospf6_top_lsdb_hook_add(struct ospf6_lsa *lsa) @@ -406,6 +408,7 @@ static struct ospf6 *ospf6_create(const char *name) o->external_id_table = route_table_init(); + o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT; o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; o->distance_table = route_table_init(); @@ -413,6 +416,8 @@ static struct ospf6 *ospf6_create(const char *name) o->max_multipath = MULTIPATH_NUM; + o->oi_write_q = list_new(); + QOBJ_REG(o, ospf6); /* Make ospf protocol socket. */ @@ -482,6 +487,7 @@ void ospf6_delete(struct ospf6 *o) ospf6_distance_reset(o); route_table_finish(o->distance_table); + list_delete(&o->oi_write_q); if (o->vrf_id != VRF_UNKNOWN) { vrf = vrf_lookup_by_id(o->vrf_id); @@ -1665,6 +1671,11 @@ static int config_write_ospf6(struct vty *vty) vty_out(vty, " auto-cost reference-bandwidth %d\n", ospf6->ref_bandwidth); + if (ospf6->write_oi_count + != OSPF6_WRITE_INTERFACE_COUNT_DEFAULT) + vty_out(vty, " write-multiplier %d\n", + ospf6->write_oi_count); + /* LSA timers print. */ if (ospf6->lsa_minarrival != OSPF_MIN_LS_ARRIVAL) vty_out(vty, " timers lsa min-arrival %d\n", diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index e4dfebe1de..5b739ac76b 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -128,7 +128,10 @@ struct ospf6 { struct thread *maxage_remover; struct thread *t_distribute_update; /* Distirbute update timer. */ struct thread *t_ospf6_receive; /* OSPF6 receive timer */ +#define OSPF6_WRITE_INTERFACE_COUNT_DEFAULT 20 + struct thread *t_write; + int write_oi_count; /* Num of packets sent per thread invocation */ uint32_t ref_bandwidth; /* Distance parameters */ @@ -150,6 +153,7 @@ struct ospf6 { /* Count of NSSA areas */ uint8_t anyNSSA; struct thread *t_abr_task; /* ABR task timer. */ + struct list *oi_write_q; uint32_t redist_count; QOBJ_FIELDS; diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 49829d86f1..c850df55bb 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2084,6 +2084,8 @@ void ospf_external_lsa_rid_change(struct ospf *ospf) { struct external_info *ei; struct ospf_external_aggr_rt *aggr; + struct ospf_lsa *lsa = NULL; + int force; int type; for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { @@ -2112,24 +2114,48 @@ void ospf_external_lsa_rid_change(struct ospf *ospf) continue; if (is_prefix_default( - (struct prefix_ipv4 *)&ei->p)) + (struct prefix_ipv4 *)&ei->p)) continue; - if (!ospf_redistribute_check(ospf, ei, NULL)) - continue; + lsa = ospf_external_info_find_lsa(ospf, &ei->p); aggr = ospf_external_aggr_match(ospf, &ei->p); if (aggr) { + + if (!ospf_redistribute_check(ospf, ei, + NULL)) + continue; + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) zlog_debug( "Originate Summary LSA after reset/router-ID change"); + /* Here the LSA is originated as new */ ospf_originate_summary_lsa(ospf, aggr, ei); - } else if (!ospf_external_lsa_originate(ospf, - ei)) - flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, - "LSA: AS-external-LSA was not originated."); + } else if (lsa) { + /* LSA needs to be refreshed even if + * there is no change in the route + * params if the LSA is in maxage. + */ + if (IS_LSA_MAXAGE(lsa)) + force = LSA_REFRESH_FORCE; + else + force = LSA_REFRESH_IF_CHANGED; + + ospf_external_lsa_refresh(ospf, lsa, + ei, force, 0); + } else { + if (!ospf_redistribute_check(ospf, ei, + NULL)) + continue; + + if (!ospf_external_lsa_originate(ospf, + NULL)) + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "LSA: AS-external-LSA was not originated."); + } } } } diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c index a1b35b2fcd..8725497f2d 100644 --- a/ospfd/ospf_neighbor.c +++ b/ospfd/ospf_neighbor.c @@ -407,6 +407,9 @@ void ospf_renegotiate_optional_capabilities(struct ospf *top) } } + /* Refresh/Re-originate external LSAs (Type-7 and Type-5).*/ + ospf_external_lsa_rid_change(top); + return; } diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index cb64187d72..54ce248d89 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -12959,6 +12959,8 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_max_multipath_cmd); install_element(OSPF_NODE, &no_ospf_max_multipath_cmd); + vrf_cmd_init(NULL, &ospfd_privs); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/pathd/path_errors.c b/pathd/path_errors.c index f8560a848c..112a3d5ee9 100644 --- a/pathd/path_errors.c +++ b/pathd/path_errors.c @@ -61,6 +61,12 @@ static struct log_ref ferr_path_warn[] = { .suggestion = "Check the connectivity between the PCC and the PCE" }, { + .code = EC_PATH_PCEP_PROTOCOL_ERROR, + .title = "PCEP protocol error", + .description = "The PCE did not respect the PCEP protocol", + .suggestion = "Open an Issue with all relevant log files" + }, + { .code = EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, .title = "PCC connection error", .description = "The PCEP module did not try to connect because it is missing a source address", diff --git a/pathd/path_errors.h b/pathd/path_errors.h index 72e127f26b..5d05b6650b 100644 --- a/pathd/path_errors.h +++ b/pathd/path_errors.h @@ -28,6 +28,7 @@ enum path_log_refs { EC_PATH_PCEP_PCC_FINI, EC_PATH_PCEP_PCC_CONF_UPDATE, EC_PATH_PCEP_LIB_CONNECT, + EC_PATH_PCEP_PROTOCOL_ERROR, EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c index 5b0f5b44e5..e1fbb37f55 100644 --- a/pathd/path_nb_config.c +++ b/pathd/path_nb_config.c @@ -266,7 +266,7 @@ int pathd_srte_policy_create(struct nb_cb_create_args *args) color = yang_dnode_get_uint32(args->dnode, "./color"); yang_dnode_get_ip(&endpoint, args->dnode, "./endpoint"); - policy = srte_policy_add(color, &endpoint); + policy = srte_policy_add(color, &endpoint, SRTE_ORIGIN_LOCAL, NULL); nb_running_set_entry(args->dnode, policy); SET_FLAG(policy->flags, F_POLICY_NEW); @@ -388,7 +388,8 @@ int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args) policy = nb_running_get_entry(args->dnode, NULL, true); preference = yang_dnode_get_uint32(args->dnode, "./preference"); - candidate = srte_candidate_add(policy, preference); + candidate = + srte_candidate_add(policy, preference, SRTE_ORIGIN_LOCAL, NULL); nb_running_set_entry(args->dnode, candidate); SET_FLAG(candidate->flags, F_CANDIDATE_NEW); diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c index ad24c2eb02..ce631eb7b0 100644 --- a/pathd/path_pcep.c +++ b/pathd/path_pcep.c @@ -37,6 +37,7 @@ #include "pathd/path_pcep_controller.h" #include "pathd/path_pcep_lib.h" #include "pathd/path_pcep_config.h" +#include "pathd/path_pcep_debug.h" DEFINE_MTYPE(PATHD, PCEP, "PCEP module"); @@ -51,6 +52,7 @@ static int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id, void *payload); static int pcep_main_event_start_sync(int pcc_id); static int pcep_main_event_start_sync_cb(struct path *path, void *arg); +static int pcep_main_event_initiate_candidate(struct path *path); static int pcep_main_event_update_candidate(struct path *path); static int pcep_main_event_remove_candidate_segments(const char *originator, bool force); @@ -64,6 +66,9 @@ static int pathd_candidate_removed_handler(struct srte_candidate *candidate); static struct path_metric *pcep_copy_metrics(struct path_metric *metric); static struct path_hop *pcep_copy_hops(struct path_hop *hop); +/* Other static functions */ +static void notify_status(struct path *path, bool not_changed); + /* Module Functions */ static int pcep_module_finish(void); static int pcep_module_late_init(struct thread_master *tm); @@ -165,6 +170,21 @@ void pcep_free_path(struct path *path) XFREE(MTYPE_PCEP, path); } +/* ------------ Other Static Functions ------------ */ + +void notify_status(struct path *path, bool not_changed) +{ + struct path *resp = NULL; + + if ((resp = path_pcep_config_get_path(&path->nbkey))) { + resp->srp_id = path->srp_id; + flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + "(%s) Send report for candidate path %s", __func__, + path->name); + pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp, + not_changed); + } +} /* ------------ Main Thread Even Handler ------------ */ @@ -177,6 +197,11 @@ int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id, case PCEP_MAIN_EVENT_START_SYNC: ret = pcep_main_event_start_sync(pcc_id); break; + case PCEP_MAIN_EVENT_INITIATE_CANDIDATE: + assert(payload != NULL); + ret = pcep_main_event_initiate_candidate( + (struct path *)payload); + break; case PCEP_MAIN_EVENT_UPDATE_CANDIDATE: assert(payload != NULL); ret = pcep_main_event_update_candidate((struct path *)payload); @@ -209,19 +234,49 @@ int pcep_main_event_start_sync_cb(struct path *path, void *arg) return 1; } +int pcep_main_event_initiate_candidate(struct path *path) +{ + int ret = 0; + + ret = path_pcep_config_initiate_path(path); + if (path->do_remove) { + struct pcep_error *error; + error = XCALLOC(MTYPE_PCEP, sizeof(*error)); + error->path = path; + error->error_type = PCEP_ERRT_INVALID_OPERATION; + switch (ret) { + case ERROR_19_1: + error->error_value = + PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP; + break; + case ERROR_19_3: + error->error_value = + PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID; + break; + case ERROR_19_9: + error->error_value = PCEP_ERRV_LSP_NOT_PCE_INITIATED; + break; + default: + zlog_warn("(%s)PCE tried to REMOVE unknown error!", + __func__); + XFREE(MTYPE_PCEP, error); + pcep_free_path(path); + return ret; + break; + } + pcep_ctrl_send_error(pcep_g->fpt, path->pcc_id, error); + } else if (ret != PATH_NB_ERR && path->srp_id != 0) + notify_status(path, ret == PATH_NB_NO_CHANGE); + return ret; +} + int pcep_main_event_update_candidate(struct path *path) { - struct path *resp = NULL; int ret = 0; ret = path_pcep_config_update_path(path); - if (ret != PATH_NB_ERR && path->srp_id != 0) { - if ((resp = path_pcep_config_get_path(&path->nbkey))) { - resp->srp_id = path->srp_id; - pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp, - ret == PATH_NB_NO_CHANGE); - } - } + if (ret != PATH_NB_ERR && path->srp_id != 0) + notify_status(path, ret == PATH_NB_NO_CHANGE); return ret; } diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h index d0af674ff9..597a4b6c83 100644 --- a/pathd/path_pcep.h +++ b/pathd/path_pcep.h @@ -317,6 +317,16 @@ struct pcep_glob { extern struct pcep_glob *pcep_g; +struct pcep_error { + struct path *path; + int error_type; + int error_value; + /* Rfc 8281 PcInitiated error on bad values */ +#define ERROR_19_1 1 +#define ERROR_19_3 2 +#define ERROR_19_9 3 +}; + /* Path Helper Functions */ struct path *pcep_new_path(void); struct path_hop *pcep_new_hop(void); diff --git a/pathd/path_pcep_config.c b/pathd/path_pcep_config.c index 609f091559..0349618304 100644 --- a/pathd/path_pcep_config.c +++ b/pathd/path_pcep_config.c @@ -32,6 +32,8 @@ #define MAX_FLOAT_LEN 22 #define INETADDR4_MAXLEN 16 #define INETADDR6_MAXLEN 40 +#define INITIATED_CANDIDATE_PREFERENCE 255 +#define INITIATED_POLICY_COLOR 1 static void copy_candidate_objfun_info(struct srte_candidate *candidate, @@ -147,7 +149,7 @@ struct path *candidate_to_path(struct srte_candidate *candidate) .plsp_id = 0, .name = name, .type = candidate->type, - .srp_id = 0, + .srp_id = policy->srp_id, .req_id = 0, .binding_sid = policy->binding_sid, .status = status, @@ -276,6 +278,93 @@ path_pcep_config_list_path_hops(struct srte_segment_list *segment_list) return hop; } +int path_pcep_config_initiate_path(struct path *path) +{ + struct srte_policy *policy; + struct srte_candidate *candidate; + + if (path->do_remove) { + zlog_warn("PCE %s tried to REMOVE pce-initiate a path ", + path->originator); + candidate = lookup_candidate(&path->nbkey); + if (candidate) { + if (!path->is_delegated) { + zlog_warn( + "(%s)PCE tried to REMOVE but it's not Delegated!", + __func__); + return ERROR_19_1; + } + if (candidate->type != SRTE_CANDIDATE_TYPE_DYNAMIC) { + zlog_warn( + "(%s)PCE tried to REMOVE but it's not PCE origin!", + __func__); + return ERROR_19_9; + } + zlog_warn( + "(%s)PCE tried to REMOVE found canidate!, let's remove", + __func__); + candidate->policy->srp_id = path->srp_id; + SET_FLAG(candidate->policy->flags, F_POLICY_DELETED); + SET_FLAG(candidate->flags, F_CANDIDATE_DELETED); + } else { + zlog_warn("(%s)PCE tried to REMOVE not existing LSP!", + __func__); + return ERROR_19_3; + } + srte_apply_changes(); + } else { + assert(!IS_IPADDR_NONE(&path->nbkey.endpoint)); + + if (path->nbkey.preference == 0) + path->nbkey.preference = INITIATED_CANDIDATE_PREFERENCE; + + if (path->nbkey.color == 0) + path->nbkey.color = INITIATED_POLICY_COLOR; + + candidate = lookup_candidate(&path->nbkey); + if (!candidate) { + policy = srte_policy_add( + path->nbkey.color, &path->nbkey.endpoint, + SRTE_ORIGIN_PCEP, path->originator); + strlcpy(policy->name, path->name, sizeof(policy->name)); + policy->binding_sid = path->binding_sid; + SET_FLAG(policy->flags, F_POLICY_NEW); + candidate = srte_candidate_add( + policy, path->nbkey.preference, + SRTE_ORIGIN_PCEP, path->originator); + strlcpy(candidate->name, path->name, + sizeof(candidate->name)); + SET_FLAG(candidate->flags, F_CANDIDATE_NEW); + } else { + policy = candidate->policy; + if ((path->originator != candidate->originator) + || (path->originator != policy->originator)) { + /* There is already an initiated path from + * another PCE, show a warning and regect the + * initiated path */ + zlog_warn( + "PCE %s tried to initiate a path already initiated by PCE %s", + path->originator, + candidate->originator); + return 1; + } + if ((policy->protocol_origin != SRTE_ORIGIN_PCEP) + || (candidate->protocol_origin + != SRTE_ORIGIN_PCEP)) { + /* There is already an initiated path from + * another PCE, show a warning and regect the + * initiated path */ + zlog_warn( + "PCE %s tried to initiate a path created localy", + path->originator); + return 1; + } + } + return path_pcep_config_update_path(path); + } + return 0; +} + int path_pcep_config_update_path(struct path *path) { assert(path != NULL); @@ -381,8 +470,12 @@ struct srte_candidate *lookup_candidate(struct lsp_nb_key *key) char *candidate_name(struct srte_candidate *candidate) { - return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name, - candidate->name); + if (candidate->protocol_origin == SRTE_ORIGIN_PCEP + || candidate->protocol_origin == SRTE_ORIGIN_BGP) + return asprintfrr(MTYPE_PCEP, "%s", candidate->policy->name); + else + return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name, + candidate->name); } enum pcep_lsp_operational_status diff --git a/pathd/path_pcep_config.h b/pathd/path_pcep_config.h index 223dd10c82..e56d497aa0 100644 --- a/pathd/path_pcep_config.h +++ b/pathd/path_pcep_config.h @@ -38,6 +38,7 @@ typedef int (*path_list_cb_t)(struct path *path, void *arg); void path_pcep_refine_path(struct path *path); struct path *path_pcep_config_get_path(struct lsp_nb_key *key); void path_pcep_config_list_path(path_list_cb_t cb, void *arg); +int path_pcep_config_initiate_path(struct path *path); int path_pcep_config_update_path(struct path *path); struct path *candidate_to_path(struct srte_candidate *candidate); diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c index 528dcc3539..449c40c16c 100644 --- a/pathd/path_pcep_controller.c +++ b/pathd/path_pcep_controller.c @@ -57,6 +57,7 @@ enum pcep_ctrl_event_type { EV_PCEPLIB_EVENT, EV_RESET_PCC_SESSION, EV_SEND_REPORT, + EV_SEND_ERROR, EV_PATH_REFINED }; @@ -328,6 +329,14 @@ int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id, } +int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id, + struct pcep_error *error) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return send_to_thread(ctrl_state, pcc_id, EV_SEND_ERROR, 0, error); +} + + /* ------------ Internal Functions Called from Main Thread ------------ */ int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res) @@ -368,6 +377,13 @@ void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id, path); } +void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id, + struct path *path) +{ + send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_INITIATE_CANDIDATE, + path); +} + void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) { @@ -743,6 +759,7 @@ int pcep_thread_event_handler(struct thread *thread) struct pcep_refine_path_event_data *refine_data = NULL; struct path *path_copy = NULL; + struct pcep_error *error = NULL; switch (type) { case EV_UPDATE_PCC_OPTS: @@ -818,6 +835,13 @@ int pcep_thread_event_handler(struct thread *thread) refine_data = (struct pcep_refine_path_event_data *)payload; pcep_thread_path_refined_event(ctrl_state, refine_data); break; + case EV_SEND_ERROR: + assert(payload != NULL); + error = (struct pcep_error *)payload; + pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id); + pcep_pcc_send_error(ctrl_state, pcc_state, error, + (bool)sub_type); + break; default: flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, "Unexpected event received in controller thread: %u", diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h index 1b7c3a4c72..f55cc0db72 100644 --- a/pathd/path_pcep_controller.h +++ b/pathd/path_pcep_controller.h @@ -27,6 +27,7 @@ struct pcc_state; enum pcep_main_event_type { PCEP_MAIN_EVENT_UNDEFINED = 0, PCEP_MAIN_EVENT_START_SYNC, + PCEP_MAIN_EVENT_INITIATE_CANDIDATE, PCEP_MAIN_EVENT_UPDATE_CANDIDATE, PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP, }; @@ -137,10 +138,15 @@ struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt, int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id, struct path *path, bool is_stable); +int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id, + struct pcep_error *error); + /* Functions called from the controller thread */ void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id); void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id, struct path *path); +void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id, + struct path *path); void pcep_thread_cancel_timer(struct thread **thread); void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id, int retry_count, struct thread **thread); diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c index e14f6bc4a5..b0802ae6c3 100644 --- a/pathd/path_pcep_debug.c +++ b/pathd/path_pcep_debug.c @@ -780,6 +780,10 @@ const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type) switch (tlv_type) { case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR: return "NO_PATH_VECTOR"; + case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST: + return "OBJECTIVE_FUNCTION_LIST"; + case PCEP_OBJ_TLV_TYPE_VENDOR_INFO: + return "VENDOR_INFO"; case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY: return "STATEFUL_PCE_CAPABILITY"; case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME: @@ -802,6 +806,18 @@ const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type) return "PATH_SETUP_TYPE"; case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY: return "PATH_SETUP_TYPE_CAPABILITY"; + case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID: + return "SRPOLICY_POL_ID"; + case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME: + return "SRPOLICY_POL_NAME"; + case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID: + return "SRPOLICY_CPATH_ID"; + case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE: + return "SRPOLICY_CPATH_PREFERENCE"; + case PCEP_OBJ_TLV_TYPE_UNKNOWN: + return "UNKNOWN"; + case PCEP_OBJ_TLV_TYPE_ARBITRARY: + return "ARBITRARY"; default: return "UNKNOWN"; } diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c index e9d699de47..9fc8c091f8 100644 --- a/pathd/path_pcep_lib.c +++ b/pathd/path_pcep_lib.c @@ -35,6 +35,7 @@ DEFINE_MTYPE_STATIC(PATHD, PCEPLIB_MESSAGES, "PCEPlib PCEP Messages"); #define DEFAULT_LSAP_SETUP_PRIO 4 #define DEFAULT_LSAP_HOLDING_PRIO 4 #define DEFAULT_LSAP_LOCAL_PRETECTION false +#define MAX_PATH_NAME_SIZE 255 /* pceplib logging callback */ static int pceplib_logging_cb(int level, const char *fmt, va_list args); @@ -76,8 +77,18 @@ static void pcep_lib_parse_srp(struct path *path, struct pcep_object_srp *srp); static void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp); static void pcep_lib_parse_lspa(struct path *path, struct pcep_object_lspa *lspa); +static void pcep_lib_parse_lsp_symbolic_name( + struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv); static void pcep_lib_parse_metric(struct path *path, struct pcep_object_metric *obj); +static void +pcep_lib_parse_endpoints_ipv4(struct path *path, + struct pcep_object_endpoints_ipv4 *obj); +static void +pcep_lib_parse_endpoints_ipv6(struct path *path, + struct pcep_object_endpoints_ipv6 *obj); +static void pcep_lib_parse_vendor_info(struct path *path, + struct pcep_object_vendor_info *obj); static void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero); static struct path_hop *pcep_lib_parse_ero_sr(struct path_hop *next, struct pcep_ro_subobj_sr *sr); @@ -160,7 +171,7 @@ pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr, } config->support_stateful_pce_lsp_update = true; - config->support_pce_lsp_instantiation = false; + config->support_pce_lsp_instantiation = pcep_options->pce_initiated; config->support_include_db_version = false; config->support_lsp_triggered_resync = false; config->support_lsp_delta_sync = false; @@ -381,9 +392,25 @@ struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps, } } -struct pcep_message *pcep_lib_format_error(int error_type, int error_value) +struct pcep_message *pcep_lib_format_error(int error_type, int error_value, + struct path *path) { - return pcep_msg_create_error(error_type, error_value); + double_linked_list *objs, *srp_tlvs; + struct pcep_object_srp *srp; + struct pcep_object_tlv_header *tlv; + + if ((path == NULL) || (path->srp_id == 0)) + return pcep_msg_create_error(error_type, error_value); + + objs = dll_initialize(); + srp_tlvs = dll_initialize(); + tlv = (struct pcep_object_tlv_header *)pcep_tlv_create_path_setup_type( + SR_TE_PST); + dll_append(srp_tlvs, tlv); + srp = pcep_obj_create_srp(path->do_remove, path->srp_id, srp_tlvs); + dll_append(objs, srp); + return pcep_msg_create_error_with_objects(error_type, error_value, + objs); } struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid) @@ -417,6 +444,9 @@ struct path *pcep_lib_parse_path(struct pcep_message *msg) struct pcep_object_metric *metric = NULL; struct pcep_object_bandwidth *bandwidth = NULL; struct pcep_object_objective_function *of = NULL; + struct pcep_object_endpoints_ipv4 *epv4 = NULL; + struct pcep_object_endpoints_ipv6 *epv6 = NULL; + struct pcep_object_vendor_info *vendor_info = NULL; path = pcep_new_path(); @@ -470,6 +500,21 @@ struct path *pcep_lib_parse_path(struct pcep_message *msg) path->has_pce_objfun = true; path->pce_objfun = of->of_code; break; + case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS, + PCEP_OBJ_TYPE_ENDPOINT_IPV4): + epv4 = (struct pcep_object_endpoints_ipv4 *)obj; + pcep_lib_parse_endpoints_ipv4(path, epv4); + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS, + PCEP_OBJ_TYPE_ENDPOINT_IPV6): + epv6 = (struct pcep_object_endpoints_ipv6 *)obj; + pcep_lib_parse_endpoints_ipv6(path, epv6); + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_VENDOR_INFO, + PCEP_OBJ_TYPE_VENDOR_INFO): + vendor_info = (struct pcep_object_vendor_info *)obj; + pcep_lib_parse_vendor_info(path, vendor_info); + break; default: flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT, "Unexpected PCEP object %s (%u) / %s (%u)", @@ -632,7 +677,8 @@ double_linked_list *pcep_lib_format_path(struct pcep_caps *caps, tlv = (struct pcep_object_tlv_header *) pcep_tlv_create_tlv_arbitrary( binding_sid_lsp_tlv_data, - sizeof(binding_sid_lsp_tlv_data), 65505); + sizeof(binding_sid_lsp_tlv_data), + PCEP_OBJ_TYPE_CISCO_BSID); assert(tlv != NULL); dll_append(lsp_tlvs, tlv); } @@ -855,6 +901,12 @@ void pcep_lib_parse_rp(struct path *path, struct pcep_object_rp *rp) double_linked_list_node *node; struct pcep_object_tlv_header *tlv; + if (tlvs == NULL) { + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + "Unexpected Empty RP's TLV plsp-id:(%d)", + path ? (int32_t)path->plsp_id : -1); + return; + } /* We ignore the other flags and priority for now */ path->req_id = rp->request_id; path->has_pce_objfun = false; @@ -884,6 +936,12 @@ void pcep_lib_parse_srp(struct path *path, struct pcep_object_srp *srp) path->do_remove = srp->flag_lsp_remove; path->srp_id = srp->srp_id_number; + if (tlvs == NULL) { + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + "Unexpected Empty SRP's TLV plsp-id:(%d)", + path ? (int32_t)path->plsp_id : -1); + return; + } for (node = tlvs->head; node != NULL; node = node->next_node) { tlv = (struct pcep_object_tlv_header *)node->data; switch (tlv->type) { @@ -904,6 +962,8 @@ void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp) double_linked_list *tlvs = lsp->header.tlv_list; double_linked_list_node *node; struct pcep_object_tlv_header *tlv; + struct pcep_object_tlv_symbolic_path_name *name; + struct pcep_object_tlv_arbitrary *arb_tlv; path->plsp_id = lsp->plsp_id; path->status = lsp->operational_status; @@ -913,12 +973,27 @@ void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp) path->is_synching = lsp->flag_s; path->is_delegated = lsp->flag_d; - if (tlvs == NULL) + if (tlvs == NULL) { + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + "Unexpected Empty LSP's TLV plsp-id:(%d)", + path ? (int32_t)path->plsp_id : -1); return; + } for (node = tlvs->head; node != NULL; node = node->next_node) { tlv = (struct pcep_object_tlv_header *)node->data; switch (tlv->type) { + case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME: + name = (struct pcep_object_tlv_symbolic_path_name *)tlv; + pcep_lib_parse_lsp_symbolic_name(path, name); + break; + case PCEP_OBJ_TYPE_CISCO_BSID: + arb_tlv = (struct pcep_object_tlv_arbitrary *)tlv; + memcpy(&path->binding_sid, arb_tlv->data + 2, + sizeof(path->binding_sid)); + path->binding_sid = ntohl(path->binding_sid); + path->binding_sid = (path->binding_sid >> 12); + break; default: flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, "Unexpected LSP TLV %s (%u)", @@ -928,6 +1003,16 @@ void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp) } } +void pcep_lib_parse_lsp_symbolic_name( + struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv) +{ + uint16_t size = tlv->symbolic_path_name_length; + assert(path->name == NULL); + size = size > MAX_PATH_NAME_SIZE ? MAX_PATH_NAME_SIZE : size; + path->name = XCALLOC(MTYPE_PCEP, size); + strlcpy((char *)path->name, tlv->symbolic_path_name, size + 1); +} + void pcep_lib_parse_lspa(struct path *path, struct pcep_object_lspa *lspa) { path->has_affinity_filters = true; @@ -952,6 +1037,34 @@ void pcep_lib_parse_metric(struct path *path, struct pcep_object_metric *obj) path->first_metric = metric; } +void pcep_lib_parse_endpoints_ipv4(struct path *path, + struct pcep_object_endpoints_ipv4 *obj) +{ + SET_IPADDR_V4(&path->pcc_addr); + path->pcc_addr.ipaddr_v4 = obj->src_ipv4; + SET_IPADDR_V4(&path->nbkey.endpoint); + path->nbkey.endpoint.ipaddr_v4 = obj->dst_ipv4; +} + +void pcep_lib_parse_endpoints_ipv6(struct path *path, + struct pcep_object_endpoints_ipv6 *obj) +{ + SET_IPADDR_V6(&path->pcc_addr); + path->pcc_addr.ipaddr_v6 = obj->src_ipv6; + SET_IPADDR_V6(&path->nbkey.endpoint); + path->nbkey.endpoint.ipaddr_v6 = obj->dst_ipv6; +} + +void pcep_lib_parse_vendor_info(struct path *path, + struct pcep_object_vendor_info *obj) +{ + if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO + && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO) + path->nbkey.color = obj->enterprise_specific_info1; + else + path->nbkey.color = 0; +} + void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero) { struct path_hop *hop = NULL; @@ -959,6 +1072,12 @@ void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero) double_linked_list_node *node; struct pcep_object_ro_subobj *obj; + if (objs == NULL) { + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + "Unexpected Empty ERO's sub_obj plsp-id:(%d)", + path ? (int32_t)path->plsp_id : -1); + return; + } for (node = objs->tail; node != NULL; node = node->prev_node) { obj = (struct pcep_object_ro_subobj *)node->data; switch (obj->ro_subobj_type) { diff --git a/pathd/path_pcep_lib.h b/pathd/path_pcep_lib.h index 3f34edcb3f..524f385d14 100644 --- a/pathd/path_pcep_lib.h +++ b/pathd/path_pcep_lib.h @@ -37,7 +37,8 @@ struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps, struct path *path); struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid); -struct pcep_message *pcep_lib_format_error(int error_type, int error_value); +struct pcep_message *pcep_lib_format_error(int error_type, int error_value, + struct path *path); struct path *pcep_lib_parse_path(struct pcep_message *msg); void pcep_lib_parse_capabilities(struct pcep_message *msg, struct pcep_caps *caps); diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c index 779c400b86..81a338ac63 100644 --- a/pathd/path_pcep_pcc.c +++ b/pathd/path_pcep_pcc.c @@ -93,7 +93,8 @@ static void send_pcep_message(struct pcc_state *pcc_state, struct pcep_message *msg); static void send_pcep_error(struct pcc_state *pcc_state, enum pcep_error_type error_type, - enum pcep_error_value error_value); + enum pcep_error_value error_value, + struct path *trigger_path); static void send_report(struct pcc_state *pcc_state, struct path *path); static void send_comp_request(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, @@ -541,8 +542,8 @@ void pcep_pcc_send_report(struct ctrl_state *ctrl_state, return; } - PCEP_DEBUG("%s Send report for candidate path %s", pcc_state->tag, - path->name); + PCEP_DEBUG("(%s)%s Send report for candidate path %s", __func__, + pcc_state->tag, path->name); /* ODL and Cisco requires the first reported * LSP to have a DOWN status, the later status changes @@ -555,6 +556,8 @@ void pcep_pcc_send_report(struct ctrl_state *ctrl_state, /* If no update is expected and the real status wasn't down, we need to * send a second report with the real status */ if (is_stable && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) { + PCEP_DEBUG("(%s)%s Send report for candidate path (!DOWN) %s", + __func__, pcc_state->tag, path->name); path->srp_id = 0; path->status = real_status; send_report(pcc_state, path); @@ -564,6 +567,19 @@ void pcep_pcc_send_report(struct ctrl_state *ctrl_state, } +void pcep_pcc_send_error(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct pcep_error *error, + bool sub_type) +{ + + PCEP_DEBUG("(%s) Send error after PcInitiated ", __func__); + + + send_pcep_error(pcc_state, error->error_type, error->error_value, + error->path); + pcep_free_path(error->path); + XFREE(MTYPE_PCEP, error); +} /* ------------ Timeout handler ------------ */ void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state, @@ -651,6 +667,9 @@ void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state, PCEP_DEBUG("%s Candidate path %s removed", pcc_state->tag, path->name); path->was_removed = true; + /* Removed as response to a PcInitiated 'R'emove*/ + /* RFC 8281 #5.4 LSP Deletion*/ + path->do_remove = path->was_removed; if (pcc_state->caps.is_stateful) send_report(pcc_state, path); return; @@ -1203,14 +1222,113 @@ void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, struct pcep_message *msg) { - PCEP_DEBUG("%s Received LSP initiate, not supported yet", - pcc_state->tag); + char err[MAX_ERROR_MSG_SIZE] = ""; + struct path *path; + + path = pcep_lib_parse_path(msg); + + if (!pcc_state->pce_opts->config_opts.pce_initiated) { + /* PCE Initiated is not enabled */ + flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "Not allowed PCE initiated path received: %s", + format_pcep_message(msg)); + send_pcep_error(pcc_state, PCEP_ERRT_LSP_INSTANTIATE_ERROR, + PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR, path); + return; + } - /* TODO when we support both PCC and PCE initiated sessions, - * we should first check the session type before - * rejecting this message. */ - send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION, - PCEP_ERRV_LSP_NOT_PCE_INITIATED); + if (path->do_remove) { + // lookup in nbkey sequential as no endpoint + struct nbkey_map_data *key; + char endpoint[46]; + + frr_each (nbkey_map, &pcc_state->nbkey_map, key) { + ipaddr2str(&key->nbkey.endpoint, endpoint, + sizeof(endpoint)); + flog_warn( + EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "FOR_EACH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ", + key->nbkey.color, endpoint, path->plsp_id); + if (path->plsp_id == key->plspid) { + flog_warn( + EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "FOR_EACH MATCH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ", + key->nbkey.color, endpoint, + path->plsp_id); + path->nbkey = key->nbkey; + break; + } + } + } else { + if (path->first_hop == NULL /*ero sets first_hop*/) { + /* If the PCC receives a PCInitiate message without an + * ERO and the R flag in the SRP object != zero, then it + * MUST send a PCErr message with Error-type=6 + * (Mandatory Object missing) and Error-value=9 (ERO + * object missing). */ + flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "ERO object missing or incomplete : %s", + format_pcep_message(msg)); + send_pcep_error(pcc_state, + PCEP_ERRT_LSP_INSTANTIATE_ERROR, + PCEP_ERRV_INTERNAL_ERROR, path); + return; + } + + if (path->plsp_id != 0) { + /* If the PCC receives a PCInitiate message with a + * non-zero PLSP-ID and the R flag in the SRP object set + * to zero, then it MUST send a PCErr message with + * Error-type=19 (Invalid Operation) and Error-value=8 + * (Non-zero PLSP-ID in the LSP Initiate Request) */ + flog_warn( + EC_PATH_PCEP_PROTOCOL_ERROR, + "PCE initiated path with non-zero PLSP ID: %s", + format_pcep_message(msg)); + send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID, + path); + return; + } + + if (path->name == NULL) { + /* If the PCC receives a PCInitiate message without a + * SYMBOLIC-PATH-NAME TLV, then it MUST send a PCErr + * message with Error-type=10 (Reception of an invalid + * object) and Error-value=8 (SYMBOLIC-PATH-NAME TLV + * missing) */ + flog_warn( + EC_PATH_PCEP_PROTOCOL_ERROR, + "PCE initiated path without symbolic name: %s", + format_pcep_message(msg)); + send_pcep_error( + pcc_state, PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING, path); + return; + } + } + + /* TODO: If there is a conflict with the symbolic path name of an + * existing LSP, the PCC MUST send a PCErr message with Error-type=23 + * (Bad Parameter value) and Error-value=1 (SYMBOLIC-PATH-NAME in + * use) */ + + specialize_incoming_path(pcc_state, path); + /* TODO: Validate the PCC address received from the PCE is valid */ + PCEP_DEBUG("%s Received LSP initiate", pcc_state->tag); + PCEP_DEBUG_PATH("%s", format_path(path)); + + if (validate_incoming_path(pcc_state, path, err, sizeof(err))) { + pcep_thread_initiate_path(ctrl_state, pcc_state->id, path); + } else { + /* FIXME: Monitor the amount of errors from the PCE and + * possibly disconnect and blacklist */ + flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "Unsupported PCEP protocol feature: %s", err); + pcep_free_path(path); + send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_NOT_PCE_INITIATED, path); + } } void handle_pcep_comp_reply(struct ctrl_state *ctrl_state, @@ -1232,7 +1350,7 @@ void handle_pcep_comp_reply(struct ctrl_state *ctrl_state, pcc_state->tag, path->req_id); PCEP_DEBUG_PATH("%s", format_path(path)); send_pcep_error(pcc_state, PCEP_ERRT_UNKNOWN_REQ_REF, - PCEP_ERRV_UNASSIGNED); + PCEP_ERRV_UNASSIGNED, NULL); return; } @@ -1447,13 +1565,14 @@ void send_pcep_message(struct pcc_state *pcc_state, struct pcep_message *msg) void send_pcep_error(struct pcc_state *pcc_state, enum pcep_error_type error_type, - enum pcep_error_value error_value) + enum pcep_error_value error_value, + struct path *trigger_path) { struct pcep_message *msg; PCEP_DEBUG("%s Sending PCEP error type %s (%d) value %s (%d)", pcc_state->tag, pcep_error_type_name(error_type), error_type, pcep_error_value_name(error_type, error_value), error_value); - msg = pcep_lib_format_error(error_type, error_value); + msg = pcep_lib_format_error(error_type, error_value, trigger_path); send_pcep_message(pcc_state, msg); } @@ -1504,7 +1623,8 @@ void specialize_outgoing_path(struct pcc_state *pcc_state, struct path *path) /* Updates the path for the PCC */ void specialize_incoming_path(struct pcc_state *pcc_state, struct path *path) { - set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr); + if (IS_IPADDR_NONE(&path->pcc_addr)) + set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr); path->sender = pcc_state->pce_opts->addr; path->pcc_id = pcc_state->id; path->update_origin = SRTE_ORIGIN_PCEP; @@ -1538,7 +1658,7 @@ bool validate_incoming_path(struct pcc_state *pcc_state, struct path *path, } if (err_type != 0) { - send_pcep_error(pcc_state, err_type, err_value); + send_pcep_error(pcc_state, err_type, err_value, NULL); return false; } @@ -1564,7 +1684,6 @@ void send_comp_request(struct ctrl_state *ctrl_state, if (!pcc_state->is_best) { return; } - /* TODO: Add a timer to retry the computation request ? */ specialize_outgoing_path(pcc_state, req->path); @@ -1579,10 +1698,7 @@ void send_comp_request(struct ctrl_state *ctrl_state, send_pcep_message(pcc_state, msg); req->was_sent = true; - /* TODO: Enable this back when the pcep config changes are merged back - */ - // timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds; - timeout = 30; + timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds; pcep_thread_schedule_timeout(ctrl_state, pcc_state->id, TO_COMPUTATION_REQUEST, timeout, (void *)req, &req->t_retry); @@ -1641,7 +1757,6 @@ void set_pcc_address(struct pcc_state *pcc_state, struct lsp_nb_key *nbkey, } } - /* ------------ Data Structure Helper Functions ------------ */ void lookup_plspid(struct pcc_state *pcc_state, struct path *path) diff --git a/pathd/path_pcep_pcc.h b/pathd/path_pcep_pcc.h index ceac6f3278..9e712baf16 100644 --- a/pathd/path_pcep_pcc.h +++ b/pathd/path_pcep_pcc.h @@ -125,6 +125,9 @@ void pcep_pcc_sync_done(struct ctrl_state *ctrl_state, void pcep_pcc_send_report(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, struct path *path, bool is_stable); +void pcep_pcc_send_error(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct pcep_error *path, + bool is_stable); int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id, struct pcc_state **pcc_state_list); int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state, diff --git a/pathd/pathd.c b/pathd/pathd.c index 9dc3a41638..90b8727284 100644 --- a/pathd/pathd.c +++ b/pathd/pathd.c @@ -294,7 +294,9 @@ void srte_segment_set_local_modification(struct srte_segment_list *s_list, * @param endpoint The IP address of the policy endpoint * @return The created policy */ -struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint) +struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint, + enum srte_protocol_origin origin, + const char *originator) { struct srte_policy *policy; @@ -302,6 +304,11 @@ struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint) policy->color = color; policy->endpoint = *endpoint; policy->binding_sid = MPLS_LABEL_NONE; + policy->protocol_origin = origin; + if (originator != NULL) + strlcpy(policy->originator, originator, + sizeof(policy->originator)); + RB_INIT(srte_candidate_head, &policy->candidate_paths); RB_INSERT(srte_policy_head, &srte_policies, policy); @@ -646,7 +653,9 @@ void srte_policy_apply_changes(struct srte_policy *policy) * @return The added candidate path */ struct srte_candidate *srte_candidate_add(struct srte_policy *policy, - uint32_t preference) + uint32_t preference, + enum srte_protocol_origin origin, + const char *originator) { struct srte_candidate *candidate; struct srte_lsp *lsp; @@ -658,7 +667,17 @@ struct srte_candidate *srte_candidate_add(struct srte_policy *policy, candidate->policy = policy; candidate->type = SRTE_CANDIDATE_TYPE_UNDEFINED; candidate->discriminator = frr_weak_random(); + candidate->protocol_origin = origin; + if (originator != NULL) { + strlcpy(candidate->originator, originator, + sizeof(candidate->originator)); + lsp->protocol_origin = origin; + } + if (candidate->protocol_origin == SRTE_ORIGIN_PCEP + || candidate->protocol_origin == SRTE_ORIGIN_BGP) { + candidate->type = SRTE_CANDIDATE_TYPE_DYNAMIC; + } lsp->candidate = candidate; candidate->lsp = lsp; diff --git a/pathd/pathd.h b/pathd/pathd.h index 7d38272e85..f790a0e3c9 100644 --- a/pathd/pathd.h +++ b/pathd/pathd.h @@ -339,6 +339,12 @@ struct srte_policy { /* Binding SID */ mpls_label_t binding_sid; + /* The Protocol-Origin. */ + enum srte_protocol_origin protocol_origin; + + /* The Originator */ + char originator[64]; + /* Operational Status of the policy */ enum srte_policy_status status; @@ -352,6 +358,8 @@ struct srte_policy { #define F_POLICY_NEW 0x0002 #define F_POLICY_MODIFIED 0x0004 #define F_POLICY_DELETED 0x0008 + /* SRP id for PcInitiated support */ + int srp_id; }; RB_HEAD(srte_policy_head, srte_policy); RB_PROTOTYPE(srte_policy_head, srte_policy, entry, srte_policy_compare) @@ -385,7 +393,9 @@ int srte_segment_entry_set_nai(struct srte_segment_entry *segment, void srte_segment_set_local_modification(struct srte_segment_list *s_list, struct srte_segment_entry *s_entry, uint32_t ted_sid); -struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint); +struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint, + enum srte_protocol_origin origin, + const char *originator); void srte_policy_del(struct srte_policy *policy); struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint); int srte_policy_update_ted_sid(void); @@ -395,7 +405,9 @@ void srte_apply_changes(void); void srte_clean_zebra(void); void srte_policy_apply_changes(struct srte_policy *policy); struct srte_candidate *srte_candidate_add(struct srte_policy *policy, - uint32_t preference); + uint32_t preference, + enum srte_protocol_origin origin, + const char *originator); void srte_candidate_del(struct srte_candidate *candidate); void srte_candidate_set_bandwidth(struct srte_candidate *candidate, float bandwidth, bool required); diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index 1badaf95bd..7861559034 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -119,6 +119,7 @@ struct quagga_signal_t pbr_signals[] = { static const struct frr_yang_module_info *const pbrd_yang_modules[] = { &frr_filter_info, &frr_interface_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT, diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 216834fe0c..3d56fc3daa 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -1143,10 +1143,14 @@ static const struct cmd_variable_handler pbr_map_name[] = { } }; +extern struct zebra_privs_t pbr_privs; + void pbr_vty_init(void) { cmd_variable_handler_register(pbr_map_name); + vrf_cmd_init(NULL, &pbr_privs); + install_node(&interface_node); if_cmd_init(); diff --git a/pceplib/pcep_msg_objects.h b/pceplib/pcep_msg_objects.h index 959a6f8cf6..f26618e291 100644 --- a/pceplib/pcep_msg_objects.h +++ b/pceplib/pcep_msg_objects.h @@ -385,11 +385,16 @@ struct pcep_object_lsp { bool flag_c; }; +#define ENTERPRISE_NUMBER_CISCO 9 +#define ENTERPRISE_COLOR_CISCO 65540 /* RFC 7470 */ struct pcep_object_vendor_info { struct pcep_object_header header; uint32_t enterprise_number; uint32_t enterprise_specific_info; + uint32_t enterprise_specific_info1; /* cisco sends color for PcInit */ + uint32_t enterprise_specific_info2; + uint32_t enterprise_specific_info3; }; /* RFC 8282 */ diff --git a/pceplib/pcep_msg_objects_encoding.c b/pceplib/pcep_msg_objects_encoding.c index 9ab96f7bce..69420f8e7a 100644 --- a/pceplib/pcep_msg_objects_encoding.c +++ b/pceplib/pcep_msg_objects_encoding.c @@ -1339,8 +1339,15 @@ pcep_decode_obj_vendor_info(struct pcep_object_header *hdr, struct pcep_object_vendor_info *obj = (struct pcep_object_vendor_info *)common_object_create( hdr, sizeof(struct pcep_object_vendor_info)); + obj->enterprise_number = ntohl(*((uint32_t *)(obj_buf))); obj->enterprise_specific_info = ntohl(*((uint32_t *)(obj_buf + 4))); + if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO + && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO) + obj->enterprise_specific_info1 = + ntohl(*((uint32_t *)(obj_buf + 8))); + else + obj->enterprise_specific_info1 = 0; return (struct pcep_object_header *)obj; } diff --git a/pceplib/pcep_msg_tlvs.h b/pceplib/pcep_msg_tlvs.h index 5197201e40..6dd2b56552 100644 --- a/pceplib/pcep_msg_tlvs.h +++ b/pceplib/pcep_msg_tlvs.h @@ -53,16 +53,16 @@ extern "C" { * https://www.iana.org/assignments/pcep/pcep.xhtml */ enum pcep_object_tlv_types { PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR = 1, - PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */ + PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */ PCEP_OBJ_TLV_TYPE_VENDOR_INFO = 7, /* RFC 7470 */ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY = 16, /* RFC 8231 */ - PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */ - PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */ - PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */ + PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */ + PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */ + PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */ PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE = 20, /* RFC 8232 */ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC = 21, /* RFC 8232 */ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION = 23, /* RFC 8232 */ - PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */ + PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY = 26, /* draft-ietf-pce-segment-routing-16 */ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE = 28, /* RFC 8408 */ @@ -77,10 +77,12 @@ enum pcep_object_tlv_types { PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE = 63, /*TDB5 draft-barth-pce-segment-routing-policy-cp-04 */ PCEP_OBJ_TLV_TYPE_UNKNOWN = 128, - PCEP_OBJ_TLV_TYPE_ARBITRARY = - 65533 /* Max IANA To write arbitrary data */ + PCEP_OBJ_TYPE_CISCO_BSID = 65505, + /* Max IANA To write arbitrary data */ + PCEP_OBJ_TLV_TYPE_ARBITRARY = 65533 }; + struct pcep_object_tlv_header { enum pcep_object_tlv_types type; /* Pointer into encoded_message field from the pcep_message */ diff --git a/pceplib/pcep_msg_tlvs_encoding.c b/pceplib/pcep_msg_tlvs_encoding.c index 37f3353f76..d59c97c9da 100644 --- a/pceplib/pcep_msg_tlvs_encoding.c +++ b/pceplib/pcep_msg_tlvs_encoding.c @@ -838,7 +838,15 @@ struct pcep_object_tlv_header *pcep_decode_tlv(const uint8_t *tlv_buf) return NULL; } - tlv_decoder_funcptr tlv_decoder = tlv_decoders[tlv_hdr.type]; + tlv_decoder_funcptr tlv_decoder = NULL; + if (tlv_hdr.type == PCEP_OBJ_TYPE_CISCO_BSID) { + pcep_log(LOG_INFO, + "%s: Cisco BSID TLV decoder found for TLV type [%d]", + __func__, tlv_hdr.type); + tlv_decoder = tlv_decoders[PCEP_OBJ_TLV_TYPE_ARBITRARY]; + } else { + tlv_decoder = tlv_decoders[tlv_hdr.type]; + } if (tlv_decoder == NULL) { pcep_log(LOG_INFO, "%s: No TLV decoder found for TLV type [%d]", __func__, tlv_hdr.type); diff --git a/pceplib/test/pcep_msg_tools_test.c b/pceplib/test/pcep_msg_tools_test.c index 5a7644b21a..e25ddb2179 100644 --- a/pceplib/test/pcep_msg_tools_test.c +++ b/pceplib/test/pcep_msg_tools_test.c @@ -809,8 +809,8 @@ void test_pcep_msg_read_pcep_report_cisco_pcc() CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP); CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 60); CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list); - /* The TLV with ID 65505 is not recognized, and its not in the list */ - CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2); + /* The TLV with ID 65505 is now recognized, and its in the list */ + CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 3); CU_ASSERT_EQUAL(lsp->plsp_id, 524303); CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN); CU_ASSERT_TRUE(lsp->flag_a); diff --git a/python/xrelfo.py b/python/xrelfo.py index 0ecd008579..17262da8d9 100644 --- a/python/xrelfo.py +++ b/python/xrelfo.py @@ -357,6 +357,7 @@ def main(): argp.add_argument('--out-by-file', type=str, help='write by-file JSON output') argp.add_argument('-Wlog-format', action='store_const', const=True) argp.add_argument('-Wlog-args', action='store_const', const=True) + argp.add_argument('-Werror', action='store_const', const=True) argp.add_argument('--profile', action='store_const', const=True) argp.add_argument('binaries', metavar='BINARY', nargs='+', type=str, help='files to read (ELF files or libtool objects)') args = argp.parse_args() @@ -380,9 +381,12 @@ def _main(args): traceback.print_exc() for option in dir(args): - if option.startswith('W'): + if option.startswith('W') and option != 'Werror': checks = sorted(xrelfo.check(args)) sys.stderr.write(''.join([c[-1] for c in checks])) + + if args.Werror and len(checks) > 0: + errors += 1 break diff --git a/ripd/ripd.c b/ripd/ripd.c index c6c82fb65a..7d940efd9c 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -3690,6 +3690,8 @@ void rip_vrf_init(void) { vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete, rip_vrf_enable); + + vrf_cmd_init(NULL, &ripd_privs); } void rip_vrf_terminate(void) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 3b8d2076f3..a0ea18f3e9 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2691,6 +2691,8 @@ void ripng_vrf_init(void) { vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable, ripng_vrf_delete, ripng_vrf_enable); + + vrf_cmd_init(NULL, &ripngd_privs); } void ripng_vrf_terminate(void) diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json new file mode 100644 index 0000000000..2eeebad4b0 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json new file mode 100644 index 0000000000..419bcc3dd9 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json @@ -0,0 +1,8 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]": null, + "[3]:[0]:[32]:[10.100.0.1]": null, + "[3]:[0]:[32]:[10.100.0.2]": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json new file mode 100644 index 0000000000..2eeebad4b0 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:101:100" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":0, + "peerId":"10.0.1.2", + "path":"102", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:102:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json new file mode 100644 index 0000000000..833f98657b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"50.0.1.11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000000..833f98657b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"50.0.1.11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000000..4a292bddbe --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": null } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json new file mode 100644 index 0000000000..3dc3fcf9cb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"50:0:1::11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global" + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000000..3dc3fcf9cb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"50:0:1::11", + "path":"111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global" + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000000..6c11d894eb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.1", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": null } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf new file mode 100644 index 0000000000..63aa99a832 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf @@ -0,0 +1,30 @@ +router bgp 101 + bgp router-id 10.100.0.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.2 remote-as 102 + ! + address-family l2vpn evpn + neighbor 10.0.1.2 activate + advertise-all-vni + exit-address-family +! +router bgp 101 vrf vrf-blue + bgp router-id 10.100.0.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 50.0.1.11 remote-as 111 + neighbor 50:0:1::11 remote-as 111 + ! + address-family ipv4 unicast + no neighbor 50:0:1::11 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 50:0:1::11 activate + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast gateway-ip + advertise ipv6 unicast gateway-ip + exit-address-family
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf new file mode 100644 index 0000000000..99a2e89ef3 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf @@ -0,0 +1,14 @@ +! +log file zebra.log +! +ip route 10.100.0.2/32 10.0.1.2 +! +vrf vrf-blue + vni 1000 prefix-routes-only + exit-vrf +! +interface lo + ip address 10.100.0.1/32 +interface PE1-eth0 + ip address 10.0.1.1/24 +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json new file mode 100644 index 0000000000..2dcf35d91b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000000..2dcf35d91b --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000000..9c3091dc50 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +} diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json new file mode 100644 index 0000000000..229c927656 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json @@ -0,0 +1,55 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000000..229c927656 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json @@ -0,0 +1,55 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000000..94f82e6d4c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json new file mode 100644 index 0000000000..7b8d38e492 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json new file mode 100644 index 0000000000..6273b3e728 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json @@ -0,0 +1,68 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]": null, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json new file mode 100644 index 0000000000..7b8d38e492 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json @@ -0,0 +1,192 @@ +{ + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":32, + "ip":"50.0.1.11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:61", + "ipLen":128, + "ip":"50:0:1::11", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{ + "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":2, + "ethTag":0, + "macLen":48, + "mac":"1a:2b:3c:4d:5e:62", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.1]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.1]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.1", + "weight":0, + "peerId":"10.0.1.1", + "path":"101", + "origin":"IGP", + "extendedCommunity":{ + "string":"RT:101:100 ET:8" + }, + "nexthops":[ + { + "ip":"10.100.0.1", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + }, + "[3]:[0]:[32]:[10.100.0.2]":{ + "prefix":"[3]:[0]:[32]:[10.100.0.2]", + "prefixLen":320, + "paths":[ + [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "routeType":3, + "ethTag":0, + "ipLen":32, + "ip":"10.100.0.2", + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "extendedCommunity":{ + "string":"ET:8 RT:102:100" + }, + "nexthops":[ + { + "ip":"10.100.0.2", + "afi":"ipv4", + "used":true + } + ] + } + ] + ] + } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json new file mode 100644 index 0000000000..c03d70195f --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000000..7f1b8d2ef4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json @@ -0,0 +1,27 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": [ + { + "valid":null, + "bestpath":null, + "pathFrom":"external", + "prefix":"100.0.0.21", + "prefixLen":32, + "network":"100.0.0.21\/32", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50.0.1.11", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000000..52e4311635 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100.0.0.21/32": null } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json new file mode 100644 index 0000000000..1d90c9c798 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json @@ -0,0 +1,28 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } +] } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000000..a0e63c6e25 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json @@ -0,0 +1,28 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": [ + { + "valid":null, + "bestpath":null, + "pathFrom":"external", + "prefix":"100::21", + "prefixLen":128, + "network":"100::21\/128", + "metric":0, + "weight":0, + "peerId":"10.0.1.1", + "path":"101 111", + "origin":"IGP", + "nexthops":[ + { + "ip":"50:0:1::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000000..789fe69b28 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json @@ -0,0 +1,6 @@ +{ + "vrfName": "vrf-blue", + "routerId": "10.100.0.2", + "defaultLocPrf": 100, + "localAS": 101, + "routes": { "100::21/128": null } }
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf new file mode 100644 index 0000000000..59fee15dfc --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 102 + bgp router-id 10.100.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.1.1 remote-as 101 + ! + address-family l2vpn evpn + neighbor 10.0.1.1 activate + advertise-all-vni + enable-resolve-overlay-index + exit-address-family +! +router bgp 101 vrf vrf-blue + bgp router-id 10.100.0.2 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf new file mode 100644 index 0000000000..b78cdcc512 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf @@ -0,0 +1,14 @@ +! +log file zebra.log +! +ip route 10.100.0.1/32 10.0.1.1 +! +vrf vrf-blue + vni 1000 prefix-routes-only + exit-vrf +! +interface lo + ip address 10.100.0.2/32 +interface PE2-eth0 + ip address 10.0.1.2/24 +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json new file mode 100644 index 0000000000..b3a3640be4 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json @@ -0,0 +1,56 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32":[ + { + "prefix":"100.0.0.21\/32", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":40, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50.0.1.11", + "afi":"ipv4", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json new file mode 100644 index 0000000000..996fe52f44 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json new file mode 100644 index 0000000000..996fe52f44 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50.0.1.0\/24":[ + { + "prefix":"50.0.1.0\/24", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100.0.0.21\/32": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json new file mode 100644 index 0000000000..d5be22a2ba --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json @@ -0,0 +1,56 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128":[ + { + "prefix":"100::21\/128", + "protocol":"bgp", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":40, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"50:0:1::11", + "afi":"ipv6", + "interfaceName":"br100", + "active":true, + "weight":1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json new file mode 100644 index 0000000000..94f82e6d4c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json new file mode 100644 index 0000000000..94f82e6d4c --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json @@ -0,0 +1,29 @@ +{ + "50:0:1::\/48":[ + { + "prefix":"50:0:1::\/48", + "protocol":"connected", + "vrfName":"vrf-blue", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"br100", + "active":true + } + ] + } + ], + "100::21\/128": null +}
\ No newline at end of file diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py b/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf new file mode 100644 index 0000000000..7608ec95c3 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 111 + bgp router-id 10.100.0.11 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 50.0.1.1 remote-as 101 + neighbor 50:0:1::1 remote-as 101 + ! + address-family ipv4 unicast + network 100.0.0.21/32 + no neighbor 50:0:1::1 activate + exit-address-family + ! + address-family ipv6 unicast + network 100::21/128 + neighbor 50:0:1::1 activate + exit-address-family + + diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf new file mode 100644 index 0000000000..c8c832e9d8 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf @@ -0,0 +1,4 @@ +! +int host1-eth0 + ip address 50.0.1.11/24 + ipv6 address 50:0:1::11/48 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf new file mode 100644 index 0000000000..9135545c58 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf @@ -0,0 +1,4 @@ +! +int host1-eth0 + ip address 50.0.1.21/24 + ipv6 address 50:0:1::21/48 diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py new file mode 100755 index 0000000000..fbce2809e0 --- /dev/null +++ b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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_bgp_evpn_overlay_index_gateway.py: Test EVPN gateway IP overlay index functionality +Following functionality is covered: + + +--------+ BGP +--------+ BGP +--------+ +--------+ + SN1 | | IPv4/v6 | | EVPN | | | | + ======+ Host1 +---------+ PE1 +------+ PE2 +------+ Host2 + + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + + Host1 is connected to PE1 and host2 is connected to PE2 + Host1 and PE1 have IPv4/v6 BGP sessions. + PE1 and PE2 gave EVPN session. + Host1 advertises IPv4/v6 prefixes to PE1. + PE1 advertises these prefixes to PE2 as EVPN type-5 routes. + Gateway IP for these EVPN type-5 routes is host1 IP. + Host1 MAC/IP is advertised by PE1 as EVPN type-2 route + +Following testcases are covered: +TC_1: +Check BGP and zebra states for above topology at PE1 and PE2. + +TC_2: +Stop advertising prefixes from host1. It should withdraw type-5 routes. Check states at PE1 and PE2 +Advertise the prefixes again. Check states. + +TC_3: +Shut down VxLAN interface at PE1. This should withdraw type-2 routes. Check states at PE1 and PE2. +Enable VxLAN interface again. Check states. +""" + +import os +import sys +import json +from functools import partial +import pytest +import time +import platform + +#Current Working Directory +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import ( + step, + write_test_header, + write_test_footer, + generate_support_bundle, +) + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +#Global variables +PES = ['PE1', 'PE2'] +HOSTS = ['host1', 'host2'] +PE_SUFFIX = {'PE1': '1', 'PE2': '2'} +HOST_SUFFIX = {'host1': '1', 'host2': '2'} +TRIGGERS = ["base", "no_rt5", "no_rt2"] + + +class TemplateTopo(Topo): + """Test topology builder""" + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers and add links. + + # Create routers + for pe in PES: + tgen.add_router(pe) + for host in HOSTS: + tgen.add_router(host) + + krel = platform.release() + logger.info('Kernel version ' + krel) + + #Add links + tgen.add_link(tgen.gears['PE1'], tgen.gears['PE2'], 'PE1-eth0', 'PE2-eth0') + tgen.add_link(tgen.gears['PE1'], tgen.gears['host1'], 'PE1-eth1', 'host1-eth0') + tgen.add_link(tgen.gears['PE2'], tgen.gears['host2'], 'PE2-eth1', 'host2-eth0') + + +def setup_module(mod): + "Sets up the pytest environment" + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15. Kernel present {}".format(kernelv)) + return + + if topotest.version_cmp(kernelv, '4.15') == 0: + l3mdev_accept = 1 + logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) + else: + l3mdev_accept = 0 + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + tgen.start_topology() + + # Configure MAC address for hosts as these MACs are advertised with EVPN type-2 routes + for (name, host) in tgen.gears.items(): + if name not in HOSTS: + continue + + host_mac = "1a:2b:3c:4d:5e:6{}".format(HOST_SUFFIX[name]) + host.run("ip link set dev {}-eth0 down").format(name) + host.run("ip link set dev {0}-eth0 address {1}".format(name, host_mac)) + host.run("ip link set dev {}-eth0 up").format(name) + + # Configure PE VxLAN and Bridge interfaces + for (name, pe) in tgen.gears.items(): + if name not in PES: + continue + vtep_ip = "10.100.0.{}".format(PE_SUFFIX[name]) + bridge_ip = "50.0.1.{}/24".format(PE_SUFFIX[name]) + bridge_ipv6 = "50:0:1::{}/48".format(PE_SUFFIX[name]) + + pe.run("ip link add vrf-blue type vrf table 10") + pe.run("ip link set dev vrf-blue up") + pe.run("ip link add vxlan100 type vxlan id 100 dstport 4789 local {}".format(vtep_ip)) + pe.run("ip link add name br100 type bridge stp_state 0") + pe.run("ip link set dev vxlan100 master br100") + pe.run("ip link set dev {}-eth1 master br100".format(name)) + pe.run("ip addr add {} dev br100".format(bridge_ip)) + pe.run("ip link set up dev br100") + pe.run("ip link set up dev vxlan100") + pe.run("ip link set up dev {}-eth1".format(name)) + pe.run("ip link set dev br100 master vrf-blue") + pe.run("ip -6 addr add {} dev br100".format(bridge_ipv6)) + + pe.run("ip link add vxlan1000 type vxlan id 1000 dstport 4789 local {}".format(vtep_ip)) + pe.run("ip link add name br1000 type bridge stp_state 0") + pe.run("ip link set dev vxlan1000 master br100") + pe.run("ip link set up dev br1000") + pe.run("ip link set up dev vxlan1000") + pe.run("ip link set dev br1000 master vrf-blue") + + pe.run("sysctl -w net.ipv4.ip_forward=1") + pe.run("sysctl -w net.ipv6.conf.all.forwarding=1") + pe.run("sysctl -w net.ipv4.udp_l3mdev_accept={}".format(l3mdev_accept)) + pe.run("sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept)) + + # For all registred routers, load the zebra configuration file + for (name, router) in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(name)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(name)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + logger.info("Running setup_module() done") + topotest.sleep(200) + + +def teardown_module(mod): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def evpn_gateway_ip_show_op_check(trigger=" "): + """ + This function checks CLI O/P for commands mentioned in show_commands for a given trigger + :param trigger: Should be a trigger present in TRIGGERS + :return: Returns a tuple (result: None for success, retmsg: Log message to be printed on failure) + """ + tgen = get_topogen() + + if trigger not in TRIGGERS: + return "Unexpected trigger", "Unexpected trigger {}".format(trigger) + + show_commands = {'bgp_vni_routes': 'show bgp l2vpn evpn route vni 100 json', + 'bgp_vrf_ipv4' : 'show bgp vrf vrf-blue ipv4 json', + 'bgp_vrf_ipv6' : 'show bgp vrf vrf-blue ipv6 json', + 'zebra_vrf_ipv4': 'show ip route vrf vrf-blue json', + 'zebra_vrf_ipv6': 'show ipv6 route vrf vrf-blue json'} + + for (name, pe) in tgen.gears.items(): + if name not in PES: + continue + + for (cmd_key, command) in show_commands.items(): + expected_op_file = "{0}/{1}/{2}_{3}.json".format(CWD, name, cmd_key, trigger) + expected_op = json.loads(open(expected_op_file).read()) + + test_func = partial(topotest.router_json_cmp, pe, command, expected_op) + ret, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{0}" JSON output mismatch for {1}'.format(name, command) + if result is not None: + return result, assertmsg + + return None, "Pass" + + +def test_evpn_gateway_ip_basic_topo(request): + """ + Tets EVPN overlay index gateway IP functionality. VErify show O/Ps on PE1 and PE2 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check O/Ps for EVPN gateway IP overlay Index functionality at PE1 and PE2") + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_evpn_gateway_ip_flap_rt5(request): + """ + Withdraw EVPN type-5 routes and check O/Ps at PE1 and PE2 + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + h1 = tgen.gears['host1'] + + step("Withdraw type-5 routes") + + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv4" \ + -c "no network 100.0.0.21/32"') + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv6" \ + -c "no network 100::21/128"') + + result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + step("Advertise type-5 routes again") + + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv4" \ + -c "network 100.0.0.21/32"') + h1.run('vtysh -c "config t" \ + -c "router bgp 111" \ + -c "address-family ipv6" \ + -c "network 100::21/128"') + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + if result is not None: + generate_support_bundle() + + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_evpn_gateway_ip_flap_rt2(request): + """ + Withdraw EVPN type-2 routes and check O/Ps at PE1 and PE2 + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + kernelv = platform.release() + if topotest.version_cmp(kernelv, "4.15") < 0: + logger.info("For EVPN, kernel version should be minimum 4.15") + write_test_footer(tc_name) + return + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + + step("Shut down VxLAN interface at PE1 which results in withdraw of type-2 routes") + + pe1 = tgen.gears['PE1'] + + pe1.run('ip link set dev vxlan100 down') + + result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + step("Bring up VxLAN interface at PE1 and advertise type-2 routes again") + + pe1.run('ip link set dev vxlan100 up') + + result, assertmsg = evpn_gateway_ip_show_op_check("base") + if result is not None: + generate_support_bundle() + assert result is None, assertmsg + + write_test_footer(tc_name) + + +def test_memory_leak(): + """Run the memory leak test and report results""" + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_route_route_map_match2/__init__.py b/tests/topotests/bgp_default_route_route_map_match2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/__init__.py diff --git a/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf new file mode 100644 index 0000000000..ee7a92f116 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +ip prefix-list r2 permit 10.0.0.0/22 +! +route-map default permit 10 + match ip address prefix-list r2 +! diff --git a/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf new file mode 100644 index 0000000000..e2c399e536 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf new file mode 100644 index 0000000000..00c96cc58b --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf new file mode 100644 index 0000000000..f355ab1517 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 10.0.0.1/22 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py new file mode 100644 index 0000000000..42a6b6edf6 --- /dev/null +++ b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +# Copyright (c) 2021 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# 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 if default-originate works with conditional match. +If 10.0.0.0/22 is recived from r2, then we announce 0.0.0.0/0 +to r2. +""" + +import os +import sys +import json +import time +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 +from mininet.topo import Topo +from lib.common_config import step + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_is_valid(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = {"paths": [{"valid": True}]} + return topotest.json_cmp(output, expected) + + step("Converge network") + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed to see bgp convergence at r2" + + step("Withdraw 10.0.0.0/22 from R2") + router.vtysh_cmd( + "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected" + ) + + step("Check if we don't have 0.0.0.0/0 at R2") + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5) + assert result is not None, "0.0.0.0/0 exists at r2" + + step("Announce 10.0.0.0/22 from R2") + router.vtysh_cmd("conf t\nrouter bgp\naddress-family ipv4\nredistribute connected") + + step("Check if we have 0.0.0.0/0 at R2") + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "0.0.0.0/0 does not exist at r2" + + step("Withdraw 10.0.0.0/22 from R2 again") + router.vtysh_cmd( + "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected" + ) + + step("Check if we don't have 0.0.0.0/0 at R2 again") + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5) + assert result is not None, "0.0.0.0/0 exists at r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf index 6ef8b1c0f4..32ac7c517b 100644 --- a/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf +++ b/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf @@ -12,6 +12,7 @@ bgp community-list standard default seq 5 permit 65000:1 route-map default permit 10 match community default set metric 123 + set as-path prepend 65000 65000 65000 ! route-map internal permit 10 set community 65000:1 diff --git a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py index d9ea5db278..12d1d01bfb 100644 --- a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py +++ b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py @@ -94,7 +94,9 @@ def test_bgp_default_originate_route_map(): def _bgp_default_route_has_metric(router): output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) - expected = {"paths": [{"metric": 123}]} + expected = { + "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}] + } return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_converge, router) diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf index cb07ea9fdf..6f6d394402 100644 --- a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf +++ b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf @@ -8,4 +8,5 @@ router bgp 65000 ! route-map default permit 10 set metric 123 + set as-path prepend 65000 65000 65000 ! diff --git a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py index 9a22c58b16..2622c33f5b 100644 --- a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py +++ b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py @@ -93,7 +93,9 @@ def test_bgp_default_originate_route_map(): def _bgp_default_route_has_metric(router): output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) - expected = {"paths": [{"metric": 123}]} + expected = { + "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}] + } return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_converge, router) diff --git a/tests/topotests/bgp_route_map/test_route_map_topo1.py b/tests/topotests/bgp_route_map/test_route_map_topo1.py index 0158e24d31..74172501db 100644 --- a/tests/topotests/bgp_route_map/test_route_map_topo1.py +++ b/tests/topotests/bgp_route_map/test_route_map_topo1.py @@ -20,47 +20,6 @@ # OF THIS SOFTWARE. # -################################# -# TOPOLOGY -################################# -""" - - +-------+ - +------- | R2 | - | +-------+ - | | - +-------+ | - | R1 | | - +-------+ | - | | - | +-------+ +-------+ - +---------- | R3 |----------| R4 | - +-------+ +-------+ - -""" - -################################# -# TEST SUMMARY -################################# -""" -Following tests are covered to test route-map functionality: -TC_34: - Verify if route-maps is applied in both inbound and - outbound direction to same neighbor/interface. -TC_36: - Test permit/deny statements operation in route-maps with a - permutation and combination of permit/deny in prefix-lists -TC_35: - Test multiple sequence numbers in a single route-map for different - match/set clauses. -TC_37: - Test add/remove route-maps with multiple set - clauses and without any match statement.(Set only) -TC_38: - Test add/remove route-maps with multiple match - clauses and without any set statement.(Match only) -""" - import sys import json import time @@ -91,6 +50,7 @@ from lib.common_config import ( create_bgp_community_lists, interface_status, create_route_maps, + create_static_routes, create_prefix_lists, verify_route_maps, check_address_types, @@ -107,6 +67,46 @@ from lib.bgp import ( ) from lib.topojson import build_topo_from_json, build_config_from_json +################################# +# TOPOLOGY +################################# +""" + + +-------+ + +------- | R2 | + | +-------+ + | | + +-------+ | + | R1 | | + +-------+ | + | | + | +-------+ +-------+ + +---------- | R3 |----------| R4 | + +-------+ +-------+ + +""" + +################################# +# TEST SUMMARY +################################# +""" +Following tests are covered to test route-map functionality: +TC_34: + Verify if route-maps is applied in both inbound and + outbound direction to same neighbor/interface. +TC_36: + Test permit/deny statements operation in route-maps with a + permutation and combination of permit/deny in prefix-lists +TC_35: + Test multiple sequence numbers in a single route-map for different + match/set clauses. +TC_37: + Test add/remove route-maps with multiple set + clauses and without any match statement.(Set only) +TC_38: + Test add/remove route-maps with multiple match + clauses and without any set statement.(Match only) +""" # Global variables bgp_convergence = False @@ -475,8 +475,8 @@ def test_route_map_inbound_outbound_same_neighbor_p0(request): result = verify_rib( tgen, adt, dut, input_dict_2, protocol=protocol, expected=False ) - assert result is not True, "Testcase {} : Failed \n" - "routes are not present in rib \n Error: {}".format(tc_name, result) + assert result is not True, ("Testcase {} : Failed \n" + "routes are not present in rib \n Error: {}".format(tc_name, result)) logger.info("Expected behaviour: {}".format(result)) # Verifying RIB routes @@ -495,8 +495,8 @@ def test_route_map_inbound_outbound_same_neighbor_p0(request): result = verify_rib( tgen, adt, dut, input_dict, protocol=protocol, expected=False ) - assert result is not True, "Testcase {} : Failed \n " - "routes are not present in rib \n Error: {}".format(tc_name, result) + assert result is not True, ("Testcase {} : Failed \n " + "routes are not present in rib \n Error: {}".format(tc_name, result)) logger.info("Expected behaviour: {}".format(result)) write_test_footer(tc_name) @@ -687,14 +687,17 @@ def test_route_map_with_action_values_combination_of_prefix_action_p0( } # tgen.mininet_cli() - result = verify_rib( - tgen, adt, dut, input_dict_2, protocol=protocol, expected=False - ) if "deny" in [prefix_action, rmap_action]: - assert result is not True, "Testcase {} : Failed \n " - "Routes are still present \n Error: {}".format(tc_name, result) + result = verify_rib( + tgen, adt, dut, input_dict_2, protocol=protocol, expected=False + ) + assert result is not True, ("Testcase {} : Failed \n " + "Routes are still present \n Error: {}".format(tc_name, result)) logger.info("Expected behaviour: {}".format(result)) else: + result = verify_rib( + tgen, adt, dut, input_dict_2, protocol=protocol + ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) diff --git a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py index f8c3476e18..bbd18a57ff 100644 --- a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py @@ -360,6 +360,36 @@ def test_linux_ipv6_kernel_routingTable(): ) +def test_ospfv3_routingTable_write_multiplier(): + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # For debugging, uncomment the next line + # tgen.mininet_cli() + + # Modify R1 write muliplier and reset the interfaces + r1 = tgen.gears["r1"] + + r1.vtysh_cmd("conf t\nrouter ospf6\n write-multiplier 100") + r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet") + r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5") + + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().items(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_show_ipv6, router, expected) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff) + + def test_shutdown_check_stderr(): tgen = get_topogen() diff --git a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py index e1857abc4f..b158099d9a 100755 --- a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py +++ b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py @@ -5,7 +5,7 @@ # Part of NetDEF Topology Tests # # Copyright (c) 2021 by Niral Networks, Inc. ("Niral Networks") -# Used Copyright (c) 2016 by Network Device Education Foundation, +# Used Copyright (c) 2016 by Network Device Education Foundation, # Inc. ("NetDEF") in this file. # # Permission to use, copy, modify, and/or distribute this software @@ -179,13 +179,9 @@ def setup_module(mod): "ip link set {0}-stubnet master {0}-cust1", ] - cmds1 = [ - "ip link set {0}-sw5 master {0}-cust1", - ] + cmds1 = ["ip link set {0}-sw5 master {0}-cust1"] - cmds2 = [ - "ip link set {0}-sw6 master {0}-cust1", - ] + cmds2 = ["ip link set {0}-sw6 master {0}-cust1"] # For all registered routers, load the zebra configuration file for rname, router in tgen.routers().items(): @@ -219,6 +215,7 @@ def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() + def test_wait_protocol_convergence(): "Wait for OSPFv3 to converge" tgen = get_topogen() @@ -261,7 +258,7 @@ def compare_show_ipv6_vrf(rname, expected): # Use the vtysh output, with some masking to make comparison easy vrf_name = "{0}-cust1".format(rname) current = topotest.ip6_route_zebra(tgen.gears[rname], vrf_name) - + # Use just the 'O'spf lines of the output linearr = [] for line in current.splitlines(): @@ -331,7 +328,11 @@ def test_linux_ipv6_kernel_routingTable(): for i in range(1, 5): # Actual output from router - actual = tgen.gears["r{}".format(i)].run("ip -6 route show vrf r{}-cust1".format(i)).rstrip() + actual = ( + tgen.gears["r{}".format(i)] + .run("ip -6 route show vrf r{}-cust1".format(i)) + .rstrip() + ) if "nhid" in actual: refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i)) else: @@ -362,9 +363,9 @@ def test_linux_ipv6_kernel_routingTable(): "unreachable fe80::/64 " ): continue - if 'anycast' in line: + if "anycast" in line: continue - if 'multicast' in line: + if "multicast" in line: continue filtered_lines.append(line) actual = "\n".join(filtered_lines).splitlines(1) @@ -398,6 +399,35 @@ def test_linux_ipv6_kernel_routingTable(): ) +def test_ospfv3_routingTable_write_multiplier(): + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # For debugging, uncomment the next line + # tgen.mininet_cli() + # Modify R1 write muliplier and reset the interfaces + r1 = tgen.gears["r1"] + + r1.vtysh_cmd("conf t\nrouter ospf6 vrf r1-cust1 \n write-multiplier 100") + r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet") + r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5") + + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().iteritems(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, "{}/show_ipv6_vrf_route.ref".format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_show_ipv6_vrf, router, expected) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff) + + def test_shutdown_check_stderr(): tgen = get_topogen() diff --git a/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json new file mode 100644 index 0000000000..28e890da50 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json @@ -0,0 +1,199 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto" + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "area": [ + { + "id": "0.0.0.2", + "type": "nssa" + } + ], + "neighbors": { + "r1": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "area": [ + { + "id": "0.0.0.2", + "type": "nssa" + } + ], + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [ + { + "id": "0.0.0.2", + "type": "nssa" + } + ], + "neighbors": { + "r1": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.3" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [ + { + "id": "0.0.0.2", + "type": "nssa" + } + ], + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py index 88b549732c..41960ac79f 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py @@ -774,6 +774,2642 @@ def test_ospf_type5_summary_tc48_p0(request): write_test_footer(tc_name) +def test_ospf_type5_summary_tc42_p0(request): + """OSPF summarisation functionality.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + protocol = "ospf" + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0" + ) + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" + " routes to one route. with aggregate timer as 6 sec" + ) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ], + "aggr_timer": 6, + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Verify that originally advertised routes are withdraw from there" " peer.") + input_dict = { + "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} + } + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Delete the configured summary") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "del_aggr_timer": True, + "delete": True, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( + tc_name + ) + + step("show ip ospf summary should not have any summary address.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + + dut = "r1" + step("All 5 routes are advertised after deletion of configured summary.") + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("configure the summary again and delete static routes .") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + input_dict = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + step("Verify that summary route is withdrawn from R1.") + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Add back static routes.") + input_dict_static_rtes = { + "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary" + " address on R0 and only one route is sent to R1." + ) + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show configure summaries.") + + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Configure new static route which is matching configured summary.") + input_dict_static_rtes = { + "r0": { + "static_routes": [{"network": NETWORK_11["ipv4"], "next_hop": "blackhole"}] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + step("Delete one of the static route.") + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "delete": True} + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + # step("Verify that deleted static route is removed from ospf LSDB.") + # show ip ospf database command is not working, waiting for DEV fix. + + step( + "Configure redistribute connected and configure ospf external" + " summary address to summarise the connected routes." + ) + + dut = "r0" + red_connected(dut) + clear_ospf(tgen, dut) + + ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] + + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + ospf_summ_r1 = { + "r0": { + "ospf": {"summary-address": [{"prefix": ip_net.split("/")[0], "mask": "8"}]} + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured " + "summary address on R0 and only one route is sent to R1." + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": "10.0.0.0/8"}]}} + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Shut one of the interface") + intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + # step("Verify that deleted connected route is removed from ospf LSDB.") + # show ip ospf database command is not working, waiting for DEV fix. + + step("Un do shut the interface") + shutdown_bringup_interface(tgen, dut, intf, True) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + # step("Verify that deleted connected route is removed from ospf LSDB.") + # show ip ospf database command is not working, waiting for DEV fix. + + step("Delete OSPF process.") + ospf_del = {"r0": {"ospf": {"delete": True}}} + result = create_router_ospf(tgen, topo, ospf_del) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Reconfigure ospf process with summary") + reset_config_on_routers(tgen) + + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + red_connected(dut) + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "Verify that external routes are summarised to configured summary " + "address on R0 and only one route is sent to R1." + ) + + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # step("verify that summary lsa is not refreshed.") + # show ip ospf database command is not working, waiting for DEV fix. + + step("Delete the redistribute command in ospf.") + dut = "r0" + red_connected(dut, config=False) + red_static(dut, config=False) + + step("Verify that summary route is withdrawn from the peer.") + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "metric": "1234", + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_type5_summary_tc45_p0(request): + """OSPF summarisation with Tag option""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + step("Configure OSPF on all the routers of the topology.") + reset_config_on_routers(tgen) + + protocol = "ospf" + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0" + ) + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" " routes to one route." + ) + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": "1234", + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary" + " address on R0 and only one route is sent to R1 with configured tag." + ) + input_dict_summary = { + "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1234"}]} + } + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries with tag.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 1234, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Delete the configured summary") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": "1234", + "delete": True, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( + tc_name + ) + + step("show ip ospf summary should not have any summary address.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 1234, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + + step("Configure Min tag value") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8", "tag": 1} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + input_dict_summary = { + "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1"}]} + } + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries with tag.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 1, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Configure Max Tag Value") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": 4294967295, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_summary = { + "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "4294967295"}]} + } + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Verify that boundary values tags are used for summary route" + " using show ip ospf route command." + ) + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 4294967295, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("configure new static route with different tag.") + input_dict_static_rtes_11 = { + "r0": { + "static_routes": [ + {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "tag": "88888"} + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes_11) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("New tag has not been used by summary address.") + + input_dict_summary = { + "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "88888"}]} + } + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, + "ipv4", + dut, + input_dict_summary, + protocol=protocol, + tag="88888", + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step( + "Verify that boundary values tags are used for summary route" + " using show ip ospf route command." + ) + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 88888, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Delete the configured summary address") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": 4294967295, + "delete": True, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that 6 routes are advertised to neighbour with 5 routes" + " without any tag, 1 route with tag." + ) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that summary address is flushed from neighbor.") + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Configure summary first & then configure matching static route.") + + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole", "delete": True}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Repeat steps 1 to 10 of summarisation in non Back bone area.") + reset_config_on_routers(tgen) + + step("Change the area id on the interface on R0") + input_dict = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"area": "0.0.0.0"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"area": "0.0.0.1"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Change the area id on the interface ") + input_dict = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"area": "0.0.0.0"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"area": "0.0.0.1"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0" + ) + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" " routes to one route." + ) + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": "1234", + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary" + " address on R0 and only one route is sent to R1 with configured tag." + ) + input_dict_summary = { + "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1234"}]} + } + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries with tag.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 1234, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the configured summary") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "delete": True, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( + tc_name + ) + + step("show ip ospf summary should not have any summary address.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 1234, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + + step("Configure Min tag value") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8", "tag": 1} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + input_dict_summary = { + "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1"}]} + } + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries with tag.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 1, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Configure Max Tag Value") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": 4294967295, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_summary = { + "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "4294967295"}]} + } + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Verify that boundary values tags are used for summary route" + " using show ip ospf route command." + ) + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 4294967295, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("configure new static route with different tag.") + input_dict_static_rtes_11 = { + "r0": { + "static_routes": [ + {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "tag": "88888"} + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes_11) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("New tag has not been used by summary address.") + + input_dict_summary = { + "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "88888"}]} + } + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, + "ipv4", + dut, + input_dict_summary, + protocol=protocol, + tag="88888", + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step( + "Verify that boundary values tags are used for summary route" + " using show ip ospf route command." + ) + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 88888, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Delete the configured summary address") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": 4294967295, + "delete": True, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that 6 routes are advertised to neighbour with 5 routes" + " without any tag, 1 route with tag." + ) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that summary address is flushed from neighbor.") + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Configure summary first & then configure matching static route.") + + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole", "delete": True}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_type5_summary_tc46_p0(request): + """OSPF summarisation with advertise and no advertise option""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + step("Configure OSPF on all the routers of the topology.") + reset_config_on_routers(tgen) + + protocol = "ospf" + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0" + ) + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" + " routes to one route with no advertise option." + ) + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "advertise": False, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary" + " address on R0 and summary route is not advertised to neighbor as" + " no advertise is configured.." + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the " "configured summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Delete the configured summary") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "delete": True, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Summary has 5 sec delay timer, sleep 5 secs...") + sleep(5) + + step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( + tc_name + ) + + step("show ip ospf summary should not have any summary address.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 1234, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + + step("Reconfigure summary with no advertise.") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "advertise": False, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary" + " address on R0 and summary route is not advertised to neighbor as" + " no advertise is configured.." + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the " "configured summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Change summary address from no advertise to advertise " + "(summary-address 10.0.0.0 255.255.0.0)" + ) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "advertise": False, + } + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Verify that originally advertised routes are withdraw from there" " peer.") + input_dict = { + "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} + } + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_ospf_type5_summary_tc47_p0(request): + """OSPF summarisation with route map filtering.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + protocol = "ospf" + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0" + ) + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" " routes to one route." + ) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Verify that originally advertised routes are withdraw from there" " peer.") + input_dict = { + "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} + } + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step( + "configure route map and add rule to permit configured static " + "routes, redistribute static & connected routes with the route map." + ) + + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "seq_id": 10, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_red_r1 = { + "r0": { + "ospf": { + "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured" + "summary address on R0 and only one route is sent to R1. Verify that " + "show ip ospf summary should show the configure summaries." + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the rule from permit to deny in configured route map.") + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "deny", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "seq_id": 10, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("summary route has 5 secs dealy, sleep 5 secs") + sleep(5) + step("Verify that advertised summary route is flushed from neighbor.") + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Delete the configured route map.") + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "delete": True, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_red_r1 = {"r0": {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + result = create_router_ospf(tgen, topo, ospf_red_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured" + "summary address on R0 and only one route is sent to R1. Verify that " + "show ip ospf summary should show the configure summaries." + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Reconfigure the route map with denying configure summary address.") + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "seq_id": 10, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": SUMMARY["ipv4"][0], "action": "deny"} + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that advertised summary route is not flushed from neighbor.") + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Redistribute static/connected routes without route map.") + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "seq_id": 10, + "delete": True, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured" + "summary address on R0 and only one route is sent to R1. Verify that " + "show ip ospf summary should show the configure summaries." + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step( + "Configure rule to deny all the routes in route map and configure" + " redistribute command in ospf using route map." + ) + + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "deny"} + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "seq_id": 10, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_red_r1 = { + "r0": { + "ospf": { + "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that no summary route is originated.") + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + "seq_id": 10, + "delete": True, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure cli in this order - 2 static routes, a route map to " + "permit those routes, summary address in ospf to match the " + "configured static route network, redistribute the static " + "routes with route map" + ) + + input_dict_static_rtes = { + "r0": { + "static_routes": [{"network": NETWORK2["ipv4"], "next_hop": "blackhole"}] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][1].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + "12.0.0.0/8": { + "Summary address": "12.0.0.0/8", + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Change route map rule for 1 of the routes to deny.") + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": NETWORK2["ipv4"][0], "action": "deny"}, + {"seqid": 20, "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that originated type 5 summary lsa is not refreshed because" + "of the route map events." + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("add rule in route map to deny configured summary address.") + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "12.0.0.0/8", "action": "deny"}, + {"seqid": 20, "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that summary route is not denied, summary route should be" + " originated if matching prefixes are present." + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_ospf_type5_summary_tc51_p2(request): + """OSPF CLI Show. + + verify ospf ASBR summary config and show commands behaviours. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step("Configure all the supported OSPF ASBR summary commands on DUT.") + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": 4294967295, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "16", + "advertise": True, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "24", + "advertise": False, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "24", + "advertise": False, + }, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure and re configure all the commands 10 times in a loop.") + + for itrate in range(0, 10): + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": 4294967295, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "16", + "advertise": True, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "24", + "advertise": False, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "24", + "advertise": False, + }, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "8", + "tag": 4294967295, + "delete": True, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "16", + "advertise": True, + "delete": True, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "24", + "advertise": False, + "delete": True, + }, + { + "prefix": SUMMARY["ipv4"][0].split("/")[0], + "mask": "24", + "advertise": False, + "delete": True, + }, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify the show commands") + + input_dict = { + SUMMARY["ipv4"][2]: { + "Summary address": SUMMARY["ipv4"][2], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 0, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + write_test_footer(tc_name) + + +def test_ospf_type5_summary_tc49_p2(request): + """OSPF summarisation Chaos.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + protocol = "ospf" + + step( + "Configure 5 static routes from the same network on R0" + "5 static routes from different networks and redistribute in R0" + ) + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + red_static(dut) + + step("Verify that routes are learnt on R1.") + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" " routes to one route." + ) + + ospf_summ_r1 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Verify that originally advertised routes are withdraw from there" " peer.") + input_dict = { + "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} + } + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r0") + start_router(tgen, "r0") + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Verify that originally advertised routes are withdraw from there" " peer.") + input_dict = { + "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} + } + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("Kill OSPFd daemon on R0.") + kill_router_daemons(tgen, "r0", ["ospfd"]) + + step("Bring up OSPFd daemon on R0.") + start_router_daemons(tgen, "r0", ["ospfd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R0") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Verify that originally advertised routes are withdraw from there" " peer.") + input_dict = { + "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} + } + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + step("restart zebrad") + kill_router_daemons(tgen, "r0", ["zebra"]) + + step("Bring up zebra daemon on R0.") + start_router_daemons(tgen, "r0", ["zebra"]) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv4"][0]: { + "Summary address": SUMMARY["ipv4"][0], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Verify that originally advertised routes are withdraw from there" " peer.") + input_dict = { + "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} + } + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py new file mode 100644 index 0000000000..393eb19a53 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py @@ -0,0 +1,439 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""OSPF Summarisation Functionality Automation.""" +import os +import sys +import time +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +import ipaddress +from time import sleep + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + kill_router_daemons, + write_test_footer, + reset_config_on_routers, + stop_router, + start_router, + verify_rib, + create_static_routes, + step, + start_router_daemons, + create_route_maps, + shutdown_bringup_interface, + topo_daemons, + create_prefix_lists, + create_route_maps, + create_interfaces_cfg, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_summary, +) + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_asbr_summary_type7_lsa.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ] +} +NETWORK2 = { + "ipv4": [ + "12.0.20.1/32", + "12.0.20.2/32", + "12.0.20.3/32", + "12.0.20.4/32", + "12.0.20.5/32", + ] +} +NETWORK3 = { + "ipv4": [ + "13.0.20.1/32", + "13.0.20.2/32", + "13.0.20.3/32", + "13.0.20.4/32", + "13.0.20.5/32", + ] +} +SUMMARY = {"ipv4": ["11.0.20.1/8", "12.0.0.0/8", "13.0.0.0/8", "11.0.0.0/8"]} +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + + + +TESTCASES = +1. OSPF summarisation with type7 LSAs. + +""" + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_type5_summary_tc44_p0(request): + """OSPF summarisation with type7 LSAs""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Bring up the base config as per the topology") + step("Configure area 1 as NSSA Area") + + reset_config_on_routers(tgen) + + dut = "r0" + protocol = "ospf" + + red_static(dut) + input_dict_static_rtes = { + "r0": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": "blackhole"}, + {"network": NETWORK2["ipv4"], "next_hop": "blackhole"}, + ] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that routes are learnt on R1.") + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step( + "Configure External Route summary in R0 to summarise 5" " routes to one route." + ) + + ospf_summ_r0 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that external routes are summarised to configured summary " + "address on R0 after 5 secs of delay timer expiry and only one " + "route is sent to R1." + ) + + step( + "Configure summary & redistribute static/connected route with " "metric type 2" + ) + + input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][3]}]}} + dut = "r1" + + result = verify_ospf_rib(tgen, dut, input_dict_summary) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Verify that show ip ospf summary should show the summaries.") + input_dict = { + SUMMARY["ipv4"][3]: { + "Summary address": SUMMARY["ipv4"][3], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Learn type 7 lsa from neighbours") + + dut = "r1" + protocol = "ospf" + + red_static(dut) + input_dict_static_rtes = { + "r1": { + "static_routes": [{"network": NETWORK3["ipv4"], "next_hop": "blackhole"}] + } + } + result = create_static_routes(tgen, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that routes are learnt on R0.") + dut = "r0" + + result = verify_ospf_rib(tgen, dut, input_dict_static_rtes) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + ospf_summ_r0 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][2].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that type7 LSAs received from neighbor are not summarised.") + input_dict = { + "13.0.0.0/8": { + "Summary address": "13.0.0.0/8", + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 0, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + step("Verify that already originated summary is intact.") + input_dict = { + SUMMARY["ipv4"][3]: { + "Summary address": SUMMARY["ipv4"][3], + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5, + } + } + dut = "r0" + result = verify_ospf_summary(tgen, topo, dut, input_dict) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + + dut = "r1" + aggr_timer = {"r1": {"ospf": {"aggr_timer": 6}}} + result = create_router_ospf(tgen, topo, aggr_timer) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + ospf_summ_r0 = { + "r0": { + "ospf": { + "summary-address": [ + {"prefix": SUMMARY["ipv4"][2].split("/")[0], "mask": "8"} + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_summ_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "wait for 6+1 seconds as ospf aggregation start after 6 secs as " + "per the above aggr_timer command" + ) + sleep(7) + dut = "r1" + aggr_timer = {"r1": {"ospf": {"del_aggr_timer": 6}}} + result = create_router_ospf(tgen, topo, aggr_timer) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index 0d64320488..732470f828 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -31,7 +31,29 @@ show bgp ipv6 statistics show bgp martian next-hop show bgp nexthop +show bgp vrf all summary +show bgp vrf all ipv4 +show bgp vrf all ipv6 +show bgp vrf all neighbors + show bgp evpn route +show bgp l2vpn evpn route vni all +show bgp l2vpn evpn vni +show bgp l2vpn evpn import-rt +show bgp l2vpn evpn vrf-import-rt +show bgp l2vpn evpn all overlay +show bgp l2vpn evpn summary +show bgp l2vpn evpn route detail +show bgp l2vpn evpn vni remote-ip-hash +show bgp l2vpn evpn vni-svi-hash + +show evpn +show evpn arp-cache vni all detail +show evpn mac vni all detail +show evpn next-hops vni all +show evpn rmac vni all +show evpn vni detail + CMD_LIST_END # Zebra Support Bundle Command List diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 7cb8a2e729..eb8753fd08 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -1391,6 +1391,53 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_del_to_del.append((ctx_keys, line)) """ + Neighbor changes of route-maps need to be accounted for in that we + do not want to do a `no route-map...` `route-map ....` when changing + a route-map. This is bad mojo as that we will send/receive + data we don't want. + Additionally we need to ensure that if we have different afi/safi + variants that they actually match and if we are going from a very + old style command such that the neighbor command is under the + `router bgp ..` node that we need to handle that appropriately + """ + re_nbr_rm = re.search("neighbor(.*)route-map(.*)(in|out)$", line) + if re_nbr_rm: + adjust_for_bgp_node = 0 + neighbor_name = re_nbr_rm.group(1) + rm_name_del = re_nbr_rm.group(2) + dir = re_nbr_rm.group(3) + search = "neighbor%sroute-map(.*)%s" % (neighbor_name, dir) + save_line = "EMPTY" + for (ctx_keys_al, add_line) in lines_to_add: + if ctx_keys_al[0].startswith("router bgp"): + if add_line: + rm_match = re.search(search, add_line) + if rm_match: + rm_name_add = rm_match.group(1) + if rm_name_add == rm_name_del: + continue + if len(ctx_keys_al) == 1: + save_line = line + adjust_for_bgp_node = 1 + else: + if ( + len(ctx_keys) > 1 + and len(ctx_keys_al) > 1 + and ctx_keys[1] == ctx_keys_al[1] + ): + lines_to_del_to_del.append((ctx_keys_al, line)) + + if adjust_for_bgp_node == 1: + for (ctx_keys_dl, dl_line) in lines_to_del: + if ( + ctx_keys_dl[0].startswith("router bgp") + and len(ctx_keys_dl) > 1 + and ctx_keys_dl[1] == "address-family ipv4 unicast" + ): + if save_line == dl_line: + lines_to_del_to_del.append((ctx_keys_dl, save_line)) + + """ We changed how we display the neighbor interface command. Older versions of frr would display the following: neighbor swp1 interface diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 6c3863132d..7af9148a8e 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -771,6 +771,7 @@ void vrrp_vty_init(void) install_node(&debug_node); install_node(&interface_node); install_node(&vrrp_node); + vrf_cmd_init(NULL, &vrrp_privs); if_cmd_init(); install_element(VIEW_NODE, &vrrp_vrid_show_cmd); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 72835e7526..507c6ce882 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1263,7 +1263,6 @@ static struct cmd_node pw_node = { .prompt = "%s(config-pw)# ", }; -#if defined(HAVE_PATHD) static struct cmd_node segment_routing_node = { .name = "segment-routing", .node = SEGMENT_ROUTING_NODE, @@ -1271,6 +1270,7 @@ static struct cmd_node segment_routing_node = { .prompt = "%s(config-sr)# ", }; +#if defined(HAVE_PATHD) static struct cmd_node sr_traffic_eng_node = { .name = "sr traffic-eng", .node = SR_TRAFFIC_ENG_NODE, @@ -2171,7 +2171,6 @@ DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfab } #endif /* HAVE_FABRICD */ -#if defined(HAVE_PATHD) DEFUNSH(VTYSH_SR, segment_routing, segment_routing_cmd, "segment-routing", "Configure segment routing\n") @@ -2180,6 +2179,7 @@ DEFUNSH(VTYSH_SR, segment_routing, segment_routing_cmd, return CMD_SUCCESS; } +#if defined (HAVE_PATHD) DEFUNSH(VTYSH_PATHD, sr_traffic_eng, sr_traffic_eng_cmd, "traffic-eng", "Configure SR traffic engineering\n") @@ -2664,6 +2664,18 @@ DEFUNSH(VTYSH_KEYS, vtysh_quit_keys, vtysh_quit_keys_cmd, "quit", return vtysh_exit_keys(self, vty, argc, argv); } +DEFUNSH(VTYSH_SR, vtysh_exit_sr, vtysh_exit_sr_cmd, "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_SR, vtysh_quit_sr, vtysh_quit_sr_cmd, "quit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + #if defined(HAVE_PATHD) DEFUNSH(VTYSH_PATHD, vtysh_exit_pathd, vtysh_exit_pathd_cmd, "exit", "Exit current mode and down to previous mode\n") @@ -2839,7 +2851,6 @@ DEFUN (vtysh_show_poll, return show_per_daemon(vty, argv, argc, "Thread statistics for %s:\n"); } -#ifndef EXCLUDE_CPU_TIME DEFUN (vtysh_show_thread, vtysh_show_thread_cmd, "show thread cpu [FILTER]", @@ -2850,7 +2861,6 @@ DEFUN (vtysh_show_thread, { return show_per_daemon(vty, argv, argc, "Thread statistics for %s:\n"); } -#endif DEFUN (vtysh_show_work_queues, vtysh_show_work_queues_cmd, @@ -4329,15 +4339,17 @@ void vtysh_init_vty(void) install_element(BFD_PROFILE_NODE, &vtysh_end_all_cmd); #endif /* HAVE_BFDD */ -#if defined(HAVE_PATHD) install_node(&segment_routing_node); + install_element(SEGMENT_ROUTING_NODE, &vtysh_exit_sr_cmd); + install_element(SEGMENT_ROUTING_NODE, &vtysh_quit_sr_cmd); + install_element(SEGMENT_ROUTING_NODE, &vtysh_end_all_cmd); + +#if defined(HAVE_PATHD) install_node(&sr_traffic_eng_node); install_node(&srte_segment_list_node); install_node(&srte_policy_node); install_node(&srte_candidate_dyn_node); - install_element(SEGMENT_ROUTING_NODE, &vtysh_exit_pathd_cmd); - install_element(SEGMENT_ROUTING_NODE, &vtysh_quit_pathd_cmd); install_element(SR_TRAFFIC_ENG_NODE, &vtysh_exit_pathd_cmd); install_element(SR_TRAFFIC_ENG_NODE, &vtysh_quit_pathd_cmd); install_element(SR_SEGMENT_LIST_NODE, &vtysh_exit_pathd_cmd); @@ -4347,7 +4359,7 @@ void vtysh_init_vty(void) install_element(SR_CANDIDATE_DYN_NODE, &vtysh_exit_pathd_cmd); install_element(SR_CANDIDATE_DYN_NODE, &vtysh_quit_pathd_cmd); - install_element(SEGMENT_ROUTING_NODE, &vtysh_end_all_cmd); + install_element(SR_TRAFFIC_ENG_NODE, &vtysh_end_all_cmd); install_element(SR_SEGMENT_LIST_NODE, &vtysh_end_all_cmd); install_element(SR_POLICY_NODE, &vtysh_end_all_cmd); @@ -4553,9 +4565,7 @@ void vtysh_init_vty(void) install_element(VIEW_NODE, &vtysh_show_modules_cmd); install_element(VIEW_NODE, &vtysh_show_work_queues_cmd); install_element(VIEW_NODE, &vtysh_show_work_queues_daemon_cmd); -#ifndef EXCLUDE_CPU_TIME install_element(VIEW_NODE, &vtysh_show_thread_cmd); -#endif install_element(VIEW_NODE, &vtysh_show_poll_cmd); /* Logging */ diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 87f1f67443..71f672554b 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -56,7 +56,7 @@ DECLARE_MGROUP(MVTYSH); #define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD #define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD -#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD +#define VTYSH_VRF VTYSH_INTERFACE|VTYSH_STATICD #define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index ca60c8f7b6..1c990b5ed9 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -300,6 +300,18 @@ module frr-bgp-route-map { "Set BGP large community list (for deletion)"; } + identity set-evpn-gateway-ip-ipv4 { + base frr-route-map:rmap-set-type; + description + "Set EVPN gateway IP overlay index IPv4"; + } + + identity set-evpn-gateway-ip-ipv6 { + base frr-route-map:rmap-set-type; + description + "Set EVPN gateway IP overlay index IPv6"; + } + grouping extcommunity-non-transitive-types { leaf two-octet-as-specific { type boolean; @@ -816,5 +828,25 @@ module frr-bgp-route-map { type bgp-filter:bgp-list-name; } } + case evpn-gateway-ip-ipv4 { + when + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, + 'frr-bgp-route-map:set-evpn-gateway-ip-ipv4')"; + description + "Set EVPN gateway IP overlay index IPv4"; + leaf evpn-gateway-ip-ipv4 { + type inet:ipv4-address; + } + } + case evpn-gateway-ip-ipv6 { + when + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, + 'frr-bgp-route-map:set-evpn-gateway-ip-ipv6')"; + description + "Set EVPN gateway IP overlay index IPv6"; + leaf evpn-gateway-ip-ipv6 { + type inet:ipv6-address; + } + } } } diff --git a/zebra/rib.h b/zebra/rib.h index 75d7ae1b67..957f38602a 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -401,7 +401,7 @@ extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, bool fromkernel); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, - union g_addr *addr, + const union g_addr *addr, struct route_node **rn_out); extern struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, struct in_addr addr, diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index a8df0c56ce..a5d672987d 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -157,12 +157,17 @@ struct dplane_pw_info { int af; int status; uint32_t flags; + uint32_t nhg_id; union g_addr dest; mpls_label_t local_label; mpls_label_t remote_label; - /* Nexthops */ - struct nexthop_group nhg; + /* Nexthops that are valid and installed */ + struct nexthop_group fib_nhg; + + /* Primary and backup nexthop sets, copied from the resolving route. */ + struct nexthop_group primary_nhg; + struct nexthop_group backup_nhg; union pw_protocol_fields fields; }; @@ -664,11 +669,21 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_PW_INSTALL: case DPLANE_OP_PW_UNINSTALL: /* Free allocated nexthops */ - if (ctx->u.pw.nhg.nexthop) { + if (ctx->u.pw.fib_nhg.nexthop) { /* This deals with recursive nexthops too */ - nexthops_free(ctx->u.pw.nhg.nexthop); + nexthops_free(ctx->u.pw.fib_nhg.nexthop); + + ctx->u.pw.fib_nhg.nexthop = NULL; + } + if (ctx->u.pw.primary_nhg.nexthop) { + nexthops_free(ctx->u.pw.primary_nhg.nexthop); + + ctx->u.pw.primary_nhg.nexthop = NULL; + } + if (ctx->u.pw.backup_nhg.nexthop) { + nexthops_free(ctx->u.pw.backup_nhg.nexthop); - ctx->u.pw.nhg.nexthop = NULL; + ctx->u.pw.backup_nhg.nexthop = NULL; } break; @@ -1630,7 +1645,23 @@ dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.pw.nhg); + return &(ctx->u.pw.fib_nhg); +} + +const struct nexthop_group * +dplane_ctx_get_pw_primary_nhg(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.pw.primary_nhg); +} + +const struct nexthop_group * +dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.pw.backup_nhg); } /* Accessors for interface information */ @@ -2461,12 +2492,14 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, struct zebra_pw *pw) { + int ret = EINVAL; struct prefix p; afi_t afi; struct route_table *table; struct route_node *rn; struct route_entry *re; const struct nexthop_group *nhg; + struct nexthop *nh, *newnh, *last_nh; if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u", @@ -2509,31 +2542,83 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, afi = (pw->af == AF_INET) ? AFI_IP : AFI_IP6; table = zebra_vrf_table(afi, SAFI_UNICAST, pw->vrf_id); - if (table) { - rn = route_node_match(table, &p); - if (rn) { - RNODE_FOREACH_RE(rn, re) { - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) - break; + if (table == NULL) + goto done; + + rn = route_node_match(table, &p); + if (rn == NULL) + goto done; + + re = NULL; + RNODE_FOREACH_RE(rn, re) { + if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) + break; + } + + if (re) { + /* We'll capture a 'fib' list of nexthops that meet our + * criteria: installed, and labelled. + */ + nhg = rib_get_fib_nhg(re); + last_nh = NULL; + + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nh)) { + if (!CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE) + || CHECK_FLAG(nh->flags, + NEXTHOP_FLAG_RECURSIVE) + || nh->nh_label == NULL) + continue; + + newnh = nexthop_dup(nh, NULL); + + if (last_nh) + NEXTHOP_APPEND(last_nh, newnh); + else + ctx->u.pw.fib_nhg.nexthop = newnh; + last_nh = newnh; } + } - if (re) { - nhg = rib_get_fib_nhg(re); - if (nhg && nhg->nexthop) - copy_nexthops(&(ctx->u.pw.nhg.nexthop), - nhg->nexthop, NULL); - - /* Include any installed backup nexthops */ - nhg = rib_get_fib_backup_nhg(re); - if (nhg && nhg->nexthop) - copy_nexthops(&(ctx->u.pw.nhg.nexthop), - nhg->nexthop, NULL); + /* Include any installed backup nexthops also. */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nh)) { + if (!CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE) + || CHECK_FLAG(nh->flags, + NEXTHOP_FLAG_RECURSIVE) + || nh->nh_label == NULL) + continue; + + newnh = nexthop_dup(nh, NULL); + + if (last_nh) + NEXTHOP_APPEND(last_nh, newnh); + else + ctx->u.pw.fib_nhg.nexthop = newnh; + last_nh = newnh; } - route_unlock_node(rn); + } + + /* Copy primary nexthops; recursive info is included too */ + assert(re->nhe != NULL); /* SA warning */ + copy_nexthops(&(ctx->u.pw.primary_nhg.nexthop), + re->nhe->nhg.nexthop, NULL); + ctx->u.pw.nhg_id = re->nhe->id; + + /* Copy backup nexthop info, if present */ + if (re->nhe->backup_info && re->nhe->backup_info->nhe) { + copy_nexthops(&(ctx->u.pw.backup_nhg.nexthop), + re->nhe->backup_info->nhe->nhg.nexthop, + NULL); } } + route_unlock_node(rn); - return AOK; + ret = AOK; + +done: + return ret; } /** diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 3a8536dda5..e091655a48 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -437,6 +437,10 @@ const union pw_protocol_fields *dplane_ctx_get_pw_proto( const struct zebra_dplane_ctx *ctx); const struct nexthop_group *dplane_ctx_get_pw_nhg( const struct zebra_dplane_ctx *ctx); +const struct nexthop_group * +dplane_ctx_get_pw_primary_nhg(const struct zebra_dplane_ctx *ctx); +const struct nexthop_group * +dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx); /* Accessors for interface information */ uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx); diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 30f4a44769..816f46bac9 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -134,6 +134,10 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) if (json == NULL) { vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name); vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex); + vty_out(vty, " SVI interface: %s\n", + (zevpn->svi_if ? zevpn->svi_if->name : "")); + vty_out(vty, " SVI ifIndex: %u\n", + (zevpn->svi_if ? zevpn->svi_if->ifindex : 0)); vty_out(vty, " Local VTEP IP: %pI4\n", &zevpn->local_vtep_ip); vty_out(vty, " Mcast group: %pI4\n", @@ -142,6 +146,12 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) json_object_string_add(json, "vxlanInterface", zevpn->vxlan_if->name); json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex); + if (zevpn->svi_if) { + json_object_string_add(json, "sviInterface", + zevpn->svi_if->name); + json_object_int_add(json, "sviIfindex", + zevpn->svi_if->ifindex); + } json_object_string_add(json, "vtepIp", inet_ntop(AF_INET, &zevpn->local_vtep_ip, buf, sizeof(buf))); @@ -1048,6 +1058,8 @@ int zebra_evpn_del(zebra_evpn_t *zevpn) zvrf = zebra_vrf_get_evpn(); assert(zvrf); + zevpn->svi_if = NULL; + /* Free the neighbor hash table. */ hash_free(zevpn->neigh_table); zevpn->neigh_table = NULL; @@ -1075,6 +1087,7 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) { struct zserv *client; struct stream *s; + ifindex_t svi_index; int rc; client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); @@ -1082,6 +1095,8 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) if (!client) return 0; + svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0; + s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); @@ -1089,15 +1104,18 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) stream_put_in_addr(s, &zevpn->local_vtep_ip); stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ stream_put_in_addr(s, &zevpn->mcast_grp); + stream_put(s, &svi_index, sizeof(ifindex_t)); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send EVPN_ADD %u %pI4 tenant vrf %s to %s", zevpn->vni, - &zevpn->local_vtep_ip, - vrf_id_to_name(zevpn->vrf_id), - zebra_route_string(client->proto)); + zlog_debug( + "Send EVPN_ADD %u %pI4 tenant vrf %s(%u) SVI index %u to %s", + zevpn->vni, &zevpn->local_vtep_ip, + vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id, + (zevpn->svi_if ? zevpn->svi_if->ifindex : 0), + zebra_route_string(client->proto)); client->vniadd_cnt++; rc = zserv_send_message(client, s); diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index 27392ec85c..ee9e1406e4 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -98,6 +98,9 @@ struct zebra_evpn_t_ { /* Corresponding VxLAN interface. */ struct interface *vxlan_if; + /* Corresponding SVI interface. */ + struct interface *svi_if; + /* List of remote VTEPs */ zebra_vtep_t *vteps; diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index cebd576365..efbd078a52 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -42,6 +42,7 @@ #include "zebra/zebra_fpm_private.h" #include "zebra/zebra_vxlan_private.h" +#include "zebra/interface.h" /* * af_addr_size @@ -164,7 +165,10 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri, { struct netlink_nh_info nhi; union g_addr *src; - zebra_l3vni_t *zl3vni = NULL; + struct zebra_vrf *zvrf = NULL; + struct interface *ifp = NULL, *link_if = NULL; + struct zebra_if *zif = NULL; + vni_t vni = 0; memset(&nhi, 0, sizeof(nhi)); src = NULL; @@ -199,12 +203,29 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri, if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN; - zl3vni = zl3vni_from_vrf(nexthop->vrf_id); - if (zl3vni && is_l3vni_oper_up(zl3vni)) { - - /* Add VNI to VxLAN encap info */ - nhi.encap_info.vxlan_encap.vni = zl3vni->vni; + /* Extract VNI id for the nexthop SVI interface */ + zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); + if (zvrf) { + ifp = if_lookup_by_index_per_ns(zvrf->zns, + nexthop->ifindex); + if (ifp) { + zif = (struct zebra_if *)ifp->info; + if (zif) { + if (IS_ZEBRA_IF_BRIDGE(ifp)) + link_if = ifp; + else if (IS_ZEBRA_IF_VLAN(ifp)) + link_if = + if_lookup_by_index_per_ns( + zvrf->zns, + zif->link_ifindex); + if (link_if) + vni = vni_id_from_svi(ifp, + link_if); + } + } } + + nhi.encap_info.vxlan_encap.vni = vni; } /* diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 850ca17636..a4576b310e 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -54,6 +54,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, FEC, "MPLS FEC object"); DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object"); int mpls_enabled; +bool mpls_pw_reach_strict; /* Strict reachability checking */ /* static function declarations */ @@ -3977,6 +3978,7 @@ void zebra_mpls_init_tables(struct zebra_vrf *zvrf) void zebra_mpls_init(void) { mpls_enabled = 0; + mpls_pw_reach_strict = false; if (mpls_kernel_init() < 0) { flog_warn(EC_ZEBRA_MPLS_SUPPORT_DISABLED, diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index dd2d2e5658..7059d393ed 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -576,6 +576,7 @@ static inline int mpls_should_lsps_be_processed(struct route_node *rn) /* Global variables. */ extern int mpls_enabled; +extern bool mpls_pw_reach_strict; /* Strict pseudowire reachability checking */ #ifdef __cplusplus } diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index b767929dc0..74b1e37278 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -458,6 +458,9 @@ int mpls_kernel_init(void) kr_state.rtseq = 1; + /* Strict pseudowire reachability checking required for obsd */ + mpls_pw_reach_strict = true; + return 0; } diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 5afb3e5926..6b4a815151 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -48,7 +48,7 @@ static int zebra_pw_enabled(struct zebra_pw *); static void zebra_pw_install(struct zebra_pw *); static void zebra_pw_uninstall(struct zebra_pw *); static int zebra_pw_install_retry(struct thread *); -static int zebra_pw_check_reachability(struct zebra_pw *); +static int zebra_pw_check_reachability(const struct zebra_pw *); static void zebra_pw_update_status(struct zebra_pw *, int); static inline int zebra_pw_compare(const struct zebra_pw *a, @@ -243,14 +243,79 @@ static void zebra_pw_update_status(struct zebra_pw *pw, int status) zsend_pw_update(pw->client, pw); } -static int zebra_pw_check_reachability(struct zebra_pw *pw) +static int zebra_pw_check_reachability_strict(const struct zebra_pw *pw, + struct route_entry *re) +{ + const struct nexthop *nexthop; + const struct nexthop_group *nhg; + bool found_p = false; + bool fail_p = false; + + /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ + + /* All active nexthops must be labelled; look at + * primary and backup fib lists, in case there's been + * a backup nexthop activation. + */ + nhg = rib_get_fib_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + if (nexthop->nh_label != NULL) + found_p = true; + else { + fail_p = true; + break; + } + } + } + } + + if (fail_p) + goto done; + + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + if (nexthop->nh_label != NULL) + found_p = true; + else { + fail_p = true; + break; + } + } + } + } + +done: + + if (fail_p || !found_p) { + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%s: unlabeled route for %s", + __func__, pw->ifname); + return -1; + } + + return 0; +} + +static int zebra_pw_check_reachability(const struct zebra_pw *pw) { struct route_entry *re; - struct nexthop *nexthop; + const struct nexthop *nexthop; + const struct nexthop_group *nhg; + bool found_p = false; /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ - /* find route to the remote end of the pseudowire */ + /* Find route to the remote end of the pseudowire */ re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); if (!re) { @@ -260,19 +325,52 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw) return -1; } - /* - * Need to ensure that there's a label binding for all nexthops. - * Otherwise, ECMP for this route could render the pseudowire unusable. + /* Stricter checking for some OSes (OBSD, e.g.) */ + if (mpls_pw_reach_strict) + return zebra_pw_check_reachability_strict(pw, re); + + /* There must be at least one installed labelled nexthop; + * look at primary and backup fib lists, in case there's been + * a backup nexthop activation. */ - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { - if (!nexthop->nh_label) { - if (IS_ZEBRA_DEBUG_PW) - zlog_debug("%s: unlabeled route for %s", - __func__, pw->ifname); - return -1; + nhg = rib_get_fib_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) && + nexthop->nh_label != NULL) { + found_p = true; + break; + } + } + } + + if (found_p) + return 0; + + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) && + nexthop->nh_label != NULL) { + found_p = true; + break; + } } } + if (!found_p) { + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%s: unlabeled route for %s", + __func__, pw->ifname); + return -1; + } + return 0; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8afb5053c4..d0acf77936 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -332,7 +332,8 @@ void rib_handle_nhg_replace(struct nhg_hash_entry *old_entry, } struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, - union g_addr *addr, struct route_node **rn_out) + const union g_addr *addr, + struct route_node **rn_out) { struct prefix p; struct route_table *table; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 09eb78917c..2f3ea7475a 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -1012,6 +1012,7 @@ static int zevpn_build_hash_table_zns(struct ns *ns, vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf( vlan_if->vrf_id); @@ -1841,6 +1842,27 @@ static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp, return zl3vni; } +vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if) +{ + vni_t vni = 0; + zebra_evpn_t *zevpn = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* Check if an L3VNI belongs to this SVI interface. + * If not, check if an L2VNI belongs to this SVI interface. + */ + zl3vni = zl3vni_from_svi(ifp, br_if); + if (zl3vni) + vni = zl3vni->vni; + else { + zevpn = zebra_evpn_from_svi(ifp, br_if); + if (zevpn) + vni = zevpn->vni; + } + + return vni; +} + static inline void zl3vni_get_vrr_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) { @@ -4527,6 +4549,7 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) zevpn = zebra_evpn_from_svi(ifp, link_if); if (zevpn) { + zevpn->svi_if = NULL; zevpn->vrf_id = VRF_DEFAULT; /* update the tenant vrf in BGP */ @@ -4582,6 +4605,7 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) vrf_id_to_name(ifp->vrf_id)); /* update the vrf information for l2-vni and inform bgp */ + zevpn->svi_if = ifp; zevpn->vrf_id = ifp->vrf_id; if (if_is_operative(zevpn->vxlan_if)) @@ -4792,6 +4816,7 @@ int zebra_vxlan_if_up(struct interface *ifp) vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) @@ -4894,6 +4919,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) struct zebra_l2info_vxlan *vxl = NULL; zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4983,6 +5009,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { /* Delete from client, remove all remote VTEPs */ /* Also, free up all MACs and neighbors. */ + zevpn->svi_if = NULL; zebra_evpn_send_del_to_client(zevpn); zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); @@ -5012,6 +5039,11 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) zebra_evpn_es_set_base_evpn(zevpn); } zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) + zevpn->svi_if = vlan_if; + /* Take further actions needed. * Note that if we are here, there is a change of interest. */ @@ -5131,6 +5163,7 @@ int zebra_vxlan_if_add(struct interface *ifp) vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { + zevpn->svi_if = vlan_if; zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 0556c4adce..84ac76b3b9 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -224,6 +224,7 @@ extern struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni); extern zebra_l3vni_t *zl3vni_lookup(vni_t vni); +extern vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if); DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, bool delete, const char *reason), (rmac, zl3vni, delete, reason)); |
