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