json_config_parser.cc 35.3 KB
Newer Older
1
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15 16
#include <config.h>

17
#include <asiolink/io_address.h>
18
#include <cc/data.h>
19
#include <config/ccsession.h>
20
#include <dhcp/libdhcp++.h>
21
#include <dhcp6/json_config_parser.h>
22
#include <dhcp6/dhcp6_log.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
23
#include <dhcp/iface_mgr.h>
24
#include <dhcpsrv/cfg_option.h>
25 26 27 28
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/triplet.h>
29 30 31
#include <dhcpsrv/parsers/dbaccess_parser.h>
#include <dhcpsrv/parsers/dhcp_config_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
32 33
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
34
#include <dhcpsrv/parsers/ifaces_config_parser.h>
35 36
#include <log/logger_support.h>
#include <util/encode/hex.h>
37
#include <util/strutil.h>
38 39 40 41 42 43 44 45

#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>

#include <iostream>
46
#include <limits>
47 48 49 50
#include <map>
#include <vector>

#include <stdint.h>
51 52

using namespace std;
53
using namespace isc;
54
using namespace isc::data;
55
using namespace isc::dhcp;
56 57
using namespace isc::asiolink;

58
namespace {
59

60 61 62 63 64
// Pointers to various parser objects.
typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;

65
/// @brief Parser for IPv6 pool definitions.
66
///
67 68 69
/// 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
70
/// PoolStorage container.
71
///
72 73
/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
class Pool6Parser : public PoolParser {
74 75 76 77
public:

    /// @brief Constructor.
    ///
78 79 80 81 82 83
    /// @param param_name name of the parameter. Note, it is passed through
    /// but unused, parameter is currently always "Dhcp6/subnet6[X]/pool"
    /// @param pools storage container in which to store the parsed pool
    /// upon "commit"
    Pool6Parser(const std::string& param_name,  PoolStoragePtr pools)
        :PoolParser(param_name, pools) {
84
    }
85

86 87
protected:
    /// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
88
    ///
89 90
    /// @param addr is the IPv6 prefix of the pool.
    /// @param len is the prefix length.
91 92
    /// @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
93
    /// polymorphic interface.
94
    /// @return returns a PoolPtr to the new Pool4 object.
95 96
    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
    {
97
        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
98
                                  (ptype), addr, len)));
99 100
    }

101
    /// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
102
    ///
103 104
    /// @param min is the first IPv6 address in the pool.
    /// @param max is the last IPv6 address in the pool.
105 106
    /// @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
107
    /// polymorphic interface.
108
    /// @return returns a PoolPtr to the new Pool4 object.
109 110
    PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
    {
111
        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
112
                                  (ptype), min, max)));
113 114 115
    }
};

116 117 118 119 120 121 122 123 124 125 126 127
class Pools6ListParser : public PoolsListParser {
public:
    Pools6ListParser(const std::string& dummy, PoolStoragePtr pools)
        :PoolsListParser(dummy, pools) {
    }

protected:
    virtual ParserPtr poolParserMaker(PoolStoragePtr storage) {
        return (ParserPtr(new Pool6Parser("pool", storage)));
    }
};

128 129 130 131 132 133 134 135
/// @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 defintions currently support three elements: prefix, prefix-len,
/// and delegated-len, as shown in the example JSON text below:
///
136
/// @code
137 138 139 140 141 142
///
/// {
///     "prefix": "2001:db8:1::",
///     "prefix-len": 64,
///     "delegated-len": 128
/// }
143 144
/// @endcode
///
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
class PdPoolParser : public DhcpConfigParser {
public:

    /// @brief Constructor.
    ///
    /// @param param_name name of the parameter. Note, it is passed through
    /// but unused, parameter is currently always "Dhcp6/subnet6[X]/pool"
    /// @param pools storage container in which to store the parsed pool
    /// upon "commit"
    PdPoolParser(const std::string&,  PoolStoragePtr pools)
        : uint32_values_(new Uint32Storage()),
          string_values_(new StringStorage()), pools_(pools) {
        if (!pools_) {
            isc_throw(isc::dhcp::DhcpConfigError,
                      "PdPoolParser context storage may not be NULL");
        }
    }

