diff options
author | Russ White <russ@riw.us> | 2023-09-21 05:09:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-21 05:09:35 +0200 |
commit | 90d19d1489c7237acaad7dfa79af2080301ad60d (patch) | |
tree | 59d903742e50c7d36edf22cfcd98cff736fbf063 | |
parent | Merge pull request #14452 from opensourcerouting/fix/coverity_issues (diff) | |
parent | tests: add srv6 multiple segs SIDs topotest (diff) | |
download | frr-90d19d1489c7237acaad7dfa79af2080301ad60d.tar.xz frr-90d19d1489c7237acaad7dfa79af2080301ad60d.zip |
Merge pull request #14089 from dmytroshytyi-6WIND/srv6_multiple_segs_sids
bgpd,doc,lib,sharpd,staticd,yang,zebra: SRv6 multiple segs SIDs
30 files changed, 1000 insertions, 214 deletions
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 3d993e12c..9e02f1b55 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1557,17 +1557,17 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, api_nh->weight = nh_weight; if (((mpinfo->attr->srv6_l3vpn && - !sid_zero(&mpinfo->attr->srv6_l3vpn->sid)) || + !sid_zero_ipv6(&mpinfo->attr->srv6_l3vpn->sid)) || (mpinfo->attr->srv6_vpn && - !sid_zero(&mpinfo->attr->srv6_vpn->sid))) && + !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, sid_tmp, - sizeof(api_nh->seg6_segs)); + 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) { @@ -1581,13 +1581,14 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, continue; } - transpose_sid(&api_nh->seg6_segs, nh_label, + 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); } @@ -1704,7 +1705,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, 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, + inet_ntop(AF_INET6, &api_nh->seg6_segs[0], sid_buf, sizeof(sid_buf)); snprintf(segs_buf, sizeof(segs_buf), "segs %s", sid_buf); diff --git a/doc/user/static.rst b/doc/user/static.rst index 6d8aca97b..d40527657 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -164,3 +164,23 @@ network 9.9.9.9/24: .. code-block:: frr ip route 9.9.9.9/24 6.6.6.6 color 123 + +SRv6 Route Commands +==================== + +It is possible to specify a static route for ipv6 prefixes using an SRv6 +`segments` instruction. The `/` separator can be used to specify +multiple segments instructions. + +.. code-block:: frr + + ipv6 route X:X::X:X <X:X::X:X|nexthop> segments U:U::U:U/Y:Y::Y:Y/Z:Z::Z:Z + + +:: + + router(config)# ipv6 route 2005::1/64 ens3 segments 2001:db8:aaaa::7/2002::4/2002::3/2002::2 + + router# show ipv6 route + [..] + S>* 2005::/64 [1/0] is directly connected, ens3, seg6 2001:db8:aaaa::7,2002::4,2002::3,2002::2, weight 1, 00:00:06 diff --git a/include/linux/seg6.h b/include/linux/seg6.h index 329163e4a..5b572ba3d 100644 --- a/include/linux/seg6.h +++ b/include/linux/seg6.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +// SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note /* * SR-IPv6 implementation * @@ -30,7 +30,7 @@ struct ipv6_sr_hdr { __u8 flags; __u16 tag; - struct in6_addr segments[0]; + struct in6_addr segments[]; }; #define SR6_FLAG1_PROTECTED (1 << 6) diff --git a/lib/nexthop.c b/lib/nexthop.c index 4f92ef9c8..8df57e36a 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -56,6 +56,7 @@ static int _nexthop_srv6_cmp(const struct nexthop *nh1, const struct nexthop *nh2) { int ret = 0; + int i = 0; if (!nh1->nh_srv6 && !nh2->nh_srv6) return 0; @@ -78,9 +79,26 @@ static int _nexthop_srv6_cmp(const struct nexthop *nh1, if (ret != 0) return ret; - ret = memcmp(&nh1->nh_srv6->seg6_segs, - &nh2->nh_srv6->seg6_segs, - sizeof(struct in6_addr)); + if (!nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs) + return 0; + + if (!nh1->nh_srv6->seg6_segs && nh2->nh_srv6->seg6_segs) + return -1; + + if (nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs) + return 1; + + if (nh1->nh_srv6->seg6_segs->num_segs != + nh2->nh_srv6->seg6_segs->num_segs) + return -1; + + for (i = 0; i < nh1->nh_srv6->seg6_segs->num_segs; i++) { + ret = memcmp(&nh1->nh_srv6->seg6_segs->seg[i], + &nh2->nh_srv6->seg6_segs->seg[i], + sizeof(struct in6_addr)); + if (ret != 0) + break; + } return ret; } @@ -362,7 +380,7 @@ struct nexthop *nexthop_new(void) * The linux kernel does some weird stuff with adding +1 to * all nexthop weights it gets over netlink. * To handle this, just default everything to 1 right from - * from the beginning so we don't have to special case + * the beginning so we don't have to special case * default weights in the linux netlink code. * * 1 should be a valid on all platforms anyway. @@ -568,15 +586,25 @@ void nexthop_del_srv6_seg6local(struct nexthop *nexthop) if (!nexthop->nh_srv6) return; + if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) + return; + nexthop->nh_srv6->seg6local_action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; - if (sid_zero(&nexthop->nh_srv6->seg6_segs)) + if (nexthop->nh_srv6->seg6_segs && + (nexthop->nh_srv6->seg6_segs->num_segs == 0 || + sid_zero(nexthop->nh_srv6->seg6_segs))) + XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6->seg6_segs); + + if (nexthop->nh_srv6->seg6_segs == NULL) XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); } -void nexthop_add_srv6_seg6(struct nexthop *nexthop, - const struct in6_addr *segs) +void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *segs, + int num_segs) { + int i; + if (!segs) return; @@ -584,7 +612,22 @@ void nexthop_add_srv6_seg6(struct nexthop *nexthop, nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6, sizeof(struct nexthop_srv6)); - nexthop->nh_srv6->seg6_segs = *segs; + /* 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], + sizeof(struct in6_addr)); } void nexthop_del_srv6_seg6(struct nexthop *nexthop) @@ -592,12 +635,14 @@ void nexthop_del_srv6_seg6(struct nexthop *nexthop) if (!nexthop->nh_srv6) return; - memset(&nexthop->nh_srv6->seg6_segs, 0, - sizeof(nexthop->nh_srv6->seg6_segs)); - - if (nexthop->nh_srv6->seg6local_action == - ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) - XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); + if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC && + nexthop->nh_srv6->seg6_segs) { + memset(nexthop->nh_srv6->seg6_segs->seg, 0, + sizeof(struct in6_addr) * + nexthop->nh_srv6->seg6_segs->num_segs); + XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6->seg6_segs); + } + XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); } const char *nexthop2str(const struct nexthop *nexthop, char *str, int size) @@ -743,11 +788,32 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop) } if (nexthop->nh_srv6) { - key = jhash_1word(nexthop->nh_srv6->seg6local_action, key); - key = jhash(&nexthop->nh_srv6->seg6local_ctx, - sizeof(nexthop->nh_srv6->seg6local_ctx), key); - key = jhash(&nexthop->nh_srv6->seg6_segs, - sizeof(nexthop->nh_srv6->seg6_segs), key); + int segs_num = 0; + int i = 0; + + if (nexthop->nh_srv6->seg6local_action != + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) { + key = jhash_1word(nexthop->nh_srv6->seg6local_action, + key); + key = jhash(&nexthop->nh_srv6->seg6local_ctx, + sizeof(nexthop->nh_srv6->seg6local_ctx), + key); + if (nexthop->nh_srv6->seg6_segs) + key = jhash(&nexthop->nh_srv6->seg6_segs->seg[0], + sizeof(struct in6_addr), key); + } else { + if (nexthop->nh_srv6->seg6_segs) { + segs_num = nexthop->nh_srv6->seg6_segs->num_segs; + while (segs_num >= 1) { + key = jhash(&nexthop->nh_srv6->seg6_segs + ->seg[i], + sizeof(struct in6_addr), + key); + segs_num -= 1; + i += 1; + } + } + } } return key; @@ -810,9 +876,13 @@ void nexthop_copy_no_recurse(struct nexthop *copy, nexthop_add_srv6_seg6local(copy, nexthop->nh_srv6->seg6local_action, &nexthop->nh_srv6->seg6local_ctx); - if (!sid_zero(&nexthop->nh_srv6->seg6_segs)) + if (nexthop->nh_srv6->seg6_segs && + nexthop->nh_srv6->seg6_segs->num_segs && + !sid_zero(nexthop->nh_srv6->seg6_segs)) nexthop_add_srv6_seg6(copy, - &nexthop->nh_srv6->seg6_segs); + &nexthop->nh_srv6->seg6_segs->seg[0], + nexthop->nh_srv6->seg6_segs + ->num_segs); } } diff --git a/lib/nexthop.h b/lib/nexthop.h index 2be89f824..bed6447d4 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -157,8 +157,8 @@ void nexthop_del_labels(struct nexthop *); void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action, const struct seg6local_context *ctx); void nexthop_del_srv6_seg6local(struct nexthop *nexthop); -void nexthop_add_srv6_seg6(struct nexthop *nexthop, - const struct in6_addr *segs); +void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *seg, + int num_segs); void nexthop_del_srv6_seg6(struct nexthop *nexthop); /* diff --git a/lib/srv6.h b/lib/srv6.h index 7c8c6b4a6..fb34f861c 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -14,8 +14,11 @@ #include <arpa/inet.h> #include <netinet/in.h> -#define SRV6_MAX_SIDS 16 +#define SRV6_MAX_SIDS 16 +#define SRV6_MAX_SEGS 8 #define SRV6_LOCNAME_SIZE 256 +#define SRH_BASE_HEADER_LENGTH 8 +#define SRH_SEGMENT_LENGTH 16 #ifdef __cplusplus extern "C" { @@ -74,6 +77,8 @@ enum seg6local_flavor_op { ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID = 4, }; +#define SRV6_SEG_STRLEN 1024 + struct seg6_segs { size_t num_segs; struct in6_addr segs[256]; @@ -89,6 +94,11 @@ struct seg6local_flavors_info { uint8_t lcnode_func_len; }; +struct seg6_seg_stack { + uint8_t num_segs; + struct in6_addr seg[0]; /* 1 or more segs */ +}; + struct seg6local_context { struct in_addr nh4; struct in6_addr nh6; @@ -170,7 +180,7 @@ struct nexthop_srv6 { struct seg6local_context seg6local_ctx; /* SRv6 Headend-behaviour */ - struct in6_addr seg6_segs; + struct seg6_seg_stack *seg6_segs; }; static inline const char *seg6_mode2str(enum seg6_mode_t mode) @@ -206,12 +216,21 @@ static inline bool sid_diff( return !sid_same(a, b); } -static inline bool sid_zero( - const struct in6_addr *a) + +static inline bool sid_zero(const struct seg6_seg_stack *a) +{ + struct in6_addr zero = {}; + + assert(a); + + return sid_same(&a->seg[0], &zero); +} + +static inline bool sid_zero_ipv6(const struct in6_addr *a) { struct in6_addr zero = {}; - return sid_same(a, &zero); + return sid_same(&a[0], &zero); } static inline void *sid_copy(struct in6_addr *dst, diff --git a/lib/zclient.c b/lib/zclient.c index 68a342982..f8f9cf7ab 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1061,10 +1061,11 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, sizeof(struct seg6local_context)); } - if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6)) - stream_write(s, &api_nh->seg6_segs, - sizeof(struct in6_addr)); - + if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6)) { + stream_putc(s, api_nh->seg_num); + stream_put(s, &api_nh->seg6_segs[0], + api_nh->seg_num * sizeof(struct in6_addr)); + } done: return ret; } @@ -1430,9 +1431,18 @@ int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, sizeof(struct seg6local_context)); } - if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)) - STREAM_GET(&api_nh->seg6_segs, s, - sizeof(struct in6_addr)); + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)) { + STREAM_GETC(s, api_nh->seg_num); + if (api_nh->seg_num > SRV6_MAX_SIDS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: invalid number of SRv6 Segs (%u)", + __func__, api_nh->seg_num); + return -1; + } + + STREAM_GET(&api_nh->seg6_segs[0], s, + api_nh->seg_num * sizeof(struct in6_addr)); + } /* Success */ ret = 0; @@ -2132,8 +2142,8 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) nexthop_add_srv6_seg6local(n, znh->seg6local_action, &znh->seg6local_ctx); - if (!sid_zero(&znh->seg6_segs)) - nexthop_add_srv6_seg6(n, &znh->seg6_segs); + if (znh->seg_num && !sid_zero_ipv6(znh->seg6_segs)) + nexthop_add_srv6_seg6(n, &znh->seg6_segs[0], znh->seg_num); return n; } @@ -2193,10 +2203,14 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, sizeof(struct seg6local_context)); } - if (!sid_zero(&nh->nh_srv6->seg6_segs)) { + if (nh->nh_srv6->seg6_segs && nh->nh_srv6->seg6_segs->num_segs && + !sid_zero(nh->nh_srv6->seg6_segs)) { SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6); - memcpy(&znh->seg6_segs, &nh->nh_srv6->seg6_segs, - sizeof(struct in6_addr)); + znh->seg_num = nh->nh_srv6->seg6_segs->num_segs; + for (i = 0; i < nh->nh_srv6->seg6_segs->num_segs; i++) + memcpy(&znh->seg6_segs[i], + &nh->nh_srv6->seg6_segs->seg[i], + sizeof(struct in6_addr)); } } diff --git a/lib/zclient.h b/lib/zclient.h index 42c5a5fda..2a3ce4e48 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -438,7 +438,8 @@ struct zapi_nexthop { struct seg6local_context seg6local_ctx; /* SRv6 Headend-behaviour */ - struct in6_addr seg6_segs; + int seg_num; + struct in6_addr seg6_segs[SRV6_MAX_SEGS]; }; /* diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index f0a75a5fc..e891c1b6b 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -402,7 +402,7 @@ DEFPY (install_seg6_routes, sg.r.nhop.gate.ipv6 = seg6_nh6; sg.r.nhop.vrf_id = vrf->vrf_id; sg.r.nhop_group.nexthop = &sg.r.nhop; - nexthop_add_srv6_seg6(&sg.r.nhop, &seg6_seg); + nexthop_add_srv6_seg6(&sg.r.nhop, &seg6_seg, 1); sg.r.vrf_id = vrf->vrf_id; sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, 0, diff --git a/staticd/static_nb.c b/staticd/static_nb.c index d8b5b167c..1c69a5803 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -75,6 +75,21 @@ const struct frr_yang_module_info frr_staticd_info = { } }, { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy, + + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy, + } + }, + { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, @@ -183,6 +198,20 @@ const struct frr_yang_module_info frr_staticd_info = { } }, { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy, + } + }, + { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, diff --git a/staticd/static_nb.h b/staticd/static_nb.h index ce3644076..9f80653b7 100644 --- a/staticd/static_nb.h +++ b/staticd/static_nb.h @@ -35,6 +35,14 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa struct nb_cb_modify_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy( struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( + struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( @@ -80,6 +88,14 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr struct nb_cb_modify_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( + struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( @@ -147,6 +163,10 @@ int routing_control_plane_protocols_name_validate( #define FRR_STATIC_ROUTE_NHLB_KEY_XPATH "/entry[id='%u']/label" +#define FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH "/srv6-segs-stack" + +#define FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH "/entry[id='%u']/seg" + /* route-list/srclist */ #define FRR_S_ROUTE_SRC_INFO_KEY_XPATH \ "/frr-routing:routing/control-plane-protocols/" \ diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index 520a3ee2c..ede2e3875 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -209,6 +209,98 @@ static bool static_nexthop_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +static int nexthop_srv6_segs_stack_entry_create(struct nb_cb_create_args *args) +{ + struct static_nexthop *nh; + uint32_t pos; + uint8_t index; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + nh = nb_running_get_entry(args->dnode, NULL, true); + pos = yang_get_list_pos(args->dnode); + if (!pos) { + flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, + "libyang returns invalid seg position"); + return NB_ERR; + } + /* Mapping to array = list-index -1 */ + index = pos - 1; + memset(&nh->snh_seg.seg[index], 0, sizeof(struct in6_addr)); + nh->snh_seg.num_segs++; + break; + } + + return NB_OK; +} + +static int nexthop_srv6_segs_stack_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct static_nexthop *nh; + uint32_t pos; + uint8_t index; + int old_num_segs; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + nh = nb_running_get_entry(args->dnode, NULL, true); + pos = yang_get_list_pos(args->dnode); + if (!pos) { + flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, + "libyang returns invalid seg position"); + return NB_ERR; + } + index = pos - 1; + old_num_segs = nh->snh_seg.num_segs; + memset(&nh->snh_seg.seg[index], 0, sizeof(struct in6_addr)); + nh->snh_seg.num_segs--; + + if (old_num_segs != nh->snh_seg.num_segs) + nh->state = STATIC_START; + break; + } + + return NB_OK; +} + +static int static_nexthop_srv6_segs_modify(struct nb_cb_modify_args *args) +{ + struct static_nexthop *nh; + uint32_t pos; + uint8_t index; + struct in6_addr old_seg; + struct in6_addr cli_seg; + + nh = nb_running_get_entry(args->dnode, NULL, true); + pos = yang_get_list_pos(lyd_parent(args->dnode)); + if (!pos) { + flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, + "libyang returns invalid seg position"); + return NB_ERR; + } + /* Mapping to array = list-index -1 */ + index = pos - 1; + + old_seg = nh->snh_seg.seg[index]; + yang_dnode_get_ipv6(&cli_seg, args->dnode, NULL); + + memcpy(&nh->snh_seg.seg[index], &cli_seg, sizeof(struct in6_addr)); + + if (memcmp(&old_seg, &nh->snh_seg.seg[index], + sizeof(struct in6_addr)) != 0) + nh->state = STATIC_START; + + return NB_OK; +} + static int nexthop_mpls_label_stack_entry_create(struct nb_cb_create_args *args) { struct static_nexthop *nh; @@ -643,6 +735,60 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa /* * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( + struct nb_cb_create_args *args) +{ + return nexthop_srv6_segs_stack_entry_create(args); +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( + struct nb_cb_destroy_args *args) +{ + return nexthop_srv6_segs_stack_entry_destroy(args); +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_srv6_segs_modify(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( + struct nb_cb_destroy_args *args) +{ + /* + * No operation is required in this call back. + * nexthop_srv6_segs_stack_entry_destroy() will take care + * to reset the seg vaue. + */ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + return NB_OK; +} + +/* + * XPath: * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry */ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( @@ -1021,6 +1167,60 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr /* * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( + struct nb_cb_create_args *args) +{ + return nexthop_srv6_segs_stack_entry_create(args); +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( + struct nb_cb_destroy_args *args) +{ + return nexthop_srv6_segs_stack_entry_destroy(args); +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_srv6_segs_modify(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( + struct nb_cb_destroy_args *args) +{ + /* + * No operation is required in this call back. + * nexthop_mpls_seg_stack_entry_destroy() will take care + * to reset the seg vaue. + */ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + return NB_OK; +} + +/* + * XPath: * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry */ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 3be6fd6b1..548148b18 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -9,6 +9,7 @@ #include "lib/bfd.h" #include "lib/mpls.h" +#include "lib/srv6.h" #include "table.h" #include "memory.h" @@ -27,6 +28,12 @@ struct static_nh_label { mpls_label_t label[MPLS_MAX_LABELS]; }; +/* Static route seg information */ +struct static_nh_seg { + int num_segs; + struct in6_addr seg[SRV6_MAX_SIDS]; +}; + enum static_blackhole_type { STATIC_BLACKHOLE_DROP = 0, STATIC_BLACKHOLE_NULL, @@ -129,6 +136,9 @@ struct static_nexthop { /* Label information */ struct static_nh_label snh_label; + /* SRv6 Seg information */ + struct static_nh_seg snh_seg; + /* * Whether to pretend the nexthop is directly attached to the specified * link. Only meaningful when both a gateway address and interface name diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 16e4cb7d8..b07878f06 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -47,6 +47,7 @@ struct static_route_args { const char *source; const char *gateway; const char *interface_name; + const char *segs; const char *flag; const char *tag; const char *distance; @@ -73,12 +74,16 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) char xpath_nexthop[XPATH_MAXLEN]; char xpath_mpls[XPATH_MAXLEN]; char xpath_label[XPATH_MAXLEN]; + char xpath_segs[XPATH_MAXLEN]; + char xpath_seg[XPATH_MAXLEN]; char ab_xpath[XPATH_MAXLEN]; char buf_prefix[PREFIX_STRLEN]; char buf_src_prefix[PREFIX_STRLEN] = {}; char buf_nh_type[PREFIX_STRLEN] = {}; char buf_tag[PREFIX_STRLEN]; uint8_t label_stack_id = 0; + uint8_t segs_stack_id = 0; + const char *buf_gate_str; uint8_t distance = ZEBRA_STATIC_DISTANCE_DEFAULT; route_tag_t tag = 0; @@ -345,7 +350,39 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY, NULL); } + if (args->segs) { + /* copy of seg string (start) */ + char *ostr; + /* pointer to next segment */ + char *nump; + strlcpy(xpath_segs, xpath_nexthop, sizeof(xpath_segs)); + strlcat(xpath_segs, FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH, + sizeof(xpath_segs)); + + nb_cli_enqueue_change(vty, xpath_segs, NB_OP_DESTROY, + NULL); + + ostr = XSTRDUP(MTYPE_TMP, args->segs); + while ((nump = strsep(&ostr, "/")) != NULL) { + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH, + segs_stack_id); + strlcpy(xpath_seg, xpath_segs, + sizeof(xpath_seg)); + strlcat(xpath_seg, ab_xpath, sizeof(xpath_seg)); + nb_cli_enqueue_change(vty, xpath_seg, + NB_OP_MODIFY, nump); + segs_stack_id++; + } + XFREE(MTYPE_TMP, ostr); + } else { + strlcpy(xpath_segs, xpath_nexthop, sizeof(xpath_segs)); + strlcat(xpath_segs, FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH, + sizeof(xpath_segs)); + nb_cli_enqueue_change(vty, xpath_segs, NB_OP_DESTROY, + NULL); + } if (args->bfd) { char xpath_bfd[XPATH_MAXLEN]; @@ -951,9 +988,8 @@ DEFPY_YANG(ipv6_route_blackhole_vrf, return static_route_nb_run(vty, &args); } -DEFPY_YANG(ipv6_route_address_interface, - ipv6_route_address_interface_cmd, - "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ +DEFPY_YANG(ipv6_route_address_interface, ipv6_route_address_interface_cmd, + "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ X:X::X:X$gate \ <INTERFACE|Null0>$ifname \ [{ \ @@ -966,33 +1002,28 @@ DEFPY_YANG(ipv6_route_address_interface, |onlink$onlink \ |color (1-4294967295) \ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ + |segments WORD \ }]", - NO_STR - IPV6_STR - "Establish static routes\n" - "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" - "IPv6 source-dest route\n" - "IPv6 source prefix\n" - "IPv6 gateway address\n" - "IPv6 gateway interface name\n" - "Null interface\n" - "Set tag for this route\n" - "Tag value\n" - "Distance value for this prefix\n" - VRF_CMD_HELP_STR - MPLS_LABEL_HELPSTR - "Table to configure\n" - "The table number to configure\n" - VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface\n" - "SR-TE color\n" - "The SR-TE color to configure\n" - BFD_INTEGRATION_STR - BFD_INTEGRATION_MULTI_HOP_STR - BFD_INTEGRATION_SOURCE_STR - BFD_INTEGRATION_SOURCEV4_STR - BFD_PROFILE_STR - BFD_PROFILE_NAME_STR) + NO_STR IPV6_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 source-dest route\n" + "IPv6 source prefix\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR + "Table to configure\n" + "The table number to configure\n" VRF_CMD_HELP_STR + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n" BFD_INTEGRATION_STR + BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR + BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR + BFD_PROFILE_NAME_STR "Value of segs\n" + "Segs (SIDs)\n") { struct static_route_args args = { .delete = !!no, @@ -1014,14 +1045,15 @@ DEFPY_YANG(ipv6_route_address_interface, .bfd_multi_hop = !!bfd_multi_hop, .bfd_source = bfd_source_str, .bfd_profile = bfd_profile, + .segs = segments, }; return static_route_nb_run(vty, &args); } DEFPY_YANG(ipv6_route_address_interface_vrf, - ipv6_route_address_interface_vrf_cmd, - "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ + ipv6_route_address_interface_vrf_cmd, + "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ X:X::X:X$gate \ <INTERFACE|Null0>$ifname \ [{ \ @@ -1033,32 +1065,28 @@ DEFPY_YANG(ipv6_route_address_interface_vrf, |onlink$onlink \ |color (1-4294967295) \ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ + |segments WORD \ }]", - NO_STR - IPV6_STR - "Establish static routes\n" - "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" - "IPv6 source-dest route\n" - "IPv6 source prefix\n" - "IPv6 gateway address\n" - "IPv6 gateway interface name\n" - "Null interface\n" - "Set tag for this route\n" - "Tag value\n" - "Distance value for this prefix\n" - MPLS_LABEL_HELPSTR - "Table to configure\n" - "The table number to configure\n" - VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface\n" - "SR-TE color\n" - "The SR-TE color to configure\n" - BFD_INTEGRATION_STR - BFD_INTEGRATION_MULTI_HOP_STR - BFD_INTEGRATION_SOURCE_STR - BFD_INTEGRATION_SOURCEV4_STR - BFD_PROFILE_STR - BFD_PROFILE_NAME_STR) + NO_STR IPV6_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 source-dest route\n" + "IPv6 source prefix\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" MPLS_LABEL_HELPSTR + "Table to configure\n" + "The table number to configure\n" VRF_CMD_HELP_STR + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n" BFD_INTEGRATION_STR + BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR + BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR + BFD_PROFILE_NAME_STR "Value of segs\n" + "Segs (SIDs)\n") { struct static_route_args args = { .delete = !!no, @@ -1080,14 +1108,14 @@ DEFPY_YANG(ipv6_route_address_interface_vrf, .bfd_multi_hop = !!bfd_multi_hop, .bfd_source = bfd_source_str, .bfd_profile = bfd_profile, + .segs = segments, }; return static_route_nb_run(vty, &args); } -DEFPY_YANG(ipv6_route, - ipv6_route_cmd, - "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ +DEFPY_YANG(ipv6_route, ipv6_route_cmd, + "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ <X:X::X:X$gate|<INTERFACE|Null0>$ifname> \ [{ \ tag (1-4294967295) \ @@ -1098,32 +1126,26 @@ DEFPY_YANG(ipv6_route, |nexthop-vrf NAME \ |color (1-4294967295) \ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ + |segments WORD \ }]", - NO_STR - IPV6_STR - "Establish static routes\n" - "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" - "IPv6 source-dest route\n" - "IPv6 source prefix\n" - "IPv6 gateway address\n" - "IPv6 gateway interface name\n" - "Null interface\n" - "Set tag for this route\n" - "Tag value\n" - "Distance value for this prefix\n" - VRF_CMD_HELP_STR - MPLS_LABEL_HELPSTR - "Table to configure\n" - "The table number to configure\n" - VRF_CMD_HELP_STR - "SR-TE color\n" - "The SR-TE color to configure\n" - BFD_INTEGRATION_STR - BFD_INTEGRATION_MULTI_HOP_STR - BFD_INTEGRATION_SOURCE_STR - BFD_INTEGRATION_SOURCEV4_STR - BFD_PROFILE_STR - BFD_PROFILE_NAME_STR) + NO_STR IPV6_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 source-dest route\n" + "IPv6 source prefix\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR + "Table to configure\n" + "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n" + "The SR-TE color to configure\n" BFD_INTEGRATION_STR + BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR + BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR + BFD_PROFILE_NAME_STR "Value of segs\n" + "Segs (SIDs)\n") { struct static_route_args args = { .delete = !!no, @@ -1144,14 +1166,15 @@ DEFPY_YANG(ipv6_route, .bfd_multi_hop = !!bfd_multi_hop, .bfd_source = bfd_source_str, .bfd_profile = bfd_profile, + .segs = segments, + }; return static_route_nb_run(vty, &args); } -DEFPY_YANG(ipv6_route_vrf, - ipv6_route_vrf_cmd, - "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ +DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd, + "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ <X:X::X:X$gate|<INTERFACE|Null0>$ifname> \ [{ \ tag (1-4294967295) \ @@ -1161,31 +1184,26 @@ DEFPY_YANG(ipv6_route_vrf, |nexthop-vrf NAME \ |color (1-4294967295) \ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ + |segments WORD \ }]", - NO_STR - IPV6_STR - "Establish static routes\n" - "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" - "IPv6 source-dest route\n" - "IPv6 source prefix\n" - "IPv6 gateway address\n" - "IPv6 gateway interface name\n" - "Null interface\n" - "Set tag for this route\n" - "Tag value\n" - "Distance value for this prefix\n" - MPLS_LABEL_HELPSTR - "Table to configure\n" - "The table number to configure\n" - VRF_CMD_HELP_STR - "SR-TE color\n" - "The SR-TE color to configure\n" - BFD_INTEGRATION_STR - BFD_INTEGRATION_MULTI_HOP_STR - BFD_INTEGRATION_SOURCE_STR - BFD_INTEGRATION_SOURCEV4_STR - BFD_PROFILE_STR - BFD_PROFILE_NAME_STR) + NO_STR IPV6_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 source-dest route\n" + "IPv6 source prefix\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" MPLS_LABEL_HELPSTR + "Table to configure\n" + "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n" + "The SR-TE color to configure\n" BFD_INTEGRATION_STR + BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR + BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR + BFD_PROFILE_NAME_STR "Value of segs\n" + "Segs (SIDs)\n") { struct static_route_args args = { .delete = !!no, @@ -1206,6 +1224,7 @@ DEFPY_YANG(ipv6_route_vrf, .bfd_multi_hop = !!bfd_multi_hop, .bfd_source = bfd_source_str, .bfd_profile = bfd_profile, + .segs = segments, }; return static_route_nb_run(vty, &args); @@ -1252,6 +1271,39 @@ static int mpls_label_iter_cb(const struct lyd_node *dnode, void *arg) return YANG_ITER_CONTINUE; } +struct srv6_seg_iter { + struct vty *vty; + bool first; +}; + +static int srv6_seg_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct srv6_seg_iter *iter = arg; + char buffer[INET6_ADDRSTRLEN]; + struct in6_addr cli_seg; + + if (yang_dnode_exists(dnode, "./seg")) { + if (iter->first) { + yang_dnode_get_ipv6(&cli_seg, dnode, "./seg"); + if (inet_ntop(AF_INET6, &cli_seg, buffer, + INET6_ADDRSTRLEN) == NULL) { + return 1; + } + vty_out(iter->vty, " segments %s", buffer); + } else { + yang_dnode_get_ipv6(&cli_seg, dnode, "./seg"); + if (inet_ntop(AF_INET6, &cli_seg, buffer, + INET6_ADDRSTRLEN) == NULL) { + return 1; + } + vty_out(iter->vty, "/%s", buffer); + } + iter->first = false; + } + + return YANG_ITER_CONTINUE; +} + static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, const struct lyd_node *src, const struct lyd_node *path, @@ -1266,6 +1318,7 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, uint32_t tag; uint8_t distance; struct mpls_label_iter iter; + struct srv6_seg_iter seg_iter; const char *nexthop_vrf; uint32_t table_id; bool onlink; @@ -1342,6 +1395,11 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, yang_dnode_iterate(mpls_label_iter_cb, &iter, nexthop, "./mpls-label-stack/entry"); + seg_iter.vty = vty; + seg_iter.first = true; + yang_dnode_iterate(srv6_seg_iter_cb, &seg_iter, nexthop, + "./srv6-segs-stack/entry"); + nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf"); if (strcmp(vrf, nexthop_vrf)) vty_out(vty, " nexthop-vrf %s", nexthop_vrf); diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 4f3ccde53..6abbdadc0 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -499,6 +499,21 @@ extern void static_zebra_route_add(struct static_path *pn, bool install) for (i = 0; i < api_nh->label_num; i++) api_nh->labels[i] = nh->snh_label.label[i]; } + if (nh->snh_seg.num_segs) { + int i; + + api_nh->seg6local_action = + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + api.safi = SAFI_UNICAST; + + api_nh->seg_num = nh->snh_seg.num_segs; + for (i = 0; i < api_nh->seg_num; i++) + memcpy(&api_nh->seg6_segs[i], + &nh->snh_seg.seg[i], + sizeof(struct in6_addr)); + } nh_num++; } diff --git a/tests/topotests/srv6_static_route/__init__.py b/tests/topotests/srv6_static_route/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/srv6_static_route/__init__.py diff --git a/tests/topotests/srv6_static_route/expected_srv6_route.json b/tests/topotests/srv6_static_route/expected_srv6_route.json new file mode 100644 index 000000000..45de6171f --- /dev/null +++ b/tests/topotests/srv6_static_route/expected_srv6_route.json @@ -0,0 +1,49 @@ +{ + "2001:db8:aaaa::/64": [ + { + "prefix": "2001:db8:aaaa::/64", + "prefixLen": 64, + "protocol": "static", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "nexthops": [ + { + "directlyConnected": true, + "active": true, + "weight": 1 + } + ] + } + ], + "2005::/64": [ + { + "prefix": "2005::/64", + "prefixLen": 64, + "protocol": "static", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "nexthops": [ + { + "directlyConnected": true, + "active": true, + "weight": 1, + "seg6local": { + "action": "unspec" + }, + "seg6": [ + "2001:db8:aaaa::7", + "2002::2", + "2003::3", + "2004::4" + ] + } + ] + } + ] +} diff --git a/tests/topotests/srv6_static_route/r1/mgmtd.conf b/tests/topotests/srv6_static_route/r1/mgmtd.conf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/srv6_static_route/r1/mgmtd.conf diff --git a/tests/topotests/srv6_static_route/r1/setup.sh b/tests/topotests/srv6_static_route/r1/setup.sh new file mode 100644 index 000000000..36ed713f2 --- /dev/null +++ b/tests/topotests/srv6_static_route/r1/setup.sh @@ -0,0 +1,2 @@ +ip link add dummy0 type dummy +ip link set dummy0 up diff --git a/tests/topotests/srv6_static_route/r1/staticd.conf b/tests/topotests/srv6_static_route/r1/staticd.conf new file mode 100644 index 000000000..a75c69f26 --- /dev/null +++ b/tests/topotests/srv6_static_route/r1/staticd.conf @@ -0,0 +1,9 @@ +hostname r1 +! +log stdout notifications +log monitor notifications +log commands +log file staticd.log debugging +! +ipv6 route 2001:db8:aaaa::/64 dummy0 +ipv6 route 2005::/64 dummy0 segments 2001:db8:aaaa::7/2002::2/2003::3/2004::4 diff --git a/tests/topotests/srv6_static_route/r1/zebra.conf b/tests/topotests/srv6_static_route/r1/zebra.conf new file mode 100644 index 000000000..cc7041860 --- /dev/null +++ b/tests/topotests/srv6_static_route/r1/zebra.conf @@ -0,0 +1,10 @@ +hostname r1 +! +! debug zebra events +! debug zebra rib detailed +! +log stdout notifications +log monitor notifications +log commands +log file zebra.log debugging +! diff --git a/tests/topotests/srv6_static_route/test_srv6_route.py b/tests/topotests/srv6_static_route/test_srv6_route.py new file mode 100755 index 000000000..7a4cd39f2 --- /dev/null +++ b/tests/topotests/srv6_static_route/test_srv6_route.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_srv6_route.py +# +# Copyright 2023 6WIND S.A. +# Dmytro Shytyi <dmytro.shytyi@6wind.com> +# + +""" +test_srv6_route.py: +Test for SRv6 static route on zebra +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd] + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_MGMTD, os.path.join(CWD, "{}/mgmtd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_srv6_static_route(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + def _check_srv6_static_route(router, expected_route_file): + logger.info("checking zebra srv6 static route with multiple segs status") + output = json.loads(router.vtysh_cmd("show ipv6 route static json")) + expected = open_json_file("{}/{}".format(CWD, expected_route_file)) + return topotest.json_cmp(output, expected) + + def check_srv6_static_route(router, expected_file): + func = functools.partial(_check_srv6_static_route, router, expected_file) + success, result = topotest.run_and_expect(func, None, count=15, wait=1) + assert result is None, "Failed" + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Test for srv6 route configuration") + check_srv6_static_route(router, "expected_srv6_route.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/yang/frr-nexthop.yang b/yang/frr-nexthop.yang index d73fdb8bd..175487d78 100644 --- a/yang/frr-nexthop.yang +++ b/yang/frr-nexthop.yang @@ -201,6 +201,11 @@ module frr-nexthop { description "Nexthop's MPLS label stack."; } + + uses srv6-segs-stack { + description + "Nexthop's SRv6 segs SIDs stack."; + } } /* @@ -298,6 +303,32 @@ module frr-nexthop { } } + /* Contaner for SRv6 segs SIDs */ + grouping srv6-segs-stack { + description + "This grouping specifies an SRv6 segs SIDs stack. The segs + SIDs stack is encoded as a list of SID entries. The + list key is an identifier that indicates the relative + ordering of each entry."; + container srv6-segs-stack { + description + "Container for a list of SRv6 segs SIDs entries."; + list entry { + key "id"; + description + "List of SRv6 segs SIDs entries."; + leaf id { + type uint8; + description + "Identifies the entry in a sequence of SRv6 segs SIDs + entries."; + } + leaf seg { + type inet:ipv6-address; + } + } + } + } container frr-nexthop-group { description "A nexthop-group, represented as a list of nexthop objects."; diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 9d8ef00b5..bfe1910a5 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -66,6 +66,7 @@ #include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_trace.h" #include "zebra/zebra_neigh.h" +#include "lib/srv6.h" #ifndef AF_MPLS #define AF_MPLS 28 @@ -77,6 +78,8 @@ #define BR_SPH_LIST_SIZE 10 #endif +DEFINE_MTYPE_STATIC(LIB, NH_SRV6, "Nexthop srv6"); + static vlanid_t filter_vlan = 0; /* We capture whether the current kernel supports nexthop ids; by @@ -476,19 +479,19 @@ static int parse_encap_seg6(struct rtattr *tb, struct in6_addr *segs) { struct rtattr *tb_encap[SEG6_IPTUNNEL_MAX + 1] = {}; struct seg6_iptunnel_encap *ipt = NULL; - struct in6_addr *segments = NULL; + int i; netlink_parse_rtattr_nested(tb_encap, SEG6_IPTUNNEL_MAX, tb); - /* - * TODO: It's not support multiple SID list. - */ if (tb_encap[SEG6_IPTUNNEL_SRH]) { ipt = (struct seg6_iptunnel_encap *) RTA_DATA(tb_encap[SEG6_IPTUNNEL_SRH]); - segments = ipt->srh[0].segments; - *segs = segments[0]; - return 1; + + for (i = ipt->srh[0].first_segment; i >= 0; i--) + memcpy(&segs[i], &ipt->srh[0].segments[i], + sizeof(struct in6_addr)); + + return ipt->srh[0].first_segment + 1; } return 0; @@ -506,7 +509,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, int num_labels = 0; enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; struct seg6local_context seg6l_ctx = {}; - struct in6_addr seg6_segs = {}; + struct in6_addr segs[SRV6_MAX_SIDS] = {}; int num_segs = 0; vrf_id_t nh_vrf_id = vrf_id; @@ -555,7 +558,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE] && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE]) == LWTUNNEL_ENCAP_SEG6) { - num_segs = parse_encap_seg6(tb[RTA_ENCAP], &seg6_segs); + num_segs = parse_encap_seg6(tb[RTA_ENCAP], segs); } if (rtm->rtm_flags & RTNH_F_ONLINK) @@ -581,7 +584,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, nexthop_add_srv6_seg6local(&nh, seg6l_act, &seg6l_ctx); if (num_segs) - nexthop_add_srv6_seg6(&nh, &seg6_segs); + nexthop_add_srv6_seg6(&nh, segs, num_segs); return nh; } @@ -601,7 +604,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, int num_labels = 0; enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; struct seg6local_context seg6l_ctx = {}; - struct in6_addr seg6_segs = {}; + struct in6_addr segs[SRV6_MAX_SIDS] = {}; int num_segs = 0; struct rtattr *rtnh_tb[RTA_MAX + 1] = {}; @@ -657,7 +660,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE]) == LWTUNNEL_ENCAP_SEG6) { num_segs = parse_encap_seg6(rtnh_tb[RTA_ENCAP], - &seg6_segs); + segs); } } @@ -700,7 +703,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, &seg6l_ctx); if (num_segs) - nexthop_add_srv6_seg6(nh, &seg6_segs); + nexthop_add_srv6_seg6(nh, segs, num_segs); if (rtnh->rtnh_flags & RTNH_F_ONLINK) SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK); @@ -1514,37 +1517,40 @@ static bool _netlink_route_encode_nexthop_src(const struct nexthop *nexthop, } static ssize_t fill_seg6ipt_encap(char *buffer, size_t buflen, - const struct in6_addr *seg) + struct seg6_seg_stack *segs) { struct seg6_iptunnel_encap *ipt; struct ipv6_sr_hdr *srh; - const size_t srhlen = 24; + size_t srhlen; + int i; - /* - * Caution: Support only SINGLE-SID, not MULTI-SID - * This function only supports the case where segs represents - * a single SID. If you want to extend the SRv6 functionality, - * you should improve the Boundary Check. - * Ex. In case of set a SID-List include multiple-SIDs as an - * argument of the Transit Behavior, we must support variable - * boundary check for buflen. - */ - if (buflen < (sizeof(struct seg6_iptunnel_encap) + - sizeof(struct ipv6_sr_hdr) + 16)) + if (segs->num_segs > SRV6_MAX_SEGS) { + /* Exceeding maximum supported SIDs */ + return -1; + } + + srhlen = SRH_BASE_HEADER_LENGTH + SRH_SEGMENT_LENGTH * segs->num_segs; + + if (buflen < (sizeof(struct seg6_iptunnel_encap) + srhlen)) return -1; memset(buffer, 0, buflen); ipt = (struct seg6_iptunnel_encap *)buffer; ipt->mode = SEG6_IPTUN_MODE_ENCAP; - srh = ipt->srh; + + srh = (struct ipv6_sr_hdr *)&ipt->srh; srh->hdrlen = (srhlen >> 3) - 1; srh->type = 4; - srh->segments_left = 0; - srh->first_segment = 0; - memcpy(&srh->segments[0], seg, sizeof(struct in6_addr)); + srh->segments_left = segs->num_segs - 1; + srh->first_segment = segs->num_segs - 1; + + for (i = 0; i < segs->num_segs; i++) { + memcpy(&srh->segments[i], &segs->seg[i], + sizeof(struct in6_addr)); + } - return srhlen + 4; + return sizeof(struct seg6_iptunnel_encap) + srhlen; } static bool @@ -1726,7 +1732,9 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, nl_attr_nest_end(nlmsg, nest); } - if (!sid_zero(&nexthop->nh_srv6->seg6_segs)) { + if (nexthop->nh_srv6->seg6_segs && + nexthop->nh_srv6->seg6_segs->num_segs && + !sid_zero(nexthop->nh_srv6->seg6_segs)) { char tun_buf[4096]; ssize_t tun_len; struct rtattr *nest; @@ -1737,8 +1745,9 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, nest = nl_attr_nest(nlmsg, req_size, RTA_ENCAP); if (!nest) return false; - tun_len = fill_seg6ipt_encap(tun_buf, sizeof(tun_buf), - &nexthop->nh_srv6->seg6_segs); + tun_len = + fill_seg6ipt_encap(tun_buf, sizeof(tun_buf), + nexthop->nh_srv6->seg6_segs); if (tun_len < 0) return false; if (!nl_attr_put(nlmsg, req_size, SEG6_IPTUNNEL_SRH, @@ -2971,7 +2980,9 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, nl_attr_nest_end(&req->n, nest); } - if (!sid_zero(&nh->nh_srv6->seg6_segs)) { + if (nh->nh_srv6->seg6_segs && + nh->nh_srv6->seg6_segs->num_segs && + !sid_zero(nh->nh_srv6->seg6_segs)) { char tun_buf[4096]; ssize_t tun_len; struct rtattr *nest; @@ -2984,9 +2995,9 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, NHA_ENCAP | NLA_F_NESTED); if (!nest) return 0; - tun_len = fill_seg6ipt_encap(tun_buf, - sizeof(tun_buf), - &nh->nh_srv6->seg6_segs); + tun_len = fill_seg6ipt_encap( + tun_buf, sizeof(tun_buf), + nh->nh_srv6->seg6_segs); if (tun_len < 0) return 0; if (!nl_attr_put(&req->n, buflen, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 6bed6d872..8a480cfa1 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1794,7 +1794,8 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, if (IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: adding seg6", __func__); - nexthop_add_srv6_seg6(nexthop, &api_nh->seg6_segs); + nexthop_add_srv6_seg6(nexthop, &api_nh->seg6_segs[0], + api_nh->seg_num); } if (IS_ZEBRA_DEBUG_RECV) { diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c index a0ef08273..a93dbbb00 100644 --- a/zebra/zebra_nb.c +++ b/zebra/zebra_nb.c @@ -598,6 +598,28 @@ const struct frr_yang_module_info frr_zebra_info = { .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem, } }, + + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry", + .cbs = { + .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_next, + .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_keys, + .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_lookup_entry, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry/id", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_id_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry/seg", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_seg_get_elem, + } + }, + { .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry", .cbs = { diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h index a066a7f9d..80d2aaa6f 100644 --- a/zebra/zebra_nb.h +++ b/zebra/zebra_nb.h @@ -240,6 +240,20 @@ struct yang_data * lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( struct nb_cb_get_elem_args *args); const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_next( + struct nb_cb_get_next_args *args); +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_keys( + struct nb_cb_get_keys_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_id_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_seg_get_elem( + struct nb_cb_get_elem_args *args); +const void * lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next( struct nb_cb_get_next_args *args); int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys( diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index acf0b80ac..ba537475c 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -842,6 +842,58 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( /* * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry + */ +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_next( + struct nb_cb_get_next_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_keys( + struct nb_cb_get_keys_args *args) +{ + /* TODO: implement me. */ + return NB_OK; +} + +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_lookup_entry( + struct nb_cb_lookup_entry_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry/id + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_id_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry/seg + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_seg_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + + +/* + * XPath: * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry */ const void * diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 3a56bf2a5..9588f65fc 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1868,11 +1868,18 @@ static struct nexthop *nexthop_set_resolved(afi_t afi, labels); if (nexthop->nh_srv6) { - nexthop_add_srv6_seg6local(resolved_hop, - nexthop->nh_srv6->seg6local_action, - &nexthop->nh_srv6->seg6local_ctx); - nexthop_add_srv6_seg6(resolved_hop, - &nexthop->nh_srv6->seg6_segs); + if (nexthop->nh_srv6->seg6local_action != + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) + nexthop_add_srv6_seg6local(resolved_hop, + nexthop->nh_srv6 + ->seg6local_action, + &nexthop->nh_srv6 + ->seg6local_ctx); + if (nexthop->nh_srv6->seg6_segs) + nexthop_add_srv6_seg6(resolved_hop, + &nexthop->nh_srv6->seg6_segs->seg[0], + nexthop->nh_srv6->seg6_segs + ->num_segs); } resolved_hop->rparent = nexthop; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 28b83ce8b..30d92c30f 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1268,6 +1268,7 @@ void show_nexthop_json_helper(json_object *json_nexthop, json_object *json_backups = NULL; json_object *json_seg6local = NULL; json_object *json_seg6 = NULL; + json_object *json_segs = NULL; int i; json_object_int_add(json_nexthop, "flags", nexthop->flags); @@ -1425,11 +1426,31 @@ void show_nexthop_json_helper(json_object *json_nexthop, nexthop->nh_srv6->seg6local_action)); json_object_object_add(json_nexthop, "seg6local", json_seg6local); - - json_seg6 = json_object_new_object(); - json_object_string_addf(json_seg6, "segs", "%pI6", - &nexthop->nh_srv6->seg6_segs); - json_object_object_add(json_nexthop, "seg6", json_seg6); + if (nexthop->nh_srv6->seg6_segs && + nexthop->nh_srv6->seg6_segs->num_segs == 1) { + json_seg6 = json_object_new_object(); + json_object_string_addf(json_seg6, "segs", "%pI6", + &nexthop->nh_srv6->seg6_segs + ->seg[0]); + json_object_object_add(json_nexthop, "seg6", json_seg6); + } else { + json_segs = json_object_new_array(); + if (nexthop->nh_srv6->seg6_segs) { + for (int seg_idx = 0; + seg_idx < + nexthop->nh_srv6->seg6_segs->num_segs; + seg_idx++) + json_object_array_add( + json_segs, + json_object_new_stringf( + "%pI6", + &nexthop->nh_srv6 + ->seg6_segs + ->seg[seg_idx])); + json_object_object_add(json_nexthop, "seg6", + json_segs); + } + } } } @@ -1440,7 +1461,9 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re, const struct nexthop *nexthop) { char buf[MPLS_LABEL_STRLEN]; - int i; + char seg_buf[SRV6_SEG_STRLEN]; + struct seg6_segs segs; + uint8_t i; switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -1538,9 +1561,17 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re, seg6local_action2str( nexthop->nh_srv6->seg6local_action), buf); - if (IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs, &in6addr_any)) - vty_out(vty, ", seg6 %pI6", - &nexthop->nh_srv6->seg6_segs); + 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 (i = 0; i < segs.num_segs; i++) + memcpy(&segs.segs[i], + &nexthop->nh_srv6->seg6_segs->seg[i], + sizeof(struct in6_addr)); + snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs); + vty_out(vty, ", seg6 %s", seg_buf); + } } if (nexthop->weight) |