Commit 3f1577b5 authored by Marcin Siodelski's avatar Marcin Siodelski

[2314] Append sub-options to DHCPv4 options.

parent 30276d77
......@@ -860,7 +860,7 @@ private:
// definition of option value makes sense.
if (def->getName() != option_name) {
isc_throw(DhcpConfigError, "specified option name '"
<< option_name << " does not match the "
<< option_name << "' does not match the "
<< "option definition: '" << option_space
<< "." << def->getName() << "'");
}
......@@ -1361,6 +1361,64 @@ private:
return (false);
}
/// @brief Append sub-options to an option.
///
/// @param option_space a name of the encapsulated option space.
/// @param option option instance to append sub-options to.
void appendSubOptions(const std::string& option_space, OptionPtr& option) {
// If invalid pointer there is nothing to do.
if (!option) {
return;
}
OptionDefinitionPtr def;
if (option_space == "dhcp4" &&
LibDHCP::isStandardOption(Option::V4, option->getType())) {
def = LibDHCP::getOptionDef(Option::V4, option->getType());
// Definitions for some of the standard options hasn't been
// implemented so it is ok to leave here.
if (!def) {
return;
}
} else {
const OptionDefContainerPtr defs =
option_def_intermediate.getItems(option_space);
const OptionDefContainerTypeIndex& idx = defs->get<1>();
const OptionDefContainerTypeRange& range =
idx.equal_range(option->getType());
// There is no definition so we have to leave.
if (std::distance(range.first, range.second) == 0) {
return;
}
def = *range.first;
// If the definition exists, it must be non-NULL.
// Otherwise it is a programming error.
assert(def);
}
// We need to get option definition fo the particular option space
// and code. This definition holds the information whether our
// option encapsulates any option space.
// Get the encapsulated option space name.
std::string encapsulated_space = def->getEncapsulatedSpace();
// If option space name is empty it means that our option does not
// encapsulate any option space (does not include sub-options).
if (!encapsulated_space.empty()) {
// Get the sub-options that belong to the encapsulated
// option space.
const Subnet::OptionContainerPtr sub_opts =
option_defaults.getItems(encapsulated_space);
// Append sub-options to the option.
BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
if (desc.option) {
option->addOption(desc.option);
}
}
}
}
/// @brief Create a new subnet using a data from child parsers.
///
/// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
......@@ -1435,6 +1493,8 @@ private:
LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
.arg(desc.option->getType()).arg(addr.toText());
}
// Add sub-options (if any).
appendSubOptions(option_space, desc.option);
// In any case, we add the option to the subnet.
subnet_->addOption(desc.option, false, option_space);
}
......@@ -1462,6 +1522,9 @@ private:
Subnet::OptionDescriptor existing_desc =
subnet_->getOptionDescriptor(option_space, desc.option->getType());
if (!existing_desc.option) {
// Add sub-options (if any).
appendSubOptions(option_space, desc.option);
subnet_->addOption(desc.option, false, option_space);
}
}
......
......@@ -61,7 +61,7 @@ public:
EXPECT_EQ(expected_value, it->second);
}
// Checks if config_result (result of DHCP server configuration) has
// Checks if the result of DHCP server configuration has
// expected code (0 for success, other for failures).
// Also stores result in rcode_ and comment_.
void checkResult(ConstElementPtr status, int expected_code) {
......@@ -1079,6 +1079,166 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
ASSERT_FALSE(desc3.option);
}
// The goal of this test is to verify that it is possible to
// encapsulate option space containing some options with
// another option. In this test we create base option that
// encapsulates option space 'isc' that comprises two other
// options. Also, for all options their definitions are
// created.
TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
// @todo DHCP configurations has many dependencies between
// parameters. First of all, configuration for subnet is
// inherited from the global values. Thus subnet has to be
// configured when all global values have been configured.
// Also, an option can encapsulate another option only
// if the latter has been configured. For this reason in this
// test we created two-stage configuration where first we
// created options that belong to encapsulated option space.
// In the second stage we add the base option. Also, the Subnet
// object is configured in the second stage so it is created
// at the very end (when all other parameters are configured).
// Starting stage 1. Configure sub-options and their definitions.
string config = "{ \"interface\": [ \"all\" ],"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
" \"name\": \"foo\","
" \"space\": \"isc\","
" \"code\": 1,"
" \"data\": \"1234\","
" \"csv-format\": True"
" },"
" {"
" \"name\": \"foo2\","
" \"space\": \"isc\","
" \"code\": 2,"
" \"data\": \"192.168.2.1\","
" \"csv-format\": True"
" } ],"
"\"option-def\": [ {"
" \"name\": \"foo\","
" \"code\": 1,"
" \"type\": \"uint32\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"isc\","
" \"encapsulate\": \"\""
" },"
" {"
" \"name\": \"foo2\","
" \"code\": 2,"
" \"type\": \"ipv4-address\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"isc\","
" \"encapsulate\": \"\""
" } ]"
"}";
ConstElementPtr status;
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
ASSERT_TRUE(status);
checkResult(status, 0);
// Stage 2. Configure base option and a subnet. Please note that
// the configuration from the stage 2 is repeated because BIND
// configuration manager sends whole configuration for the lists
// where at least one element is being modified or added.
config = "{ \"interface\": [ \"all\" ],"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
" \"name\": \"base-option\","
" \"space\": \"dhcp4\","
" \"code\": 222,"
" \"data\": \"11\","
" \"csv-format\": True"
" },"
" {"
" \"name\": \"foo\","
" \"space\": \"isc\","
" \"code\": 1,"
" \"data\": \"1234\","
" \"csv-format\": True"
" },"
" {"
" \"name\": \"foo2\","
" \"space\": \"isc\","
" \"code\": 2,"
" \"data\": \"192.168.2.1\","
" \"csv-format\": True"
" } ],"
"\"option-def\": [ {"
" \"name\": \"base-option\","
" \"code\": 222,"
" \"type\": \"uint8\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"dhcp4\","
" \"encapsulate\": \"isc\""
"},"
"{"
" \"name\": \"foo\","
" \"code\": 1,"
" \"type\": \"uint32\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"isc\","
" \"encapsulate\": \"\""
" },"
" {"
" \"name\": \"foo2\","
" \"code\": 2,"
" \"type\": \"ipv4-address\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"isc\","
" \"encapsulate\": \"\""
" } ],"
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
" \"subnet\": \"192.0.2.0/24\""
" } ]"
"}";
json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
ASSERT_TRUE(status);
checkResult(status, 0);
// Get the subnet.
Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
ASSERT_TRUE(subnet);
// We should have one option available.
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
ASSERT_TRUE(options);
ASSERT_EQ(1, options->size());
// Get the option.
Subnet::OptionDescriptor desc = subnet->getOptionDescriptor("dhcp4", 222);
EXPECT_TRUE(desc.option);
EXPECT_EQ(222, desc.option->getType());
// This opton should comprise two sub-options.
// One of them is 'foo' with code 1.
OptionPtr option_foo = desc.option->getOption(1);
ASSERT_TRUE(option_foo);
EXPECT_EQ(1, option_foo->getType());
// ...another one 'foo2' with code 2.
OptionPtr option_foo2 = desc.option->getOption(2);
ASSERT_TRUE(option_foo2);
EXPECT_EQ(2, option_foo2->getType());
}
// Goal of this test is to verify options configuration
// for a single subnet. In particular this test checks
// that local options configuration overrides global
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment