Merge pull request #14089 from dmytroshytyi-6WIND/srv6_multiple_segs_sids

bgpd,doc,lib,sharpd,staticd,yang,zebra: SRv6 multiple segs SIDs
This commit is contained in:
Russ White 2023-09-20 23:09:35 -04:00 committed by GitHub
commit 90d19d1489
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1000 additions and 214 deletions

View File

@ -1557,17 +1557,17 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
api_nh->weight = nh_weight; api_nh->weight = nh_weight;
if (((mpinfo->attr->srv6_l3vpn && if (((mpinfo->attr->srv6_l3vpn &&
!sid_zero(&mpinfo->attr->srv6_l3vpn->sid)) || !sid_zero_ipv6(&mpinfo->attr->srv6_l3vpn->sid)) ||
(mpinfo->attr->srv6_vpn && (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])) { !is_evpn && bgp_is_valid_label(&labels[0])) {
struct in6_addr *sid_tmp = struct in6_addr *sid_tmp =
mpinfo->attr->srv6_l3vpn mpinfo->attr->srv6_l3vpn
? (&mpinfo->attr->srv6_l3vpn->sid) ? (&mpinfo->attr->srv6_l3vpn->sid)
: (&mpinfo->attr->srv6_vpn->sid); : (&mpinfo->attr->srv6_vpn->sid);
memcpy(&api_nh->seg6_segs, sid_tmp, memcpy(&api_nh->seg6_segs[0], sid_tmp,
sizeof(api_nh->seg6_segs)); sizeof(api_nh->seg6_segs[0]));
if (mpinfo->attr->srv6_l3vpn && if (mpinfo->attr->srv6_l3vpn &&
mpinfo->attr->srv6_l3vpn->transposition_len != 0) { mpinfo->attr->srv6_l3vpn->transposition_len != 0) {
@ -1581,13 +1581,14 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
continue; continue;
} }
transpose_sid(&api_nh->seg6_segs, nh_label, transpose_sid(&api_nh->seg6_segs[0], nh_label,
mpinfo->attr->srv6_l3vpn mpinfo->attr->srv6_l3vpn
->transposition_offset, ->transposition_offset,
mpinfo->attr->srv6_l3vpn mpinfo->attr->srv6_l3vpn
->transposition_len); ->transposition_len);
} }
api_nh->seg_num = 1;
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6); 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) && if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6) &&
!CHECK_FLAG(api_nh->flags, !CHECK_FLAG(api_nh->flags,
ZAPI_NEXTHOP_FLAG_EVPN)) { 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)); sid_buf, sizeof(sid_buf));
snprintf(segs_buf, sizeof(segs_buf), "segs %s", snprintf(segs_buf, sizeof(segs_buf), "segs %s",
sid_buf); sid_buf);

View File

@ -164,3 +164,23 @@ network 9.9.9.9/24:
.. code-block:: frr .. code-block:: frr
ip route 9.9.9.9/24 6.6.6.6 color 123 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

View File

@ -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 * SR-IPv6 implementation
* *
@ -30,7 +30,7 @@ struct ipv6_sr_hdr {
__u8 flags; __u8 flags;
__u16 tag; __u16 tag;
struct in6_addr segments[0]; struct in6_addr segments[];
}; };
#define SR6_FLAG1_PROTECTED (1 << 6) #define SR6_FLAG1_PROTECTED (1 << 6)

View File

