Commit 30276d77 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[2314] Encapsulate standard DHCPv6 options.

parent 2cecfb45
......@@ -1395,21 +1395,39 @@ private:
if (!option) {
return;
}
OptionDefinitionPtr def;
if (option_space == "dhcp6" &&
LibDHCP::isStandardOption(Option::V6, option->getType())) {
def = LibDHCP::getOptionDef(Option::V6, 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.
const OptionDefContainerPtr defs =
option_def_defaults.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;
}
// Get the encapsulated option space name.
std::string encapsulated_space = (*range.first)->getEncapsulatedSpace();
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()) {
......
......@@ -17,6 +17,8 @@
#include <config/ccsession.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_int.h>
#include <dhcp6/config_parser.h>
#include <dhcp6/dhcp6_srv.h>
#include <dhcpsrv/cfgmgr.h>
......@@ -1481,4 +1483,164 @@ TEST_F(Dhcp6ParserTest, stdOptionData) {
EXPECT_EQ(1516, optionIA->getT2());
}
// The goal of this test is to verify that the standard option can
// be configured to encapsulate multiple other options.
TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
// The configuration is two stage process in this test.
// In the first stahe we create definitions of suboptions
// that we will add to the base option.
// Let's create some dummy options: foo and foo2.
string config = "{ \"interface\": [ \"all\" ],"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
" \"name\": \"foo\","
" \"space\": \"vendor-opts-space\","
" \"code\": 110,"
" \"data\": \"1234\","
" \"csv-format\": True"
" },"
" {"
" \"name\": \"foo2\","
" \"space\": \"vendor-opts-space\","
" \"code\": 111,"
" \"data\": \"192.168.2.1\","
" \"csv-format\": True"
" } ],"
"\"option-def\": [ {"
" \"name\": \"foo\","
" \"code\": 110,"
" \"type\": \"uint32\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"vendor-opts-space\","
" \"encapsulate\": \"\""
" },"
" {"
" \"name\": \"foo2\","
" \"code\": 111,"
" \"type\": \"ipv4-address\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"vendor-opts-space\","
" \"encapsulate\": \"\""
" } ]"
"}";
ConstElementPtr status;
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
ASSERT_TRUE(status);
checkResult(status, 0);
// Once the definitions have been added we can configure the
// standard option #17. This option comprises an enterprise
// number and sub options. By convention (introduced in
// std_option_defs.h) option named 'vendor-opts'
// encapsulates the option space named 'vendor-opts-space'.
// We add our dummy options to this option space and thus
// they should be included as sub-options in the 'vendor-opts'
// option.
config = "{ \"interface\": [ \"all\" ],"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
" \"name\": \"vendor-opts\","
" \"space\": \"dhcp6\","
" \"code\": 17,"
" \"data\": \"1234\","
" \"csv-format\": True"
" },"
" {"
" \"name\": \"foo\","
" \"space\": \"vendor-opts-space\","
" \"code\": 110,"
" \"data\": \"1234\","
" \"csv-format\": True"
" },"
" {"
" \"name\": \"foo2\","
" \"space\": \"vendor-opts-space\","
" \"code\": 111,"
" \"data\": \"192.168.2.1\","
" \"csv-format\": True"
" } ],"
"\"option-def\": [ {"
" \"name\": \"foo\","
" \"code\": 110,"
" \"type\": \"uint32\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"vendor-opts-space\","
" \"encapsulate\": \"\""
" },"
" {"
" \"name\": \"foo2\","
" \"code\": 111,"
" \"type\": \"ipv4-address\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"vendor-opts-space\","
" \"encapsulate\": \"\""
" } ],"
"\"subnet6\": [ { "
" \"pool\": [ \"2001:db8:1::/80\" ],"
" \"subnet\": \"2001:db8:1::/64\""
" } ]"
"}";
json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
ASSERT_TRUE(status);
checkResult(status, 0);
// Get the subnet.
Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
ASSERT_TRUE(subnet);
// We should have one option available.
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
ASSERT_TRUE(options);
ASSERT_EQ(1, options->size());
// Get the option.
Subnet::OptionDescriptor desc =
subnet->getOptionDescriptor("dhcp6", D6O_VENDOR_OPTS);
EXPECT_TRUE(desc.option);
EXPECT_EQ(D6O_VENDOR_OPTS, desc.option->getType());
// Option with the code 110 should be added as a sub-option.
OptionPtr option_foo = desc.option->getOption(110);
ASSERT_TRUE(option_foo);
EXPECT_EQ(110, option_foo->getType());
// This option comprises a single uint32_t value thus it is
// represented by OptionInt<uint32_t> class. Let's get the
// object of this type.
boost::shared_ptr<OptionInt<uint32_t> > option_foo_uint32 =
boost::dynamic_pointer_cast<OptionInt<uint32_t> >(option_foo);
ASSERT_TRUE(option_foo_uint32);
// Validate the value according to the configuration.
EXPECT_EQ(1234, option_foo_uint32->getValue());
// Option with the code 111 should be added as a sub-option.
OptionPtr option_foo2 = desc.option->getOption(111);
ASSERT_TRUE(option_foo2);
EXPECT_EQ(111, option_foo2->getType());
// This option comprises the IPV4 address. Such option is
// represented by OptionCustom object.
OptionCustomPtr option_foo2_v4 =
boost::dynamic_pointer_cast<OptionCustom>(option_foo2);
ASSERT_TRUE(option_foo2_v4);
// Get the IP address carried by this option and validate it.
EXPECT_EQ("192.168.2.1", option_foo2_v4->readAddress().toText());
// Option with the code 112 should not be added.
EXPECT_FALSE(desc.option->getOption(112));
}
};
......@@ -352,6 +352,9 @@ private:
std::vector<OptionBuffer> buffers_;
};
/// A pointer to the OptionCustom object.
typedef boost::shared_ptr<OptionCustom> OptionCustomPtr;
} // namespace isc::dhcp
} // namespace isc
......
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