diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 418b978b0e..ce6fbfc8db 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -194,6 +194,8 @@ protected: parser = new RelayInfoParser(config_id, relay_info_, Option::V4); } else if (config_id.compare("option-data") == 0) { parser = new OptionDataListParser(config_id, options_, AF_INET); + } else if (config_id.compare("ignore-client-id") == 0) { + parser = new BooleanParser(config_id, boolean_values_); } else { isc_throw(NotImplemented, "unsupported parameter: " << config_id); } @@ -250,7 +252,28 @@ protected: Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id)); subnet_ = subnet4; - // Try global value first + // ignore-client-id + isc::util::OptionalValue ignore_client_id; + try { + ignore_client_id = boolean_values_->getParam("ignore-client-id"); + + } catch (...) { + // Ignore because this parameter is optional and it may be specified + // in the global scope. + } + + // If the ignore-client-id wasn't specified as a subnet specific parameter + // check if there is global value specified. + if (!ignore_client_id.isSpecified()) { + // If not specified, use false. + ignore_client_id.specify(globalContext()->boolean_values_-> + getOptionalParam("ignore-client-id", false)); + } + + // Set the ignore-client-id value for the subnet. + subnet4->setIgnoreClientId(ignore_client_id.get()); + + // next-server try { string next_server = globalContext()->string_values_->getParam("next-server"); if (!next_server.empty()) { @@ -374,6 +397,8 @@ namespace dhcp { parser = new BooleanParser(config_id, globalContext()->boolean_values_); } else if (config_id.compare("dhcp-ddns") == 0) { parser = new D2ClientConfigParser(config_id); + } else if (config_id.compare("ignore-client-id") == 0) { + parser = new BooleanParser(config_id, globalContext()->boolean_values_); } else { isc_throw(DhcpConfigError, "unsupported global configuration parameter: " diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 7cb289ca06..84a96d809b 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -1102,6 +1102,77 @@ TEST_F(Dhcp4ParserTest, echoClientId) { CfgMgr::instance().echoClientId(true); } +// This test checks that the global ignore-client-id parameter is optional +// and that values under the subnet are used. +TEST_F(Dhcp4ParserTest, ignoreClientIdNoGlobal) { + ConstElementPtr status; + + std::string config = "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ " + "{" + " \"ignore-client-id\": true," + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"" + "}," + "{" + " \"ignore-client-id\": false," + " \"pools\": [ { \"pool\": \"192.0.3.1 - 192.0.3.100\" } ]," + " \"subnet\": \"192.0.3.0/24\"" + "} ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr json = Element::fromJSON(config); + ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json)); + checkResult(status, 0); + + CfgSubnets4Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets4(); + Subnet4Ptr subnet1 = cfg->selectSubnet(IOAddress("192.0.2.1")); + ASSERT_TRUE(subnet1); + EXPECT_TRUE(subnet1->getIgnoreClientId()); + + Subnet4Ptr subnet2 = cfg->selectSubnet(IOAddress("192.0.3.1")); + ASSERT_TRUE(subnet2); + EXPECT_FALSE(subnet2->getIgnoreClientId()); +} + +// This test checks that the global ignore-client-id parameter is used +// when there is no such parameter under subnet and that the parameter +// specified for a subnet overrides the global setting. +TEST_F(Dhcp4ParserTest, ignoreClientIdGlobal) { + ConstElementPtr status; + + std::string config = "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"ignore-client-id\": true," + "\"subnet4\": [ " + "{" + " \"ignore-client-id\": false," + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"" + "}," + "{" + " \"pools\": [ { \"pool\": \"192.0.3.1 - 192.0.3.100\" } ]," + " \"subnet\": \"192.0.3.0/24\"" + "} ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr json = Element::fromJSON(config); + ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json)); + checkResult(status, 0); + + CfgSubnets4Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets4(); + Subnet4Ptr subnet1 = cfg->selectSubnet(IOAddress("192.0.2.1")); + ASSERT_TRUE(subnet1); + EXPECT_FALSE(subnet1->getIgnoreClientId()); + + Subnet4Ptr subnet2 = cfg->selectSubnet(IOAddress("192.0.3.1")); + ASSERT_TRUE(subnet2); + EXPECT_TRUE(subnet2->getIgnoreClientId()); +} + // This test checks if it is possible to override global values // on a per subnet basis. TEST_F(Dhcp4ParserTest, subnetLocal) { diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index a709bab875..aeb5b1c952 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -238,7 +238,7 @@ Iface::setActive(const IOAddress& address, const bool active) { for (AddressCollection::iterator addr_it = addrs_.begin(); addr_it != addrs_.end(); ++addr_it) { if (address == addr_it->get()) { - addr_it->specify(active); + addr_it->specify(OptionalValueState(active)); return; } } @@ -250,7 +250,7 @@ void Iface::setActive(const bool active) { for (AddressCollection::iterator addr_it = addrs_.begin(); addr_it != addrs_.end(); ++addr_it) { - addr_it->specify(active); + addr_it->specify(OptionalValueState(active)); } } diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index 6898049ce8..68da05f44a 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -1030,7 +1030,9 @@ PoolParser::commit() { SubnetConfigParser::SubnetConfigParser(const std::string&, ParserContextPtr global_context, const isc::asiolink::IOAddress& default_addr) - : uint32_values_(new Uint32Storage()), string_values_(new StringStorage()), + : uint32_values_(new Uint32Storage()), + string_values_(new StringStorage()), + boolean_values_(new BooleanStorage()), pools_(new PoolStorage()), global_context_(global_context), relay_info_(new isc::dhcp::Subnet::RelayInfo(default_addr)), options_(new CfgOption()) { diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h index 8d3b89be91..efed05e61a 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h @@ -1063,6 +1063,9 @@ protected: /// Storage for subnet-specific string values. StringStoragePtr string_values_; + /// Storage for subnet-specific boolean values. + BooleanStoragePtr boolean_values_; + /// Storage for pools belonging to this subnet. PoolStoragePtr pools_; diff --git a/src/lib/util/optional_value.h b/src/lib/util/optional_value.h index 81fce94fc2..e8bf4d666f 100644 --- a/src/lib/util/optional_value.h +++ b/src/lib/util/optional_value.h @@ -100,11 +100,7 @@ public: /// @param value New actual value. void specify(const T& value) { set(value); - specify(true); - } - - void specify(const OptionalValueState& state) { - specified_ = state.specified_; + specify(OptionalValueState(true)); } /// @brief Sets the value to "specified" or "unspecified". @@ -112,8 +108,8 @@ public: /// It does not alter the actual value. It only marks it "specified" or /// "unspecified". /// @param specified boolean that determined if a value is specified or not - void specify(const bool specified) { - specified_ = specified; + void specify(const OptionalValueState& state) { + specified_ = state.specified_; } /// @brief Checks if the value is specified or unspecified.