From 78d714a6b770e66b92435d2bb93e5d26233c9312 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Wed, 26 Feb 2025 02:32:28 +0100 Subject: [PATCH] [#3683] Checkpoint: created dedicated UT file --- src/bin/dhcp6/tests/Makefile.am | 1 + src/bin/dhcp6/tests/addr_reg_unittest.cc | 410 ++++++++++++++++++ .../dhcp6/tests/ctrl_dhcp6_srv_unittest.cc | 3 + src/bin/dhcp6/tests/dhcp6_client.cc | 16 +- src/bin/dhcp6/tests/dhcp6_client.h | 8 +- src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 5 + src/bin/dhcp6/tests/dhcp6_test_utils.cc | 2 +- src/bin/dhcp6/tests/dhcp6_test_utils.h | 24 +- .../tests/http_control_socket_unittest.cc | 3 + src/bin/dhcp6/tests/meson.build | 1 + 10 files changed, 469 insertions(+), 4 deletions(-) create mode 100644 src/bin/dhcp6/tests/addr_reg_unittest.cc diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 4bafa819d7..c2a9ff32bb 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -87,6 +87,7 @@ PROGRAM_TESTS = dhcp6_unittests # This list is ordered alphabetically. When adding new files, please maintain # this order. dhcp6_unittests_SOURCES = +dhcp6_unittests_SOURCES += addr_reg_unittest.cc dhcp6_unittests_SOURCES += classify_unittest.cc dhcp6_unittests_SOURCES += client_handler_unittest.cc dhcp6_unittests_SOURCES += config_parser_unittest.cc diff --git a/src/bin/dhcp6/tests/addr_reg_unittest.cc b/src/bin/dhcp6/tests/addr_reg_unittest.cc new file mode 100644 index 0000000000..84dfbb872e --- /dev/null +++ b/src/bin/dhcp6/tests/addr_reg_unittest.cc @@ -0,0 +1,410 @@ +// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include +#include + +using namespace isc; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace std; + +namespace { + +/// @brief Test fixture class for testing address registration.. +class AddrRegTest : public Dhcpv6MessageTest { +public: + + /// @brief Constructor. + /// + /// Sets up fake interfaces. + AddrRegTest() : Dhcpv6MessageTest() { + } + + /// @brief Generate IAADDR option with specified parameters. + /// + /// @param addr The address. + /// @param preferred_lft The preferred lifetime. + /// @param valid_lft The valid lifetime. + Option6IAAddrPtr generateIAAddr(const IOAddress& addr, + uint32_t preferred_lft, + uint32_t valid_lft) { + Option6IAAddrPtr iaaddr(new Option6IAAddr(D6O_IAADDR, addr, + preferred_lft, valid_lft)); + return (iaaddr); + } + + /// @brief Generate IAADDR option with specified parameters. + /// + /// @param addr The address in textual form. + /// @param preferred_lft The preferred lifetime. + /// @param valid_lft The valid lifetime. + Option6IAAddrPtr generateIAAddr(const string& addr, + uint32_t preferred_lft, + uint32_t valid_lft) { + return (generateIAAddr(IOAddress(addr), preferred_lft, valid_lft)); + } + + /// @brief Basic configuration. + string config_ = "{\n" + "\"interfaces-config\": { \"interfaces\": [ \"*\" ] },\n" + "\"valid-lifetime\": 4000,\n" + "\"preferred-lifetime\": 3000,\n" + "\"rebind-timer\": 2000,\n" + "\"renew-timer\": 1000,\n" + "\"subnet6\": [ {\n" + " \"id\": 1,\n" + " \"subnet\": \"2001:db8:1::/64\",\n" + " \"interface\": \"eth0\"\n" + "} ]\n" + "}\n"; +}; + +// Test that client-id is mandatory and server-id forbidden for +// Addr-reg-inform messages +TEST_F(AddrRegTest, sanityCheck) { + // A message with no client-id should fail + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + EXPECT_FALSE(srv_.sanityCheck(addr_reg_inf)); + + // A message with a single client-id should succeed + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + EXPECT_TRUE(srv_.sanityCheck(addr_reg_inf)); + + // A message with server-id present should fail + addr_reg_inf->addOption(srv_.getServerID()); + EXPECT_FALSE(srv_.sanityCheck(addr_reg_inf)); +} + +// Test that subnet selection must return a subnet for processAddrRegInform. +TEST_F(AddrRegTest, noSubnet) { + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("fe80::abcd")); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_FALSE(ctx.subnet_); + + // No server: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + // The query is silently rejected so no log to check. +} + +// Test that an IA_NA option is required. +TEST_F(AddrRegTest, noIA_NA) { + IfaceMgrTestConfig test_config(true); + + ASSERT_NO_THROW(configure(config_)); + + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("fe80::abcd")); + addr_reg_inf->setIface("eth0"); + addr_reg_inf->setIndex(ETH0_INDEX); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_TRUE(ctx.subnet_); + + // No IA_NA: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + string expected = "DHCP6_ADDR_REG_INFORM_FAIL "; + expected += "error on addr-reg-inform from client fe80::abcd: "; + expected += "Exactly 1 IA_NA option expected, but 0 received"; + EXPECT_EQ(1, countFile(expected)); +} + +// Test that exactly one IA_NA option is required. +TEST_F(AddrRegTest, twoIA_NAs) { + IfaceMgrTestConfig test_config(true); + + ASSERT_NO_THROW(configure(config_)); + + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("fe80::abcd")); + addr_reg_inf->setIface("eth0"); + addr_reg_inf->setIndex(ETH0_INDEX); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + addr_reg_inf->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000)); + addr_reg_inf->addOption(generateIA(D6O_IA_NA, 345, 1500, 3000)); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_TRUE(ctx.subnet_); + + // 2 IA_NA options: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + string expected = "DHCP6_ADDR_REG_INFORM_FAIL "; + expected += "error on addr-reg-inform from client fe80::abcd: "; + expected += "Exactly 1 IA_NA option expected, but 2 received"; + EXPECT_EQ(1, countFile(expected)); +} + +// Test that an IA_NA sub-option is required. +TEST_F(AddrRegTest, noIA_NAsub) { + IfaceMgrTestConfig test_config(true); + + ASSERT_NO_THROW(configure(config_)); + + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("fe80::abcd")); + addr_reg_inf->setIface("eth0"); + addr_reg_inf->setIndex(ETH0_INDEX); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + addr_reg_inf->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000)); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_TRUE(ctx.subnet_); + + // No IA_NA sub-options: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + string expected = "DHCP6_ADDR_REG_INFORM_FAIL "; + expected += "error on addr-reg-inform from client fe80::abcd: "; + expected += "Exactly 1 IA_NA sub-option expected, but 0 received"; + EXPECT_EQ(1, countFile(expected)); +} + +// Test that an exactly one IA_NA sub-option is required. +TEST_F(AddrRegTest, twoIA_NAsub) { + IfaceMgrTestConfig test_config(true); + + ASSERT_NO_THROW(configure(config_)); + + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("fe80::abcd")); + addr_reg_inf->setIface("eth0"); + addr_reg_inf->setIndex(ETH0_INDEX); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + auto ia = generateIA(D6O_IA_NA, 234, 1500, 3000); + ia->addOption(generateIAAddr("2001:db8:1::1", 3000, 4000)); + ia->addOption(generateIAAddr("2001:db8:1::2", 3000, 4000)); + addr_reg_inf->addOption(ia); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_TRUE(ctx.subnet_); + + // Two IA_NA sub-options: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + string expected = "DHCP6_ADDR_REG_INFORM_FAIL "; + expected += "error on addr-reg-inform from client fe80::abcd: "; + expected += "Exactly 1 IA_NA sub-option expected, but 2 received"; + EXPECT_EQ(1, countFile(expected)); +} + +// Test that addresses must match. +TEST_F(AddrRegTest, noAddrMatch) { + IfaceMgrTestConfig test_config(true); + + ASSERT_NO_THROW(configure(config_)); + + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("fe80::abcd")); + addr_reg_inf->setIface("eth0"); + addr_reg_inf->setIndex(ETH0_INDEX); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + auto ia = generateIA(D6O_IA_NA, 234, 1500, 3000); + ia->addOption(generateIAAddr("2001:db8:1::1", 3000, 4000)); + addr_reg_inf->addOption(ia); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_TRUE(ctx.subnet_); + + // Address mismatch: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + string expected = "DHCP6_ADDR_REG_INFORM_FAIL "; + expected += "error on addr-reg-inform from client fe80::abcd: "; + expected += "Address mismatch: client at fe80::abcd "; + expected += "wants to register 2001:db8:1::1"; + EXPECT_EQ(1, countFile(expected)); +} + +// Test that addresses must match with a relay. +TEST_F(AddrRegTest, noAddrMatchRelay) { + IfaceMgrTestConfig test_config(true); + + ASSERT_NO_THROW(configure(config_)); + + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("fe80::abcd")); + addr_reg_inf->setIface("eth0"); + addr_reg_inf->setIndex(ETH0_INDEX); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + auto ia = generateIA(D6O_IA_NA, 234, 1500, 3000); + ia->addOption(generateIAAddr("2001:db8:1::1", 3000, 4000)); + addr_reg_inf->addOption(ia); + + // Add a relay. + Pkt6::RelayInfo relay; + relay.linkaddr_ = IOAddress("fe80::ef01"); + relay.peeraddr_ = IOAddress("fe80::1"); + addr_reg_inf->relay_info_.push_back(relay); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_TRUE(ctx.subnet_); + + // Address mismatch: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + string expected = "DHCP6_ADDR_REG_INFORM_FAIL "; + expected += "error on addr-reg-inform from client fe80::ef01: "; + expected += "Address mismatch: client at fe80::ef01 "; + expected += "wants to register 2001:db8:1::1"; + EXPECT_EQ(1, countFile(expected)); +} + +// Test that addresses must match with relays. +TEST_F(AddrRegTest, noAddrMatch2Relays) { + IfaceMgrTestConfig test_config(true); + + ASSERT_NO_THROW(configure(config_)); + + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("fe80::abcd")); + addr_reg_inf->setIface("eth0"); + addr_reg_inf->setIndex(ETH0_INDEX); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + auto ia = generateIA(D6O_IA_NA, 234, 1500, 3000); + ia->addOption(generateIAAddr("2001:db8:1::1", 3000, 4000)); + addr_reg_inf->addOption(ia); + + // Add a relay: it will be the outer one. + Pkt6::RelayInfo relay; + relay.linkaddr_ = IOAddress("fe80::ef01"); + relay.peeraddr_ = IOAddress("fe80::1"); + addr_reg_inf->relay_info_.push_back(relay); + + // Add a second relay: it will be the inner one. + Pkt6::RelayInfo relay2; + relay2.linkaddr_ = IOAddress("fe80::2345"); + relay2.peeraddr_ = IOAddress("fe80::1"); + addr_reg_inf->relay_info_.push_back(relay2); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_TRUE(ctx.subnet_); + + // Address mismatch: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + string expected = "DHCP6_ADDR_REG_INFORM_FAIL "; + expected += "error on addr-reg-inform from client fe80::2345: "; + expected += "Address mismatch: client at fe80::2345 "; + expected += "wants to register 2001:db8:1::1"; + EXPECT_EQ(1, countFile(expected)); +} + +// Test that the registering address must be in the subnet. +TEST_F(AddrRegTest, noInSubnet) { + IfaceMgrTestConfig test_config(true); + + ASSERT_NO_THROW(configure(config_)); + + Pkt6Ptr addr_reg_inf = Pkt6Ptr(new Pkt6(DHCPV6_ADDR_REG_INFORM, 1234)); + addr_reg_inf->setRemoteAddr(IOAddress("2001:db8::1")); + addr_reg_inf->setIface("eth0"); + addr_reg_inf->setIndex(ETH0_INDEX); + OptionPtr clientid = generateClientId(); + addr_reg_inf->addOption(clientid); + auto ia = generateIA(D6O_IA_NA, 234, 1500, 3000); + ia->addOption(generateIAAddr("2001:db8::1", 3000, 4000)); + addr_reg_inf->addOption(ia); + + // Pass it to the server. + AllocEngine::ClientContext6 ctx; + bool drop = !srv_.earlyGHRLookup(addr_reg_inf, ctx); + ASSERT_FALSE(drop); + ctx.subnet_ = srv_.selectSubnet(addr_reg_inf, drop); + ASSERT_FALSE(drop); + srv_.initContext(ctx, drop); + ASSERT_FALSE(drop); + ASSERT_TRUE(ctx.subnet_); + + // Not in subnet: no response. + EXPECT_FALSE(srv_.processAddrRegInform(ctx)); + + string expected = "DHCP6_ADDR_REG_INFORM_FAIL "; + expected += "error on addr-reg-inform from client 2001:db8::1: "; + expected += "Address 2001:db8::1 is not in subnet "; + expected += "2001:db8:1::/64 (id 1)"; + EXPECT_EQ(1, countFile(expected)); +} + +} // end of anonymous namespace diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index adfc0892b4..150a668d28 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -617,11 +617,14 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelStats) { "pkt6-infrequest-received", "pkt6-dhcpv4-query-received", "pkt6-dhcpv4-response-received", + "pkt6-addr-reg-inform-received", + "pkt6-addr-reg-reply-received", "pkt6-unknown-received", "pkt6-sent", "pkt6-advertise-sent", "pkt6-reply-sent", "pkt6-dhcpv4-response-sent", + "pkt6-addr-reg-reply-sent", "pkt6-parse-failed", "pkt6-receive-drop", "v6-allocation-fail", diff --git a/src/bin/dhcp6/tests/dhcp6_client.cc b/src/bin/dhcp6/tests/dhcp6_client.cc index cef3bcaf63..6034b7fd35 100644 --- a/src/bin/dhcp6/tests/dhcp6_client.cc +++ b/src/bin/dhcp6/tests/dhcp6_client.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2024 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2025 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -608,6 +608,20 @@ Dhcp6Client::doRelease() { } } +void +Dhcp6Client::doAddrRegInform() { + context_.query_ = createMsg(DHCPV6_ADDR_REG_INFORM); + + // Append requested IAs. + appendRequestedIAs(context_.query_); + + // Add Client FQDN if configured. + appendFQDN(); + + sendMsg(context_.query_); + context_.response_ = receiveOneMsg(); +} + void Dhcp6Client::generateIAFromLeases(const Pkt6Ptr& query, const bool include_address) { diff --git a/src/bin/dhcp6/tests/dhcp6_client.h b/src/bin/dhcp6/tests/dhcp6_client.h index e236a3d9b7..c3966bae49 100644 --- a/src/bin/dhcp6/tests/dhcp6_client.h +++ b/src/bin/dhcp6/tests/dhcp6_client.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2025 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -311,6 +311,12 @@ public: /// and receiving server's response. void doRelease(); + /// @brief This function generates Addr-reg-inform message, sends it + /// to the server and then receives the Addr-reg-reply. + /// This method does not process the response in any specific way, + /// just stores it. + void doAddrRegInform(); + /// @brief Removes the stateful configuration obtained from the server. /// /// It removes all leases held by the client. diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index bba2193593..b8ced23c46 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -3640,6 +3640,11 @@ TEST_F(Dhcpv6SrvTest, receiveDhcpv4QueryStat) { testReceiveStats(DHCPV6_DHCPV4_QUERY, "pkt6-dhcpv4-query-received"); } +// Test checks if pkt6-addr-reg-inform-received is bumped up correctly. +TEST_F(Dhcpv6SrvTest, receiveAddrRegInformStat) { + testReceiveStats(DHCPV6_ADDR_REG_INFORM, "pkt6-addr-reg-inform-received"); +} + // Test checks if reception of a malformed packet increases pkt-parse-failed // and pkt6-receive-drop TEST_F(Dhcpv6SrvTest, receiveParseFailedStat) { diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index c3a36473de..24b2a7d3ef 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2025 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h index b0e23488a1..7fe4b4c6cb 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.h +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2025 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -345,6 +345,27 @@ public: return (processDecline(ctx)); } + /// @brief Processes incoming Addr-reg-inform message. + /// + /// @param addr_reg_inf a message received from client + /// @return Addr-reg-reply message or null + Pkt6Ptr processAddrRegInform(const Pkt6Ptr& decline) { + AllocEngine::ClientContext6 ctx; + bool drop = !earlyGHRLookup(decline, ctx); + if (drop) { + return (Pkt6Ptr()); + } + ctx.subnet_ = selectSubnet(decline, drop); + if (drop) { + return (Pkt6Ptr()); + } + initContext(ctx, drop); + if (drop) { + return (Pkt6Ptr()); + } + return (processAddrRegInform(ctx)); + } + using Dhcpv6Srv::processSolicit; using Dhcpv6Srv::processRequest; using Dhcpv6Srv::processRenew; @@ -353,6 +374,7 @@ public: using Dhcpv6Srv::processRelease; using Dhcpv6Srv::processDecline; using Dhcpv6Srv::processInfRequest; + using Dhcpv6Srv::processAddrRegInform; using Dhcpv6Srv::processClientFqdn; using Dhcpv6Srv::createNameChangeRequests; using Dhcpv6Srv::selectSubnet; diff --git a/src/bin/dhcp6/tests/http_control_socket_unittest.cc b/src/bin/dhcp6/tests/http_control_socket_unittest.cc index 2a6b4f49ec..d09216e795 100644 --- a/src/bin/dhcp6/tests/http_control_socket_unittest.cc +++ b/src/bin/dhcp6/tests/http_control_socket_unittest.cc @@ -910,11 +910,14 @@ BaseCtrlChannelDhcpv6Test::testControlChannelStats() { "pkt6-infrequest-received", "pkt6-dhcpv4-query-received", "pkt6-dhcpv4-response-received", + "pkt6-addr-reg-inform-received", + "pkt6-addr-reg-reply-received", "pkt6-unknown-received", "pkt6-sent", "pkt6-advertise-sent", "pkt6-reply-sent", "pkt6-dhcpv4-response-sent", + "pkt6-addr-reg-reply-sent", "pkt6-parse-failed", "pkt6-receive-drop", "v6-allocation-fail", diff --git a/src/bin/dhcp6/tests/meson.build b/src/bin/dhcp6/tests/meson.build index 18d3cdab3b..0a894d02e0 100644 --- a/src/bin/dhcp6/tests/meson.build +++ b/src/bin/dhcp6/tests/meson.build @@ -102,6 +102,7 @@ co4 = shared_library( kea_dhcp6_tests = executable( 'kea-dhcp6-tests', + 'addr_reg_unittest.cc', 'classify_unittest.cc', 'client_handler_unittest.cc', 'config_backend_unittest.cc',