]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#4358] Fixed L2 relay support
authorFrancis Dupont <fdupont@isc.org>
Sat, 9 May 2026 13:25:40 +0000 (15:25 +0200)
committerFrancis Dupont <fdupont@isc.org>
Wed, 27 May 2026 15:01:30 +0000 (17:01 +0200)
changelog_unreleased/4358-dhcpv6-through-any-relay-option18-interface-id [new file with mode: 0644]
src/lib/dhcpsrv/cfg_subnets6.cc
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc

diff --git a/changelog_unreleased/4358-dhcpv6-through-any-relay-option18-interface-id b/changelog_unreleased/4358-dhcpv6-through-any-relay-option18-interface-id
new file mode 100644 (file)
index 0000000..7746658
--- /dev/null
@@ -0,0 +1,6 @@
+[bug]          fdupont
+       Extended DHCPv6 relayed query processing to consider
+       the 'interface-id' option (18) in subnet selection
+       even when no relay sets its link-address to a global
+       unicast address, e.g. is a layer-2 relay.
+       (Gitlab #4358)
index 2cafa726bb9d98ab3f297fd6d33c8a7584115802..fec39746a6d242479fc7f0ed9b092a2e935f0863 100644 (file)
@@ -211,6 +211,7 @@ CfgSubnets6::initSelector(const Pkt6Ptr& query) {
     // Initialize fields specific to relayed messages.
     if (!query->relay_info_.empty()) {
         for (auto const& relay : boost::adaptors::reverse(query->relay_info_)) {
+            // Note that a link local address is useless so skip it.
             if (!relay.linkaddr_.isV6Zero() &&
                 !relay.linkaddr_.isV6LinkLocal()) {
                 selector.first_relay_linkaddr_ = relay.linkaddr_;
@@ -229,9 +230,10 @@ ConstSubnet6Ptr
 CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
     ConstSubnet6Ptr subnet;
 
-    // If relay agent link address is set to zero it means that we're dealing
-    // with a directly connected client.
-    if (selector.first_relay_linkaddr_ == IOAddress("::")) {
+    // If relay agent link address is set to zero and there is no interface id
+    // it means that we're dealing with a directly connected client.
+    if ((selector.first_relay_linkaddr_ == IOAddress("::")) &&
+        !selector.interface_id_) {
         // If interface name is known try to match it with interface names
         // specified for configured subnets.
         if (!selector.iface_name_.empty()) {
@@ -252,7 +254,7 @@ CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
 
         // If Interface ID option could not be matched for any subnet, try
         // the relay agent link address.
-        if (!subnet) {
+        if (!subnet && (selector.first_relay_linkaddr_ != IOAddress("::"))) {
             subnet = selectSubnet(selector.first_relay_linkaddr_,
                                   selector.client_classes_,
                                   true);
index 3e622cbc56b806b3d8f1dc6b501ea640d37aa8ea..0cae6cc8956b8658b5d2ba3eecacd8810e5ed141 100644 (file)
@@ -372,7 +372,7 @@ TEST(CfgSubnets6Test, selectSubnetByNetworkRelayAddress) {
 }
 
 // This test checks that the subnet can be selected using an interface
-// name associated with a asubnet.
+// name associated with a subnet.
 TEST(CfgSubnets6Test, selectSubnetByInterfaceName) {
     CfgSubnets6 cfg;
 
@@ -422,7 +422,7 @@ TEST(CfgSubnets6Test, selectSubnetByInterfaceName) {
 }
 
 // This test checks that the subnet can be selected using an Interface ID
-// option inserted by a relay.
+// option inserted by a relay (TODO: merge with the L2 variant).
 TEST(CfgSubnets6Test, selectSubnetByInterfaceId) {
     CfgSubnets6 cfg;
 
@@ -482,6 +482,65 @@ TEST(CfgSubnets6Test, selectSubnetByInterfaceId) {
     EXPECT_FALSE(cfg.selectSubnet(selector));
 }
 
+// This test checks that the subnet can be selected using an Interface ID
+// option inserted by a L2 relay.
+TEST(CfgSubnets6Test, selectSubnetByInterfaceIdL2) {
+    CfgSubnets6 cfg;
+
+    // Create 3 subnets.
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"),
+                                   48, 1, 2, 3, 4, SubnetID(1)));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"),
+                                   48, 1, 2, 3, 4, SubnetID(2)));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"),
+                                   48, 1, 2, 3, 4, SubnetID(3)));
+
+    // Create Interface-id options used in subnets 1,2, and 3
+    OptionPtr ifaceid1 = generateInterfaceId("relay1.eth0");
+    OptionPtr ifaceid2 = generateInterfaceId("VL32");
+    // That's a strange interface-id, but this is a real life example
+    OptionPtr ifaceid3 = generateInterfaceId("ISAM144|299|ipv6|nt:vp:1:110");
+
+    // Bogus interface-id.
+    OptionPtr ifaceid_bogus = generateInterfaceId("non-existent");
+
+    // Assign interface ids to the respective subnets.
+    subnet1->setInterfaceId(ifaceid1);
+    subnet2->setInterfaceId(ifaceid2);
+    subnet3->setInterfaceId(ifaceid3);
+
+    // There shouldn't be any subnet configured at this stage.
+    SubnetSelector selector;
+    selector.interface_id_ = ifaceid1;
+    // No longer need to set the relay link address.
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Add one of the subnets.
+    cfg.add(subnet1);
+
+    // If only one subnet has been specified, it should be returned when the
+    // interface id matches. But, for a different interface id there should be
+    // no match.
+    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
+    selector.interface_id_ = ifaceid2;
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Add other subnets.
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    // Now that we have all subnets added. we should be able to retrieve them
+    // using appropriate interface ids.
+    selector.interface_id_ = ifaceid3;
+    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
+    selector.interface_id_ = ifaceid2;
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+
+    // For invalid interface id, there should be nothing returned.
+    selector.interface_id_ = ifaceid_bogus;
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+}
+
 // Test that the client classes are considered when the subnet is selected by
 // the relay link address.
 TEST(CfgSubnets6Test, selectSubnetByRelayAddressAndClassify) {
@@ -626,7 +685,7 @@ TEST(CfgSubnets6Test, selectSubnetByInterfaceIdAndClassify) {
     // If we have only a single subnet and the request came from a local
     // address, let's use that subnet
     SubnetSelector selector;
-    selector.first_relay_linkaddr_ = IOAddress("5000::1");
+    // No longer need to set the relay link address.
     selector.client_classes_.insert("bar");
     selector.interface_id_ = ifaceid1;
     EXPECT_EQ(subnet1, cfg.selectSubnet(selector));