    /// @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 pd_pool_ pointer to an element that holds configuration entries
    /// that define a prefix delegation pool.
    ///
    /// @throw DhcpConfigError if configuration parsing fails.
    virtual void build(ConstElementPtr pd_pool_) {
        // Parse the elements that make up the option definition.
        BOOST_FOREACH(ConfigPair param, pd_pool_->mapValue()) {
            std::string entry(param.first);
            ParserPtr parser;
            if (entry == "prefix") {
                StringParserPtr str_parser(new StringParser(entry,
                                                            string_values_));
                parser = str_parser;
            } else if (entry == "prefix-len" || entry == "delegated-len") {
                Uint32ParserPtr code_parser(new Uint32Parser(entry,
                                                             uint32_values_));
                parser = code_parser;
            } else {
186 187
                isc_throw(DhcpConfigError, "unsupported parameter: " << entry
                          << " (" << param.second->getPosition() << ")");
188 189 190 191 192 193
            }

            parser->build(param.second);
            parser->commit();
        }

194 195 196 197 198
        // Try to obtain the pool parameters. It will throw an exception if any
        // of the required parameters are not present or invalid.
        std::string addr_str;
        uint32_t prefix_len;
        uint32_t delegated_len;
199
        try {
200 201 202
            addr_str = string_values_->getParam("prefix");
            prefix_len = uint32_values_->getParam("prefix-len");
            delegated_len = uint32_values_->getParam("delegated-len");
203 204

            // Attempt to construct the local pool.
205 206
            pool_.reset(new Pool6(Lease::TYPE_PD, IOAddress(addr_str),
                                  prefix_len, delegated_len));
207
        } catch (const std::exception& ex) {
208 209 210 211 212
            // 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() << ")");
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
        }
    }

    // @brief Commits the constructed local pool to the pool storage.
    virtual void commit() {
        // Add the local pool to the external storage ptr.
        pools_->push_back(pool_);
    }

protected:
    /// Storage for subnet-specific integer values.
    Uint32StoragePtr uint32_values_;

    /// Storage for subnet-specific string values.
    StringStoragePtr string_values_;

    /// Parsers are stored here.
    ParserCollection parsers_;

    /// Pointer to the created pool object.
    isc::dhcp::Pool6Ptr pool_;

    /// Pointer to storage to which the local pool is written upon commit.
    isc::dhcp::PoolStoragePtr pools_;
};

/// @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 PdPoolListParser : public DhcpConfigParser {
public:
    /// @brief Constructor.
    ///
    /// @param dummy first argument is ignored, all Parser constructors
    /// accept string as first argument.
    /// @param storage is the pool storage in which to store the parsed
    /// pools in this list
    /// @throw isc::dhcp::DhcpConfigError if storage is null.
    PdPoolListParser(const std::string&, PoolStoragePtr pools)
        : local_pools_(new PoolStorage()), pools_(pools) {
        if (!pools_) {
            isc_throw(isc::dhcp::DhcpConfigError,
                      "PdPoolListParser pools storage may not be NULL");
        }
    }

    /// @brief Parse configuration entries.
    ///
    /// This function parses configuration entries and creates instances
    /// of prefix delegation pools .
    ///
    /// @param pd_pool_list pointer to an element that holds entries
    /// that define a prefix delegation pool.
    ///
    /// @throw DhcpConfigError if configuration parsing fails.
    void build(isc::data::ConstElementPtr pd_pool_list) {
        // Make sure the local list is empty.
        local_pools_.reset(new PoolStorage());

        // Make sure we have a configuration elements to parse.
        if (!pd_pool_list) {
            isc_throw(DhcpConfigError,
277
                      "PdPoolListParser: list of pool definitions is NULL");
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
        }

        // Loop through the list of pd pools.
        BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
            boost::shared_ptr<PdPoolParser>
                // Create the PdPool parser.
                parser(new PdPoolParser("pd-pool", local_pools_));
                // Build the pool instance
                parser->build(pd_pool);
                // Commit the pool to the local list of pools.
                parser->commit();
        }
    }

    /// @brief  Commits the pools created to the external storage area.
    ///
    /// Note that this method adds the local list of pools to the storage area
    /// rather than replacing its contents.  This permits other parsers to
    /// contribute to the set of pools.
    void commit() {
        // local_pools_ holds the values produced by the build function.
        // At this point parsing should have completed successfully so
        // we can append new data to the supplied storage.
        pools_->insert(pools_->end(), local_pools_->begin(),
                       local_pools_->end());
    }

