Commit 502d746dd5 for frr

commit 502d746dd5bb4f1746c1d4a829afc5e1a5da493d
Author: Philippe Guibert <philippe.guibert@6wind.com>
Date:   Tue Jun 16 18:09:32 2026 +0200

    isisd: implement rfc7981 appendix A

    On IPv6 only networks, the router identifier is unset: isisd only uses
    the IPv4 router-id provided by zebra, which is 0.0.0.0 when no IPv4
    address and no router-id are configured. Consequently, the router
    capability tlv is not sent in LSP packets, and the incoming device
    receives an IPv6 IP reachability TLV without capability TLV.
    Consequently, the incoming device is unable to compute the label that
    will be associated with the incoming IPv6 prefix, because the SRGB
    sub-tlv does not tell which SR range to use.

    This is compliant to RFC4971, but not with RFC7981: a solution consists
    in using a null router-id, provided that either TE router ID or TE ipv6
    router-id is present. Implement this proposed solution. With a null
    router-id, the Router Capability TLV carries the IPv6 TE Router ID
    sub-TLV, as added by "isisd: add the IPv6 TE router ID sub-TLV to the
    router capability".

    Following the order of RFC7981 section 3, this changes the TLVs advertised
    when an MPLS-TE router-address is configured:
    - the Router ID of the Router Capability TLV is the TE router-address,
      instead of the router-id;
    - when no router-id is available, the Router Capability TLV is advertised
      with an IPv4 or IPv6 TE router-address, and the TE Router ID TLV (134)
      with an IPv4 TE router-address;
    - when a router-id is also available, the IP Interface Address TLV (132)
      carries the TE router-address instead of the router-id.
    Without MPLS-TE router-address, the advertised TLVs are unchanged.

    This has been tested with the isis_sr_ipv6_only topotest: pe1, p and pe2
    have no IPv4 address, and p has the following configuration:
    > router isis 1
    >  mpls-te on
    >  mpls-te router-address ipv6 2001:db8::2

    Before, p advertises no Router Capability TLV, and pe1 installs no label
    for the pe2 loopback:
    > pe1# show isis database detail
    > [..]
    > 0000.0000.0002.00-00      128   0x00000003  0xc4ab    1092    0/0/0
    >   Hostname: p
    >   Protocols Supported: IPv6
    >   Area Address: 49.0001
    >   IPv6 TE Router ID: 2001:db8::2
    >   Extended Reachability: 0000.0000.0001.00 (Metric: 10)
    > [..]
    > pe1# show isis route
    > [..]
    >  Prefix           Metric  Interface  Nexthop                  Label(s)
    >  ----------------------------------------------------------------------------
    >  2001:db8::1/128  0       -          -                        -
    >  2001:db8::2/128  20      pe1-eth0   fe80::cf:10ff:fe15:d6cc  implicit-null
    >  2001:db8::3/128  30      pe1-eth0   fe80::cf:10ff:fe15:d6cc  -

    After, p advertises a Router Capability TLV with a Router ID of 0.0.0.0
    and its SRGB, and pe1 installs the label of the pe2 loopback:
    > pe1# show isis database detail
    > [..]
    > 0000.0000.0002.00-00      160   0x00000003  0xb2db    1155    0/0/0
    >   Hostname: p
    >   Protocols Supported: IPv6
    >   Area Address: 49.0001
    >   IPv6 TE Router ID: 2001:db8::2
    >   Router Capability: 0.0.0.0 , D:0, S:0
    >     Segment Routing: I:1 V:1, Global Block Base: 20000 Range: 5001
    >     SR Local Block Base: 15000 Range: 1000
    >     SR Algorithm:
    >       0: SPF
    >   Extended Reachability: 0000.0000.0001.00 (Metric: 10)
    > [..]
    > pe1# show isis route
    > [..]
    >  Prefix           Metric  Interface  Nexthop                   Label(s)
    >  -----------------------------------------------------------------------------
    >  2001:db8::1/128  0       -          -                         -
    >  2001:db8::2/128  20      pe1-eth0   fe80::801:d3ff:fea6:daa6  implicit-null
    >  2001:db8::3/128  30      pe1-eth0   fe80::801:d3ff:fea6:daa6  20003

    Link: https://github.com/FRRouting/frr/issues/20121
    Link: https://datatracker.ietf.org/doc/html/rfc7981#appendix-A

    Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>

diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 800718c98e..2e08a34f93 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -1060,6 +1060,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
 	int level = lsp->level;
 	struct listnode *node;
 	struct isis_lsp *frag;
+	uint32_t router_id = 0;
+	bool router_capability_authorized = false;

 	lsp_clear_data(lsp);
 	for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag))
@@ -1126,7 +1128,28 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
 	}

 	/* Add Router Capability TLV. */
-	if (area->isis->router_id != 0) {
+	if (area->newmetric && IS_MPLS_TE(area->mta) && area->mta->router_id.s_addr != INADDR_ANY) {
+		/* RFC7981: The Router ID SHOULD be identical to the value advertised
+		 * in the Traffic Engineering Router ID TLV (134)
+		 */
+		router_id = area->mta->router_id.s_addr;
+		router_capability_authorized = true;
+	} else if (area->isis->router_id != 0) {
+		/* RFC7981: If no Traffic Engineering Router ID is assigned, the Router ID
+		 * SHOULD be identical to an IP Interface Address [RFC1195]
+		 * advertised by the originating IS.
+		 */
+		router_id = area->isis->router_id;
+		router_capability_authorized = true;
+	} else if (area->newmetric && IS_MPLS_TE(area->mta) &&
+		   !IN6_IS_ADDR_UNSPECIFIED(&area->mta->router_id_ipv6))
+		/* RFC7981: If the originating node does not support IPv4, then the
+		 * reserved value 0.0.0.0 MUST be used in the Router ID field,
+		 * and the IPv6 TE Router ID sub-TLV [RFC5316] MUST be present in the TLV.
+		 */
+		router_capability_authorized = true;
+
+	if (router_capability_authorized) {
 		struct isis_router_cap *rcap;
 #ifndef FABRICD
 		struct isis_router_cap_fad *rcap_fad;
@@ -1135,7 +1158,7 @@ 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;
+		rcap->router_id.s_addr = router_id;
 		if (area->newmetric && IS_MPLS_TE(area->mta))
 			rcap->te_router_id_ipv6 = area->mta->router_id_ipv6;

@@ -1255,24 +1278,31 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
 	 * into LSP. TE router ID will be the same if MPLS-TE
 	 * is not activate or MPLS-TE router-id not specified
 	 */
-	if (area->isis->router_id != 0) {
-		struct in_addr id = {.s_addr = area->isis->router_id};
-		lsp_debug("ISIS (%s): Adding router ID %pI4 as IPv4 tlv.",
-			  area->area_tag, &id);
-		isis_tlvs_add_ipv4_address(lsp->tlvs, &id);
+	if (router_capability_authorized) {
+		if (area->isis->router_id != 0) {
+			lsp_debug("ISIS (%s): Adding router ID %pI4 as IPv4 tlv.", area->area_tag,
+				  (struct in_addr *)&router_id);
+			isis_tlvs_add_ipv4_address(lsp->tlvs, (struct in_addr *)&router_id);
+		}

 		/* If new style TLV's are in use, add TE router ID TLV
 		 * Check if MPLS-TE is activate and mpls-te router-id set
 		 * otherwise add exactly same data as for IPv4 address
 		 */
 		if (area->newmetric) {
-			if (IS_MPLS_TE(area->mta)
-			    && area->mta->router_id.s_addr != INADDR_ANY)
-				id.s_addr = area->mta->router_id.s_addr;
-			lsp_debug(
-				"ISIS (%s): Adding router ID also as TE router ID tlv.",
-				area->area_tag);
-			isis_tlvs_set_te_router_id(lsp->tlvs, &id);
+			if (IS_MPLS_TE(area->mta) && router_id != 0 &&
+			    area->isis->router_id == router_id &&
+			    area->mta->router_id.s_addr != INADDR_ANY)
+				/* RFC7981: The Router ID SHOULD be identical to the value advertised
+				 * in the Traffic Engineering Router ID TLV (134)
+				 * update router-id of the TE
+				 */
+				router_id = area->mta->router_id.s_addr;
+			if (router_id != 0) {
+				lsp_debug("ISIS (%s): Adding router ID as TE router ID %pI4 tlv.",
+					  area->area_tag, (struct in_addr *)&router_id);
+				isis_tlvs_set_te_router_id(lsp->tlvs, (struct in_addr *)&router_id);
+			}
 		}
 	} else {
 		lsp_debug("ISIS (%s): Router ID is unset. Not adding tlv.",