Browse Source

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

Marcin Siodelski 7 years ago
parent
commit
0a61518acf

+ 1 - 267
src/bin/dhcp4/json_config_parser.cc

@@ -21,6 +21,7 @@
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservations_list_parser.h>
 #include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <dhcpsrv/timer_mgr.h>
 #include <hooks/hooks_parser.h>
 #include <config/command_mgr.h>
@@ -46,273 +47,6 @@ using namespace isc::hooks;
 
 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.
 ///
 /// See @ref parse method for a list of supported parameters.

+ 0 - 9
src/bin/dhcp6/dhcp6_messages.mes

@@ -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
 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
 A debug message listing the configuration received by the DHCPv6 server.
 The source of that configuration depends on used configuration backend.

+ 1 - 385
src/bin/dhcp6/json_config_parser.cc

@@ -30,6 +30,7 @@
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservations_list_parser.h>
 #include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <hooks/hooks_parser.h>
 #include <log/logger_support.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<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
 ///
 /// This parser handles a Dhcp6/relay-supplied-options entry. It contains a

+ 3 - 0
src/lib/dhcpsrv/Makefile.am

@@ -43,6 +43,7 @@ EXTRA_DIST += parsers/host_reservation_parser.h
 EXTRA_DIST += parsers/host_reservations_list_parser.h
 EXTRA_DIST += parsers/ifaces_config_parser.cc
 EXTRA_DIST += parsers/ifaces_config_parser.h
+EXTRA_DIST += parsers/option_data_parser.h
 
 # Devel guide diagrams
 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/ifaces_config_parser.cc
 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.cc

+ 13 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -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
 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
 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
 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
 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
@@ -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
 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
 This warning message is logged when the user specified that the
 DHCPv4 server should use the raw sockets to receive the DHCP

+ 1 - 0
src/lib/dhcpsrv/parsers/client_class_def_parser.cc

@@ -9,6 +9,7 @@
 #include <dhcpsrv/client_class_def.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/client_class_def_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <eval/eval_context.h>
 #include <asiolink/io_address.h>
 #include <asiolink/io_error.h>

+ 461 - 0
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -10,7 +10,11 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/dhcpsrv_log.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 <util/encode/hex.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(uint16_t family)
@@ -905,6 +931,441 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) {
     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 **********************
 
 dhcp_ddns::NameChangeProtocol

+ 274 - 1
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -341,7 +341,6 @@ public:
     void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value);
 };
 
-
 /// @brief Parser for option data value.
 ///
 /// This parser parses configuration entries that specify value of
@@ -590,6 +589,37 @@ protected:
                               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
 ///
 /// 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;
 };
 
+/// @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
 ///
 /// This concrete parser handles RelayInfo structure definitions.
@@ -735,6 +779,235 @@ protected:
     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
 ///
 /// This class parses the configuration element "dhcp-ddns" common to the

+ 4 - 4
src/lib/dhcpsrv/parsers/host_reservation_parser.cc

@@ -7,8 +7,8 @@
 #include <config.h>
 #include <asiolink/io_address.h>
 #include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/host_reservation_parser.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <algorithm>
@@ -113,7 +113,7 @@ HostReservationParser::parseInternal(const SubnetID&,
     try {
         // Gather those parameters that are common for both IPv4 and IPv6
         // reservations.
-        BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
+        BOOST_FOREACH(auto element, reservation_data->mapValue()) {
             // Check if we support this parameter.
             if (!isSupportedParameter(element.first)) {
                 isc_throw(DhcpConfigError, "unsupported configuration"
@@ -183,7 +183,7 @@ HostReservationParser4::parseInternal(const SubnetID& 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
         // already returns errors with position appended, so don't
         // surround it with try-catch.
@@ -242,7 +242,7 @@ HostReservationParser6::parseInternal(const SubnetID& 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
         // returns errors with position information appended, so there is no
         // need to surround it with try-clause (and rethrow with position

+ 348 - 0
src/lib/dhcpsrv/parsers/option_data_parser.cc

@@ -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 - 0
src/lib/dhcpsrv/parsers/option_data_parser.h

@@ -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

+ 1 - 0
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -18,6 +18,7 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/cfg_mac_source.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/option_data_parser.h>
 #include <dhcpsrv/tests/test_libraries.h>
 #include <dhcpsrv/testutils/config_result_check.h>
 #include <exceptions/exceptions.h>