mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-03 15:35:17 +00:00
[5315] Moved Subnet parsers from DHCP binaries to libdhcpsrv.
This commit is contained in:
committed by
Tomek Mrugalski
parent
433313ef4c
commit
0a61518acf
@@ -21,6 +21,7 @@
|
|||||||
#include <dhcpsrv/parsers/host_reservation_parser.h>
|
#include <dhcpsrv/parsers/host_reservation_parser.h>
|
||||||
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
|
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
|
||||||
#include <dhcpsrv/parsers/ifaces_config_parser.h>
|
#include <dhcpsrv/parsers/ifaces_config_parser.h>
|
||||||
|
#include <dhcpsrv/parsers/option_data_parser.h>
|
||||||
#include <dhcpsrv/timer_mgr.h>
|
#include <dhcpsrv/timer_mgr.h>
|
||||||
#include <hooks/hooks_parser.h>
|
#include <hooks/hooks_parser.h>
|
||||||
#include <config/command_mgr.h>
|
#include <config/command_mgr.h>
|
||||||
@@ -46,273 +47,6 @@ using namespace isc::hooks;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// @brief Parser for IPv4 pool definitions.
|
|
||||||
///
|
|
||||||
/// This is the IPv4 derivation of the PoolParser class and handles pool
|
|
||||||
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
|
|
||||||
/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
|
|
||||||
/// PoolStorage container.
|
|
||||||
///
|
|
||||||
/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
|
|
||||||
class Pool4Parser : public PoolParser {
|
|
||||||
protected:
|
|
||||||
/// @brief Creates a Pool4 object given a IPv4 prefix and the prefix length.
|
|
||||||
///
|
|
||||||
/// @param addr is the IPv4 prefix of the pool.
|
|
||||||
/// @param len is the prefix length.
|
|
||||||
/// @param ignored dummy parameter to provide symmetry between the
|
|
||||||
/// PoolParser derivations. The V6 derivation requires a third value.
|
|
||||||
/// @return returns a PoolPtr to the new Pool4 object.
|
|
||||||
PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t) {
|
|
||||||
return (PoolPtr(new Pool4(addr, len)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Creates a Pool4 object given starting and ending IPv4 addresses.
|
|
||||||
///
|
|
||||||
/// @param min is the first IPv4 address in the pool.
|
|
||||||
/// @param max is the last IPv4 address in the pool.
|
|
||||||
/// @param ignored dummy parameter to provide symmetry between the
|
|
||||||
/// PoolParser derivations. The V6 derivation requires a third value.
|
|
||||||
/// @return returns a PoolPtr to the new Pool4 object.
|
|
||||||
PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t) {
|
|
||||||
return (PoolPtr(new Pool4(min, max)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Specialization of the pool list parser for DHCPv4
|
|
||||||
class Pools4ListParser : PoolsListParser {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief parses the actual structure
|
|
||||||
///
|
|
||||||
/// This method parses the actual list of pools.
|
|
||||||
///
|
|
||||||
/// @param pools storage container in which to store the parsed pool.
|
|
||||||
/// @param pools_list a list of pool structures
|
|
||||||
/// @throw isc::dhcp::DhcpConfigError when pool parsing fails
|
|
||||||
void parse(PoolStoragePtr pools,
|
|
||||||
isc::data::ConstElementPtr pools_list) {
|
|
||||||
BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
|
|
||||||
Pool4Parser parser;
|
|
||||||
parser.parse(pools, pool, AF_INET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @anchor Subnet4ConfigParser
|
|
||||||
/// @brief This class parses a single IPv4 subnet.
|
|
||||||
///
|
|
||||||
/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
|
|
||||||
/// the whole subnet definition. It creates parsersfor received configuration
|
|
||||||
/// parameters as needed.
|
|
||||||
class Subnet4ConfigParser : public SubnetConfigParser {
|
|
||||||
public:
|
|
||||||
/// @brief Constructor
|
|
||||||
///
|
|
||||||
/// stores global scope parameters, options, option definitions.
|
|
||||||
Subnet4ConfigParser()
|
|
||||||
:SubnetConfigParser(AF_INET) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Parses a single IPv4 subnet configuration and adds to the
|
|
||||||
/// Configuration Manager.
|
|
||||||
///
|
|
||||||
/// @param subnet A new subnet being configured.
|
|
||||||
/// @return a pointer to created Subnet4 object
|
|
||||||
Subnet4Ptr parse(ConstElementPtr subnet) {
|
|
||||||
/// Parse Pools first.
|
|
||||||
ConstElementPtr pools = subnet->get("pools");
|
|
||||||
if (pools) {
|
|
||||||
Pools4ListParser parser;
|
|
||||||
parser.parse(pools_, pools);
|
|
||||||
}
|
|
||||||
|
|
||||||
SubnetPtr generic = SubnetConfigParser::parse(subnet);
|
|
||||||
|
|
||||||
if (!generic) {
|
|
||||||
isc_throw(DhcpConfigError,
|
|
||||||
"Failed to create an IPv4 subnet (" <<
|
|
||||||
subnet->getPosition() << ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
Subnet4Ptr sn4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
|
|
||||||
if (!sn4ptr) {
|
|
||||||
// If we hit this, it is a programming error.
|
|
||||||
isc_throw(Unexpected,
|
|
||||||
"Invalid Subnet4 cast in Subnet4ConfigParser::parse");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set relay information if it was parsed
|
|
||||||
if (relay_info_) {
|
|
||||||
sn4ptr->setRelayInfo(*relay_info_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse Host Reservations for this subnet if any.
|
|
||||||
ConstElementPtr reservations = subnet->get("reservations");
|
|
||||||
if (reservations) {
|
|
||||||
HostCollection hosts;
|
|
||||||
HostReservationsListParser<HostReservationParser4> parser;
|
|
||||||
parser.parse(subnet_->getID(), reservations, hosts);
|
|
||||||
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
|
|
||||||
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (sn4ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
|
|
||||||
/// and prefix length.
|
|
||||||
///
|
|
||||||
/// @param addr is IPv4 address of the subnet.
|
|
||||||
/// @param len is the prefix length
|
|
||||||
void initSubnet(isc::data::ConstElementPtr params,
|
|
||||||
isc::asiolink::IOAddress addr, uint8_t len) {
|
|
||||||
// The renew-timer and rebind-timer are optional. If not set, the
|
|
||||||
// option 58 and 59 will not be sent to a client. In this case the
|
|
||||||
// client will use default values based on the valid-lifetime.
|
|
||||||
Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
|
|
||||||
Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
|
|
||||||
|
|
||||||
// The valid-lifetime is mandatory. It may be specified for a
|
|
||||||
// particular subnet. If not, the global value should be present.
|
|
||||||
// If there is no global value, exception is thrown.
|
|
||||||
Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
|
|
||||||
|
|
||||||
// Subnet ID is optional. If it is not supplied the value of 0 is used,
|
|
||||||
// which means autogenerate. The value was inserted earlier by calling
|
|
||||||
// SimpleParser4::setAllDefaults.
|
|
||||||
SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
|
|
||||||
|
|
||||||
stringstream s;
|
|
||||||
s << addr << "/" << static_cast<int>(len) << " with params: ";
|
|
||||||
// t1 and t2 are optional may be not specified.
|
|
||||||
if (!t1.unspecified()) {
|
|
||||||
s << "t1=" << t1 << ", ";
|
|
||||||
}
|
|
||||||
if (!t2.unspecified()) {
|
|
||||||
s << "t2=" << t2 << ", ";
|
|
||||||
}
|
|
||||||
s <<"valid-lifetime=" << valid;
|
|
||||||
|
|
||||||
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(s.str());
|
|
||||||
|
|
||||||
Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
|
|
||||||
subnet_ = subnet4;
|
|
||||||
|
|
||||||
// Set the match-client-id value for the subnet. It is always present.
|
|
||||||
// If not explicitly specified, the default value was filled in when
|
|
||||||
// SimpleParser4::setAllDefaults was called.
|
|
||||||
bool match_client_id = getBoolean(params, "match-client-id");
|
|
||||||
subnet4->setMatchClientId(match_client_id);
|
|
||||||
|
|
||||||
// Set next-server. The default value is 0.0.0.0. Nevertheless, the
|
|
||||||
// user could have messed that up by specifying incorrect value.
|
|
||||||
// To avoid using 0.0.0.0, user can specify "".
|
|
||||||
string next_server;
|
|
||||||
try {
|
|
||||||
next_server = getString(params, "next-server");
|
|
||||||
if (!next_server.empty()) {
|
|
||||||
subnet4->setSiaddr(IOAddress(next_server));
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
ConstElementPtr next = params->get("next-server");
|
|
||||||
string pos;
|
|
||||||
if (next)
|
|
||||||
pos = next->getPosition().str();
|
|
||||||
else
|
|
||||||
pos = params->getPosition().str();
|
|
||||||
isc_throw(DhcpConfigError, "invalid parameter next-server : "
|
|
||||||
<< next_server << "(" << pos << ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4o6 specific parameter: 4o6-interface. If not explicitly specified,
|
|
||||||
// it will have the default value of "".
|
|
||||||
string iface4o6 = getString(params, "4o6-interface");
|
|
||||||
if (!iface4o6.empty()) {
|
|
||||||
subnet4->get4o6().setIface4o6(iface4o6);
|
|
||||||
subnet4->get4o6().enabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4o6 specific parameter: 4o6-subnet. If not explicitly specified, it
|
|
||||||
// will have the default value of "".
|
|
||||||
string subnet4o6 = getString(params, "4o6-subnet");
|
|
||||||
if (!subnet4o6.empty()) {
|
|
||||||
size_t slash = subnet4o6.find("/");
|
|
||||||
if (slash == std::string::npos) {
|
|
||||||
isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
|
|
||||||
<< subnet4o6 << ", expected format: prefix6/length");
|
|
||||||
}
|
|
||||||
string prefix = subnet4o6.substr(0, slash);
|
|
||||||
string lenstr = subnet4o6.substr(slash + 1);
|
|
||||||
|
|
||||||
uint8_t len = 128;
|
|
||||||
try {
|
|
||||||
len = boost::lexical_cast<unsigned int>(lenstr.c_str());
|
|
||||||
} catch (const boost::bad_lexical_cast &) {
|
|
||||||
isc_throw(DhcpConfigError, "Invalid prefix length specified in "
|
|
||||||
"4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
|
|
||||||
}
|
|
||||||
subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
|
|
||||||
subnet4->get4o6().enabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try 4o6 specific parameter: 4o6-interface-id
|
|
||||||
std::string ifaceid = getString(params, "4o6-interface-id");
|
|
||||||
if (!ifaceid.empty()) {
|
|
||||||
OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
|
|
||||||
OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
|
|
||||||
subnet4->get4o6().setInterfaceId(opt);
|
|
||||||
subnet4->get4o6().enabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// client-class processing is now generic and handled in the common
|
|
||||||
/// code (see @ref isc::data::SubnetConfigParser::createSubnet)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief this class parses list of DHCP4 subnets
|
|
||||||
///
|
|
||||||
/// This is a wrapper parser that handles the whole list of Subnet4
|
|
||||||
/// definitions. It iterates over all entries and creates Subnet4ConfigParser
|
|
||||||
/// for each entry.
|
|
||||||
class Subnets4ListConfigParser : public isc::data::SimpleParser {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief parses contents of the list
|
|
||||||
///
|
|
||||||
/// Iterates over all entries on the list, parses its content
|
|
||||||
/// (by instantiating Subnet6ConfigParser) and adds to specified
|
|
||||||
/// configuration.
|
|
||||||
///
|
|
||||||
/// @param subnets_list pointer to a list of IPv4 subnets
|
|
||||||
/// @return number of subnets created
|
|
||||||
size_t parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
|
|
||||||
size_t cnt = 0;
|
|
||||||
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
|
|
||||||
|
|
||||||
Subnet4ConfigParser parser;
|
|
||||||
Subnet4Ptr subnet = parser.parse(subnet_json);
|
|
||||||
if (subnet) {
|
|
||||||
|
|
||||||
// Adding a subnet to the Configuration Manager may fail if the
|
|
||||||
// subnet id is invalid (duplicate). Thus, we catch exceptions
|
|
||||||
// here to append a position in the configuration string.
|
|
||||||
try {
|
|
||||||
cfg->getCfgSubnets4()->add(subnet);
|
|
||||||
cnt++;
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
isc_throw(DhcpConfigError, ex.what() << " ("
|
|
||||||
<< subnet_json->getPosition() << ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (cnt);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Parser that takes care of global DHCPv4 parameters.
|
/// @brief Parser that takes care of global DHCPv4 parameters.
|
||||||
///
|
///
|
||||||
/// See @ref parse method for a list of supported parameters.
|
/// See @ref parse method for a list of supported parameters.
|
||||||
|
@@ -97,15 +97,6 @@ If this is an initial configuration (during server's startup) the server
|
|||||||
will fail to start. If this is a dynamic reconfiguration attempt the
|
will fail to start. If this is a dynamic reconfiguration attempt the
|
||||||
server will continue to use an old configuration.
|
server will continue to use an old configuration.
|
||||||
|
|
||||||
% DHCP6_CONFIG_NEW_SUBNET a new subnet has been added to configuration: %1
|
|
||||||
This is an informational message reporting that the configuration has
|
|
||||||
been extended to include the specified subnet.
|
|
||||||
|
|
||||||
% DHCP6_CONFIG_OPTION_DUPLICATE multiple options with the code: %1 added to the subnet: %2
|
|
||||||
This warning message is issued on an attempt to configure multiple options with the
|
|
||||||
same option code for the particular subnet. Adding multiple options is uncommon
|
|
||||||
for DHCPv6, but it is not prohibited.
|
|
||||||
|
|
||||||
% DHCP6_CONFIG_RECEIVED received configuration: %1
|
% DHCP6_CONFIG_RECEIVED received configuration: %1
|
||||||
A debug message listing the configuration received by the DHCPv6 server.
|
A debug message listing the configuration received by the DHCPv6 server.
|
||||||
The source of that configuration depends on used configuration backend.
|
The source of that configuration depends on used configuration backend.
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include <dhcpsrv/parsers/host_reservation_parser.h>
|
#include <dhcpsrv/parsers/host_reservation_parser.h>
|
||||||
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
|
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
|
||||||
#include <dhcpsrv/parsers/ifaces_config_parser.h>
|
#include <dhcpsrv/parsers/ifaces_config_parser.h>
|
||||||
|
#include <dhcpsrv/parsers/option_data_parser.h>
|
||||||
#include <hooks/hooks_parser.h>
|
#include <hooks/hooks_parser.h>
|
||||||
#include <log/logger_support.h>
|
#include <log/logger_support.h>
|
||||||
#include <util/encode/hex.h>
|
#include <util/encode/hex.h>
|
||||||
@@ -63,391 +64,6 @@ typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
|
|||||||
typedef boost::shared_ptr<StringParser> StringParserPtr;
|
typedef boost::shared_ptr<StringParser> StringParserPtr;
|
||||||
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
|
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
|
||||||
|
|
||||||
/// @brief Parser for IPv6 pool definitions.
|
|
||||||
///
|
|
||||||
/// This is the IPv6 derivation of the PoolParser class and handles pool
|
|
||||||
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
|
|
||||||
/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
|
|
||||||
/// PoolStorage container.
|
|
||||||
///
|
|
||||||
/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
|
|
||||||
class Pool6Parser : public PoolParser {
|
|
||||||
protected:
|
|
||||||
/// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
|
|
||||||
///
|
|
||||||
/// @param addr is the IPv6 prefix of the pool.
|
|
||||||
/// @param len is the prefix length.
|
|
||||||
/// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
|
|
||||||
/// passed in as an int32_t and cast to PoolType to accommodate a
|
|
||||||
/// polymorphic interface.
|
|
||||||
/// @return returns a PoolPtr to the new Pool4 object.
|
|
||||||
PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
|
|
||||||
{
|
|
||||||
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
|
|
||||||
(ptype), addr, len)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
|
|
||||||
///
|
|
||||||
/// @param min is the first IPv6 address in the pool.
|
|
||||||
/// @param max is the last IPv6 address in the pool.
|
|
||||||
/// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
|
|
||||||
/// passed in as an int32_t and cast to PoolType to accommodate a
|
|
||||||
/// polymorphic interface.
|
|
||||||
/// @return returns a PoolPtr to the new Pool4 object.
|
|
||||||
PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
|
|
||||||
{
|
|
||||||
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
|
|
||||||
(ptype), min, max)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Specialization of the pool list parser for DHCPv6
|
|
||||||
class Pools6ListParser : PoolsListParser {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief parses the actual structure
|
|
||||||
///
|
|
||||||
/// This method parses the actual list of pools.
|
|
||||||
///
|
|
||||||
/// @param pools storage container in which to store the parsed pool.
|
|
||||||
/// @param pools_list a list of pool structures
|
|
||||||
/// @throw isc::dhcp::DhcpConfigError when pool parsing fails
|
|
||||||
void parse(PoolStoragePtr pools,
|
|
||||||
isc::data::ConstElementPtr pools_list) {
|
|
||||||
BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
|
|
||||||
Pool6Parser parser;
|
|
||||||
parser.parse(pools, pool, AF_INET6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Parser for IPv6 prefix delegation definitions.
|
|
||||||
///
|
|
||||||
/// This class handles prefix delegation pool definitions for IPv6 subnets
|
|
||||||
/// Pool6 objects are created and stored in the given PoolStorage container.
|
|
||||||
///
|
|
||||||
/// PdPool definitions currently support three elements: prefix, prefix-len,
|
|
||||||
/// and delegated-len, as shown in the example JSON text below:
|
|
||||||
///
|
|
||||||
/// @code
|
|
||||||
///
|
|
||||||
/// {
|
|
||||||
/// "prefix": "2001:db8:1::",
|
|
||||||
/// "prefix-len": 64,
|
|
||||||
/// "delegated-len": 128
|
|
||||||
/// }
|
|
||||||
/// @endcode
|
|
||||||
///
|
|
||||||
class PdPoolParser : public isc::data::SimpleParser {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief Constructor.
|
|
||||||
///
|
|
||||||
PdPoolParser() : options_(new CfgOption()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Builds a prefix delegation pool from the given configuration
|
|
||||||
///
|
|
||||||
/// This function parses configuration entries and creates an instance
|
|
||||||
/// of a dhcp::Pool6 configured for prefix delegation.
|
|
||||||
///
|
|
||||||
/// @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.
|
|
||||||
///
|
|
||||||
/// @throw DhcpConfigError if configuration parsing fails.
|
|
||||||
void parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
|
|
||||||
std::string addr_str = getString(pd_pool_, "prefix");
|
|
||||||
|
|
||||||
uint8_t prefix_len = getUint8(pd_pool_, "prefix-len");
|
|
||||||
|
|
||||||
uint8_t delegated_len = getUint8(pd_pool_, "delegated-len");
|
|
||||||
|
|
||||||
std::string excluded_prefix_str = "::";
|
|
||||||
if (pd_pool_->contains("excluded-prefix")) {
|
|
||||||
excluded_prefix_str = getString(pd_pool_, "excluded-prefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t excluded_prefix_len = 0;
|
|
||||||
if (pd_pool_->contains("excluded-prefix-len")) {
|
|
||||||
excluded_prefix_len = getUint8(pd_pool_, "excluded-prefix-len");
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstElementPtr option_data = pd_pool_->get("option-data");
|
|
||||||
if (option_data) {
|
|
||||||
OptionDataListParser opts_parser(AF_INET6);
|
|
||||||
opts_parser.parse(options_, option_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstElementPtr user_context = pd_pool_->get("user-context");
|
|
||||||
if (user_context) {
|
|
||||||
user_context_ = user_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the pool parameters. It will throw an exception if any
|
|
||||||
// of the required parameters are invalid.
|
|
||||||
try {
|
|
||||||
// Attempt to construct the local pool.
|
|
||||||
pool_.reset(new Pool6(IOAddress(addr_str),
|
|
||||||
prefix_len,
|
|
||||||
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
|
|
||||||
// the position of the pool map element.
|
|
||||||
isc_throw(isc::dhcp::DhcpConfigError, ex.what()
|
|
||||||
<< " (" << pd_pool_->getPosition() << ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user_context_) {
|
|
||||||
pool_->setUserContext(user_context_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the local pool to the external storage ptr.
|
|
||||||
pools->push_back(pool_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/// Pointer to the created pool object.
|
|
||||||
isc::dhcp::Pool6Ptr pool_;
|
|
||||||
|
|
||||||
/// A storage for pool specific option values.
|
|
||||||
CfgOptionPtr options_;
|
|
||||||
|
|
||||||
isc::data::ConstElementPtr user_context_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Parser for a list of prefix delegation pools.
|
|
||||||
///
|
|
||||||
/// This parser iterates over a list of prefix delegation pool entries and
|
|
||||||
/// creates pool instances for each one. If the parsing is successful, the
|
|
||||||
/// collection of pools is committed to the provided storage.
|
|
||||||
class PdPoolsListParser : public PoolsListParser {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief Parse configuration entries.
|
|
||||||
///
|
|
||||||
/// This function parses configuration entries and creates instances
|
|
||||||
/// of prefix delegation pools .
|
|
||||||
///
|
|
||||||
/// @param storage is the pool storage in which to store the parsed
|
|
||||||
/// @param pd_pool_list pointer to an element that holds entries
|
|
||||||
/// that define a prefix delegation pool.
|
|
||||||
///
|
|
||||||
/// @throw DhcpConfigError if configuration parsing fails.
|
|
||||||
void parse(PoolStoragePtr pools,
|
|
||||||
isc::data::ConstElementPtr pd_pool_list) {
|
|
||||||
// Loop through the list of pd pools.
|
|
||||||
BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
|
|
||||||
PdPoolParser parser;
|
|
||||||
parser.parse(pools, pd_pool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @anchor Subnet6ConfigParser
|
|
||||||
/// @brief This class parses a single IPv6 subnet.
|
|
||||||
///
|
|
||||||
/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
|
|
||||||
/// the whole subnet definition. It creates parsersfor received configuration
|
|
||||||
/// parameters as needed.
|
|
||||||
class Subnet6ConfigParser : public SubnetConfigParser {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
///
|
|
||||||
/// stores global scope parameters, options, option definitions.
|
|
||||||
Subnet6ConfigParser()
|
|
||||||
:SubnetConfigParser(AF_INET6) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Parses a single IPv6 subnet configuration and adds to the
|
|
||||||
/// Configuration Manager.
|
|
||||||
///
|
|
||||||
/// @param subnet A new subnet being configured.
|
|
||||||
/// @return a pointer to created Subnet6 object
|
|
||||||
Subnet6Ptr parse(ConstElementPtr subnet) {
|
|
||||||
/// Parse all pools first.
|
|
||||||
ConstElementPtr pools = subnet->get("pools");
|
|
||||||
if (pools) {
|
|
||||||
Pools6ListParser parser;
|
|
||||||
parser.parse(pools_, pools);
|
|
||||||
}
|
|
||||||
ConstElementPtr pd_pools = subnet->get("pd-pools");
|
|
||||||
if (pd_pools) {
|
|
||||||
PdPoolsListParser parser;
|
|
||||||
parser.parse(pools_, pd_pools);
|
|
||||||
}
|
|
||||||
|
|
||||||
SubnetPtr generic = SubnetConfigParser::parse(subnet);
|
|
||||||
|
|
||||||
if (!generic) {
|
|
||||||
isc_throw(DhcpConfigError,
|
|
||||||
"Failed to create an IPv6 subnet (" <<
|
|
||||||
subnet->getPosition() << ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
Subnet6Ptr sn6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
|
|
||||||
if (!sn6ptr) {
|
|
||||||
// If we hit this, it is a programming error.
|
|
||||||
isc_throw(Unexpected,
|
|
||||||
"Invalid Subnet6 cast in Subnet6ConfigParser::parse");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set relay information if it was provided
|
|
||||||
if (relay_info_) {
|
|
||||||
sn6ptr->setRelayInfo(*relay_info_);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Parse Host Reservations for this subnet if any.
|
|
||||||
ConstElementPtr reservations = subnet->get("reservations");
|
|
||||||
if (reservations) {
|
|
||||||
HostCollection hosts;
|
|
||||||
HostReservationsListParser<HostReservationParser6> parser;
|
|
||||||
parser.parse(subnet_->getID(), reservations, hosts);
|
|
||||||
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
|
|
||||||
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (sn6ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
|
|
||||||
/// options.
|
|
||||||
///
|
|
||||||
/// @param code is the numeric option code of the duplicate option
|
|
||||||
/// @param addr is the subnet address
|
|
||||||
/// @todo A means to know the correct logger and perhaps a common
|
|
||||||
/// message would allow this message to be emitted by the base class.
|
|
||||||
virtual void duplicate_option_warning(uint32_t code,
|
|
||||||
isc::asiolink::IOAddress& addr) {
|
|
||||||
LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
|
|
||||||
.arg(code).arg(addr.toText());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
|
|
||||||
/// and prefix length.
|
|
||||||
///
|
|
||||||
/// @param addr is IPv6 prefix of the subnet.
|
|
||||||
/// @param len is the prefix length
|
|
||||||
void initSubnet(isc::data::ConstElementPtr params,
|
|
||||||
isc::asiolink::IOAddress addr, uint8_t len) {
|
|
||||||
// Get all 'time' parameters using inheritance.
|
|
||||||
// If the subnet-specific value is defined then use it, else
|
|
||||||
// use the global value. The global value must always be
|
|
||||||
// present. If it is not, it is an internal error and exception
|
|
||||||
// is thrown.
|
|
||||||
Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
|
|
||||||
Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
|
|
||||||
Triplet<uint32_t> pref = getInteger(params, "preferred-lifetime");
|
|
||||||
Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
|
|
||||||
|
|
||||||
// Subnet ID is optional. If it is not supplied the value of 0 is used,
|
|
||||||
// which means autogenerate. The value was inserted earlier by calling
|
|
||||||
// SimpleParser6::setAllDefaults.
|
|
||||||
SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
|
|
||||||
|
|
||||||
// We want to log whether rapid-commit is enabled, so we get this
|
|
||||||
// before the actual subnet creation.
|
|
||||||
bool rapid_commit = getBoolean(params, "rapid-commit");
|
|
||||||
|
|
||||||
std::ostringstream output;
|
|
||||||
output << addr << "/" << static_cast<int>(len)
|
|
||||||
<< " with params t1=" << t1 << ", t2="
|
|
||||||
<< t2 << ", preferred-lifetime=" << pref
|
|
||||||
<< ", valid-lifetime=" << valid
|
|
||||||
<< ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled");
|
|
||||||
|
|
||||||
|
|
||||||
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(output.str());
|
|
||||||
|
|
||||||
// Create a new subnet.
|
|
||||||
Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
|
|
||||||
subnet_id);
|
|
||||||
subnet_.reset(subnet6);
|
|
||||||
|
|
||||||
// Enable or disable Rapid Commit option support for the subnet.
|
|
||||||
subnet6->setRapidCommit(rapid_commit);
|
|
||||||
|
|
||||||
// Get interface-id option content. For now we support string
|
|
||||||
// representation only
|
|
||||||
std::string ifaceid = getString(params, "interface-id");
|
|
||||||
std::string iface = getString(params, "interface");
|
|
||||||
|
|
||||||
// Specifying both interface for locally reachable subnets and
|
|
||||||
// interface id for relays is mutually exclusive. Need to test for
|
|
||||||
// this condition.
|
|
||||||
if (!ifaceid.empty() && !iface.empty()) {
|
|
||||||
isc_throw(isc::dhcp::DhcpConfigError,
|
|
||||||
"parser error: interface (defined for locally reachable "
|
|
||||||
"subnets) and interface-id (defined for subnets reachable"
|
|
||||||
" via relays) cannot be defined at the same time for "
|
|
||||||
"subnet " << addr << "/" << (int)len << "("
|
|
||||||
<< params->getPosition() << ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure interface-id for remote interfaces, if defined
|
|
||||||
if (!ifaceid.empty()) {
|
|
||||||
OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
|
|
||||||
OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
|
|
||||||
subnet6->setInterfaceId(opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// client-class processing is now generic and handled in the common
|
|
||||||
/// code (see @ref isc::data::SubnetConfigParser::createSubnet)
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief this class parses a list of DHCP6 subnets
|
|
||||||
///
|
|
||||||
/// This is a wrapper parser that handles the whole list of Subnet6
|
|
||||||
/// definitions. It iterates over all entries and creates Subnet6ConfigParser
|
|
||||||
/// for each entry.
|
|
||||||
class Subnets6ListConfigParser : public isc::data::SimpleParser {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief parses contents of the list
|
|
||||||
///
|
|
||||||
/// Iterates over all entries on the list, parses its content
|
|
||||||
/// (by instantiating Subnet6ConfigParser) and adds to specified
|
|
||||||
/// configuration.
|
|
||||||
///
|
|
||||||
/// @param cfg configuration (parsed subnets will be stored here)
|
|
||||||
/// @param subnets_list pointer to a list of IPv6 subnets
|
|
||||||
/// @throw DhcpConfigError if CfgMgr rejects the subnet (e.g. subnet-id is a duplicate)
|
|
||||||
size_t parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
|
|
||||||
size_t cnt = 0;
|
|
||||||
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
|
|
||||||
|
|
||||||
Subnet6ConfigParser parser;
|
|
||||||
Subnet6Ptr subnet = parser.parse(subnet_json);
|
|
||||||
|
|
||||||
// Adding a subnet to the Configuration Manager may fail if the
|
|
||||||
// subnet id is invalid (duplicate). Thus, we catch exceptions
|
|
||||||
// here to append a position in the configuration string.
|
|
||||||
try {
|
|
||||||
cfg->getCfgSubnets6()->add(subnet);
|
|
||||||
cnt++;
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
isc_throw(DhcpConfigError, ex.what() << " ("
|
|
||||||
<< subnet_json->getPosition() << ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (cnt);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Parser for list of RSOO options
|
/// @brief Parser for list of RSOO options
|
||||||
///
|
///
|
||||||
/// This parser handles a Dhcp6/relay-supplied-options entry. It contains a
|
/// This parser handles a Dhcp6/relay-supplied-options entry. It contains a
|
||||||
|
@@ -43,6 +43,7 @@ EXTRA_DIST += parsers/host_reservation_parser.h
|
|||||||
EXTRA_DIST += parsers/host_reservations_list_parser.h
|
EXTRA_DIST += parsers/host_reservations_list_parser.h
|
||||||
EXTRA_DIST += parsers/ifaces_config_parser.cc
|
EXTRA_DIST += parsers/ifaces_config_parser.cc
|
||||||
EXTRA_DIST += parsers/ifaces_config_parser.h
|
EXTRA_DIST += parsers/ifaces_config_parser.h
|
||||||
|
EXTRA_DIST += parsers/option_data_parser.h
|
||||||
|
|
||||||
# Devel guide diagrams
|
# Devel guide diagrams
|
||||||
EXTRA_DIST += images/pgsql_host_data_source.svg
|
EXTRA_DIST += images/pgsql_host_data_source.svg
|
||||||
@@ -171,6 +172,8 @@ libkea_dhcpsrv_la_SOURCES += parsers/host_reservation_parser.h
|
|||||||
libkea_dhcpsrv_la_SOURCES += parsers/host_reservations_list_parser.h
|
libkea_dhcpsrv_la_SOURCES += parsers/host_reservations_list_parser.h
|
||||||
libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.cc
|
libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.cc
|
||||||
libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.h
|
libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.h
|
||||||
|
libkea_dhcpsrv_la_SOURCES += parsers/option_data_parser.cc
|
||||||
|
libkea_dhcpsrv_la_SOURCES += parsers/option_data_parser.h
|
||||||
|
|
||||||
nodist_libkea_dhcpsrv_la_SOURCES = alloc_engine__messages.h
|
nodist_libkea_dhcpsrv_la_SOURCES = alloc_engine__messages.h
|
||||||
nodist_libkea_dhcpsrv_la_SOURCES += alloc_engine_messages.cc
|
nodist_libkea_dhcpsrv_la_SOURCES += alloc_engine_messages.cc
|
||||||
|
@@ -44,11 +44,19 @@ there is a good reason for it, to avoid increased number of renewals and
|
|||||||
a need for rebinding (increase of multicast traffic, which may be received
|
a need for rebinding (increase of multicast traffic, which may be received
|
||||||
by multiple servers).
|
by multiple servers).
|
||||||
|
|
||||||
|
% DHCPSRV_CFGMGR_NEW_SUBNET4 a new subnet has been added to configuration: %1
|
||||||
|
This is an informational message reporting that the configuration has
|
||||||
|
been extended to include the specified IPv4 subnet.
|
||||||
|
|
||||||
% DHCPSRV_CFGMGR_NO_SUBNET4 no suitable subnet is defined for address hint %1
|
% DHCPSRV_CFGMGR_NO_SUBNET4 no suitable subnet is defined for address hint %1
|
||||||
This debug message is output when the DHCP configuration manager has received
|
This debug message is output when the DHCP configuration manager has received
|
||||||
a request for an IPv4 subnet for the specified address, but no such
|
a request for an IPv4 subnet for the specified address, but no such
|
||||||
subnet exists.
|
subnet exists.
|
||||||
|
|
||||||
|
% DHCPSRV_CFGMGR_NEW_SUBNET6 a new subnet has been added to configuration: %1
|
||||||
|
This is an informational message reporting that the configuration has
|
||||||
|
been extended to include the specified subnet.
|
||||||
|
|
||||||
% DHCPSRV_CFGMGR_NO_SUBNET6 no suitable subnet is defined for address hint %1
|
% DHCPSRV_CFGMGR_NO_SUBNET6 no suitable subnet is defined for address hint %1
|
||||||
This debug message is output when the DHCP configuration manager has received
|
This debug message is output when the DHCP configuration manager has received
|
||||||
a request for an IPv6 subnet for the specified address, but no such
|
a request for an IPv6 subnet for the specified address, but no such
|
||||||
@@ -64,6 +72,11 @@ This is a debug message reporting that the DHCP configuration manager has
|
|||||||
returned the specified IPv6 subnet when given the address hint specified
|
returned the specified IPv6 subnet when given the address hint specified
|
||||||
because it is the only subnet defined.
|
because it is the only subnet defined.
|
||||||
|
|
||||||
|
% DHCPSRV_CFGMGR_OPTION_DUPLICATE multiple options with the code: %1 added to the subnet: %2
|
||||||
|
This warning message is issued on an attempt to configure multiple options with the
|
||||||
|
same option code for the particular subnet. Adding multiple options is uncommon
|
||||||
|
for DHCPv6, but it is not prohibited.
|
||||||
|
|
||||||
% DHCPSRV_CFGMGR_SOCKET_RAW_UNSUPPORTED use of raw sockets is unsupported on this OS, UDP sockets will be used
|
% DHCPSRV_CFGMGR_SOCKET_RAW_UNSUPPORTED use of raw sockets is unsupported on this OS, UDP sockets will be used
|
||||||
This warning message is logged when the user specified that the
|
This warning message is logged when the user specified that the
|
||||||
DHCPv4 server should use the raw sockets to receive the DHCP
|
DHCPv4 server should use the raw sockets to receive the DHCP
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
#include <dhcpsrv/client_class_def.h>
|
#include <dhcpsrv/client_class_def.h>
|
||||||
#include <dhcpsrv/parsers/dhcp_parsers.h>
|
#include <dhcpsrv/parsers/dhcp_parsers.h>
|
||||||
#include <dhcpsrv/parsers/client_class_def_parser.h>
|
#include <dhcpsrv/parsers/client_class_def_parser.h>
|
||||||
|
#include <dhcpsrv/parsers/option_data_parser.h>
|
||||||
#include <eval/eval_context.h>
|
#include <eval/eval_context.h>
|
||||||
#include <asiolink/io_address.h>
|
#include <asiolink/io_address.h>
|
||||||
#include <asiolink/io_error.h>
|
#include <asiolink/io_error.h>
|
||||||
|
@@ -10,7 +10,11 @@
|
|||||||
#include <dhcp/libdhcp++.h>
|
#include <dhcp/libdhcp++.h>
|
||||||
#include <dhcpsrv/cfgmgr.h>
|
#include <dhcpsrv/cfgmgr.h>
|
||||||
#include <dhcpsrv/cfg_option.h>
|
#include <dhcpsrv/cfg_option.h>
|
||||||
|
#include <dhcpsrv/dhcpsrv_log.h>
|
||||||
#include <dhcpsrv/parsers/dhcp_parsers.h>
|
#include <dhcpsrv/parsers/dhcp_parsers.h>
|
||||||
|
#include <dhcpsrv/parsers/host_reservation_parser.h>
|
||||||
|
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
|
||||||
|
#include <dhcpsrv/parsers/option_data_parser.h>
|
||||||
#include <dhcpsrv/cfg_mac_source.h>
|
#include <dhcpsrv/cfg_mac_source.h>
|
||||||
#include <util/encode/hex.h>
|
#include <util/encode/hex.h>
|
||||||
#include <util/strutil.h>
|
#include <util/strutil.h>
|
||||||
@@ -761,6 +765,28 @@ PoolParser::parse(PoolStoragePtr pools,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//****************************** Pool4Parser *************************
|
||||||
|
|
||||||
|
PoolPtr
|
||||||
|
Pool4Parser::poolMaker (IOAddress &addr, uint32_t len, int32_t) {
|
||||||
|
return (PoolPtr(new Pool4(addr, len)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PoolPtr
|
||||||
|
Pool4Parser::poolMaker (IOAddress &min, IOAddress &max, int32_t) {
|
||||||
|
return (PoolPtr(new Pool4(min, max)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************** Pool4ListParser *************************
|
||||||
|
|
||||||
|
void
|
||||||
|
Pools4ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list) {
|
||||||
|
BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
|
||||||
|
Pool4Parser parser;
|
||||||
|
parser.parse(pools, pool, AF_INET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//****************************** SubnetConfigParser *************************
|
//****************************** SubnetConfigParser *************************
|
||||||
|
|
||||||
SubnetConfigParser::SubnetConfigParser(uint16_t family)
|
SubnetConfigParser::SubnetConfigParser(uint16_t family)
|
||||||
@@ -905,6 +931,441 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) {
|
|||||||
options_->copyTo(*subnet_->getCfgOption());
|
options_->copyTo(*subnet_->getCfgOption());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//****************************** Subnet4ConfigParser *************************
|
||||||
|
|
||||||
|
Subnet4ConfigParser::Subnet4ConfigParser()
|
||||||
|
:SubnetConfigParser(AF_INET) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Subnet4Ptr
|
||||||
|
Subnet4ConfigParser::parse(ConstElementPtr subnet) {
|
||||||
|
/// Parse Pools first.
|
||||||
|
ConstElementPtr pools = subnet->get("pools");
|
||||||
|
if (pools) {
|
||||||
|
Pools4ListParser parser;
|
||||||
|
parser.parse(pools_, pools);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubnetPtr generic = SubnetConfigParser::parse(subnet);
|
||||||
|
|
||||||
|
if (!generic) {
|
||||||
|
isc_throw(DhcpConfigError,
|
||||||
|
"Failed to create an IPv4 subnet (" <<
|
||||||
|
subnet->getPosition() << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
Subnet4Ptr sn4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
|
||||||
|
if (!sn4ptr) {
|
||||||
|
// If we hit this, it is a programming error.
|
||||||
|
isc_throw(Unexpected,
|
||||||
|
"Invalid Subnet4 cast in Subnet4ConfigParser::parse");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set relay information if it was parsed
|
||||||
|
if (relay_info_) {
|
||||||
|
sn4ptr->setRelayInfo(*relay_info_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse Host Reservations for this subnet if any.
|
||||||
|
ConstElementPtr reservations = subnet->get("reservations");
|
||||||
|
if (reservations) {
|
||||||
|
HostCollection hosts;
|
||||||
|
HostReservationsListParser<HostReservationParser4> parser;
|
||||||
|
parser.parse(subnet_->getID(), reservations, hosts);
|
||||||
|
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
|
||||||
|
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sn4ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
|
||||||
|
asiolink::IOAddress addr, uint8_t len) {
|
||||||
|
// The renew-timer and rebind-timer are optional. If not set, the
|
||||||
|
// option 58 and 59 will not be sent to a client. In this case the
|
||||||
|
// client will use default values based on the valid-lifetime.
|
||||||
|
Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
|
||||||
|
Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
|
||||||
|
|
||||||
|
// The valid-lifetime is mandatory. It may be specified for a
|
||||||
|
// particular subnet. If not, the global value should be present.
|
||||||
|
// If there is no global value, exception is thrown.
|
||||||
|
Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
|
||||||
|
|
||||||
|
// Subnet ID is optional. If it is not supplied the value of 0 is used,
|
||||||
|
// which means autogenerate. The value was inserted earlier by calling
|
||||||
|
// SimpleParser4::setAllDefaults.
|
||||||
|
SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
|
||||||
|
|
||||||
|
stringstream s;
|
||||||
|
s << addr << "/" << static_cast<int>(len) << " with params: ";
|
||||||
|
// t1 and t2 are optional may be not specified.
|
||||||
|
if (!t1.unspecified()) {
|
||||||
|
s << "t1=" << t1 << ", ";
|
||||||
|
}
|
||||||
|
if (!t2.unspecified()) {
|
||||||
|
s << "t2=" << t2 << ", ";
|
||||||
|
}
|
||||||
|
s <<"valid-lifetime=" << valid;
|
||||||
|
|
||||||
|
LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_NEW_SUBNET4).arg(s.str());
|
||||||
|
|
||||||
|
Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
|
||||||
|
subnet_ = subnet4;
|
||||||
|
|
||||||
|
// Set the match-client-id value for the subnet. It is always present.
|
||||||
|
// If not explicitly specified, the default value was filled in when
|
||||||
|
// SimpleParser4::setAllDefaults was called.
|
||||||
|
bool match_client_id = getBoolean(params, "match-client-id");
|
||||||
|
subnet4->setMatchClientId(match_client_id);
|
||||||
|
|
||||||
|
// Set next-server. The default value is 0.0.0.0. Nevertheless, the
|
||||||
|
// user could have messed that up by specifying incorrect value.
|
||||||
|
// To avoid using 0.0.0.0, user can specify "".
|
||||||
|
string next_server;
|
||||||
|
try {
|
||||||
|
next_server = getString(params, "next-server");
|
||||||
|
if (!next_server.empty()) {
|
||||||
|
subnet4->setSiaddr(IOAddress(next_server));
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
ConstElementPtr next = params->get("next-server");
|
||||||
|
string pos;
|
||||||
|
if (next) {
|
||||||
|
pos = next->getPosition().str();
|
||||||
|
} else {
|
||||||
|
pos = params->getPosition().str();
|
||||||
|
}
|
||||||
|
isc_throw(DhcpConfigError, "invalid parameter next-server : "
|
||||||
|
<< next_server << "(" << pos << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4o6 specific parameter: 4o6-interface. If not explicitly specified,
|
||||||
|
// it will have the default value of "".
|
||||||
|
string iface4o6 = getString(params, "4o6-interface");
|
||||||
|
if (!iface4o6.empty()) {
|
||||||
|
subnet4->get4o6().setIface4o6(iface4o6);
|
||||||
|
subnet4->get4o6().enabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4o6 specific parameter: 4o6-subnet. If not explicitly specified, it
|
||||||
|
// will have the default value of "".
|
||||||
|
string subnet4o6 = getString(params, "4o6-subnet");
|
||||||
|
if (!subnet4o6.empty()) {
|
||||||
|
size_t slash = subnet4o6.find("/");
|
||||||
|
if (slash == std::string::npos) {
|
||||||
|
isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
|
||||||
|
<< subnet4o6 << ", expected format: prefix6/length");
|
||||||
|
}
|
||||||
|
string prefix = subnet4o6.substr(0, slash);
|
||||||
|
string lenstr = subnet4o6.substr(slash + 1);
|
||||||
|
|
||||||
|
uint8_t len = 128;
|
||||||
|
try {
|
||||||
|
len = boost::lexical_cast<unsigned int>(lenstr.c_str());
|
||||||
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
|
isc_throw(DhcpConfigError, "Invalid prefix length specified in "
|
||||||
|
"4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
|
||||||
|
}
|
||||||
|
subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
|
||||||
|
subnet4->get4o6().enabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try 4o6 specific parameter: 4o6-interface-id
|
||||||
|
std::string ifaceid = getString(params, "4o6-interface-id");
|
||||||
|
if (!ifaceid.empty()) {
|
||||||
|
OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
|
||||||
|
OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
|
||||||
|
subnet4->get4o6().setInterfaceId(opt);
|
||||||
|
subnet4->get4o6().enabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// client-class processing is now generic and handled in the common
|
||||||
|
/// code (see isc::data::SubnetConfigParser::createSubnet)
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************************** Subnets4ListConfigParser **********************
|
||||||
|
|
||||||
|
size_t
|
||||||
|
Subnets4ListConfigParser::parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
|
||||||
|
size_t cnt = 0;
|
||||||
|
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
|
||||||
|
|
||||||
|
Subnet4ConfigParser parser;
|
||||||
|
Subnet4Ptr subnet = parser.parse(subnet_json);
|
||||||
|
if (subnet) {
|
||||||
|
|
||||||
|
// Adding a subnet to the Configuration Manager may fail if the
|
||||||
|
// subnet id is invalid (duplicate). Thus, we catch exceptions
|
||||||
|
// here to append a position in the configuration string.
|
||||||
|
try {
|
||||||
|
cfg->getCfgSubnets4()->add(subnet);
|
||||||
|
cnt++;
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||||
|
<< subnet_json->getPosition() << ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************************** Pool6Parser *********************************
|
||||||
|
|
||||||
|
PoolPtr
|
||||||
|
Pool6Parser::poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
|
||||||
|
{
|
||||||
|
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
|
||||||
|
(ptype), addr, len)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PoolPtr
|
||||||
|
Pool6Parser::poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
|
||||||
|
{
|
||||||
|
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
|
||||||
|
(ptype), min, max)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//**************************** Pool6ListParser ***************************
|
||||||
|
|
||||||
|
void
|
||||||
|
Pools6ListParser::parse(PoolStoragePtr pools, ConstElementPtr pools_list) {
|
||||||
|
BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
|
||||||
|
Pool6Parser parser;
|
||||||
|
parser.parse(pools, pool, AF_INET6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************************** PdPoolParser ******************************
|
||||||
|
|
||||||
|
PdPoolParser::PdPoolParser() : options_(new CfgOption()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
|
||||||
|
std::string addr_str = getString(pd_pool_, "prefix");
|
||||||
|
|
||||||
|
uint8_t prefix_len = getUint8(pd_pool_, "prefix-len");
|
||||||
|
|
||||||
|
uint8_t delegated_len = getUint8(pd_pool_, "delegated-len");
|
||||||
|
|
||||||
|
std::string excluded_prefix_str = "::";
|
||||||
|
if (pd_pool_->contains("excluded-prefix")) {
|
||||||
|
excluded_prefix_str = getString(pd_pool_, "excluded-prefix");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t excluded_prefix_len = 0;
|
||||||
|
if (pd_pool_->contains("excluded-prefix-len")) {
|
||||||
|
excluded_prefix_len = getUint8(pd_pool_, "excluded-prefix-len");
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstElementPtr option_data = pd_pool_->get("option-data");
|
||||||
|
if (option_data) {
|
||||||
|
OptionDataListParser opts_parser(AF_INET6);
|
||||||
|
opts_parser.parse(options_, option_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstElementPtr user_context = pd_pool_->get("user-context");
|
||||||
|
if (user_context) {
|
||||||
|
user_context_ = user_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the pool parameters. It will throw an exception if any
|
||||||
|
// of the required parameters are invalid.
|
||||||
|
try {
|
||||||
|
// Attempt to construct the local pool.
|
||||||
|
pool_.reset(new Pool6(IOAddress(addr_str),
|
||||||
|
prefix_len,
|
||||||
|
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
|
||||||
|
// the position of the pool map element.
|
||||||
|
isc_throw(isc::dhcp::DhcpConfigError, ex.what()
|
||||||
|
<< " (" << pd_pool_->getPosition() << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_context_) {
|
||||||
|
pool_->setUserContext(user_context_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the local pool to the external storage ptr.
|
||||||
|
pools->push_back(pool_);
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************************** PdPoolsListParser ************************
|
||||||
|
|
||||||
|
void
|
||||||
|
PdPoolsListParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_list) {
|
||||||
|
// Loop through the list of pd pools.
|
||||||
|
BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
|
||||||
|
PdPoolParser parser;
|
||||||
|
parser.parse(pools, pd_pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************************** Subnet6ConfigParser ***********************
|
||||||
|
|
||||||
|
Subnet6ConfigParser::Subnet6ConfigParser()
|
||||||
|
:SubnetConfigParser(AF_INET6) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Subnet6Ptr
|
||||||
|
Subnet6ConfigParser::parse(ConstElementPtr subnet) {
|
||||||
|
/// Parse all pools first.
|
||||||
|
ConstElementPtr pools = subnet->get("pools");
|
||||||
|
if (pools) {
|
||||||
|
Pools6ListParser parser;
|
||||||
|
parser.parse(pools_, pools);
|
||||||
|
}
|
||||||
|
ConstElementPtr pd_pools = subnet->get("pd-pools");
|
||||||
|
if (pd_pools) {
|
||||||
|
PdPoolsListParser parser;
|
||||||
|
parser.parse(pools_, pd_pools);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubnetPtr generic = SubnetConfigParser::parse(subnet);
|
||||||
|
|
||||||
|
if (!generic) {
|
||||||
|
isc_throw(DhcpConfigError,
|
||||||
|
"Failed to create an IPv6 subnet (" <<
|
||||||
|
subnet->getPosition() << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
Subnet6Ptr sn6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
|
||||||
|
if (!sn6ptr) {
|
||||||
|
// If we hit this, it is a programming error.
|
||||||
|
isc_throw(Unexpected,
|
||||||
|
"Invalid Subnet6 cast in Subnet6ConfigParser::parse");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set relay information if it was provided
|
||||||
|
if (relay_info_) {
|
||||||
|
sn6ptr->setRelayInfo(*relay_info_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parse Host Reservations for this subnet if any.
|
||||||
|
ConstElementPtr reservations = subnet->get("reservations");
|
||||||
|
if (reservations) {
|
||||||
|
HostCollection hosts;
|
||||||
|
HostReservationsListParser<HostReservationParser6> parser;
|
||||||
|
parser.parse(subnet_->getID(), reservations, hosts);
|
||||||
|
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
|
||||||
|
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sn6ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Subnet6ConfigParser::duplicate_option_warning(uint32_t code,
|
||||||
|
asiolink::IOAddress& addr) {
|
||||||
|
LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_OPTION_DUPLICATE)
|
||||||
|
.arg(code).arg(addr.toText());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
|
||||||
|
asiolink::IOAddress addr, uint8_t len) {
|
||||||
|
// Get all 'time' parameters using inheritance.
|
||||||
|
// If the subnet-specific value is defined then use it, else
|
||||||
|
// use the global value. The global value must always be
|
||||||
|
// present. If it is not, it is an internal error and exception
|
||||||
|
// is thrown.
|
||||||
|
Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
|
||||||
|
Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
|
||||||
|
Triplet<uint32_t> pref = getInteger(params, "preferred-lifetime");
|
||||||
|
Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
|
||||||
|
|
||||||
|
// Subnet ID is optional. If it is not supplied the value of 0 is used,
|
||||||
|
// which means autogenerate. The value was inserted earlier by calling
|
||||||
|
// SimpleParser6::setAllDefaults.
|
||||||
|
SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
|
||||||
|
|
||||||
|
// We want to log whether rapid-commit is enabled, so we get this
|
||||||
|
// before the actual subnet creation.
|
||||||
|
bool rapid_commit = getBoolean(params, "rapid-commit");
|
||||||
|
|
||||||
|
std::ostringstream output;
|
||||||
|
output << addr << "/" << static_cast<int>(len)
|
||||||
|
<< " with params t1=" << t1 << ", t2="
|
||||||
|
<< t2 << ", preferred-lifetime=" << pref
|
||||||
|
<< ", valid-lifetime=" << valid
|
||||||
|
<< ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
|
||||||
|
LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_NEW_SUBNET4).arg(output.str());
|
||||||
|
|
||||||
|
// Create a new subnet.
|
||||||
|
Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
|
||||||
|
subnet_id);
|
||||||
|
subnet_.reset(subnet6);
|
||||||
|
|
||||||
|
// Enable or disable Rapid Commit option support for the subnet.
|
||||||
|
subnet6->setRapidCommit(rapid_commit);
|
||||||
|
|
||||||
|
// Get interface-id option content. For now we support string
|
||||||
|
// representation only
|
||||||
|
std::string ifaceid = getString(params, "interface-id");
|
||||||
|
std::string iface = getString(params, "interface");
|
||||||
|
|
||||||
|
// Specifying both interface for locally reachable subnets and
|
||||||
|
// interface id for relays is mutually exclusive. Need to test for
|
||||||
|
// this condition.
|
||||||
|
if (!ifaceid.empty() && !iface.empty()) {
|
||||||
|
isc_throw(isc::dhcp::DhcpConfigError,
|
||||||
|
"parser error: interface (defined for locally reachable "
|
||||||
|
"subnets) and interface-id (defined for subnets reachable"
|
||||||
|
" via relays) cannot be defined at the same time for "
|
||||||
|
"subnet " << addr << "/" << (int)len << "("
|
||||||
|
<< params->getPosition() << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure interface-id for remote interfaces, if defined
|
||||||
|
if (!ifaceid.empty()) {
|
||||||
|
OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
|
||||||
|
OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
|
||||||
|
subnet6->setInterfaceId(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// client-class processing is now generic and handled in the common
|
||||||
|
/// code (see isc::data::SubnetConfigParser::createSubnet)
|
||||||
|
}
|
||||||
|
|
||||||
|
//**************************** Subnet6ListConfigParser ********************
|
||||||
|
|
||||||
|
size_t
|
||||||
|
Subnets6ListConfigParser::parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
|
||||||
|
size_t cnt = 0;
|
||||||
|
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
|
||||||
|
|
||||||
|
Subnet6ConfigParser parser;
|
||||||
|
Subnet6Ptr subnet = parser.parse(subnet_json);
|
||||||
|
|
||||||
|
// Adding a subnet to the Configuration Manager may fail if the
|
||||||
|
// subnet id is invalid (duplicate). Thus, we catch exceptions
|
||||||
|
// here to append a position in the configuration string.
|
||||||
|
try {
|
||||||
|
cfg->getCfgSubnets6()->add(subnet);
|
||||||
|
cnt++;
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||||
|
<< subnet_json->getPosition() << ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//**************************** D2ClientConfigParser **********************
|
//**************************** D2ClientConfigParser **********************
|
||||||
|
|
||||||
dhcp_ddns::NameChangeProtocol
|
dhcp_ddns::NameChangeProtocol
|
||||||
|
@@ -341,7 +341,6 @@ public:
|
|||||||
void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value);
|
void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// @brief Parser for option data value.
|
/// @brief Parser for option data value.
|
||||||
///
|
///
|
||||||
/// This parser parses configuration entries that specify value of
|
/// This parser parses configuration entries that specify value of
|
||||||
@@ -590,6 +589,37 @@ protected:
|
|||||||
int32_t ptype = 0) = 0;
|
int32_t ptype = 0) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Parser for IPv4 pool definitions.
|
||||||
|
///
|
||||||
|
/// This is the IPv4 derivation of the PoolParser class and handles pool
|
||||||
|
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
|
||||||
|
/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
|
||||||
|
/// PoolStorage container.
|
||||||
|
///
|
||||||
|
/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
|
||||||
|
class Pool4Parser : public PoolParser {
|
||||||
|
protected:
|
||||||
|
/// @brief Creates a Pool4 object given a IPv4 prefix and the prefix length.
|
||||||
|
///
|
||||||
|
/// @param addr is the IPv4 prefix of the pool.
|
||||||
|
/// @param len is the prefix length.
|
||||||
|
/// @param ignored dummy parameter to provide symmetry between the
|
||||||
|
/// PoolParser derivations. The V6 derivation requires a third value.
|
||||||
|
/// @return returns a PoolPtr to the new Pool4 object.
|
||||||
|
PoolPtr poolMaker (asiolink::IOAddress &addr, uint32_t len,
|
||||||
|
int32_t ignored);
|
||||||
|
|
||||||
|
/// @brief Creates a Pool4 object given starting and ending IPv4 addresses.
|
||||||
|
///
|
||||||
|
/// @param min is the first IPv4 address in the pool.
|
||||||
|
/// @param max is the last IPv4 address in the pool.
|
||||||
|
/// @param ignored dummy parameter to provide symmetry between the
|
||||||
|
/// PoolParser derivations. The V6 derivation requires a third value.
|
||||||
|
/// @return returns a PoolPtr to the new Pool4 object.
|
||||||
|
PoolPtr poolMaker (asiolink::IOAddress &min, asiolink::IOAddress &max,
|
||||||
|
int32_t ignored);
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Parser for a list of pools
|
/// @brief Parser for a list of pools
|
||||||
///
|
///
|
||||||
/// This parser parses a list pools. Each element on that list gets its own
|
/// This parser parses a list pools. Each element on that list gets its own
|
||||||
@@ -613,6 +643,20 @@ public:
|
|||||||
isc::data::ConstElementPtr pools_list) = 0;
|
isc::data::ConstElementPtr pools_list) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Specialization of the pool list parser for DHCPv4
|
||||||
|
class Pools4ListParser : PoolsListParser {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief parses the actual structure
|
||||||
|
///
|
||||||
|
/// This method parses the actual list of pools.
|
||||||
|
///
|
||||||
|
/// @param pools storage container in which to store the parsed pool.
|
||||||
|
/// @param pools_list a list of pool structures
|
||||||
|
/// @throw isc::dhcp::DhcpConfigError when pool parsing fails
|
||||||
|
void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list);
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief parser for additional relay information
|
/// @brief parser for additional relay information
|
||||||
///
|
///
|
||||||
/// This concrete parser handles RelayInfo structure definitions.
|
/// This concrete parser handles RelayInfo structure definitions.
|
||||||
@@ -735,6 +779,235 @@ protected:
|
|||||||
CfgOptionPtr options_;
|
CfgOptionPtr options_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @anchor Subnet4ConfigParser
|
||||||
|
/// @brief This class parses a single IPv4 subnet.
|
||||||
|
///
|
||||||
|
/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
|
||||||
|
/// the whole subnet definition. It creates parsersfor received configuration
|
||||||
|
/// parameters as needed.
|
||||||
|
class Subnet4ConfigParser : public SubnetConfigParser {
|
||||||
|
public:
|
||||||
|
/// @brief Constructor
|
||||||
|
///
|
||||||
|
/// stores global scope parameters, options, option definitions.
|
||||||
|
Subnet4ConfigParser();
|
||||||
|
|
||||||
|
/// @brief Parses a single IPv4 subnet configuration and adds to the
|
||||||
|
/// Configuration Manager.
|
||||||
|
///
|
||||||
|
/// @param subnet A new subnet being configured.
|
||||||
|
/// @return a pointer to created Subnet4 object
|
||||||
|
Subnet4Ptr parse(data::ConstElementPtr subnet);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
|
||||||
|
/// and prefix length.
|
||||||
|
///
|
||||||
|
/// @param params Data structure describing a subnet.
|
||||||
|
/// @param addr is IPv4 address of the subnet.
|
||||||
|
/// @param len is the prefix length
|
||||||
|
void initSubnet(data::ConstElementPtr params,
|
||||||
|
asiolink::IOAddress addr, uint8_t len);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief this class parses list of DHCP4 subnets
|
||||||
|
///
|
||||||
|
/// This is a wrapper parser that handles the whole list of Subnet4
|
||||||
|
/// definitions. It iterates over all entries and creates Subnet4ConfigParser
|
||||||
|
/// for each entry.
|
||||||
|
class Subnets4ListConfigParser : public isc::data::SimpleParser {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief parses contents of the list
|
||||||
|
///
|
||||||
|
/// Iterates over all entries on the list, parses its content
|
||||||
|
/// (by instantiating Subnet6ConfigParser) and adds to specified
|
||||||
|
/// configuration.
|
||||||
|
///
|
||||||
|
/// @param cfg Pointer to server configuration.
|
||||||
|
/// @param subnets_list pointer to a list of IPv4 subnets
|
||||||
|
/// @return number of subnets created
|
||||||
|
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Parser for IPv6 pool definitions.
|
||||||
|
///
|
||||||
|
/// This is the IPv6 derivation of the PoolParser class and handles pool
|
||||||
|
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
|
||||||
|
/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
|
||||||
|
/// PoolStorage container.
|
||||||
|
///
|
||||||
|
/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
|
||||||
|
class Pool6Parser : public PoolParser {
|
||||||
|
protected:
|
||||||
|
/// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
|
||||||
|
///
|
||||||
|
/// @param addr is the IPv6 prefix of the pool.
|
||||||
|
/// @param len is the prefix length.
|
||||||
|
/// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
|
||||||
|
/// passed in as an int32_t and cast to PoolType to accommodate a
|
||||||
|
/// polymorphic interface.
|
||||||
|
/// @return returns a PoolPtr to the new Pool4 object.
|
||||||
|
PoolPtr poolMaker (asiolink::IOAddress &addr, uint32_t len, int32_t ptype);
|
||||||
|
|
||||||
|
/// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
|
||||||
|
///
|
||||||
|
/// @param min is the first IPv6 address in the pool.
|
||||||
|
/// @param max is the last IPv6 address in the pool.
|
||||||
|
/// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
|
||||||
|
/// passed in as an int32_t and cast to PoolType to accommodate a
|
||||||
|
/// polymorphic interface.
|
||||||
|
/// @return returns a PoolPtr to the new Pool4 object.
|
||||||
|
PoolPtr poolMaker (asiolink::IOAddress &min, asiolink::IOAddress &max,
|
||||||
|
int32_t ptype);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Specialization of the pool list parser for DHCPv6
|
||||||
|
class Pools6ListParser : PoolsListParser {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief parses the actual structure
|
||||||
|
///
|
||||||
|
/// This method parses the actual list of pools.
|
||||||
|
///
|
||||||
|
/// @param pools storage container in which to store the parsed pool.
|
||||||
|
/// @param pools_list a list of pool structures
|
||||||
|
/// @throw isc::dhcp::DhcpConfigError when pool parsing fails
|
||||||
|
void parse(PoolStoragePtr pools, data::ConstElementPtr pools_list);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Parser for IPv6 prefix delegation definitions.
|
||||||
|
///
|
||||||
|
/// This class handles prefix delegation pool definitions for IPv6 subnets
|
||||||
|
/// Pool6 objects are created and stored in the given PoolStorage container.
|
||||||
|
///
|
||||||
|
/// PdPool definitions currently support three elements: prefix, prefix-len,
|
||||||
|
/// and delegated-len, as shown in the example JSON text below:
|
||||||
|
///
|
||||||
|
/// @code
|
||||||
|
///
|
||||||
|
/// {
|
||||||
|
/// "prefix": "2001:db8:1::",
|
||||||
|
/// "prefix-len": 64,
|
||||||
|
/// "delegated-len": 128
|
||||||
|
/// }
|
||||||
|
/// @endcode
|
||||||
|
///
|
||||||
|
class PdPoolParser : public isc::data::SimpleParser {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor.
|
||||||
|
///
|
||||||
|
PdPoolParser();
|
||||||
|
|
||||||
|
/// @brief Builds a prefix delegation pool from the given configuration
|
||||||
|
///
|
||||||
|
/// This function parses configuration entries and creates an instance
|
||||||
|
/// of a dhcp::Pool6 configured for prefix delegation.
|
||||||
|
///
|
||||||
|
/// @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.
|
||||||
|
///
|
||||||
|
/// @throw DhcpConfigError if configuration parsing fails.
|
||||||
|
void parse(PoolStoragePtr pools, data::ConstElementPtr pd_pool_);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// Pointer to the created pool object.
|
||||||
|
isc::dhcp::Pool6Ptr pool_;
|
||||||
|
|
||||||
|
/// A storage for pool specific option values.
|
||||||
|
CfgOptionPtr options_;
|
||||||
|
|
||||||
|
isc::data::ConstElementPtr user_context_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Parser for a list of prefix delegation pools.
|
||||||
|
///
|
||||||
|
/// This parser iterates over a list of prefix delegation pool entries and
|
||||||
|
/// creates pool instances for each one. If the parsing is successful, the
|
||||||
|
/// collection of pools is committed to the provided storage.
|
||||||
|
class PdPoolsListParser : public PoolsListParser {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Parse configuration entries.
|
||||||
|
///
|
||||||
|
/// This function parses configuration entries and creates instances
|
||||||
|
/// of prefix delegation pools .
|
||||||
|
///
|
||||||
|
/// @param storage is the pool storage in which to store the parsed
|
||||||
|
/// @param pd_pool_list pointer to an element that holds entries
|
||||||
|
/// that define a prefix delegation pool.
|
||||||
|
///
|
||||||
|
/// @throw DhcpConfigError if configuration parsing fails.
|
||||||
|
void parse(PoolStoragePtr pools, data::ConstElementPtr pd_pool_list);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @anchor Subnet6ConfigParser
|
||||||
|
/// @brief This class parses a single IPv6 subnet.
|
||||||
|
///
|
||||||
|
/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
|
||||||
|
/// the whole subnet definition. It creates parsersfor received configuration
|
||||||
|
/// parameters as needed.
|
||||||
|
class Subnet6ConfigParser : public SubnetConfigParser {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor
|
||||||
|
///
|
||||||
|
/// stores global scope parameters, options, option definitions.
|
||||||
|
Subnet6ConfigParser();
|
||||||
|
|
||||||
|
/// @brief Parses a single IPv6 subnet configuration and adds to the
|
||||||
|
/// Configuration Manager.
|
||||||
|
///
|
||||||
|
/// @param subnet A new subnet being configured.
|
||||||
|
/// @return a pointer to created Subnet6 object
|
||||||
|
Subnet6Ptr parse(data::ConstElementPtr subnet);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
|
||||||
|
/// options.
|
||||||
|
///
|
||||||
|
/// @param code is the numeric option code of the duplicate option
|
||||||
|
/// @param addr is the subnet address
|
||||||
|
/// @todo A means to know the correct logger and perhaps a common
|
||||||
|
/// message would allow this message to be emitted by the base class.
|
||||||
|
virtual void duplicate_option_warning(uint32_t code,
|
||||||
|
asiolink::IOAddress& addr);
|
||||||
|
|
||||||
|
/// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
|
||||||
|
/// and prefix length.
|
||||||
|
///
|
||||||
|
/// @param params Data structure describing a subnet.
|
||||||
|
/// @param addr is IPv6 prefix of the subnet.
|
||||||
|
/// @param len is the prefix length
|
||||||
|
void initSubnet(isc::data::ConstElementPtr params,
|
||||||
|
isc::asiolink::IOAddress addr, uint8_t len);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief this class parses a list of DHCP6 subnets
|
||||||
|
///
|
||||||
|
/// This is a wrapper parser that handles the whole list of Subnet6
|
||||||
|
/// definitions. It iterates over all entries and creates Subnet6ConfigParser
|
||||||
|
/// for each entry.
|
||||||
|
class Subnets6ListConfigParser : public isc::data::SimpleParser {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief parses contents of the list
|
||||||
|
///
|
||||||
|
/// Iterates over all entries on the list, parses its content
|
||||||
|
/// (by instantiating Subnet6ConfigParser) and adds to specified
|
||||||
|
/// configuration.
|
||||||
|
///
|
||||||
|
/// @param cfg configuration (parsed subnets will be stored here)
|
||||||
|
/// @param subnets_list pointer to a list of IPv6 subnets
|
||||||
|
/// @throw DhcpConfigError if CfgMgr rejects the subnet (e.g. subnet-id is a duplicate)
|
||||||
|
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list);
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Parser for D2ClientConfig
|
/// @brief Parser for D2ClientConfig
|
||||||
///
|
///
|
||||||
/// This class parses the configuration element "dhcp-ddns" common to the
|
/// This class parses the configuration element "dhcp-ddns" common to the
|
||||||
|
@@ -7,8 +7,8 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <asiolink/io_address.h>
|
#include <asiolink/io_address.h>
|
||||||
#include <dhcpsrv/cfgmgr.h>
|
#include <dhcpsrv/cfgmgr.h>
|
||||||
#include <dhcpsrv/parsers/dhcp_parsers.h>
|
|
||||||
#include <dhcpsrv/parsers/host_reservation_parser.h>
|
#include <dhcpsrv/parsers/host_reservation_parser.h>
|
||||||
|
#include <dhcpsrv/parsers/option_data_parser.h>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -113,7 +113,7 @@ HostReservationParser::parseInternal(const SubnetID&,
|
|||||||
try {
|
try {
|
||||||
// Gather those parameters that are common for both IPv4 and IPv6
|
// Gather those parameters that are common for both IPv4 and IPv6
|
||||||
// reservations.
|
// reservations.
|
||||||
BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
|
BOOST_FOREACH(auto element, reservation_data->mapValue()) {
|
||||||
// Check if we support this parameter.
|
// Check if we support this parameter.
|
||||||
if (!isSupportedParameter(element.first)) {
|
if (!isSupportedParameter(element.first)) {
|
||||||
isc_throw(DhcpConfigError, "unsupported configuration"
|
isc_throw(DhcpConfigError, "unsupported configuration"
|
||||||
@@ -183,7 +183,7 @@ HostReservationParser4::parseInternal(const SubnetID& subnet_id,
|
|||||||
|
|
||||||
host->setIPv4SubnetID(subnet_id);
|
host->setIPv4SubnetID(subnet_id);
|
||||||
|
|
||||||
BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
|
BOOST_FOREACH(auto element, reservation_data->mapValue()) {
|
||||||
// For 'option-data' element we will use another parser which
|
// For 'option-data' element we will use another parser which
|
||||||
// already returns errors with position appended, so don't
|
// already returns errors with position appended, so don't
|
||||||
// surround it with try-catch.
|
// surround it with try-catch.
|
||||||
@@ -242,7 +242,7 @@ HostReservationParser6::parseInternal(const SubnetID& subnet_id,
|
|||||||
|
|
||||||
host->setIPv6SubnetID(subnet_id);
|
host->setIPv6SubnetID(subnet_id);
|
||||||
|
|
||||||
BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
|
BOOST_FOREACH(auto element, reservation_data->mapValue()) {
|
||||||
// Parse option values. Note that the configuration option parser
|
// Parse option values. Note that the configuration option parser
|
||||||
// returns errors with position information appended, so there is no
|
// returns errors with position information appended, so there is no
|
||||||
// need to surround it with try-clause (and rethrow with position
|
// need to surround it with try-clause (and rethrow with position
|
||||||
|
348
src/lib/dhcpsrv/parsers/option_data_parser.cc
Normal file
348
src/lib/dhcpsrv/parsers/option_data_parser.cc
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
|
#include <dhcp/libdhcp++.h>
|
||||||
|
#include <dhcp/option_definition.h>
|
||||||
|
#include <dhcp/option_space.h>
|
||||||
|
#include <dhcpsrv/cfgmgr.h>
|
||||||
|
#include <dhcpsrv/parsers/option_data_parser.h>
|
||||||
|
#include <util/encode/hex.h>
|
||||||
|
#include <util/strutil.h>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace isc::data;
|
||||||
|
using namespace isc::util;
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace dhcp {
|
||||||
|
|
||||||
|
// **************************** OptionDataParser *************************
|
||||||
|
|
||||||
|
OptionDataParser::OptionDataParser(const uint16_t address_family)
|
||||||
|
: address_family_(address_family) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<OptionDescriptor, std::string>
|
||||||
|
OptionDataParser::parse(isc::data::ConstElementPtr single_option) {
|
||||||
|
|
||||||
|
// Try to create the option instance.
|
||||||
|
std::pair<OptionDescriptor, std::string> opt = createOption(single_option);
|
||||||
|
|
||||||
|
if (!opt.first.option_) {
|
||||||
|
isc_throw(isc::InvalidOperation,
|
||||||
|
"parser logic error: no option has been configured and"
|
||||||
|
" thus there is nothing to commit. Has build() been called?");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalValue<uint32_t>
|
||||||
|
OptionDataParser::extractCode(ConstElementPtr parent) const {
|
||||||
|
uint32_t code;
|
||||||
|
try {
|
||||||
|
code = getInteger(parent, "code");
|
||||||
|
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
// The code parameter was not found. Return an unspecified
|
||||||
|
// value.
|
||||||
|
return (OptionalValue<uint32_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == 0) {
|
||||||
|
isc_throw(DhcpConfigError, "option code must not be zero "
|
||||||
|
"(" << getPosition("code", parent) << ")");
|
||||||
|
|
||||||
|
} else if (address_family_ == AF_INET &&
|
||||||
|
code > std::numeric_limits<uint8_t>::max()) {
|
||||||
|
isc_throw(DhcpConfigError, "invalid option code '" << code
|
||||||
|
<< "', it must not be greater than '"
|
||||||
|
<< static_cast<int>(std::numeric_limits<uint8_t>::max())
|
||||||
|
<< "' (" << getPosition("code", parent)
|
||||||
|
<< ")");
|
||||||
|
|
||||||
|
} else if (address_family_ == AF_INET6 &&
|
||||||
|
code > std::numeric_limits<uint16_t>::max()) {
|
||||||
|
isc_throw(DhcpConfigError, "invalid option code '" << code
|
||||||
|
<< "', it must not exceed '"
|
||||||
|
<< std::numeric_limits<uint16_t>::max()
|
||||||
|
<< "' (" << getPosition("code", parent)
|
||||||
|
<< ")");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return (OptionalValue<uint32_t>(code, OptionalValueState(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalValue<std::string>
|
||||||
|
OptionDataParser::extractName(ConstElementPtr parent) const {
|
||||||
|
std::string name;
|
||||||
|
try {
|
||||||
|
name = getString(parent, "name");
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
return (OptionalValue<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.find(" ") != std::string::npos) {
|
||||||
|
isc_throw(DhcpConfigError, "invalid option name '" << name
|
||||||
|
<< "', space character is not allowed ("
|
||||||
|
<< getPosition("name", parent) << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (OptionalValue<std::string>(name, OptionalValueState(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
OptionDataParser::extractData(ConstElementPtr parent) const {
|
||||||
|
std::string data;
|
||||||
|
try {
|
||||||
|
data = getString(parent, "data");
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
// The "data" parameter was not found. Return an empty value.
|
||||||
|
return (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalValue<bool>
|
||||||
|
OptionDataParser::extractCSVFormat(ConstElementPtr parent) const {
|
||||||
|
bool csv_format = true;
|
||||||
|
try {
|
||||||
|
csv_format = getBoolean(parent, "csv-format");
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
return (OptionalValue<bool>(csv_format));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (OptionalValue<bool>(csv_format, OptionalValueState(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
OptionDataParser::extractSpace(ConstElementPtr parent) const {
|
||||||
|
std::string space = address_family_ == AF_INET ?
|
||||||
|
DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
|
||||||
|
try {
|
||||||
|
space = getString(parent, "space");
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
return (space);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!OptionSpace::validateName(space)) {
|
||||||
|
isc_throw(DhcpConfigError, "invalid option space name '"
|
||||||
|
<< space << "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((space == DHCP4_OPTION_SPACE) && (address_family_ == AF_INET6)) {
|
||||||
|
isc_throw(DhcpConfigError, "'" << DHCP4_OPTION_SPACE
|
||||||
|
<< "' option space name is reserved for DHCPv4 server");
|
||||||
|
|
||||||
|
} else if ((space == DHCP6_OPTION_SPACE) &&
|
||||||
|
(address_family_ == AF_INET)) {
|
||||||
|
isc_throw(DhcpConfigError, "'" << DHCP6_OPTION_SPACE
|
||||||
|
<< "' option space name is reserved for DHCPv6 server");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
// Append position of the option space parameter.
|
||||||
|
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||||
|
<< getPosition("space", parent) << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (space);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SearchKey>
|
||||||
|
OptionDefinitionPtr
|
||||||
|
OptionDataParser::findOptionDefinition(const std::string& option_space,
|
||||||
|
const SearchKey& search_key) const {
|
||||||
|
OptionDefinitionPtr def = LibDHCP::getOptionDef(option_space, search_key);
|
||||||
|
|
||||||
|
if (!def) {
|
||||||
|
// Check if this is a vendor-option. If it is, get vendor-specific
|
||||||
|
// definition.
|
||||||
|
uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
|
||||||
|
if (vendor_id) {
|
||||||
|
const Option::Universe u = address_family_ == AF_INET ?
|
||||||
|
Option::V4 : Option::V6;
|
||||||
|
def = LibDHCP::getVendorOptionDef(u, vendor_id, search_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!def) {
|
||||||
|
// Check if this is an option specified by a user.
|
||||||
|
def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()
|
||||||
|
->get(option_space, search_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (def);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<OptionDescriptor, std::string>
|
||||||
|
OptionDataParser::createOption(ConstElementPtr option_data) {
|
||||||
|
const Option::Universe universe = address_family_ == AF_INET ?
|
||||||
|
Option::V4 : Option::V6;
|
||||||
|
|
||||||
|
OptionalValue<uint32_t> code_param = extractCode(option_data);
|
||||||
|
OptionalValue<std::string> name_param = extractName(option_data);
|
||||||
|
OptionalValue<bool> csv_format_param = extractCSVFormat(option_data);
|
||||||
|
std::string data_param = extractData(option_data);
|
||||||
|
std::string space_param = extractSpace(option_data);
|
||||||
|
|
||||||
|
// Require that option code or option name is specified.
|
||||||
|
if (!code_param.isSpecified() && !name_param.isSpecified()) {
|
||||||
|
isc_throw(DhcpConfigError, "option data configuration requires one of"
|
||||||
|
" 'code' or 'name' parameters to be specified"
|
||||||
|
<< " (" << option_data->getPosition() << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find a corresponding option definition using option code or
|
||||||
|
// option name.
|
||||||
|
OptionDefinitionPtr def = code_param.isSpecified() ?
|
||||||
|
findOptionDefinition(space_param, code_param) :
|
||||||
|
findOptionDefinition(space_param, name_param);
|
||||||
|
|
||||||
|
// If there is no definition, the user must not explicitly enable the
|
||||||
|
// use of csv-format.
|
||||||
|
if (!def) {
|
||||||
|
// If explicitly requested that the CSV format is to be used,
|
||||||
|
// the option definition is a must.
|
||||||
|
if (csv_format_param.isSpecified() && csv_format_param) {
|
||||||
|
isc_throw(DhcpConfigError, "definition for the option '"
|
||||||
|
<< space_param << "." << name_param
|
||||||
|
<< "' having code '" << code_param
|
||||||
|
<< "' does not exist ("
|
||||||
|
<< getPosition("name", option_data)
|
||||||
|
<< ")");
|
||||||
|
|
||||||
|
// If there is no option definition and the option code is not specified
|
||||||
|
// we have no means to find the option code.
|
||||||
|
} else if (name_param.isSpecified() && !code_param.isSpecified()) {
|
||||||
|
isc_throw(DhcpConfigError, "definition for the option '"
|
||||||
|
<< space_param << "." << name_param
|
||||||
|
<< "' does not exist ("
|
||||||
|
<< getPosition("name", option_data)
|
||||||
|
<< ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform string of hexadecimal digits into binary format.
|
||||||
|
std::vector<uint8_t> binary;
|
||||||
|
std::vector<std::string> data_tokens;
|
||||||
|
|
||||||
|
// If the definition is available and csv-format hasn't been explicitly
|
||||||
|
// disabled, we will parse the data as comma separated values.
|
||||||
|
if (def && (!csv_format_param.isSpecified() || csv_format_param)) {
|
||||||
|
// If the option data is specified as a string of comma
|
||||||
|
// separated values then we need to split this string into
|
||||||
|
// individual values - each value will be used to initialize
|
||||||
|
// one data field of an option.
|
||||||
|
// It is the only usage of the escape option: this allows
|
||||||
|
// to embed commas in individual values and to return
|
||||||
|
// for instance a string value with embedded commas.
|
||||||
|
data_tokens = isc::util::str::tokens(data_param, ",", true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Otherwise, the option data is specified as a string of
|
||||||
|
// hexadecimal digits that we have to turn into binary format.
|
||||||
|
try {
|
||||||
|
// The decodeHex function expects that the string contains an
|
||||||
|
// even number of digits. If we don't meet this requirement,
|
||||||
|
// we have to insert a leading 0.
|
||||||
|
if (!data_param.empty() && ((data_param.length() % 2) != 0)) {
|
||||||
|
data_param = data_param.insert(0, "0");
|
||||||
|
}
|
||||||
|
util::encode::decodeHex(data_param, binary);
|
||||||
|
} catch (...) {
|
||||||
|
isc_throw(DhcpConfigError, "option data is not a valid"
|
||||||
|
<< " string of hexadecimal digits: " << data_param
|
||||||
|
<< " ("
|
||||||
|
<< getPosition("data", option_data)
|
||||||
|
<< ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionPtr option;
|
||||||
|
OptionDescriptor desc(false);
|
||||||
|
|
||||||
|
if (!def) {
|
||||||
|
// @todo We have a limited set of option definitions initalized at
|
||||||
|
// the moment. In the future we want to initialize option definitions
|
||||||
|
// for all options. Consequently an error will be issued if an option
|
||||||
|
// definition does not exist for a particular option code. For now it is
|
||||||
|
// ok to create generic option if definition does not exist.
|
||||||
|
OptionPtr option(new Option(universe, static_cast<uint16_t>(code_param),
|
||||||
|
binary));
|
||||||
|
|
||||||
|
desc.option_ = option;
|
||||||
|
desc.persistent_ = false;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Option name is specified it should match the name in the definition.
|
||||||
|
if (name_param.isSpecified() && (def->getName() != name_param.get())) {
|
||||||
|
isc_throw(DhcpConfigError, "specified option name '"
|
||||||
|
<< name_param << "' does not match the "
|
||||||
|
<< "option definition: '" << space_param
|
||||||
|
<< "." << def->getName() << "' ("
|
||||||
|
<< getPosition("name", option_data)
|
||||||
|
<< ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option definition has been found so let's use it to create
|
||||||
|
// an instance of our option.
|
||||||
|
try {
|
||||||
|
bool use_csv = !csv_format_param.isSpecified() || csv_format_param;
|
||||||
|
OptionPtr option = use_csv ?
|
||||||
|
def->optionFactory(universe, def->getCode(), data_tokens) :
|
||||||
|
def->optionFactory(universe, def->getCode(), binary);
|
||||||
|
desc.option_ = option;
|
||||||
|
desc.persistent_ = false;
|
||||||
|
if (use_csv) {
|
||||||
|
desc.formatted_value_ = data_param;
|
||||||
|
}
|
||||||
|
} catch (const isc::Exception& ex) {
|
||||||
|
isc_throw(DhcpConfigError, "option data does not match"
|
||||||
|
<< " option definition (space: " << space_param
|
||||||
|
<< ", code: " << def->getCode() << "): "
|
||||||
|
<< ex.what() << " ("
|
||||||
|
<< getPosition("data", option_data)
|
||||||
|
<< ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All went good, so we can set the option space name.
|
||||||
|
return make_pair(desc, space_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// **************************** OptionDataListParser *************************
|
||||||
|
OptionDataListParser::OptionDataListParser(//const std::string&,
|
||||||
|
//const CfgOptionPtr& cfg,
|
||||||
|
const uint16_t address_family)
|
||||||
|
: address_family_(address_family) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OptionDataListParser::parse(const CfgOptionPtr& cfg,
|
||||||
|
isc::data::ConstElementPtr option_data_list) {
|
||||||
|
OptionDataParser option_parser(address_family_);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of namespace isc::dhcp
|
||||||
|
} // end of namespace isc
|
177
src/lib/dhcpsrv/parsers/option_data_parser.h
Normal file
177
src/lib/dhcpsrv/parsers/option_data_parser.h
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#ifndef OPTION_DATA_PARSER_H
|
||||||
|
#define OPTION_DATA_PARSER_H
|
||||||
|
|
||||||
|
#include <cc/data.h>
|
||||||
|
#include <cc/simple_parser.h>
|
||||||
|
#include <dhcpsrv/cfg_option.h>
|
||||||
|
#include <util/optional_value.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace dhcp {
|
||||||
|
|
||||||
|
/// @brief Parser for option data value.
|
||||||
|
///
|
||||||
|
/// This parser parses configuration entries that specify value of
|
||||||
|
/// a single option. These entries include option name, option code
|
||||||
|
/// and data carried by the option. The option data can be specified
|
||||||
|
/// in one of the two available formats: binary value represented as
|
||||||
|
/// a string of hexadecimal digits or a list of comma separated values.
|
||||||
|
/// The format being used is controlled by csv-format configuration
|
||||||
|
/// parameter. When setting this value to True, the latter format is
|
||||||
|
/// used. The subsequent values in the CSV format apply to relevant
|
||||||
|
/// option data fields in the configured option. For example the
|
||||||
|
/// configuration: "data" : "192.168.2.0, 56, hello world" can be
|
||||||
|
/// used to set values for the option comprising IPv4 address,
|
||||||
|
/// integer and string data field. Note that order matters. If the
|
||||||
|
/// order of values does not match the order of data fields within
|
||||||
|
/// an option the configuration will not be accepted. If parsing
|
||||||
|
/// is successful then an instance of an option is created and
|
||||||
|
/// added to the storage provided by the calling class.
|
||||||
|
class OptionDataParser : public isc::data::SimpleParser {
|
||||||
|
public:
|
||||||
|
/// @brief Constructor.
|
||||||
|
///
|
||||||
|
/// @param address_family Address family: @c AF_INET or @c AF_INET6.
|
||||||
|
explicit OptionDataParser(const uint16_t address_family);
|
||||||
|
|
||||||
|
/// @brief Parses ElementPtr containing option definition
|
||||||
|
///
|
||||||
|
/// This method parses ElementPtr containing the option definition,
|
||||||
|
/// instantiates the option for it and then returns a pair
|
||||||
|
/// of option descriptor (that holds that new option) and
|
||||||
|
/// a string that specifies the option space.
|
||||||
|
///
|
||||||
|
/// Note: ElementPtr is expected to contain all fields. If your
|
||||||
|
/// ElementPtr does not have them, please use
|
||||||
|
/// @ref isc::data::SimpleParser::setDefaults to fill the missing fields
|
||||||
|
/// with default values.
|
||||||
|
///
|
||||||
|
/// @param single_option ElementPtr containing option definition
|
||||||
|
/// @return Option object wrapped in option description and an option
|
||||||
|
/// space
|
||||||
|
std::pair<OptionDescriptor, std::string>
|
||||||
|
parse(isc::data::ConstElementPtr single_option);
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief Finds an option definition within an option space
|
||||||
|
///
|
||||||
|
/// Given an option space and an option code, find the corresponding
|
||||||
|
/// option definition within the option definition storage.
|
||||||
|
///
|
||||||
|
/// @param option_space name of the parameter option space
|
||||||
|
/// @param search_key an option code or name to be used to lookup the
|
||||||
|
/// option definition.
|
||||||
|
/// @tparam A numeric type for searching using an option code or the
|
||||||
|
/// string for searching using the option name.
|
||||||
|
///
|
||||||
|
/// @return OptionDefinitionPtr of the option definition or an
|
||||||
|
/// empty OptionDefinitionPtr if not found.
|
||||||
|
/// @throw DhcpConfigError if the option space requested is not valid
|
||||||
|
/// for this server.
|
||||||
|
template<typename SearchKey>
|
||||||
|
OptionDefinitionPtr findOptionDefinition(const std::string& option_space,
|
||||||
|
const SearchKey& search_key) const;
|
||||||
|
|
||||||
|
/// @brief Create option instance.
|
||||||
|
///
|
||||||
|
/// Creates an instance of an option and adds it to the provided
|
||||||
|
/// options storage. If the option data parsed by \ref build function
|
||||||
|
/// are invalid or insufficient this function emits an exception.
|
||||||
|
///
|
||||||
|
/// @param option_data An element holding data for a single option being
|
||||||
|
/// created.
|
||||||
|
///
|
||||||
|
/// @return created option descriptor
|
||||||
|
///
|
||||||
|
/// @throw DhcpConfigError if parameters provided in the configuration
|
||||||
|
/// are invalid.
|
||||||
|
std::pair<OptionDescriptor, std::string>
|
||||||
|
createOption(isc::data::ConstElementPtr option_data);
|
||||||
|
|
||||||
|
/// @brief Retrieves parsed option code as an optional value.
|
||||||
|
///
|
||||||
|
/// @param parent A data element holding full option data configuration.
|
||||||
|
///
|
||||||
|
/// @return Option code, possibly unspecified.
|
||||||
|
/// @throw DhcpConfigError if option code is invalid.
|
||||||
|
util::OptionalValue<uint32_t>
|
||||||
|
extractCode(data::ConstElementPtr parent) const;
|
||||||
|
|
||||||
|
/// @brief Retrieves parsed option name as an optional value.
|
||||||
|
///
|
||||||
|
/// @param parent A data element holding full option data configuration.
|
||||||
|
///
|
||||||
|
/// @return Option name, possibly unspecified.
|
||||||
|
/// @throw DhcpConfigError if option name is invalid.
|
||||||
|
util::OptionalValue<std::string>
|
||||||
|
extractName(data::ConstElementPtr parent) const;
|
||||||
|
|
||||||
|
/// @brief Retrieves csv-format parameter as an optional value.
|
||||||
|
///
|
||||||
|
/// @return Value of the csv-format parameter, possibly unspecified.
|
||||||
|
util::OptionalValue<bool> extractCSVFormat(data::ConstElementPtr parent) const;
|
||||||
|
|
||||||
|
/// @brief Retrieves option data as a string.
|
||||||
|
///
|
||||||
|
/// @param parent A data element holding full option data configuration.
|
||||||
|
/// @return Option data as a string. It will return empty string if
|
||||||
|
/// option data is unspecified.
|
||||||
|
std::string extractData(data::ConstElementPtr parent) const;
|
||||||
|
|
||||||
|
/// @brief Retrieves option space name.
|
||||||
|
///
|
||||||
|
/// If option space name is not specified in the configuration the
|
||||||
|
/// 'dhcp4' or 'dhcp6' option space name is returned, depending on
|
||||||
|
/// the universe specified in the parser context.
|
||||||
|
///
|
||||||
|
/// @param parent A data element holding full option data configuration.
|
||||||
|
///
|
||||||
|
/// @return Option space name.
|
||||||
|
std::string extractSpace(data::ConstElementPtr parent) const;
|
||||||
|
|
||||||
|
/// @brief Address family: @c AF_INET or @c AF_INET6.
|
||||||
|
uint16_t address_family_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Parser for option data values within a subnet.
|
||||||
|
///
|
||||||
|
/// This parser iterates over all entries that define options
|
||||||
|
/// data for a particular subnet and creates a collection of options.
|
||||||
|
/// If parsing is successful, all these options are added to the Subnet
|
||||||
|
/// object.
|
||||||
|
class OptionDataListParser : public isc::data::SimpleParser {
|
||||||
|
public:
|
||||||
|
/// @brief Constructor.
|
||||||
|
///
|
||||||
|
/// @param address_family Address family: @c AF_INET or AF_INET6
|
||||||
|
explicit OptionDataListParser(const uint16_t address_family);
|
||||||
|
|
||||||
|
/// @brief Parses a list of options, instantiates them and stores in cfg
|
||||||
|
///
|
||||||
|
/// This method expects to get a list of options in option_data_list,
|
||||||
|
/// iterates over them, creates option objects, wraps them with
|
||||||
|
/// option descriptor and stores in specified cfg.
|
||||||
|
///
|
||||||
|
/// @param cfg created options will be stored here
|
||||||
|
/// @param option_data_list configuration that describes the options
|
||||||
|
void parse(const CfgOptionPtr& cfg,
|
||||||
|
isc::data::ConstElementPtr option_data_list);
|
||||||
|
private:
|
||||||
|
/// @brief Address family: @c AF_INET or @c AF_INET6
|
||||||
|
uint16_t address_family_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // end of namespace isc::dhcp
|
||||||
|
} // end of namespace isc
|
||||||
|
|
||||||
|
#endif // OPTION_DATA_PARSER_H
|
@@ -18,6 +18,7 @@
|
|||||||
#include <dhcpsrv/subnet.h>
|
#include <dhcpsrv/subnet.h>
|
||||||
#include <dhcpsrv/cfg_mac_source.h>
|
#include <dhcpsrv/cfg_mac_source.h>
|
||||||
#include <dhcpsrv/parsers/dhcp_parsers.h>
|
#include <dhcpsrv/parsers/dhcp_parsers.h>
|
||||||
|
#include <dhcpsrv/parsers/option_data_parser.h>
|
||||||
#include <dhcpsrv/tests/test_libraries.h>
|
#include <dhcpsrv/tests/test_libraries.h>
|
||||||
#include <dhcpsrv/testutils/config_result_check.h>
|
#include <dhcpsrv/testutils/config_result_check.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
Reference in New Issue
Block a user