@ -56,6 +56,7 @@ static int _nexthop_srv6_cmp(const struct nexthop *nh1,
const struct nexthop *nh2) const struct nexthop *nh2)
{ {
int ret = 0; int ret = 0;
int i = 0;
if (!nh1->nh_srv6 && !nh2->nh_srv6) if (!nh1->nh_srv6 && !nh2->nh_srv6)
return 0; return 0;
@ -78,9 +79,26 @@ static int _nexthop_srv6_cmp(const struct nexthop *nh1,
if (ret != 0) if (ret != 0)
return ret; return ret;
ret = memcmp(&nh1->nh_srv6->seg6_segs, if (!nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs)
&nh2->nh_srv6->seg6_segs, return 0;
sizeof(struct in6_addr));
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; return ret;
} }
@ -362,7 +380,7 @@ struct nexthop *nexthop_new(void)
* The linux kernel does some weird stuff with adding +1 to * The linux kernel does some weird stuff with adding +1 to
* all nexthop weights it gets over netlink. * all nexthop weights it gets over netlink.
* To handle this, just default everything to 1 right from * 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. * default weights in the linux netlink code.
* *
* 1 should be a valid on all platforms anyway. * 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) if (!nexthop->nh_srv6)
return; return;
if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
return;
nexthop->nh_srv6->seg6local_action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; 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); XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
} }
void nexthop_add_srv6_seg6(struct nexthop *nexthop, void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *segs,
const struct in6_addr *segs) int num_segs)
{ {
int i;
if (!segs) if (!segs)
return; return;
@ -584,7 +612,22 @@ void nexthop_add_srv6_seg6(struct nexthop *nexthop,
nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6, nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6,
sizeof(struct nexthop_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) void nexthop_del_srv6_seg6(struct nexthop *nexthop)
@ -592,12 +635,14 @@ void nexthop_del_srv6_seg6(struct nexthop *nexthop)
if (!nexthop->nh_srv6) if (!nexthop->nh_srv6)
return; return;
memset(&nexthop->nh_srv6->seg6_segs, 0, if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC &&
sizeof(nexthop->nh_srv6->seg6_segs)); nexthop->nh_srv6->seg6_segs) {
memset(nexthop->nh_srv6->seg6_segs->seg, 0,
if (nexthop->nh_srv6->seg6local_action == sizeof(struct in6_addr) *
ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) nexthop->nh_srv6->seg6_segs->num_segs);
XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); 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) 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) { if (nexthop->nh_srv6) {
key = jhash_1word(nexthop->nh_srv6->seg6local_action, key); int segs_num = 0;
key = jhash(&nexthop->nh_srv6->seg6local_ctx, int i = 0;
sizeof(nexthop->nh_srv6->seg6local_ctx), key);
key = jhash(&nexthop->nh_srv6->seg6_segs, if (nexthop->nh_srv6->seg6local_action !=
sizeof(nexthop->nh_srv6->seg6_segs), key); 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; return key;
@ -810,9 +876,13 @@ void nexthop_copy_no_recurse(struct nexthop *copy,
nexthop_add_srv6_seg6local(copy, nexthop_add_srv6_seg6local(copy,
nexthop->nh_srv6->seg6local_action, nexthop->nh_srv6->seg6local_action,
&nexthop->nh_srv6->seg6local_ctx); &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_add_srv6_seg6(copy,
&nexthop->nh_srv6->seg6_segs); &nexthop->nh_srv6->seg6_segs->seg[0],
nexthop->nh_srv6->seg6_segs
->num_segs);
} }
} }

View File

@ -157,8 +157,8 @@ void nexthop_del_labels(struct nexthop *);
void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action, void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action,
const struct seg6local_context *ctx); const struct seg6local_context *ctx);
void nexthop_del_srv6_seg6local(struct nexthop *nexthop); void nexthop_del_srv6_seg6local(struct nexthop *nexthop);
void nexthop_add_srv6_seg6(struct nexthop *nexthop, void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *seg,
const struct in6_addr *segs); int num_segs);
void nexthop_del_srv6_seg6(struct nexthop *nexthop); void nexthop_del_srv6_seg6(struct nexthop *nexthop);
/* /*

View File

@ -14,8 +14,11 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.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 SRV6_LOCNAME_SIZE 256
#define SRH_BASE_HEADER_LENGTH 8
#define SRH_SEGMENT_LENGTH 16
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -74,6 +77,8 @@ enum seg6local_flavor_op {
ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID = 4, ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID = 4,
}; };
#define SRV6_SEG_STRLEN 1024
struct seg6_segs { struct seg6_segs {
size_t num_segs; size_t num_segs;
struct in6_addr segs[256]; struct in6_addr segs[256];
@ -89,6 +94,11 @@ struct seg6local_flavors_info {
uint8_t lcnode_func_len; 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 seg6local_context {
struct in_addr nh4; struct in_addr nh4;
struct in6_addr nh6; struct in6_addr nh6;
@ -170,7 +180,7 @@ struct nexthop_srv6 {
struct seg6local_context seg6local_ctx; struct seg6local_context seg6local_ctx;
/* SRv6 Headend-behaviour */ /* 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) 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); 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 = {}; struct in6_addr zero = {};
return sid_same(a, &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[0], &zero);
} }
static inline void *sid_copy(struct in6_addr *dst, static inline void *sid_copy(struct in6_addr *dst,

View File

@ -1061,10 +1061,11 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
sizeof(struct seg6local_context)); sizeof(struct seg6local_context));
} }
if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6)) if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6)) {
stream_write(s, &api_nh->seg6_segs, stream_putc(s, api_nh->seg_num);
sizeof(struct in6_addr)); stream_put(s, &api_nh->seg6_segs[0],
api_nh->seg_num * sizeof(struct in6_addr));
}
done: done:
return ret; return ret;
} }
@ -1430,9 +1431,18 @@ int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
sizeof(struct seg6local_context)); sizeof(struct seg6local_context));
} }
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)) if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)) {
STREAM_GET(&api_nh->seg6_segs, s, STREAM_GETC(s, api_nh->seg_num);
sizeof(struct in6_addr)); 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 */ /* Success */
ret = 0; 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, nexthop_add_srv6_seg6local(n, znh->seg6local_action,
&znh->seg6local_ctx); &znh->seg6local_ctx);
if (!sid_zero(&znh->seg6_segs)) if (znh->seg_num && !sid_zero_ipv6(znh->seg6_segs))
nexthop_add_srv6_seg6(n, &znh->seg6_segs); nexthop_add_srv6_seg6(n, &znh->seg6_segs[0], znh->seg_num);
return n; return n;
} }
@ -2193,10 +2203,14 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
sizeof(struct seg6local_context)); 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); SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
memcpy(&znh->seg6_segs, &nh->nh_srv6->seg6_segs, znh->seg_num = nh->nh_srv6->seg6_segs->num_segs;
sizeof(struct in6_addr)); 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));
} }
} }

