From bc824ab6094ef391ce3f3dc284afb4a781e1eeed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Parisot?= <sparisot@iliad-free.fr>
Date: Fri, 5 Sep 2025 10:55:38 +0200
Subject: [PATCH 2/2] Static: added support for SRv6 SID stack

---
 doc/bird.sgml         |  2 +-
 proto/static/config.Y | 10 +++++++++-
 proto/static/static.c | 41 ++++++++++++++++++++++++++++-------------
 proto/static/static.h |  5 ++++-
 4 files changed, 42 insertions(+), 16 deletions(-)

diff --git a/doc/bird.sgml b/doc/bird.sgml
index 98a480f7..d11cc9e3 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -6440,7 +6440,7 @@ each labeled route.
 <ref id="type-prefix" name="dependent on network type">.
 
 <descrip>
-	<tag><label id="static-route-regular">route <m/prefix/ [mpls <m/number/] via <m/ip/|<m/"interface"/ [<m/per-nexthop options/] [via ...]</tag>
+	<tag><label id="static-route-regular">route <m/prefix/ [mpls <m/number/] via <m/ip/|<m/"interface"/ [srv6 <m/srv6-sid-stack/] [<m/per-nexthop options/] [via ...]</tag>
 	Regular routes may bear one or more <ref id="route-next-hop" name="next
 	hops">. Every next hop is preceded by <cf/via/ and configured as shown.
 
diff --git a/proto/static/config.Y b/proto/static/config.Y
index f840947b..1f0d1f6a 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -47,7 +47,7 @@ static_route_finish(void)
 CF_DECLS
 
 CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK, DEV)
-CF_KEYWORDS(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
+CF_KEYWORDS(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS, SRV6)
 CF_KEYWORDS(TRANSIT, PROVIDERS)
 
 
@@ -97,6 +97,9 @@ stat_nexthop:
   | stat_nexthop MPLS label_stack {
     this_snh->mls = $3;
   }
+  | stat_nexthop SRV6 srv6_sid_stack {
+    this_snh->sid6 = $3;
+  }
   | stat_nexthop ONLINK bool {
     this_snh->onlink = $3;
     if (this_snh->use_bfd && this_snh->onlink)
@@ -146,6 +149,11 @@ stat_route:
       this_srt->via = $3;
       this_srt->mls = $5;
    }
+ | stat_route0 RECURSIVE ipa SRV6 srv6_sid_stack {
+      this_srt->dest = RTDX_RECURSIVE;
+      this_srt->via = $3;
+      this_srt->sid6 = $5;
+   }
  | stat_route0			{ this_srt->dest = RTD_NONE; }
  | stat_route0 DROP		{ this_srt->dest = RTD_BLACKHOLE; }
  | stat_route0 REJECT		{ this_srt->dest = RTD_UNREACHABLE; }
diff --git a/proto/static/static.c b/proto/static/static.c
index ee8ecd5f..6cd2a36f 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -83,6 +83,11 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
 	nh->labels = r2->mls->len;
 	memcpy(nh->label, r2->mls->stack, r2->mls->len * sizeof(u32));
       }
+      if (r2->sid6)
+      {
+        nh->sid6s = r2->sid6->len;
+        memcpy(nh->sid6, r2->sid6->sid, r2->sid6->len * sizeof(nh->sid6[0]));
+      }
 
       nexthop_insert(&nhs, nh);
     }
@@ -96,7 +101,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
   if (r->dest == RTDX_RECURSIVE)
   {
     rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6;
-    rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls, NULL);
+    rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls, r->sid6);
   }
 
   if (r->net->type == NET_ASPA)
@@ -387,30 +392,40 @@ static_same_dest(struct static_route *x, struct static_route *y)
 	  (x->weight != y->weight) ||
 	  (x->use_bfd != y->use_bfd) ||
 	  (!x->mls != !y->mls) ||
-	  ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
+	  (!x->sid6 != !y->sid6) ||
+	  ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)) ||
+	  ((x->sid6) && (y->sid6) && (x->sid6->len != y->sid6->len)))
 	return 0;
 
-      if (!x->mls)
-	continue;
+      if (x->mls)
+        for (uint i = 0; i < x->mls->len; i++)
+	  if (x->mls->stack[i] != y->mls->stack[i])
+	    return 0;
 
-      for (uint i = 0; i < x->mls->len; i++)
-	if (x->mls->stack[i] != y->mls->stack[i])
-	  return 0;
+      if (x->sid6)
+        for (uint i = 0; i < x->sid6->len; i++)
+	  if (ip6_compare(x->sid6->sid[i], y->sid6->sid[i]) != 0)
+	    return 0;
     }
     return !x && !y;
 
   case RTDX_RECURSIVE:
     if (!ipa_equal(x->via, y->via) ||
 	(!x->mls != !y->mls) ||
-	((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
+	(!x->sid6 != !y->sid6) ||
+	((x->mls) && (y->mls) && (x->mls->len != y->mls->len)) ||
+	((x->sid6) && (y->sid6) && (x->sid6->len != y->sid6->len)))
       return 0;
 
-    if (!x->mls)
-      return 1;
+    if (x->mls)
+      for (uint i = 0; i < x->mls->len; i++)
+        if (x->mls->stack[i] != y->mls->stack[i])
+	  return 0;
 
-    for (uint i = 0; i < x->mls->len; i++)
-      if (x->mls->stack[i] != y->mls->stack[i])
-	return 0;
+    if (x->sid6)
+      for (uint i = 0; i < x->sid6->len; i++)
+        if (ip6_compare(x->sid6->sid[i], y->sid6->sid[i]) != 0)
+	  return 0;
 
     return 1;
 
diff --git a/proto/static/static.h b/proto/static/static.h
index edd2dc22..d7dd80a5 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -51,7 +51,10 @@ struct static_route {
   uint mpls_label;			/* Local MPLS label, -1 if unused */
   struct bfd_request *bfd_req;		/* BFD request, if BFD is used */
   union {
-    mpls_label_stack *mls;		/* MPLS label stack; may be NULL */
+    struct {
+      mpls_label_stack *mls;		/* MPLS label stack; may be NULL */
+      srv6_sid_stack *sid6;		/* SRv6 SID; may be NULL */
+    };
     adata *aspa;			/* ASPA provider list; may be NULL */
   };
 };
-- 
2.39.5

