Commit 0a61518a authored by Marcin Siodelski's avatar Marcin Siodelski Committed by Tomek Mrugalski

[5315] Moved Subnet parsers from DHCP binaries to libdhcpsrv.

parent 433313ef
...@@ -21,6 +21,7 @@ ...@@ -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; ...@@ -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 ...@@ -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 @@ ...@@ -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; ...@@ -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);