diff options
Diffstat (limited to 'src/bin/dhcp6/tests/infrequest_unittest.cc')
-rw-r--r-- | src/bin/dhcp6/tests/infrequest_unittest.cc | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/src/bin/dhcp6/tests/infrequest_unittest.cc b/src/bin/dhcp6/tests/infrequest_unittest.cc new file mode 100644 index 0000000..df3ac76 --- /dev/null +++ b/src/bin/dhcp6/tests/infrequest_unittest.cc @@ -0,0 +1,363 @@ +// Copyright (C) 2015-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 <config.h> +#include <dhcp/tests/iface_mgr_test_config.h> +#include <dhcp6/tests/dhcp6_test_utils.h> +#include <dhcp6/tests/dhcp6_client.h> +#include <dhcp/option6_addrlst.h> +#include <dhcp/option6_client_fqdn.h> +#include <stats/stats_mgr.h> + +using namespace isc; +using namespace isc::dhcp; +using namespace isc::dhcp::test; + +namespace { + +/// @brief Set of JSON configurations used by the Information-Request unit tests. +/// +/// - Configuration 0: +/// - one subnet used on eth0 interface +/// - with address and prefix pools +/// - dns-servers option +/// - Configuration 1: +/// - one subnet used on eth0 interface +/// - no addresses or prefixes +/// - domain-search option +/// - Configuration 2: +/// - one subnet used on eth0 interface +/// - dns-servers option for subnet +/// - sip-servers defined in global scope +/// - Configuration 3: +/// - nis-server, nis-domain specified in global scope +/// - no subnets defined +const char* CONFIGS[] = { + // Configuration 0 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"id\": 1, " + " \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ]," + " \"pd-pools\": [" + " { \"prefix\": \"2001:db8:3::\", " + " \"prefix-len\": 48, " + " \"delegated-len\": 64" + " } ]," + " \"option-data\": [ {" + " \"name\": \"dns-servers\"," + " \"data\": \"2001:db8::1, 2001:db8::2\"" + " } ]," + " \"subnet\": \"2001:db8::/32\", " + " \"interface\": \"eth0\"" + " } ]," + "\"valid-lifetime\": 4000 }", + + // Configuration 1 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"id\": 1, " + " \"option-data\": [ {" + " \"name\": \"sip-server-addr\"," + " \"data\": \"2001:db8::abcd\"" + " } ]," + " \"subnet\": \"2001:db8::/32\", " + " \"interface\": \"eth0\"" + " } ]," + "\"valid-lifetime\": 4000 }", + + // Configuration 2 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + " \"option-data\": [ {" + " \"name\": \"sip-server-addr\"," + " \"data\": \"2001:db8::1\"" + " } ]," + "\"subnet6\": [ { " + " \"id\": 1, " + " \"subnet\": \"2001:db8::/32\", " + " \"interface\": \"eth0\"," + " \"option-data\": [ {" + " \"name\": \"dns-servers\"," + " \"data\": \"2001:db8::2\"" + " } ]" + " } ]," + "\"valid-lifetime\": 4000 }", + + // Configuration 3 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"option-data\": [ {" + " \"name\": \"nis-servers\"," + " \"data\": \"2001:db8::1, 2001:db8::2\"" + " } ]" + "}" +}; + +/// @brief Test fixture class for testing 4-way exchange: Solicit-Advertise, +/// Request-Reply. +class InfRequestTest : public Dhcpv6SrvTest { +public: + /// @brief Constructor. + /// + /// Sets up fake interfaces. + InfRequestTest() + : Dhcpv6SrvTest(), + iface_mgr_test_config_(true) { + + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); + } + + /// @brief Destructor. + /// + /// Removes any statistics that may have been set. + ~InfRequestTest() { + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); + } + + /// @brief Interface Manager's fake configuration control. + IfaceMgrTestConfig iface_mgr_test_config_; +}; + +/// Check that server processes correctly an incoming inf-request in a +/// typical subnet that has also address and prefix pools. +TEST_F(InfRequestTest, infRequestBasic) { + Dhcp6Client client; + + // Configure client to request IA_PD. + configure(CONFIGS[0], *client.getServer()); + // Make sure we ended-up having expected number of subnets configured. + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(1, subnets->size()); + + // Perform 2-way exchange (Inf-request/reply) + client.requestOption(D6O_NAME_SERVERS); + ASSERT_NO_THROW(client.doInfRequest()); + + // Confirm that there's a response + Pkt6Ptr response = client.getContext().response_; + ASSERT_TRUE(response); + + // Check that it contains our client-id + OptionPtr client_id = response->getOption(D6O_CLIENTID); + ASSERT_TRUE(client_id); + EXPECT_TRUE(compareOptions(client_id, client.getClientId())); + + // Check that it contains proper server-id + OptionPtr server_id = response->getOption(D6O_SERVERID); + ASSERT_TRUE(server_id); + EXPECT_TRUE(compareOptions(server_id, client.getServer()->getServerID())); + + // Check that we received requested DNS servers option + Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst> + (response->getOption(D6O_NAME_SERVERS)); + ASSERT_TRUE(dns); + Option6AddrLst::AddressContainer addrs = dns->getAddresses(); + ASSERT_EQ(2, addrs.size()); + EXPECT_EQ("2001:db8::1", addrs[0].toText()); + EXPECT_EQ("2001:db8::2", addrs[1].toText()); +} + +/// Check that server processes correctly an incoming inf-request +/// that does not hold client-id. It's so called anonymous inf-request. +/// Uncommon, but certainly valid behavior. +TEST_F(InfRequestTest, infRequestAnonymous) { + Dhcp6Client client; + + // Configure client to request IA_PD. + configure(CONFIGS[0], *client.getServer()); + // Make sure we ended-up having expected number of subnets configured. + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(1, subnets->size()); + + // Perform 2-way exchange (Inf-request/reply) + client.requestOption(D6O_NAME_SERVERS); + client.useClientId(false); + ASSERT_NO_THROW(client.doInfRequest()); + + // Confirm that there's a response + Pkt6Ptr response = client.getContext().response_; + ASSERT_TRUE(response); + + // Check that we received the requested DNS servers option + Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst> + (response->getOption(D6O_NAME_SERVERS)); + ASSERT_TRUE(dns); + Option6AddrLst::AddressContainer addrs = dns->getAddresses(); + ASSERT_EQ(2, addrs.size()); + EXPECT_EQ("2001:db8::1", addrs[0].toText()); + EXPECT_EQ("2001:db8::2", addrs[1].toText()); +} + +/// Check that server processes correctly an incoming inf-request +/// if there is a subnet without any addresses or prefixes configured. +TEST_F(InfRequestTest, infRequestStateless) { + Dhcp6Client client; + + // Configure client to request IA_PD. + configure(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(1, subnets->size()); + + // Perform 2-way exchange (Inf-request/reply) + client.requestOption(D6O_SIP_SERVERS_ADDR); + ASSERT_NO_THROW(client.doInfRequest()); + + // Confirm that there's a response + Pkt6Ptr response = client.getContext().response_; + ASSERT_TRUE(response); + + // Check that we received the requested SIP servers option + Option6AddrLstPtr sip = boost::dynamic_pointer_cast<Option6AddrLst> + (response->getOption(D6O_SIP_SERVERS_ADDR)); + ASSERT_TRUE(sip); + Option6AddrLst::AddressContainer addrs = sip->getAddresses(); + ASSERT_EQ(1, addrs.size()); + EXPECT_EQ("2001:db8::abcd", addrs[0].toText()); +} + +/// Check that server processes correctly an incoming inf-request +/// if there are options defined at both global and subnet scope. +TEST_F(InfRequestTest, infRequestSubnetAndGlobal) { + Dhcp6Client client; + + // Configure client to request IA_PD. + configure(CONFIGS[2], *client.getServer()); + // Make sure we ended-up having expected number of subnets configured. + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(1, subnets->size()); + + // Perform 2-way exchange (Inf-request/reply) + client.requestOption(D6O_SIP_SERVERS_ADDR); + client.requestOption(D6O_NAME_SERVERS); + ASSERT_NO_THROW(client.doInfRequest()); + + // Confirm that there's a response + Pkt6Ptr response = client.getContext().response_; + ASSERT_TRUE(response); + + // Check that we received the requested sip servers option + Option6AddrLstPtr sip = boost::dynamic_pointer_cast<Option6AddrLst> + (response->getOption(D6O_SIP_SERVERS_ADDR)); + ASSERT_TRUE(sip); + Option6AddrLst::AddressContainer addrs = sip->getAddresses(); + ASSERT_EQ(1, addrs.size()); + EXPECT_EQ("2001:db8::1", addrs[0].toText()); + + // Check that we received the requested dns servers option + Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst> + (response->getOption(D6O_NAME_SERVERS)); + ASSERT_TRUE(dns); + addrs = dns->getAddresses(); + ASSERT_EQ(1, addrs.size()); + EXPECT_EQ("2001:db8::2", addrs[0].toText()); +} + +/// Check that server processes correctly an incoming inf-request +/// if there are options defined at global scope only (no subnets). +TEST_F(InfRequestTest, infRequestNoSubnets) { + Dhcp6Client client; + + // Configure client to request IA_PD. + configure(CONFIGS[3], *client.getServer()); + // Make sure we ended-up having expected number of subnets configured. + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(0, subnets->size()); + + // Perform 2-way exchange (Inf-request/reply) + client.requestOption(D6O_NIS_SERVERS); + ASSERT_NO_THROW(client.doInfRequest()); + + // Confirm that there's a response + Pkt6Ptr response = client.getContext().response_; + ASSERT_TRUE(response); + + // Check that we received the requested sip servers option + Option6AddrLstPtr nis = boost::dynamic_pointer_cast<Option6AddrLst> + (response->getOption(D6O_NIS_SERVERS)); + ASSERT_TRUE(nis); + Option6AddrLst::AddressContainer addrs = nis->getAddresses(); + ASSERT_EQ(2, addrs.size()); + EXPECT_EQ("2001:db8::1", addrs[0].toText()); + EXPECT_EQ("2001:db8::2", addrs[1].toText()); +} + +/// Check that server processes correctly an incoming inf-request in a +/// typical subnet that has also address and prefix pools. +TEST_F(InfRequestTest, infRequestStats) { + Dhcp6Client client; + + // Configure client to request IA_PD. + configure(CONFIGS[0], *client.getServer()); + // Make sure we ended-up having expected number of subnets configured. + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(1, subnets->size()); + + // Check that the tested statistics is initially set to 0 + using namespace isc::stats; + StatsMgr& mgr = StatsMgr::instance(); + ObservationPtr pkt6_rcvd = mgr.getObservation("pkt6-received"); + ObservationPtr pkt6_infreq_rcvd = mgr.getObservation("pkt6-infrequest-received"); + ObservationPtr pkt6_reply_sent = mgr.getObservation("pkt6-reply-sent"); + ObservationPtr pkt6_sent = mgr.getObservation("pkt6-sent"); + ASSERT_TRUE(pkt6_rcvd); + ASSERT_TRUE(pkt6_infreq_rcvd); + ASSERT_TRUE(pkt6_reply_sent); + ASSERT_TRUE(pkt6_sent); + EXPECT_EQ(0, pkt6_rcvd->getInteger().first); + EXPECT_EQ(0, pkt6_infreq_rcvd->getInteger().first); + EXPECT_EQ(0, pkt6_reply_sent->getInteger().first); + EXPECT_EQ(0, pkt6_sent->getInteger().first); + + // Perform 2-way exchange (Inf-request/reply) + client.requestOption(D6O_NAME_SERVERS); + ASSERT_NO_THROW(client.doInfRequest()); + + // Confirm that there's a response + Pkt6Ptr response = client.getContext().response_; + ASSERT_TRUE(response); + + pkt6_rcvd = mgr.getObservation("pkt6-received"); + pkt6_infreq_rcvd = mgr.getObservation("pkt6-infrequest-received"); + pkt6_reply_sent = mgr.getObservation("pkt6-reply-sent"); + pkt6_sent = mgr.getObservation("pkt6-sent"); + + ASSERT_TRUE(pkt6_rcvd); + ASSERT_TRUE(pkt6_infreq_rcvd); + ASSERT_TRUE(pkt6_reply_sent); + ASSERT_TRUE(pkt6_sent); + + // They also must have expected values. + EXPECT_EQ(1, pkt6_rcvd->getInteger().first); + EXPECT_EQ(1, pkt6_infreq_rcvd->getInteger().first); + EXPECT_EQ(1, pkt6_reply_sent->getInteger().first); + EXPECT_EQ(1, pkt6_sent->getInteger().first); +} + +} // end of anonymous namespace |