From 64e35281a8e8e4f7dddc8c8c48ebdb05de1b0da9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Parisot?= <sparisot@free-mobile.fr>
Date: Thu, 12 Feb 2026 16:09:31 +0000
Subject: [PATCH 1/5] nest: Add SRv6 SID stack support to nexthop

Add SRv6 SID stack type and constants (SRV6_MAX_SID_STACK,
SRV6_MAX_SID_STRING), srv6_sid_stack struct, and nexthop fields
(sid6s_orig, sid6s, sid6[]).

Extend nexthop_hash(), nexthop_equal_1(), nexthop__same(),
nexthop_compare_node(), nexthop_copy() and rta_dump() for SRv6 SID
handling. Update rta_apply_hostentry() and rta_set_recursive_next_hop()
signatures with SRv6 parameter.

Add SRv6 SID display in route show output and propagate SRv6 SIDs
through recursive next hop resolution.
---
 conf/confbase.Y | 16 ++++++++++++++++
 lib/ip.h        |  7 +++++++
 nest/mpls.c     |  2 +-
 nest/route.h    |  9 ++++++---
 nest/rt-attr.c  | 32 ++++++++++++++++++++++++++++++--
 nest/rt-show.c  | 18 ++++++++++++++----
 nest/rt-table.c | 38 +++++++++++++++++++++++++++++++++++---
 7 files changed, 109 insertions(+), 13 deletions(-)

diff --git a/conf/confbase.Y b/conf/confbase.Y
index 27c422e..3621f37 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -125,6 +125,7 @@ CF_DECLS
   struct timeformat tf;
   struct timeformat *tfp;
   mpls_label_stack *mls;
+  srv6_sid_stack *sid6;
   const struct adata *bs;
   struct aggr_item_node *ai;
 }
@@ -148,6 +149,7 @@ CF_DECLS
 %type <net> net_ip4_ net_ip4 net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
 %type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_ net_aspa_
 %type <mls> label_stack_start label_stack
+%type <sid6> srv6_sid_stack
 
 %type <t> text opttext
 %type <bs> bytestring
@@ -442,6 +444,20 @@ label_stack:
   }
 ;
 
+srv6_sid_stack: IP6
+{
+  $$ = cfg_allocz(sizeof(srv6_sid_stack));
+  $$->len = 1;
+  $$->sid[0] = ipa_from_ip6($1);
+}
+ | srv6_sid_stack ',' IP6
+{
+  $$ = $1;
+  if ($$->len >= SRV6_MAX_SID_STACK)
+    cf_error("Too many SRv6 SIDs (max %d)", SRV6_MAX_SID_STACK);
+  $$->sid[$$->len++] = ipa_from_ip6($3);
+}
+;
 
 /* Strings */
 
diff --git a/lib/ip.h b/lib/ip.h
index bae0526..7f3c8cc 100644
--- a/lib/ip.h
+++ b/lib/ip.h
@@ -393,6 +393,13 @@ static inline ip6_addr ip6_ntoh(ip6_addr a)
 { return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); }
 
 
+#define SRV6_MAX_SID_STACK 1
+#define SRV6_MAX_SID_STRING (8 /* " srv6 <" */ + IP6_MAX_TEXT_LENGTH + (1 /* "," */ + IP6_MAX_TEXT_LENGTH) * (SRV6_MAX_SID_STACK - 1) + 1 /* ">" */ + 1 /* '\0' */)
+typedef struct srv6_sid_stack {
+  uint len;
+  ip6_addr sid[SRV6_MAX_SID_STACK];
+} srv6_sid_stack;
+
 /*
  *	Unaligned data access (in network order)
  */
diff --git a/nest/mpls.c b/nest/mpls.c
index 9cdcd57..20aa09e 100644
--- a/nest/mpls.c
+++ b/nest/mpls.c
@@ -1065,7 +1065,7 @@ mpls_announce_fec(struct mpls_fec_map *m, struct mpls_fec *fec, const rta *src)
 
     /* The same hostentry, but different dependent table */
     struct hostentry *s = src->hostentry;
-    rta_set_recursive_next_hop(m->channel->table, a, s->owner, s->addr, s->link, &ms);
+    rta_set_recursive_next_hop(m->channel->table, a, s->owner, s->addr, s->link, &ms, NULL);
   }
 
   net_addr_mpls n = NET_ADDR_MPLS(fec->label);
