Browse Source

[3573] Further extended tests for DHCPv6 options reservations.

Added tests for Renew and Rebind and did some minor cleanups.
Marcin Siodelski 9 years ago
parent
commit
2ebc4da9d7
1 changed files with 300 additions and 25 deletions
  1. 300 25
      src/bin/dhcp6/tests/host_unittest.cc

+ 300 - 25
src/bin/dhcp6/tests/host_unittest.cc

@@ -6,8 +6,11 @@
 
 #include <config.h>
 #include <asiolink/io_address.h>
-#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp/docsis3_option_defs.h>
 #include <dhcp/option6_addrlst.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_vendor.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/dhcp6_client.h>
 
@@ -18,12 +21,6 @@ using namespace isc::dhcp::test;
 
 namespace {
 
-/// @brief Boolean value used to signal stateless configuration test.
-const bool STATELESS = true;
-
-/// @brief Boolean value used to signal stateful configuration test.
-const bool STATEFUL = false;
-
 /// @brief Set of JSON configurations used by the Host reservation unit tests.
 ///
 /// - Configuration 0:
@@ -31,13 +28,22 @@ const bool STATEFUL = false;
 ///
 /// - Configuration 1:
 ///   Multiple reservations using different host identifiers.
+///
 /// - Configuration 2:
 ///   Same as configuration 1 but 'host-reservation-identifiers' specified
 ///   in non-default order.
 ///
-/// - Configuration 2:
+/// - Configuration 3:
 ///   - Used to test that host specific options override subnet specific
 ///     options and global options.
+///
+/// - Configuration 4:
+///   - Used to test that client receives options solely specified in a
+///     host scope.
+///
+/// - Configuration 5:
+///   - Used to test that host specific vendor options override globally
+///     specified vendor options.
 const char* CONFIGS[] = {
     // Configuration 0:
     "{ "
@@ -168,6 +174,78 @@ const char* CONFIGS[] = {
         " } ]"
     "}",
 
+    // Configuration 4:
+    "{ "
+        "\"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"host-reservation-identifiers\": [ \"duid\" ],"
+        "\"valid-lifetime\": 4000, "
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ "
+        " { "
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"interface\" : \"eth0\","
+        "    \"reservations\": ["
+        "    {"
+        "        \"duid\": \"01:02:03:05\","
+        "        \"ip-addresses\": [ \"2001:db8:1::2\" ],"
+        "        \"option-data\": [ {"
+        "            \"name\": \"dns-servers\","
+        "            \"data\": \"3001:1::234\""
+        "        },"
+        "        {"
+        "            \"name\": \"nis-servers\","
+        "            \"data\": \"3001:1::234\""
+        "        } ]"
+        "    } ]"
+        " } ]"
+    "}",
+
+    // Configuration 5:
+    "{ "
+        "\"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"host-reservation-identifiers\": [ \"duid\" ],"
+        "\"valid-lifetime\": 4000, "
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"option-data\": [ {"
+        "    \"name\": \"vendor-opts\","
+        "    \"data\": 4491"
+        "},"
+        "{"
+        "    \"name\": \"tftp-servers\","
+        "    \"space\": \"vendor-4491\","
+        "    \"data\": \"3001:3::123\""
+        "} ],"
+        "\"subnet6\": [ "
+        " { "
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"interface\" : \"eth0\","
+        "    \"reservations\": ["
+        "    {"
+        "        \"duid\": \"01:02:03:05\","
+        "        \"ip-addresses\": [ \"2001:db8:1::2\" ],"
+        "        \"option-data\": [ {"
+        "            \"name\": \"vendor-opts\","
+        "            \"data\": 4491"
+        "        },"
+        "        {"
+        "            \"name\": \"tftp-servers\","
+        "            \"space\": \"vendor-4491\","
+        "            \"data\": \"3001:1::234\""
+        "        } ]"
+        "    } ]"
+        " } ]"
+    "}"
+
 };
 
 /// @brief Test fixture class for testing host reservations
@@ -181,6 +259,15 @@ public:
           iface_mgr_test_config_(true) {
     }
 
+    /// @brief Checks that specified option contains a desired address.
+    ///
+    /// The option must cast to the @ref Option6AddrLst type. The function
+    /// expects that this option contains at least one address and checks
+    /// first address for equality with @ref expected_addr.
+    ///
+    /// @param option_type Option type.
+    /// @param expected_addr Desired address.
+    /// @param config Configuration obtained from the server.
     void verifyAddressOption(const uint16_t option_type,
                              const std::string& expected_addr,
                              const Dhcp6Client::Configuration& config) const {
@@ -226,23 +313,90 @@ public:
         EXPECT_EQ(exp_ip_address, lease_client.addr_.toText());
     }
 
+    /// @brief Initiate exchange with DHCPv6 server.
+    ///
+    /// This method initiates DHCPv6 message exchange between a specified
+    /// client a the server. The msg_type is used to indicate what kind
+    /// of exchange should be initiated. If the message type is a Renew
+    /// or Rebind, the 4-way handshake is made first. If the message type
+    /// is a Request, the Solicit-Advertise is done prior to this.
+    ///
+    /// @param msg_type Message type to be sent to the server.
+    /// @param client Reference to a client to be used to initiate the
+    /// exchange with the server.
+    void doExchange(const uint16_t msg_type, Dhcp6Client& client);
+
     /// @brief Verifies that host specific options override subnet specific
     /// options.
     ///
     /// Overriden options are requested with Option Request option.
     ///
-    /// @param stateless Boolean value indicating if stateless or stateful
-    /// configuration should be performed.
-    void testOverrideRequestedOptions(const bool stateless);
+    /// @param msg_type DHCPv6 message type to be sent to the server. If the
+    /// message type is Renew or Rebind, the 4-way exchange is made prior to
+    /// sending a Renew or Rebind. For a Request case, the Solicit-Advertise
+    /// is also performed.
+    void testOverrideRequestedOptions(const uint16_t msg_type);
+
+    /// @brief Verifies that client receives options when they are solely
+    /// defined in the host scope (and not in the global or subnet scope).
+    ///
+    /// @param msg_type DHCPv6 message type to be sent to the server. If the
+    /// message type is Renew or Rebind, the 4-way exchange is made prior to
+    /// sending a Renew or Rebind. For a Request case, the Solicit-Advertise
+    /// is also performed.
+    void testHostOnlyOptions(const uint16_t msg_type);
+
+    /// @brief Verifies that host specific vendor options override vendor
+    /// options defined in the global scope.
+    ///
+    /// @param msg_type DHCPv6 message type to be sent to the server. If the
+    /// message type is Renew or Rebind, the 4-way exchange is made prior to
+    /// sending a Renew or Rebind. For a Request case, the Solicit-Advertise
+    /// is also performed.
+    void testOverrideVendorOptions(const uint16_t msg_type);
 
     /// @brief Interface Manager's fake configuration control.
     IfaceMgrTestConfig iface_mgr_test_config_;
 };
 
 void
-HostTest::testOverrideRequestedOptions(const bool stateless) {
+HostTest::doExchange(const uint16_t msg_type, Dhcp6Client& client) {
+    switch (msg_type) {
+    case DHCPV6_INFORMATION_REQUEST:
+        ASSERT_NO_THROW(client.doInfRequest());
+        break;
+    case DHCPV6_REQUEST:
+        ASSERT_NO_THROW(client.doSARR());
+        break;
+    case DHCPV6_SOLICIT:
+        ASSERT_NO_THROW(client.doSolicit());
+        break;
+    case DHCPV6_RENEW:
+        ASSERT_NO_THROW(client.doSARR());
+        ASSERT_NO_THROW(client.doRenew());
+        break;
+    case DHCPV6_REBIND:
+        ASSERT_NO_THROW(client.doSARR());
+        ASSERT_NO_THROW(client.doRebind());
+        break;
+    default:
+        ;
+    }
+
+    // Make sure that the server has responded with a Reply.
+    ASSERT_TRUE(client.getContext().response_);
+    ASSERT_EQ(DHCPV6_REPLY, client.getContext().response_->getType());
+
+}
+
+
+void
+HostTest::testOverrideRequestedOptions(const uint16_t msg_type) {
     Dhcp6Client client;
+    // Reservation has been made for a client with this DUID.
     client.setDUID("01:02:03:05");
+
+    // Request all options specified in the configuration.
     client.requestOption(D6O_NAME_SERVERS);
     client.requestOption(D6O_NIS_SERVERS);
     client.requestOption(D6O_NISP_SERVERS);
@@ -250,35 +404,95 @@ HostTest::testOverrideRequestedOptions(const bool stateless) {
 
     configure(CONFIGS[3], *client.getServer());
 
-    if (stateless) {
-        ASSERT_NO_THROW(client.doInfRequest());
-
-    } else {
-        client.useNA();
-        ASSERT_NO_THROW(client.doSARR());
-    }
+    ASSERT_NO_FATAL_FAILURE(doExchange(msg_type, client));
 
     {
         SCOPED_TRACE("host specific dns-servers");
+        // Host specific DNS server should be used.
         verifyAddressOption(D6O_NAME_SERVERS, "3000:1::234", client.config_);
     }
 
     {
         SCOPED_TRACE("host specific nis-servers");
+        // Host specific NIS server should be used.
         verifyAddressOption(D6O_NIS_SERVERS, "3000:1::234", client.config_);
     }
 
     {
         SCOPED_TRACE("subnet specific sntp-servers");
+        // Subnet specific SNTP server should be used as it is not specified
+        // in a host scope.
         verifyAddressOption(D6O_SNTP_SERVERS, "3000:2::123", client.config_);
     }
 
     {
         SCOPED_TRACE("global nisp-servers");
+        // Globally specified NISP server should be used as it is not
+        // specified in a host scope.
         verifyAddressOption(D6O_NISP_SERVERS, "3000:3::123", client.config_);
     }
 }
 
+void
+HostTest::testHostOnlyOptions(const uint16_t msg_type) {
+    Dhcp6Client client;
+    client.setDUID("01:02:03:05");
+    client.requestOption(D6O_NAME_SERVERS);
+    client.requestOption(D6O_NIS_SERVERS);
+
+    configure(CONFIGS[3], *client.getServer());
+
+    ASSERT_NO_FATAL_FAILURE(doExchange(msg_type, client));
+
+    {
+        SCOPED_TRACE("host specific dns-servers");
+        // DNS servers are specified only in a host scope.
+        verifyAddressOption(D6O_NAME_SERVERS, "3000:1::234", client.config_);
+    }
+
+    {
+        SCOPED_TRACE("host specific nis-servers");
+        // NIS servers are specified only in a host scope.
+        verifyAddressOption(D6O_NIS_SERVERS, "3000:1::234", client.config_);
+    }
+}
+
+void
+HostTest::testOverrideVendorOptions(const uint16_t msg_type) {
+    Dhcp6Client client;
+    client.setDUID("01:02:03:05");
+
+    // Client needs to include Vendor Specific Information option
+    // with ORO suboption, which the server will use to determine
+    // which suboptions should be returned to the client.
+    OptionVendorPtr opt_vendor(new OptionVendor(Option::V6,
+                                                VENDOR_ID_CABLE_LABS));
+    // Include ORO with TFTP servers suboption code being requested.
+    opt_vendor->addOption(OptionPtr(new OptionUint16(Option::V6, DOCSIS3_V6_ORO,
+                                                     DOCSIS3_V6_TFTP_SERVERS)));
+    client.addExtraOption(opt_vendor);
+
+    configure(CONFIGS[5], *client.getServer());
+
+    ASSERT_NO_FATAL_FAILURE(doExchange(msg_type, client));
+
+    // Vendor Specific Information option should be returned by the server.
+    OptionVendorPtr vendor_opt = boost::dynamic_pointer_cast<
+        OptionVendor>(client.config_.findOption(D6O_VENDOR_OPTS));
+    ASSERT_TRUE(vendor_opt);
+
+    // TFTP server suboption should be returned because it was requested
+    // with Option Request suboption.
+    Option6AddrLstPtr tftp = boost::dynamic_pointer_cast<
+        Option6AddrLst>(vendor_opt->getOption(DOCSIS3_V6_TFTP_SERVERS));
+    ASSERT_TRUE(tftp);
+
+    // Address specified in the host scope should be used.
+    Option6AddrLst::AddressContainer addrs = tftp->getAddresses();
+    ASSERT_EQ(addrs.size(), 1);
+    EXPECT_EQ("3001:1::234", addrs[0].toText());
+}
+
 // Test basic SARR scenarios against a server configured with one subnet
 // containing two reservations.  One reservation with a hostname, one
 // without a hostname. Scenarios:
@@ -499,16 +713,77 @@ TEST_F(HostTest, hostIdentifiersOrder) {
 
 // This test checks that host specific options override subnet specific
 // options. Overridden options are requested with Option Request
-// option (stateless case).
-TEST_F(HostTest, overrideRequestedOptionsStateless) {
-    testOverrideRequestedOptions(STATELESS);
+// option (Information-request case).
+TEST_F(HostTest, overrideRequestedOptionsInformationRequest) {
+    testOverrideRequestedOptions(DHCPV6_INFORMATION_REQUEST);
 }
 
 // This test checks that host specific options override subnet specific
 // options. Overridden options are requested with Option Request
-// option (stateful case).
-TEST_F(HostTest, overrideRequestedOptionsStateful) {
-    testOverrideRequestedOptions(STATEFUL);
+// option (Reuqest case).
+TEST_F(HostTest, overrideRequestedOptionsRequest) {
+    testOverrideRequestedOptions(DHCPV6_REQUEST);
 }
 
+// This test checks that host specific options override subnet specific
+// options. Overridden options are requested with Option Request
+// option (Renew case).
+TEST_F(HostTest, overrideRequestedOptionsRenew) {
+    testOverrideRequestedOptions(DHCPV6_RENEW);
+}
+
+// This test checks that host specific options override subnet specific
+// options. Overridden options are requested with Option Request
+// option (Rebind case).
+TEST_F(HostTest, overrideRequestedOptionsRebind) {
+    testOverrideRequestedOptions(DHCPV6_REBIND);
+}
+
+// This test checks that client receives options when they are
+// solely defined in the host scope and not in the global or subnet
+// scope (Information-request case).
+TEST_F(HostTest, testHostOnlyOptionsInformationRequest) {
+    testHostOnlyOptions(DHCPV6_INFORMATION_REQUEST);
+}
+
+// This test checks that client receives options when they are
+// solely defined in the host scope and not in the global or subnet
+// scope (Reuqest case).
+TEST_F(HostTest, testHostOnlyOptionsRequest) {
+    testHostOnlyOptions(DHCPV6_REQUEST);
+}
+
+// This test checks that client receives options when they are
+// solely defined in the host scope and not in the global or subnet
+// scope (Renew case).
+TEST_F(HostTest, testHostOnlyOptionsRenew) {
+    testHostOnlyOptions(DHCPV6_RENEW);
+}
+
+// This test checks that client receives options when they are
+// solely defined in the host scope and not in the global or subnet
+// scope (Rebind case).
+TEST_F(HostTest, testHostOnlyOptionsRebind) {
+    testHostOnlyOptions(DHCPV6_REBIND);
+}
+
+// This test checks that host specific vendor options override vendor
+// options defined in the global scope (Reuqest case).
+TEST_F(HostTest, overrideVendorOptionsRequest) {
+    testOverrideVendorOptions(DHCPV6_REQUEST);
+}
+
+// This test checks that host specific vendor options override vendor
+// options defined in the global scope (Renew case).
+TEST_F(HostTest, overrideVendorOptionsRenew) {
+    testOverrideVendorOptions(DHCPV6_RENEW);
+}
+
+// This test checks that host specific vendor options override vendor
+// options defined in the global scope (Rebind case).
+TEST_F(HostTest, overrideVendorOptionsRebind) {
+    testOverrideVendorOptions(DHCPV6_REBIND);
+}
+
+
 } // end of anonymous namespace