// We need definitions first
ConstElementPtr option_defs = mutable_cfg->get("option-def");
if (option_defs) {
- OptionDefListParser parser;
+ OptionDefListParser parser(AF_INET);
CfgOptionDefPtr cfg_option_def = srv_cfg->getCfgOptionDef();
parser.parse(cfg_option_def, option_defs);
}
return (answer);
}
+void databaseConfigFetch(const SrvConfigPtr& srv_cfg) {
+
+ ConfigBackendDHCPv4Mgr& mgr = ConfigBackendDHCPv4Mgr::instance();
+
+ // Close any existing CB databasess, then open all in srv_cfg (if any)
+ if (!databaseConfigConnect(srv_cfg)) {
+ // There are no CB databases so we're done
+ return;
+ }
+
+ LOG_INFO(dhcp4_logger, DHCP4_CONFIG_FETCH);
+
+ // For now we find data based on first backend that has it.
+ BackendSelector backend_selector(BackendSelector::Type::UNSPEC);
+
+ // Use the server_tag if set, otherwise use ALL.
+ std::string server_tag = srv_cfg->getServerTag();
+ ServerSelector& server_selector = (server_tag.empty()? ServerSelector::ALL()
+ : ServerSelector::ONE(server_tag));
+ // Create the external config into which we'll fetch backend config data.
+ SrvConfigPtr external_cfg = CfgMgr::instance().createExternalCfg();
+
+ // First let's fetch the globals and add them to external config.
+ data::StampedValueCollection globals;
+ globals = mgr.getPool()->getAllGlobalParameters4(backend_selector, server_selector);
+ addGlobalsToConfig(external_cfg, globals);
+
+ // Now we fetch the option definitions and add them.
+ OptionDefContainer option_defs = mgr.getPool()->getAllOptionDefs4(backend_selector,
+ server_selector);
+ for (auto option_def = option_defs.begin(); option_def != option_defs.end(); ++option_def) {
+ external_cfg->getCfgOptionDef()->add((*option_def), (*option_def)->getOptionSpaceName());
+ }
+
+ // Next fetch the options. They are returned as a container of OptionDescriptors.
+ OptionContainer options = mgr.getPool()->getAllOptions4(backend_selector, server_selector);
+ for (auto option = options.begin(); option != options.end(); ++option) {
+ external_cfg->getCfgOption()->add((*option), (*option).space_name_);
+ }
+
+ // Now fetch the shared networks.
+ SharedNetwork4Collection networks = mgr.getPool()->getAllSharedNetworks4(backend_selector,
+ server_selector);
+ for (auto network = networks.begin(); network != networks.end(); ++network) {
+ external_cfg->getCfgSharedNetworks4()->add((*network));
+ }
+
+ // Next we fetch subnets.
+ Subnet4Collection subnets = mgr.getPool()->getAllSubnets4(backend_selector, server_selector);
+ for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
+ external_cfg->getCfgSubnets4()->add((*subnet));
+ }
+
+ // Now we merge the fecthed configuration into the staging configuration.
+ CfgMgr::instance().mergeIntoStagingCfg(external_cfg->getSequence());
+ LOG_INFO(dhcp4_logger, DHCP4_CONFIG_MERGED);
+}
+
+bool databaseConfigConnect(const SrvConfigPtr& srv_cfg) {
+ // We need to get rid of any existing backends. These would be any
+ // opened by previous configuration cycle.
+ ConfigBackendDHCPv4Mgr& mgr = ConfigBackendDHCPv4Mgr::instance();
+ mgr.delAllBackends();
+
+ // Fetch the config-control info.
+ ConstConfigControlInfoPtr config_ctl = srv_cfg->getConfigControlInfo();
+ if (!config_ctl || config_ctl->getConfigDatabases().empty()) {
+ // No config dbs, nothing to do.
+ return (false);
+ }
+
+ // Iterate over the configured DBs and instantiate them.
+ for (auto db : config_ctl->getConfigDatabases()) {
+ LOG_INFO(dhcp4_logger, DHCP4_OPEN_CONFIG_DB)
+ .arg(db.redactedAccessString());
+ mgr.addBackend(db.getAccessString());
+ }
+
+ // Let the caller know we have opened DBs.
+ return (true);
+}
+
+
+void addGlobalsToConfig(SrvConfigPtr external_cfg, data::StampedValueCollection& cb_globals) {
+ const auto& index = cb_globals.get<StampedValueNameIndexTag>();
+ for (auto cb_global = index.begin(); cb_global != index.end(); ++cb_global) {
+
+ if ((*cb_global)->amNull()) {
+ continue;
+ }
+
+ external_cfg->addConfiguredGlobal((*cb_global)->getName(),
+ (*cb_global)->getElementValue());
+ }
+}
+
}; // end of isc::dhcp namespace
}; // end of isc namespace
// We need definitions first
ConstElementPtr option_defs = mutable_cfg->get("option-def");
if (option_defs) {
- OptionDefListParser parser;
+ OptionDefListParser parser(AF_INET6);
CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
parser.parse(cfg_option_def, option_defs);
}
-// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2019 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
SimpleParser4::OPTION4_DEF_DEFAULTS :
SimpleParser6::OPTION6_DEF_DEFAULTS);
- OptionDefParser parser;
+ OptionDefParser parser(family);
BOOST_FOREACH(ConstElementPtr option_def, option_defs->listValue()) {
OptionDefinitionTuple def;
-
+
def = parser.parse(option_def);
// Verify if the defition is for an option which are
// in a deferred processing list.
// ******************************** OptionDefParser ****************************
+OptionDefParser::OptionDefParser(const uint16_t address_family)
+ : address_family_(address_family) {
+}
+
std::pair<isc::dhcp::OptionDefinitionPtr, std::string>
OptionDefParser::parse(ConstElementPtr option_def) {
// Get mandatory parameters.
std::string name = getString(option_def, "name");
- uint32_t code = getInteger(option_def, "code");
+ int64_t code64 = getInteger(option_def, "code");
std::string type = getString(option_def, "type");
// Get optional parameters. Whoever called this parser, should have
std::string encapsulates = getString(option_def, "encapsulate");
ConstElementPtr user_context = option_def->get("user-context");
+ // Check code value.
+ if (code64 < 0) {
+ isc_throw(DhcpConfigError, "option code must not be negative "
+ "(" << getPosition("code", option_def) << ")");
+ } else if (code64 == 0) {
+ isc_throw(DhcpConfigError, "option code must not be zero "
+ "(" << getPosition("code", option_def) << ")");
+ } else if (address_family_ == AF_INET &&
+ code64 > std::numeric_limits<uint8_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << code64
+ << "', it must not be greater than '"
+ << static_cast<int>(std::numeric_limits<uint8_t>::max())
+ << "' (" << getPosition("code", option_def) << ")");
+ } else if (address_family_ == AF_INET6 &&
+ code64 > std::numeric_limits<uint16_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << code64
+ << "', it must not be greater than '"
+ << std::numeric_limits<uint16_t>::max()
+ << "' (" << getPosition("code", option_def) << ")");
+ }
+ uint32_t code = static_cast<uint32_t>(code64);
+
+ // Validate space name.
if (!OptionSpace::validateName(space)) {
isc_throw(DhcpConfigError, "invalid option space name '"
<< space << "' ("
}
// ******************************** OptionDefListParser ************************
+
+OptionDefListParser::OptionDefListParser(const uint16_t address_family)
+ : address_family_(address_family) {
+}
+
void
OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_list) {
if (!option_def_list) {
<< option_def_list->getPosition() << ")");
}
- OptionDefParser parser;
+ OptionDefParser parser(address_family_);
BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
OptionDefinitionTuple def;
/// This parser creates an instance of a single option definition.
class OptionDefParser : public isc::data::SimpleParser {
public:
+ /// @brief Constructor.
+ ///
+ /// @param address_family Address family: @c AF_INET or @c AF_INET6.
+ OptionDefParser(const uint16_t address_family);
+
/// @brief Parses an entry that describes single option definition.
///
/// @param option_def a configuration entry to be parsed.
/// @throw DhcpConfigError if parsing was unsuccessful.
OptionDefinitionTuple
parse(isc::data::ConstElementPtr option_def);
+
+private:
+ /// @brief Address family: @c AF_INET or @c AF_INET6.
+ uint16_t address_family_;
};
/// @brief Parser for a list of option definitions.
/// is put into the provided storage.
class OptionDefListParser : public isc::data::SimpleParser {
public:
+ /// @brief Constructor.
+ ///
+ /// @param address_family Address family: @c AF_INET or @c AF_INET6.
+ OptionDefListParser(const uint16_t address_family);
+
/// @brief Parses a list of option definitions, create them and store in cfg
///
/// This method iterates over def_list, which is a JSON list of option definitions,
/// @param def_list JSON list describing option definitions
/// @param cfg parsed option definitions will be stored here
void parse(CfgOptionDefPtr cfg, isc::data::ConstElementPtr def_list);
+
+private:
+ /// @brief Address family: @c AF_INET or @c AF_INET6.
+ uint16_t address_family_;
};
/// @brief a collection of pools
/// @throw isc::dhcp::DhcpConfigError if the address string is not a valid
/// IP address, is an address of the wrong family, or is already in the
/// relay address list
- void addAddress(const std::string& name, const std::string& address_str,
+ void addAddress(const std::string& name, const std::string& address_str,
isc::data::ConstElementPtr relay_elem,
const isc::dhcp::Network::RelayInfoPtr& relay_info);
private:
if (def_config != values_map.end()) {
CfgOptionDefPtr cfg_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
- OptionDefListParser def_list_parser;
+ OptionDefListParser def_list_parser(family_);
def_list_parser.parse(cfg_def, def_config->second);
}
cfg.runCfgOptionsTest(family_, config);
}
+/// @brief Check parsing of option definitions using invalid code fails.
+TEST_F(ParseConfigTest, badCodeOptionDefTest) {
+
+ {
+
+ SCOPED_TRACE("negative code");
+ std::string config =
+ "{ \"option-def\": [ {"
+ " \"name\": \"negative\","
+ " \"code\": -1,"
+ " \"type\": \"ipv6-address\","
+ " \"space\": \"isc\""
+ " } ]"
+ "}";
+
+ int rcode = parseConfiguration(config, true);
+ ASSERT_NE(0, rcode);
+ }
+
+ {
+ SCOPED_TRACE("zero code");
+ std::string config =
+ "{ \"option-def\": [ {"
+ " \"name\": \"zero\","
+ " \"code\": 0,"
+ " \"type\": \"ipv6-address\","
+ " \"space\": \"isc\""
+ " } ]"
+ "}";
+
+ int rcode = parseConfiguration(config, true);
+ ASSERT_NE(0, rcode);
+ }
+
+ {
+ SCOPED_TRACE("out of range code (v6)");
+ std::string config =
+ "{ \"option-def\": [ {"
+ " \"name\": \"hundred-thousands\","
+ " \"code\": 100000,"
+ " \"type\": \"ipv6-address\","
+ " \"space\": \"isc\""
+ " } ]"
+ "}";
+
+ int rcode = parseConfiguration(config, true);
+ ASSERT_NE(0, rcode);
+ }
+
+ {
+ SCOPED_TRACE("out of range code (v4)");
+ family_ = AF_INET; // Switch to DHCPv4.
+
+ std::string config =
+ "{ \"option-def\": [ {"
+ " \"name\": \"thousand\","
+ " \"code\": 1000,"
+ " \"type\": \"ip-address\","
+ " \"space\": \"isc\""
+ " } ]"
+ "}";
+
+ int rcode = parseConfiguration(config, false);
+ ASSERT_NE(0, rcode);
+ }
+}
+
/// @brief Check parsing of option definitions using invalid space fails.
TEST_F(ParseConfigTest, badSpaceOptionDefTest) {
std::string config =
"{ \"option-def\": [ {"
" \"name\": \"foo\","
- " \"code\": 100000,"
+ " \"code\": 100,"
" \"type\": \"ipv6-address\","
" \"space\": \"-1\""
" } ]"