From f5f56e1a1c4d9e9496fcb9d81131066a964ccd23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 14:15:43 +0200 Subject: Adding upstream version 2.4.1. Signed-off-by: Daniel Baumann --- src/bin/dhcp6/tests/confirm_unittest.cc | 348 ++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 src/bin/dhcp6/tests/confirm_unittest.cc (limited to 'src/bin/dhcp6/tests/confirm_unittest.cc') diff --git a/src/bin/dhcp6/tests/confirm_unittest.cc b/src/bin/dhcp6/tests/confirm_unittest.cc new file mode 100644 index 0000000..f100c2b --- /dev/null +++ b/src/bin/dhcp6/tests/confirm_unittest.cc @@ -0,0 +1,348 @@ +// Copyright (C) 2014-2023 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; + +namespace { + +/// @brief Set of JSON configurations used throughout the Confirm tests. +/// +/// - Configuration 0: +/// - only addresses (no prefixes) +/// - 2 subnets with 2001:db8:1::/64 and 2001:db8:2::/64 +/// - 1 subnet for eth0 and 1 subnet for eth1 +/// +/// - Configuration 1: +/// - similar to Configuration 0 +/// - pools configured: 3000:1::/64 and 3000:2::/64 +/// - this specific configuration is used by tests using relays +/// +const char* CONFIRM_CONFIGS[] = { +// Configuration 0 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"id\": 1, " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface-id\": \"\"," + " \"interface\": \"eth0\"" + " }," + " {" + " \"id\": 2, " + " \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ]," + " \"subnet\": \"2001:db8:2::/48\", " + " \"interface-id\": \"\"," + " \"interface\": \"eth1\"" + " } ]," + "\"valid-lifetime\": 4000 }", + +// Configuration 1 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"id\": 1, " + " \"pools\": [ { \"pool\": \"3000:1::/64\" } ]," + " \"subnet\": \"3000:1::/48\", " + " \"interface-id\": \"\"," + " \"interface\": \"eth0\"" + " }," + " {" + " \"id\": 2, " + " \"pools\": [ { \"pool\": \"3000:2::/64\" } ]," + " \"subnet\": \"3000:2::/48\", " + " \"interface-id\": \"\"," + " \"interface\": \"eth1\"" + " } ]," + "\"valid-lifetime\": 4000 }" +}; + +/// @brief Test fixture class for testing Confirm.. +class ConfirmTest : public isc::dhcp::test::Dhcpv6MessageTest { +public: + + /// @brief Constructor. + /// + /// Sets up fake interfaces. + ConfirmTest() + : Dhcpv6MessageTest() { + } + +}; + + +// Test that client-id is mandatory and server-id forbidden for Confirm messages +TEST_F(ConfirmTest, sanityCheck) { + NakedDhcpv6Srv srv(0); + + // A message with no client-id should fail + Pkt6Ptr confirm = Pkt6Ptr(new Pkt6(DHCPV6_CONFIRM, 1234)); + EXPECT_FALSE(srv.sanityCheck(confirm)); + + // A message with a single client-id should succeed + OptionPtr clientid = generateClientId(); + confirm->addOption(clientid); + EXPECT_TRUE(srv.sanityCheck(confirm)); + + // A message with server-id present should fail + confirm->addOption(srv.getServerID()); + EXPECT_FALSE(srv.sanityCheck(confirm)); +} + +// Test that directly connected client's Confirm message is processed and Reply +// message is sent back. In this test case, the client sends Confirm for two +// addresses that belong to the same IAID and are sent within the same IA_NA +// option (RFC 8415, section 18.3.3). +TEST_F(ConfirmTest, directClientSameIAID) { + Dhcp6Client client; + // Configure client to request IA_NA. + client.requestAddress(); + // Make 4-way exchange to get the lease. + ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client)); + // Keep the client's lease for future reference. + Lease6 lease_client1 = client.getLease(0); + // Clone the lease and modify its address so as it is still in the range + // of the subnet to which the first lease belongs. When the client sends + // the Confirm it should include both addresses and the server should + // send Success because both of these addresses are on-link, regardless + // what the server has in the lease database. + Lease6 lease_client2 = lease_client1; + lease_client2.addr_ = bumpAddress(lease_client2.addr_); + client.createLease(lease_client2); + ASSERT_EQ(2, client.getLeaseNum()); + // Send Confirm message to the server. + ASSERT_NO_THROW(client.doConfirm()); + // Client should have received a status code option and this option should + // indicate the success. + ASSERT_TRUE(client.receivedStatusCode()); + ASSERT_EQ(STATUS_Success, client.getStatusCode()); + + ASSERT_EQ(2, client.getLeaseNum()); + lease_client2 = client.getLease(1); + lease_client2.addr_ = bumpSubnet(lease_client2.addr_); + client.createLease(lease_client2); + // Send confirm to the server. This time, one of the leases contains the + // address which doesn't belong to the configured subnet and the server + // should respond with STATUS_NotOnLink. + ASSERT_NO_THROW(client.doConfirm()); + ASSERT_TRUE(client.receivedStatusCode()); + ASSERT_EQ(STATUS_NotOnLink, client.getStatusCode()); + // Make sure that the server id has been included. + EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID)); +} + +// Test that directly connected client's Confirm message is processed and Reply +// message is sent back. In this test case, the client sends Confirm for two +// addresses that belong to different IAIDs and are sent within the different +// IA_NA options (RFC 8415, section 18.3.3). +TEST_F(ConfirmTest, directClientDifferentIAID) { + Dhcp6Client client; + // Configure client to request IA_NA. + client.requestAddress(); + // Make 4-way exchange to get the lease. + ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client)); + // Keep the client's lease for future reference. + Lease6 lease_client1 = client.getLease(0); + // Clone the lease and modify its address so as it is still in the range + // of the subnet to which the first lease belongs. When the client sends + // the Confirm it should include both addresses and the server should + // send Success because both of these addresses are on-link, regardless + // what the server has in the lease database. + Lease6 lease_client2 = lease_client1; + ++lease_client2.iaid_; + lease_client2.addr_ = bumpAddress(lease_client2.addr_); + client.createLease(lease_client2); + ASSERT_EQ(2, client.getLeaseNum()); + // Send Confirm message to the server. + ASSERT_NO_THROW(client.doConfirm()); + // Client should have received a status code option and this option should + // indicate the success. + ASSERT_TRUE(client.receivedStatusCode()); + ASSERT_EQ(STATUS_Success, client.getStatusCode()); + // Make sure that the server id and client id have been included. + EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID)); + EXPECT_TRUE(client.getContext().response_->getOption(D6O_CLIENTID)); + + ASSERT_EQ(2, client.getLeaseNum()); + lease_client2 = client.getLease(1); + lease_client2.addr_ = bumpSubnet(lease_client2.addr_); + client.createLease(lease_client2); + // Send confirm to the server. This time, one of the leases contains the + // address which doesn't belong to the configured subnet and the server + // should respond with STATUS_NotOnLink. + ASSERT_NO_THROW(client.doConfirm()); + ASSERT_TRUE(client.receivedStatusCode()); + ASSERT_EQ(STATUS_NotOnLink, client.getStatusCode()); + // Make sure that the server id have been included. + EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID)); + EXPECT_TRUE(client.getContext().response_->getOption(D6O_CLIENTID)); +} + + +// Test that relayed client's Confirm message is processed and Reply message +// is sent back (RFC 8415, section 18.3.3). +TEST_F(ConfirmTest, relayedClient) { + Dhcp6Client client; + // Client to send relayed message. + client.useRelay(); + // Configure client to request IA_NA. + client.requestAddress(); + // Make 4-way exchange to get the lease. + ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client)); + // Keep the client's lease for future reference. + Lease6 lease_client1 = client.getLease(0); + // Clone the lease and modify its address so as it is still in the range + // of the subnet to which the first lease belongs. When the client sends + // the Confirm it should include both addresses and the server should + // send Success because both of these addresses are on-link, regardless + // what the server has in the lease database. + Lease6 lease_client2 = lease_client1; + lease_client2.addr_ = bumpAddress(lease_client2.addr_); + ++lease_client2.iaid_; + client.createLease(lease_client2); + // Send Confirm message to the server. + ASSERT_NO_THROW(client.doConfirm()); + // Client should have received a status code option and this option should + // indicate the success. + ASSERT_TRUE(client.receivedStatusCode()); + ASSERT_EQ(STATUS_Success, client.getStatusCode()); + + lease_client2 = client.getLease(1); + lease_client2.addr_ = bumpSubnet(lease_client2.addr_); + client.createLease(lease_client2); + // Send confirm to the server. This time, one of the leases contains the + // address which doesn't belong to the configured subnet and the server + // should respond with STATUS_NotOnLink. + ASSERT_NO_THROW(client.doConfirm()); + ASSERT_TRUE(client.receivedStatusCode()); + ASSERT_EQ(STATUS_NotOnLink, client.getStatusCode()); + // Make sure that the server id and client id have been included. + EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID)); + EXPECT_TRUE(client.getContext().response_->getOption(D6O_CLIENTID)); +} + +// Test that the Confirm message without any addresses is discarded +// (RFC 8415, section 18.3.3). +TEST_F(ConfirmTest, relayedClientNoAddress) { + Dhcp6Client client; + // Configure the server. + configure(CONFIRM_CONFIGS[1], *client.getServer()); + // Make sure we ended-up having expected number of subnets configured. + const Subnet6Collection* subnets = + CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll(); + ASSERT_EQ(2, subnets->size()); + // Client to send relayed message. + client.useRelay(); + // Send Confirm message to the server. This message will contain no + // addresses because client has no leases. + ASSERT_NO_THROW(client.doConfirm()); + EXPECT_FALSE(client.getContext().response_); +} + +// This test checks that the server processes Confirm message correctly if +// the subnet can't be selected for the client (RFC 8415, section 18.3.3). +TEST_F(ConfirmTest, relayedClientNoSubnet) { + Dhcp6Client client; + // Client to send relayed message. + client.useRelay(); + // Configure client to request IA_NA. + client.requestAddress(); + // Make 4-way exchange to get the lease. + ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client)); + // Now that the client has a lease, let's remove any subnets to check + // how the server would respond to the Confirm. + ASSERT_NO_THROW(CfgMgr::instance().clear()); + // Send Confirm message to the server. + ASSERT_NO_THROW(client.doConfirm()); + // Client should have received a status code option and this option should + // indicate that the client is NotOnLink because subnet could not be + // selected. + ASSERT_TRUE(client.receivedStatusCode()); + ASSERT_EQ(STATUS_NotOnLink, client.getStatusCode()); + + // Let's test another case that the client sends no addresses in the Confirm + // message. The subnet can't be selected for that client as in the previous + // case but this time the server must discard the client's message because + // it contains no addresses (is invalid). + + // Set lifetimes to 0 so as the Confirm will ignore the specific address + // and send an empty IA_NA. + client.config_.leases_[0].preferred_lft_ = 0; + client.config_.leases_[0].valid_lft_ = 0; + ASSERT_NO_THROW(client.doConfirm()); + EXPECT_FALSE(client.getContext().response_); + + // Do similar test but this time remove the lease so as no IA_NA option + // is sent. + client.config_.clear(); + ASSERT_NO_THROW(client.doConfirm()); + EXPECT_FALSE(client.getContext().response_); +} + +// This test checks that the relayed Confirm message is processed by the server +// when sent to unicast address. +TEST_F(ConfirmTest, relayedUnicast) { + Dhcp6Client client; + // Client to send relayed message. + client.useRelay(); + // Configure client to request IA_NA. + client.requestAddress(); + // Make 4-way exchange to get the lease. + ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client)); + // Make sure we have got the lease. + ASSERT_GT(client.getLeaseNum(), 0); + client.setDestAddress(IOAddress("2001:db8:1::1")); + // Send Confirm message to the server. + ASSERT_NO_THROW(client.doConfirm()); + // Client should have received a response. + ASSERT_TRUE(client.getContext().response_); + // Client should have received a status code option and this option should + // indicate the success. + ASSERT_TRUE(client.receivedStatusCode()); + ASSERT_EQ(STATUS_Success, client.getStatusCode()); + // Make sure that the server id and client id have been included. + EXPECT_TRUE(client.getContext().response_->getOption(D6O_SERVERID)); + EXPECT_TRUE(client.getContext().response_->getOption(D6O_CLIENTID)); +} + +// This test checks that the Confirm message is discarded by the server if it +// has been sent to unicast address (RFC 8415, section 18.3.3). +TEST_F(ConfirmTest, unicast) { + Dhcp6Client client; + // Configure client to request IA_NA. + client.requestAddress(); + // Make 4-way exchange to get the lease. + ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client)); + // Make sure the client has got the lease. + ASSERT_GT(client.getLeaseNum(), 0); + // Send Confirm message to the server to the unicast address. + client.setDestAddress(IOAddress("2001:db8:1::1")); + ASSERT_NO_THROW(client.doConfirm()); + // Mak sure that the server discarded client's Confirm message. + EXPECT_FALSE(client.getContext().response_); +} + +} // end of anonymous namespace -- cgit v1.2.3