bgpd,lib,sharpd,zebra: srv6 introduce multiple segs/SIDs in nexthop

Append zebra and lib to use muliple SRv6 segs SIDs, and keep one
seg SID for bgpd and sharpd.

Note: bgpd and sharpd compilation relies on the lib and zebra files,
i.e if we separate this: lib or zebra or bgpd or sharpd in different
commits - this will not compile.

Signed-off-by: Dmytro Shytyi <dmytro.shytyi@6wind.com>
This commit is contained in:
Dmytro Shytyi 2023-07-26 17:56:32 +02:00
parent bc6d311d28
commit f20cf1457d
12 changed files with 194 additions and 88 deletions

View File

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

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

View File

@ -570,13 +570,15 @@ void nexthop_del_srv6_seg6local(struct nexthop *nexthop)
nexthop->nh_srv6->seg6local_action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
if (sid_zero(&nexthop->nh_srv6->seg6_segs))
if (nexthop->nh_srv6->seg6_segs == NULL)
XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
}
void nexthop_add_srv6_seg6(struct nexthop *nexthop,
const struct in6_addr *segs)
void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *segs,
int num_segs)
{
int i;
if (!segs)
return;
@ -584,7 +586,22 @@ void nexthop_add_srv6_seg6(struct nexthop *nexthop,
nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6,
sizeof(struct nexthop_srv6));
nexthop->nh_srv6->seg6_segs = *segs;
/* Enforce limit on srv6 seg stack size */
if (num_segs > SRV6_MAX_SIDS)
num_segs = SRV6_MAX_SIDS;
if (!nexthop->nh_srv6->seg6_segs) {
nexthop->nh_srv6->seg6_segs =
XCALLOC(MTYPE_NH_SRV6,
sizeof(struct seg6_seg_stack) +
num_segs * sizeof(struct in6_addr));
}
nexthop->nh_srv6->seg6_segs->num_segs = num_segs;
for (i = 0; i < num_segs; i++)
memcpy(&nexthop->nh_srv6->seg6_segs->seg[i], &segs[i],
sizeof(struct in6_addr));
}
void nexthop_del_srv6_seg6(struct nexthop *nexthop)
@ -810,9 +827,13 @@ void nexthop_copy_no_recurse(struct nexthop *copy,
nexthop_add_srv6_seg6local(copy,
nexthop->nh_srv6->seg6local_action,
&nexthop->nh_srv6->seg6local_ctx);
if (!sid_zero(&nexthop->nh_srv6->seg6_segs))
if (nexthop->nh_srv6->seg6_segs &&
nexthop->nh_srv6->seg6_segs->num_segs &&
!sid_zero(nexthop->nh_srv6->seg6_segs))
nexthop_add_srv6_seg6(copy,
&nexthop->nh_srv6->seg6_segs);
&nexthop->nh_srv6->seg6_segs->seg[0],
nexthop->nh_srv6->seg6_segs
->num_segs);
}
}

View File

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

View File

@ -14,8 +14,11 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#define SRV6_MAX_SIDS 16
#define SRV6_MAX_SIDS 16
#define SRV6_MAX_SEGS 8
#define SRV6_LOCNAME_SIZE 256
#define SRH_BASE_HEADER_LENGTH 8
#define SRH_SEGMENT_LENGTH 16
#ifdef __cplusplus
extern "C" {
@ -74,6 +77,8 @@ enum seg6local_flavor_op {
ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID = 4,
};
#define SRV6_SEG_STRLEN 1024
struct seg6_segs {
size_t num_segs;
struct in6_addr segs[256];
@ -89,6 +94,11 @@ struct seg6local_flavors_info {
uint8_t lcnode_func_len;
};
struct seg6_seg_stack {
uint8_t num_segs;
struct in6_addr seg[0]; /* 1 or more segs */
};
struct seg6local_context {
struct in_addr nh4;
struct in6_addr nh6;
@ -170,7 +180,7 @@ struct nexthop_srv6 {
struct seg6local_context seg6local_ctx;
/* SRv6 Headend-behaviour */
struct in6_addr seg6_segs;
struct seg6_seg_stack *seg6_segs;
};
static inline const char *seg6_mode2str(enum seg6_mode_t mode)
@ -206,12 +216,21 @@ static inline bool sid_diff(
return !sid_same(a, b);
}
static inline bool sid_zero(
const struct in6_addr *a)
static inline bool sid_zero(const struct seg6_seg_stack *a)
{
struct in6_addr zero = {};
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,

View File

@ -1061,10 +1061,11 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
sizeof(struct seg6local_context));
}
if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6))
stream_write(s, &api_nh->seg6_segs,
sizeof(struct in6_addr));
if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6)) {
stream_putc(s, api_nh->seg_num);
stream_put(s, &api_nh->seg6_segs[0],
api_nh->seg_num * sizeof(struct in6_addr));
}
done:
return ret;
}
@ -1430,9 +1431,18 @@ int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
sizeof(struct seg6local_context));
}
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6))
STREAM_GET(&api_nh->seg6_segs, s,
sizeof(struct in6_addr));
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)) {
STREAM_GETC(s, api_nh->seg_num);
if (api_nh->seg_num > SRV6_MAX_SIDS) {
flog_err(EC_LIB_ZAPI_ENCODE,
"%s: invalid number of SRv6 Segs (%u)",
__func__, api_nh->seg_num);
return -1;
}
STREAM_GET(&api_nh->seg6_segs[0], s,
api_nh->seg_num * sizeof(struct in6_addr));
}
/* Success */
ret = 0;
@ -2132,8 +2142,8 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
nexthop_add_srv6_seg6local(n, znh->seg6local_action,
&znh->seg6local_ctx);
if (!sid_zero(&znh->seg6_segs))
nexthop_add_srv6_seg6(n, &znh->seg6_segs);
if (znh->seg_num && !sid_zero_ipv6(znh->seg6_segs))
nexthop_add_srv6_seg6(n, &znh->seg6_segs[0], znh->seg_num);
return n;
}
@ -2193,10 +2203,14 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
sizeof(struct seg6local_context));
}
if (!sid_zero(&nh->nh_srv6->seg6_segs)) {
if (nh->nh_srv6->seg6_segs && nh->nh_srv6->seg6_segs->num_segs &&
!sid_zero(nh->nh_srv6->seg6_segs)) {
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
memcpy(&znh->seg6_segs, &nh->nh_srv6->seg6_segs,
sizeof(struct in6_addr));
znh->seg_num = nh->nh_srv6->seg6_segs->num_segs;
for (i = 0; i < nh->nh_srv6->seg6_segs->num_segs; i++)
memcpy(&znh->seg6_segs[i],
&nh->nh_srv6->seg6_segs->seg[i],
sizeof(struct in6_addr));
}
}

