2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 04:57:52 +00:00

[#2826] Options in db are not encapsulated

Returned options from db are not encapsulated and have to be encapsulated
before the server returns them to a client. Parsers, depending on the use
case, may or may not encapsulate options. When they are used for parsing
a config file, the options are encapsulated. When they are used by the
host_cmds or cb_cmds, they are not.
This commit is contained in:
Marcin Siodelski 2023-06-26 12:09:23 +02:00
parent 659b251df9
commit 132ef88b47
18 changed files with 285 additions and 93 deletions

View File

@ -420,6 +420,13 @@ AllocEngine::findReservation(ClientContext6& ctx) {
// done here.
subnet = subnet->getNextSubnet(ctx.subnet_, classes);
}
// The hosts can be used by the server to return reserved options to
// the DHCP client. Such options must be encapsulated (i.e., they must
// include suboptions).
for (auto host : ctx.hosts_) {
host.second->encapsulateOptions();
}
}
ConstHostPtr
@ -3776,6 +3783,13 @@ AllocEngine::findReservation(ClientContext4& ctx) {
// done here.
subnet = subnet->getNextSubnet(ctx.subnet_, classes);
}
// The hosts can be used by the server to return reserved options to
// the DHCP client. Such options must be encapsulated (i.e., they must
// include suboptions).
for (auto host : ctx.hosts_) {
host.second->encapsulateOptions();
}
}
ConstHostPtr

View File

@ -51,7 +51,8 @@ OptionDescriptor::equals(const OptionDescriptor& other) const {
option_->equals(other.option_));
}
CfgOption::CfgOption() {
CfgOption::CfgOption()
: encapsulated_(false) {
}
bool
@ -263,6 +264,7 @@ CfgOption::encapsulate() {
encapsulateInternal(DHCP4_OPTION_SPACE);
// Append sub-options to the top level "dhcp6" option space.
encapsulateInternal(DHCP6_OPTION_SPACE);
encapsulated_ = true;
}
void

View File

@ -543,6 +543,13 @@ public:
/// options from this option space are appended to top-level options.
void encapsulate();
/// @brief Checks if options have been encapsulated.
///
/// @return true if options have been encapsulated, false otherwise.
bool isEncapsulated() const {
return (encapsulated_);
}
/// @brief Returns all options for the specified option space.
///
/// This method will not return vendor options, i.e. having option space
@ -774,6 +781,9 @@ private:
OptionSpaceContainer<OptionContainer,
OptionDescriptor, Selector>& dest_container) const;
/// @brief A flag indicating if options have been encapsulated.
bool encapsulated_;
/// @brief Type of the container holding options grouped by option space.
typedef OptionSpaceContainer<OptionContainer, OptionDescriptor,
std::string> OptionSpaceCollection;

View File

@ -637,6 +637,16 @@ Host::toElement6() const {
return (map);
}
void
Host::encapsulateOptions() const {
if (!cfg_option4_->isEncapsulated()) {
cfg_option4_->encapsulate();
}
if (!cfg_option6_->isEncapsulated()) {
cfg_option6_->encapsulate();
}
}
std::string
Host::toText() const {
std::ostringstream s;

View File

@ -669,6 +669,12 @@ public:
return (cfg_option6_);
}
/// @brief Encapsulates host-specific options with their suboptions.
///
/// This function must be called before the server returns host-specific
/// DHCP options to the client.
void encapsulateOptions() const;
/// @brief Returns information about the host in the textual format.
std::string toText() const;

View File