private:
    /// @brief storage for local pools
    PoolStoragePtr local_pools_;

    /// @brief External storage where pools are stored upon list commit.
    PoolStoragePtr pools_;
};

313
/// @brief This class parses a single IPv6 subnet.
314
///
315 316
/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
/// the whole subnet definition. It creates parsersfor received configuration
317 318
/// parameters as needed.
class Subnet6ConfigParser : public SubnetConfigParser {
319 320
public:

321
    /// @brief Constructor
322
    ///
323 324
    /// @param ignored first parameter
    /// stores global scope parameters, options, option defintions.
325
    Subnet6ConfigParser(const std::string&)
326
        :SubnetConfigParser("", globalContext(), IOAddress("::")) {
327 328
    }

329
    /// @brief Parses a single IPv6 subnet configuration and adds to the
330 331 332 333 334 335
    /// Configuration Manager.
    ///
    /// @param subnet A new subnet being configured.
    void build(ConstElementPtr subnet) {
        SubnetConfigParser::build(subnet);

336
        if (subnet_) {
337 338 339 340
            Subnet6Ptr sub6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
            if (!sub6ptr) {
                // If we hit this, it is a programming error.
                isc_throw(Unexpected,
341
                          "Invalid cast in Subnet6ConfigParser::commit");
342
            }
343 344 345

            // Set relay infomation if it was provided
            if (relay_info_) {
346
                sub6ptr->setRelayInfo(*relay_info_);
347 348
            }

349 350 351 352
            // 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 {
353
                CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(sub6ptr);
354 355 356 357 358
            } catch (const std::exception& ex) {
                isc_throw(DhcpConfigError, ex.what() << " ("
                          << subnet->getPosition() << ")");
            }

359 360 361 362 363 364 365
            // Parse Host Reservations for this subnet if any.
            ConstElementPtr reservations = subnet->get("reservations");
            if (reservations) {
                ParserPtr parser(new HostReservationsListParser<
                                 HostReservationParser6>(subnet_->getID()));
                parser->build(reservations);
            }
366 367 368
        }
    }

369 370 371 372 373 374
    /// @brief Commits subnet configuration.
    ///
    /// This function is currently no-op because subnet should already
    /// be added into the Config Manager in the build().
    void commit() { }

375
protected:
376

377
    /// @brief creates parsers for entries in subnet definition
378
    ///
379
    /// @param config_id name of the entry
380
    ///
381 382 383 384 385 386 387 388 389
    /// @return parser object for specified entry name. Note the caller is
    /// responsible for deleting the parser created.
    /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
    /// for unknown config element
    DhcpConfigParser* createSubnetConfigParser(const std::string& config_id) {
        DhcpConfigParser* parser = NULL;
        if ((config_id.compare("preferred-lifetime") == 0)  ||
            (config_id.compare("valid-lifetime") == 0)  ||
            (config_id.compare("renew-timer") == 0)  ||
390 391
            (config_id.compare("rebind-timer") == 0) ||
            (config_id.compare("id") == 0)) {
392 393
            parser = new Uint32Parser(config_id, uint32_values_);
        } else if ((config_id.compare("subnet") == 0) ||
394
                   (config_id.compare("interface") == 0) ||
395
                   (config_id.compare("client-class") == 0) ||
396 397
                   (config_id.compare("interface-id") == 0) ||
                   (config_id.compare("reservation-mode") == 0)) {
398
            parser = new StringParser(config_id, string_values_);
399 400
        } else if (config_id.compare("pools") == 0) {
            parser = new Pools6ListParser(config_id, pools_);
401
        } else if (config_id.compare("relay") == 0) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
402
            parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
403 404
        } else if (config_id.compare("pd-pools") == 0) {
            parser = new PdPoolListParser(config_id, pools_);
405
        } else if (config_id.compare("option-data") == 0) {
406
            parser = new OptionDataListParser(config_id, options_, AF_INET6);
407
        } else {
408
            isc_throw(NotImplemented, "unsupported parameter: " << config_id);
409
        }
410

411
        return (parser);
412 413
    }

414
    /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
415 416
    /// options.
    ///
417 418 419 420
    /// @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.
421
    virtual void duplicate_option_warning(uint32_t code,
422 423 424 425
                                         isc::asiolink::IOAddress& addr) {
        LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
            .arg(code).arg(addr.toText());
    }
