diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index e856565a68..7b928c68e4 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -924,13 +924,21 @@ void Dhcpv4Srv::buildCfgOptionList(Dhcpv4Exchange& ex) { CfgOptionList& co_list = ex.getCfgOptionList(); - // First subnet configured options + // Retrieve subnet. Subnet4Ptr subnet = ex.getContext()->subnet_; if (!subnet) { // All methods using the CfgOptionList object return soon when // there is no subnet so do the same return; } + + // Firstly, host specific options. + const ConstHostPtr& host = ex.getContext()->host_; + if (host && !host->getCfgOption4()->empty()) { + co_list.push_back(host->getCfgOption4()); + } + + // Secondly, subnet configured options. if (!subnet->getCfgOption()->empty()) { co_list.push_back(subnet->getCfgOption()); } diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am index a0856b21fd..a11d771b7e 100644 --- a/src/bin/dhcp4/tests/Makefile.am +++ b/src/bin/dhcp4/tests/Makefile.am @@ -89,6 +89,7 @@ dhcp4_unittests_SOURCES += dhcp4_client.cc dhcp4_client.h dhcp4_unittests_SOURCES += hooks_unittest.cc dhcp4_unittests_SOURCES += inform_unittest.cc dhcp4_unittests_SOURCES += dora_unittest.cc +dhcp4_unittests_SOURCES += host_options_unittest.cc dhcp4_unittests_SOURCES += release_unittest.cc dhcp4_unittests_SOURCES += out_of_range_unittest.cc dhcp4_unittests_SOURCES += decline_unittest.cc diff --git a/src/bin/dhcp4/tests/dhcp4_client.cc b/src/bin/dhcp4/tests/dhcp4_client.cc index f4926de0df..82c78e7a60 100644 --- a/src/bin/dhcp4/tests/dhcp4_client.cc +++ b/src/bin/dhcp4/tests/dhcp4_client.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -163,13 +164,19 @@ Dhcp4Client::applyConfiguration() { Option4AddrLstPtr opt_log_servers = boost::dynamic_pointer_cast< Option4AddrLst>(resp->getOption(DHO_LOG_SERVERS)); if (opt_log_servers) { - config_.log_servers_ = opt_routers->getAddresses(); + config_.log_servers_ = opt_log_servers->getAddresses(); } // Quotes Servers Option4AddrLstPtr opt_quotes_servers = boost::dynamic_pointer_cast< Option4AddrLst>(resp->getOption(DHO_COOKIE_SERVERS)); if (opt_quotes_servers) { - config_.quotes_servers_ = opt_dns_servers->getAddresses(); + config_.quotes_servers_ = opt_quotes_servers->getAddresses(); + } + // Vendor Specific options + OptionVendorPtr opt_vendor = boost::dynamic_pointer_cast< + OptionVendor>(resp->getOption(DHO_VIVSO_SUBOPTIONS)); + if (opt_vendor) { + config_.vendor_suboptions_ = opt_vendor->getOptions(); } // Server Identifier OptionCustomPtr opt_serverid = boost::dynamic_pointer_cast< @@ -254,6 +261,8 @@ Dhcp4Client::doInform(const bool set_ciaddr) { context_.query_ = createMsg(DHCPINFORM); // Request options if any. appendPRL(); + // Any other options to be sent by a client. + appendExtraOptions(); // The client sending a DHCPINFORM message has an IP address obtained // by some other means, e.g. static configuration. The lease which we // are using here is most likely set by the createLease method. @@ -368,6 +377,8 @@ Dhcp4Client::doRequest() { appendName(); // Include Client Identifier appendClientId(); + // Any other options to be sent by a client. + appendExtraOptions(); // Send the message to the server. sendMsg(context_.query_); // Expect response. diff --git a/src/bin/dhcp4/tests/dhcp4_client.h b/src/bin/dhcp4/tests/dhcp4_client.h index 71dc9544ee..480a8a426d 100644 --- a/src/bin/dhcp4/tests/dhcp4_client.h +++ b/src/bin/dhcp4/tests/dhcp4_client.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -73,6 +74,8 @@ public: Option4AddrLst::AddressContainer log_servers_; /// @brief Holds IP addresses received in the Quotes Servers option. Option4AddrLst::AddressContainer quotes_servers_; + /// @brief Vendor Specific options + OptionCollection vendor_suboptions_; /// @brief Holds a lease obtained by the client. Lease4 lease_; /// @brief Holds server id of the server which responded to the client's diff --git a/src/bin/dhcp4/tests/host_options_unittest.cc b/src/bin/dhcp4/tests/host_options_unittest.cc new file mode 100644 index 0000000000..b2c86e0660 --- /dev/null +++ b/src/bin/dhcp4/tests/host_options_unittest.cc @@ -0,0 +1,555 @@ +// Copyright (C) 2016 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 +#include +#include +#include + +using namespace isc; +using namespace isc::asiolink; +using namespace isc::dhcp; +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 throughout the tests. +/// +/// - Configuration 0: +/// - Used to test that host specific options override subnet specific +/// options when these options are requested with PRL option. +/// - Single subnet 10.0.0.0/24 with a pool of 10.0.0.10-10.0.0.100 +/// - 4 options configured within subnet scope +/// - routers: 10.0.0.200,10.0.0.201, +/// - domain-name-servers: 10.0.0.202,10.0.0.203, +/// - log-servers: 10.0.0.200,10.0.0.201, +/// - cookie-servers: 10.0.0.202,10.0.0.203 +/// - Single reservation within the subnet: +/// - HW address: aa:bb:cc:dd:ee:ff +/// - ip-address: 10.0.0.7 +/// - Two options overriding subnet specific options: +/// - cookie-servers: 10.1.1.202, 10.1.1.203 +/// - log-servers: 10.1.1.200, 10.1.1.201 +/// +/// - Configuration 1: +/// - Used to test that host specific options override subnet specific +/// default options. Default options are those that are sent even when +/// not requested by a client. +/// - Single subnet 10.0.0.0/24 with a pool of 10.0.0.10-10.0.0.100 +/// - 4 options configured within subnet scope +/// - routers: 10.0.0.200,10.0.0.201, +/// - domain-name-servers: 10.0.0.202,10.0.0.203, +/// - log-servers: 10.0.0.200,10.0.0.201, +/// - cookie-servers: 10.0.0.202,10.0.0.203 +/// - Single reservation within the subnet: +/// - HW address: aa:bb:cc:dd:ee:ff +/// - ip-address: 10.0.0.7 +/// - Two options overriding subnet specific default options: +/// - routers: 10.1.1.200, 10.1.1.201 +/// - domain-name-servers: 10.1.1.202, 10.1.1.203 +/// +/// - Configuration 2: +/// - Used to test that client receives options solely specified in a +/// host scope. +/// - Single reservation within the subnet: +/// - HW address: aa:bb:cc:dd:ee:ff +/// - ip-address: 10.0.0.7 +/// - Two options: +/// - routers: 10.1.1.200, 10.1.1.201 +/// - cookie-servers: 10.1.1.202, 10.1.1.203 +/// +/// - Configuration 3: +/// - Used to test that host specific vendor options override globally +/// specified vendor options. +/// - Globally specified option 125 with Cable Labs vendor id. +/// - TFTP servers sub option: 10.0.0.202,10.0.0.203 +/// - Single subnet 10.0.0.0/24 with a pool of 10.0.0.10-10.0.0.100 +/// - Single reservation within the subnet: +/// - HW address: aa:bb:cc:dd:ee:ff +/// - ip-adress 10.0.0.7 +/// - Vendor option for Cable Labs vendor id specified for the reservation: +/// - TFTP servers suboption overriding globally spececified suboption: +/// 10.1.1.202,10.1.1.203 +/// +const char* HOST_CONFIGS[] = { +// Configuration 0 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"valid-lifetime\": 600," + "\"subnet4\": [ { " + " \"subnet\": \"10.0.0.0/24\", " + " \"id\": 1," + " \"relay\": { \"ip-address\": \"10.0.0.233\" }," + " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," + " \"option-data\": [ {" + " \"name\": \"routers\"," + " \"data\": \"10.0.0.200,10.0.0.201\"" + " }," + " {" + " \"name\": \"domain-name-servers\"," + " \"data\": \"10.0.0.202,10.0.0.203\"" + " }," + " {" + " \"name\": \"log-servers\"," + " \"data\": \"10.0.0.204,10.0.0.205\"" + " }," + " {" + " \"name\": \"cookie-servers\"," + " \"data\": \"10.0.0.206,10.0.0.207\"" + " } ]," + " \"reservations\": [ " + " {" + " \"hw-address\": \"aa:bb:cc:dd:ee:ff\"," + " \"ip-address\": \"10.0.0.7\"," + " \"option-data\": [ {" + " \"name\": \"cookie-servers\"," + " \"data\": \"10.1.1.202,10.1.1.203\"" + " }," + " {" + " \"name\": \"log-servers\"," + " \"data\": \"10.1.1.200,10.1.1.201\"" + " } ]" + " } ]" + " } ]" + "}", + +// Configuration 1 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"valid-lifetime\": 600," + "\"subnet4\": [ { " + " \"subnet\": \"10.0.0.0/24\", " + " \"id\": 1," + " \"relay\": { \"ip-address\": \"10.0.0.233\" }," + " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," + " \"option-data\": [ {" + " \"name\": \"routers\"," + " \"data\": \"10.0.0.200,10.0.0.201\"" + " }," + " {" + " \"name\": \"domain-name-servers\"," + " \"data\": \"10.0.0.202,10.0.0.203\"" + " }," + " {" + " \"name\": \"log-servers\"," + " \"data\": \"10.0.0.204,10.0.0.205\"" + " }," + " {" + " \"name\": \"cookie-servers\"," + " \"data\": \"10.0.0.206,10.0.0.207\"" + " } ]," + " \"reservations\": [ " + " {" + " \"hw-address\": \"aa:bb:cc:dd:ee:ff\"," + " \"ip-address\": \"10.0.0.7\"," + " \"option-data\": [ {" + " \"name\": \"routers\"," + " \"data\": \"10.1.1.200,10.1.1.201\"" + " }," + " {" + " \"name\": \"domain-name-servers\"," + " \"data\": \"10.1.1.202,10.1.1.203\"" + " } ]" + " } ]" + " } ]" + "}", + +// Configuration 2 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"valid-lifetime\": 600," + "\"subnet4\": [ { " + " \"subnet\": \"10.0.0.0/24\", " + " \"id\": 1," + " \"relay\": { \"ip-address\": \"10.0.0.233\" }," + " \"reservations\": [ " + " {" + " \"hw-address\": \"aa:bb:cc:dd:ee:ff\"," + " \"ip-address\": \"10.0.0.7\"," + " \"option-data\": [ {" + " \"name\": \"routers\"," + " \"data\": \"10.1.1.200,10.1.1.201\"" + " }," + " {" + " \"name\": \"cookie-servers\"," + " \"data\": \"10.1.1.206,10.1.1.207\"" + " } ]" + " } ]" + " } ]" + "}", + +// Configuration 3 + "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"valid-lifetime\": 600," + "\"option-data\": [ {" + " \"name\": \"vivso-suboptions\"," + " \"data\": 4491" + "}," + "{" + " \"name\": \"tftp-servers\"," + " \"space\": \"vendor-4491\"," + " \"data\": \"10.0.0.202,10.0.0.203\"" + "} ]," + "\"subnet4\": [ { " + " \"subnet\": \"10.0.0.0/24\", " + " \"id\": 1," + " \"relay\": { \"ip-address\": \"10.0.0.233\" }," + " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," + " \"reservations\": [ " + " {" + " \"hw-address\": \"aa:bb:cc:dd:ee:ff\"," + " \"ip-address\": \"10.0.0.7\"," + " \"option-data\": [ {" + " \"name\": \"vivso-suboptions\"," + " \"data\": 4491" + " }," + " {" + " \"name\": \"tftp-servers\"," + " \"space\": \"vendor-4491\"," + " \"data\": \"10.1.1.202,10.1.1.203\"" + " } ]" + " } ]" + " } ]" + "}" +}; + +/// @brief Test fixture class for testing static reservations of options. +class HostOptionsTest : public Dhcpv4SrvTest { +public: + + /// @brief Constructor. + /// + /// Sets up fake interfaces. + HostOptionsTest() + : Dhcpv4SrvTest(), + iface_mgr_test_config_(true) { + IfaceMgr::instance().openSockets4(); + } + + /// @brief Verifies that host specific options override subnet specific + /// options. + /// + /// Overridden options are requested with Parameter Request List + /// option. + /// + /// @param stateless Boolean value indicating if statless or stateful + /// configuration should be performed. + void testOverrideRequestedOptions(const bool stateless); + + /// @brief Verifies that host specific options override subnet specific + /// options. + /// + /// Overridden options are the options which server sends regardless + /// if they are requested with Parameter Request List option or not. + /// + /// @param stateless Boolean value indicating if statless or stateful + /// configuration should be performed. + void testOverrideDefaultOptions(const bool stateless); + + /// @brief Verifies that client receives options when they are solely + /// defined in the host scope (and not in the global or subnet scope). + /// + /// @param stateless Boolean value indicating if statless or stateful + /// configuration should be performed. + void testHostOnlyOptions(const bool stateless); + + /// @brief Verifies that host specific vendor options override vendor + /// options defined in the global scope. + /// + /// @param stateless Boolean value indicating if statless or stateful + /// configuration should be performed. + void testOverrideVendorOptions(const bool stateless); + + /// @brief Interface Manager's fake configuration control. + IfaceMgrTestConfig iface_mgr_test_config_; + +}; + +void +HostOptionsTest::testOverrideRequestedOptions(const bool stateless) { + Dhcp4Client client(Dhcp4Client::SELECTING); + client.setHWAddress("aa:bb:cc:dd:ee:ff"); + client.requestOptions(DHO_DOMAIN_NAME_SERVERS, DHO_LOG_SERVERS, + DHO_COOKIE_SERVERS); + + // Configure DHCP server. + configure(HOST_CONFIGS[0], *client.getServer()); + + if (stateless) { + // Need to relay the message from a specific address which can + // be matched with a configured subnet. + client.useRelay(true, IOAddress("10.0.0.233")); + ASSERT_NO_THROW(client.doInform()); + + } else { + // Perform 4-way exchange with the server but to not request any + // specific address in the DHCPDISCOVER message. + ASSERT_NO_THROW(client.doDORA()); + } + + // Make sure that the server responded. + ASSERT_TRUE(client.getContext().response_); + Pkt4Ptr resp = client.getContext().response_; + // Make sure that the server has responded with DHCPACK. + ASSERT_EQ(DHCPACK, static_cast(resp->getType())); + + if (!stateless) { + // Make sure that the client has got the lease for the reserved + // address. + ASSERT_EQ("10.0.0.7", client.config_.lease_.addr_.toText()); + } + + ASSERT_EQ(2, client.config_.routers_.size()); + EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText()); + EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText()); + // Make sure that the DNS Servers option has been received. + ASSERT_EQ(2, client.config_.dns_servers_.size()); + EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText()); + EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText()); + // Make sure that the Quotes Servers option has been received. + ASSERT_EQ(2, client.config_.quotes_servers_.size()); + EXPECT_EQ("10.1.1.202", client.config_.quotes_servers_[0].toText()); + EXPECT_EQ("10.1.1.203", client.config_.quotes_servers_[1].toText()); + // Make sure that the Log Servers option has been received. + ASSERT_EQ(2, client.config_.log_servers_.size()); + EXPECT_EQ("10.1.1.200", client.config_.log_servers_[0].toText()); + EXPECT_EQ("10.1.1.201", client.config_.log_servers_[1].toText()); +} + +void +HostOptionsTest::testOverrideDefaultOptions(const bool stateless) { + Dhcp4Client client(Dhcp4Client::SELECTING); + client.setHWAddress("aa:bb:cc:dd:ee:ff"); + + client.requestOptions(DHO_LOG_SERVERS, DHO_COOKIE_SERVERS); + + // Configure DHCP server. + configure(HOST_CONFIGS[1], *client.getServer()); + + if (stateless) { + // Need to relay the message from a specific address which can + // be matched with a configured subnet. + client.useRelay(true, IOAddress("10.0.0.233")); + ASSERT_NO_THROW(client.doInform()); + + } else { + // Perform 4-way exchange with the server but to not request any + // specific address in the DHCPDISCOVER message. + ASSERT_NO_THROW(client.doDORA()); + } + + // Perform 4-way exchange with the server but to not request any + // specific address in the DHCPDISCOVER message. + ASSERT_NO_THROW(client.doDORA()); + + // Make sure that the server responded. + ASSERT_TRUE(client.getContext().response_); + Pkt4Ptr resp = client.getContext().response_; + // Make sure that the server has responded with DHCPACK. + ASSERT_EQ(DHCPACK, static_cast(resp->getType())); + + if (!stateless) { + // Make sure that the client has got the lease for the reserved + // address. + ASSERT_EQ("10.0.0.7", client.config_.lease_.addr_.toText()); + } + + ASSERT_EQ(2, client.config_.routers_.size()); + EXPECT_EQ("10.1.1.200", client.config_.routers_[0].toText()); + EXPECT_EQ("10.1.1.201", client.config_.routers_[1].toText()); + // Make sure that the DNS Servers option has been received. + ASSERT_EQ(2, client.config_.dns_servers_.size()); + EXPECT_EQ("10.1.1.202", client.config_.dns_servers_[0].toText()); + EXPECT_EQ("10.1.1.203", client.config_.dns_servers_[1].toText()); + // Make sure that the Quotes Servers option has been received. + ASSERT_EQ(2, client.config_.quotes_servers_.size()); + EXPECT_EQ("10.0.0.206", client.config_.quotes_servers_[0].toText()); + EXPECT_EQ("10.0.0.207", client.config_.quotes_servers_[1].toText()); + // Make sure that the Log Servers option has been received. + ASSERT_EQ(2, client.config_.log_servers_.size()); + EXPECT_EQ("10.0.0.204", client.config_.log_servers_[0].toText()); + EXPECT_EQ("10.0.0.205", client.config_.log_servers_[1].toText()); +} + +void +HostOptionsTest::testHostOnlyOptions(const bool stateless) { + Dhcp4Client client(Dhcp4Client::SELECTING); + client.setHWAddress("aa:bb:cc:dd:ee:ff"); + client.requestOptions(DHO_COOKIE_SERVERS); + + // Configure DHCP server. + configure(HOST_CONFIGS[2], *client.getServer()); + + if (stateless) { + // Need to relay the message from a specific address which can + // be matched with a configured subnet. + client.useRelay(true, IOAddress("10.0.0.233")); + ASSERT_NO_THROW(client.doInform()); + + } else { + // Perform 4-way exchange with the server but to not request any + // specific address in the DHCPDISCOVER message. + ASSERT_NO_THROW(client.doDORA()); + } + + // Make sure that the server responded. + ASSERT_TRUE(client.getContext().response_); + Pkt4Ptr resp = client.getContext().response_; + // Make sure that the server has responded with DHCPACK. + ASSERT_EQ(DHCPACK, static_cast(resp->getType())); + + if (!stateless) { + // Make sure that the client has got the lease for the reserved + // address. + ASSERT_EQ("10.0.0.7", client.config_.lease_.addr_.toText()); + } + + // Make sure that the Routers options has been received. + ASSERT_EQ(2, client.config_.routers_.size()); + EXPECT_EQ("10.1.1.200", client.config_.routers_[0].toText()); + EXPECT_EQ("10.1.1.201", client.config_.routers_[1].toText()); + // Make sure that the Quotes Servers option has been received. + ASSERT_EQ(2, client.config_.quotes_servers_.size()); + EXPECT_EQ("10.1.1.206", client.config_.quotes_servers_[0].toText()); + EXPECT_EQ("10.1.1.207", client.config_.quotes_servers_[1].toText()); + + // Other options are not configured and should not be delivered. + EXPECT_EQ(0, client.config_.dns_servers_.size()); + EXPECT_EQ(0, client.config_.log_servers_.size()); +} + +void +HostOptionsTest::testOverrideVendorOptions(const bool stateless) { + Dhcp4Client client(Dhcp4Client::SELECTING); + client.setHWAddress("aa:bb:cc:dd:ee:ff"); + + // Client needs to include V-I Vendor Specific Information option + // to include ORO suboption, which the server will use to determine + // which suboptions should be returned to the client. + OptionVendorPtr opt_vendor(new OptionVendor(Option::V4, + VENDOR_ID_CABLE_LABS)); + // Include ORO with TFTP servers suboption code being requested. + opt_vendor->addOption(OptionPtr(new OptionUint8(Option::V4, DOCSIS3_V4_ORO, + DOCSIS3_V4_TFTP_SERVERS))); + client.addExtraOption(opt_vendor); + + // Configure DHCP server. + configure(HOST_CONFIGS[3], *client.getServer()); + + if (stateless) { + // Need to relay the message from a specific address which can + // be matched with a configured subnet. + client.useRelay(true, IOAddress("10.0.0.233")); + ASSERT_NO_THROW(client.doInform()); + + } else { + // Perform 4-way exchange with the server. + ASSERT_NO_THROW(client.doDORA()); + } + + // Make sure that the server responded. + ASSERT_TRUE(client.getContext().response_); + Pkt4Ptr resp = client.getContext().response_; + // Make sure that the server has responded with DHCPACK. + ASSERT_EQ(DHCPACK, static_cast(resp->getType())); + + if (!stateless) { + // Make sure that the client has got the lease for the reserved + // address. + ASSERT_EQ("10.0.0.7", client.config_.lease_.addr_.toText()); + } + + // Make sure the server has responded with a V-I Vendor Specific + // Information option with exactly one suboption. + ASSERT_EQ(1, client.config_.vendor_suboptions_.size()); + // Assume this suboption is a TFTP servers suboption. + std::multimap::const_iterator opt = + client.config_.vendor_suboptions_.find(DOCSIS3_V4_TFTP_SERVERS); + ASSERT_TRUE(opt->second); + Option4AddrLstPtr opt_tftp = boost::dynamic_pointer_cast< + Option4AddrLst>(opt->second); + ASSERT_TRUE(opt_tftp); + // TFTP servers suboption should contain addresses specified on host level. + const Option4AddrLst::AddressContainer& tftps = opt_tftp->getAddresses(); + ASSERT_EQ(2, tftps.size()); + EXPECT_EQ("10.1.1.202", tftps[0].toText()); + EXPECT_EQ("10.1.1.203", tftps[1].toText()); +} + +// This test checks that host specific options override subnet specific +// options. Overridden options are requested with Parameter Request List +// option (stateless case). +TEST_F(HostOptionsTest, overrideRequestedOptionsStateless) { + testOverrideRequestedOptions(STATELESS); +} + +// This test checks that host specific options override subnet specific +// options. Overridden options are requested with Parameter Request List +// option (stateful case). +TEST_F(HostOptionsTest, overrideRequestedOptionsStateful) { + testOverrideRequestedOptions(STATEFUL); +} + +// This test checks that host specific options override subnet specific +// options. Overridden options are the options which server sends +// regardless if they are requested with Parameter Request List option +// or not (stateless case). +TEST_F(HostOptionsTest, overrideDefaultOptionsStateless) { + testOverrideDefaultOptions(STATELESS); +} + +// This test checks that host specific options override subnet specific +// options. Overridden options are the options which server sends +// regardless if they are requested with Parameter Request List option +// or not (stateful case). +TEST_F(HostOptionsTest, overrideDefaultOptionsStateful) { + testOverrideDefaultOptions(STATEFUL); +} + +// This test checks that client receives options when they are +// solely defined in the host scope and not in the global or subnet +// scope (stateless case). +TEST_F(HostOptionsTest, hostOnlyOptionsStateless) { + testHostOnlyOptions(STATELESS); +} + +// This test checks that client receives options when they are +// solely defined in the host scope and not in the global or subnet +// scope (stateful case). +TEST_F(HostOptionsTest, hostOnlyOptionsStateful) { + testHostOnlyOptions(STATEFUL); +} + +// This test checks that host specific vendor options override vendor +// options defined in the global scope (stateless case). +TEST_F(HostOptionsTest, overrideVendorOptionsStateless) { + testOverrideVendorOptions(STATELESS); +} + +// This test checks that host specific vendor options override vendor +// options defined in the global scope (stateful case). +TEST_F(HostOptionsTest, overrideVendorOptionsStateful) { + testOverrideVendorOptions(STATEFUL); +} + +} // end of anonymous namespace diff --git a/src/bin/dhcp4/tests/inform_unittest.cc b/src/bin/dhcp4/tests/inform_unittest.cc index f168f38d9e..dd75f4f3a3 100644 --- a/src/bin/dhcp4/tests/inform_unittest.cc +++ b/src/bin/dhcp4/tests/inform_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2016 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 @@ -58,11 +58,11 @@ const char* INFORM_CONFIGS[] = { " }," " {" " \"name\": \"log-servers\"," - " \"data\": \"10.0.0.200,10.0.0.201\"" + " \"data\": \"10.0.0.202,10.0.0.203\"" " }," " {" " \"name\": \"cookie-servers\"," - " \"data\": \"10.0.0.202,10.0.0.203\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " } ]" " } ]" "}", @@ -159,12 +159,12 @@ TEST_F(InformTest, directClientBroadcast) { EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText()); // Make sure that the Log Servers option has been received. ASSERT_EQ(2, client.config_.quotes_servers_.size()); - EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText()); - EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText()); + EXPECT_EQ("10.0.0.200", client.config_.quotes_servers_[0].toText()); + EXPECT_EQ("10.0.0.201", client.config_.quotes_servers_[1].toText()); // Make sure that the Quotes Servers option has been received. ASSERT_EQ(2, client.config_.log_servers_.size()); - EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText()); - EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText()); + EXPECT_EQ("10.0.0.202", client.config_.log_servers_[0].toText()); + EXPECT_EQ("10.0.0.203", client.config_.log_servers_[1].toText()); // Check that we can send another DHCPINFORM message using // different ciaddr and we will get the configuration. @@ -313,14 +313,14 @@ TEST_F(InformTest, relayedClient) { ASSERT_EQ(2, client.config_.dns_servers_.size()); EXPECT_EQ("192.0.2.202", client.config_.dns_servers_[0].toText()); EXPECT_EQ("192.0.2.203", client.config_.dns_servers_[1].toText()); - // Make sure that the Log Servers option has been received. - ASSERT_EQ(2, client.config_.quotes_servers_.size()); - EXPECT_EQ("192.0.2.200", client.config_.routers_[0].toText()); - EXPECT_EQ("192.0.2.201", client.config_.routers_[1].toText()); // Make sure that the Quotes Servers option has been received. + ASSERT_EQ(2, client.config_.quotes_servers_.size()); + EXPECT_EQ("10.0.0.202", client.config_.quotes_servers_[0].toText()); + EXPECT_EQ("10.0.0.203", client.config_.quotes_servers_[1].toText()); + // Make sure that the Log Servers option has been received. ASSERT_EQ(2, client.config_.log_servers_.size()); - EXPECT_EQ("192.0.2.202", client.config_.dns_servers_[0].toText()); - EXPECT_EQ("192.0.2.203", client.config_.dns_servers_[1].toText()); + EXPECT_EQ("10.0.0.200", client.config_.log_servers_[0].toText()); + EXPECT_EQ("10.0.0.201", client.config_.log_servers_[1].toText()); } // This test checks that the server can respond to the DHCPINFORM message diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index 9bbc29f88c..10935453ba 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -211,7 +211,19 @@ const OptionDefParams OPTION_DEF_PARAMS4[] = { { "domain-search", DHO_DOMAIN_SEARCH, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" }, { "vivco-suboptions", DHO_VIVCO_SUBOPTIONS, OPT_RECORD_TYPE, false, RECORD_DEF(VIVCO_RECORDS), "" }, - { "vivso-suboptions", DHO_VIVSO_SUBOPTIONS, OPT_BINARY_TYPE, + // Vendor-Identifying Vendor Specific Information option payload begins with a + // 32-bit log enterprise number, followed by a tuple of data-len/option-data. + // The format defined here includes 32-bit field holding enterprise number. + // This allows for specifying option-data information where the enterprise-id + // is represented by a uint32_t value. Previously we represented this option + // as a binary, but that would imply that enterprise number would have to be + // represented in binary format in the server configuration. That would be + // inconvenient and non-intuitive. + /// @todo We need to extend support for vendor options with ability to specify + /// multiple enterprise numbers for a single option. Perhaps it would be + /// ok to specify multiple instances of the "vivso-suboptions" which will be + /// combined in a single option by the server before responding to a client. + { "vivso-suboptions", DHO_VIVSO_SUBOPTIONS, OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" } // @todo add definitions for all remaning options.