View File

@ -438,7 +438,8 @@ struct zapi_nexthop {
struct seg6local_context seg6local_ctx; struct seg6local_context seg6local_ctx;
/* SRv6 Headend-behaviour */ /* SRv6 Headend-behaviour */
struct in6_addr seg6_segs; int seg_num;
struct in6_addr seg6_segs[SRV6_MAX_SEGS];
}; };
/* /*

View File

@ -402,7 +402,7 @@ DEFPY (install_seg6_routes,
sg.r.nhop.gate.ipv6 = seg6_nh6; sg.r.nhop.gate.ipv6 = seg6_nh6;
sg.r.nhop.vrf_id = vrf->vrf_id; sg.r.nhop.vrf_id = vrf->vrf_id;
sg.r.nhop_group.nexthop = &sg.r.nhop; 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; sg.r.vrf_id = vrf->vrf_id;
sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, 0, sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, 0,

View File

@ -74,6 +74,21 @@ const struct frr_yang_module_info frr_staticd_info = {
.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_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",
.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", .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 = { .cbs = {
@ -182,6 +197,20 @@ const struct frr_yang_module_info frr_staticd_info = {
.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_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",
.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", .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 = { .cbs = {

View File

@ -35,6 +35,14 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
struct nb_cb_modify_args *args); struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy( int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy(
struct nb_cb_destroy_args *args); 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( 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); 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( 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); 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( 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); 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( 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); 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( 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_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 */ /* route-list/srclist */
#define FRR_S_ROUTE_SRC_INFO_KEY_XPATH \ #define FRR_S_ROUTE_SRC_INFO_KEY_XPATH \
"/frr-routing:routing/control-plane-protocols/" \ "/frr-routing:routing/control-plane-protocols/" \

View File

@ -209,6 +209,98 @@ static bool static_nexthop_destroy(struct nb_cb_destroy_args *args)
return NB_OK; 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) static int nexthop_mpls_label_stack_entry_create(struct nb_cb_create_args *args)
{ {
struct static_nexthop *nh; struct static_nexthop *nh;
@ -641,6 +733,60 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
return NB_OK; return NB_OK;
} }
/*
* 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: * XPath:
* /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry
@ -1019,6 +1165,60 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr
return NB_OK; 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/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: * 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 * /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

View File

@ -9,6 +9,7 @@
#include "lib/bfd.h" #include "lib/bfd.h"
#include "lib/mpls.h" #include "lib/mpls.h"
#include "lib/srv6.h"
#include "table.h" #include "table.h"
#include "memory.h" #include "memory.h"
@ -27,6 +28,12 @@ struct static_nh_label {
mpls_label_t label[MPLS_MAX_LABELS]; 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 { enum static_blackhole_type {
STATIC_BLACKHOLE_DROP = 0, STATIC_BLACKHOLE_DROP = 0,
STATIC_BLACKHOLE_NULL, STATIC_BLACKHOLE_NULL,
@ -129,6 +136,9 @@ struct static_nexthop {
/* Label information */ /* Label information */
struct static_nh_label snh_label; 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 * Whether to pretend the nexthop is directly attached to the specified
* link. Only meaningful when both a gateway address and interface name * link. Only meaningful when both a gateway address and interface name

View File

