From: Francis Dupont Date: Sat, 9 May 2026 13:25:40 +0000 (+0200) Subject: [#4358] Fixed L2 relay support X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=939fbd222541ff2d6b842c8fb7bef9f66c2db2cc;p=thirdparty%2Fkea.git [#4358] Fixed L2 relay support --- 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 index 0000000000..77466582c4 --- /dev/null +++ b/changelog_unreleased/4358-dhcpv6-through-any-relay-option18-interface-id @@ -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) diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index 2cafa726bb..fec39746a6 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -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); diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index 3e622cbc56..0cae6cc895 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -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));