426

427
    /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
428 429
    /// and prefix length.
    ///
430
    /// @param addr is IPv6 prefix of the subnet.
431
    /// @param len is the prefix length
432
    void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
433 434
        // Get all 'time' parameters using inheritance.
        // If the subnet-specific value is defined then use it, else
435 436 437
        // use the global value. The global value must always be
        // present. If it is not, it is an internal error and exception
        // is thrown.
438 439 440 441
        Triplet<uint32_t> t1 = getParam("renew-timer");
        Triplet<uint32_t> t2 = getParam("rebind-timer");
        Triplet<uint32_t> pref = getParam("preferred-lifetime");
        Triplet<uint32_t> valid = getParam("valid-lifetime");
442 443 444 445
        // Subnet ID is optional. If it is not supplied the value of 0 is used,
        // which means autogenerate.
        SubnetID subnet_id =
            static_cast<SubnetID>(uint32_values_->getOptionalParam("id", 0));
446

447 448 449 450
        // Get interface-id option content. For now we support string
        // represenation only
        std::string ifaceid;
        try {
451 452
            ifaceid = string_values_->getParam("interface-id");
        } catch (const DhcpConfigError &) {
453 454 455
            // interface-id is not mandatory
        }

456 457
        // Specifying both interface for locally reachable subnets and
        // interface id for relays is mutually exclusive. Need to test for
458
        // this condition.
459 460 461 462 463
        if (!ifaceid.empty()) {
            std::string iface;
            try {
                iface = string_values_->getParam("interface");
            } catch (const DhcpConfigError &) {
464
                // iface not mandatory
465 466 467 468
            }

            if (!iface.empty()) {
                isc_throw(isc::dhcp::DhcpConfigError,
469 470 471
                      "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 "
472
                      "subnet " << addr << "/" << (int)len);
473
            }
474 475
        }

476
        stringstream tmp;
477
        tmp << addr << "/" << static_cast<int>(len)
478 479
            << " with params t1=" << t1 << ", t2=" << t2 << ", pref="
            << pref << ", valid=" << valid;
480

481
        LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
482

483
        // Create a new subnet.
484 485
        Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
                                       subnet_id);
486

487 488 489 490
        // 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));
491
            subnet6->setInterfaceId(opt);
492 493
        }

494 495 496 497 498 499 500 501
        // Try setting up client class (if specified)
        try {
            string client_class = string_values_->getParam("client-class");
            subnet6->allowClientClass(client_class);
        } catch (const DhcpConfigError&) {
            // That's ok if it fails. client-class is optional.
        }

502
        subnet_.reset(subnet6);
503
    }
504

505 506
};

507 508

/// @brief this class parses a list of DHCP6 subnets
509 510 511 512
///
/// This is a wrapper parser that handles the whole list of Subnet6
/// definitions. It iterates over all entries and creates Subnet6ConfigParser
/// for each entry.
513
class Subnets6ListConfigParser : public DhcpConfigParser {
514
public:
515 516 517

    /// @brief constructor
    ///
518
    /// @param dummy first argument, always ignored. All parsers accept a
519
    /// string parameter "name" as their first argument.
520
    Subnets6ListConfigParser(const std::string&) {
521 522
    }

523 524
    /// @brief parses contents of the list
    ///
525
    /// Iterates over all entries on the list and creates a Subnet6ConfigParser
526 527 528
    /// for each entry.
    ///
    /// @param subnets_list pointer to a list of IPv6 subnets
529 530 531 532 533 534 535 536 537
    void build(ConstElementPtr subnets_list) {
        BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
            ParserPtr parser(new Subnet6ConfigParser("subnet"));
            parser->build(subnet);
            subnets_.push_back(parser);
        }

    }

538 539
    /// @brief commits subnets definitions.
    ///
540 541
    /// Iterates over all Subnet6 parsers. Each parser contains definitions of
    /// a single subnet and its parameters and commits each subnet separately.
542 543 544 545 546 547 548
    void commit() {
        BOOST_FOREACH(ParserPtr subnet, subnets_) {
            subnet->commit();
        }

    }

549 550 551
    /// @brief Returns Subnet6ListConfigParser object
    /// @param param_name name of the parameter
    /// @return Subnets6ListConfigParser object
