Commit 27e61190 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac2314'

Conflicts:
	src/bin/dhcp6/tests/config_parser_unittest.cc
parents de49cb64 79acb119
......@@ -766,7 +766,10 @@ private:
}
std::string option_space = getParam<std::string>("space", string_values_);
/// @todo Validate option space once #2313 is merged.
if (!OptionSpace::validateName(option_space)) {
isc_throw(DhcpConfigError, "invalid option space name'"
<< option_space << "'");
}
OptionDefinitionPtr def;
if (option_space == "dhcp4" &&
......@@ -855,7 +858,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() << "'");
}
......@@ -875,6 +878,7 @@ private:
<< ", code: " << option_code << "): "
<< ex.what());
}
}
// All went good, so we can set the option space name.
option_space_ = option_space;
......@@ -976,7 +980,7 @@ public:
/// @brief Parser for a single option definition.
///
/// This parser creates an instance of a single option definition.
class OptionDefParser: DhcpConfigParser {
class OptionDefParser : public DhcpConfigParser {
public:
/// @brief Constructor.
......@@ -1003,7 +1007,8 @@ public:
std::string entry(param.first);
ParserPtr parser;
if (entry == "name" || entry == "type" ||
entry == "record-types" || entry == "space") {
entry == "record-types" || entry == "space" ||
entry == "encapsulate") {
StringParserPtr
str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
if (str_parser) {
......@@ -1053,8 +1058,8 @@ public:
/// @brief Stores the parsed option definition in a storage.
void commit() {
// @todo validate option space name once 2313 is merged.
if (storage_ && option_definition_) {
if (storage_ && option_definition_ &&
OptionSpace::validateName(option_space_name_)) {
storage_->addItem(option_definition_, option_space_name_);
}
}
......@@ -1076,11 +1081,10 @@ private:
void createOptionDef() {
// Get the option space name and validate it.
std::string space = getParam<std::string>("space", string_values_);
// @todo uncomment the code below when the #2313 is merged.
/* if (!OptionSpace::validateName()) {
if (!OptionSpace::validateName(space)) {
isc_throw(DhcpConfigError, "invalid option space name '"
<< space << "'");
} */
}
// Get other parameters that are needed to create the
// option definition.
......@@ -1088,9 +1092,35 @@ private:
uint32_t code = getParam<uint32_t>("code", uint32_values_);
std::string type = getParam<std::string>("type", string_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
// of data types that form a record.
std::string record_types = getParam<std::string>("record-types",
......@@ -1108,7 +1138,7 @@ private:
}
} catch (const Exception& ex) {
isc_throw(DhcpConfigError, "invalid record type values"
<< " specified for the option definition: "
<< " specified for the option definition: "
<< ex.what());
}
}
......@@ -1330,6 +1360,63 @@ 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) {
// Only non-NULL options are stored in option container.
// If this option pointer is NULL this is a serious error.
assert(option);
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 for 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.
......@@ -1405,6 +1492,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);
}
......@@ -1432,6 +1521,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);
}
}
......
......@@ -80,6 +80,12 @@
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "encapsulate",
"item_type": "string",
"item_optional": false,
"item_default": ""
} ]
}
},
......
......@@ -102,7 +102,6 @@ OptionStorage option_defaults;
/// @brief Global storage for option definitions.
OptionDefStorage option_def_intermediate;
/// @brief a dummy configuration parser
///
/// This is a debugging parser. It does not configure anything,
......@@ -797,7 +796,10 @@ private:
}
std::string option_space = getParam<std::string>("space", string_values_);
/// @todo Validate option space once #2313 is merged.
if (!OptionSpace::validateName(option_space)) {
isc_throw(DhcpConfigError, "invalid option space name'"
<< option_space << "'");
}
OptionDefinitionPtr def;
if (option_space == "dhcp6" &&
......@@ -886,7 +888,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() << "'");
}
......@@ -1033,8 +1035,8 @@ public:
BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
std::string entry(param.first);
ParserPtr parser;
if (entry == "name" || entry == "type" ||
entry == "record-types" || entry == "space") {
if (entry == "name" || entry == "type" || entry == "record-types" ||
entry == "space" || entry == "encapsulate") {
StringParserPtr
str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
if (str_parser) {
......@@ -1084,8 +1086,8 @@ public:
/// @brief Stores the parsed option definition in the data store.
void commit() {
// @todo validate option space name once 2313 is merged.
if (storage_ && option_definition_) {
if (storage_ && option_definition_ &&
OptionSpace::validateName(option_space_name_)) {
storage_->addItem(option_definition_, option_space_name_);
}
}
......@@ -1107,11 +1109,10 @@ private:
void createOptionDef() {
// Get the option space name and validate it.
std::string space = getParam<std::string>("space", string_values_);
// @todo uncomment the code below when the #2313 is merged.
/* if (!OptionSpace::validateName()) {
if (!OptionSpace::validateName(space)) {
isc_throw(DhcpConfigError, "invalid option space name '"
<< space << "'");
} */
}
// Get other parameters that are needed to create the
// option definition.
......@@ -1119,9 +1120,36 @@ private:
uint32_t code = getParam<uint32_t>("code", uint32_values_);
std::string type = getParam<std::string>("type", string_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
// of data types that form a record.
std::string record_types = getParam<std::string>("record-types",
......@@ -1139,7 +1167,7 @@ private:
}
} catch (const Exception& ex) {
isc_throw(DhcpConfigError, "invalid record type values"
<< " specified for the option definition: "
<< " specified for the option definition: "
<< ex.what());
}
}
......@@ -1358,6 +1386,63 @@ 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) {
// Only non-NULL options are stored in option container.
// If this option pointer is NULL this is a serious error.
assert(option);
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 for 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.
......@@ -1458,6 +1543,8 @@ private:
LOG_WARN(dhcp6_logger, DHCP6_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);
}
......@@ -1485,6 +1572,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);
}
}
......
......@@ -86,6 +86,12 @@
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "encapsulate",
"item_type": "string",
"item_optional": false,
"item_default": ""
} ]
}
},
......
......@@ -411,14 +411,9 @@ void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer)
}
// Get the list of options that client requested.
const std::vector<uint16_t>& requested_opts = option_oro->getValues();
// Get the list of options configured for a subnet.
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
// Try to match requested options with those configured for a subnet.
// If match is found, append configured option to the answer message.
BOOST_FOREACH(uint16_t opt, requested_opts) {
const Subnet::OptionContainerTypeRange& range = idx.equal_range(opt);
BOOST_FOREACH(Subnet::OptionDescriptor desc, range) {
Subnet::OptionDescriptor desc = subnet->getOptionDescriptor("dhcp6", opt);
if (desc.option) {
answer->addOption(desc.option);
}
}
......
......@@ -32,6 +32,7 @@ libb10_dhcp___la_SOURCES += option.cc option.h
libb10_dhcp___la_SOURCES += option_custom.cc option_custom.h
libb10_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
libb10_dhcp___la_SOURCES += option_space.cc option_space.h
libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
libb10_dhcp___la_SOURCES += std_option_defs.h
......
......@@ -329,10 +329,35 @@ LibDHCP::initStdOptionDefs4() {
// Now let's add all option definitions.
for (int i = 0; i < OPTION_DEF_PARAMS_SIZE4; ++i) {
OptionDefinitionPtr definition(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
OPTION_DEF_PARAMS4[i].code,
OPTION_DEF_PARAMS4[i].type,
OPTION_DEF_PARAMS4[i].array));
std::string encapsulates(OPTION_DEF_PARAMS4[i].encapsulates);
if (!encapsulates.empty() && OPTION_DEF_PARAMS4[i].array) {
isc_throw(isc::BadValue, "invalid standard option definition: "
<< "option with code '" << OPTION_DEF_PARAMS4[i].code
<< "' may not encapsulate option space '"
<< encapsulates << "' because the definition"
<< " indicates that this option comprises an array"
<< " of values");
}
// Depending whether the option encapsulates an option space or not
// we pick different constructor to create an instance of the option
// definition.
OptionDefinitionPtr definition;
if (encapsulates.empty()) {
// Option does not encapsulate any option space.
definition.reset(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
OPTION_DEF_PARAMS4[i].code,
OPTION_DEF_PARAMS4[i].type,
OPTION_DEF_PARAMS4[i].array));
} else {
// Option does encapsulate an option space.
definition.reset(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
OPTION_DEF_PARAMS4[i].code,
OPTION_DEF_PARAMS4[i].type,
OPTION_DEF_PARAMS4[i].encapsulates));
}
for (int rec = 0; rec < OPTION_DEF_PARAMS4[i].records_size; ++rec) {
definition->addRecordField(OPTION_DEF_PARAMS4[i].records[rec]);
......@@ -358,10 +383,34 @@ LibDHCP::initStdOptionDefs6() {
v6option_defs_.clear();
for (int i = 0; i < OPTION_DEF_PARAMS_SIZE6; ++i) {
OptionDefinitionPtr definition(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
OPTION_DEF_PARAMS6[i].code,
OPTION_DEF_PARAMS6[i].type,
OPTION_DEF_PARAMS6[i].array));
std::string encapsulates(OPTION_DEF_PARAMS6[i].encapsulates);
if (!encapsulates.empty() && OPTION_DEF_PARAMS6[i].array) {
isc_throw(isc::BadValue, "invalid standard option definition: "
<< "option with code '" << OPTION_DEF_PARAMS6[i].code
<< "' may not encapsulate option space '"
<< encapsulates << "' because the definition"
<< " indicates that this option comprises an array"
<< " of values");
}
// Depending whether an option encapsulates an option space or not
// we pick different constructor to create an instance of the option
// definition.
OptionDefinitionPtr definition;
if (encapsulates.empty()) {
// Option does not encapsulate any option space.
definition.reset(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
OPTION_DEF_PARAMS6[i].code,
OPTION_DEF_PARAMS6[i].type,
OPTION_DEF_PARAMS6[i].array));
} else {
// Option does encapsulate an option space.
definition.reset(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
OPTION_DEF_PARAMS6[i].code,
OPTION_DEF_PARAMS6[i].type,
OPTION_DEF_PARAMS6[i].encapsulates));
}
for (int rec = 0; rec < OPTION_DEF_PARAMS6[i].records_size; ++rec) {
definition->addRecordField(OPTION_DEF_PARAMS6[i].records[rec]);
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -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
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -21,6 +21,7 @@
#include <dhcp/option_definition.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_space.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
#include <boost/algorithm/string/classification.hpp>
......@@ -40,7 +41,8 @@ OptionDefinition::OptionDefinition(const std::string& name,
: name_(name),
code_(code),
type_(OPT_UNKNOWN_TYPE),
array_type_(array_type) {
array_type_(array_type),
encapsulated_space_("") {
// Data type is held as enum value by this class.
// Use the provided option type string to get the
// corresponding enum value.
......@@ -54,7 +56,33 @@ OptionDefinition::OptionDefinition(const std::string& name,
: name_(name),
code_(code),
type_(type),
array_type_(array_type) {
array_type_(array_type),
encapsulated_space_("") {
}
OptionDefinition::OptionDefinition(const std::string& name,
const uint16_t code,
const std::string& type,
const char* encapsulated_space)
: name_(name),
code_(code),
// Data type is held as enum value by this class.
// Use the provided option type string to get the
// corresponding enum value.
type_(OptionDataTypeUtil::getDataType(type)),
array_type_(false),
encapsulated_space_(encapsulated_space) {
}
OptionDefinition::OptionDefinition(const std::string& name,
const uint16_t code,
const OptionDataType type,
const char* encapsulated_space)
: name_(name),
code_(code),
type_(type),
array_type_(false),
encapsulated_space_(encapsulated_space) {
}
void
......@@ -228,6 +256,11 @@ OptionDefinition::validate() const {
all(find_tail(name_, 1), boost::is_any_of(std::string("-_")))) {
err_str << "invalid option name '" << name_ << "'";
} else if (!encapsulated_space_.empty() &&
!OptionSpace::validateName(encapsulated_space_)) {
err_str << "invalid encapsulated option space name: '"