Commit 5e9cdab34c for frr
commit 5e9cdab34cd56596d9ccc188aa640bffd2ac3544
Author: Philippe Guibert <philippe.guibert@6wind.com>
Date: Mon Oct 26 10:32:36 2026 +0100
topotests: add isis_sr_ipv6_only test
Add a test for an IPv6-only SR-MPLS network, as reported in issue #20121:
pe1, p and pe2 have no IPv4 address, and pe1 needs the SRGB of its next
hop p to install the label of the pe2 loopback.
The test checks, on pe1, the Router Capability TLV of p and the label of
the pe2 loopback route:
- without router-id, p advertises no Router Capability TLV, and there is
no label;
- with an IPv6 MPLS-TE router-address on p, the Router Capability TLV has
a Router ID of 0.0.0.0 and the IPv6 TE Router ID sub-TLV, and the label
is installed;
- with an IPv4 MPLS-TE router-address on p, the Router ID is this
address, and the label is installed.
Link: https://github.com/FRRouting/frr/issues/20121
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
diff --git a/tests/topotests/isis_sr_ipv6_only/p/frr.conf b/tests/topotests/isis_sr_ipv6_only/p/frr.conf
new file mode 100644
index 0000000000..0cd6299f24
--- /dev/null
+++ b/tests/topotests/isis_sr_ipv6_only/p/frr.conf
@@ -0,0 +1,25 @@
+hostname p
+!
+interface lo
+ ipv6 address 2001:db8::2/128
+ ipv6 router isis 1
+ isis passive
+exit
+!
+interface p-eth0
+ ipv6 router isis 1
+ isis network point-to-point
+exit
+!
+interface p-eth1
+ ipv6 router isis 1
+ isis network point-to-point
+exit
+!
+router isis 1
+ net 49.0001.0000.0000.0002.00
+ is-type level-2-only
+ segment-routing on
+ segment-routing global-block 20000 25000
+ segment-routing prefix 2001:db8::2/128 index 2
+exit
diff --git a/tests/topotests/isis_sr_ipv6_only/pe1/frr.conf b/tests/topotests/isis_sr_ipv6_only/pe1/frr.conf
new file mode 100644
index 0000000000..570eeacfa2
--- /dev/null
+++ b/tests/topotests/isis_sr_ipv6_only/pe1/frr.conf
@@ -0,0 +1,20 @@
+hostname pe1
+!
+interface lo
+ ipv6 address 2001:db8::1/128
+ ipv6 router isis 1
+ isis passive
+exit
+!
+interface pe1-eth0
+ ipv6 router isis 1
+ isis network point-to-point
+exit
+!
+router isis 1
+ net 49.0001.0000.0000.0001.00
+ is-type level-2-only
+ segment-routing on
+ segment-routing global-block 20000 25000
+ segment-routing prefix 2001:db8::1/128 index 1
+exit
diff --git a/tests/topotests/isis_sr_ipv6_only/pe2/frr.conf b/tests/topotests/isis_sr_ipv6_only/pe2/frr.conf
new file mode 100644
index 0000000000..6cf15317ee
--- /dev/null
+++ b/tests/topotests/isis_sr_ipv6_only/pe2/frr.conf
@@ -0,0 +1,20 @@
+hostname pe2
+!
+interface lo
+ ipv6 address 2001:db8::3/128
+ ipv6 router isis 1
+ isis passive
+exit
+!
+interface pe2-eth0
+ ipv6 router isis 1
+ isis network point-to-point
+exit
+!
+router isis 1
+ net 49.0001.0000.0000.0003.00
+ is-type level-2-only
+ segment-routing on
+ segment-routing global-block 20000 25000
+ segment-routing prefix 2001:db8::3/128 index 3
+exit
diff --git a/tests/topotests/isis_sr_ipv6_only/test_isis_sr_ipv6_only.py b/tests/topotests/isis_sr_ipv6_only/test_isis_sr_ipv6_only.py
new file mode 100644
index 0000000000..44534c22df
--- /dev/null
+++ b/tests/topotests/isis_sr_ipv6_only/test_isis_sr_ipv6_only.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright 2026 6WIND S.A.
+#
+
+"""
+Check that an IS-IS router without any IPv4 address advertises its SRGB
+when an MPLS-TE router-address is configured (RFC 7981 appendix A), with
+the IPv6 TE Router ID sub-TLV when the Router ID is 0.0.0.0.
+
+ lo: 2001:db8::1 lo: 2001:db8::2 lo: 2001:db8::3
+ +-----+ +-----+ +-----+
+ | pe1 +--------------+ p +--------------+ pe2 |
+ +-----+ +-----+ +-----+
+
+pe1 needs the SRGB of its nexthop p to compute the label of 2001:db8::3/128.
+"""
+
+import os
+import sys
+
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.common_config import retry, step
+from lib.topogen import Topogen, get_topogen
+
+pytestmark = [pytest.mark.isisd]
+
+
+def build_topo(tgen):
+ for router in ("pe1", "p", "pe2"):
+ tgen.add_router(router)
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["pe1"])
+ switch.add_link(tgen.gears["p"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["p"])
+ switch.add_link(tgen.gears["pe2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ for router in tgen.routers().values():
+ router.load_frr_config()
+
+ tgen.start_router()
+
+
+def teardown_module():
+ get_topogen().stop_topology()
+
+
+@retry(retry_timeout=60)
+def check_pe1_router_cap_and_label(router_cap, labels):
+ pe1 = get_topogen().gears["pe1"]
+
+ output = pe1.vtysh_cmd("show isis database detail p.00-00 json", isjson=True)
+ expected = {
+ "areas": [
+ {
+ "levels": [
+ {
+ "id": 2,
+ "lsps": [
+ {"lsp": {"id": "p.00-00"}, "routerCapability": router_cap}
+ ],
+ }
+ ]
+ }
+ ]
+ }
+ result = topotest.json_cmp(output, expected)
+ if result is not None:
+ return "p LSP: {}".format(result)
+
+ output = pe1.vtysh_cmd("show ipv6 route 2001:db8::3/128 json", isjson=True)
+ expected = {
+ "2001:db8::3/128": [
+ {"protocol": "isis", "selected": True, "nexthops": [{"labels": labels}]}
+ ]
+ }
+ result = topotest.json_cmp(output, expected)
+ if result is not None:
+ return "pe1 route to pe2: {}".format(result)
+
+ return True
+
+
+def test_isis_no_router_id():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Without router-id, p sends no router capability: no label to pe2")
+ result = check_pe1_router_cap_and_label(None, None)
+ assert result is True, result
+
+
+def test_isis_mpls_te_ipv6_router_address():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Set an IPv6 TE router-address on p: router capability with id 0.0.0.0")
+ tgen.gears["p"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ mpls-te on
+ mpls-te router-address ipv6 2001:db8::2
+ """
+ )
+ result = check_pe1_router_cap_and_label(
+ {"id": "0.0.0.0", "ipv6TeRouterId": "2001:db8::2"}, [20003]
+ )
+ assert result is True, result
+
+
+def test_isis_mpls_te_ipv4_router_address():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Set an IPv4 TE router-address on p: router capability with this id")
+ tgen.gears["p"].vtysh_cmd(
+ """
+ configure terminal
+ router isis 1
+ mpls-te router-address 10.0.0.2
+ """
+ )
+ result = check_pe1_router_cap_and_label(
+ {"id": "10.0.0.2", "ipv6TeRouterId": "2001:db8::2"}, [20003]
+ )
+ assert result is True, result
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))