@ -47,6 +47,7 @@ struct static_route_args {
const char *source; const char *source;
const char *gateway; const char *gateway;
const char *interface_name; const char *interface_name;
const char *segs;
const char *flag; const char *flag;
const char *tag; const char *tag;
const char *distance; 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_nexthop[XPATH_MAXLEN];
char xpath_mpls[XPATH_MAXLEN]; char xpath_mpls[XPATH_MAXLEN];
char xpath_label[XPATH_MAXLEN]; char xpath_label[XPATH_MAXLEN];
char xpath_segs[XPATH_MAXLEN];
char xpath_seg[XPATH_MAXLEN];
char ab_xpath[XPATH_MAXLEN]; char ab_xpath[XPATH_MAXLEN];
char buf_prefix[PREFIX_STRLEN]; char buf_prefix[PREFIX_STRLEN];
char buf_src_prefix[PREFIX_STRLEN] = {}; char buf_src_prefix[PREFIX_STRLEN] = {};
char buf_nh_type[PREFIX_STRLEN] = {}; char buf_nh_type[PREFIX_STRLEN] = {};
char buf_tag[PREFIX_STRLEN]; char buf_tag[PREFIX_STRLEN];
uint8_t label_stack_id = 0; uint8_t label_stack_id = 0;
uint8_t segs_stack_id = 0;
const char *buf_gate_str; const char *buf_gate_str;
uint8_t distance = ZEBRA_STATIC_DISTANCE_DEFAULT; uint8_t distance = ZEBRA_STATIC_DISTANCE_DEFAULT;
route_tag_t tag = 0; 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, nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
NULL); 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) { if (args->bfd) {
char xpath_bfd[XPATH_MAXLEN]; char xpath_bfd[XPATH_MAXLEN];
@ -951,9 +988,8 @@ DEFPY_YANG(ipv6_route_blackhole_vrf,
return static_route_nb_run(vty, &args); return static_route_nb_run(vty, &args);
} }
DEFPY_YANG(ipv6_route_address_interface, DEFPY_YANG(ipv6_route_address_interface, ipv6_route_address_interface_cmd,
ipv6_route_address_interface_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
X:X::X:X$gate \ X:X::X:X$gate \
<INTERFACE|Null0>$ifname \ <INTERFACE|Null0>$ifname \
[{ \ [{ \
@ -966,33 +1002,28 @@ DEFPY_YANG(ipv6_route_address_interface,
|onlink$onlink \ |onlink$onlink \
|color (1-4294967295) \ |color (1-4294967295) \
|bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
|segments WORD \
}]", }]",
NO_STR NO_STR IPV6_STR
IPV6_STR "Establish static routes\n"
"Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
"IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 source-dest route\n"
"IPv6 source-dest route\n" "IPv6 source prefix\n"
"IPv6 source prefix\n" "IPv6 gateway address\n"
"IPv6 gateway address\n" "IPv6 gateway interface name\n"
"IPv6 gateway interface name\n" "Null interface\n"
"Null interface\n" "Set tag for this route\n"
"Set tag for this route\n" "Tag value\n"
"Tag value\n" "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR
"Distance value for this prefix\n" "Table to configure\n"
VRF_CMD_HELP_STR "The table number to configure\n" VRF_CMD_HELP_STR
MPLS_LABEL_HELPSTR "Treat the nexthop as directly attached to the interface\n"
"Table to configure\n" "SR-TE color\n"
"The table number to configure\n" "The SR-TE color to configure\n" BFD_INTEGRATION_STR
VRF_CMD_HELP_STR BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
"Treat the nexthop as directly attached to the interface\n" BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
"SR-TE color\n" BFD_PROFILE_NAME_STR "Value of segs\n"
"The SR-TE color to configure\n" "Segs (SIDs)\n")
BFD_INTEGRATION_STR
BFD_INTEGRATION_MULTI_HOP_STR
BFD_INTEGRATION_SOURCE_STR
BFD_INTEGRATION_SOURCEV4_STR
BFD_PROFILE_STR
BFD_PROFILE_NAME_STR)
{ {
struct static_route_args args = { struct static_route_args args = {
.delete = !!no, .delete = !!no,
@ -1014,14 +1045,15 @@ DEFPY_YANG(ipv6_route_address_interface,
.bfd_multi_hop = !!bfd_multi_hop, .bfd_multi_hop = !!bfd_multi_hop,
.bfd_source = bfd_source_str, .bfd_source = bfd_source_str,
.bfd_profile = bfd_profile, .bfd_profile = bfd_profile,
.segs = segments,
}; };
return static_route_nb_run(vty, &args); return static_route_nb_run(vty, &args);
} }
DEFPY_YANG(ipv6_route_address_interface_vrf, DEFPY_YANG(ipv6_route_address_interface_vrf,
ipv6_route_address_interface_vrf_cmd, ipv6_route_address_interface_vrf_cmd,
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
X:X::X:X$gate \ X:X::X:X$gate \
<INTERFACE|Null0>$ifname \ <INTERFACE|Null0>$ifname \
[{ \ [{ \
@ -1033,32 +1065,28 @@ DEFPY_YANG(ipv6_route_address_interface_vrf,
|onlink$onlink \ |onlink$onlink \
|color (1-4294967295) \ |color (1-4294967295) \
|bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
|segments WORD \
}]", }]",
NO_STR NO_STR IPV6_STR
IPV6_STR "Establish static routes\n"
"Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
"IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 source-dest route\n"
"IPv6 source-dest route\n" "IPv6 source prefix\n"
"IPv6 source prefix\n" "IPv6 gateway address\n"
"IPv6 gateway address\n" "IPv6 gateway interface name\n"
"IPv6 gateway interface name\n" "Null interface\n"
"Null interface\n" "Set tag for this route\n"
"Set tag for this route\n" "Tag value\n"
"Tag value\n" "Distance value for this prefix\n" MPLS_LABEL_HELPSTR
"Distance value for this prefix\n" "Table to configure\n"
MPLS_LABEL_HELPSTR "The table number to configure\n" VRF_CMD_HELP_STR
"Table to configure\n" "Treat the nexthop as directly attached to the interface\n"
"The table number to configure\n" "SR-TE color\n"
VRF_CMD_HELP_STR "The SR-TE color to configure\n" BFD_INTEGRATION_STR
"Treat the nexthop as directly attached to the interface\n" BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
"SR-TE color\n" BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
"The SR-TE color to configure\n" BFD_PROFILE_NAME_STR "Value of segs\n"
BFD_INTEGRATION_STR "Segs (SIDs)\n")
BFD_INTEGRATION_MULTI_HOP_STR
BFD_INTEGRATION_SOURCE_STR
BFD_INTEGRATION_SOURCEV4_STR
BFD_PROFILE_STR
BFD_PROFILE_NAME_STR)
{ {
struct static_route_args args = { struct static_route_args args = {
.delete = !!no, .delete = !!no,
@ -1080,14 +1108,14 @@ DEFPY_YANG(ipv6_route_address_interface_vrf,
.bfd_multi_hop = !!bfd_multi_hop, .bfd_multi_hop = !!bfd_multi_hop,
.bfd_source = bfd_source_str, .bfd_source = bfd_source_str,
.bfd_profile = bfd_profile, .bfd_profile = bfd_profile,
.segs = segments,
}; };
return static_route_nb_run(vty, &args); return static_route_nb_run(vty, &args);
} }
DEFPY_YANG(ipv6_route, DEFPY_YANG(ipv6_route, ipv6_route_cmd,
ipv6_route_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
<X:X::X:X$gate|<INTERFACE|Null0>$ifname> \ <X:X::X:X$gate|<INTERFACE|Null0>$ifname> \
[{ \ [{ \
tag (1-4294967295) \ tag (1-4294967295) \
@ -1098,32 +1126,26 @@ DEFPY_YANG(ipv6_route,
|nexthop-vrf NAME \ |nexthop-vrf NAME \
|color (1-4294967295) \ |color (1-4294967295) \
|bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
|segments WORD \
}]", }]",
NO_STR NO_STR IPV6_STR
IPV6_STR "Establish static routes\n"
"Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
"IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 source-dest route\n"
"IPv6 source-dest route\n" "IPv6 source prefix\n"
"IPv6 source prefix\n" "IPv6 gateway address\n"
"IPv6 gateway address\n" "IPv6 gateway interface name\n"
"IPv6 gateway interface name\n" "Null interface\n"
"Null interface\n" "Set tag for this route\n"
"Set tag for this route\n" "Tag value\n"
"Tag value\n" "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR
"Distance value for this prefix\n" "Table to configure\n"
VRF_CMD_HELP_STR "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n"
MPLS_LABEL_HELPSTR "The SR-TE color to configure\n" BFD_INTEGRATION_STR
"Table to configure\n" BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
"The table number to configure\n" BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
VRF_CMD_HELP_STR BFD_PROFILE_NAME_STR "Value of segs\n"
"SR-TE color\n" "Segs (SIDs)\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)
{ {
struct static_route_args args = { struct static_route_args args = {
.delete = !!no, .delete = !!no,
@ -1144,14 +1166,15 @@ DEFPY_YANG(ipv6_route,
.bfd_multi_hop = !!bfd_multi_hop, .bfd_multi_hop = !!bfd_multi_hop,
.bfd_source = bfd_source_str, .bfd_source = bfd_source_str,
.bfd_profile = bfd_profile, .bfd_profile = bfd_profile,
.segs = segments,
}; };
return static_route_nb_run(vty, &args); return static_route_nb_run(vty, &args);
} }
DEFPY_YANG(ipv6_route_vrf, DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd,
ipv6_route_vrf_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
<X:X::X:X$gate|<INTERFACE|Null0>$ifname> \ <X:X::X:X$gate|<INTERFACE|Null0>$ifname> \
[{ \ [{ \
tag (1-4294967295) \ tag (1-4294967295) \
@ -1161,31 +1184,26 @@ DEFPY_YANG(ipv6_route_vrf,
|nexthop-vrf NAME \ |nexthop-vrf NAME \
|color (1-4294967295) \ |color (1-4294967295) \
|bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
|segments WORD \
}]", }]",
NO_STR NO_STR IPV6_STR
IPV6_STR "Establish static routes\n"
"Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
"IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 source-dest route\n"
"IPv6 source-dest route\n" "IPv6 source prefix\n"
"IPv6 source prefix\n" "IPv6 gateway address\n"
"IPv6 gateway address\n" "IPv6 gateway interface name\n"
"IPv6 gateway interface name\n" "Null interface\n"
"Null interface\n" "Set tag for this route\n"
"Set tag for this route\n" "Tag value\n"
"Tag value\n" "Distance value for this prefix\n" MPLS_LABEL_HELPSTR
"Distance value for this prefix\n" "Table to configure\n"
MPLS_LABEL_HELPSTR "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n"
"Table to configure\n" "The SR-TE color to configure\n" BFD_INTEGRATION_STR
"The table number to configure\n" BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
VRF_CMD_HELP_STR BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
"SR-TE color\n" BFD_PROFILE_NAME_STR "Value of segs\n"
"The SR-TE color to configure\n" "Segs (SIDs)\n")
BFD_INTEGRATION_STR
BFD_INTEGRATION_MULTI_HOP_STR
BFD_INTEGRATION_SOURCE_STR
BFD_INTEGRATION_SOURCEV4_STR
BFD_PROFILE_STR
BFD_PROFILE_NAME_STR)
{ {
struct static_route_args args = { struct static_route_args args = {
.delete = !!no, .delete = !!no,
@ -1206,6 +1224,7 @@ DEFPY_YANG(ipv6_route_vrf,
.bfd_multi_hop = !!bfd_multi_hop, .bfd_multi_hop = !!bfd_multi_hop,
.bfd_source = bfd_source_str, .bfd_source = bfd_source_str,
.bfd_profile = bfd_profile, .bfd_profile = bfd_profile,
.segs = segments,
}; };
return static_route_nb_run(vty, &args); 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; 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, static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route,
const struct lyd_node *src, const struct lyd_node *src,
const struct lyd_node *path, 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; uint32_t tag;
uint8_t distance; uint8_t distance;
struct mpls_label_iter iter; struct mpls_label_iter iter;
struct srv6_seg_iter seg_iter;
const char *nexthop_vrf; const char *nexthop_vrf;
uint32_t table_id; uint32_t table_id;
bool onlink; 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, yang_dnode_iterate(mpls_label_iter_cb, &iter, nexthop,
"./mpls-label-stack/entry"); "./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"); nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf");
if (strcmp(vrf, nexthop_vrf)) if (strcmp(vrf, nexthop_vrf))
vty_out(vty, " nexthop-vrf %s", nexthop_vrf); vty_out(vty, " nexthop-vrf %s", nexthop_vrf);

View File

@ -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++) for (i = 0; i < api_nh->label_num; i++)
api_nh->labels[i] = nh->snh_label.label[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++; nh_num++;
} }

View File

@ -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"
]
}
]
}
]
}

View File

@ -0,0 +1,2 @@
ip link add dummy0 type dummy
ip link set dummy0 up

View File

@ -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

View File

@ -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
!

View File

@ -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))

View File

@ -201,6 +201,11 @@ module frr-nexthop {
description description
"Nexthop's MPLS label stack."; "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 { container frr-nexthop-group {
description description
"A nexthop-group, represented as a list of nexthop objects."; "A nexthop-group, represented as a list of nexthop objects.";

View File

@ -66,6 +66,7 @@
#include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_evpn_mh.h"
#include "zebra/zebra_trace.h" #include "zebra/zebra_trace.h"
#include "zebra/zebra_neigh.h" #include "zebra/zebra_neigh.h"
#include "lib/srv6.h"
#ifndef AF_MPLS #ifndef AF_MPLS
#define AF_MPLS 28 #define AF_MPLS 28
@ -77,6 +78,8 @@
#define BR_SPH_LIST_SIZE 10 #define BR_SPH_LIST_SIZE 10
#endif #endif
DEFINE_MTYPE_STATIC(LIB, NH_SRV6, "Nexthop srv6");
static vlanid_t filter_vlan = 0; static vlanid_t filter_vlan = 0;
/* We capture whether the current kernel supports nexthop ids; by /* 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 rtattr *tb_encap[SEG6_IPTUNNEL_MAX + 1] = {};
struct seg6_iptunnel_encap *ipt = NULL; struct seg6_iptunnel_encap *ipt = NULL;
struct in6_addr *segments = NULL; int i;
netlink_parse_rtattr_nested(tb_encap, SEG6_IPTUNNEL_MAX, tb); netlink_parse_rtattr_nested(tb_encap, SEG6_IPTUNNEL_MAX, tb);
/*
* TODO: It's not support multiple SID list.
*/
if (tb_encap[SEG6_IPTUNNEL_SRH]) { if (tb_encap[SEG6_IPTUNNEL_SRH]) {
ipt = (struct seg6_iptunnel_encap *) ipt = (struct seg6_iptunnel_encap *)
RTA_DATA(tb_encap[SEG6_IPTUNNEL_SRH]); RTA_DATA(tb_encap[SEG6_IPTUNNEL_SRH]);
segments = ipt->srh[0].segments;
*segs = segments[0]; for (i = ipt->srh[0].first_segment; i >= 0; i--)
return 1; memcpy(&segs[i], &ipt->srh[0].segments[i],
sizeof(struct in6_addr));
return ipt->srh[0].first_segment + 1;
} }
return 0; return 0;
@ -506,7 +509,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb,
int num_labels = 0; int num_labels = 0;
enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
struct seg6local_context seg6l_ctx = {}; struct seg6local_context seg6l_ctx = {};
struct in6_addr seg6_segs = {}; struct in6_addr segs[SRV6_MAX_SIDS] = {};
int num_segs = 0; int num_segs = 0;
vrf_id_t nh_vrf_id = vrf_id; 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] if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
&& *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE]) && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE])
== LWTUNNEL_ENCAP_SEG6) { == 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) 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); nexthop_add_srv6_seg6local(&nh, seg6l_act, &seg6l_ctx);
if (num_segs) if (num_segs)
nexthop_add_srv6_seg6(&nh, &seg6_segs); nexthop_add_srv6_seg6(&nh, segs, num_segs);
return nh; return nh;
} }
@ -601,7 +604,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
int num_labels = 0; int num_labels = 0;
enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
struct seg6local_context seg6l_ctx = {}; struct seg6local_context seg6l_ctx = {};
struct in6_addr seg6_segs = {}; struct in6_addr segs[SRV6_MAX_SIDS] = {};
int num_segs = 0; int num_segs = 0;
struct rtattr *rtnh_tb[RTA_MAX + 1] = {}; 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]) && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE])
== LWTUNNEL_ENCAP_SEG6) { == LWTUNNEL_ENCAP_SEG6) {
num_segs = parse_encap_seg6(rtnh_tb[RTA_ENCAP], 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); &seg6l_ctx);
if (num_segs) 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) if (rtnh->rtnh_flags & RTNH_F_ONLINK)
SET_FLAG(nh->flags, NEXTHOP_FLAG_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, 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 seg6_iptunnel_encap *ipt;
struct ipv6_sr_hdr *srh; struct ipv6_sr_hdr *srh;
const size_t srhlen = 24; size_t srhlen;
int i;
/* if (segs->num_segs > SRV6_MAX_SEGS) {
* Caution: Support only SINGLE-SID, not MULTI-SID /* Exceeding maximum supported SIDs */
* This function only supports the case where segs represents return -1;
* 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 srhlen = SRH_BASE_HEADER_LENGTH + SRH_SEGMENT_LENGTH * segs->num_segs;
* argument of the Transit Behavior, we must support variable
* boundary check for buflen. if (buflen < (sizeof(struct seg6_iptunnel_encap) + srhlen))
*/
if (buflen < (sizeof(struct seg6_iptunnel_encap) +
sizeof(struct ipv6_sr_hdr) + 16))
return -1; return -1;
memset(buffer, 0, buflen); memset(buffer, 0, buflen);
ipt = (struct seg6_iptunnel_encap *)buffer; ipt = (struct seg6_iptunnel_encap *)buffer;
ipt->mode = SEG6_IPTUN_MODE_ENCAP; ipt->mode = SEG6_IPTUN_MODE_ENCAP;
srh = ipt->srh;
srh = (struct ipv6_sr_hdr *)&ipt->srh;
srh->hdrlen = (srhlen >> 3) - 1; srh->hdrlen = (srhlen >> 3) - 1;
srh->type = 4; srh->type = 4;
srh->segments_left = 0; srh->segments_left = segs->num_segs - 1;
srh->first_segment = 0; srh->first_segment = segs->num_segs - 1;
memcpy(&srh->segments[0], seg, sizeof(struct in6_addr));
return srhlen + 4; for (i = 0; i < segs->num_segs; i++) {
memcpy(&srh->segments[i], &segs->seg[i],
sizeof(struct in6_addr));
}
return sizeof(struct seg6_iptunnel_encap) + srhlen;
} }
static bool static bool
@ -1726,7 +1732,9 @@ static bool _netlink_route_build_singlepath(const struct prefix *p,
nl_attr_nest_end(nlmsg, nest); 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]; char tun_buf[4096];
ssize_t tun_len; ssize_t tun_len;
struct rtattr *nest; 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); nest = nl_attr_nest(nlmsg, req_size, RTA_ENCAP);
if (!nest) if (!nest)
return false; return false;
tun_len = fill_seg6ipt_encap(tun_buf, sizeof(tun_buf), tun_len =
&nexthop->nh_srv6->seg6_segs); fill_seg6ipt_encap(tun_buf, sizeof(tun_buf),
nexthop->nh_srv6->seg6_segs);
if (tun_len < 0) if (tun_len < 0)
return false; return false;
if (!nl_attr_put(nlmsg, req_size, SEG6_IPTUNNEL_SRH, 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); 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]; char tun_buf[4096];
ssize_t tun_len; ssize_t tun_len;
struct rtattr *nest; struct rtattr *nest;
@ -2984,9 +2995,9 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd,
NHA_ENCAP | NLA_F_NESTED); NHA_ENCAP | NLA_F_NESTED);
if (!nest) if (!nest)
return 0; return 0;
tun_len = fill_seg6ipt_encap(tun_buf, tun_len = fill_seg6ipt_encap(
sizeof(tun_buf), tun_buf, sizeof(tun_buf),
&nh->nh_srv6->seg6_segs); nh->nh_srv6->seg6_segs);
if (tun_len < 0) if (tun_len < 0)
return 0; return 0;
if (!nl_attr_put(&req->n, buflen, if (!nl_attr_put(&req->n, buflen,

View File

@ -1794,7 +1794,8 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p,
if (IS_ZEBRA_DEBUG_RECV) if (IS_ZEBRA_DEBUG_RECV)
zlog_debug("%s: adding seg6", __func__); 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) { if (IS_ZEBRA_DEBUG_RECV) {

View File

@ -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, .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", .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry",
.cbs = { .cbs = {

View File

@ -240,6 +240,20 @@ struct yang_data *
lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem(
struct nb_cb_get_elem_args *args); struct nb_cb_get_elem_args *args);
const void * 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( lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next(
struct nb_cb_get_next_args *args); 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( int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys(

View File

@ -840,6 +840,58 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem(
return NULL; return NULL;
} }
/*
* 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: * XPath:
* /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry

View File

@ -1868,11 +1868,18 @@ static struct nexthop *nexthop_set_resolved(afi_t afi,
labels); labels);
if (nexthop->nh_srv6) { if (nexthop->nh_srv6) {
nexthop_add_srv6_seg6local(resolved_hop, if (nexthop->nh_srv6->seg6local_action !=
nexthop->nh_srv6->seg6local_action, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
&nexthop->nh_srv6->seg6local_ctx); nexthop_add_srv6_seg6local(resolved_hop,
nexthop_add_srv6_seg6(resolved_hop, nexthop->nh_srv6
&nexthop->nh_srv6->seg6_segs); ->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; resolved_hop->rparent = nexthop;

View File

@ -1268,6 +1268,7 @@ void show_nexthop_json_helper(json_object *json_nexthop,
json_object *json_backups = NULL; json_object *json_backups = NULL;
json_object *json_seg6local = NULL; json_object *json_seg6local = NULL;
json_object *json_seg6 = NULL; json_object *json_seg6 = NULL;
json_object *json_segs = NULL;
int i; int i;
json_object_int_add(json_nexthop, "flags", nexthop->flags); 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)); nexthop->nh_srv6->seg6local_action));
json_object_object_add(json_nexthop, "seg6local", json_object_object_add(json_nexthop, "seg6local",
json_seg6local); json_seg6local);
if (nexthop->nh_srv6->seg6_segs &&
json_seg6 = json_object_new_object(); nexthop->nh_srv6->seg6_segs->num_segs == 1) {
json_object_string_addf(json_seg6, "segs", "%pI6", json_seg6 = json_object_new_object();
&nexthop->nh_srv6->seg6_segs); json_object_string_addf(json_seg6, "segs", "%pI6",
json_object_object_add(json_nexthop, "seg6", json_seg6); &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) const struct nexthop *nexthop)
{ {
char buf[MPLS_LABEL_STRLEN]; char buf[MPLS_LABEL_STRLEN];
int i; char seg_buf[SRV6_SEG_STRLEN];
struct seg6_segs segs;
uint8_t i;
switch (nexthop->type) { switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4:
@ -1538,9 +1561,17 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re,
seg6local_action2str( seg6local_action2str(
nexthop->nh_srv6->seg6local_action), nexthop->nh_srv6->seg6local_action),
buf); buf);
if (IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs, &in6addr_any)) if (nexthop->nh_srv6->seg6_segs &&
vty_out(vty, ", seg6 %pI6", IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs->seg[0],
&nexthop->nh_srv6->seg6_segs); &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) if (nexthop->weight)