@ -994,13 +994,27 @@ private:
def = LibDHCP::getRuntimeOptionDef(space, code_);
}
// Finish with a last resort option definition.
if (!def) {
def = LibDHCP::getLastResortOptionDef(space, code_);
}
OptionPtr option;
// If no definition found, we use generic option type.
if (!def) {
// If no definition found, we use generic option type.
OptionBuffer buf(value_, value_ + value_length_);
option.reset(new Option(universe_, code_, buf.begin(),
buf.end()));
// We have to pay attention if the value is NULL. If it is,
// we must create an empty option instance. We can't rely on
// the value_length_ because it may contain garbage for the
// null values. Thus we check explicitly whether or not the
// NULL flag is set.
if (value_null_ == MLM_FALSE) {
OptionBuffer buf(value_, value_ + value_length_);
option.reset(new Option(universe_, code_, buf.begin(),
buf.end()));
} else {
option.reset(new Option(universe_, code_));
}
} else {
// The option value may be specified in textual or binary format
// in the database. If formatted_value is empty, the binary
@ -1008,9 +1022,12 @@ private:
// variant of the optionFactory function.
if (formatted_value.empty()) {
OptionBuffer buf(value_, value_ + value_length_);
// Again, check if the value is null before submitting the
// buffer to the factory function.
option = def->optionFactory(universe_, code_, buf.begin(),
buf.end());
} else {
value_null_ == MLM_FALSE ? buf.end() :
buf.begin());
} else {
// Spit the value specified in comma separated values
// format.
std::vector<std::string> split_vec;

View File

@ -350,7 +350,8 @@ RelayInfoParser::addAddress(const std::string& name,
void
PoolParser::parse(PoolStoragePtr pools,
ConstElementPtr pool_structure,
const uint16_t address_family) {
const uint16_t address_family,
bool encapsulate_options) {
if (address_family == AF_INET) {
checkKeywords(SimpleParser4::POOL4_PARAMETERS, pool_structure);
@ -484,7 +485,7 @@ PoolParser::parse(PoolStoragePtr pools,
try {
CfgOptionPtr cfg = pool->getCfgOption();
auto option_parser = createOptionDataListParser(address_family);
option_parser->parse(cfg, option_data);
option_parser->parse(cfg, option_data, encapsulate_options);
} catch (const std::exception& ex) {
isc_throw(isc::dhcp::DhcpConfigError, ex.what()
<< " (" << option_data->getPosition() << ")");
@ -537,10 +538,11 @@ Pool4Parser::poolMaker (IOAddress &min, IOAddress &max, int32_t) {
//****************************** Pools4ListParser *************************
void
Pools4ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list) {
Pools4ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list,
bool encapsulate_options) {
BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
auto parser = createPoolConfigParser();
parser->parse(pools, pool, AF_INET);
parser->parse(pools, pool, AF_INET, encapsulate_options);
}
}
@ -555,19 +557,12 @@ Pools4ListParser::createPoolConfigParser() const {
SubnetConfigParser::SubnetConfigParser(uint16_t family, bool check_iface)
: pools_(new PoolStorage()),
address_family_(family),
options_(new CfgOption()),
check_iface_(check_iface) {
relay_info_.reset(new isc::dhcp::Network::RelayInfo());
}
SubnetPtr
SubnetConfigParser::parse(ConstElementPtr subnet) {
ConstElementPtr options_params = subnet->get("option-data");
if (options_params) {
auto opt_parser = createOptionDataListParser();
opt_parser->parse(options_, options_params);
}
SubnetConfigParser::parse(ConstElementPtr subnet, bool encapsulate_options) {
ConstElementPtr relay_params = subnet->get("relay");
if (relay_params) {
@ -584,6 +579,12 @@ SubnetConfigParser::parse(ConstElementPtr subnet) {
"subnet configuration failed: " << ex.what());
}
ConstElementPtr options_params = subnet->get("option-data");
if (options_params) {
auto opt_parser = createOptionDataListParser();
opt_parser->parse(subnet_->getCfgOption(), options_params, encapsulate_options);
}
return (subnet_);
}
@ -686,7 +687,7 @@ Subnet4ConfigParser::Subnet4ConfigParser(bool check_iface)
}
Subnet4Ptr
Subnet4ConfigParser::parse(ConstElementPtr subnet) {
Subnet4ConfigParser::parse(ConstElementPtr subnet, bool encapsulate_options) {
// Check parameters.
checkKeywords(SimpleParser4::SUBNET4_PARAMETERS, subnet);
@ -694,10 +695,10 @@ Subnet4ConfigParser::parse(ConstElementPtr subnet) {
ConstElementPtr pools = subnet->get("pools");
if (pools) {
auto parser = createPoolsListParser();
parser->parse(pools_, pools);
parser->parse(pools_, pools, encapsulate_options);
}
SubnetPtr generic = SubnetConfigParser::parse(subnet);
SubnetPtr generic = SubnetConfigParser::parse(subnet, encapsulate_options);
if (!generic) {
// Sanity check: not supposed to fail.
@ -945,9 +946,6 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
// options but this is no longer the case (they have a different
// and not consecutive priority).
// Copy options to the subnet configuration.
options_->copyTo(*subnet4->getCfgOption());
// Parse t1-percent and t2-percent
parseTeePercents(params, network);
@ -993,12 +991,13 @@ Subnets4ListConfigParser::Subnets4ListConfigParser(bool check_iface)
size_t
Subnets4ListConfigParser::parse(SrvConfigPtr cfg,
ConstElementPtr subnets_list) {
ConstElementPtr subnets_list,
bool encapsulate_options) {
size_t cnt = 0;
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
auto parser = createSubnetConfigParser();
Subnet4Ptr subnet = parser->parse(subnet_json);
Subnet4Ptr subnet = parser->parse(subnet_json, encapsulate_options);
if (subnet) {
// Adding a subnet to the Configuration Manager may fail if the
@ -1018,12 +1017,13 @@ Subnets4ListConfigParser::parse(SrvConfigPtr cfg,
size_t
Subnets4ListConfigParser::parse(Subnet4Collection& subnets,
data::ConstElementPtr subnets_list) {
data::ConstElementPtr subnets_list,
bool encapsulate_options) {
size_t cnt = 0;
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
auto parser = createSubnetConfigParser();
Subnet4Ptr subnet = parser->parse(subnet_json);
Subnet4Ptr subnet = parser->parse(subnet_json, encapsulate_options);
if (subnet) {
try {
auto ret = subnets.insert(subnet);
@ -1067,10 +1067,11 @@ Pool6Parser::poolMaker(IOAddress &min, IOAddress &max, int32_t ptype)
//**************************** Pool6ListParser ***************************
void
Pools6ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list) {
Pools6ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list,
bool encapsulate_options) {
BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
auto parser = createPoolConfigParser();
parser->parse(pools, pool, AF_INET6);
parser->parse(pools, pool, AF_INET6, encapsulate_options);
}
}
@ -1082,11 +1083,12 @@ Pools6ListParser::createPoolConfigParser() const {
//**************************** PdPoolParser ******************************
PdPoolParser::PdPoolParser() : options_(new CfgOption()) {
PdPoolParser::PdPoolParser() {
}
void
PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_,
bool encapsulate_options) {
checkKeywords(SimpleParser6::PD_POOL6_PARAMETERS, pd_pool_);
std::string addr_str = getString(pd_pool_, "prefix");
@ -1105,12 +1107,6 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
excluded_prefix_len = getUint8(pd_pool_, "excluded-prefix-len");
}
ConstElementPtr option_data = pd_pool_->get("option-data");
if (option_data) {
auto opts_parser = createOptionDataListParser();
opts_parser->parse(options_, option_data);
}
ConstElementPtr user_context = pd_pool_->get("user-context");
if (user_context) {
user_context_ = user_context;
@ -1132,8 +1128,6 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
delegated_len,
IOAddress(excluded_prefix_str),
excluded_prefix_len));
// Merge options specified for a pool into pool configuration.
options_->copyTo(*pool_->getCfgOption());
} catch (const std::exception& ex) {
// Some parameters don't exist or are invalid. Since we are not
// aware whether they don't exist or are invalid, let's append
@ -1142,6 +1136,12 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
<< " (" << pd_pool_->getPosition() << ")");
}
ConstElementPtr option_data = pd_pool_->get("option-data");
if (option_data) {
auto opts_parser = createOptionDataListParser();
opts_parser->parse(pool_->getCfgOption(), option_data, encapsulate_options);
}
if (user_context_) {
pool_->setContext(user_context_);
}
@ -1200,7 +1200,7 @@ Subnet6ConfigParser::Subnet6ConfigParser(bool check_iface)
}
Subnet6Ptr
Subnet6ConfigParser::parse(ConstElementPtr subnet) {
Subnet6ConfigParser::parse(ConstElementPtr subnet, bool encapsulate_options) {
// Check parameters.
checkKeywords(SimpleParser6::SUBNET6_PARAMETERS, subnet);
@ -1208,7 +1208,7 @@ Subnet6ConfigParser::parse(ConstElementPtr subnet) {
ConstElementPtr pools = subnet->get("pools");
if (pools) {
auto parser = createPoolsListParser();
parser->parse(pools_, pools);
parser->parse(pools_, pools, encapsulate_options);
}
ConstElementPtr pd_pools = subnet->get("pd-pools");
if (pd_pools) {
@ -1216,7 +1216,7 @@ Subnet6ConfigParser::parse(ConstElementPtr subnet) {
parser->parse(pools_, pd_pools);
}
SubnetPtr generic = SubnetConfigParser::parse(subnet);
SubnetPtr generic = SubnetConfigParser::parse(subnet, encapsulate_options);
if (!generic) {
// Sanity check: not supposed to fail.
@ -1420,9 +1420,6 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
/// client-class processing is now generic and handled in the common
/// code (see isc::data::SubnetConfigParser::createSubnet)
// Copy options to the subnet configuration.
options_->copyTo(*subnet6->getCfgOption());
// Parse t1-percent and t2-percent
parseTeePercents(params, network);
@ -1466,12 +1463,13 @@ Subnets6ListConfigParser::Subnets6ListConfigParser(bool check_iface)
size_t
Subnets6ListConfigParser::parse(SrvConfigPtr cfg,
ConstElementPtr subnets_list) {
ConstElementPtr subnets_list,
bool encapsulate_options) {
size_t cnt = 0;
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
auto parser = createSubnetConfigParser();
Subnet6Ptr subnet = parser->parse(subnet_json);
Subnet6Ptr subnet = parser->parse(subnet_json, encapsulate_options);
// Adding a subnet to the Configuration Manager may fail if the
// subnet id is invalid (duplicate). Thus, we catch exceptions
@ -1489,12 +1487,13 @@ Subnets6ListConfigParser::parse(SrvConfigPtr cfg,
size_t
Subnets6ListConfigParser::parse(Subnet6Collection& subnets,
ConstElementPtr subnets_list) {
ConstElementPtr subnets_list,
bool encapsulate_options) {
size_t cnt = 0;
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
auto parser = createSubnetConfigParser();
Subnet6Ptr subnet = parser->parse(subnet_json);
Subnet6Ptr subnet = parser->parse(subnet_json, encapsulate_options);
if (subnet) {
try {
auto ret = subnets.insert(subnet);

View File

@ -302,10 +302,13 @@ public:
/// @param pools is the storage in which to store the parsed pool
/// @param pool_structure a single entry on a list of pools
/// @param address_family AF_INET (for DHCPv4) or AF_INET6 (for DHCPv6).
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @throw isc::dhcp::DhcpConfigError when pool parsing fails
virtual void parse(PoolStoragePtr pools,
isc::data::ConstElementPtr pool_structure,
const uint16_t address_family);
const uint16_t address_family,
bool encapsulate_options = true);
protected:
/// @brief Creates a Pool object given a IPv4 prefix and the prefix length.
@ -389,9 +392,12 @@ public:
///
/// @param pools is the storage in which to store the parsed pools.
/// @param pools_list a list of pool structures
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @throw isc::dhcp::DhcpConfigError when pool parsing fails
virtual void parse(PoolStoragePtr pools,
isc::data::ConstElementPtr pools_list) = 0;
isc::data::ConstElementPtr pools_list,
bool encapsulate_options) = 0;
protected:
@ -415,8 +421,11 @@ public:
///
/// @param pools storage container in which to store the parsed pool.
/// @param pools_list a list of pool structures
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @throw isc::dhcp::DhcpConfigError when pool parsing fails
void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list);
void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list,
bool encapsulate_options = true);
protected:
@ -520,10 +529,13 @@ protected:
/// Subnet6ConfigParser) classes.
///
/// @param subnet pointer to the content of subnet definition
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @return a pointer to newly created subnet
///
/// @throw isc::DhcpConfigError if subnet configuration parsing failed.
SubnetPtr parse(isc::data::ConstElementPtr subnet);
SubnetPtr parse(isc::data::ConstElementPtr subnet,
bool encapsulate_options);
/// @brief Instantiates the subnet based on a given IP prefix and prefix
/// length.
@ -574,9 +586,6 @@ protected:
/// Pointer to relay information
isc::dhcp::Network::RelayInfoPtr relay_info_;
/// Pointer to the options configuration.
CfgOptionPtr options_;
/// Check if the specified interface exists in the system.
bool check_iface_;
};
@ -601,8 +610,11 @@ public:
/// Configuration Manager.
///
/// @param subnet A new subnet being configured.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @return a pointer to created Subnet4 object
Subnet4Ptr parse(data::ConstElementPtr subnet);
Subnet4Ptr parse(data::ConstElementPtr subnet,
bool encapsulate_options = true);
protected:
@ -659,16 +671,22 @@ public:
///
/// @param cfg Pointer to server configuration.
/// @param subnets_list pointer to a list of IPv4 subnets
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @return number of subnets created
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list);
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list,
bool encapsulate_options = true);
/// @brief Parses contents of the subnet4 list.
///
/// @param [out] subnets Container where parsed subnets will be stored.
/// @param subnets_list pointer to a list of IPv4 subnets
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @return Number of subnets created.
size_t parse(Subnet4Collection& subnets,
data::ConstElementPtr subnets_list);
data::ConstElementPtr subnets_list,
bool encapsulate_options = true);
protected:
@ -727,8 +745,11 @@ public:
///
/// @param pools storage container in which to store the parsed pool.
/// @param pools_list a list of pool structures
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @throw isc::dhcp::DhcpConfigError when pool parsing fails
void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list);
void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list,
bool encapsulate_options = true);
protected:
@ -778,9 +799,12 @@ public:
/// @param pools storage container in which to store the parsed pool.
/// @param pd_pool_ pointer to an element that holds configuration entries
/// that define a prefix delegation pool.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
///
/// @throw DhcpConfigError if configuration parsing fails.
void parse(PoolStoragePtr pools, data::ConstElementPtr pd_pool_);
void parse(PoolStoragePtr pools, data::ConstElementPtr pd_pool_,
bool encapsulate_options = true);
protected:
@ -797,9 +821,6 @@ protected:
/// Pointer to the created pool object.
isc::dhcp::Pool6Ptr pool_;
/// A storage for pool specific option values.
CfgOptionPtr options_;
/// @brief User context (optional, may be null)
///
/// User context is arbitrary user data, to be used by hooks.
@ -869,8 +890,11 @@ public:
/// Configuration Manager.
///
/// @param subnet A new subnet being configured.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @return a pointer to created Subnet6 object
Subnet6Ptr parse(data::ConstElementPtr subnet);
Subnet6Ptr parse(data::ConstElementPtr subnet,
bool encapsulate_options = true);
protected:
/// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
@ -947,8 +971,11 @@ public:
///
/// @param cfg configuration (parsed subnets will be stored here)
/// @param subnets_list pointer to a list of IPv6 subnets
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
/// @throw DhcpConfigError if CfgMgr rejects the subnet (e.g. subnet-id is a duplicate)
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list);
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list,
bool encapsulate_options = true);
/// @brief Parses contents of the subnet6 list.
///
@ -956,7 +983,8 @@ public:
/// @param subnets_list pointer to a list of IPv6 subnets
/// @return Number of subnets created.
size_t parse(Subnet6Collection& subnets,
data::ConstElementPtr subnets_list);
data::ConstElementPtr subnets_list,
bool encapsulate_options = true);
protected:

View File

@ -101,13 +101,15 @@ namespace dhcp {
HostPtr
HostReservationParser::parse(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data) {
return (parseInternal(subnet_id, reservation_data));
isc::data::ConstElementPtr reservation_data,
bool encapsulate_options) {
return (parseInternal(subnet_id, reservation_data, encapsulate_options));
}
HostPtr
HostReservationParser::parseInternal(const SubnetID&,
isc::data::ConstElementPtr reservation_data) {
isc::data::ConstElementPtr reservation_data,
bool) {
std::string identifier;
std::string identifier_name;
std::string hostname;
@ -187,8 +189,10 @@ HostReservationParser::isSupportedParameter(const std::string& param_name) const
HostPtr
HostReservationParser4::parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data) {
HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data);
isc::data::ConstElementPtr reservation_data,
bool encapsulate_options) {
HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data,
encapsulate_options);
host->setIPv4SubnetID(subnet_id);
@ -203,7 +207,7 @@ HostReservationParser4::parseInternal(const SubnetID& subnet_id,
// parses the Element structure immediately, there's no need
// to go through build/commit phases.
OptionDataListParser parser(AF_INET);
parser.parse(cfg_option, element.second);
parser.parse(cfg_option, element.second, encapsulate_options);
// Everything else should be surrounded with try-catch to append
// position.
@ -246,8 +250,10 @@ HostReservationParser4::getSupportedParameters(const bool identifiers_only) cons
HostPtr
HostReservationParser6::parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data) {
HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data);
isc::data::ConstElementPtr reservation_data,
bool encapsulate_options) {
HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data,
encapsulate_options);
host->setIPv6SubnetID(subnet_id);
@ -263,7 +269,7 @@ HostReservationParser6::parseInternal(const SubnetID& subnet_id,
// parses the Element structure immediately, there's no need
// to go through build/commit phases.
OptionDataListParser parser(AF_INET6);
parser.parse(cfg_option, element.second);
parser.parse(cfg_option, element.second, encapsulate_options);
} else if (element.first == "ip-addresses" || element.first == "prefixes") {
BOOST_FOREACH(ConstElementPtr prefix_element,

View File

@ -27,12 +27,15 @@ public:
/// connected to.
/// @param reservation_data Data element holding map with a host
/// reservation configuration.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
///
/// @return Pointer to the object representing parsed host.
/// @throw DhcpConfigError If the configuration is invalid.
virtual HostPtr
parse(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data) final;
isc::data::ConstElementPtr reservation_data,
bool encapsulate_options = true) final;
protected:
@ -45,11 +48,14 @@ protected:
/// connected to.
/// @param reservation_data Data element holding map with a host
/// reservation configuration.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
///
/// @return Pointer to the object representing parsed host.
/// @throw DhcpConfigError If the configuration is invalid.
virtual HostPtr parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
isc::data::ConstElementPtr reservation_data,
bool encapsulate_options);
/// @brief Checks if the specified parameter is a host identifier.
///
@ -87,11 +93,14 @@ protected:
/// connected to.
/// @param reservation_data Data element holding map with a host
/// reservation configuration.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
///
/// @return Pointer to the object representing parsed host.
/// @throw DhcpConfigError If the configuration is invalid.
virtual HostPtr parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
isc::data::ConstElementPtr reservation_data,
bool encapsulate_options);
/// @brief Returns set of the supported parameters for DHCPv4.
///
@ -114,11 +123,14 @@ protected:
/// connected to.
/// @param reservation_data Data element holding map with a host
/// reservation configuration.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
///
/// @return Pointer to the object representing parsed host.
/// @throw DhcpConfigError If the configuration is invalid.
virtual HostPtr parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
isc::data::ConstElementPtr reservation_data,
bool encapsulate_options);
/// @brief Returns set of the supported parameters for DHCPv6.
///

View File

@ -455,14 +455,17 @@ OptionDataListParser::OptionDataListParser(//const std::string&,
void OptionDataListParser::parse(const CfgOptionPtr& cfg,
isc::data::ConstElementPtr option_data_list) {
isc::data::ConstElementPtr option_data_list,
bool encapsulate) {
auto option_parser = createOptionDataParser();
BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
std::pair<OptionDescriptor, std::string> option =
option_parser->parse(data);
// Use the option description to keep the formatted value
cfg->add(option.first, option.second);
cfg->encapsulate();
if (encapsulate) {
cfg->encapsulate();
}
}
}

View File

@ -209,8 +209,12 @@ public:
///
/// @param cfg created options will be stored here
/// @param option_data_list configuration that describes the options
/// @param encapsulate a boolean value indicating whether or not the
/// parser should encapsulate options with suboptions. The default
/// value is true (encapsulate).
void parse(const CfgOptionPtr& cfg,
isc::data::ConstElementPtr option_data_list);
isc::data::ConstElementPtr option_data_list,
bool encapsulate = true);
protected:
/// @brief Returns an instance of the @c OptionDataListParser to

View File

@ -31,7 +31,8 @@ SharedNetwork4Parser::SharedNetwork4Parser(bool check_iface)
}
SharedNetwork4Ptr
SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data) {
SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data,
bool encapsulate_options) {
SharedNetwork4Ptr shared_network;
try {
@ -75,7 +76,7 @@ SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data) {
// Create parser instance for option-data.
CfgOptionPtr cfg_option = shared_network->getCfgOption();
auto parser = createOptionDataListParser();
parser->parse(cfg_option, json);
parser->parse(cfg_option, json, encapsulate_options);
}
if (shared_network_data->contains("subnet4")) {
@ -240,7 +241,8 @@ SharedNetwork6Parser::SharedNetwork6Parser(bool check_iface)
}
SharedNetwork6Ptr
SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data) {
SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data,
bool encapsulate_options) {
SharedNetwork6Ptr shared_network;
std::string name;
try {
@ -324,7 +326,7 @@ SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data) {
// Create parser instance for option-data.
CfgOptionPtr cfg_option = shared_network->getCfgOption();
auto parser = createOptionDataListParser();
parser->parse(cfg_option, json);
parser->parse(cfg_option, json, encapsulate_options);
}
if (shared_network_data->contains("client-class")) {

View File

@ -37,11 +37,14 @@ public:
///
/// @param shared_network_data Data element holding shared network
/// configuration to be parsed.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
///
/// @return Pointer to an object representing shared network.
/// @throw DhcpConfigError when shared network configuration is invalid.
SharedNetwork4Ptr
parse(const data::ConstElementPtr& shared_network_data);
parse(const data::ConstElementPtr& shared_network_data,
bool encapsulate_options = true);
protected:
@ -84,11 +87,14 @@ public:
///
/// @param shared_network_data Data element holding shared network
/// configuration to be parsed.
/// @param encapsulate_options a boolean parameter indicating if the
/// parsed options should be encapsulated with suboptions.
///
/// @return Pointer to an object representing shared network.
/// @throw DhcpConfigError when shared network configuration is invalid.
SharedNetwork6Ptr
parse(const data::ConstElementPtr& shared_network_data);
parse(const data::ConstElementPtr& shared_network_data,
bool encapsulate_options = true);
protected:

View File

@ -651,6 +651,11 @@ private:
def = LibDHCP::getRuntimeOptionDef(space, code);
}
// Finish with a last resort option definition.
if (!def) {
def = LibDHCP::getLastResortOptionDef(space, code);
}
OptionPtr option;
if (!def) {

View File

@ -694,11 +694,15 @@ TEST_F(CfgOptionTest, encapsulate) {
generateEncapsulatedOptions(cfg);
EXPECT_FALSE(cfg.isEncapsulated());
// Append options from "foo" and "bar" space as sub-options and options
// from "foo-subs" and "bar-subs" as sub-options of "foo" and "bar"
// options.
ASSERT_NO_THROW(cfg.encapsulate());
EXPECT_TRUE(cfg.isEncapsulated());
// Verify that we have 40 top-level options.
OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
ASSERT_EQ(40, options->size());

View File

@ -951,6 +951,29 @@ TEST_F(HostTest, addOptions4) {
EXPECT_TRUE(options->empty());
}
// This test checks that host-specific DHCPv4 options can be encapsulated.
TEST_F(HostTest, encapsulateOptions4) {
Host host("01:02:03:04:05:06", "hw-address", SubnetID(1), SubnetID(2),
IOAddress("192.0.2.3"));
OptionPtr option43(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS));
option43->setEncapsulatedSpace(VENDOR_ENCAPSULATED_OPTION_SPACE);
ASSERT_NO_THROW(host.getCfgOption4()->add(option43, false, false, DHCP4_OPTION_SPACE));
OptionPtr option1(new Option(Option::V4, 1));
ASSERT_NO_THROW(host.getCfgOption4()->add(option1, false, false,
VENDOR_ENCAPSULATED_OPTION_SPACE));
ASSERT_NO_THROW(host.encapsulateOptions());
auto returned_option43 = host.getCfgOption4()->get(DHCP4_OPTION_SPACE,
DHO_VENDOR_ENCAPSULATED_OPTIONS);
ASSERT_TRUE(returned_option43.option_);
auto returned_option1 = returned_option43.option_->getOption(1);
ASSERT_TRUE(returned_option1);
}
// This test checks that it is possible to add DHCPv6 options for a host.
TEST_F(HostTest, addOptions6) {
Host host("01:02:03:04:05:06", "hw-address", SubnetID(1), SubnetID(2),
@ -1016,6 +1039,29 @@ TEST_F(HostTest, addOptions6) {
EXPECT_TRUE(options->empty());
}
// This test checks that it is possible to add DHCPv6 options for a host.
TEST_F(HostTest, encapsulateOptions6) {
Host host("01:02:03:04:05:06", "hw-address", SubnetID(1), SubnetID(2),
IOAddress("192.0.2.3"));
OptionPtr option94(new Option(Option::V6, D6O_S46_CONT_MAPE));
option94->setEncapsulatedSpace(MAPE_V6_OPTION_SPACE);
ASSERT_NO_THROW(host.getCfgOption6()->add(option94, false, false, DHCP6_OPTION_SPACE));
OptionPtr option1(new Option(Option::V6, 1));
ASSERT_NO_THROW(host.getCfgOption6()->add(option1, false, false,
MAPE_V6_OPTION_SPACE));
ASSERT_NO_THROW(host.encapsulateOptions());
auto returned_option94 = host.getCfgOption6()->get(DHCP6_OPTION_SPACE,
D6O_S46_CONT_MAPE);
ASSERT_TRUE(returned_option94.option_);
auto returned_option1 = returned_option94.option_->getOption(1);
ASSERT_TRUE(returned_option1);
}
// This test verifies that it is possible to retrieve a textual
// representation of the host identifier.
TEST_F(HostTest, getIdentifierAsText) {

View File

@ -128,7 +128,7 @@ GenericHostDataSourceTest::addTestOptions(const HostPtr& host,
DHCP4_OPTION_SPACE);
opts->add(createOption<OptionUint32>(Option::V4, 1, false, false,
formatted, 312131),
"vendor-encapsulated-options");
"vendor-encapsulated-options-space");
opts->add(createAddressOption<Option4AddrLst>(254, false, false,
formatted, "192.0.2.3"),
DHCP4_OPTION_SPACE);
@ -137,11 +137,17 @@ GenericHostDataSourceTest::addTestOptions(const HostPtr& host,
formatted, "10.0.0.5",
"10.0.0.3", "10.0.3.4"),
"isc");
auto def = LibDHCP::getLastResortOptionDef(DHCP4_OPTION_SPACE,
DHO_VENDOR_ENCAPSULATED_OPTIONS);
opts->add(OptionDescriptor(def->optionFactory(Option::V4,
DHO_VENDOR_ENCAPSULATED_OPTIONS,
OptionBuffer()),
true, false), DHCP4_OPTION_SPACE);
// Add definitions for DHCPv4 non-standard options.
defs.addItem(OptionDefinitionPtr(new OptionDefinition(
"vendor-encapsulated-1", 1,
"vendor-encapsulated-options", "uint32")));
"vendor-encapsulated-options-space", "uint32")));
defs.addItem(OptionDefinitionPtr(new OptionDefinition(
"option-254", 254, DHCP4_OPTION_SPACE,
"ipv4-address", true)));
@ -2047,6 +2053,18 @@ GenericHostDataSourceTest::testOptionsReservations4(const bool formatted,
ASSERT_EQ(1, hosts_by_subnet.size());
ASSERT_NO_FATAL_FAILURE(HostDataSourceUtils::compareHosts(host, *hosts_by_subnet.begin()));
auto returned_host = *hosts_by_subnet.begin();
ASSERT_NO_THROW(returned_host->encapsulateOptions());
auto cfg_option = returned_host->getCfgOption4();
auto option43 = cfg_option->get(DHCP4_OPTION_SPACE, DHO_VENDOR_ENCAPSULATED_OPTIONS);
ASSERT_TRUE(option43.option_);
EXPECT_TRUE(cfg_option->get("vendor-encapsulated-options-space", 1).option_);
auto option43_1 = option43.option_->getOption(1);
EXPECT_TRUE(option43_1);
// getAll4(address)
ConstHostCollection hosts_by_addr =
hdsptr_->getAll4(host->getIPv4Reservation());