View File

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

View File

@ -402,7 +402,7 @@ DEFPY (install_seg6_routes,
sg.r.nhop.gate.ipv6 = seg6_nh6;
sg.r.nhop.vrf_id = vrf->vrf_id;
sg.r.nhop_group.nexthop = &sg.r.nhop;
nexthop_add_srv6_seg6(&sg.r.nhop, &seg6_seg);
nexthop_add_srv6_seg6(&sg.r.nhop, &seg6_seg, 1);
sg.r.vrf_id = vrf->vrf_id;
sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, 0,

View File

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

View File

@ -1794,7 +1794,8 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p,
if (IS_ZEBRA_DEBUG_RECV)
zlog_debug("%s: adding seg6", __func__);
nexthop_add_srv6_seg6(nexthop, &api_nh->seg6_segs);
nexthop_add_srv6_seg6(nexthop, &api_nh->seg6_segs[0],
api_nh->seg_num);
}
if (IS_ZEBRA_DEBUG_RECV) {

View File

@ -1868,11 +1868,18 @@ static struct nexthop *nexthop_set_resolved(afi_t afi,
labels);
if (nexthop->nh_srv6) {
nexthop_add_srv6_seg6local(resolved_hop,
nexthop->nh_srv6->seg6local_action,
&nexthop->nh_srv6->seg6local_ctx);
nexthop_add_srv6_seg6(resolved_hop,
&nexthop->nh_srv6->seg6_segs);
if (nexthop->nh_srv6->seg6local_action !=
ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
nexthop_add_srv6_seg6local(resolved_hop,
nexthop->nh_srv6
->seg6local_action,
&nexthop->nh_srv6
->seg6local_ctx);
if (nexthop->nh_srv6->seg6_segs)
nexthop_add_srv6_seg6(resolved_hop,
&nexthop->nh_srv6->seg6_segs->seg[0],
nexthop->nh_srv6->seg6_segs
->num_segs);
}
resolved_hop->rparent = nexthop;

View File

@ -1268,6 +1268,7 @@ void show_nexthop_json_helper(json_object *json_nexthop,
json_object *json_backups = NULL;
json_object *json_seg6local = NULL;
json_object *json_seg6 = NULL;
json_object *json_segs = NULL;
int i;
json_object_int_add(json_nexthop, "flags", nexthop->flags);
@ -1425,11 +1426,31 @@ void show_nexthop_json_helper(json_object *json_nexthop,
nexthop->nh_srv6->seg6local_action));
json_object_object_add(json_nexthop, "seg6local",
json_seg6local);
json_seg6 = json_object_new_object();
json_object_string_addf(json_seg6, "segs", "%pI6",
&nexthop->nh_srv6->seg6_segs);
json_object_object_add(json_nexthop, "seg6", json_seg6);
if (nexthop->nh_srv6->seg6_segs &&
nexthop->nh_srv6->seg6_segs->num_segs == 1) {
json_seg6 = json_object_new_object();
json_object_string_addf(json_seg6, "segs", "%pI6",
&nexthop->nh_srv6->seg6_segs
->seg[0]);
json_object_object_add(json_nexthop, "seg6", json_seg6);
} else {
json_segs = json_object_new_array();
if (nexthop->nh_srv6->seg6_segs) {
for (int seg_idx = 0;
seg_idx <
nexthop->nh_srv6->seg6_segs->num_segs;
seg_idx++)
json_object_array_add(
json_segs,
json_object_new_stringf(
"%pI6",
&nexthop->nh_srv6
->seg6_segs
->seg[seg_idx]));
json_object_object_add(json_nexthop, "seg6",
json_segs);
}
}
}
}
@ -1440,7 +1461,9 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re,
const struct nexthop *nexthop)
{
char buf[MPLS_LABEL_STRLEN];
int i;
char seg_buf[SRV6_SEG_STRLEN];
struct seg6_segs segs;
uint8_t i;
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
@ -1538,9 +1561,17 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re,
seg6local_action2str(
nexthop->nh_srv6->seg6local_action),
buf);
if (IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs, &in6addr_any))
vty_out(vty, ", seg6 %pI6",
&nexthop->nh_srv6->seg6_segs);
if (nexthop->nh_srv6->seg6_segs &&
IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs->seg[0],
&in6addr_any)) {
segs.num_segs = nexthop->nh_srv6->seg6_segs->num_segs;
for (i = 0; i < segs.num_segs; i++)
memcpy(&segs.segs[i],
&nexthop->nh_srv6->seg6_segs->seg[i],
sizeof(struct in6_addr));
snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs);
vty_out(vty, ", seg6 %s", seg_buf);
}
}
if (nexthop->weight)