diff --git a/nest/route.h b/nest/route.h
index 5a9e7fa..3addc17 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -434,6 +434,9 @@ struct nexthop {
   struct nexthop *next;
   byte flags;
   byte weight;
+  byte sid6s_orig;                      /* Number of SRv6 SIDs before hostentry was applied */
+  byte sid6s;                           /* Number of all SRv6 SIDs */
+  ip6_addr sid6[SRV6_MAX_SID_STACK];
   byte labels_orig;			/* Number of labels before hostentry was applied */
   byte labels;				/* Number of all labels */
   u32 label[0];
@@ -724,12 +727,12 @@ void rta_show(struct cli *, rta *);
 
 u32 rt_get_igp_metric(rte *rt);
 struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
-void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
+void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls, srv6_sid_stack *sid6);
 
 static inline void
-rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls)
+rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls, srv6_sid_stack *sid6)
 {
-  rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls);
+  rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls, sid6);
 }
 
 /*
diff --git a/nest/rt-attr.c b/nest/rt-attr.c
index e10e1ec..4a3366d 100644
--- a/nest/rt-attr.c
+++ b/nest/rt-attr.c
@@ -180,6 +180,9 @@ nexthop_hash(struct nexthop *x)
 
     for (int i = 0; i < x->labels; i++)
       h ^= x->label[i] ^ (h << 6) ^ (h >> 7);
+
+    for (int i = 0; i < x->sid6s; i++)
+      h ^= ip6_hash(x->sid6[i]) ^ (h << 7) ^ (h >> 8);
   }
 
   return h;
@@ -190,13 +193,18 @@ nexthop_equal_1(struct nexthop *x, struct nexthop *y)
 {
   if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) ||
       (x->flags != y->flags) || (x->weight != y->weight) ||
-      (x->labels != y->labels))
+      (x->labels != y->labels) ||
+      (x->sid6s != y->sid6s))
     return 0;
 
   for (int i = 0; i < x->labels; i++)
     if (x->label[i] != y->label[i])
       return 0;
 
+  for (int i = 0; i < x->sid6s; i++)
+    if (memcmp(&x->sid6[i], &y->sid6[i], sizeof(x->sid6[0])) != 0)
+      return 0;
+
   return 1;
 }
 
@@ -217,7 +225,8 @@ nexthop__same(struct nexthop *x, struct nexthop *y)
 {
   for (; x && y; x = x->next, y = y->next)
     if (!nexthop_equal_1(x, y) ||
-	(x->labels_orig != y->labels_orig))
+	(x->labels_orig != y->labels_orig) ||
+        (x->sid6s_orig != y->sid6s_orig))
       return 0;
 
   return x == y;
@@ -255,6 +264,17 @@ nexthop_compare_node(const struct nexthop *x, const struct nexthop *y)
       return r;
   }
 
+  r = ((int) y->sid6s) - ((int) x->sid6s);
+  if (r)
+    return r;
+
+  for (int i = 0; i < y->sid6s; i++)
+  {
+    r = ip6_compare(x->sid6[i], y->sid6[i]);
+    if (r)
+      return r;
+  }
+
   return ((int) x->iface->index) - ((int) y->iface->index);
 }
 
@@ -398,6 +418,10 @@ nexthop_copy(struct nexthop *o)
       n->labels = o->labels;
       for (int i=0; i<o->labels; i++)
 	n->label[i] = o->label[i];
+      n->sid6s_orig = o->sid6s_orig;
+      n->sid6s = o->sid6s;
+      for (int i=0; i<o->sid6s; i++)
+        memcpy(&n->sid6[i], &o->sid6[i], sizeof(n->sid6[0]));
 
       *last = n;
       last = &(n->next);
@@ -1340,6 +1364,10 @@ rta_dump(struct dump_request *dreq, rta *a)
 	if (nh->labels) RDUMP(" L %d", nh->label[0]);
 	for (int i=1; i<nh->labels; i++)
 	  RDUMP("/%d", nh->label[i]);
+	if (nh->sid6s) RDUMP(" SRv6 <%I6", nh->sid6[0]);
+	for (int i=1; i<nh->sid6s; i++)
+	  RDUMP(",%I6", nh->sid6[i]);
+	if (nh->sid6s) RDUMP(">");
 	RDUMP(" [%s]", nh->iface ? nh->iface->name : "???");
       }
   if (a->eattrs)
diff --git a/nest/rt-show.c b/nest/rt-show.c
index 0ecc863..c8f07ea 100644
--- a/nest/rt-show.c
+++ b/nest/rt-show.c
@@ -73,6 +73,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
     for (nh = &(a->nh); nh; nh = nh->next)
     {
       char mpls[MPLS_MAX_LABEL_STRING], *lsp = mpls;
+      char srv6[SRV6_MAX_SID_STRING], *sid6 = srv6;
       char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : "";
       char weight[16] = "";
 
@@ -84,15 +85,24 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
 	}
       *lsp = '\0';
 
+      if (nh->sid6s)
+	{
+	  sid6 += bsprintf(sid6, " srv6 <%I6", nh->sid6[0]);
+	  for (int i = 1; i < nh->sid6s; ++i)
+	    sid6 += bsprintf(sid6, ",%I6", nh->sid6[i]);
+	  sid6 += bsprintf(sid6, ">");
+	}
+      *sid6 = '\0';
+
       if (a->nh.next)
 	bsprintf(weight, " weight %d", nh->weight + 1);
 
       if (ipa_nonzero(nh->gw))
-	cli_printf(c, -1007, "\tvia %I on %s%s%s%s",
-		   nh->gw, nh->iface->name, mpls, onlink, weight);
+	cli_printf(c, -1007, "\tvia %I on %s%s%s%s%s",
+		   nh->gw, nh->iface->name, mpls, srv6, onlink, weight);
       else
-	cli_printf(c, -1007, "\tdev %s%s%s",
-		   nh->iface->name, mpls,  onlink, weight);
+	cli_printf(c, -1007, "\tdev %s%s%s%s",
+		   nh->iface->name, mpls, srv6, onlink, weight);
     }
 
   if (d->verbose)
diff --git a/nest/rt-table.c b/nest/rt-table.c
index ed364d3..cdcebd5 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -2391,7 +2391,7 @@ rt_postconfig(struct config *c)
  */
 
 void
-rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls)
+rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls, srv6_sid_stack *sid6)
 {
   a->hostentry = he;
   a->dest = he->dest;
@@ -2407,10 +2407,15 @@ no_nexthop:
       a->nh.labels_orig = a->nh.labels = mls->len;
       memcpy(a->nh.label, mls->stack, mls->len * sizeof(u32));
     }
+    if (sid6)
+    {
+      a->nh.sid6s_orig = a->nh.sid6s = sid6->len;
+      memcpy(a->nh.sid6, sid6->sid, sid6->len * sizeof(sid6->sid[0]));
+    }
     return;
   }
 
-  if (((!mls) || (!mls->len)) && he->nexthop_linkable)
+  if ((((!mls) || (!mls->len)) && (!(sid6) || !(sid6->len))) && he->nexthop_linkable)
   { /* Just link the nexthop chain, no label append happens. */
     memcpy(&(a->nh), &(he->src->nh), nexthop_size(&(he->src->nh)));
     return;
@@ -2457,6 +2462,30 @@ no_nexthop:
       memcpy(nhp->label, nh->label, nh->labels * sizeof(u32));
     }
 
+    if (sid6)
+    {
+      nhp->sid6s = nh->sid6s + sid6->len;
+      nhp->sid6s_orig = sid6->len;
+      if (nhp->sid6s <= SRV6_MAX_SID_STACK)
+      {
+        memcpy(nhp->sid6, nh->sid6, nh->sid6s * sizeof(nhp->sid6[0])); /* First the hostentry SIDs */
+        memcpy(&(nhp->sid6[nh->sid6s]), sid6->sid, sid6->len * sizeof(nhp->sid6[0])); /* Then the bottom SIDs */
+      }
+      else
+      {
+        log(L_WARN "Sum of SID stack sizes %d + %d = %d exceedes allowed maximum (%d)",
+            nh->sid6s, sid6->len, nhp->sid6s, SRV6_MAX_SID_STACK);
+        skip_nexthop++;
+        continue;
+      }
+    }
+    else if (nh->sid6s)
+    {
+      nhp->sid6s = nh->sid6s;
+      nhp->sid6s_orig = 0;
+      memcpy(nhp->sid6, nh->sid6, nhp->sid6s * sizeof(nhp->sid6[0]));
+    }
+
     if (ipa_nonzero(nh->gw))
     {
       nhp->gw = nh->gw;			/* Router nexthop */
@@ -2508,7 +2537,10 @@ rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
   mpls_label_stack mls = { .len = a->nh.labels_orig };
   memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32));
 
-  rta_apply_hostentry(a, old->attrs->hostentry, &mls);
+  srv6_sid_stack sid6 = { .len = a->nh.sid6s_orig };
+  memcpy(sid6.sid, &a->nh.sid6[a->nh.sid6s - sid6.len], sid6.len * sizeof(sid6.sid[0]));
+
+  rta_apply_hostentry(a, old->attrs->hostentry, &mls, &sid6);
   a->cached = 0;
 
   rte *e = sl_alloc(rte_slab);
-- 
2.47.3