552
    static DhcpConfigParser* factory(const std::string& param_name) {
553 554 555
        return (new Subnets6ListConfigParser(param_name));
    }

556
    /// @brief collection of subnet parsers.
557 558 559
    ParserCollection subnets_;
};

560
/// @brief Parser for list of RSOO options
561
///
562
/// This parser handles a Dhcp6/relay-supplied-options entry. It contains a
563 564
/// list of RSOO-enabled options which should be sent back to the client.
///
565
/// The options on this list can be specified using an option code or option
566 567
/// name. Therefore, the values on the list should always be enclosed in
/// "quotes".
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
class RSOOListConfigParser : public DhcpConfigParser {
public:

    /// @brief constructor
    ///
    /// As this is a dedicated parser, it must be used to parse
    /// "relay-supplied-options" parameter only. All other types will throw exception.
    ///
    /// @param param_name name of the configuration parameter being parsed
    /// @throw BadValue if supplied parameter name is not "relay-supplied-options"
    RSOOListConfigParser(const std::string& param_name) {
        if (param_name != "relay-supplied-options") {
            isc_throw(BadValue, "Internal error. RSOO configuration "
                      "parser called for the wrong parameter: " << param_name);
        }
    }

    /// @brief parses parameters value
    ///
    /// Parses configuration entry (list of sources) and adds each element
    /// to the RSOO list.
    ///
    /// @param value pointer to the content of parsed values
    virtual void build(isc::data::ConstElementPtr value) {
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
        try {
            BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
                std::string option_str = source_elem->stringValue();
                // This option can be either code (integer) or name. Let's try code first
                int64_t code = 0;
                try {
                    code = boost::lexical_cast<int64_t>(option_str);
                    // Protect against the negative value and too high value.
                    if (code < 0) {
                        isc_throw(BadValue, "invalid option code value specified '"
                                  << option_str << "', the option code must be a"
                                  " non-negative value");

                    } else if (code > std::numeric_limits<uint16_t>::max()) {
                        isc_throw(BadValue, "invalid option code value specified '"
                                  << option_str << "', the option code must not be"
                                  " greater than '" << std::numeric_limits<uint16_t>::max()
                                  << "'");
                    }

                } catch (const boost::bad_lexical_cast &) {
                    // Oh well, it's not a number
                }
615

616 617 618 619 620 621 622 623 624 625
                if (!code) {
                    OptionDefinitionPtr def = LibDHCP::getOptionDef(Option::V6, option_str);
                    if (def) {
                        code = def->getCode();
                    } else {
                        isc_throw(BadValue, "unable to find option code for the "
                                  " specified option name '" << option_str << "'"
                                  " while parsing the list of enabled"
                                  " relay-supplied-options");
                    }
626
                }
627
                CfgMgr::instance().getStagingCfg()->getCfgRSOO()->enable(code);
628
            }
629 630 631 632
        } catch (const std::exception& ex) {
            // Rethrow exception with the appended position of the parsed
            // element.
            isc_throw(DhcpConfigError, ex.what() << " (" << value->getPosition() << ")");
633 634 635 636 637 638 639 640
        }
    }

    /// @brief Does nothing.
    virtual void commit() {}
};


641 642 643 644 645
} // anonymous namespace

