Commit c24210ef92 for frr
commit c24210ef92d199831d4d14375f8df6e11bc287ed
Author: Philippe Guibert <philippe.guibert@6wind.com>
Date: Mon Oct 26 12:21:50 2026 +0100
isisd: add the IPv6 TE router ID sub-TLV to the router capability
RFC7981 section 3 says that when the originating router does not
support IPv4, the Router ID of the Router Capability TLV (242) is
0.0.0.0, and the IPv6 TE Router ID sub-TLV MUST be present in the TLV.
A Router Capability TLV with a Router ID of 0.0.0.0 and without this
sub-TLV MUST NOT be used.
isisd does not implement the IPv6 TE Router ID sub-TLV (type 12, RFC9346
section 3.5.2): the IPv6 MPLS-TE router-address is only advertised in
the top-level IPv6 TE Router ID TLV (140, RFC6119).
> router isis 1
> mpls-te on
> mpls-te router-address ipv6 2001:db8::2
This was not needed as long as the Router Capability TLV is only
advertised with a non-zero IPv4 router-id, but it is a prerequisite to
advertise the Router Capability TLV on routers without IPv4 address.
The top-level TLV 140 is not a substitute: the Router ID identifies the
router that originated the Router Capability TLV, and this TLV may be
advertised in the LSP of another system, when it is leaked between
levels (RFC7981 section 3). The TLV 140 of that LSP then belongs to the
leaking system, so the originator is only known from the content of the
Router Capability TLV itself.
Add the IPv6 TE Router ID sub-TLV:
- in the Router Capability TLV, when an IPv6 MPLS-TE router-address is
configured, including in the additional Router Capability TLVs
carrying the flex-algo definitions;
- decode and display it;
- ignore the received Router Capability TLVs that have a Router ID of
0.0.0.0 without this sub-TLV.
Link: https://datatracker.ietf.org/doc/html/rfc7981#section-3
Link: https://datatracker.ietf.org/doc/html/rfc9346#section-3.5.2
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 62fce43618..800718c98e 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -1136,6 +1136,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
rcap = isis_tlvs_init_router_capability(lsp->tlvs);
rcap->router_id.s_addr = area->isis->router_id;
+ if (area->newmetric && IS_MPLS_TE(area->mta))
+ rcap->te_router_id_ipv6 = area->mta->router_id_ipv6;
#ifndef FABRICD
/* Set flex-algo definitions */
diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c
index 5898f57aee..852cfe426e 100644
--- a/isisd/isis_tlvs.c
+++ b/isisd/isis_tlvs.c
@@ -4717,7 +4717,9 @@ static void format_tlv_router_cap_json(const struct isis_router_cap *router_cap,
json_object_string_add(cap_json, "id", addrbuf);
json_object_boolean_add(cap_json, "flagD", !!(router_cap->flags & ISIS_ROUTER_CAP_FLAG_D));
json_object_boolean_add(cap_json, "flagS", !!(router_cap->flags & ISIS_ROUTER_CAP_FLAG_S));
-
+ if (!IN6_IS_ADDR_UNSPECIFIED(&router_cap->te_router_id_ipv6))
+ json_object_string_addf(cap_json, "ipv6TeRouterId", "%pI6",
+ &router_cap->te_router_id_ipv6);
/* Segment Routing Global Block as per RFC8667 section #3.1 */
if (router_cap->srgb.range_size != 0) {
@@ -4778,6 +4780,10 @@ static void format_tlv_router_cap(const struct isis_router_cap *router_cap, stru
router_cap->flags & ISIS_ROUTER_CAP_FLAG_D ? '1' : '0',
router_cap->flags & ISIS_ROUTER_CAP_FLAG_S ? '1' : '0');
+ if (!IN6_IS_ADDR_UNSPECIFIED(&router_cap->te_router_id_ipv6))
+ sbuf_push(buf, indent, " IPv6 TE Router ID: %pI6\n",
+ &router_cap->te_router_id_ipv6);
+
/* Segment Routing Global Block as per RFC8667 section #3.1 */
if (router_cap->srgb.range_size != 0)
sbuf_push(buf, indent,
@@ -4934,11 +4940,15 @@ static size_t isis_router_cap_subtlv_size(size_t *tlv_len, size_t hdr_len, size_
static size_t isis_router_cap_tlv_size(const struct isis_router_cap *router_cap)
{
size_t hdr_len = ISIS_ROUTER_CAP_SIZE;
- size_t tlv_len = hdr_len;
+ size_t tlv_len;
size_t sz;
int nb_algo, nb_msd;
bool sr_algo_subtlv_present = false;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&router_cap->te_router_id_ipv6))
+ hdr_len += ISIS_SUBTLV_HDR_SIZE + IPV6_MAX_BYTELEN;
+ tlv_len = hdr_len;
+
if ((router_cap->srgb.range_size != 0) && (router_cap->srgb.lower_bound != 0)) {
tlv_len += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE;
tlv_len += 2 + ISIS_SUBTLV_SID_LABEL_SIZE;
@@ -4997,6 +5007,13 @@ static void pack_tlv_router_cap_header(const struct isis_router_cap *router_cap,
{
stream_put_ipv4(s, router_cap->router_id.s_addr);
stream_putc(s, router_cap->flags);
+
+ /* RFC 7981 section #3: mandatory with a Router ID of 0.0.0.0 */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&router_cap->te_router_id_ipv6)) {
+ stream_putc(s, ISIS_SUBTLV_IPV6_TE_ROUTER_ID);
+ stream_putc(s, IPV6_MAX_BYTELEN);
+ stream_put(s, &router_cap->te_router_id_ipv6, IPV6_MAX_BYTELEN);
+ }
}
/*
@@ -5226,6 +5243,25 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, struct
return 0;
}
+static bool router_cap_has_ipv6_te_router_id(struct stream *s, uint8_t subtlv_len)
+{
+ size_t pos = stream_get_getp(s);
+ uint8_t type, length;
+
+ while (subtlv_len >= ISIS_SUBTLV_HDR_SIZE) {
+ type = stream_getc_from(s, pos);
+ length = stream_getc_from(s, pos + 1);
+ if (length > subtlv_len - ISIS_SUBTLV_HDR_SIZE)
+ return false;
+ if (type == ISIS_SUBTLV_IPV6_TE_ROUTER_ID && length == IPV6_MAX_BYTELEN)
+ return true;
+ pos += ISIS_SUBTLV_HDR_SIZE + length;
+ subtlv_len -= ISIS_SUBTLV_HDR_SIZE + length;
+ }
+
+ return false;
+}
+
static int unpack_tlv_router_cap(enum isis_tlv_context context, uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log, void *dest, int indent)
{
@@ -5236,6 +5272,8 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, uint8_t tlv_type
uint8_t subtlv_len;
uint8_t size;
int num_msd;
+ struct in_addr router_id;
+ uint8_t flags;
sbuf_push(log, indent, "Unpacking Router Capability TLV...\n");
if (tlv_len < ISIS_ROUTER_CAP_SIZE) {
@@ -5244,6 +5282,19 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, uint8_t tlv_type
return 0;
}
+ /* Get Router ID and Flags */
+ router_id.s_addr = stream_get_ipv4(s);
+ flags = stream_getc(s);
+ subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE;
+
+ /* RFC 7981 section #3 */
+ if (router_id.s_addr == INADDR_ANY && !router_cap_has_ipv6_te_router_id(s, subtlv_len)) {
+ sbuf_push(log, indent,
+ "WARNING: Router ID 0.0.0.0 without IPv6 TE Router ID sub-TLV, ignoring TLV\n");
+ stream_forward_getp(s, subtlv_len);
+ return 0;
+ }
+
if (!tlvs->router_cap) {
/* First Router Capability TLV.
* Allocate router cap structure and initialize SR Algorithms */
@@ -5253,13 +5304,10 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, uint8_t tlv_type
}
rcap = tlvs->router_cap;
-
- /* Get Router ID and Flags */
- rcap->router_id.s_addr = stream_get_ipv4(s);
- rcap->flags = stream_getc(s);
+ rcap->router_id = router_id;
+ rcap->flags = flags;
/* Parse remaining part of the TLV if present */
- subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE;
while (subtlv_len >= ISIS_SUBTLV_HDR_SIZE) {
#ifndef FABRICD
struct isis_router_cap_fad *fad;
@@ -5278,6 +5326,13 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, uint8_t tlv_type
}
switch (type) {
+ case ISIS_SUBTLV_IPV6_TE_ROUTER_ID:
+ if (length != IPV6_MAX_BYTELEN) {
+ stream_forward_getp(s, length);
+ break;
+ }
+ stream_get(&rcap->te_router_id_ipv6, s, IPV6_MAX_BYTELEN);
+ break;
case ISIS_SUBTLV_SID_LABEL_RANGE:
/* Check that SRGB is correctly formatted */
if (length < SUBTLV_RANGE_LABEL_SIZE || length > SUBTLV_RANGE_INDEX_SIZE) {
diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h
index ba6ea67612..e016a28d38 100644
--- a/isisd/isis_tlvs.h
+++ b/isisd/isis_tlvs.h
@@ -294,6 +294,9 @@ struct isis_router_cap {
struct in_addr router_id;
uint8_t flags;
+ /* RFC 9346 section #3.5.2 */
+ struct in6_addr te_router_id_ipv6;
+
/* RFC 8667 section #3 */
struct isis_sr_block srgb;
struct isis_sr_block srlb;
@@ -532,6 +535,9 @@ enum isis_tlv_type {
/* RFC 8491 */
ISIS_SUBTLV_NODE_MSD = 23,
+ /* RFC 9346 */
+ ISIS_SUBTLV_IPV6_TE_ROUTER_ID = 12,
+
/* RFC 5316 */
ISIS_SUBTLV_RAS = 24,
ISIS_SUBTLV_RIP = 25,
diff --git a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz
index 116a262f9e..ba52b2c3e8 100644
Binary files a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz and b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz differ