From: Thomas Markwalder Date: Mon, 20 Aug 2018 18:46:07 +0000 (-0400) Subject: [#13,!6] - Added unit tests to bin/dhcp6 to verify global HRs X-Git-Tag: gitlab20_base~17^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ac87dd2585eb68e22e07d2132e1d7b2f45ca27f6;p=thirdparty%2Fkea.git [#13,!6] - Added unit tests to bin/dhcp6 to verify global HRs src/bin/dhcp6/tests/dhcp6_test_utils.cc Minor mods to emit configuration/parsing errors src/bin/dhcp6/tests/host_unittest.cc HostTest::sarrTest() - new function for testing a SARR that should result in a lease TEST_F(HostTest, globalReservationsNA) TEST_F(HostTest, globalReservationsPD) - new tests to verify global HRs --- diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index b836b8fbc9..274c7a04c6 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -699,7 +699,13 @@ Dhcpv6SrvTest::configure(const std::string& config) { void Dhcpv6SrvTest::configure(const std::string& config, NakedDhcpv6Srv& srv) { ConstElementPtr json; - ASSERT_NO_THROW(json = parseJSON(config)); + try { + json = parseJSON(config); + } catch (const std::exception& ex) { + // Fatal failure on parsing error + FAIL() << "config parsing failed, test is broken: " << ex.what(); + } + ConstElementPtr status; // Disable the re-detect flag @@ -710,7 +716,8 @@ Dhcpv6SrvTest::configure(const std::string& config, NakedDhcpv6Srv& srv) { ASSERT_TRUE(status); int rcode; ConstElementPtr comment = isc::config::parseAnswer(rcode, status); - ASSERT_EQ(0, rcode); + ASSERT_EQ(0, rcode) << "configuration failed, test is broken: " + << comment->str(); CfgMgr::instance().commit(); } diff --git a/src/bin/dhcp6/tests/host_unittest.cc b/src/bin/dhcp6/tests/host_unittest.cc index 0028b3ddb0..0940dde2fc 100644 --- a/src/bin/dhcp6/tests/host_unittest.cc +++ b/src/bin/dhcp6/tests/host_unittest.cc @@ -321,7 +321,105 @@ const char* CONFIGS[] = { " }" " ]" "} ]" - "}" + "}", + + // Configuration 8: Global HRs TYPE_NAs + "{ " + "\"interfaces-config\": { \n" + " \"interfaces\": [ \"*\" ] \n" + "},\n " + "\"host-reservation-identifiers\": [ \"duid\", \"hw-address\" ], \n" + "\"reservations\": [ \n" + "{ \n" + " \"duid\": \"01:02:03:04\", \n" + " \"hostname\": \"duid-host-fixed\", \n" + " \"ip-addresses\": [ \"3001::1\" ] \n" + "}, \n" + "{ \n" + " \"duid\": \"01:02:03:05\", \n" + " \"hostname\": \"duid-host-dynamic\" \n" + "}, \n" + "{ \n" + " \"hw-address\": \"38:60:77:d5:ff:ee\", \n" + " \"hostname\": \"hw-host\" \n" + "} \n" + "], \n" + "\"valid-lifetime\": 4000, \n" + "\"preferred-lifetime\": 3000, \n" + "\"rebind-timer\": 2000, \n" + "\"renew-timer\": 1000, \n" + "\"mac-sources\": [ \"ipv6-link-local\" ], \n" + "\"subnet6\": [ \n" + " { \n" + " \"subnet\": \"2001:db8:1::/48\", \n" + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], \n" + " \"interface\" : \"eth0\", \n" + " \"reservation-mode\": \"global\" \n" + " }," + " { \n" + " \"subnet\": \"2001:db8:2::/48\", \n" + " \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ], \n" + " \"interface\" : \"eth1\", \n" + " \"reservations\": [ \n" + " { \n" + " \"duid\": \"01:02:03:05\", \n" + " \"hostname\": \"subnet-duid-host\" \n" + " }] \n" + " }" + " ] \n" + "} \n" + , + // Configuration 9: Global HRs TYPE_PDs + "{ " + "\"interfaces-config\": { \n" + " \"interfaces\": [ \"*\" ] \n" + "},\n " + "\"host-reservation-identifiers\": [ \"duid\", \"hw-address\" ], \n" + "\"reservations\": [ \n" + "{ \n" + " \"duid\": \"01:02:03:04\", \n" + " \"hostname\": \"duid-host-fixed\", \n" + " \"prefixes\": [ \"4000::100/120\" ]" + "}, \n" + "{ \n" + " \"duid\": \"01:02:03:05\", \n" + " \"hostname\": \"duid-host-dynamic\" \n" + "} \n" + "], \n" + "\"valid-lifetime\": 4000, \n" + "\"preferred-lifetime\": 3000, \n" + "\"rebind-timer\": 2000, \n" + "\"renew-timer\": 1000, \n" + "\"mac-sources\": [ \"ipv6-link-local\" ], \n" + "\"subnet6\": [ \n" + " { \n" + " \"subnet\": \"2001:db8:1::/48\", \n" + " \"interface\" : \"eth0\", \n" + " \"reservation-mode\": \"global\", \n" + " \"pd-pools\": [ \n" + " { \n" + " \"prefix\": \"3000::\", \n" + " \"prefix-len\": 119, \n" + " \"delegated-len\": 120 \n" + " }] \n" + " }," + " { \n" + " \"subnet\": \"2001:db8:2::/48\", \n" + " \"interface\" : \"eth1\", \n" + " \"pd-pools\": [ \n" + " { \n" + " \"prefix\": \"3001::\", \n" + " \"prefix-len\": 119, \n" + " \"delegated-len\": 120 \n" + " }], \n" + " \"reservations\": [ \n" + " { \n" + " \"duid\": \"01:02:03:05\", \n" + " \"hostname\": \"subnet-duid-host\" \n" + " }] \n" + " }" + " ] \n" + "} \n" }; /// @brief Base class representing leases and hints conveyed within IAs. @@ -701,6 +799,14 @@ public: const Reservation& r5 = Reservation::UNSPEC(), const Reservation& r6 = Reservation::UNSPEC()) const; + /// @brief Verifies that an SARR exchange results in the expected lease + /// + /// @param client Client configured to request a single lease + /// @param exp_address expected address/prefix of the lease + /// @param exp_hostname expected hostname on the lease + void sarrTest(Dhcp6Client& client, const std::string& exp_address, + const std::string& exp_hostname); + /// @brief Configures client to include hint. /// /// @param client Reference to a client. @@ -1063,6 +1169,25 @@ HostTest::requestEmptyIAs(Dhcp6Client& client) { client.requestPrefix(6); } +void +HostTest::sarrTest(Dhcp6Client& client, const std::string& exp_address, + const std::string& exp_hostname) { + // Perform 4-way exchange. + ASSERT_NO_THROW(client.doSARR()); + + // Verify that the client got a dynamic address + ASSERT_EQ(1, client.getLeaseNum()); + Lease6 lease_client = client.getLease(0); + EXPECT_EQ(exp_address, lease_client.addr_.toText()); + + // Check that the server recorded the lease. + // and that the server lease has NO hostname + Lease6Ptr lease_server = checkLease(lease_client); + ASSERT_TRUE(lease_server); + EXPECT_EQ(exp_hostname, lease_server->hostname_); +} + + // Test basic SARR scenarios against a server configured with one subnet // containing two reservations. One reservation with a hostname, one // without a hostname. Scenarios: @@ -1918,5 +2043,108 @@ TEST_F(HostTest, conflictResolutionReuseExpired) { EXPECT_FALSE(client2.hasLeaseWithZeroLifetimeForPrefix(IOAddress("3000::"), 120)); } +// Verifies fundamental Global vs Subnet host reservations for NA leases +TEST_F(HostTest, globalReservationsNA) { + Dhcp6Client client; + ASSERT_NO_FATAL_FAILURE(configure(CONFIGS[8], *client.getServer())); + + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(2, subnets->size()); + + { + SCOPED_TRACE("Global HR by DUID with reserved address"); + client.setDUID("01:02:03:04"); + client.requestAddress(1234, IOAddress("::")); + // Should get global reserved address and reserved host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3001::1", "duid-host-fixed")); + } + + { + SCOPED_TRACE("Global HR by DUID with dynamic address"); + client.clearConfig(); + client.setDUID("01:02:03:05"); + client.requestAddress(1234, IOAddress("::")); + // Should get dynamic address and reserved host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:1::", "duid-host-dynamic")); + } + + { + SCOPED_TRACE("Global HR by HW Address with dynamic address"); + client.clearConfig(); + client.setDUID("33:44:55:66"); + client.setLinkLocal(IOAddress("fe80::3a60:77ff:fed5:ffee")); + client.requestAddress(1234, IOAddress("::")); + // Should get dynamic address and hardware host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:1::1", "hw-host")); + } + + { + SCOPED_TRACE("Default subnet mode excludes Global HR"); + client.clearConfig(); + client.setInterface("eth1"); + client.setDUID("01:02:03:04"); + client.requestAddress(1234, IOAddress("::")); + // Should get dynamic address and no host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:2::", "")); + } + + { + SCOPED_TRACE("Subnet reservation over global"); + client.clearConfig(); + client.setInterface("eth1"); + client.setDUID("01:02:03:05"); + client.requestAddress(1234, IOAddress("::")); + // Should get dynamic address and host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:2::1", "subnet-duid-host")); + } +} + +// Verifies fundamental Global vs Subnet host reservations for PD leases +TEST_F(HostTest, globalReservationsPD) { + Dhcp6Client client; + ASSERT_NO_FATAL_FAILURE(configure(CONFIGS[9], *client.getServer())); + + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(2, subnets->size()); + + { + SCOPED_TRACE("Global HR by DUID with reserved prefix"); + client.setDUID("01:02:03:04"); + client.requestPrefix(1); + // Should get global reserved prefix and reserved host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "4000::100", "duid-host-fixed")); + } + + { + SCOPED_TRACE("Global HR by DUID with dynamic prefix"); + client.clearConfig(); + client.setDUID("01:02:03:05"); + client.requestPrefix(1); + // Should get dynamic prefix and reserved host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3000::", "duid-host-dynamic")); + } + + { + SCOPED_TRACE("Default subnet mode excludes Global HR"); + client.clearConfig(); + client.setInterface("eth1"); + client.setDUID("01:02:03:04"); + client.requestPrefix(1); + // Should get dynamic prefix and no host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3001::", "")); + } + + { + SCOPED_TRACE("Subnet reservation over global"); + client.clearConfig(); + client.setInterface("eth1"); + client.setDUID("01:02:03:05"); + client.requestPrefix(1); + // Should get dynamic prefix and subnet reserved host name + ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3001::100", "subnet-duid-host")); + } +} } // end of anonymous namespace