diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index b96c287f8600..78003bc08506 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1246,10 +1246,14 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi, source_bpi, bpi, bgp_orig, p, - debug)) + debug)) { bgp_path_info_set_flag(bn, bpi, BGP_PATH_VALID); - else + if (CHECK_FLAG(bpi->flags, BGP_PATH_SRV6_TE)) + SET_FLAG(bpi->flags, BGP_PATH_SRV6_TE_VALID); + } else { bgp_path_info_unset_flag(bn, bpi, BGP_PATH_VALID); + UNSET_FLAG(bpi->flags, BGP_PATH_SRV6_TE_VALID); + } /* Process change. */ bgp_aggregate_increment(to_bgp, p, bpi, afi, safi); @@ -1307,10 +1311,14 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, new->extra->vrfleak->nexthop_orig = *nexthop_orig; if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi, - source_bpi, new, bgp_orig, p, debug)) + source_bpi, new, bgp_orig, p, debug)) { bgp_path_info_set_flag(bn, new, BGP_PATH_VALID); - else + if (CHECK_FLAG(new->flags, BGP_PATH_SRV6_TE)) + SET_FLAG(new->flags, BGP_PATH_SRV6_TE_VALID); + } else { bgp_path_info_unset_flag(bn, new, BGP_PATH_VALID); + UNSET_FLAG(new->flags, BGP_PATH_SRV6_TE_VALID); + } bgp_aggregate_increment(to_bgp, p, new, afi, safi); bgp_path_info_add(bn, new); @@ -2691,6 +2699,7 @@ void vpn_leak_no_retain(struct bgp *to_bgp, struct bgp *vpn_from, afi_t afi) continue; bgp_unlink_nexthop(bpi); + bgp_unlink_te_nexthop(bpi); bgp_rib_remove(bn, bpi, bpi->peer, afi, safi); } } diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index ed689c8bac08..9bec68417e44 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -82,7 +82,8 @@ bool bnc_existing_for_prefix(struct bgp_nexthop_cache *bnc) frr_each (bgp_nexthop_cache, bnc->tree, bnc_tmp) { if (bnc_tmp == bnc) continue; - if (prefix_cmp(&bnc->prefix, &bnc_tmp->prefix) == 0) + if (prefix_cmp(&bnc->prefix, &bnc_tmp->prefix) == 0 + && (bnc->srte_color == bnc_tmp->srte_color)) return true; } return false; @@ -125,6 +126,7 @@ static void bgp_nexthop_cache_reset(struct bgp_nexthop_cache_head *tree) bgp_mplsvpn_path_nh_label_bind_unlink(path); path_nh_map(path, bnc, false); + path_tenh_map(path, bnc, false); } bnc_free(bnc); @@ -797,45 +799,86 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, paths = json_object_new_array(); else vty_out(vty, " Paths:\n"); - LIST_FOREACH (path, &(bnc->paths), nh_thread) { - dest = path->net; - assert(dest && bgp_dest_table(dest)); - afi = family2afi(bgp_dest_get_prefix(dest)->family); - table = bgp_dest_table(dest); - safi = table->safi; - bgp_path = table->bgp; - - - if (json) { - json_path = json_object_new_object(); - json_object_string_add(json_path, "afi", afi2str(afi)); - json_object_string_add(json_path, "safi", - safi2str(safi)); - json_object_string_addf(json_path, "prefix", "%pBD", - dest); - if (dest->pdest) - json_object_string_addf( - json_path, "rd", - BGP_RD_AS_FORMAT(bgp->asnotation), + if (bnc->srte_color) { + LIST_FOREACH (path, &(bnc->paths), te_nh_thread) { + dest = path->net; + assert(dest && bgp_dest_table(dest)); + afi = family2afi(bgp_dest_get_prefix(dest)->family); + table = bgp_dest_table(dest); + safi = table->safi; + bgp_path = table->bgp; + + if (json) { + json_path = json_object_new_object(); + json_object_string_add(json_path, "afi", afi2str(afi)); + json_object_string_add(json_path, "safi", + safi2str(safi)); + json_object_string_addf(json_path, "prefix", "%pBD", + dest); + if (dest->pdest) + json_object_string_addf( + json_path, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), + (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest)); + json_object_string_add( + json_path, "vrf", + vrf_id_to_name(bgp_path->vrf_id)); + bgp_show_bgp_path_info_flags(path->flags, json_path); + json_object_array_add(paths, json_path); + continue; + } + if (dest->pdest) { + vty_out(vty, " %d/%d %pBD RD ", afi, safi, dest); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( dest->pdest)); - json_object_string_add( - json_path, "vrf", - vrf_id_to_name(bgp_path->vrf_id)); - bgp_show_bgp_path_info_flags(path->flags, json_path); - json_object_array_add(paths, json_path); - continue; + vty_out(vty, " %s flags 0x%x\n", bgp_path->name_pretty, + path->flags); + } else + vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", + afi, safi, dest, bgp_path->name_pretty, path->flags); + } + } else { + LIST_FOREACH (path, &(bnc->paths), nh_thread) { + dest = path->net; + assert(dest && bgp_dest_table(dest)); + afi = family2afi(bgp_dest_get_prefix(dest)->family); + table = bgp_dest_table(dest); + safi = table->safi; + bgp_path = table->bgp; + + if (json) { + json_path = json_object_new_object(); + json_object_string_add(json_path, "afi", afi2str(afi)); + json_object_string_add(json_path, "safi", + safi2str(safi)); + json_object_string_addf(json_path, "prefix", "%pBD", + dest); + if (dest->pdest) + json_object_string_addf( + json_path, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), + (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest)); + json_object_string_add( + json_path, "vrf", + vrf_id_to_name(bgp_path->vrf_id)); + bgp_show_bgp_path_info_flags(path->flags, json_path); + json_object_array_add(paths, json_path); + continue; + } + if (dest->pdest) { + vty_out(vty, " %d/%d %pBD RD ", afi, safi, dest); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), + (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest)); + vty_out(vty, " %s flags 0x%x\n", bgp_path->name_pretty, + path->flags); + } else + vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", + afi, safi, dest, bgp_path->name_pretty, path->flags); } - if (dest->pdest) { - vty_out(vty, " %d/%d %pBD RD ", afi, safi, dest); - vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), - (struct prefix_rd *)bgp_dest_get_prefix( - dest->pdest)); - vty_out(vty, " %s flags 0x%x\n", bgp_path->name_pretty, - path->flags); - } else - vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", - afi, safi, dest, bgp_path->name_pretty, path->flags); } if (json) json_object_object_add(json, "paths", paths); @@ -1368,7 +1411,7 @@ char *bgp_nexthop_dump_bnc_flags(struct bgp_nexthop_cache *bnc, char *buf, return buf; } - snprintfrr(buf, len, "%s%s%s%s%s%s%s", + snprintfrr(buf, len, "%s%s%s%s%s%s%s%s", CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) ? "Valid " : "", CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) ? "Reg " : "", CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED) ? "Conn " : "", @@ -1380,6 +1423,9 @@ char *bgp_nexthop_dump_bnc_flags(struct bgp_nexthop_cache *bnc, char *buf, : "", CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) ? "Label Valid " + : "", + CHECK_FLAG(bnc->flags, BGP_NEXTHOP_SRV6TE_VALID) + ? "SRv6 TE Valid " : ""); return buf; diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 5679c215b157..938abe5fcdbd 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -66,6 +66,7 @@ struct bgp_nexthop_cache { #define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5) #define BGP_NEXTHOP_LABELED_VALID (1 << 6) #define BGP_NEXTHOP_ULTIMATE (1 << 7) +#define BGP_NEXTHOP_SRV6TE_VALID (1 << 8) /* * This flag is added for EVPN gateway IP nexthops. diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 164e2300c09e..486988859ce1 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -38,7 +38,7 @@ extern struct zclient *zclient; static void register_zebra_rnh(struct bgp_nexthop_cache *bnc); static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc); -static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p); +static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p, bool is_te); static void bgp_nht_ifp_initial(struct event *thread); DEFINE_HOOK(bgp_nht_path_update, (struct bgp *bgp, struct bgp_path_info *pi, bool valid), @@ -180,6 +180,18 @@ void bgp_unlink_nexthop(struct bgp_path_info *path) bgp_unlink_nexthop_check(bnc); } +void bgp_unlink_te_nexthop(struct bgp_path_info *path) +{ + struct bgp_nexthop_cache *bnc = path->te_nexthop; + + if (!bnc) + return; + + path_tenh_map(path, NULL, false); + + bgp_unlink_nexthop_check(bnc); +} + void bgp_replace_nexthop_by_peer(struct peer *from, struct peer *to) { struct prefix pp; @@ -303,12 +315,17 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, const struct prefix *orig_prefix) { struct bgp_nexthop_cache_head *tree = NULL; - struct bgp_nexthop_cache *bnc; + struct bgp_nexthop_cache *bnc = NULL; + struct bgp_nexthop_cache *te_bnc = NULL; struct bgp_path_info *bpi_ultimate; struct prefix p; uint32_t srte_color = 0; int is_bgp_static_route = 0; ifindex_t ifindex = 0; + bool srv6_route = false; + + if (pi && (pi->attr->srv6_l3vpn || pi->attr->srv6_vpn)) + srv6_route = true; if (pi) { is_bgp_static_route = ((pi->type == ZEBRA_ROUTE_BGP) @@ -330,7 +347,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, /* This will return true if the global IPv6 NH is a link local * addr */ - if (make_prefix(afi, pi, &p) < 0) + if (make_prefix(afi, pi, &p, false) < 0) return 1; /* @@ -395,9 +412,9 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, else tree = &bgp_nexthop->nexthop_cache_table[afi]; - bnc = bnc_find(tree, &p, srte_color, ifindex); + bnc = bnc_find(tree, &p, 0, ifindex); if (!bnc) { - bnc = bnc_new(tree, &p, srte_color, ifindex); + bnc = bnc_new(tree, &p, 0, ifindex); bnc->afi = afi; bnc->bgp = bgp_nexthop; if (BGP_DEBUG(nht, NHT)) @@ -407,13 +424,42 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, peer); } else { if (BGP_DEBUG(nht, NHT)) - zlog_debug("Found existing bnc %pFX(%d)(%s) flags 0x%x ifindex %d #paths %d peer %p, resolved prefix %pFX", - &bnc->prefix, bnc->ifindex_ipv6_ll, + zlog_debug("Found existing bnc %pFX(%d)(%u)(%s) flags 0x%x ifindex %d #paths %d peer %p, resolved prefix %pFX", + &bnc->prefix, bnc->ifindex_ipv6_ll, bnc->srte_color, bnc->bgp->name_pretty, bnc->flags, bnc->ifindex_ipv6_ll, bnc->path_count, bnc->nht_info, &bnc->resolved_prefix); } + if (srte_color) { + if (make_prefix(afi, pi, &p, true) < 0) + return 1; + + te_bnc = bnc_find(tree, &p, srte_color, ifindex); + if (!te_bnc) { + te_bnc = bnc_new(tree, &p, srte_color, ifindex); + te_bnc->afi = afi; + te_bnc->bgp = bgp_nexthop; + if (BGP_DEBUG(nht, NHT)) + zlog_debug("Allocated te bnc %pFX(%d)(%u)(%s) peer %p", + &bnc->prefix, bnc->ifindex_ipv6_ll, + bnc->srte_color, bnc->bgp->name_pretty, + peer); + } else { + if (BGP_DEBUG(nht, NHT)) + zlog_debug("Found existing te bnc %pFX(%d)(%u)(%s) flags 0x%x ifindex %d #paths %d peer %p, resolved prefix %pFX", + &bnc->prefix, bnc->ifindex_ipv6_ll, bnc->srte_color, + bnc->bgp->name_pretty, bnc->flags, + bnc->ifindex_ipv6_ll, bnc->path_count, + bnc->nht_info, &bnc->resolved_prefix); + } + if (pi && srv6_route) + SET_FLAG(pi->flags, BGP_PATH_SRV6_TE); + } else { + if (pi && srv6_route) + UNSET_FLAG(pi->flags, BGP_PATH_SRV6_TE); + } + if (pi && is_route_parent_evpn(pi)) bnc->is_evpn_gwip_nexthop = true; @@ -470,6 +516,24 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, && !is_default_host_route(&bnc->prefix)) register_zebra_rnh(bnc); + if (te_bnc) { + if (peer && (te_bnc->ifindex_ipv6_ll != ifindex)) { + UNSET_FLAG(te_bnc->flags, BGP_NEXTHOP_REGISTERED); + UNSET_FLAG(te_bnc->flags, BGP_NEXTHOP_VALID); + te_bnc->ifindex_ipv6_ll = ifindex; + } + + if (!CHECK_FLAG(te_bnc->flags, BGP_NEXTHOP_REGISTERED)) + register_zebra_rnh(te_bnc); + + if (pi && pi->te_nexthop != te_bnc) { + bgp_unlink_te_nexthop(pi); + /* updates NHT pi list reference */ + path_tenh_map(pi, te_bnc, true); + + } + } + if (pi && pi->nexthop != bnc) { /* Unlink from existing nexthop cache, if any. This will also * free @@ -644,9 +708,11 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, UNSET_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID); /* check below */ + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_SRV6TE_VALID); for (i = 0; i < nhr->nexthop_num; i++) { int num_labels = 0; + int num_segs = 0; nexthop = nexthop_from_zapi_nexthop(&nhr->nexthops[i]); @@ -678,12 +744,27 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, num_labels = nexthop->nh_label->num_labels; } + if (bnc->srte_color && nexthop->nh_srv6 && nexthop->nh_srv6->seg6_segs) { + SET_FLAG(bnc->flags, BGP_NEXTHOP_SRV6TE_VALID); + num_segs = nexthop->nh_srv6->seg6_segs->num_segs; + } + if (BGP_DEBUG(nht, NHT)) { - char buf[NEXTHOP_STRLEN]; + char buf[NEXTHOP_STRLEN] = {0}; + char labelstr[MPLS_LABEL_STRLEN] = {0}; + char seg_buf[SRV6_SEG_STRLEN] = {0}; + if (num_labels) + mpls_label2str(num_labels, nexthop->nh_label->label, + labelstr, sizeof(labelstr), nexthop->nh_label_type, 1); + + if (num_segs) + snprintf_seg6_seg_stack(seg_buf, SRV6_SEG_STRLEN, + nexthop->nh_srv6->seg6_segs); + zlog_debug( - " nhop via %s (%d labels)", + " nhop via %s labels(%d):%s segs(%d):%s", nexthop2str(nexthop, buf, sizeof(buf)), - num_labels); + num_labels, labelstr, num_segs, seg_buf); } if (nhlist_tail) { @@ -746,6 +827,7 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_SRV6TE_VALID); bnc->nexthop_num = nhr->nexthop_num; /* notify bgp fsm if nbr ip goes from valid->invalid */ @@ -1026,7 +1108,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp) * make_prefix - make a prefix structure from the path (essentially * path's node. */ -static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) +static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p, bool is_te) { int is_bgp_static = ((pi->type == ZEBRA_ROUTE_BGP) @@ -1068,7 +1150,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) break; case AFI_IP6: p->family = AF_INET6; - if (pi->attr->srv6_l3vpn) { + if (pi->attr->srv6_l3vpn && !is_te) { p->prefixlen = IPV6_MAX_BITLEN; if (pi->attr->srv6_l3vpn->transposition_len != 0 && BGP_PATH_INFO_NUM_LABELS(pi)) { @@ -1133,9 +1215,8 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) */ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) { - bool exact_match = false; - bool resolve_via_default = false; int ret; + uint8_t flags = 0; if (!zclient) return; @@ -1157,19 +1238,21 @@ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) } if (command == ZEBRA_NEXTHOP_REGISTER) { if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) - exact_match = true; + SET_FLAG(flags, NEXTHOP_REGISTER_FLAG_CONNECTED); if (CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)) - resolve_via_default = true; + SET_FLAG(flags, NEXTHOP_REGISTER_FLAG_RESOLVE_VIA_DEFAULT); } if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: sending cmd %s for %pFX (vrf %s)", __func__, + zlog_debug("%s: sending cmd %s for %pFX (vrf %s) color %d", __func__, zserv_command_string(command), &bnc->prefix, - bnc->bgp->name_pretty); + bnc->bgp->name_pretty, bnc->srte_color); + + if (bnc->srte_color) + SET_FLAG(flags, NEXTHOP_REGISTER_FLAG_COLOR); ret = zclient_send_rnh(zclient, command, &bnc->prefix, SAFI_UNICAST, - exact_match, resolve_via_default, - bnc->bgp->vrf_id); + flags, bnc->bgp->vrf_id, bnc->srte_color); if (ret == ZCLIENT_SEND_FAILURE) { flog_warn(EC_BGP_ZEBRA_SEND, "sendmsg_nexthop: zclient_send_message() failed"); @@ -1244,6 +1327,144 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc) sendmsg_zebra_rnh(bnc, ZEBRA_NEXTHOP_UNREGISTER); } +static bool check_bnc_is_valid_nexthop(struct bgp_nexthop_cache *bnc, + struct bgp_path_info *path) +{ + struct bgp_dest *dest; + struct bgp_table *table; + afi_t afi; + safi_t safi; + struct bgp *bgp_path; + const struct prefix *p; + bool bnc_is_valid_nexthop = false; + + dest = path->net; + assert(dest && bgp_dest_table(dest)); + p = bgp_dest_get_prefix(dest); + afi = family2afi(p->family); + table = bgp_dest_table(dest); + safi = table->safi; + + bgp_path = table->bgp; + + struct bgp_route_evpn *bre = + bgp_attr_get_evpn_overlay(path->attr); + + if (safi == SAFI_UNICAST && + path->sub_type == BGP_ROUTE_IMPORTED && + BGP_PATH_INFO_NUM_LABELS(path) && + !(bre && bre->type == OVERLAY_INDEX_GATEWAY_IP)) { + bnc_is_valid_nexthop = + bgp_isvalid_nexthop_for_l3vpn(bnc, path) + ? true + : false; + } else if (safi == SAFI_MPLS_VPN && + path->sub_type != BGP_ROUTE_IMPORTED) { + /* avoid not redistributing mpls vpn routes */ + bnc_is_valid_nexthop = true; + } else { + /* mpls-vpn routes with BGP_ROUTE_IMPORTED subtype */ + if (bgp_update_martian_nexthop( + bnc->bgp, afi, safi, path->type, + path->sub_type, path->attr, dest)) { + if (BGP_DEBUG(nht, NHT)) + zlog_debug( + "%s: prefix %pBD (vrf %s), ignoring path due to martian or self-next-hop", + __func__, dest, bgp_path->name); + } else + bnc_is_valid_nexthop = + bgp_isvalid_nexthop(bnc) ? true : false; + } + return bnc_is_valid_nexthop; +} + +static void evaluate_paths_for_vpn_leak(struct bgp_nexthop_cache *bnc, + struct bgp_path_info *path, bool bnc_is_valid, bool is_te) +{ + struct bgp_dest *dest; + struct bgp_table *table; + afi_t afi; + safi_t safi; + struct bgp *bgp_path; + const struct prefix *p; + bool path_valid = false; + + dest = path->net; + assert(dest && bgp_dest_table(dest)); + p = bgp_dest_get_prefix(dest); + afi = family2afi(p->family); + table = bgp_dest_table(dest); + safi = table->safi; + bgp_path = table->bgp; + path_valid = CHECK_FLAG(path->flags, BGP_PATH_VALID); + + if (path->type == ZEBRA_ROUTE_BGP && + path->sub_type == BGP_ROUTE_STATIC && + !CHECK_FLAG(bgp_path->flags, BGP_FLAG_IMPORT_CHECK)) + /* static routes with 'no bgp network import-check' are + * always valid. if nht is called with static routes, + * the vpn exportation needs to be triggered + */ + vpn_leak_from_vrf_update(bgp_get_default(), bgp_path, + path); + else if (path->sub_type == BGP_ROUTE_REDISTRIBUTE && + safi == SAFI_UNICAST && + (bgp_path->inst_type == BGP_INSTANCE_TYPE_VRF || + bgp_path->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) + /* redistribute routes are always valid + * if nht is called with redistribute routes, the vpn + * exportation needs to be triggered + */ + vpn_leak_from_vrf_update(bgp_get_default(), bgp_path, + path); + else if (path_valid != bnc_is_valid) { + if (path_valid) { + /* No longer valid, clear flag; also for EVPN + * routes, unimport from VRFs if needed. + */ + bgp_aggregate_decrement(bgp_path, p, path, afi, + safi); + UNSET_FLAG(path->flags, BGP_PATH_SRV6_TE_VALID); + + if (!is_te) { + bgp_path_info_unset_flag(dest, path, + BGP_PATH_VALID); + if (safi == SAFI_EVPN && + bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest))) + bgp_evpn_unimport_route(bgp_path, + afi, safi, bgp_dest_get_prefix(dest), path); + if (safi == SAFI_UNICAST && + (bgp_path->inst_type != + BGP_INSTANCE_TYPE_VIEW)) + vpn_leak_from_vrf_withdraw( + bgp_get_default(), bgp_path, + path); + } + + } else { + /* Path becomes valid, set flag; also for EVPN + * routes, import from VRFs if needed. + */ + bgp_path_info_set_flag(dest, path, + BGP_PATH_VALID); + if (is_te) + SET_FLAG(path->flags, BGP_PATH_SRV6_TE_VALID); + + bgp_aggregate_increment(bgp_path, p, path, afi, + safi); + if (safi == SAFI_EVPN && + bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest))) + bgp_evpn_import_route(bgp_path, + afi, safi, bgp_dest_get_prefix(dest), path); + if (safi == SAFI_UNICAST && + (bgp_path->inst_type != + BGP_INSTANCE_TYPE_VIEW)) + vpn_leak_from_vrf_update( + bgp_get_default(), bgp_path, + path); + } + } +} /** * evaluate_paths - Evaluate the paths/nets associated with a nexthop. * ARGUMENTS: @@ -1277,185 +1498,193 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) sizeof(bnc_buf))); } - LIST_FOREACH (path, &(bnc->paths), nh_thread) { - if (path->type == ZEBRA_ROUTE_BGP && - (path->sub_type == BGP_ROUTE_NORMAL || - path->sub_type == BGP_ROUTE_STATIC || - path->sub_type == BGP_ROUTE_IMPORTED)) - /* evaluate the path */ - ; - else if (path->sub_type == BGP_ROUTE_REDISTRIBUTE) { - /* evaluate the path for redistributed routes - * except those from VNC - */ - if ((path->type == ZEBRA_ROUTE_VNC) || - (path->type == ZEBRA_ROUTE_VNC_DIRECT)) + if (bnc->srte_color) { + LIST_FOREACH (path, &(bnc->paths), te_nh_thread) { + if (path->type == ZEBRA_ROUTE_BGP && + (path->sub_type == BGP_ROUTE_NORMAL || + path->sub_type == BGP_ROUTE_STATIC || + path->sub_type == BGP_ROUTE_IMPORTED)) + /* evaluate the path */ + ; + else if (path->sub_type == BGP_ROUTE_REDISTRIBUTE) { + /* evaluate the path for redistributed routes + * except those from VNC + */ + if ((path->type == ZEBRA_ROUTE_VNC) || + (path->type == ZEBRA_ROUTE_VNC_DIRECT)) + continue; + } else + /* don't evaluate the path */ continue; - } else - /* don't evaluate the path */ - continue; - dest = path->net; - assert(dest && bgp_dest_table(dest)); - p = bgp_dest_get_prefix(dest); - afi = family2afi(p->family); - table = bgp_dest_table(dest); - safi = table->safi; + dest = path->net; + assert(dest && bgp_dest_table(dest)); + p = bgp_dest_get_prefix(dest); + afi = family2afi(p->family); + table = bgp_dest_table(dest); + safi = table->safi; - /* - * handle routes from other VRFs (they can have a - * nexthop in THIS VRF). bgp_path is the bgp instance - * that owns the route referencing this nexthop. - */ - bgp_path = table->bgp; + /* + * handle routes from other VRFs (they can have a + * nexthop in THIS VRF). bgp_path is the bgp instance + * that owns the route referencing this nexthop. + */ + bgp_path = table->bgp; - /* - * Path becomes valid/invalid depending on whether the nexthop - * reachable/unreachable. - * - * 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. - */ + /* + * Path becomes valid/invalid depending on whether the nexthop + * reachable/unreachable. + * + * 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; - struct bgp_route_evpn *bre = - bgp_attr_get_evpn_overlay(path->attr); + bool bnc_is_valid_nexthop = check_bnc_is_valid_nexthop(bnc, path); + bool path_valid = false; - if (safi == SAFI_UNICAST && - path->sub_type == BGP_ROUTE_IMPORTED && - BGP_PATH_INFO_NUM_LABELS(path) && - !(bre && bre->type == OVERLAY_INDEX_GATEWAY_IP)) { - bnc_is_valid_nexthop = - bgp_isvalid_nexthop_for_l3vpn(bnc, path) - ? true - : false; - } else if (safi == SAFI_MPLS_VPN && - path->sub_type != BGP_ROUTE_IMPORTED) { - /* avoid not redistributing mpls vpn routes */ - bnc_is_valid_nexthop = true; - } else { - /* mpls-vpn routes with BGP_ROUTE_IMPORTED subtype */ - if (bgp_update_martian_nexthop( - bnc->bgp, afi, safi, path->type, - path->sub_type, path->attr, dest)) { - if (BGP_DEBUG(nht, NHT)) + + if (BGP_DEBUG(nht, NHT)) { + + if (dest->pdest) { + char rd_buf[RD_ADDRSTRLEN]; + + prefix_rd2str( + (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest), + rd_buf, sizeof(rd_buf), + bgp_get_asnotation(bnc->bgp)); zlog_debug( - "%s: prefix %pBD (vrf %s), ignoring path due to martian or self-next-hop", - __func__, dest, bgp_path->name); - } else - bnc_is_valid_nexthop = - bgp_isvalid_nexthop(bnc) ? true : false; - } + "... eval te path %d/%d %pBD RD %s %s flags 0x%x", + afi, safi, dest, rd_buf, + bgp_path->name_pretty, path->flags); + } else + zlog_debug( + "... eval te path %d/%d %pBD %s flags 0x%x", + afi, safi, dest, bgp_path->name_pretty, + path->flags); + } - if (BGP_DEBUG(nht, NHT)) { + /* Skip paths marked for removal or as history. */ + if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED) + || CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) + continue; - if (dest->pdest) { - char rd_buf[RD_ADDRSTRLEN]; - prefix_rd2str( - (struct prefix_rd *)bgp_dest_get_prefix( - dest->pdest), - rd_buf, sizeof(rd_buf), - bgp_get_asnotation(bnc->bgp)); - zlog_debug( - "... eval path %d/%d %pBD RD %s %s flags 0x%x", - afi, safi, dest, rd_buf, - bgp_path->name_pretty, path->flags); - } else - zlog_debug( - "... eval path %d/%d %pBD %s flags 0x%x", - afi, safi, dest, bgp_path->name_pretty, - path->flags); - } + if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) || + CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) || + bgp_attr_get_color(path->attr)) + SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); - /* Skip paths marked for removal or as history. */ - if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED) - || CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) - continue; + path_valid = CHECK_FLAG(path->flags, BGP_PATH_VALID); + if (bnc_is_valid_nexthop) + SET_FLAG(path->flags, BGP_PATH_SRV6_TE_VALID); - /* Copy the metric to the path. Will be used for bestpath - * computation */ - bpi_ultimate = bgp_get_imported_bpi_ultimate(path); - if (bgp_isvalid_nexthop(bnc) && bnc->metric) - (bgp_path_info_extra_get(bpi_ultimate))->igpmetric = - bnc->metric; - else if (bpi_ultimate->extra) - bpi_ultimate->extra->igpmetric = 0; + evaluate_paths_for_vpn_leak(bnc, path, bnc_is_valid_nexthop, true); + + if (path_valid != bnc_is_valid_nexthop) + hook_call(bgp_nht_path_update, bgp_path, path, bnc_is_valid_nexthop); + bgp_process(bgp_path, dest, path, afi, safi); + } + } else { + LIST_FOREACH (path, &(bnc->paths), nh_thread) { + if (path->type == ZEBRA_ROUTE_BGP && + (path->sub_type == BGP_ROUTE_NORMAL || + path->sub_type == BGP_ROUTE_STATIC || + path->sub_type == BGP_ROUTE_IMPORTED)) + /* evaluate the path */ + ; + else if (path->sub_type == BGP_ROUTE_REDISTRIBUTE) { + /* evaluate the path for redistributed routes + * except those from VNC + */ + if ((path->type == ZEBRA_ROUTE_VNC) || + (path->type == ZEBRA_ROUTE_VNC_DIRECT)) + continue; + } else + /* don't evaluate the path */ + continue; - if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) || - CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) || - bgp_attr_get_color(path->attr)) - SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); - - path_valid = CHECK_FLAG(path->flags, BGP_PATH_VALID); - if (path->type == ZEBRA_ROUTE_BGP && - path->sub_type == BGP_ROUTE_STATIC && - !CHECK_FLAG(bgp_path->flags, BGP_FLAG_IMPORT_CHECK)) - /* static routes with 'no bgp network import-check' are - * always valid. if nht is called with static routes, - * the vpn exportation needs to be triggered + dest = path->net; + assert(dest && bgp_dest_table(dest)); + p = bgp_dest_get_prefix(dest); + afi = family2afi(p->family); + table = bgp_dest_table(dest); + safi = table->safi; + + /* + * handle routes from other VRFs (they can have a + * nexthop in THIS VRF). bgp_path is the bgp instance + * that owns the route referencing this nexthop. */ - vpn_leak_from_vrf_update(bgp_get_default(), bgp_path, - path); - else if (path->sub_type == BGP_ROUTE_REDISTRIBUTE && - safi == SAFI_UNICAST && - (bgp_path->inst_type == BGP_INSTANCE_TYPE_VRF || - bgp_path->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) - /* redistribute routes are always valid - * if nht is called with redistribute routes, the vpn - * exportation needs to be triggered + bgp_path = table->bgp; + + /* + * Path becomes valid/invalid depending on whether the nexthop + * reachable/unreachable. + * + * 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. */ - vpn_leak_from_vrf_update(bgp_get_default(), bgp_path, - path); - else if (path_valid != bnc_is_valid_nexthop) { - if (path_valid) { - /* No longer valid, clear flag; also for EVPN - * routes, unimport from VRFs if needed. - */ - bgp_aggregate_decrement(bgp_path, p, path, afi, - safi); - bgp_path_info_unset_flag(dest, path, - BGP_PATH_VALID); - if (safi == SAFI_EVPN && - bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest))) - bgp_evpn_unimport_route(bgp_path, - afi, safi, bgp_dest_get_prefix(dest), path); - if (safi == SAFI_UNICAST && - (bgp_path->inst_type != - BGP_INSTANCE_TYPE_VIEW)) - vpn_leak_from_vrf_withdraw( - bgp_get_default(), bgp_path, - path); - } else { - /* Path becomes valid, set flag; also for EVPN - * routes, import from VRFs if needed. - */ - bgp_path_info_set_flag(dest, path, - BGP_PATH_VALID); - bgp_aggregate_increment(bgp_path, p, path, afi, - safi); - if (safi == SAFI_EVPN && - bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest))) - bgp_evpn_import_route(bgp_path, - afi, safi, bgp_dest_get_prefix(dest), path); - if (safi == SAFI_UNICAST && - (bgp_path->inst_type != - BGP_INSTANCE_TYPE_VIEW)) - vpn_leak_from_vrf_update( - bgp_get_default(), bgp_path, - path); + + bool bnc_is_valid_nexthop = check_bnc_is_valid_nexthop(bnc, path); + bool path_valid = false; + + if (BGP_DEBUG(nht, NHT)) { + + if (dest->pdest) { + char rd_buf[RD_ADDRSTRLEN]; + + prefix_rd2str( + (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest), + rd_buf, sizeof(rd_buf), + bgp_get_asnotation(bnc->bgp)); + zlog_debug( + "... eval path %d/%d %pBD RD %s %s flags 0x%x", + afi, safi, dest, rd_buf, + bgp_path->name_pretty, path->flags); + } else + zlog_debug( + "... eval path %d/%d %pBD %s flags 0x%x", + afi, safi, dest, bgp_path->name_pretty, + path->flags); } - } - if (path_valid != bnc_is_valid_nexthop) - hook_call(bgp_nht_path_update, bgp_path, path, bnc_is_valid_nexthop); + /* Skip paths marked for removal or as history. */ + if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED) + || CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) + continue; - bgp_process(bgp_path, dest, path, afi, safi); + /* Copy the metric to the path. Will be used for bestpath + * computation + */ + bpi_ultimate = bgp_get_imported_bpi_ultimate(path); + if (bgp_isvalid_nexthop(bnc) && bnc->metric) + (bgp_path_info_extra_get(bpi_ultimate))->igpmetric = + bnc->metric; + else if (bpi_ultimate->extra) + bpi_ultimate->extra->igpmetric = 0; + + if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) || + CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) || + bgp_attr_get_color(path->attr)) + SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); + + path_valid = CHECK_FLAG(path->flags, BGP_PATH_VALID); + evaluate_paths_for_vpn_leak(bnc, path, bnc_is_valid_nexthop, false); + + if (path_valid != bnc_is_valid_nexthop) + hook_call(bgp_nht_path_update, bgp_path, path, bnc_is_valid_nexthop); + bgp_process(bgp_path, dest, path, afi, safi); + } } if (peer) { @@ -1513,6 +1742,21 @@ void path_nh_map(struct bgp_path_info *path, struct bgp_nexthop_cache *bnc, } } +void path_tenh_map(struct bgp_path_info *path, struct bgp_nexthop_cache *te_bnc, + bool make) +{ + if (path->te_nexthop) { + LIST_REMOVE(path, te_nh_thread); + path->te_nexthop->path_count--; + path->te_nexthop = NULL; + } + if (make) { + LIST_INSERT_HEAD(&(te_bnc->paths), path, te_nh_thread); + path->te_nexthop = te_bnc; + path->te_nexthop->path_count++; + } +} + /* * This function is called to register nexthops to zebra * as that we may have tried to install the nexthops diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index 345089ac5acf..f4fdfc7fe130 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -37,6 +37,8 @@ extern int bgp_find_or_add_nexthop(struct bgp *bgp_route, * p - path structure. */ extern void bgp_unlink_nexthop(struct bgp_path_info *p); +extern void bgp_unlink_te_nexthop(struct bgp_path_info *path); + void bgp_unlink_nexthop_by_peer(struct peer *peer); void bgp_replace_nexthop_by_peer(struct peer *from, struct peer *to); /** @@ -61,6 +63,8 @@ extern void bgp_cleanup_nexthops(struct bgp *bgp); */ extern void path_nh_map(struct bgp_path_info *path, struct bgp_nexthop_cache *bnc, bool make); +extern void path_tenh_map(struct bgp_path_info *path, + struct bgp_nexthop_cache *te_bnc, bool make); /* * When we actually have the connection to * the zebra daemon, we need to reregister diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index beb5f40b8b91..5f992f7f11af 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -127,7 +127,7 @@ static inline char *bgp_route_dump_path_info_flags(struct bgp_path_info *pi, return buf; } - snprintfrr(buf, len, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + snprintfrr(buf, len, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", CHECK_FLAG(flags, BGP_PATH_IGP_CHANGED) ? "IGP Changed " : "", CHECK_FLAG(flags, BGP_PATH_DAMPED) ? "Damped" : "", CHECK_FLAG(flags, BGP_PATH_HISTORY) ? "History " : "", @@ -152,7 +152,8 @@ static inline char *bgp_route_dump_path_info_flags(struct bgp_path_info *pi, CHECK_FLAG(flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND) ? "MPLS Label Bind " : "", - CHECK_FLAG(flags, BGP_PATH_UNSORTED) ? "Unsorted " : ""); + CHECK_FLAG(flags, BGP_PATH_UNSORTED) ? "Unsorted " : "", + CHECK_FLAG(flags, BGP_PATH_SRV6_TE_VALID) ? "SRv6 TE " : ""); return buf; } @@ -357,6 +358,7 @@ void bgp_path_info_free_with_caller(const char *name, bgp_attr_unintern(&path->attr); bgp_unlink_nexthop(path); + bgp_unlink_te_nexthop(path); bgp_path_info_extra_free(&path->extra); bgp_path_info_mpath_free(&path->mpath); if (path->net) @@ -558,6 +560,7 @@ void bgp_path_info_delete(struct bgp_dest *dest, struct bgp_path_info *pi) bgp_path_info_set_flag(dest, pi, BGP_PATH_REMOVED); /* set of previous already took care of pcount */ UNSET_FLAG(pi->flags, BGP_PATH_VALID); + UNSET_FLAG(pi->flags, BGP_PATH_SRV6_TE_VALID); } /* undo the effects of a previous call to bgp_path_info_delete; typically @@ -4875,6 +4878,9 @@ bgp_update_nexthop_reachability_check(struct bgp *bgp, struct peer *peer, struct bgp_path_info_set_flag(dest, pi, BGP_PATH_ACCEPT_OWN); bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); + if (CHECK_FLAG(pi->flags, BGP_PATH_SRV6_TE)) + SET_FLAG(pi->flags, BGP_PATH_SRV6_TE_VALID); + } else { if (BGP_DEBUG(nht, NHT)) { zlog_debug("%s(%pI4): NH unresolved for existing %pFX pi %p flags 0x%x", @@ -4882,6 +4888,7 @@ bgp_update_nexthop_reachability_check(struct bgp *bgp, struct peer *peer, struct pi->flags); } bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID); + UNSET_FLAG(pi->flags, BGP_PATH_SRV6_TE_VALID); } } else { /* case mpls-vpn routes with accept-own community @@ -4891,6 +4898,8 @@ bgp_update_nexthop_reachability_check(struct bgp *bgp, struct peer *peer, struct if (accept_own) bgp_path_info_set_flag(dest, pi, BGP_PATH_ACCEPT_OWN); bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); + if (CHECK_FLAG(pi->flags, BGP_PATH_SRV6_TE)) + SET_FLAG(pi->flags, BGP_PATH_SRV6_TE_VALID); } } @@ -5655,6 +5664,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, filtered: if (new) { bgp_unlink_nexthop(new); + bgp_unlink_te_nexthop(new); bgp_path_info_delete(dest, new); bgp_path_info_extra_free(&new->extra); XFREE(MTYPE_BGP_ROUTE, new); @@ -6909,10 +6919,11 @@ static void bgp_nexthop_reachability_check(afi_t afi, safi_t safi, if (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST) { if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)) { if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, safi, - bpi, NULL, 0, p)) - bgp_path_info_set_flag(dest, bpi, - BGP_PATH_VALID); - else { + bpi, NULL, 0, p)) { + bgp_path_info_set_flag(dest, bpi, BGP_PATH_VALID); + if (CHECK_FLAG(bpi->flags, BGP_PATH_SRV6_TE)) + SET_FLAG(bpi->flags, BGP_PATH_SRV6_TE_VALID); + } else { if (BGP_DEBUG(nht, NHT)) { char buf1[INET6_ADDRSTRLEN]; @@ -6923,6 +6934,7 @@ static void bgp_nexthop_reachability_check(afi_t afi, safi_t safi, } bgp_path_info_unset_flag(dest, bpi, BGP_PATH_VALID); + UNSET_FLAG(bpi->flags, BGP_PATH_SRV6_TE_VALID); } } else { /* Delete the NHT structure if any, if we're toggling between @@ -6930,8 +6942,10 @@ static void bgp_nexthop_reachability_check(afi_t afi, safi_t safi, * from NHT to avoid overloading NHT and the process interaction */ bgp_unlink_nexthop(bpi); - + bgp_unlink_te_nexthop(bpi); bgp_path_info_set_flag(dest, bpi, BGP_PATH_VALID); + if (CHECK_FLAG(bpi->flags, BGP_PATH_SRV6_TE)) + SET_FLAG(bpi->flags, BGP_PATH_SRV6_TE_VALID); } } } @@ -7254,6 +7268,7 @@ void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi, } bgp_aggregate_decrement(bgp, p, pi, afi, safi); bgp_unlink_nexthop(pi); + bgp_unlink_te_nexthop(pi); bgp_path_info_delete(dest, pi); bgp_process(bgp, dest, pi, afi, safi); } @@ -7660,6 +7675,7 @@ static void bgp_purge_af_static_redist_routes(struct bgp *bgp, afi_t afi, bgp, bgp_dest_get_prefix(dest), pi, afi, safi); bgp_unlink_nexthop(pi); + bgp_unlink_te_nexthop(pi); bgp_path_info_delete(dest, pi); bgp_process(bgp, dest, pi, afi, safi); } @@ -11431,6 +11447,67 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, if (!json_paths) vty_out(vty, "\n"); + if (path->nexthop) { + struct nexthop *nexthop; + struct bgp_nexthop_cache *bnc = path->nexthop; + + if (!json_paths) { + vty_out(vty, " Relay-Nexthop(ip):"); + for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) { + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV6: + vty_out(vty, " gate %s, ", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + sizeof(buf))); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, " gate %s, if %s, ", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + sizeof(buf)), + ifindex2ifname(bnc->ifindex_ipv6_ll ? bnc->ifindex_ipv6_ll + : nexthop->ifindex, + bgp->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + vty_out(vty, " gate %s, ", + inet_ntop(AF_INET, &nexthop->gate.ipv4, buf, + sizeof(buf))); + break; + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, " if %s, ", + ifindex2ifname(bnc->ifindex_ipv6_ll ? bnc->ifindex_ipv6_ll + : nexthop->ifindex, + bgp->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, " gate %s, if %s, ", + inet_ntop(AF_INET, &nexthop->gate.ipv4, buf, + sizeof(buf)), + ifindex2ifname(bnc->ifindex_ipv6_ll ? bnc->ifindex_ipv6_ll + : nexthop->ifindex, + bgp->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, " blackhole, "); + break; + default: + vty_out(vty, " invalid nexthop type %u\n", + nexthop->type); + } + } + + vty_out(vty, "\n"); + if (path->te_nexthop) { + bnc = path->te_nexthop; + vty_out(vty, " Relay-Nexthop(tunnel):"); + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_SRV6TE_VALID)) + vty_out(vty, " srv6-tunnel:%s|%u(endpoint|color), ", + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, buf, sizeof(buf)), + bnc->srte_color); + vty_out(vty, "\n"); + } + } + } /* Line 4 display Community */ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) { if (json_paths) { diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 43033c8c3949..7c441dd59c54 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -275,12 +275,18 @@ struct bgp_path_info { /* For nexthop linked list */ LIST_ENTRY(bgp_path_info) nh_thread; + /* For te policy nexthop linked list */ + LIST_ENTRY(bgp_path_info) te_nh_thread; + /* Back pointer to the prefix node */ struct bgp_dest *net; /* Back pointer to the nexthop structure */ struct bgp_nexthop_cache *nexthop; + /* Back pointer to the te policy nexthop structure */ + struct bgp_nexthop_cache *te_nexthop; + /* Peer structure. */ struct peer *peer; @@ -339,6 +345,8 @@ struct bgp_path_info { * the actual ecmp path. */ #define BGP_PATH_MULTIPATH_NEW (1 << 20) +#define BGP_PATH_SRV6_TE (1 << 21) +#define BGP_PATH_SRV6_TE_VALID (1 << 22) /* BGP route type. This can be static, RIP, OSPF, BGP etc. */ uint8_t type; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 7ad9ce472603..44ebe59e3f96 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1237,6 +1237,42 @@ static bool bgp_zebra_use_nhop_weighted(struct bgp *bgp, struct attr *attr, return true; } +static int bgp_zebra_fill_seg6_segs(struct bgp_path_info *mpinfo, + struct zapi_route *api, + mpls_label_t *labels, + struct zapi_nexthop *api_nh) +{ + mpls_label_t nh_label; + uint32_t ttl = 0; + uint32_t bos = 0; + uint32_t exp = 0; + + struct in6_addr *sid_tmp = + mpinfo->attr->srv6_l3vpn + ? (&mpinfo->attr->srv6_l3vpn->sid) + : (&mpinfo->attr->srv6_vpn->sid); + + memcpy(&api_nh->seg6_segs[0], sid_tmp, + sizeof(api_nh->seg6_segs[0])); + + if (mpinfo->attr->srv6_l3vpn && + mpinfo->attr->srv6_l3vpn->transposition_len != 0) { + mpls_lse_decode(labels[0], &nh_label, &ttl, + &exp, &bos); + + if (nh_label < MPLS_LABEL_UNRESERVED_MIN) { + if (bgp_debug_zebra(&api->prefix)) + zlog_debug( + "skip invalid SRv6 routes: transposition scheme is used, but label is too small"); + return 1; + } + transpose_sid(&api_nh->seg6_segs[0], nh_label, + mpinfo->attr->srv6_l3vpn->transposition_offset, + mpinfo->attr->srv6_l3vpn->transposition_len); + } + return 0; +} + static void bgp_zebra_announce_parse_nexthop( struct bgp_path_info *info, const struct prefix *p, struct bgp *bgp, struct zapi_route *api, unsigned int *valid_nh_count, afi_t afi, @@ -1260,6 +1296,8 @@ static void bgp_zebra_announce_parse_nexthop( uint32_t bos = 0; uint32_t exp = 0; struct bgp_route_evpn *bre = NULL; + struct nexthop_srv6 *nh_srv6 = NULL; + uint8_t num_segs = 0; /* Determine if we're doing weighted ECMP or not */ do_wt_ecmp = bgp_path_info_mpath_chkwtd(bgp, info); @@ -1415,41 +1453,31 @@ static void bgp_zebra_announce_parse_nexthop( sizeof(struct ethaddr)); api_nh->weight = nh_weight; + if (CHECK_FLAG(mpinfo->flags, BGP_PATH_SRV6_TE_VALID) && mpinfo->te_nexthop) { + nh_srv6 = mpinfo->te_nexthop->nexthop->nh_srv6; + SET_FLAG(api->message, ZAPI_MESSAGE_SRTE); + } - if (((mpinfo->attr->srv6_l3vpn && - !sid_zero_ipv6(&mpinfo->attr->srv6_l3vpn->sid)) || - (mpinfo->attr->srv6_vpn && - !sid_zero_ipv6(&mpinfo->attr->srv6_vpn->sid))) && - !is_evpn && bgp_is_valid_label(&labels[0])) { - struct in6_addr *sid_tmp = - mpinfo->attr->srv6_l3vpn - ? (&mpinfo->attr->srv6_l3vpn->sid) - : (&mpinfo->attr->srv6_vpn->sid); - - memcpy(&api_nh->seg6_segs[0], sid_tmp, - sizeof(api_nh->seg6_segs[0])); - - if (mpinfo->attr->srv6_l3vpn && - mpinfo->attr->srv6_l3vpn->transposition_len != 0) { - mpls_lse_decode(labels[0], &nh_label, &ttl, - &exp, &bos); + if (nh_srv6 && nh_srv6->seg6_segs && nh_srv6->seg6_segs->num_segs) { + for (num_segs = 0; num_segs < nh_srv6->seg6_segs->num_segs; num_segs++) { + memcpy(&api_nh->seg6_segs[num_segs], &nh_srv6->seg6_segs->seg[num_segs], + sizeof(struct in6_addr)); + } + api_nh->seg_num = nh_srv6->seg6_segs->num_segs; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6); + } else { + if (((mpinfo->attr->srv6_l3vpn && + !sid_zero_ipv6(&mpinfo->attr->srv6_l3vpn->sid)) || + (mpinfo->attr->srv6_vpn && + !sid_zero_ipv6(&mpinfo->attr->srv6_vpn->sid))) && + !is_evpn && bgp_is_valid_label(&labels[0])) { - if (nh_label < MPLS_LABEL_UNRESERVED_MIN) { - if (bgp_debug_zebra(&api->prefix)) - zlog_debug( - "skip invalid SRv6 routes: transposition scheme is used, but label is too small"); + if (bgp_zebra_fill_seg6_segs(mpinfo, api, labels, api_nh)) continue; - } - transpose_sid(&api_nh->seg6_segs[0], nh_label, - mpinfo->attr->srv6_l3vpn - ->transposition_offset, - mpinfo->attr->srv6_l3vpn - ->transposition_len); + api_nh->seg_num = 1; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6); } - - api_nh->seg_num = 1; - SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6); } (*valid_nh_count)++; @@ -1464,8 +1492,8 @@ static void bgp_debug_zebra_nh(struct zapi_route *api) char eth_buf[ETHER_ADDR_STRLEN + 7] = { '\0' }; char buf1[ETHER_ADDR_STRLEN]; char label_buf[20]; - char sid_buf[20]; - char segs_buf[256]; + char sid_buf[INET6_ADDRSTRLEN]; + char segs_buf[512]; struct zapi_nexthop *api_nh; int count; @@ -1504,20 +1532,31 @@ static void bgp_debug_zebra_nh(struct zapi_route *api) !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) snprintf(label_buf, sizeof(label_buf), "label %u", api_nh->labels[0]); + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6) && - !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) { - inet_ntop(AF_INET6, &api_nh->seg6_segs[0], sid_buf, - sizeof(sid_buf)); - snprintf(segs_buf, sizeof(segs_buf), "segs %s", sid_buf); + !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) { + + strlcat(segs_buf, "segs ", sizeof(segs_buf)); + for (int j = 0; j < api_nh->seg_num; j++) { + memset(sid_buf, 0, sizeof(sid_buf)); + inet_ntop(AF_INET6, &api_nh->seg6_segs[j], + sid_buf, sizeof(sid_buf)); + + if (j > 0 && j < api_nh->seg_num - 1) + strlcat(segs_buf, "/", sizeof(segs_buf)); + + strlcat(segs_buf, sid_buf, sizeof(segs_buf)); + } } if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN) && !is_zero_mac(&api_nh->rmac)) snprintf(eth_buf, sizeof(eth_buf), " RMAC %s", prefix_mac2str(&api_nh->rmac, buf1, sizeof(buf1))); - zlog_debug(" nhop [%d]: %s if %u VRF %u wt %" PRIu64 + + zlog_debug(" nhop [%d][%u]: %s if %u VRF %u wt %" PRIu64 " %s %s %s", - i + 1, nh_buf, api_nh->ifindex, api_nh->vrf_id, + i + 1, api_nh->srte_color, nh_buf, api_nh->ifindex, api_nh->vrf_id, api_nh->weight, label_buf, segs_buf, eth_buf); } } @@ -1643,9 +1682,9 @@ bgp_zebra_announce_actual(struct bgp_dest *dest, struct bgp_path_info *info, } if (bgp_debug_zebra(p)) { - zlog_debug("Tx route add %s (table id %u) %pFX metric %u tag %" ROUTE_TAG_PRI + zlog_debug("Tx route add %s (table id %u) %pFX color %u metric %u tag %" ROUTE_TAG_PRI " count %d nhg %d", - bgp->name_pretty, api.tableid, &api.prefix, + bgp->name_pretty, api.tableid, &api.prefix, api.srte_color, api.metric, api.tag, api.nexthop_num, nhg_id); bgp_debug_zebra_nh(&api); diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst index 2519ac491228..739bcecb4446 100644 --- a/doc/user/pathd.rst +++ b/doc/user/pathd.rst @@ -28,7 +28,7 @@ Explicit Segment Lists This is the simplest way of configuration, no remote PCE is necessary. In order to create a config that match the graphics used in this documentation, we will create a segment list (SL) called SL1 with an element for each hop and -that element will be assigned a MPLS label. +that element will be assigned a MPLS label/IPv6 address. Then the SL1 will be used in the policy ``example1``, please note also the preference as in the case of multiple segment list it will be used with the criteria of bigger number more preference. @@ -49,6 +49,18 @@ Let see now the final configuration that match the graphics shown above. candidate-path preference 100 name CP1 explicit segment-list SL1 +.. code-block:: frr + + segment-routing + traffic-eng + segment-list SL1 + index 10 ipv6-address 10:10::10 + index 10 ipv6-address 20:20::20 + ! + policy color 1 endpoint 100:100:1::100 + name example1 + candidate-path preference 100 name CP1 explicit segment-list SL1 + Explicit Segment Lists and Traffic Engineering Database (TED) ------------------------------------------------------------- @@ -206,7 +218,7 @@ An example of command line with pcep module could be this Pathd Configuration =================== -Example: +Example of MPLS: .. code-block:: frr @@ -258,6 +270,28 @@ Example: ! ! +Example of srv6: + +.. code-block:: frr + + segment-routing + traffic-eng + segment-list SL1 + index 10 ipv6-address 10:10::10 + index 10 ipv6-address 20:2::20 + ! + segment-list SL2 + index 10 ipv6-address 30:30::30 + index 10 ipv6-address 30:3::30 + ! + policy color 1 endpoint 100:100:1::100 + name default + candidate-path preference 100 name CP1 explicit segment-list SL1 + candidate-path preference 200 name CP2 explicit segment-list SL2 + ! + ! + ! + .. _path-commands: @@ -285,6 +319,7 @@ Configuration Commands Delete or start a segment list definition. .. clicmd:: index INDEX mpls label LABEL +.. clicmd:: index INDEX ipv6-address X:X::X:X .. clicmd:: index INDEX nai adjacency A.B.C.D A.B.C.D .. clicmd:: index INDEX nai prefix A.B.C.D/M algorithm <0|1> .. clicmd:: index INDEX nai prefix A.B.C.D/M iface (0-65535) @@ -501,18 +536,21 @@ Introspection Commands router# show sr-te policy - Endpoint Color Name BSID Status - ------------------------------------------ - 192.0.2.1 1 default 4000 Active + Endpoint Color Name BSID Status Type + -------------------------------------------------- + 192.0.2.1 1 default 4000 Active MPLS + 1::1 100 a - Active SRV6 .. code-block:: frr router# show sr-te policy detail - Endpoint: 192.0.2.1 Color: 1 Name: LOW_DELAY BSID: 4000 Status: Active + Endpoint: 192.0.2.1 Color: 1 Name: LOW_DELAY BSID: 4000 Status: Active Type: MPLS Preference: 100 Name: cand1 Type: explicit Segment-List: sl1 Protocol-Origin: Local * Preference: 200 Name: cand1 Type: dynamic Segment-List: 32453452 Protocol-Origin: PCEP + Endpoint: 1::1 Color: 100 Name: BSID: - Status: Active Type: SRV6 + * Preference: 1 Name: a Type: explicit Segment-List: a Protocol-Origin: Local The asterisk (*) marks the best, e.g. active, candidate path. Note that for segment-lists which are retrieved via PCEP a random number based name is generated. diff --git a/lib/log.c b/lib/log.c index bc1ed5c5ccae..6050464feea3 100644 --- a/lib/log.c +++ b/lib/log.c @@ -464,7 +464,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_TC_FILTER_ADD), DESC_ENTRY(ZEBRA_TC_FILTER_DELETE), DESC_ENTRY(ZEBRA_OPAQUE_NOTIFY), - DESC_ENTRY(ZEBRA_SRV6_SID_NOTIFY) + DESC_ENTRY(ZEBRA_SRV6_SID_NOTIFY), + DESC_ENTRY(ZEBRA_SRV6_POLICY_SET), + DESC_ENTRY(ZEBRA_SRV6_POLICY_DELETE) }; #undef DESC_ENTRY diff --git a/lib/nexthop.c b/lib/nexthop.c index 332581fbd8f4..d9b4daf0da91 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -670,6 +670,37 @@ void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *segs, sizeof(struct in6_addr)); } +void nexthop_add_srv6_seg6_ipaddr(struct nexthop *nexthop, const struct ipaddr *segs, + int num_segs) +{ + int i; + + if (!segs) + return; + + if (!nexthop->nh_srv6) + nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6, + sizeof(struct nexthop_srv6)); + + /* Enforce limit on srv6 seg stack size */ + if (num_segs > SRV6_MAX_SIDS) + num_segs = SRV6_MAX_SIDS; + + if (!nexthop->nh_srv6->seg6_segs) { + nexthop->nh_srv6->seg6_segs = + XCALLOC(MTYPE_NH_SRV6, + sizeof(struct seg6_seg_stack) + + num_segs * sizeof(struct in6_addr)); + } + + nexthop->nh_srv6->seg6_segs->num_segs = num_segs; + + for (i = 0; i < num_segs; i++) + memcpy(&nexthop->nh_srv6->seg6_segs->seg[i], + &segs[i].ipaddr_v6, + sizeof(struct in6_addr)); +} + void nexthop_del_srv6_seg6(struct nexthop *nexthop) { if (!nexthop->nh_srv6) diff --git a/lib/nexthop.h b/lib/nexthop.h index 5dfb58d84661..d030eb8e07f9 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -77,10 +77,12 @@ struct nexthop { * active one */ #define NEXTHOP_FLAG_RNH_FILTERED (1 << 5) /* rmap filtered, used by rnh */ -#define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */ -#define NEXTHOP_FLAG_SRTE (1 << 7) /* SR-TE color used for BGP traffic */ -#define NEXTHOP_FLAG_EVPN (1 << 8) /* nexthop is EVPN */ -#define NEXTHOP_FLAG_LINKDOWN (1 << 9) /* is not removed on link down */ +#define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */ +#define NEXTHOP_FLAG_SRTE (1 << 7) /* SR-TE color used for BGP traffic */ +#define NEXTHOP_FLAG_EVPN (1 << 8) /* nexthop is EVPN */ +#define NEXTHOP_FLAG_LINKDOWN (1 << 9) /* is not removed on link down */ +#define NEXTHOP_FLAG_SRV6TE (1 << 10) /* SRv6-TE color used for BGP traffic */ +#define NEXTHOP_FLAG_SRV6_TUNNEL (1 << 11) /* SRv6-TUNNEL used for BGP traffic */ #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ @@ -161,6 +163,8 @@ void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action, void nexthop_del_srv6_seg6local(struct nexthop *nexthop); void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *seg, int num_segs); +void nexthop_add_srv6_seg6_ipaddr(struct nexthop *nexthop, const struct ipaddr *segs, + int num_segs); void nexthop_del_srv6_seg6(struct nexthop *nexthop); /* diff --git a/lib/prefix.c b/lib/prefix.c index 2485c3e61bdd..eb16d3662d5a 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1124,6 +1124,24 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) return str; } +int prefix2ipaddr(union prefixconstptr pu, struct ipaddr *ip) +{ + const struct prefix *p = pu.p; + + memset(ip, 0, sizeof(struct ipaddr)); + + if (p->family == AF_INET) { + ip->ipa_type = IPADDR_V4; + IPV4_ADDR_COPY(&ip->ipaddr_v4, &p->u.prefix4); + } else if (p->family == AF_INET6) { + ip->ipa_type = IPADDR_V6; + IPV6_ADDR_COPY(&ip->ipaddr_v6, &p->u.prefix6); + } else + return -1; + + return 0; +} + void prefix_mcast_ip_dump(const char *onfail, const struct ipaddr *addr, char *buf, int buf_size) { diff --git a/lib/prefix.h b/lib/prefix.h index 2d679d06224f..7baa540899ff 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -422,6 +422,7 @@ extern void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, extern const char *prefix_sg2str(const struct prefix_sg *sg, char *str); extern const char *prefix2str(union prefixconstptr upfx, char *buffer, int size); +extern int prefix2ipaddr(union prefixconstptr pu, struct ipaddr *ip); extern int evpn_type5_prefix_match(const struct prefix *evpn_pfx, const struct prefix *match_pfx); extern int prefix_match(union prefixconstptr unet, union prefixconstptr upfx); diff --git a/lib/srv6.c b/lib/srv6.c index e6fc375fbb11..37d8f020066e 100644 --- a/lib/srv6.c +++ b/lib/srv6.c @@ -71,6 +71,21 @@ int snprintf_seg6_segs(char *str, return strlen(str); } +int snprintf_seg6_seg_stack(char *str, + size_t size, const struct seg6_seg_stack *segs) +{ + str[0] = '\0'; + for (size_t i = 0; i < segs->num_segs; i++) { + char addr[INET6_ADDRSTRLEN]; + bool not_last = (i + 1) < segs->num_segs; + + inet_ntop(AF_INET6, &segs->seg[i], addr, sizeof(addr)); + strlcat(str, addr, size); + strlcat(str, not_last ? "," : "", size); + } + return strlen(str); +} + void seg6local_context2json(const struct seg6local_context *ctx, uint32_t action, json_object *json) { diff --git a/lib/srv6.h b/lib/srv6.h index 9a041e3d85b2..ee69f41eed1b 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -375,7 +375,8 @@ static inline const char *srv6_sid_ctx2str(char *str, size_t size, int snprintf_seg6_segs(char *str, size_t size, const struct seg6_segs *segs); - +int snprintf_seg6_seg_stack(char *str, + size_t size, const struct seg6_seg_stack *segs); extern struct srv6_locator *srv6_locator_alloc(const char *name); extern struct srv6_locator_chunk *srv6_locator_chunk_alloc(void); extern void srv6_locator_free(struct srv6_locator *locator); diff --git a/lib/zclient.c b/lib/zclient.c index 063944fd3b23..d520639f5dc0 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -772,17 +772,15 @@ static void zclient_connect(struct event *t) } enum zclient_send_status zclient_send_rnh(struct zclient *zclient, int command, - const struct prefix *p, safi_t safi, - bool connected, bool resolve_via_def, - vrf_id_t vrf_id) + const struct prefix *p, safi_t safi, uint8_t flags, + vrf_id_t vrf_id, uint32_t srte_color) { struct stream *s; s = zclient->obuf; stream_reset(s); zclient_create_header(s, command, vrf_id); - stream_putc(s, (connected) ? 1 : 0); - stream_putc(s, (resolve_via_def) ? 1 : 0); + stream_putl(s, flags); stream_putw(s, safi); stream_putw(s, PREFIX_FAMILY(p)); stream_putc(s, p->prefixlen); @@ -796,6 +794,10 @@ enum zclient_send_status zclient_send_rnh(struct zclient *zclient, int command, default: break; } + + if (CHECK_FLAG(flags, NEXTHOP_REGISTER_FLAG_COLOR)) + stream_putl(s, srte_color); + stream_putw_at(s, 0, stream_get_endp(s)); return zclient_send_message(zclient); @@ -2377,7 +2379,7 @@ static bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match, STREAM_GETW(s, nhr->nexthop_num); for (i = 0; i < nhr->nexthop_num; i++) { - if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0, 0) != 0) + if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0, nhr->message) != 0) return false; } @@ -3847,8 +3849,14 @@ int tm_release_table_chunk(struct zclient *zclient, uint32_t start, enum zclient_send_status zebra_send_sr_policy(struct zclient *zclient, int cmd, struct zapi_sr_policy *zp) { - if (zapi_sr_policy_encode(zclient->obuf, cmd, zp) < 0) - return ZCLIENT_SEND_FAILURE; + if (cmd == ZEBRA_SR_POLICY_SET || cmd == ZEBRA_SR_POLICY_DELETE) { + if (zapi_sr_policy_encode(zclient->obuf, cmd, zp) < 0) + return ZCLIENT_SEND_FAILURE; + } else if (cmd == ZEBRA_SRV6_POLICY_SET || cmd == ZEBRA_SRV6_POLICY_DELETE) { + if (zapi_srv6_policy_encode(zclient->obuf, cmd, zp) < 0) + return ZCLIENT_SEND_FAILURE; + } + return zclient_send_message(zclient); } @@ -3914,6 +3922,65 @@ int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp) return -1; } +int zapi_srv6_policy_encode(struct stream *s, int cmd, struct zapi_sr_policy *zp) +{ + struct zapi_srv6te_tunnel *zt = &zp->segment_list_v6; + + stream_reset(s); + + zclient_create_header(s, cmd, VRF_DEFAULT); + stream_putl(s, zp->color); + stream_put_ipaddr(s, &zp->endpoint); + stream_write(s, &zp->name, SRTE_POLICY_NAME_MAX_LENGTH); + + if (zt->seg_num > SRV6_MAX_SEGS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: seg: can't encode %u segs (maximum is %u)", + __func__, zt->seg_num, SRV6_MAX_SEGS); + return -1; + } + + stream_putw(s, zt->seg_num); + + for (int i = 0; i < zt->seg_num; i++) + stream_put_ipaddr(s, &zt->segs[i]); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return 0; +} + +int zapi_srv6_policy_decode(struct stream *s, struct zapi_sr_policy *zp) +{ + memset(zp, 0, sizeof(*zp)); + + struct zapi_srv6te_tunnel *zt = &zp->segment_list_v6; + + STREAM_GETL(s, zp->color); + STREAM_GET_IPADDR(s, &zp->endpoint); + STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH); + + /* segment list of active candidate path */ + STREAM_GETW(s, zt->seg_num); + + if (zt->seg_num > SRV6_MAX_SEGS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: seg: can't decode %u segs (maximum is %u)", + __func__, zt->seg_num, SRV6_MAX_SEGS); + return -1; + } + + for (int i = 0; i < zt->seg_num; i++) + STREAM_GET_IPADDR(s, &zt->segs[i]); + + return 0; + +stream_failure: + return -1; +} + + int zapi_sr_policy_notify_status_decode(struct stream *s, struct zapi_sr_policy *zp) { diff --git a/lib/zclient.h b/lib/zclient.h index 2385a8a2197b..ff92560fab67 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -70,6 +70,10 @@ typedef uint16_t zebra_size_t; #define ZEBRA_FEC_REGISTER_LABEL 0x1 #define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x2 +#define NEXTHOP_REGISTER_FLAG_CONNECTED (1 << 0) +#define NEXTHOP_REGISTER_FLAG_RESOLVE_VIA_DEFAULT (1 << 1) +#define NEXTHOP_REGISTER_FLAG_COLOR (1 << 2) + /* Client capabilities */ enum zserv_client_capabilities { ZEBRA_CLIENT_GR_CAPABILITIES = 1, @@ -238,6 +242,8 @@ typedef enum { ZEBRA_TC_FILTER_DELETE, ZEBRA_OPAQUE_NOTIFY, ZEBRA_SRV6_SID_NOTIFY, + ZEBRA_SRV6_POLICY_SET, + ZEBRA_SRV6_POLICY_DELETE, } zebra_message_types_t; /* Zebra message types. Please update the corresponding * command_types array with any changes! @@ -636,11 +642,16 @@ struct zapi_srte_tunnel { mpls_label_t labels[MPLS_MAX_LABELS]; }; +struct zapi_srv6te_tunnel { + uint8_t seg_num; + struct ipaddr segs[SRV6_MAX_SEGS]; +}; struct zapi_sr_policy { uint32_t color; struct ipaddr endpoint; char name[SRTE_POLICY_NAME_MAX_LENGTH]; struct zapi_srte_tunnel segment_list; + struct zapi_srv6te_tunnel segment_list_v6; int status; }; @@ -1106,6 +1117,8 @@ extern int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp); extern int zapi_sr_policy_notify_status_decode(struct stream *s, struct zapi_sr_policy *zp); +extern int zapi_srv6_policy_encode(struct stream *s, int cmd, struct zapi_sr_policy *zp); +extern int zapi_srv6_policy_decode(struct stream *s, struct zapi_sr_policy *zp); extern enum zclient_send_status zebra_send_mpls_labels(struct zclient *zclient, int cmd, struct zapi_labels *zl); @@ -1130,8 +1143,7 @@ extern enum zclient_send_status zclient_route_send(uint8_t, struct zclient *, struct zapi_route *); extern enum zclient_send_status zclient_send_rnh(struct zclient *zclient, int command, const struct prefix *p, - safi_t safi, bool connected, bool resolve_via_default, - vrf_id_t vrf_id); + safi_t safi, uint8_t flags, vrf_id_t vrf_id, uint32_t srte_color); int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, uint32_t api_flags, uint32_t api_message); extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 466301309f2a..49820829ea41 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -121,6 +121,7 @@ void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg) { struct prefix prefix = {}; int command; + uint8_t flags = 0; if (zclient->sock < 0) { if (IS_OSPF6_DEBUG_ZEBRA(SEND)) @@ -140,9 +141,8 @@ void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg) zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__, zserv_command_string(command), &prefix, ospf6->vrf_id); - - if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, false, - true, ospf6->vrf_id) + SET_FLAG(flags, NEXTHOP_REGISTER_FLAG_RESOLVE_VIA_DEFAULT); + if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, flags, ospf6->vrf_id, 0) == ZCLIENT_SEND_FAILURE) flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed", __func__); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index b718d498ae0d..199d8bcc6f12 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -1473,6 +1473,7 @@ void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg) { struct prefix prefix = {}; int command; + uint8_t flags = 0; if (zclient->sock < 0) { if (IS_DEBUG_OSPF(zebra, ZEBRA)) @@ -1494,8 +1495,9 @@ void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg) zserv_command_string(command), &prefix, ospf_vrf_id_to_name(ospf->vrf_id)); - if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, false, - true, ospf->vrf_id) == ZCLIENT_SEND_FAILURE) + SET_FLAG(flags, NEXTHOP_REGISTER_FLAG_RESOLVE_VIA_DEFAULT); + if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, flags, + ospf->vrf_id, 0) == ZCLIENT_SEND_FAILURE) flog_err(EC_LIB_ZAPI_SOCKET, "%s(%s): zclient_send_rnh() failed", __func__, ospf_vrf_id_to_name(ospf->vrf_id)); } diff --git a/pathd/path_cli.c b/pathd/path_cli.c index bf8a9ea02841..58d20df4a2f5 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -106,7 +106,7 @@ DEFPY(show_srte_policy, /* Prepare table. */ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); - ttable_add_row(tt, "Endpoint|Color|Name|BSID|Status"); + ttable_add_row(tt, "Endpoint|Color|Name|BSID|Status|Type"); tt->style.cell.rpad = 2; tt->style.corner = '+'; ttable_restyle(tt); @@ -121,11 +121,12 @@ DEFPY(show_srte_policy, snprintf(binding_sid, sizeof(binding_sid), "%u", policy->binding_sid); - ttable_add_row(tt, "%s|%u|%s|%s|%s", endpoint, policy->color, - policy->name, binding_sid, - policy->status == SRTE_POLICY_STATUS_UP - ? "Active" - : "Inactive"); + ttable_add_row(tt, "%s|%u|%s|%s|%s|%s", endpoint, policy->color, + policy->name, binding_sid, + policy->status == SRTE_POLICY_STATUS_UP ? "Active" : "Inactive", + policy->type == SRTE_POLICY_TYPE_MPLS + ? "MPLS" + : (policy->type == SRTE_POLICY_TYPE_SRV6 ? "SRV6" : "Undefined")); } /* Dump the generated table. */ @@ -172,37 +173,60 @@ DEFPY(show_srte_policy_detail, snprintf(binding_sid, sizeof(binding_sid), "%u", policy->binding_sid); vty_out(vty, - "Endpoint: %s Color: %u Name: %s BSID: %s Status: %s\n", + "Endpoint: %s Color: %u Name: %s BSID: %s Status: %s Type: %s\n", endpoint, policy->color, policy->name, binding_sid, - policy->status == SRTE_POLICY_STATUS_UP ? "Active" - : "Inactive"); + policy->status == SRTE_POLICY_STATUS_UP ? "Active" : "Inactive", + policy->type == SRTE_POLICY_TYPE_MPLS + ? "MPLS" + : (policy->type == SRTE_POLICY_TYPE_SRV6 ? "SRV6" : "Undefined")); RB_FOREACH (candidate, srte_candidate_head, &policy->candidate_paths) { struct srte_segment_list *segment_list; - - segment_list = candidate->lsp->segment_list; - if (segment_list == NULL) - segment_list_info = undefined_info; - else if (segment_list->protocol_origin - == SRTE_ORIGIN_PCEP) - segment_list_info = created_by_pce_info; - else - segment_list_info = - candidate->lsp->segment_list->name; - - vty_out(vty, - " %s Preference: %d Name: %s Type: %s Segment-List: %s Protocol-Origin: %s\n", - CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST) - ? "*" - : " ", - candidate->preference, candidate->name, - candidate->type == SRTE_CANDIDATE_TYPE_EXPLICIT - ? "explicit" - : "dynamic", - segment_list_info, - srte_origin2str( - candidate->lsp->protocol_origin)); + if (policy->type == SRTE_POLICY_TYPE_MPLS) { + segment_list = candidate->lsp->segment_list; + if (segment_list == NULL) + segment_list_info = undefined_info; + else if (segment_list->protocol_origin + == SRTE_ORIGIN_PCEP) + segment_list_info = created_by_pce_info; + else + segment_list_info = + candidate->lsp->segment_list->name; + + vty_out(vty, + " %s Preference: %d Name: %s Type: %s Segment-List: %s Protocol-Origin: %s\n", + CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST) + ? "*" + : " ", + candidate->preference, candidate->name, + candidate->type == SRTE_CANDIDATE_TYPE_EXPLICIT + ? "explicit" + : "dynamic", + segment_list_info, + srte_origin2str( + candidate->lsp->protocol_origin)); + } else if (policy->type == SRTE_POLICY_TYPE_SRV6) { + segment_list = candidate->segment_list; + if (segment_list == NULL) + segment_list_info = undefined_info; + else + segment_list_info = + candidate->segment_list->name; + + vty_out(vty, + " %s Preference: %d Name: %s Type: %s Segment-List: %s Protocol-Origin: %s\n", + CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST) + ? "*" + : " ", + candidate->preference, candidate->name, + candidate->type == SRTE_CANDIDATE_TYPE_EXPLICIT + ? "explicit" + : "dynamic", + segment_list_info, + srte_origin2str( + candidate->protocol_origin)); + } } vty_out(vty, "\n"); @@ -308,6 +332,7 @@ static int segment_list_has_src_dst( struct ipaddr ip_src = {}; struct ipaddr ip_dst = {}; + if (adj_src_ipv4_str != NULL) { ip_src.ipa_type = IPADDR_V4; ip_src.ip._v4_addr = adj_src_ipv4; @@ -527,6 +552,24 @@ DEFPY(srte_segment_list_segment, srte_segment_list_segment_cmd, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG(srv6te_segment_list_segment, srv6te_segment_list_segment_cmd, + "index (0-4294967295)$index ipv6-address X:X::X:X$ipv6_addr", + "Index\n" + "Index Value\n" + IPV6_STR + "IPv6 address\n") +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/srv6-sid-value", index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, ipv6_addr_str); + return nb_cli_apply_changes(vty, NULL); +} + DEFPY(srte_segment_list_no_segment, srte_segment_list_no_segment_cmd, "no index (0-4294967295)$index", @@ -547,6 +590,12 @@ void cli_show_srte_segment_list_segment(struct vty *vty, bool show_defaults) { vty_out(vty, " index %s", yang_dnode_get_string(dnode, "index")); + + if (yang_dnode_exists(dnode, "srv6-sid-value")) { + vty_out(vty, " ipv6-address %s", + yang_dnode_get_string(dnode, "srv6-sid-value")); + } + if (yang_dnode_exists(dnode, "sid-value")) { vty_out(vty, " mpls label %s", yang_dnode_get_string(dnode, "sid-value")); @@ -1331,6 +1380,8 @@ void path_cli_init(void) install_element(SR_TRAFFIC_ENG_NODE, &srte_no_segment_list_cmd); install_element(SR_SEGMENT_LIST_NODE, &srte_segment_list_segment_cmd); + install_element(SR_SEGMENT_LIST_NODE, + &srv6te_segment_list_segment_cmd); install_element(SR_SEGMENT_LIST_NODE, &srte_segment_list_no_segment_cmd); install_element(SR_TRAFFIC_ENG_NODE, &srte_policy_cmd); diff --git a/pathd/path_nb.c b/pathd/path_nb.c index e1c0cc3efa6c..523d3152434a 100644 --- a/pathd/path_nb.c +++ b/pathd/path_nb.c @@ -82,6 +82,14 @@ const struct frr_yang_module_info frr_pathd_info = { }, .priority = NB_DFLT_PRIORITY - 1 }, + { + .xpath = "/frr-pathd:pathd/srte/segment-list/segment/srv6-sid-value", + .cbs = { + .modify = pathd_srte_segment_list_segment_v6_sid_value_modify, + .destroy = pathd_srte_segment_list_segment_v6_sid_value_destroy, + }, + .priority = NB_DFLT_PRIORITY - 1 + }, { .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai", .cbs = { diff --git a/pathd/path_nb.h b/pathd/path_nb.h index 21876d788303..4d1895cc3a3e 100644 --- a/pathd/path_nb.h +++ b/pathd/path_nb.h @@ -20,6 +20,7 @@ const void * pathd_srte_segment_list_lookup_entry(struct nb_cb_lookup_entry_args *args); int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args); +int pathd_srte_segment_list_segment_check_validate(struct nb_cb_create_args *args); int pathd_srte_segment_list_segment_destroy(struct nb_cb_destroy_args *args); int pathd_srte_segment_list_protocol_origin_modify( struct nb_cb_modify_args *args); @@ -32,6 +33,10 @@ void pathd_srte_segment_list_segment_nai_apply_finish( struct nb_cb_apply_finish_args *args); int pathd_srte_segment_list_segment_sid_value_destroy( struct nb_cb_destroy_args *args); +int pathd_srte_segment_list_segment_v6_sid_value_modify( + struct nb_cb_modify_args *args); +int pathd_srte_segment_list_segment_v6_sid_value_destroy( + struct nb_cb_destroy_args *args); int pathd_srte_policy_create(struct nb_cb_create_args *args); int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args); const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args); @@ -53,6 +58,8 @@ pathd_srte_policy_candidate_path_get_next(struct nb_cb_get_next_args *args); int pathd_srte_policy_candidate_path_get_keys(struct nb_cb_get_keys_args *args); const void *pathd_srte_policy_candidate_path_lookup_entry( struct nb_cb_lookup_entry_args *args); +int pathd_srte_policy_candidate_path_check_validate( + struct nb_cb_create_args *args); void pathd_srte_policy_candidate_path_bandwidth_apply_finish( struct nb_cb_apply_finish_args *args); int pathd_srte_policy_candidate_path_bandwidth_destroy( diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c index 48531ba43339..68495dc23f2c 100644 --- a/pathd/path_nb_config.c +++ b/pathd/path_nb_config.c @@ -96,9 +96,16 @@ int pathd_srte_segment_list_originator_modify(struct nb_cb_modify_args *args) */ int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args) { - struct srte_segment_list *segment_list; struct srte_segment_entry *segment; + struct srte_segment_list *segment_list; uint32_t index; + enum nb_error nb_code = NB_OK; + + if (args->event == NB_EV_VALIDATE) { + nb_code = pathd_srte_segment_list_segment_check_validate(args); + if (nb_code != NB_OK) + return nb_code; + } if (args->event != NB_EV_APPLY) return NB_OK; @@ -142,6 +149,7 @@ int pathd_srte_segment_list_segment_sid_value_modify( segment = nb_running_get_entry(args->dnode, NULL, true); sid_value = yang_dnode_get_uint32(args->dnode, NULL); segment->sid_value = sid_value; + segment->segment_list->type = SRTE_SEGMENT_LIST_TYPE_MPLS; SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED); return NB_OK; @@ -162,6 +170,41 @@ int pathd_srte_segment_list_segment_sid_value_destroy( return NB_OK; } +/* + * XPath: /frr-pathd:pathd/srte/segment-list/segment/srv6-sid-value + */ +int pathd_srte_segment_list_segment_v6_sid_value_modify( + struct nb_cb_modify_args *args) +{ + struct ipaddr sid_value; + struct srte_segment_entry *segment; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + segment = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ip(&sid_value, args->dnode, NULL); + segment->srv6_sid_value = sid_value; + segment->segment_list->type = SRTE_SEGMENT_LIST_TYPE_SRV6; + SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED); + + return NB_OK; +} + +int pathd_srte_segment_list_segment_v6_sid_value_destroy( + struct nb_cb_destroy_args *args) +{ + struct srte_segment_entry *segment; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + segment = nb_running_get_entry(args->dnode, NULL, true); + memset(&segment->srv6_sid_value, 0, sizeof(struct ipaddr)); + SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED); + + return NB_OK; +} int pathd_srte_segment_list_segment_nai_destroy(struct nb_cb_destroy_args *args) { @@ -236,10 +279,12 @@ void pathd_srte_segment_list_segment_nai_apply_finish( zlog_debug(" Segment list name (%d) index (%s) ", segment->index, segment->segment_list->name); if (srte_segment_entry_set_nai(segment, type, &local_addr, local_iface, - &remote_addr, remote_iface, algo, - local_prefix_len)) + &remote_addr, remote_iface, algo, + local_prefix_len)) { SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_SID_CONFLICT); + segment->segment_list->type = SRTE_SEGMENT_LIST_TYPE_MPLS; + } } /* @@ -372,7 +417,13 @@ int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args) struct srte_policy *policy; struct srte_candidate *candidate; uint32_t preference; + enum nb_error nb_code = NB_OK; + if (args->event == NB_EV_VALIDATE) { + nb_code = pathd_srte_policy_candidate_path_check_validate(args); + if (nb_code != NB_OK) + return nb_code; + } if (args->event != NB_EV_APPLY) return NB_OK; @@ -703,7 +754,13 @@ int pathd_srte_policy_candidate_path_segment_list_name_modify( segment_list_name = yang_dnode_get_string(args->dnode, NULL); candidate->segment_list = srte_segment_list_find(segment_list_name); - candidate->lsp->segment_list = candidate->segment_list; + + if (candidate->segment_list->type == SRTE_SEGMENT_LIST_TYPE_MPLS) { + candidate->lsp->segment_list = candidate->segment_list; + candidate->policy->type = SRTE_POLICY_TYPE_MPLS; + } else if (candidate->segment_list->type == SRTE_SEGMENT_LIST_TYPE_SRV6) + candidate->policy->type = SRTE_POLICY_TYPE_SRV6; + assert(candidate->segment_list); SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); diff --git a/pathd/path_nb_state.c b/pathd/path_nb_state.c index 35b9e37c7370..d5a4d1460acf 100644 --- a/pathd/path_nb_state.c +++ b/pathd/path_nb_state.c @@ -4,6 +4,7 @@ */ #include +#include #include "log.h" #include "prefix.h" @@ -149,6 +150,68 @@ const void *pathd_srte_policy_candidate_path_lookup_entry( return srte_candidate_find(policy, preference); } +int pathd_srte_segment_list_segment_check_validate(struct nb_cb_create_args *args) +{ + struct srte_segment_list *segment_list; + + segment_list = nb_running_get_entry(args->dnode, NULL, false); + if (segment_list == NULL) + return NB_OK; + + if (yang_dnode_exists(args->dnode, "sid-value") || yang_dnode_exists(args->dnode, "nai")) { + if (segment_list->type == SRTE_SEGMENT_LIST_TYPE_SRV6) { + snprintf(args->errmsg, args->errmsg_len, + "The Segment List(%s) Type must be SRv6!", segment_list->name); + flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, + "The Segment List(%s) Type must be SRv6!", segment_list->name); + return NB_ERR_VALIDATION; + } else + return NB_OK; + } + + if (yang_dnode_exists(args->dnode, "srv6-sid-value")) { + if (segment_list->type == SRTE_SEGMENT_LIST_TYPE_MPLS) { + snprintf(args->errmsg, args->errmsg_len, + "The Segment List(%s) Type must be MPLS!", segment_list->name); + flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, + "The Segment List(%s) Type must be MPLS!", segment_list->name); + return NB_ERR_VALIDATION; + } else + return NB_OK; + } + return NB_OK; +} + +int pathd_srte_policy_candidate_path_check_validate( + struct nb_cb_create_args *args) +{ + struct srte_policy *policy; + const char *segment_list_name; + struct srte_segment_list *segment_list; + + policy = nb_running_get_entry(args->dnode, NULL, false); + segment_list_name = yang_dnode_get_string(args->dnode, "segment-list-name"); + segment_list = srte_segment_list_find(segment_list_name); + + if (segment_list == NULL) + return NB_OK; + + if ((segment_list->type == SRTE_SEGMENT_LIST_TYPE_SRV6 + && policy->type == SRTE_POLICY_TYPE_MPLS) + || (segment_list->type == SRTE_SEGMENT_LIST_TYPE_MPLS + && policy->type == SRTE_POLICY_TYPE_SRV6)) { + snprintf(args->errmsg, args->errmsg_len, + "The Segment List type(%d) and Policy type(%d) must match!", + segment_list->type, policy->type); + flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, + "The Segment List type(%d) and Policy type(%d) must match!", + segment_list->type, policy->type); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + /* * XPath: /frr-pathd:pathd/srte/policy/candidate_path/is-best-candidate-path */ diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c index ba03315c82f7..b4662c342c9d 100644 --- a/pathd/path_zebra.c +++ b/pathd/path_zebra.c @@ -101,12 +101,19 @@ static void path_zebra_connected(struct zclient *zclient) candidate = policy->best_candidate; if (!candidate) continue; - - segment_list = candidate->lsp->segment_list; - if (!segment_list) - continue; - - path_zebra_add_sr_policy(policy, segment_list); + if (policy->type == SRTE_POLICY_TYPE_MPLS) { + segment_list = candidate->lsp->segment_list; + if (!segment_list) + continue; + + path_zebra_add_sr_policy(policy, segment_list); + } else if (policy->type == SRTE_POLICY_TYPE_SRV6) { + segment_list = candidate->segment_list; + if (!segment_list) + continue; + + path_zebra_add_srv6_policy(policy, segment_list); + } } } @@ -212,6 +219,45 @@ void path_zebra_delete_sr_policy(struct srte_policy *policy) (void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_DELETE, &zp); } +/** + * Adds a segment routing policy to Zebra. + * + * @param policy The policy to add + * @param segment_list The segment list for the policy + */ +void path_zebra_add_srv6_policy(struct srte_policy *policy, + struct srte_segment_list *segment_list) +{ + struct zapi_sr_policy zp = {}; + struct srte_segment_entry *segment; + + zp.color = policy->color; + zp.endpoint = policy->endpoint; + strlcpy(zp.name, policy->name, sizeof(zp.name)); + zp.segment_list_v6.seg_num = 0; + RB_FOREACH (segment, srte_segment_entry_head, &segment_list->segments) { + memcpy(&zp.segment_list_v6.segs[zp.segment_list_v6.seg_num], + &segment->srv6_sid_value, sizeof(struct ipaddr)); + zp.segment_list_v6.seg_num++; + } + policy->status = SRTE_POLICY_STATUS_UP; + + (void)zebra_send_sr_policy(zclient, ZEBRA_SRV6_POLICY_SET, &zp); +} + +void path_zebra_delete_srv6_policy(struct srte_policy *policy) +{ + struct zapi_sr_policy zp = {}; + + zp.color = policy->color; + zp.endpoint = policy->endpoint; + strlcpy(zp.name, policy->name, sizeof(zp.name)); + zp.segment_list_v6.seg_num = 0; + policy->status = SRTE_POLICY_STATUS_DOWN; + + (void)zebra_send_sr_policy(zclient, ZEBRA_SRV6_POLICY_DELETE, &zp); +} + /** * Allocates a label from Zebra's label manager. * diff --git a/pathd/path_zebra.h b/pathd/path_zebra.h index 74a62e38b328..f0cebfdfa574 100644 --- a/pathd/path_zebra.h +++ b/pathd/path_zebra.h @@ -14,6 +14,9 @@ bool get_ipv6_router_id(struct in6_addr *router_id); void path_zebra_add_sr_policy(struct srte_policy *policy, struct srte_segment_list *segment_list); void path_zebra_delete_sr_policy(struct srte_policy *policy); +void path_zebra_add_srv6_policy(struct srte_policy *policy, + struct srte_segment_list *segment_list); +void path_zebra_delete_srv6_policy(struct srte_policy *policy); int path_zebra_request_label(mpls_label_t label); void path_zebra_release_label(mpls_label_t label); void path_zebra_init(struct event_loop *master); diff --git a/pathd/pathd.c b/pathd/pathd.c index 431fe4d1e31e..469b91143d68 100644 --- a/pathd/pathd.c +++ b/pathd/pathd.c @@ -121,6 +121,7 @@ struct srte_segment_list *srte_segment_list_add(const char *name) segment_list = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment_list)); strlcpy(segment_list->name, name, sizeof(segment_list->name)); + segment_list->type = SRTE_SEGMENT_LIST_TYPE_UNDEFINED; RB_INIT(srte_segment_entry_head, &segment_list->segments); RB_INSERT(srte_segment_list_head, &srte_segment_lists, segment_list); @@ -329,6 +330,7 @@ struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint, policy->endpoint = *endpoint; policy->binding_sid = MPLS_LABEL_NONE; policy->protocol_origin = origin; + policy->type = SRTE_POLICY_TYPE_UNDEFINED; if (originator != NULL) strlcpy(policy->originator, originator, sizeof(policy->originator)); @@ -351,9 +353,14 @@ void srte_policy_del(struct srte_policy *policy) { struct srte_candidate *candidate; - path_zebra_delete_sr_policy(policy); + if (policy->type == SRTE_POLICY_TYPE_MPLS) { + path_zebra_delete_sr_policy(policy); - path_zebra_release_label(policy->binding_sid); + path_zebra_release_label(policy->binding_sid); + } + + if (policy->type == SRTE_POLICY_TYPE_SRV6) + path_zebra_delete_srv6_policy(policy); while (!RB_EMPTY(srte_candidate_head, &policy->candidate_paths)) { candidate = @@ -518,14 +525,23 @@ srte_policy_best_candidate(const struct srte_policy *policy) { struct srte_candidate *candidate; - RB_FOREACH_REVERSE (candidate, srte_candidate_head, - &policy->candidate_paths) { - /* search for highest preference with existing segment list */ - if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED) - && candidate->lsp->segment_list - && (!CHECK_FLAG(candidate->lsp->segment_list->flags, - F_SEGMENT_LIST_SID_CONFLICT))) - return candidate; + if (policy->type == SRTE_POLICY_TYPE_MPLS) { + RB_FOREACH_REVERSE (candidate, srte_candidate_head, + &policy->candidate_paths) { + /* search for highest preference with existing segment list */ + if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED) + && candidate->lsp->segment_list + && (!CHECK_FLAG(candidate->lsp->segment_list->flags, + F_SEGMENT_LIST_SID_CONFLICT))) + return candidate; + } + } else if (policy->type == SRTE_POLICY_TYPE_SRV6) { + RB_FOREACH_REVERSE (candidate, srte_candidate_head, + &policy->candidate_paths) { + if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED) + && candidate->segment_list) + return candidate; + } } return NULL; @@ -598,6 +614,9 @@ void srte_policy_apply_changes(struct srte_policy *policy) struct srte_candidate *new_best_candidate; char endpoint[ENDPOINT_STR_LENGTH]; + if (policy->type != SRTE_POLICY_TYPE_MPLS && policy->type != SRTE_POLICY_TYPE_SRV6) + return; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); /* Get old and new best candidate path. */ @@ -621,17 +640,24 @@ void srte_policy_apply_changes(struct srte_policy *policy) * Rely on replace semantics if there's a new best * candidate. */ - if (!new_best_candidate) - path_zebra_delete_sr_policy(policy); + if (!new_best_candidate) { + if (policy->type == SRTE_POLICY_TYPE_MPLS) + path_zebra_delete_sr_policy(policy); + else + path_zebra_delete_srv6_policy(policy); + } } if (new_best_candidate) { policy->best_candidate = new_best_candidate; SET_FLAG(new_best_candidate->flags, F_CANDIDATE_BEST); SET_FLAG(new_best_candidate->flags, F_CANDIDATE_MODIFIED); - - path_zebra_add_sr_policy( - policy, new_best_candidate->lsp->segment_list); + if (policy->type == SRTE_POLICY_TYPE_MPLS) + path_zebra_add_sr_policy( + policy, new_best_candidate->lsp->segment_list); + else + path_zebra_add_srv6_policy( + policy, new_best_candidate->segment_list); } } else if (new_best_candidate) { /* The best candidate path did not change, but some of its @@ -652,8 +678,12 @@ void srte_policy_apply_changes(struct srte_policy *policy) endpoint, policy->color, new_best_candidate->name); - path_zebra_add_sr_policy( - policy, new_best_candidate->lsp->segment_list); + if (policy->type == SRTE_POLICY_TYPE_MPLS) + path_zebra_add_sr_policy( + policy, new_best_candidate->lsp->segment_list); + else + path_zebra_add_srv6_policy( + policy, new_best_candidate->segment_list); } } @@ -734,8 +764,8 @@ void srte_candidate_del(struct srte_candidate *candidate) RB_REMOVE(srte_candidate_head, &srte_policy->candidate_paths, candidate); - - XFREE(MTYPE_PATH_SR_CANDIDATE, candidate->lsp); + if (candidate->lsp) + XFREE(MTYPE_PATH_SR_CANDIDATE, candidate->lsp); XFREE(MTYPE_PATH_SR_CANDIDATE, candidate); } diff --git a/pathd/pathd.h b/pathd/pathd.h index 75e7eff920e7..33e8664d929d 100644 --- a/pathd/pathd.h +++ b/pathd/pathd.h @@ -153,6 +153,17 @@ enum affinity_filter_type { }; #define MAX_AFFINITY_FILTER_TYPE 3 +enum srte_segment_list_type { + SRTE_SEGMENT_LIST_TYPE_UNDEFINED = 0, + SRTE_SEGMENT_LIST_TYPE_MPLS = 1, + SRTE_SEGMENT_LIST_TYPE_SRV6 = 2, +}; + +enum srte_policy_type { + SRTE_POLICY_TYPE_UNDEFINED = 0, + SRTE_POLICY_TYPE_MPLS = 1, + SRTE_POLICY_TYPE_SRV6 = 2, +}; struct srte_segment_list; struct srte_segment_entry { @@ -167,6 +178,9 @@ struct srte_segment_entry { /* Label Value. */ mpls_label_t sid_value; + /*Srv6 Sid*/ + struct ipaddr srv6_sid_value; + /* NAI Type */ enum srte_segment_nai_type nai_type; /* NAI local address when nai type is not NONE */ @@ -201,6 +215,8 @@ struct srte_segment_list { /* Nexthops. */ struct srte_segment_entry_head segments; + enum srte_segment_list_type type; + /* Status flags. */ uint16_t flags; #define F_SEGMENT_LIST_NEW 0x0002 @@ -341,6 +357,9 @@ struct srte_policy { /* Operational Status of the policy */ enum srte_policy_status status; + /* The Type (mpls or srv6) */ + enum srte_policy_type type; + /* Best candidate path. */ struct srte_candidate *best_candidate; diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index dd15beaff440..dd8f7ab61dfa 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -425,6 +425,7 @@ void pbr_send_rnh(struct nexthop *nhop, bool reg) { uint32_t command; struct prefix p; + uint8_t flags = 0; command = (reg) ? ZEBRA_NEXTHOP_REGISTER : ZEBRA_NEXTHOP_UNREGISTER; @@ -454,8 +455,8 @@ void pbr_send_rnh(struct nexthop *nhop, bool reg) break; } - if (zclient_send_rnh(zclient, command, &p, SAFI_UNICAST, false, false, - nhop->vrf_id) + if (zclient_send_rnh(zclient, command, &p, SAFI_UNICAST, flags, + nhop->vrf_id, 0) == ZCLIENT_SEND_FAILURE) { zlog_warn("%s: Failure to send nexthop to zebra", __func__); } diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 00ab46b4cd4b..7732917089fa 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -43,11 +43,13 @@ static void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zcli { struct prefix p; int ret; + uint8_t flags = 0; pim_addr_to_prefix(&p, addr); /* Register to track nexthops from the MRIB */ - ret = zclient_send_rnh(zclient, command, &p, SAFI_MULTICAST, false, false, pim->vrf->vrf_id); + ret = zclient_send_rnh(zclient, command, &p, SAFI_MULTICAST, flags, + pim->vrf->vrf_id, 0); if (ret == ZCLIENT_SEND_FAILURE) zlog_warn( "sendmsg_nexthop: zclient_send_message() failed registering MRIB tracking"); @@ -58,7 +60,8 @@ static void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zcli ret); /* Also register to track nexthops from the URIB */ - ret = zclient_send_rnh(zclient, command, &p, SAFI_UNICAST, false, false, pim->vrf->vrf_id); + ret = zclient_send_rnh(zclient, command, &p, SAFI_UNICAST, flags, + pim->vrf->vrf_id, 0); if (ret == ZCLIENT_SEND_FAILURE) zlog_warn( "sendmsg_nexthop: zclient_send_message() failed registering URIB tracking"); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 4447b69bf619..ff4de90a3af0 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -623,13 +623,17 @@ void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, b { int command = ZEBRA_NEXTHOP_REGISTER; safi_t safi = mrib ? SAFI_MULTICAST : SAFI_UNICAST; + uint8_t flags = 0; command = ZEBRA_NEXTHOP_REGISTER; if (!watch) command = ZEBRA_NEXTHOP_UNREGISTER; - if (zclient_send_rnh(zclient, command, p, safi, connected, false, vrf_id) == + if (connected) + SET_FLAG(flags, NEXTHOP_REGISTER_FLAG_CONNECTED); + + if (zclient_send_rnh(zclient, command, p, safi, flags, vrf_id, 0) == ZCLIENT_SEND_FAILURE) zlog_warn("%s: Failure to send nexthop to zebra", __func__); } diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index d76befc1318f..111b2694b77f 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -308,6 +308,7 @@ void static_zebra_nht_register(struct static_nexthop *nh, bool reg) struct static_route_info *si = static_route_info_from_rnode(rn); struct static_nht_data *nhtd, lookup = {}; uint32_t cmd; + uint8_t flags = 0; if (!static_zebra_nht_get_prefix(nh, &lookup.nh)) return; @@ -373,8 +374,8 @@ void static_zebra_nht_register(struct static_nexthop *nh, bool reg) "Unregistering nexthop(%pFX) for %pRN", &lookup.nh, rn); } - if (zclient_send_rnh(zclient, cmd, &lookup.nh, si->safi, false, false, - nh->nh_vrf_id) == ZCLIENT_SEND_FAILURE) + if (zclient_send_rnh(zclient, cmd, &lookup.nh, si->safi, flags, + nh->nh_vrf_id, 0) == ZCLIENT_SEND_FAILURE) zlog_warn("%s: Failure to send nexthop %pFX for %pRN to zebra", __func__, &lookup.nh, rn); else if (reg) diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/ce1/frr.conf b/tests/topotests/bgp_srv6l3vpn_te_policy/ce1/frr.conf new file mode 100644 index 000000000000..5afbf6fdd759 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/ce1/frr.conf @@ -0,0 +1,14 @@ +hostname ce1 +! +interface eth0 + ip address 192.168.1.2/24 + ipv6 address 1001:1::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.1.1 +ipv6 route ::/0 1001:1::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/ce2/frr.conf b/tests/topotests/bgp_srv6l3vpn_te_policy/ce2/frr.conf new file mode 100644 index 000000000000..aa37ee19d6d2 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/ce2/frr.conf @@ -0,0 +1,14 @@ +hostname ce2 +! +interface eth0 + ip address 192.168.2.2/24 + ipv6 address 1001:2::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.2.1 +ipv6 route ::/0 1001:2::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/ce3/frr.conf b/tests/topotests/bgp_srv6l3vpn_te_policy/ce3/frr.conf new file mode 100644 index 000000000000..0dd8cde58baa --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/ce3/frr.conf @@ -0,0 +1,14 @@ +hostname ce3 +! +interface eth0 + ip address 192.168.3.2/24 + ipv6 address 1001:3::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.3.1 +ipv6 route ::/0 1001:3::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/ce4/frr.conf b/tests/topotests/bgp_srv6l3vpn_te_policy/ce4/frr.conf new file mode 100644 index 000000000000..34fc1ca74064 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/ce4/frr.conf @@ -0,0 +1,14 @@ +hostname ce2 +! +interface eth0 + ip address 192.168.4.2/24 + ipv6 address 1001:4::2/64 +! +ip forwarding +ipv6 forwarding +! +ip route 0.0.0.0/0 192.168.4.1 +ipv6 route ::/0 1001:4::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv4_vrf1_rib_route.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv4_vrf1_rib_route.json new file mode 100644 index 000000000000..e1d0695a9783 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv4_vrf1_rib_route.json @@ -0,0 +1,60 @@ +{ + "1:10":{ + "prefix":"192.168.1.0/24", + "version":1, + "advertisedTo":{ + "1000::1":{ + "hostname":"pe1" + } + }, + "paths":[ + { + "aspath":{ + "string":"65501", + "segments":[ + { + "type":"as-sequence", + "list":[ + 65501 + ] + } + ], + "length":1 + }, + "origin":"incomplete", + "metric":0, + "valid":true, + "version":1, + "bestpath":{ + "overall":true, + "selectionReason":"First path received" + }, + "extendedCommunity":{ + "string":"RT:99:99 Color:01:100" + }, + "remoteLabel":"*", + "remoteSid":"2001:db8:1:1::", + "lastUpdate":{ + "epoch":"*", + "string":"*" + }, + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"pe1", + "afi":"ipv4", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"1000::1", + "routerId":"1.1.1.1", + "hostname":"pe1", + "type":"external" + } + } + ] + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv4_vrf2_rib_route.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv4_vrf2_rib_route.json new file mode 100644 index 000000000000..2308493255e9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv4_vrf2_rib_route.json @@ -0,0 +1,60 @@ +{ + "1:20":{ + "prefix":"192.168.2.0/24", + "version":1, + "advertisedTo":{ + "1000::1":{ + "hostname":"pe1" + } + }, + "paths":[ + { + "aspath":{ + "string":"65501", + "segments":[ + { + "type":"as-sequence", + "list":[ + 65501 + ] + } + ], + "length":1 + }, + "origin":"incomplete", + "metric":0, + "valid":true, + "version":1, + "bestpath":{ + "overall":true, + "selectionReason":"First path received" + }, + "extendedCommunity":{ + "string":"RT:88:88 Color:01:200" + }, + "remoteLabel":"*", + "remoteSid":"2001:db8:1:1::", + "lastUpdate":{ + "epoch":"*", + "string":"*" + }, + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"pe1", + "afi":"ipv4", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"1000::1", + "routerId":"1.1.1.1", + "hostname":"pe1", + "type":"external" + } + } + ] + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv6_vrf1_rib_route.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv6_vrf1_rib_route.json new file mode 100644 index 000000000000..5ed7a0a6bda9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv6_vrf1_rib_route.json @@ -0,0 +1,61 @@ +{ + "1:10":{ + "prefix":"1001:1::/64", + "version":1, + "advertisedTo":{ + "1000::1":{ + "hostname":"pe1" + } + }, + "paths":[ + { + "aspath":{ + "string":"65501", + "segments":[ + { + "type":"as-sequence", + "list":[ + 65501 + ] + } + ], + "length":1 + }, + "origin":"incomplete", + "metric":0, + "valid":true, + "version":1, + "bestpath":{ + "overall":true, + "selectionReason":"First path received" + }, + "extendedCommunity":{ + "string":"RT:99:99 Color:01:100" + }, + "remoteLabel":"*", + "remoteSid":"2001:db8:1:1::", + "lastUpdate":{ + "epoch":"*", + "string":"*" + }, + "nexthops":[ + { + "ip":"1000::1", + "hostname":"pe1", + "afi":"ipv6", + "scope":"global", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"1000::1", + "routerId":"1.1.1.1", + "hostname":"pe1", + "type":"external" + } + } + ] + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv6_vrf2_rib_route.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv6_vrf2_rib_route.json new file mode 100644 index 000000000000..f7d6a1249f4d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vpnv6_vrf2_rib_route.json @@ -0,0 +1,61 @@ +{ + "1:20":{ + "prefix":"1001:2::/64", + "version":1, + "advertisedTo":{ + "1000::1":{ + "hostname":"pe1" + } + }, + "paths":[ + { + "aspath":{ + "string":"65501", + "segments":[ + { + "type":"as-sequence", + "list":[ + 65501 + ] + } + ], + "length":1 + }, + "origin":"incomplete", + "metric":0, + "valid":true, + "version":1, + "bestpath":{ + "overall":true, + "selectionReason":"First path received" + }, + "extendedCommunity":{ + "string":"RT:88:88 Color:01:200" + }, + "remoteLabel":"*", + "remoteSid":"2001:db8:1:1::", + "lastUpdate":{ + "epoch":"*", + "string":"*" + }, + "nexthops":[ + { + "ip":"1000::1", + "hostname":"pe1", + "afi":"ipv6", + "scope":"global", + "metric":0, + "accessible":true, + "used":true + } + ], + "peer":{ + "peerId":"1000::1", + "routerId":"1.1.1.1", + "hostname":"pe1", + "type":"external" + } + } + ] + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv4_route.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv4_route.json new file mode 100644 index 000000000000..a10287700a95 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv4_route.json @@ -0,0 +1,67 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":"*", + "vrfName":"vrf1", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":1, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":"*", + "internalNextHopActiveNum":"*", + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":5, + "ip":"1000::1", + "afi":"ipv6", + "vrf":"default", + "active":true, + "recursive":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":{ + "segs":"2001:db8:1:1:1::" + } + }, + { + "flags":3, + "fib":true, + "ip":"*", + "afi":"ipv6", + "interfaceIndex":"*", + "interfaceName":"eth1", + "resolver":true, + "vrf":"default", + "active":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":{ + "segs":"2001:db8:1:1:1::" + } + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv4_route_te_policy.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv4_route_te_policy.json new file mode 100644 index 000000000000..953390b5518f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv4_route_te_policy.json @@ -0,0 +1,70 @@ +{ + "192.168.1.0/24":[ + { + "prefix":"192.168.1.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":"*", + "vrfName":"vrf1", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":1, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":"*", + "internalNextHopActiveNum":"*", + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3205, + "ip":"1000::1", + "afi":"ipv6", + "vrf":"default", + "active":true, + "recursive":true, + "labels":"*", + "weight":1, + "srteColor":100, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":[ + "2001:db8:3:3::3", + "2001:db8:1:1::1" + ] + }, + { + "flags":2051, + "fib":true, + "ip":"*", + "afi":"ipv6", + "interfaceIndex":"*", + "interfaceName":"eth1", + "resolver":true, + "vrf":"default", + "active":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":[ + "2001:db8:3:3::3", + "2001:db8:1:1::1" + ] + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv6_route.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv6_route.json new file mode 100644 index 000000000000..ce71b0271014 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv6_route.json @@ -0,0 +1,67 @@ +{ + "1001:1::/64":[ + { + "prefix":"1001:1::/64", + "prefixLen":64, + "protocol":"bgp", + "vrfId":"*", + "vrfName":"vrf1", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":1, + "internalStatus":"*", + "internalFlags":"*", + "internalNextHopNum":"*", + "internalNextHopActiveNum":"*", + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":5, + "ip":"1000::1", + "afi":"ipv6", + "vrf":"default", + "active":true, + "recursive":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":{ + "segs":"2001:db8:1:1:1::" + } + }, + { + "flags":3, + "fib":true, + "ip":"*", + "afi":"ipv6", + "interfaceIndex":"*", + "interfaceName":"eth1", + "resolver":true, + "vrf":"default", + "active":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":{ + "segs":"2001:db8:1:1:1::" + } + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv6_route_te_policy.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv6_route_te_policy.json new file mode 100644 index 000000000000..7afed522d9e8 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf1_ipv6_route_te_policy.json @@ -0,0 +1,70 @@ +{ + "1001:1::/64":[ + { + "prefix":"1001:1::/64", + "prefixLen":64, + "protocol":"bgp", + "vrfId":"*", + "vrfName":"vrf1", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":1, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":"*", + "internalNextHopActiveNum":"*", + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3205, + "ip":"1000::1", + "afi":"ipv6", + "vrf":"default", + "active":true, + "recursive":true, + "labels":"*", + "weight":1, + "srteColor":100, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":[ + "2001:db8:3:3::3", + "2001:db8:1:1::1" + ] + }, + { + "flags":2051, + "fib":true, + "ip":"*", + "afi":"ipv6", + "interfaceIndex":"*", + "interfaceName":"eth1", + "resolver":true, + "vrf":"default", + "active":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":[ + "2001:db8:3:3::3", + "2001:db8:1:1::1" + ] + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv4_route.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv4_route.json new file mode 100644 index 000000000000..4e7b5e029d58 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv4_route.json @@ -0,0 +1,67 @@ +{ + "192.168.2.0/24":[ + { + "prefix":"192.168.2.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":"*", + "vrfName":"vrf2", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":2, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":"*", + "internalNextHopActiveNum":"*", + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":5, + "ip":"1000::1", + "afi":"ipv6", + "vrf":"default", + "active":true, + "recursive":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + }, + { + "flags":3, + "fib":true, + "ip":"*", + "afi":"ipv6", + "interfaceIndex":"*", + "interfaceName":"eth1", + "resolver":true, + "vrf":"default", + "active":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv4_route_te_policy.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv4_route_te_policy.json new file mode 100644 index 000000000000..17d56859cca0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv4_route_te_policy.json @@ -0,0 +1,70 @@ +{ + "192.168.2.0/24":[ + { + "prefix":"192.168.2.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":"*", + "vrfName":"vrf2", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":2, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":"*", + "internalNextHopActiveNum":"*", + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3205, + "ip":"1000::1", + "afi":"ipv6", + "vrf":"default", + "active":true, + "recursive":true, + "labels":"*", + "weight":1, + "srteColor":200, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":[ + "2001:db8:4:4::4", + "2001:db8:1:1::1" + ] + }, + { + "flags":2051, + "fib":true, + "ip":"*", + "afi":"ipv6", + "interfaceIndex":"*", + "interfaceName":"eth1", + "resolver":true, + "vrf":"default", + "active":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":[ + "2001:db8:4:4::4", + "2001:db8:1:1::1" + ] + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv6_route.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv6_route.json new file mode 100644 index 000000000000..2e5df058296e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv6_route.json @@ -0,0 +1,67 @@ +{ + "1001:2::/64":[ + { + "prefix":"1001:2::/64", + "prefixLen":64, + "protocol":"bgp", + "vrfId":"*", + "vrfName":"vrf2", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":2, + "internalStatus":"*", + "internalFlags":"*", + "internalNextHopNum":"*", + "internalNextHopActiveNum":"*", + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":5, + "ip":"1000::1", + "afi":"ipv6", + "vrf":"default", + "active":true, + "recursive":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + }, + { + "flags":3, + "fib":true, + "ip":"*", + "afi":"ipv6", + "interfaceIndex":"*", + "interfaceName":"eth1", + "resolver":true, + "vrf":"default", + "active":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv6_route_te_policy.json b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv6_route_te_policy.json new file mode 100644 index 000000000000..6ce752f6da90 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/json/vrf2_ipv6_route_te_policy.json @@ -0,0 +1,70 @@ +{ + "1001:2::/64":[ + { + "prefix":"1001:2::/64", + "prefixLen":64, + "protocol":"bgp", + "vrfId":"*", + "vrfName":"vrf2", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":2, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":"*", + "internalNextHopActiveNum":"*", + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3205, + "ip":"1000::1", + "afi":"ipv6", + "vrf":"default", + "active":true, + "recursive":true, + "labels":"*", + "weight":1, + "srteColor":200, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":[ + "2001:db8:4:4::4", + "2001:db8:1:1::1" + ] + }, + { + "flags":2051, + "fib":true, + "ip":"*", + "afi":"ipv6", + "interfaceIndex":"*", + "interfaceName":"eth1", + "resolver":true, + "vrf":"default", + "active":true, + "labels":"*", + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6localContext":{ + "unknown":true + }, + "seg6":[ + "2001:db8:4:4::4", + "2001:db8:1:1::1" + ] + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/p1/frr.conf b/tests/topotests/bgp_srv6l3vpn_te_policy/p1/frr.conf new file mode 100644 index 000000000000..6f38d125e894 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/p1/frr.conf @@ -0,0 +1,47 @@ +hostname p1 +! +interface lo + ipv6 address 3000::1/64 +! +interface eth1 + ipv6 address 2001:1::2/64 +! +interface eth2 + ipv6 address 2002:1::1/64 +! +interface eth3 + ipv6 address 2002:3::2/64 +! +router bgp 65503 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001:1::1 remote-as 65501 + neighbor 2001:1::1 timers 3 10 + neighbor 2001:1::1 timers connect 1 + neighbor 2002:1::2 remote-as 65502 + neighbor 2002:1::2 timers 3 10 + neighbor 2002:1::2 timers connect 1 + ! + address-family ipv6 unicast + neighbor 2001:1::1 activate + neighbor 2002:1::2 activate + exit-address-family + ! +! +ip forwarding +ipv6 forwarding +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:3:3::/64 + ! + ! +! +ipv6 route 2001:db8:1:1::/64 2001:1::1 +ipv6 route 2001:db8:2:2::/64 2002:1::2 +ipv6 route 2001:db8:4:4::/64 2002:3::1 +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/p2/frr.conf b/tests/topotests/bgp_srv6l3vpn_te_policy/p2/frr.conf new file mode 100644 index 000000000000..4ebe8bdb9def --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/p2/frr.conf @@ -0,0 +1,48 @@ +hostname p2 +! +interface lo + ipv6 address 4000::1/64 +! +interface eth1 + ipv6 address 2001:2::2/64 +! +interface eth2 + ipv6 address 2002:2::1/64 +! +interface eth3 + ipv6 address 2002:3::1/64 +! +router bgp 65504 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001:2::1 remote-as 65501 + neighbor 2001:2::1 timers 3 10 + neighbor 2001:2::1 timers connect 1 + neighbor 2002:2::2 remote-as 65502 + neighbor 2002:2::2 timers 3 10 + neighbor 2002:2::2 timers connect 1 + ! + address-family ipv6 unicast + neighbor 2001:2::1 activate + neighbor 2002:2::2 activate + exit-address-family + ! +! +ip forwarding +ipv6 forwarding +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:4:4::/64 + ! + ! +! +! +ipv6 route 2001:db8:1:1::/64 2001:2::1 +ipv6 route 2001:db8:2:2::/64 2002:2::2 +ipv6 route 2001:db8:3:3::/64 2002:3::2 +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/pe1/frr.conf b/tests/topotests/bgp_srv6l3vpn_te_policy/pe1/frr.conf new file mode 100644 index 000000000000..3ac0240e4a8f --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/pe1/frr.conf @@ -0,0 +1,125 @@ +hostname pe1 +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface lo + ipv6 address 1000::1/64 +! +interface eth1 + ipv6 address 2001:1::1/64 +! +interface eth2 + ipv6 address 2001:2::1/64 +! +interface eth3 vrf vrf1 + ip address 192.168.1.1/24 + ipv6 address 1001:1::1/64 +! +interface eth4 vrf vrf2 + ip address 192.168.2.1/24 + ipv6 address 1001:2::1/64 +! +! +route-map color_vrf1 permit 10 + set extcommunity color 01:100 +! +route-map color_vrf2 permit 10 + set extcommunity color 01:200 +! +router bgp 65501 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001:1::2 remote-as 65503 + neighbor 2001:1::2 timers 3 10 + neighbor 2001:1::2 timers connect 1 + neighbor 2001:2::2 remote-as 65504 + neighbor 2001:2::2 timers 3 10 + neighbor 2001:2::2 timers connect 1 + neighbor 2000::1 remote-as 65502 + neighbor 2000::1 timers 3 10 + neighbor 2000::1 timers connect 1 + neighbor 2000::1 ebgp-multihop 254 + neighbor 2000::1 update-source 1000::1 + neighbor 2000::1 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2000::1 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2000::1 activate + exit-address-family + ! + address-family ipv6 unicast + network 1000::1/64 + neighbor 2001:1::2 activate + neighbor 2001:2::2 activate + exit-address-family + + segment-routing srv6 + locator loc1 + ! +! +router bgp 65501 vrf vrf1 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + sid vpn per-vrf export auto + ! + address-family ipv4 unicast + nexthop vpn export 2001::1 + rd vpn export 1:10 + rt vpn both 99:99 + export vpn + route-map vpn export color_vrf1 + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + rd vpn export 1:10 + rt vpn both 99:99 + export vpn + route-map vpn export color_vrf1 + redistribute connected + exit-address-family +! +router bgp 65501 vrf vrf2 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + sid vpn per-vrf export auto + ! + address-family ipv4 unicast + nexthop vpn export 2001::1 + rd vpn export 1:20 + rt vpn both 88:88 + export vpn + route-map vpn export color_vrf2 + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + rd vpn export 1:20 + rt vpn both 88:88 + export vpn + route-map vpn export color_vrf2 + redistribute connected + exit-address-family +! +ip forwarding +ipv6 forwarding +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 + ! + ! +! +ipv6 route 2001:db8:2:2::/64 2001:1::2 +ipv6 route 2001:db8:2:2::/64 2001:2::2 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/pe2/frr.conf b/tests/topotests/bgp_srv6l3vpn_te_policy/pe2/frr.conf new file mode 100644 index 000000000000..d237f6f07a57 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/pe2/frr.conf @@ -0,0 +1,121 @@ +hostname pe2 +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface lo + ipv6 address 2000::1/64 +! +interface eth1 + ipv6 address 2002:1::2/64 +! +interface eth2 + ipv6 address 2002:2::2/64 +! +interface eth3 vrf vrf1 + ip address 192.168.3.1/24 + ipv6 address 1001:3::1/64 +! +interface eth4 vrf vrf2 + ip address 192.168.4.1/24 + ipv6 address 1001:4::1/64 +! +! +route-map color permit 10 + set extcommunity color 01:200 +! +router bgp 65502 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2002:1::1 remote-as 65503 + neighbor 2002:1::1 timers 3 10 + neighbor 2002:1::1 timers connect 1 + neighbor 2002:2::1 remote-as 65504 + neighbor 2002:2::1 timers 3 10 + neighbor 2002:2::1 timers connect 1 + neighbor 1000::1 remote-as 65501 + neighbor 1000::1 timers 3 10 + neighbor 1000::1 timers connect 1 + neighbor 1000::1 ebgp-multihop 254 + neighbor 1000::1 update-source 2000::1 + neighbor 1000::1 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 1000::1 activate + neighbor 1000::1 route-map color out + exit-address-family + ! + address-family ipv6 vpn + neighbor 1000::1 activate + neighbor 1000::1 route-map color out + exit-address-family + ! + address-family ipv6 unicast + network 2000::1/64 + neighbor 2002:1::1 activate + neighbor 2002:2::1 activate + exit-address-family + + segment-routing srv6 + locator loc1 + ! +! +router bgp 65502 vrf vrf1 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + ! + address-family ipv4 unicast + nexthop vpn export 2001::2 + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + exit-address-family + ! + address-family ipv6 unicast + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + exit-address-family +! +router bgp 65502 vrf vrf2 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + ! + address-family ipv4 unicast + nexthop vpn export 2001::2 + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + exit-address-family + ! + address-family ipv6 unicast + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + exit-address-family +! +ip forwarding +ipv6 forwarding +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:2:2::/64 + ! + ! + traffic-eng + segment-list default + index 10 ipv6-address 2001:db8:4:4::4 + index 20 ipv6-address 2001:db8:1:1::1 + segment-list test + index 10 ipv6-address 2001:db8:3:3::3 + index 20 ipv6-address 2001:db8:1:1::1 +! +ipv6 route 2001:db8:1:1::/64 2002:1::1 +ipv6 route 2001:db8:1:1::/64 2002:2::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_te_policy/test_bgp_srv6l3vpn_te_policy.py b/tests/topotests/bgp_srv6l3vpn_te_policy/test_bgp_srv6l3vpn_te_policy.py new file mode 100644 index 000000000000..b7830e5488fd --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_te_policy/test_bgp_srv6l3vpn_te_policy.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2022, Alibaba Group +# Authored by Guoguo +# + +import os +import sys +import json +import functools +import pytest + +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 required_linux_kernel_version +from lib.checkping import check_ping + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r""" + ┌──────┐ ┌──────┐ ┌──────┐ + │ CE1 ┼─┐ ┌───┤ P1 ┼──┐ ┌─┤ CE3 │ + └──────┘ │ Vrf1 │ └───|──┘ │ │ └──────┘ + │ ┌──────┐ │ | │ ┌──────┐ │ Vrf1 + ┼─┼ PE1 ┼───┤ | ┼────┤ PE2 ┼──┤ + │ └──────┘ │ | │ └──────┘ │ Vrf2 + ┌──────┐ │ Vrf2 │ ┌───|──┐ │ │ ┌──────┐ + │ CE2 ┼─┘ └───┤ P2 ┼──┘ └─┼ CE4 │ + └──────┘ └──────┘ └──────┘ + """ + tgen.add_router("pe1") + tgen.add_router("pe2") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + tgen.add_router("p1") + tgen.add_router("p2") + + tgen.add_link(tgen.gears["pe1"], tgen.gears["p1"], "eth1", "eth1") + tgen.add_link(tgen.gears["pe1"], tgen.gears["p2"], "eth2", "eth1") + tgen.add_link(tgen.gears["pe2"], tgen.gears["p1"], "eth1", "eth2") + tgen.add_link(tgen.gears["pe2"], tgen.gears["p2"], "eth2", "eth2") + tgen.add_link(tgen.gears["ce1"], tgen.gears["pe1"], "eth0", "eth3") + tgen.add_link(tgen.gears["ce2"], tgen.gears["pe1"], "eth0", "eth4") + tgen.add_link(tgen.gears["ce3"], tgen.gears["pe2"], "eth0", "eth3") + tgen.add_link(tgen.gears["ce4"], tgen.gears["pe2"], "eth0", "eth4") + tgen.add_link(tgen.gears["p1"], tgen.gears["p2"], "eth3", "eth3") + +def setup_module(module): + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_frr_config( + os.path.join(CWD, "{}/frr.conf".format(rname)), + [ + (TopoRouter.RD_ZEBRA, None), + (TopoRouter.RD_PATH, None), + (TopoRouter.RD_BGP, None), + (TopoRouter.RD_STATIC, None), + (TopoRouter.RD_MGMTD, None), + ], + ) + tgen.gears["pe1"].run("ip link add vrf1 type vrf table 1") + tgen.gears["pe1"].run("ip link set vrf1 up") + tgen.gears["pe1"].run("ip link add vrf2 type vrf table 2") + tgen.gears["pe1"].run("ip link set vrf2 up") + tgen.gears["pe1"].run("ip link set eth3 master vrf1") + tgen.gears["pe1"].run("ip link set eth4 master vrf2") + + tgen.gears["pe2"].run("ip link add vrf1 type vrf table 1") + tgen.gears["pe2"].run("ip link set vrf1 up") + tgen.gears["pe2"].run("ip link add vrf2 type vrf table 2") + tgen.gears["pe2"].run("ip link set vrf2 up") + tgen.gears["pe2"].run("ip link set eth3 master vrf1") + tgen.gears["pe2"].run("ip link set eth4 master vrf2") + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_rib(name, cmd, expected_file): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + _, result = topotest.run_and_expect(func, None, count=30, wait=0.5) + assert result is None, "Failed" + +def create_srv6_policy(rname, endpoint, color=100): + tgen = get_topogen() + router = tgen.gears[rname] + router.vtysh_cmd( + "configure terminal\nsegment-routing\ntraffic-eng\npolicy color {} endpoint {}".format(color, endpoint) + ) + + +def delete_srv6_policy(rname, endpoint, color=100): + tgen = get_topogen() + router = tgen.gears[rname] + router.vtysh_cmd( + "configure terminal\nsegment-routing\ntraffic-eng\nno policy color {} endpoint {}".format(color, endpoint) + ) + +def add_candidate_path(rname, endpoint, pref, name, segment_list="default", color=100): + tgen = get_topogen() + router = tgen.gears[rname] + router.vtysh_cmd( + "configure terminal\nsegment-routing\ntraffic-eng\npolicy color {} endpoint {}\ncandidate-path preference {} name {} explicit segment-list {}".format( + color, endpoint, pref, name, segment_list) + ) + + +def delete_candidate_path(rname, endpoint, pref, color=100): + tgen = get_topogen() + router = tgen.gears[rname] + router.vtysh_cmd( + "configure terminal\nsegment-routing\ntraffic-eng\npolicy color {} endpoint {}\nno candidate-path preference {}".format(color, endpoint, pref) + ) + +def test_bgp_srv6_ipv4_vpn_route(): + check_rib("pe2", "show bgp ipv4 vpn 192.168.1.0/24 json", "json/vpnv4_vrf1_rib_route.json") + check_rib("pe2", "show bgp ipv4 vpn 192.168.2.0/24 json", "json/vpnv4_vrf2_rib_route.json") + check_rib("pe2", "show ip route vrf vrf1 192.168.1.0/24 json", "json/vrf1_ipv4_route.json") + check_rib("pe2", "show ip route vrf vrf2 192.168.2.0/24 json", "json/vrf2_ipv4_route.json") + + add_candidate_path("pe2", "1000::1", 100, "test", "test", 100) + + add_candidate_path("pe2", "1000::1", 100, "default", "default", 200) + check_rib("pe2", "show ip route vrf vrf1 192.168.1.0/24 json", "json/vrf1_ipv4_route_te_policy.json") + check_rib("pe2", "show ip route vrf vrf2 192.168.2.0/24 json", "json/vrf2_ipv4_route_te_policy.json") + + delete_candidate_path("pe2", "1000::1", 100) + delete_candidate_path("pe2", "1000::1", 100, 200) + check_rib("pe2", "show ip route vrf vrf1 192.168.1.0/24 json", "json/vrf1_ipv4_route.json") + check_rib("pe2", "show ip route vrf vrf2 192.168.2.0/24 json", "json/vrf2_ipv4_route.json") + +def test_bgp_srv6_ipv6_vpn_route(): + check_rib("pe2", "show bgp ipv6 vpn 1001:1::/64 json", "json/vpnv6_vrf1_rib_route.json") + check_rib("pe2", "show bgp ipv6 vpn 1001:2::/64 json", "json/vpnv6_vrf2_rib_route.json") + check_rib("pe2", "show ipv6 route vrf vrf1 1001:1::/64 json", "json/vrf1_ipv6_route.json") + check_rib("pe2", "show ipv6 route vrf vrf2 1001:2::/64 json", "json/vrf2_ipv6_route.json") + + add_candidate_path("pe2", "1000::1", 100, "test", "test", 100) + + add_candidate_path("pe2", "1000::1", 100, "default", "default", 200) + check_rib("pe2", "show ipv6 route vrf vrf1 1001:1::/64 json", "json/vrf1_ipv6_route_te_policy.json") + check_rib("pe2", "show ipv6 route vrf vrf2 1001:2::/64 json", "json/vrf2_ipv6_route_te_policy.json") + + delete_srv6_policy("pe2", "1000::1", 100) + delete_srv6_policy("pe2", "1000::1", 200) + check_rib("pe2", "show ipv6 route vrf vrf1 1001:1::/64 json", "json/vrf1_ipv6_route.json") + check_rib("pe2", "show ipv6 route vrf vrf2 1001:2::/64 json", "json/vrf2_ipv6_route.json") + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/yang/frr-pathd.yang b/yang/frr-pathd.yang index 5beda769c1a3..dc6701d436ca 100644 --- a/yang/frr-pathd.yang +++ b/yang/frr-pathd.yang @@ -89,6 +89,10 @@ module frr-pathd { type rt-types:mpls-label; description "MPLS label value"; } + leaf srv6-sid-value { + type inet:ip-address; + description "SRv6 sid value"; + } container nai { presence "The segment has a Node or Adjacency Identifier"; leaf type { diff --git a/zebra/rib.h b/zebra/rib.h index 8484fe1291a2..3e6869052943 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -22,6 +22,7 @@ #include "mpls.h" #include "srcdest_table.h" #include "zebra/zebra_nhg.h" +#include "srte.h" #ifdef __cplusplus extern "C" { @@ -35,6 +36,10 @@ PREDECL_LIST(rnh_list); /* Nexthop structure. */ struct rnh { + /* For linked list. */ + struct rnh *next; + struct rnh *prev; + uint8_t flags; #define ZEBRA_NHT_CONNECTED 0x1 @@ -64,6 +69,8 @@ struct rnh { int filtered[ZEBRA_ROUTE_MAX]; struct rnh_list_item rnh_list_item; + uint32_t srte_color; + enum zebra_sr_policy_status srp_status; }; #define DISTANCE_INFINITY 255 diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ab55998af046..da7a408c270a 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1233,12 +1233,14 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) struct stream *s; struct prefix p; unsigned short l = 0; - uint8_t connected = 0; - uint8_t resolve_via_default; + bool connected = false; + bool resolve_via_default = false; bool exist; bool flag_changed = false; uint8_t orig_flags; safi_t safi; + uint8_t flags = 0; + uint32_t srte_color = 0; if (IS_ZEBRA_DEBUG_NHT) zlog_debug( @@ -1252,12 +1254,13 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) client->nh_reg_time = monotime(NULL); while (l < hdr->length) { - STREAM_GETC(s, connected); - STREAM_GETC(s, resolve_via_default); + srte_color = 0; + + STREAM_GETL(s, flags); STREAM_GETW(s, safi); STREAM_GETW(s, p.family); STREAM_GETC(s, p.prefixlen); - l += 7; + l += 9; if (p.family == AF_INET) { client->v4_nh_watch_add_cnt++; if (p.prefixlen > IPV4_MAX_BITLEN) { @@ -1285,10 +1288,25 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) p.family); return; } - rnh = zebra_add_rnh(&p, zvrf_id(zvrf), safi, &exist); + if (CHECK_FLAG(flags, NEXTHOP_REGISTER_FLAG_COLOR)) { + STREAM_GETL(s, srte_color); + l += 4; + } + + rnh = zebra_add_rnh(&p, zvrf_id(zvrf), safi, &exist, srte_color); if (!rnh) return; + if (CHECK_FLAG(flags, NEXTHOP_REGISTER_FLAG_CONNECTED)) + connected = true; + else + connected = false; + + if (CHECK_FLAG(flags, NEXTHOP_REGISTER_FLAG_RESOLVE_VIA_DEFAULT)) + resolve_via_default = true; + else + resolve_via_default = false; + orig_flags = rnh->flags; if (connected && !CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) SET_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED); @@ -1322,6 +1340,8 @@ static void zread_rnh_unregister(ZAPI_HANDLER_ARGS) struct prefix p; unsigned short l = 0; safi_t safi; + uint32_t srte_color = 0; + uint8_t flags = 0; if (IS_ZEBRA_DEBUG_NHT) zlog_debug( @@ -1332,19 +1352,17 @@ static void zread_rnh_unregister(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { - uint8_t ignore; + srte_color = 0; - STREAM_GETC(s, ignore); - if (ignore != 0) - goto stream_failure; - STREAM_GETC(s, ignore); - if (ignore != 0) + STREAM_GETL(s, flags); + if (CHECK_FLAG(flags, NEXTHOP_REGISTER_FLAG_CONNECTED) + || CHECK_FLAG(flags, NEXTHOP_REGISTER_FLAG_RESOLVE_VIA_DEFAULT)) goto stream_failure; STREAM_GETW(s, safi); STREAM_GETW(s, p.family); STREAM_GETC(s, p.prefixlen); - l += 7; + l += 9; if (p.family == AF_INET) { client->v4_nh_watch_rem_cnt++; if (p.prefixlen > IPV4_MAX_BITLEN) { @@ -1372,10 +1390,20 @@ static void zread_rnh_unregister(ZAPI_HANDLER_ARGS) p.family); return; } + if (CHECK_FLAG(flags, NEXTHOP_REGISTER_FLAG_COLOR)) { + STREAM_GETL(s, srte_color); + l += 4; + } + rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), safi); + /* check color */ + for (; rnh; rnh = rnh->next) + if (rnh->srte_color == srte_color) + break; + if (rnh) { client->nh_dereg_time = monotime(NULL); - zebra_remove_rnh_client(rnh, client); + zebra_remove_rnh_client(rnh, client, false); } } stream_failure: @@ -1820,6 +1848,8 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, enum lsp_types_t label_type; char nhbuf[NEXTHOP_STRLEN]; char labelbuf[MPLS_LABEL_STRLEN]; + char seg_buf[SRV6_SEG_STRLEN]; + struct seg6_segs segs; struct zapi_nexthop *api_nh = &nhops[i]; /* Convert zapi nexthop */ @@ -1849,6 +1879,7 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, if (CHECK_FLAG(message, ZAPI_MESSAGE_SRTE)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRV6TE); nexthop->srte_color = api_nh->srte_color; } @@ -1885,9 +1916,6 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6) && api_nh->type != NEXTHOP_TYPE_BLACKHOLE) { - if (IS_ZEBRA_DEBUG_RECV) - zlog_debug("%s: adding seg6", __func__); - nexthop_add_srv6_seg6(nexthop, &api_nh->seg6_segs[0], api_nh->seg_num); } @@ -1895,6 +1923,7 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, if (IS_ZEBRA_DEBUG_RECV) { labelbuf[0] = '\0'; nhbuf[0] = '\0'; + seg_buf[0] = '\0'; nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); @@ -1906,8 +1935,20 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, nexthop->nh_label_type, false); } - zlog_debug("%s: nh=%s, vrf_id=%d %s", - __func__, nhbuf, api_nh->vrf_id, labelbuf); + if (nexthop->nh_srv6 && nexthop->nh_srv6->seg6_segs) { + segs.num_segs = nexthop->nh_srv6->seg6_segs->num_segs; + size_t num = 0; + + for (num = 0; num < segs.num_segs; num++) + memcpy(&segs.segs[num], + &nexthop->nh_srv6->seg6_segs->seg[num], + sizeof(struct in6_addr)); + + snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs); + } + + zlog_debug("%s: nh=%s, vrf_id=%d label=%s seg6=%s color=%u", + __func__, nhbuf, api_nh->vrf_id, labelbuf, seg_buf, nexthop->srte_color); } if (ng) { @@ -2138,9 +2179,9 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) vrf_id = zvrf_id(zvrf); if (IS_ZEBRA_DEBUG_RECV) - zlog_debug("%s: p=(%s:%u)%pFX, msg flags=0x%x, flags=0x%x", + zlog_debug("%s: p=(%s:%u)%pFX, msg flags=0x%x, flags=0x%x, color=%u", __func__, zvrf_name(zvrf), api.tableid, &api.prefix, - (int)api.message, api.flags); + (int)api.message, api.flags, api.srte_color); /* Allocate new route. */ re = zebra_rib_route_entry_new( @@ -2684,6 +2725,7 @@ static void zread_sr_policy_set(ZAPI_HANDLER_ARGS) if (!policy) { policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name); policy->sock = client->sock; + policy->type = ZEBRA_SR_POLICY_TYPE_MPLS; } /* TODO: per-VRF list of SR-TE policies. */ policy->zvrf = zvrf; @@ -2719,6 +2761,67 @@ static void zread_sr_policy_delete(ZAPI_HANDLER_ARGS) zebra_sr_policy_del(policy); } +static void zread_srv6_policy_set(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zapi_srv6te_tunnel *zt; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_srv6_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __func__); + return; + } + zt = &zp.segment_list_v6; + if (zt->seg_num < 1) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug( + "%s: SRV6-TE tunnel must contain at least one sid", + __func__); + return; + } + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) { + policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name); + policy->sock = client->sock; + policy->type = ZEBRA_SR_POLICY_TYPE_SRV6; + } + /* TODO: per-VRF list of SR-TE policies. */ + policy->zvrf = zvrf; + + zebra_srv6_policy_validate(policy, &zp.segment_list_v6); +} + +static void zread_srv6_policy_delete(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_srv6_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __func__); + return; + } + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to find SRV6-TE policy", __func__); + return; + } + + zebra_sr_policy_del(policy); +} + int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint, char *name, int status) { @@ -4123,6 +4226,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_TC_CLASS_DELETE] = zread_tc_class, [ZEBRA_TC_FILTER_ADD] = zread_tc_filter, [ZEBRA_TC_FILTER_DELETE] = zread_tc_filter, + [ZEBRA_SRV6_POLICY_SET] = zread_srv6_policy_set, + [ZEBRA_SRV6_POLICY_DELETE] = zread_srv6_policy_delete, }; /* diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index 6ed11f75f173..3dfd89cce648 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -945,7 +945,7 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( { struct nexthop *nexthop = (struct nexthop *)args->list_entry; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE) || CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRV6TE)) return yang_data_new_uint32(args->xpath, nexthop->srte_color); return NULL; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 6a43fe0c84ba..2092388f5da2 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1869,7 +1869,7 @@ static struct nexthop *nexthop_set_resolved(afi_t afi, SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ONLINK); /* Copy labels of the resolved route and the parent resolving to it */ - if (policy) { + if (policy && policy->type == ZEBRA_SR_POLICY_TYPE_MPLS) { int label_num = 0; /* @@ -1936,7 +1936,12 @@ static struct nexthop *nexthop_set_resolved(afi_t afi, nexthop->nh_srv6->seg6_segs ->num_segs); } - + if (policy && policy->type == ZEBRA_SR_POLICY_TYPE_SRV6) { + SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_SRV6_TUNNEL); + nexthop_add_srv6_seg6_ipaddr(resolved_hop, + &policy->segment_list_v6.segs[0], + policy->segment_list_v6.seg_num); + } resolved_hop->rparent = nexthop; _nexthop_add(&nexthop->resolved, resolved_hop); @@ -2225,6 +2230,7 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, struct in_addr local_ipv4; struct in_addr *ipv4; afi_t afi = AFI_IP; + struct zebra_sr_policy *policy = NULL; /* Reset some nexthop attributes that we'll recompute if necessary */ if ((nexthop->type == NEXTHOP_TYPE_IPV4) @@ -2317,7 +2323,6 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, */ if (nexthop->srte_color) { struct ipaddr endpoint = {0}; - struct zebra_sr_policy *policy; switch (afi) { case AFI_IP: @@ -2340,18 +2345,20 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, policy = zebra_sr_policy_find(nexthop->srte_color, &endpoint); if (policy && policy->status == ZEBRA_SR_POLICY_UP) { resolved = 0; - frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, - nhlfe) { - if (!CHECK_FLAG(nhlfe->flags, - NHLFE_FLAG_SELECTED) - || CHECK_FLAG(nhlfe->flags, - NHLFE_FLAG_DELETED)) - continue; - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, nhlfe->nexthop, - nexthop, policy); - resolved = 1; + if (policy->type == ZEBRA_SR_POLICY_TYPE_MPLS) { + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, + nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_DELETED)) + continue; + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, nhlfe->nexthop, + nexthop, policy); + resolved = 1; + } } if (resolved) return 1; @@ -2524,10 +2531,24 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, __func__, match, match->nhe, newhop); - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - resolver = nexthop_set_resolved(afi, newhop, + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + + if (policy && policy->status == ZEBRA_SR_POLICY_UP) { + if (policy->type == ZEBRA_SR_POLICY_TYPE_SRV6) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRV6_TUNNEL); + resolver = nexthop_set_resolved(afi, newhop, + nexthop, policy); + } else { + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRV6_TUNNEL); + resolver = nexthop_set_resolved(afi, newhop, nexthop, NULL); + } + } else { + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRV6_TUNNEL); + resolver = nexthop_set_resolved(afi, newhop, + nexthop, NULL); + } + resolved = 1; /* If there are backup nexthops, capture diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 0226c355c8a9..496504b37a51 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -4136,7 +4136,7 @@ void route_entry_dump_nh(const struct route_entry *re, const char *straddr, if (nexthop->weight) snprintf(wgt_str, sizeof(wgt_str), "wgt %d,", nexthop->weight); - zlog_debug("%s(%s): %s %s[%u] %svrf %s(%u) %s%s with flags %s%s%s%s%s%s%s%s%s", + zlog_debug("%s(%s): %s %s[%u] %svrf %s(%u) %s%s with flags %s%s%s%s%s%s%s%s%s%s", straddr, VRF_LOGNAME(re_vrf), (nexthop->rparent ? " NH" : "NH"), nhname, nexthop->ifindex, label_str, vrf ? vrf->name : "Unknown", nexthop->vrf_id, @@ -4160,6 +4160,8 @@ void route_entry_dump_nh(const struct route_entry *re, const char *straddr, : ""), (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE) ? "SRTE " : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRV6TE) ? "SRv6TE " + : ""), (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_EVPN) ? "EVPN " : "")); } diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 640e6551a774..4b20a3ece25b 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -118,8 +118,33 @@ static void zebra_rnh_store_in_routing_table(struct rnh *rnh) route_unlock_node(rn); } +static void zebra_rnh_info_add(struct route_node *dest, struct rnh *pi) +{ + struct rnh *top; + + top = dest->info; + + pi->next = top; + pi->prev = NULL; + if (top) + top->prev = pi; + dest->info = pi; + + route_lock_node(dest); +} + +static void zebra_rnh_info_del(struct route_node *dest, struct rnh *pi) +{ + if (pi->next) + pi->next->prev = pi->prev; + if (pi->prev) + pi->prev->next = pi->next; + else + dest->info = pi->next; +} + struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi, - bool *exists) + bool *exists, uint32_t srte_color) { struct route_table *table; struct route_node *rn; @@ -129,8 +154,8 @@ struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi, if (IS_ZEBRA_DEBUG_NHT) { struct vrf *vrf = vrf_lookup_by_id(vrfid); - zlog_debug("%s(%u): Add RNH %pFX for safi: %u", - VRF_LOGNAME(vrf), vrfid, p, safi); + zlog_debug("%s(%u): Add RNH %pFX for safi: %u, srte_color: %u", + VRF_LOGNAME(vrf), vrfid, p, safi, srte_color); } table = get_rnh_table(vrfid, afi, safi); @@ -150,7 +175,11 @@ struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi, /* Lookup (or add) route node.*/ rn = route_node_get(table, p); - if (!rn->info) { + for (rnh = rn->info; rnh; rnh = rnh->next) + if (rnh->srte_color == srte_color) + break; + + if (!rnh) { rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh)); /* @@ -167,16 +196,18 @@ struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi, rnh->safi = safi; rnh->zebra_pseudowire_list = list_new(); route_lock_node(rn); - rn->info = rnh; rnh->node = rn; + rnh->srte_color = srte_color; + rnh->srp_status = ZEBRA_SR_POLICY_DOWN; *exists = false; - - zebra_rnh_store_in_routing_table(rnh); + zebra_rnh_info_add(rn, rnh); + if (!srte_color) + zebra_rnh_store_in_routing_table(rnh); } else *exists = true; route_unlock_node(rn); - return (rn->info); + return rnh; } struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi) @@ -247,11 +278,21 @@ static void zebra_delete_rnh(struct rnh *rnh) rnh->vrf_id, rnh->node); } + zebra_rnh_info_del(rn, rnh); zebra_free_rnh(rnh); - rn->info = NULL; route_unlock_node(rn); } +static struct zebra_sr_policy *zebra_sr_policy_find_by_rnh(struct rnh *rnh) +{ + struct ipaddr ip = {0}; + + if (!prefix2ipaddr(&rnh->node->p, &ip)) + return zebra_sr_policy_find(rnh->srte_color, &ip); + + return NULL; +} + /* * This code will send to the registering client * the looked up rnh. @@ -263,6 +304,10 @@ static void zebra_delete_rnh(struct rnh *rnh) void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id) { + struct zebra_sr_policy *policy = NULL; + struct ipaddr ip = {0}; + struct prefix *p = &rnh->node->p; + if (IS_ZEBRA_DEBUG_NHT) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); @@ -277,11 +322,23 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, * We always need to respond with known information, * currently multiple daemons expect this behavior */ - zebra_send_rnh_update(rnh, client, vrf_id, 0); + if (!rnh->srte_color) + zebra_send_rnh_update(rnh, client, vrf_id, 0); + else { + if (!prefix2ipaddr(p, &ip)) { + policy = zebra_sr_policy_find_by_rnh(rnh); + if (policy) + zebra_sr_policy_notify_update(policy); + else + zebra_sr_policy_notify_unknown(rnh, client); + } + } } -void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client) +void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, bool delete_all) { + struct rnh *rnh_next = NULL; + if (IS_ZEBRA_DEBUG_NHT) { struct vrf *vrf = vrf_lookup_by_id(rnh->vrf_id); @@ -289,8 +346,17 @@ void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client) zebra_route_string(client->proto), VRF_LOGNAME(vrf), vrf->vrf_id, rnh->node); } - listnode_delete(rnh->client_list, client); - zebra_delete_rnh(rnh); + if (delete_all) { + while (rnh) { + listnode_delete(rnh->client_list, client); + rnh_next = rnh->next; + zebra_delete_rnh(rnh); + rnh = rnh_next; + } + } else { + listnode_delete(rnh->client_list, client); + zebra_delete_rnh(rnh); + } } /* XXX move this utility function elsewhere? */ @@ -330,7 +396,7 @@ void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw, return; addr2hostprefix(pw->af, &pw->nexthop, &nh); - rnh = zebra_add_rnh(&nh, vrf_id, SAFI_UNICAST, &exists); + rnh = zebra_add_rnh(&nh, vrf_id, SAFI_UNICAST, &exists, 0); if (!rnh) return; @@ -1444,7 +1510,7 @@ static int zebra_cleanup_rnh_client(vrf_id_t vrf_id, afi_t afi, safi_t safi, continue; rnh = nrn->info; - zebra_remove_rnh_client(rnh, client); + zebra_remove_rnh_client(rnh, client, true); } return 1; } diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index f0b10d825c84..7f61a136281b 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -17,7 +17,7 @@ extern "C" { extern void zebra_rnh_init(void); extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi, - bool *exists); + bool *exists, uint32_t srte_color); extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi); extern void zebra_free_rnh(struct rnh *rnh); @@ -27,7 +27,7 @@ extern int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id, uint32_t srte_color); extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *); extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *); -extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client); +extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, bool delete_all); extern void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force, const struct prefix *p, safi_t safi); extern void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, safi_t safi, diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c index bb8d4b3b4077..f0c91a3f7d3a 100644 --- a/zebra/zebra_srte.c +++ b/zebra/zebra_srte.c @@ -8,6 +8,7 @@ #include "lib/zclient.h" #include "lib/lib_errors.h" +#include "zebra/debug.h" #include "zebra/zebra_srte.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_rnh.h" @@ -91,6 +92,7 @@ static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, uint8_t num; struct zapi_nexthop znh; int ret; + struct nexthop nh = {0}; /* Get output stream. */ s = stream_new(ZEBRA_MAX_PACKET_SIZ); @@ -133,28 +135,79 @@ static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, } stream_putl(s, policy->color); + if (IS_ZEBRA_DEBUG_SRV6) { + char endpoint[IPADDR_STRING_SIZE]; + + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug("%s: endpoint %s color %u status %u type %d", + __func__, endpoint, policy->color, policy->status, + policy->type); + } + num = 0; - frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) { - if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) - || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) - continue; + if (policy->type == ZEBRA_SR_POLICY_TYPE_MPLS) { + frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + if (num == 0) { + stream_putc(s, re_type_from_lsp_type(nhlfe->type)); + stream_putw(s, 0); /* instance - not available */ + stream_putc(s, nhlfe->distance); + stream_putl(s, 0); /* metric - not available */ + nump = stream_get_endp(s); + stream_putw(s, 0); + } - if (num == 0) { - stream_putc(s, re_type_from_lsp_type(nhlfe->type)); - stream_putw(s, 0); /* instance - not available */ - stream_putc(s, nhlfe->distance); - stream_putl(s, 0); /* metric - not available */ - nump = stream_get_endp(s); - stream_putw(s, 0); + zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop); + znh.srte_color = policy->color; + ret = zapi_nexthop_encode(s, &znh, 0, message); + if (ret < 0) + goto failure; + + num++; + } + } else if (policy->type == ZEBRA_SR_POLICY_TYPE_SRV6) { + stream_putc(s, ZEBRA_ROUTE_SRTE); + stream_putw(s, 0); /* instance - not available */ + stream_putc(s, 0); + stream_putl(s, 0); /* metric - not available */ + nump = stream_get_endp(s); + stream_putw(s, 0); + + memset(&nh, 0, sizeof(struct nexthop)); + nh.vrf_id = policy->zvrf->vrf->vrf_id; + + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + memcpy(&nh.gate.ipv4, &policy->endpoint.ipaddr_v4, sizeof(struct in_addr)); + nh.type = NEXTHOP_TYPE_IPV4; + break; + case IPADDR_V6: + memcpy(&nh.gate.ipv6, &policy->endpoint.ipaddr_v6, sizeof(struct in6_addr)); + nh.type = NEXTHOP_TYPE_IPV6; + break; + case IPADDR_NONE: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); } + nexthop_add_srv6_seg6_ipaddr(&nh, + &policy->segment_list_v6.segs[0], + policy->segment_list_v6.seg_num); - zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop); + nh.srte_color = policy->color; + zapi_nexthop_from_nexthop(&znh, &nh); + nexthop_del_srv6_seg6(&nh); ret = zapi_nexthop_encode(s, &znh, 0, message); if (ret < 0) goto failure; num++; } + stream_putw_at(s, nump, num); stream_putw_at(s, 0, stream_get_endp(s)); @@ -167,9 +220,9 @@ static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, return -1; } -static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy) +void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy) { - struct rnh *rnh; + struct rnh *rnh = NULL; struct prefix p = {}; struct zebra_vrf *zvrf; struct listnode *node; @@ -195,19 +248,100 @@ static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy) } rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), SAFI_UNICAST); - if (!rnh) + if (rnh == NULL) return; + if (policy->type == ZEBRA_SR_POLICY_TYPE_SRV6) { + /* check color */ + for (; rnh; rnh = rnh->next) + if (rnh->srte_color == policy->color) + break; + if (rnh == NULL) + return; + rnh->srp_status = policy->status; + } + + if (IS_ZEBRA_DEBUG_SRV6) { + char endpoint[IPADDR_STRING_SIZE]; + + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug("%s: endpoint %s color %u status %u", + __func__, endpoint, policy->color, policy->status); + } + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { if (policy->status == ZEBRA_SR_POLICY_UP) zebra_sr_policy_notify_update_client(policy, client); - else - /* Fallback to the IGP shortest path. */ - zebra_send_rnh_update(rnh, client, zvrf_id(zvrf), - policy->color); + else { + if (policy->type == ZEBRA_SR_POLICY_TYPE_SRV6) + zebra_sr_policy_notify_unknown(rnh, client); + else + /* Fallback to the IGP shortest path. */ + zebra_send_rnh_update(rnh, client, zvrf_id(zvrf), + policy->color); + } } } +int zebra_sr_policy_notify_unknown(struct rnh *rnh, + struct zserv *client) +{ + struct stream *s; + uint32_t message = 0; + struct route_node *rn; + + rn = rnh->node; + + /* Get output stream. */ + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, rnh->vrf_id); + + /* Message flags. */ + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + stream_putw(s, rnh->safi); + + switch (rn->p.family) { + case AF_INET: + stream_putw(s, AF_INET); + stream_putc(s, IPV4_MAX_BITLEN); + stream_put_in_addr(s, &rn->p.u.prefix4); + stream_putw(s, AF_INET); + stream_putc(s, IPV4_MAX_BITLEN); + stream_put_in_addr(s, &rn->p.u.prefix4); + break; + case AF_INET6: + stream_putw(s, AF_INET6); + stream_putc(s, IPV6_MAX_BITLEN); + stream_put(s, &rn->p.u.prefix6, IPV6_MAX_BYTELEN); + stream_putw(s, AF_INET6); + stream_putc(s, IPV6_MAX_BITLEN); + stream_put(s, &rn->p.u.prefix6, IPV6_MAX_BYTELEN); + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, rn->p.family); + exit(1); + } + + stream_putl(s, rnh->srte_color); + + stream_putc(s, ZEBRA_ROUTE_SRTE); + stream_putw(s, 0); /* instance - not available */ + stream_putc(s, 0);/* distance - not available */ + stream_putl(s, 0); /* metric - not available */ + /*set nexthop num to 0 */ + stream_putw(s, 0); + + stream_putw_at(s, 0, stream_get_endp(s)); + client->nh_last_upd_time = monotime(NULL); + client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE; + return zserv_send_message(client, s); + +} + static void zebra_sr_policy_activate(struct zebra_sr_policy *policy, struct zebra_lsp *lsp) { @@ -254,10 +388,14 @@ static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy) { policy->status = ZEBRA_SR_POLICY_DOWN; policy->lsp = NULL; - zebra_sr_policy_bsid_uninstall(policy, - policy->segment_list.local_label); - zsend_sr_policy_notify_status(policy->color, &policy->endpoint, - policy->name, ZEBRA_SR_POLICY_DOWN); + + if (policy->type == ZEBRA_SR_POLICY_TYPE_MPLS) { + zebra_sr_policy_bsid_uninstall(policy, + policy->segment_list.local_label); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_DOWN); + } + zebra_sr_policy_notify_update(policy); } @@ -288,6 +426,31 @@ int zebra_sr_policy_validate(struct zebra_sr_policy *policy, return 0; } +int zebra_srv6_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srv6te_tunnel *new_tunnel) +{ + bool segment_list_changed = false; + struct zapi_srv6te_tunnel old_tunnel = policy->segment_list_v6; + + if (new_tunnel) + policy->segment_list_v6 = *new_tunnel; + + /* First label was resolved successfully. */ + if (policy->status == ZEBRA_SR_POLICY_DOWN) { + policy->status = ZEBRA_SR_POLICY_UP; + segment_list_changed = true; + } else { + segment_list_changed = + policy->segment_list_v6.seg_num != old_tunnel.seg_num + || memcmp(&policy->segment_list_v6.segs[0], &old_tunnel.segs[0], + sizeof(struct ipaddr) * policy->segment_list_v6.seg_num); + } + + if (segment_list_changed) + zebra_sr_policy_notify_update(policy); + + return 0; +} int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy) { struct zapi_srte_tunnel *zt = &policy->segment_list; diff --git a/zebra/zebra_srte.h b/zebra/zebra_srte.h index 1cdee454048a..bedd3d511b9a 100644 --- a/zebra/zebra_srte.h +++ b/zebra/zebra_srte.h @@ -21,13 +21,21 @@ enum zebra_sr_policy_update_label_mode { ZEBRA_SR_POLICY_LABEL_REMOVED = 3, }; +enum zebra_sr_policy_type { + ZEBRA_SR_POLICY_TYPE_UNDEFINED = 0, + ZEBRA_SR_POLICY_TYPE_MPLS = 1, + ZEBRA_SR_POLICY_TYPE_SRV6 = 2, +}; + struct zebra_sr_policy { RB_ENTRY(zebra_sr_policy) entry; uint32_t color; struct ipaddr endpoint; char name[SRTE_POLICY_NAME_MAX_LENGTH]; enum zebra_sr_policy_status status; + enum zebra_sr_policy_type type; struct zapi_srte_tunnel segment_list; + struct zapi_srv6te_tunnel segment_list_v6; struct zebra_lsp *lsp; struct zebra_vrf *zvrf; int sock; @@ -46,13 +54,16 @@ struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name); int zebra_sr_policy_validate(struct zebra_sr_policy *policy, struct zapi_srte_tunnel *new_tunnel); +int zebra_srv6_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srv6te_tunnel *new_tunnel); int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy); void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, mpls_label_t old_bsid); void zebra_srte_init(void); int zebra_sr_policy_label_update(mpls_label_t label, enum zebra_sr_policy_update_label_mode mode); - +extern void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy); +extern int zebra_sr_policy_notify_unknown(struct rnh *rnh, struct zserv *client); #ifdef __cplusplus } #endif diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 582d15627c37..7f0ab26eab72 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -201,7 +201,10 @@ static void show_nexthop_detail_helper(struct vty *vty, bool is_backup) { char buf[MPLS_LABEL_STRLEN]; + char seg_buf[SRV6_SEG_STRLEN]; + struct seg6_segs segs; int i; + size_t num = 0; if (is_backup) vty_out(vty, " b%s", @@ -317,6 +320,38 @@ static void show_nexthop_detail_helper(struct vty *vty, 1 /*pretty*/)); } + if (nexthop->nh_srv6) { + seg6local_context2str(buf, sizeof(buf), + &nexthop->nh_srv6->seg6local_ctx, + nexthop->nh_srv6->seg6local_action); + if (nexthop->nh_srv6->seg6local_action != + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) + vty_out(vty, ", seg6local %s %s", + seg6local_action2str( + nexthop->nh_srv6->seg6local_action), + buf); + if (nexthop->nh_srv6->seg6_segs && + IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs->seg[0], + &in6addr_any)) { + segs.num_segs = nexthop->nh_srv6->seg6_segs->num_segs; + for (num = 0; num < segs.num_segs; num++) + memcpy(&segs.segs[num], + &nexthop->nh_srv6->seg6_segs->seg[num], + sizeof(struct in6_addr)); + snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs); + vty_out(vty, ", seg6 %s", seg_buf); + } + } + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRV6_TUNNEL)) { + if (inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, INET6_ADDRSTRLEN)) + vty_out(vty, ", srv6(endpoint|color):%s|%u", + buf, + nexthop->srte_color); + else + vty_out(vty, ", srv6(endpoint|color):unknown"); + } + if (nexthop->weight) vty_out(vty, ", weight %u", nexthop->weight);