mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-29 13:51:53 +00:00
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:
commit
90d19d1489
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
112
lib/nexthop.c
112
lib/nexthop.c
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
29
lib/srv6.h
29
lib/srv6.h
@ -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,
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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,
|
||||||
|
@ -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 = {
|
||||||
|
@ -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/" \
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
0
tests/topotests/srv6_static_route/__init__.py
Normal file
0
tests/topotests/srv6_static_route/__init__.py
Normal file
49
tests/topotests/srv6_static_route/expected_srv6_route.json
Normal file
49
tests/topotests/srv6_static_route/expected_srv6_route.json
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
0
tests/topotests/srv6_static_route/r1/mgmtd.conf
Normal file
0
tests/topotests/srv6_static_route/r1/mgmtd.conf
Normal file
2
tests/topotests/srv6_static_route/r1/setup.sh
Normal file
2
tests/topotests/srv6_static_route/r1/setup.sh
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ip link add dummy0 type dummy
|
||||||
|
ip link set dummy0 up
|
9
tests/topotests/srv6_static_route/r1/staticd.conf
Normal file
9
tests/topotests/srv6_static_route/r1/staticd.conf
Normal 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
|
10
tests/topotests/srv6_static_route/r1/zebra.conf
Normal file
10
tests/topotests/srv6_static_route/r1/zebra.conf
Normal 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
|
||||||
|
!
|
90
tests/topotests/srv6_static_route/test_srv6_route.py
Executable file
90
tests/topotests/srv6_static_route/test_srv6_route.py
Executable 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))
|
@ -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.";
|
||||||
|
@ -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,
|
||||||
|
@ -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) {
|
||||||
|
@ -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 = {
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user