namespace isc {
namespace dhcp {

646 647 648 649
/// @brief creates global parsers
///
/// This method creates global parsers that parse global parameters, i.e.
/// those that take format of Dhcp6/param1, Dhcp6/param2 and so forth.
650 651
///
/// @param config_id pointer to received global configuration entry
652
/// @param element pointer to the element to be parsed
653
/// @return parser for specified global DHCPv6 parameter
654
/// @throw NotImplemented if trying to create a parser for unknown config
655
/// element
656 657
    DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
                                                    ConstElementPtr element) {
658
    DhcpConfigParser* parser = NULL;
659 660 661 662
    if ((config_id.compare("preferred-lifetime") == 0)  ||
        (config_id.compare("valid-lifetime") == 0)  ||
        (config_id.compare("renew-timer") == 0)  ||
        (config_id.compare("rebind-timer") == 0))  {
663
        parser = new Uint32Parser(config_id,
664
                                 globalContext()->uint32_values_);
665
    } else if (config_id.compare("interfaces-config") == 0) {
666
        parser = new IfacesConfigParser6();
667
    } else if (config_id.compare("subnet6") == 0) {
668
        parser = new Subnets6ListConfigParser(config_id);
669
    } else if (config_id.compare("option-data") == 0) {
670
        parser = new OptionDataListParser(config_id, CfgOptionPtr(), AF_INET6);
671
    } else if (config_id.compare("option-def") == 0) {
672
        parser  = new OptionDefListParser(config_id, globalContext());
673
    } else if (config_id.compare("version") == 0) {
674
        parser  = new StringParser(config_id,
675
                                   globalContext()->string_values_);
676
    } else if (config_id.compare("lease-database") == 0) {
677
        parser = new DbAccessParser(config_id, *globalContext());
678 679
    } else if (config_id.compare("hooks-libraries") == 0) {
        parser = new HooksLibrariesParser(config_id);
680 681
    } else if (config_id.compare("dhcp-ddns") == 0) {
        parser = new D2ClientConfigParser(config_id);
682 683 684
    } else if (config_id.compare("mac-sources") == 0) {
        parser = new MACSourcesListConfigParser(config_id,
                                                globalContext());
685 686
    } else if (config_id.compare("relay-supplied-options") == 0) {
        parser = new RSOOListConfigParser(config_id);
687
    } else {
688 689 690
        isc_throw(DhcpConfigError,
                "unsupported global configuration parameter: "
                  << config_id << " (" << element->getPosition() << ")");
691
    }
692 693

    return (parser);
694 695
}

696 697
isc::data::ConstElementPtr
configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
698
    if (!config_set) {
699 700 701
        ConstElementPtr answer = isc::config::createAnswer(1,
                                 string("Can't parse NULL config"));
        return (answer);
702 703
    }

704
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
705
              DHCP6_CONFIG_START).arg(config_set->str());
706

707 708 709 710
    // Before starting any subnet operations, let's reset the subnet-id counter,
    // so newly recreated configuration starts with first subnet-id equal 1.
    Subnet::resetSubnetID();

711
    // Some of the values specified in the configuration depend on
712
    // other values. Typically, the values in the subnet6 structure
713 714
    // depend on the global values. Also, option values configuration
    // must be performed after the option definitions configurations.
715
    // Thus we group parsers and will fire them in the right order:
716 717 718 719
    // all parsers other than lease-database, subnet6 and
    // option-data parser, then option-data parser, subnet6 parser,
    // lease-database parser.
    // Please do not change this order!
720
    ParserCollection independent_parsers;
721 722
    ParserPtr subnet_parser;
    ParserPtr option_parser;
723
    ParserPtr iface_parser;
724
    ParserPtr leases_parser;
725

726 727 728 729 730
    // Some of the parsers alter state of the system that can't easily
    // be undone. (Or alter it in a way such that undoing the change
    // has the same risk of failure as doing the change.)
    ParserPtr hooks_parser;

731
    // The subnet parsers implement data inheritance by directly
732
    // accessing global storage. For this reason the global data
733
    // parsers must store the parsed data into global storages
734 735 736 737
    // immediately. This may cause data inconsistency if the
    // parsing operation fails after the global storage has been
    // modified. We need to preserve the original global data here
    // so as we can rollback changes when an error occurs.
738
    ParserContext original_context(*globalContext());
739 740

    // answer will hold the result.
741
    ConstElementPtr answer;
742 743
    // rollback informs whether error occured and original data
    // have to be restored to global storages.
744
    bool rollback = false;
745 746 747
    // config_pair holds ther details of the current parser when iterating over
    // the parsers.  It is declared outside the loop so in case of error, the
    // name of the failing parser can be retrieved within the "catch" clause.
748
    ConfigPair config_pair;
749
    try {
750 751 752 753

        // Make parsers grouping.
        const std::map<std::string, ConstElementPtr>& values_map =
            config_set->mapValue();
754
        BOOST_FOREACH(config_pair, values_map) {
755 756
            ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first,
                                                           config_pair.second));
757 758
            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
                      .arg(config_pair.first);
