mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 21:45:37 +00:00
[2314] Configure encapsulated option space for DHCPv6 options.
This commit is contained in:
@@ -880,6 +880,7 @@ private:
|
|||||||
<< ", code: " << option_code << "): "
|
<< ", code: " << option_code << "): "
|
||||||
<< ex.what());
|
<< ex.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// All went good, so we can set the option space name.
|
// All went good, so we can set the option space name.
|
||||||
option_space_ = option_space;
|
option_space_ = option_space;
|
||||||
@@ -1139,7 +1140,7 @@ private:
|
|||||||
}
|
}
|
||||||
} catch (const Exception& ex) {
|
} catch (const Exception& ex) {
|
||||||
isc_throw(DhcpConfigError, "invalid record type values"
|
isc_throw(DhcpConfigError, "invalid record type values"
|
||||||
<< " specified for the option definition: "
|
<< " specified for the option definition: "
|
||||||
<< ex.what());
|
<< ex.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -103,7 +103,6 @@ OptionStorage option_defaults;
|
|||||||
/// @brief Global storage for option definitions.
|
/// @brief Global storage for option definitions.
|
||||||
OptionDefStorage option_def_intermediate;
|
OptionDefStorage option_def_intermediate;
|
||||||
|
|
||||||
|
|
||||||
/// @brief a dummy configuration parser
|
/// @brief a dummy configuration parser
|
||||||
///
|
///
|
||||||
/// This is a debugging parser. It does not configure anything,
|
/// This is a debugging parser. It does not configure anything,
|
||||||
@@ -1037,8 +1036,8 @@ public:
|
|||||||
BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
|
BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
|
||||||
std::string entry(param.first);
|
std::string entry(param.first);
|
||||||
ParserPtr parser;
|
ParserPtr parser;
|
||||||
if (entry == "name" || entry == "type" ||
|
if (entry == "name" || entry == "type" || entry == "record-types" ||
|
||||||
entry == "record-types" || entry == "space") {
|
entry == "space" || entry == "encapsulate") {
|
||||||
StringParserPtr
|
StringParserPtr
|
||||||
str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
|
str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
|
||||||
if (str_parser) {
|
if (str_parser) {
|
||||||
@@ -1122,9 +1121,36 @@ private:
|
|||||||
uint32_t code = getParam<uint32_t>("code", uint32_values_);
|
uint32_t code = getParam<uint32_t>("code", uint32_values_);
|
||||||
std::string type = getParam<std::string>("type", string_values_);
|
std::string type = getParam<std::string>("type", string_values_);
|
||||||
bool array_type = getParam<bool>("array", boolean_values_);
|
bool array_type = getParam<bool>("array", boolean_values_);
|
||||||
|
std::string encapsulates = getParam<std::string>("encapsulate",
|
||||||
|
string_values_);
|
||||||
|
|
||||||
|
// Create option definition.
|
||||||
|
OptionDefinitionPtr def;
|
||||||
|
// We need to check if user has set encapsulated option space
|
||||||
|
// name. If so, different constructor will be used.
|
||||||
|
if (!encapsulates.empty()) {
|
||||||
|
// Arrays can't be used together with sub-options.
|
||||||
|
if (array_type) {
|
||||||
|
isc_throw(DhcpConfigError, "option '" << space << "."
|
||||||
|
<< "name" << "', comprising an array of data"
|
||||||
|
<< " fields may not encapsulate any option space");
|
||||||
|
|
||||||
|
} else if (encapsulates == space) {
|
||||||
|
isc_throw(DhcpConfigError, "option must not encapsulate"
|
||||||
|
<< " an option space it belongs to: '"
|
||||||
|
<< space << "." << name << "' is set to"
|
||||||
|
<< " encapsulate '" << space << "'");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
def.reset(new OptionDefinition(name, code, type,
|
||||||
|
encapsulates.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
def.reset(new OptionDefinition(name, code, type, array_type));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
OptionDefinitionPtr def(new OptionDefinition(name, code,
|
|
||||||
type, array_type));
|
|
||||||
// The record-types field may carry a list of comma separated names
|
// The record-types field may carry a list of comma separated names
|
||||||
// of data types that form a record.
|
// of data types that form a record.
|
||||||
std::string record_types = getParam<std::string>("record-types",
|
std::string record_types = getParam<std::string>("record-types",
|
||||||
@@ -1142,7 +1168,7 @@ private:
|
|||||||
}
|
}
|
||||||
} catch (const Exception& ex) {
|
} catch (const Exception& ex) {
|
||||||
isc_throw(DhcpConfigError, "invalid record type values"
|
isc_throw(DhcpConfigError, "invalid record type values"
|
||||||
<< " specified for the option definition: "
|
<< " specified for the option definition: "
|
||||||
<< ex.what());
|
<< ex.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -86,6 +86,12 @@
|
|||||||
"item_type": "string",
|
"item_type": "string",
|
||||||
"item_optional": false,
|
"item_optional": false,
|
||||||
"item_default": ""
|
"item_default": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
{ "item_name": "encapsulate",
|
||||||
|
"item_type": "string",
|
||||||
|
"item_optional": false,
|
||||||
|
"item_default": ""
|
||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -461,7 +461,8 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
|
|||||||
" \"type\": \"ipv6-address\","
|
" \"type\": \"ipv6-address\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -499,7 +500,8 @@ TEST_F(Dhcp6ParserTest, optionDefRecord) {
|
|||||||
" \"type\": \"record\","
|
" \"type\": \"record\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
|
" \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -546,7 +548,8 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) {
|
|||||||
" \"type\": \"uint32\","
|
" \"type\": \"uint32\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" },"
|
" },"
|
||||||
" {"
|
" {"
|
||||||
" \"name\": \"foo-2\","
|
" \"name\": \"foo-2\","
|
||||||
@@ -554,7 +557,8 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) {
|
|||||||
" \"type\": \"ipv4-address\","
|
" \"type\": \"ipv4-address\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -604,7 +608,8 @@ TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
|
|||||||
" \"type\": \"uint32\","
|
" \"type\": \"uint32\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" },"
|
" },"
|
||||||
" {"
|
" {"
|
||||||
" \"name\": \"foo-2\","
|
" \"name\": \"foo-2\","
|
||||||
@@ -612,7 +617,8 @@ TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
|
|||||||
" \"type\": \"ipv4-address\","
|
" \"type\": \"ipv4-address\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -640,7 +646,8 @@ TEST_F(Dhcp6ParserTest, optionDefArray) {
|
|||||||
" \"type\": \"uint32\","
|
" \"type\": \"uint32\","
|
||||||
" \"array\": True,"
|
" \"array\": True,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -666,6 +673,47 @@ TEST_F(Dhcp6ParserTest, optionDefArray) {
|
|||||||
EXPECT_TRUE(def->getArrayType());
|
EXPECT_TRUE(def->getArrayType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The purpose of this test to verify that encapsulated option
|
||||||
|
// space name may be specified.
|
||||||
|
TEST_F(Dhcp6ParserTest, optionDefEncapsulate) {
|
||||||
|
|
||||||
|
// Configuration string. Included the encapsulated
|
||||||
|
// option space name.
|
||||||
|
std::string config =
|
||||||
|
"{ \"option-def\": [ {"
|
||||||
|
" \"name\": \"foo\","
|
||||||
|
" \"code\": 100,"
|
||||||
|
" \"type\": \"uint32\","
|
||||||
|
" \"array\": False,"
|
||||||
|
" \"record-types\": \"\","
|
||||||
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"sub-opts-space\""
|
||||||
|
" } ]"
|
||||||
|
"}";
|
||||||
|
ElementPtr json = Element::fromJSON(config);
|
||||||
|
|
||||||
|
// Make sure that the particular option definition does not exist.
|
||||||
|
OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
|
||||||
|
ASSERT_FALSE(def);
|
||||||
|
|
||||||
|
// Use the configuration string to create new option definition.
|
||||||
|
ConstElementPtr status;
|
||||||
|
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||||
|
ASSERT_TRUE(status);
|
||||||
|
checkResult(status, 0);
|
||||||
|
|
||||||
|
// The option definition should now be available in the CfgMgr.
|
||||||
|
def = CfgMgr::instance().getOptionDef("isc", 100);
|
||||||
|
ASSERT_TRUE(def);
|
||||||
|
|
||||||
|
// Check the option data.
|
||||||
|
EXPECT_EQ("foo", def->getName());
|
||||||
|
EXPECT_EQ(100, def->getCode());
|
||||||
|
EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
|
||||||
|
EXPECT_FALSE(def->getArrayType());
|
||||||
|
EXPECT_EQ("sub-opts-space", def->getEncapsulatedSpace());
|
||||||
|
}
|
||||||
|
|
||||||
/// The purpose of this test is to verify that the option definition
|
/// The purpose of this test is to verify that the option definition
|
||||||
/// with invalid name is not accepted.
|
/// with invalid name is not accepted.
|
||||||
TEST_F(Dhcp6ParserTest, optionDefInvalidName) {
|
TEST_F(Dhcp6ParserTest, optionDefInvalidName) {
|
||||||
@@ -678,7 +726,8 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidName) {
|
|||||||
" \"type\": \"string\","
|
" \"type\": \"string\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -703,7 +752,8 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidType) {
|
|||||||
" \"type\": \"sting\","
|
" \"type\": \"sting\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -728,7 +778,8 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidRecordType) {
|
|||||||
" \"type\": \"record\","
|
" \"type\": \"record\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"uint32,uint8,sting\","
|
" \"record-types\": \"uint32,uint8,sting\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -741,6 +792,85 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidRecordType) {
|
|||||||
checkResult(status, 1);
|
checkResult(status, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The goal of this test is to verify that the invalid encapsulated
|
||||||
|
/// option space name is not accepted.
|
||||||
|
TEST_F(Dhcp6ParserTest, optionDefInvalidEncapsulatedSpace) {
|
||||||
|
// Configuration string. The encapsulated option space
|
||||||
|
// name is invalid (% character is not allowed).
|
||||||
|
std::string config =
|
||||||
|
"{ \"option-def\": [ {"
|
||||||
|
" \"name\": \"foo\","
|
||||||
|
" \"code\": 100,"
|
||||||
|
" \"type\": \"uint32\","
|
||||||
|
" \"array\": False,"
|
||||||
|
" \"record-types\": \"\","
|
||||||
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"invalid%space%name\""
|
||||||
|
" } ]"
|
||||||
|
"}";
|
||||||
|
ElementPtr json = Element::fromJSON(config);
|
||||||
|
|
||||||
|
// Use the configuration string to create new option definition.
|
||||||
|
ConstElementPtr status;
|
||||||
|
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||||
|
ASSERT_TRUE(status);
|
||||||
|
// Expecting parsing error (error code 1).
|
||||||
|
checkResult(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The goal of this test is to verify that the encapsulated
|
||||||
|
/// option space name can't be specified for the option that
|
||||||
|
/// comprises an array of data fields.
|
||||||
|
TEST_F(Dhcp6ParserTest, optionDefEncapsulatedSpaceAndArray) {
|
||||||
|
// Configuration string. The encapsulated option space
|
||||||
|
// name is set to non-empty value and the array flag
|
||||||
|
// is set.
|
||||||
|
std::string config =
|
||||||
|
"{ \"option-def\": [ {"
|
||||||
|
" \"name\": \"foo\","
|
||||||
|
" \"code\": 100,"
|
||||||
|
" \"type\": \"uint32\","
|
||||||
|
" \"array\": True,"
|
||||||
|
" \"record-types\": \"\","
|
||||||
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"valid-space-name\""
|
||||||
|
" } ]"
|
||||||
|
"}";
|
||||||
|
ElementPtr json = Element::fromJSON(config);
|
||||||
|
|
||||||
|
// Use the configuration string to create new option definition.
|
||||||
|
ConstElementPtr status;
|
||||||
|
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||||
|
ASSERT_TRUE(status);
|
||||||
|
// Expecting parsing error (error code 1).
|
||||||
|
checkResult(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The goal of this test is to verify that the option may not
|
||||||
|
/// encapsulate option space it belongs to.
|
||||||
|
TEST_F(Dhcp6ParserTest, optionDefEncapsulateOwnSpace) {
|
||||||
|
// Configuration string. Option is set to encapsulate
|
||||||
|
// option space it belongs to.
|
||||||
|
std::string config =
|
||||||
|
"{ \"option-def\": [ {"
|
||||||
|
" \"name\": \"foo\","
|
||||||
|
" \"code\": 100,"
|
||||||
|
" \"type\": \"uint32\","
|
||||||
|
" \"array\": False,"
|
||||||
|
" \"record-types\": \"\","
|
||||||
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"isc\""
|
||||||
|
" } ]"
|
||||||
|
"}";
|
||||||
|
ElementPtr json = Element::fromJSON(config);
|
||||||
|
|
||||||
|
// Use the configuration string to create new option definition.
|
||||||
|
ConstElementPtr status;
|
||||||
|
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||||
|
ASSERT_TRUE(status);
|
||||||
|
// Expecting parsing error (error code 1).
|
||||||
|
checkResult(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/// The purpose of this test is to verify that it is not allowed
|
/// The purpose of this test is to verify that it is not allowed
|
||||||
/// to override the standard option (that belongs to dhcp6 option
|
/// to override the standard option (that belongs to dhcp6 option
|
||||||
@@ -759,7 +889,8 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
|
|||||||
" \"type\": \"string\","
|
" \"type\": \"string\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"dhcp6\""
|
" \"space\": \"dhcp6\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
ElementPtr json = Element::fromJSON(config);
|
ElementPtr json = Element::fromJSON(config);
|
||||||
@@ -794,7 +925,8 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
|
|||||||
" \"type\": \"string\","
|
" \"type\": \"string\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"dhcp6\""
|
" \"space\": \"dhcp6\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ]"
|
" } ]"
|
||||||
"}";
|
"}";
|
||||||
json = Element::fromJSON(config);
|
json = Element::fromJSON(config);
|
||||||
@@ -916,7 +1048,8 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
|
|||||||
" \"type\": \"uint32\","
|
" \"type\": \"uint32\","
|
||||||
" \"array\": False,"
|
" \"array\": False,"
|
||||||
" \"record-types\": \"\","
|
" \"record-types\": \"\","
|
||||||
" \"space\": \"isc\""
|
" \"space\": \"isc\","
|
||||||
|
" \"encapsulate\": \"\""
|
||||||
" } ],"
|
" } ],"
|
||||||
"\"subnet6\": [ { "
|
"\"subnet6\": [ { "
|
||||||
" \"pool\": [ \"2001:db8:1::/80\" ],"
|
" \"pool\": [ \"2001:db8:1::/80\" ],"
|
||||||
|
Reference in New Issue
Block a user