759 760
            if (config_pair.first == "subnet6") {
                subnet_parser = parser;
761 762
            } else if (config_pair.first == "lease-database") {
                leases_parser = parser;
763 764
            } else if (config_pair.first == "option-data") {
                option_parser = parser;
765 766 767 768 769 770 771
            } else if (config_pair.first == "hooks-libraries") {
                // Executing the commit will alter currently loaded hooks
                // libraries. Check if the supplied libraries are valid,
                // but defer the commit until after everything else has
                // committed.
                hooks_parser = parser;
                hooks_parser->build(config_pair.second);
772
            } else if (config_pair.first == "interfaces-config") {
773 774 775 776
                // The interface parser is independent from any other parser and
                // can be run here before other parsers.
                parser->build(config_pair.second);
                iface_parser = parser;
777 778 779
            } else {
                // Those parsers should be started before other
                // parsers so we can call build straight away.
780 781
                independent_parsers.push_back(parser);
                parser->build(config_pair.second);
782 783 784
                // The commit operation here may modify the global storage
                // but we need it so as the subnet6 parser can access the
                // parsed data.
785 786 787
                parser->commit();
            }
        }
788

789 790 791 792
        // The option values parser is the next one to be run.
        std::map<std::string, ConstElementPtr>::const_iterator option_config =
            values_map.find("option-data");
        if (option_config != values_map.end()) {
793
            config_pair.first = "option-data";
794 795 796 797
            option_parser->build(option_config->second);
            option_parser->commit();
        }

798
        // The subnet parser is the next one to be run.
799 800 801
        std::map<std::string, ConstElementPtr>::const_iterator subnet_config =
            values_map.find("subnet6");
        if (subnet_config != values_map.end()) {
802
            config_pair.first = "subnet6";
803
            subnet_parser->build(subnet_config->second);
804
        }
805

806 807 808 809 810 811 812 813 814
        // The lease database parser is the last to be run.
        std::map<std::string, ConstElementPtr>::const_iterator leases_config =
            values_map.find("lease-database");
        if (leases_config != values_map.end()) {
            config_pair.first = "lease-database";
            leases_parser->build(leases_config->second);
            leases_parser->commit();
        }

815
    } catch (const isc::Exception& ex) {
816
        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL)
817
                  .arg(config_pair.first).arg(ex.what());
818
        answer = isc::config::createAnswer(1, ex.what());
819
        // An error occured, so make sure that we restore original data.
820 821
        rollback = true;

822 823
    } catch (...) {
        // for things like bad_cast in boost::lexical_cast
824
        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(config_pair.first);
825 826
        answer = isc::config::createAnswer(1, "undefined configuration"
                                           " processing error");
827
        // An error occured, so make sure that we restore original data.
828
        rollback = true;
829 830
    }

831 832 833 834
    // So far so good, there was no parsing error so let's commit the
    // configuration. This will add created subnets and option values into
    // the server's configuration.
    // This operation should be exception safe but let's make sure.
835 836
    if (!rollback) {
        try {
837 838
            if (subnet_parser) {
                subnet_parser->commit();
839
            }
840

841 842
            // No need to commit interface names as this is handled by the
            // CfgMgr::commit() function.
843 844 845 846 847 848 849

            // This occurs last as if it succeeds, there is no easy way to
            // revert it.  As a result, the failure to commit a subsequent
            // change causes problems when trying to roll back.
            if (hooks_parser) {
                hooks_parser->commit();
            }
850 851
        }
        catch (const isc::Exception& ex) {
852
            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
853
            answer = isc::config::createAnswer(2, ex.what());
854
            // An error occured, so make sure to restore the original data.
855 856 857
            rollback = true;
        } catch (...) {
            // for things like bad_cast in boost::lexical_cast
858
            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_EXCEPTION);
859 860
            answer = isc::config::createAnswer(2, "undefined configuration"
                                               " parsing error");
861
            // An error occured, so make sure to restore the original data.
862
            rollback = true;
863
        }
864
    }
865

866
    // Rollback changes as the configuration parsing failed.
867
    if (rollback) {
868
        globalContext().reset(new ParserContext(original_context));
869
        return (answer);
870
    }
871

872
    LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE)
873
        .arg(CfgMgr::instance().getCurrentCfg()->
874
             getConfigSummary(SrvConfig::CFGSEL_ALL6));
875

876
    // Everything was fine. Configuration is successful.
877
    answer = isc::config::createAnswer(0, "Configuration successful.");
878
    return (answer);
879 880
}

881
ParserContextPtr& globalContext() {
882 883
    static ParserContextPtr global_context_ptr(new ParserContext(Option::V6));
    return (global_context_ptr);
884 885
}

886 887
}; // end of isc::dhcp namespace
}; // end of isc namespace