json_config_parser.cc 33.8 KB
Newer Older
1
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6

7 8
#include <config.h>

9
#include <asiolink/io_address.h>
10
#include <cc/data.h>
11
#include <cc/command_interpreter.h>
12
#include <config/command_mgr.h>
13
#include <dhcp/libdhcp++.h>
14
#include <dhcp6/json_config_parser.h>
15
#include <dhcp6/dhcp6_log.h>
16
#include <dhcp6/simple_parser6.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
17
#include <dhcp/iface_mgr.h>
18
#include <dhcpsrv/cfg_option.h>
19 20 21
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
22
#include <dhcpsrv/timer_mgr.h>
23
#include <dhcpsrv/triplet.h>
24
#include <dhcpsrv/parsers/client_class_def_parser.h>
25 26 27
#include <dhcpsrv/parsers/dbaccess_parser.h>
#include <dhcpsrv/parsers/dhcp_config_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
28
#include <dhcpsrv/parsers/duid_config_parser.h>
29
#include <dhcpsrv/parsers/expiration_config_parser.h>
30 31
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
32
#include <dhcpsrv/parsers/ifaces_config_parser.h>
33
#include <hooks/hooks_parser.h>
34 35
#include <log/logger_support.h>
#include <util/encode/hex.h>
36
#include <util/strutil.h>
37 38 39 40 41 42 43 44

#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>
45
#include <limits>
46
#include <map>
47
#include <netinet/in.h>
48 49 50
#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
using namespace isc::asiolink;
57
using namespace isc::hooks;
58

59
namespace {
60

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

66
/// @brief Parser for IPv6 pool definitions.
67
///
68 69 70
/// 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
71
/// PoolStorage container.
72
///
73 74 75 76
/// 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.
77
    ///
78 79
    /// @param addr is the IPv6 prefix of the pool.
    /// @param len is the prefix length.
80 81
    /// @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
82
    /// polymorphic interface.
83
    /// @return returns a PoolPtr to the new Pool4 object.
84 85
    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
    {
86
        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
87
                                  (ptype), addr, len)));
88 89
    }

90
    /// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
91
    ///
92 93
    /// @param min is the first IPv6 address in the pool.
    /// @param max is the last IPv6 address in the pool.
94 95
    /// @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
96
    /// polymorphic interface.
97
    /// @return returns a PoolPtr to the new Pool4 object.
98 99
    PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
    {
100
        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
101
                                  (ptype), min, max)));
102 103 104
    }
};

105 106
/// @brief Specialization of the pool list parser for DHCPv6
class Pools6ListParser : PoolsListParser {
107 108
public:

109 110 111 112
    /// @brief parses the actual structure
    ///
    /// This method parses the actual list of pools.
    ///
113
    /// @param pools storage container in which to store the parsed pool.
114 115
    /// @param pools_list a list of pool structures
    /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
116 117
    void parse(PoolStoragePtr pools,
               isc::data::ConstElementPtr pools_list) {
118
        BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
119 120
            Pool6Parser parser;
            parser.parse(pools, pool, AF_INET6);
121
        }
122 123 124
    }
};

125 126 127 128 129
/// @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.
///
Francis Dupont's avatar
Francis Dupont committed
130
/// PdPool definitions currently support three elements: prefix, prefix-len,
131 132
/// and delegated-len, as shown in the example JSON text below:
///
133
/// @code
134 135 136 137 138 139
///
/// {
///     "prefix": "2001:db8:1::",
///     "prefix-len": 64,
///     "delegated-len": 128
/// }
140 141
/// @endcode
///
142
class PdPoolParser : public isc::data::SimpleParser {
143 144 145 146
public:

    /// @brief Constructor.
    ///
147
    PdPoolParser() : options_(new CfgOption()) {
148 149 150 151 152 153 154
    }

    /// @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.
    ///
155
    /// @param pools storage container in which to store the parsed pool.
156 157 158 159
    /// @param pd_pool_ pointer to an element that holds configuration entries
    /// that define a prefix delegation pool.
    ///
    /// @throw DhcpConfigError if configuration parsing fails.
160 161 162 163 164 165 166
    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");

167
        std::string excluded_prefix_str = "::";
168 169 170 171
        if (pd_pool_->contains("excluded-prefix")) {
            excluded_prefix_str = getString(pd_pool_, "excluded-prefix");
        }

172
        uint8_t excluded_prefix_len = 0;
173 174 175
        if (pd_pool_->contains("excluded-prefix-len")) {
            excluded_prefix_len = getUint8(pd_pool_, "excluded-prefix-len");
        }
176

177 178
        ConstElementPtr option_data = pd_pool_->get("option-data");
        if (option_data) {
179
            OptionDataListParser opts_parser(AF_INET6);
180
            opts_parser.parse(options_, option_data);
181 182
        }
                    
183 184 185
        ConstElementPtr user_context = pd_pool_->get("user-context");
        if (user_context) {
            user_context_ = user_context;
186 187
        }

188
        // Check the pool parameters. It will throw an exception if any
189
        // of the required parameters are invalid.
190 191
        try {
            // Attempt to construct the local pool.
192 193 194 195
            pool_.reset(new Pool6(IOAddress(addr_str),
                                  prefix_len,
                                  delegated_len,
                                  IOAddress(excluded_prefix_str),
196
                                  excluded_prefix_len));
197 198
            // Merge options specified for a pool into pool configuration.
            options_->copyTo(*pool_->getCfgOption());
199
        } catch (const std::exception& ex) {
200 201 202 203 204
            // 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() << ")");
205
        }
206 207 208 209

        if (user_context_) {
            pool_->setUserContext(user_context_);
        }
210 211

        // Add the local pool to the external storage ptr.
212
        pools->push_back(pool_);
213 214
    }

215
private:
216 217 218 219

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

220 221
    /// A storage for pool specific option values.
    CfgOptionPtr options_;
222 223

    isc::data::ConstElementPtr user_context_;
224 225 226 227 228 229 230
};

/// @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.
231
class PdPoolsListParser : public PoolsListParser {
232 233 234 235 236 237 238
public:

    /// @brief Parse configuration entries.
    ///
    /// This function parses configuration entries and creates instances
    /// of prefix delegation pools .
    ///
239
    /// @param storage is the pool storage in which to store the parsed
240 241 242 243
    /// @param pd_pool_list pointer to an element that holds entries
    /// that define a prefix delegation pool.
    ///
    /// @throw DhcpConfigError if configuration parsing fails.
244 245
    void parse(PoolStoragePtr pools,
               isc::data::ConstElementPtr pd_pool_list) {
246 247
        // Loop through the list of pd pools.
        BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
248 249
            PdPoolParser parser;
            parser.parse(pools, pd_pool);
250 251 252 253
        }
    }
};

254
/// @anchor Subnet6ConfigParser
255
/// @brief This class parses a single IPv6 subnet.
256
///
257 258
/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
/// the whole subnet definition. It creates parsersfor received configuration
259 260
/// parameters as needed.
class Subnet6ConfigParser : public SubnetConfigParser {
261 262
public:

263
    /// @brief Constructor
264
    ///
Francis Dupont's avatar
Francis Dupont committed
265
    /// stores global scope parameters, options, option definitions.
266
    Subnet6ConfigParser()
267
        :SubnetConfigParser(AF_INET6) {
268 269
    }

270
    /// @brief Parses a single IPv6 subnet configuration and adds to the
271 272 273
    /// Configuration Manager.
    ///
    /// @param subnet A new subnet being configured.
274 275
    /// @return a pointer to created Subnet6 object
    Subnet6Ptr parse(ConstElementPtr subnet) {
276 277 278
        /// Parse all pools first.
        ConstElementPtr pools = subnet->get("pools");
        if (pools) {
279 280
            Pools6ListParser parser;
            parser.parse(pools_, pools);
281 282 283
        }
        ConstElementPtr pd_pools = subnet->get("pd-pools");
        if (pd_pools) {
284 285
            PdPoolsListParser parser;
            parser.parse(pools_, pd_pools);
286 287
        }

288
        SubnetPtr generic = SubnetConfigParser::parse(subnet);
289

290 291 292 293 294
        if (!generic) {
            isc_throw(DhcpConfigError,
                      "Failed to create an IPv6 subnet (" <<
                      subnet->getPosition() << ")");
        }
295

296 297
        Subnet6Ptr sn6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
        if (!sn6ptr) {
298 299 300 301
            // If we hit this, it is a programming error.
            isc_throw(Unexpected,
                      "Invalid Subnet6 cast in Subnet6ConfigParser::parse");
        }
302

303 304
        // Set relay information if it was provided
        if (relay_info_) {
305
            sn6ptr->setRelayInfo(*relay_info_);
306
        }
307

308 309 310 311 312 313

        // Parse Host Reservations for this subnet if any.
        ConstElementPtr reservations = subnet->get("reservations");
        if (reservations) {
            HostReservationsListParser<HostReservationParser6> parser;
            parser.parse(subnet_->getID(), reservations);
314 315
        }

316
        return (sn6ptr);
317
    }
318

319 320
protected:
    /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
321 322
    /// options.
    ///
323 324 325 326
    /// @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.
327
    virtual void duplicate_option_warning(uint32_t code,
328 329 330 331
                                         isc::asiolink::IOAddress& addr) {
        LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
            .arg(code).arg(addr.toText());
    }
332

333
    /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
334 335
    /// and prefix length.
    ///
336
    /// @param addr is IPv6 prefix of the subnet.
337
    /// @param len is the prefix length
338 339
    void initSubnet(isc::data::ConstElementPtr params,
                    isc::asiolink::IOAddress addr, uint8_t len) {
340 341
        // Get all 'time' parameters using inheritance.
        // If the subnet-specific value is defined then use it, else
342 343 344
        // use the global value. The global value must always be
        // present. If it is not, it is an internal error and exception
        // is thrown.
345 346 347 348
        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");
349

350
        // Subnet ID is optional. If it is not supplied the value of 0 is used,
351 352 353
        // which means autogenerate. The value was inserted earlier by calling
        // SimpleParser6::setAllDefaults.
        SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
354

355 356
        // We want to log whether rapid-commit is enabled, so we get this
        // before the actual subnet creation.
357
        bool rapid_commit = getBoolean(params, "rapid-commit");
358

359 360 361 362 363
        std::ostringstream output;
        output << addr << "/" << static_cast<int>(len)
               << " with params t1=" << t1 << ", t2="
               << t2 << ", preferred-lifetime=" << pref
               << ", valid-lifetime=" << valid
364
               << ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled");
365

366 367

        LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(output.str());
368

369
        // Create a new subnet.
370 371
        Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
                                       subnet_id);
372 373 374 375
        subnet_.reset(subnet6);

        // Enable or disable Rapid Commit option support for the subnet.
        subnet6->setRapidCommit(rapid_commit);
376

377 378 379 380 381 382 383 384 385 386 387 388 389
        // 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 "
390 391
                      "subnet " << addr << "/" << (int)len << "("
                      << params->getPosition() << ")");
392 393
        }

394 395 396 397
        // 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));
398
            subnet6->setInterfaceId(opt);
399 400
        }

401 402
        /// client-class processing is now generic and handled in the common
        /// code (see @ref isc::data::SubnetConfigParser::createSubnet)
403
    }
404

405 406
};

407 408

/// @brief this class parses a list of DHCP6 subnets
409 410 411 412
///
/// This is a wrapper parser that handles the whole list of Subnet6
/// definitions. It iterates over all entries and creates Subnet6ConfigParser
/// for each entry.
413
class Subnets6ListConfigParser : public isc::data::SimpleParser {
414
public:
415 416 417

    /// @brief parses contents of the list
    ///
418 419 420
    /// Iterates over all entries on the list, parses its content
    /// (by instantiating Subnet6ConfigParser) and adds to specified
    /// configuration.
421
    ///
422
    /// @param cfg configuration (parsed subnets will be stored here)
423
    /// @param subnets_list pointer to a list of IPv6 subnets
424 425 426 427
    /// @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()) {
428

429 430
            Subnet6ConfigParser parser;
            Subnet6Ptr subnet = parser.parse(subnet_json);
431

432 433 434 435 436 437 438 439 440 441
            // 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() << ")");
            }
442
        }
443
        return (cnt);
444 445 446
    }
};

447
/// @brief Parser for list of RSOO options
448
///
449
/// This parser handles a Dhcp6/relay-supplied-options entry. It contains a
450 451
/// list of RSOO-enabled options which should be sent back to the client.
///
452
/// The options on this list can be specified using an option code or option
453 454
/// name. Therefore, the values on the list should always be enclosed in
/// "quotes".
455
class RSOOListConfigParser : public isc::data::SimpleParser {
456 457 458 459 460 461 462 463
public:

    /// @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
464 465
    /// @param cfg server configuration (RSOO will be stored here)
    void parse(SrvConfigPtr cfg, isc::data::ConstElementPtr value) {
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
        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
                }
489

490
                if (!code) {
491 492
                    const OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE,
                                                                          option_str);
493 494 495 496 497 498 499 500
                    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");
                    }
501
                }
502
                cfg->getCfgRSOO()->enable(code);
503
            }
504 505 506 507
        } catch (const std::exception& ex) {
            // Rethrow exception with the appended position of the parsed
            // element.
            isc_throw(DhcpConfigError, ex.what() << " (" << value->getPosition() << ")");
508 509 510 511
        }
    }
};

512 513 514
/// @brief Parser that takes care of global DHCPv6 parameters.
///
/// See @ref parse method for a list of supported parameters.
515 516 517 518 519 520
class Dhcp6ConfigParser : public isc::data::SimpleParser {
public:

    /// @brief Sets global parameters in staging configuration
    ///
    /// @param global global configuration scope
521
    /// @param cfg Server configuration (parsed parameters will be stored here)
522 523 524 525 526 527 528 529
    ///
    /// Currently this method sets the following global parameters:
    ///
    /// - decline-probation-period
    /// - dhcp4o6-port
    ///
    /// @throw DhcpConfigError if parameters are missing or
    /// or having incorrect values.
530
    void parse(SrvConfigPtr srv_config, ConstElementPtr global) {
531

532 533 534 535 536 537
        // Set the probation period for decline handling.
        uint32_t probation_period =
            getUint32(global, "decline-probation-period");
        srv_config->setDeclinePeriod(probation_period);

        // Set the DHCPv4-over-DHCPv6 interserver port.
538
        uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
539
        srv_config->setDhcp4o6Port(dhcp4o6_port);
540 541
    }
};
542

543 544 545 546 547
} // anonymous namespace

namespace isc {
namespace dhcp {

548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
/// @brief Initialize the command channel based on the staging configuration
///
/// Only close the current channel, if the new channel configuration is
/// different.  This avoids disconnecting a client and hence not sending them
/// a command result, unless they specifically alter the channel configuration.
/// In that case the user simply has to accept they'll be disconnected.
///
void configureCommandChannel() {
    // Get new socket configuration.
    ConstElementPtr sock_cfg =
        CfgMgr::instance().getStagingCfg()->getControlSocketInfo();

    // Get current socket configuration.
    ConstElementPtr current_sock_cfg =
            CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();

    // Determine if the socket configuration has changed. It has if
    // both old and new configuration is specified but respective
    // data elements are't equal.
    bool sock_changed = (sock_cfg && current_sock_cfg &&
                         !sock_cfg->equals(*current_sock_cfg));

    // If the previous or new socket configuration doesn't exist or
    // the new configuration differs from the old configuration we
    // close the exisitng socket and open a new socket as appropriate.
    // Note that closing an existing socket means the clien will not
    // receive the configuration result.
    if (!sock_cfg || !current_sock_cfg || sock_changed) {
        // Close the existing socket (if any).
        isc::config::CommandMgr::instance().closeCommandSocket();

        if (sock_cfg) {
            // This will create a control socket and install the external
            // socket in IfaceMgr. That socket will be monitored when
            // Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
            // callback in CommandMgr will be called, if necessary.
            isc::config::CommandMgr::instance().openCommandSocket(sock_cfg);
        }
    }
}

589
isc::data::ConstElementPtr
590 591 592
configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set,
                     bool check_only) {

593
    if (!config_set) {
594 595 596
        ConstElementPtr answer = isc::config::createAnswer(1,
                                 string("Can't parse NULL config"));
        return (answer);
597 598
    }

599
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
600
              DHCP6_CONFIG_START).arg(config_set->str());
601

602 603 604 605
    // 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();

606 607 608
    // Remove any existing timers.
    TimerMgr::instance()->unregisterTimers();

609 610
    // Revert any runtime option definitions configured so far and not committed.
    LibDHCP::revertRuntimeOptionDefs();
611 612 613
    // Let's set empty container in case a user hasn't specified any configuration
    // for option definitions. This is equivalent to commiting empty container.
    LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
614

615 616 617 618 619
    // This is a way to convert ConstElementPtr to ElementPtr.
    // We need a config that can be edited, because we will insert
    // default values and will insert derived values as well.
    ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(config_set);

620
    // answer will hold the result.
621
    ConstElementPtr answer;
Francis Dupont's avatar
Francis Dupont committed
622
    // rollback informs whether error occurred and original data
623
    // have to be restored to global storages.
624
    bool rollback = false;
Francis Dupont's avatar
Francis Dupont committed
625
    // config_pair holds the details of the current parser when iterating over
626 627
    // 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.
628
    ConfigPair config_pair;
629
    try {
630

631 632
        SrvConfigPtr srv_config = CfgMgr::instance().getStagingCfg();

633
        // Set all default values if not specified by the user.
634
        SimpleParser6::setAllDefaults(mutable_cfg);
635

636 637 638
        // And now derive (inherit) global parameters to subnets, if not specified.
        SimpleParser6::deriveParameters(mutable_cfg);

639 640
        // Make parsers grouping.
        const std::map<std::string, ConstElementPtr>& values_map =
641 642 643 644 645
            mutable_cfg->mapValue();

        // We need definitions first
        ConstElementPtr option_defs = mutable_cfg->get("option-def");
        if (option_defs) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
646
            OptionDefListParser parser;
647
            CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
648 649 650
            parser.parse(cfg_option_def, option_defs);
        }

651
        BOOST_FOREACH(config_pair, values_map) {
652 653 654
            // In principle we could have the following code structured as a series
            // of long if else if clauses. That would give a marginal performance
            // boost, but would make the code less readable. We had serious issues
Tomek Mrugalski's avatar
Tomek Mrugalski committed
655
            // with the parser code debugability, so I decided to keep it as a
656
            // series of independent ifs.
657 658 659 660 661 662 663 664

            if (config_pair.first == "option-def") {
                // This is converted to SimpleParser and is handled already above.
                continue;
            }

            if (config_pair.first == "option-data") {
                OptionDataListParser parser(AF_INET6);
665
                CfgOptionPtr cfg_option = srv_config->getCfgOption();
666 667 668 669
                parser.parse(cfg_option, config_pair.second);
                continue;
            }

670 671
            if (config_pair.first == "mac-sources") {
                MACSourcesListConfigParser parser;
672
                CfgMACSource& mac_source = srv_config->getMACSources();
673 674 675 676 677 678 679 680 681 682
                parser.parse(mac_source, config_pair.second);
                continue;
            }

            if (config_pair.first == "control-socket") {
                ControlSocketParser parser;
                parser.parse(*srv_config, config_pair.second);
                continue;
            }

683 684 685 686 687 688
            if (config_pair.first == "host-reservation-identifiers") {
                HostReservationIdsParser6 parser;
                parser.parse(config_pair.second);
                continue;
            }

689 690
            if (config_pair.first == "server-id") {
                DUIDConfigParser parser;
691
                const CfgDUIDPtr& cfg = srv_config->getCfgDUID();
692 693 694 695
                parser.parse(cfg, config_pair.second);
                continue;
            }

696 697
            if (config_pair.first == "interfaces-config") {
                IfacesConfigParser parser(AF_INET6);
698
                CfgIfacePtr cfg_iface = srv_config->getCfgIface();
699 700 701 702
                parser.parse(cfg_iface, config_pair.second);
                continue;
            }

Francis Dupont's avatar
Francis Dupont committed
703 704 705 706 707 708
            if (config_pair.first == "expired-leases-processing") {
                ExpirationConfigParser parser;
                parser.parse(config_pair.second);
                continue;
            }

709
            if (config_pair.first == "hooks-libraries") {
710 711 712 713
                HooksLibrariesParser hooks_parser;
                HooksConfig& libraries = srv_config->getHooksConfig();
                hooks_parser.parse(libraries, config_pair.second);
                libraries.verifyLibraries(config_pair.second->getPosition());
714 715 716
                continue;
            }

717
            if (config_pair.first == "dhcp-ddns") {
718 719
                // Apply defaults
                D2ClientConfigParser::setAllDefaults(config_pair.second);
720 721
                D2ClientConfigParser parser;
                D2ClientConfigPtr cfg = parser.parse(config_pair.second);
722
                srv_config->setD2ClientConfig(cfg);
723 724 725
                continue;
            }

726 727 728 729
            if (config_pair.first =="client-classes") {
                ClientClassDefListParser parser;
                ClientClassDictionaryPtr dictionary =
                    parser.parse(config_pair.second, AF_INET6);
730 731 732 733
                srv_config->setClientClassDictionary(dictionary);
                continue;
            }

734 735 736 737 738 739 740 741
            // Please move at the end when migration will be finished.
            if (config_pair.first == "lease-database") {
                DbAccessParser parser(DbAccessParser::LEASE_DB);
                CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
                parser.parse(cfg_db_access, config_pair.second);
                continue;
            }

742
            if (config_pair.first == "hosts-database") {
743 744 745
                DbAccessParser parser(DbAccessParser::HOSTS_DB);
                CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
                parser.parse(cfg_db_access, config_pair.second);
746 747
                continue;
            }
748

749 750 751 752 753 754 755 756
            if (config_pair.first == "subnet6") {
                SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg();
                Subnets6ListConfigParser subnets_parser;
                // parse() returns number of subnets parsed. We may log it one day.
                subnets_parser.parse(srv_cfg, config_pair.second);
                continue;
            }

757 758 759
            // Timers are not used in the global scope. Their values are derived
            // to specific subnets (see SimpleParser6::deriveParameters).
            // decline-probation-period and dhcp4o6-port are handled in the
760
            // global_parser.parse() which sets global parameters.
761 762 763 764 765 766 767 768 769
            if ( (config_pair.first == "renew-timer") ||
                 (config_pair.first == "rebind-timer") ||
                 (config_pair.first == "preferred-lifetime") ||
                 (config_pair.first == "valid-lifetime") ||
                 (config_pair.first == "decline-probation-period") ||
                 (config_pair.first == "dhcp4o6-port")) {
                continue;
            }

770 771 772 773 774 775
            if (config_pair.first == "relay-supplied-options") {
                RSOOListConfigParser parser;
                parser.parse(srv_config, config_pair.second);
                continue;
            }

776 777 778 779
            // If we got here, no code handled this parameter, so we bail out.
            isc_throw(DhcpConfigError,
                      "unsupported global configuration parameter: " << config_pair.first
                      << " (" << config_pair.second->getPosition() << ")");
780
        }
781

782
        // Apply global options in the staging config.
783
        Dhcp6ConfigParser global_parser;
784
        global_parser.parse(srv_config, mutable_cfg);
785

786
    } catch (const isc::Exception& ex) {
787
        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL)
788
                  .arg(config_pair.first).arg(ex.what());
789
        answer = isc::config::createAnswer(1, ex.what());
Francis Dupont's avatar
Francis Dupont committed
790
        // An error occurred, so make sure that we restore original data.
791 792
        rollback = true;

793 794
    } catch (...) {
        // for things like bad_cast in boost::lexical_cast
795
        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(config_pair.first);
796 797
        answer = isc::config::createAnswer(1, "undefined configuration"
                                           " processing error");
Francis Dupont's avatar
Francis Dupont committed
798
        // An error occurred, so make sure that we restore original data.
799
        rollback = true;
800 801
    }

802 803 804 805 806 807 808 809 810
    if (check_only) {
        rollback = true;
        if (!answer) {
            answer = isc::config::createAnswer(0,
            "Configuration seems sane. Control-socket, hook-libraries, and D2 "
            "configuration were sanity checked, but not applied.");
        }
    }

811 812 813 814
    // 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.
815 816
    if (!rollback) {
        try {
817

818 819 820
            // Setup the command channel.
            configureCommandChannel();
            
821 822
            // No need to commit interface names as this is handled by the
            // CfgMgr::commit() function.
823

824 825 826 827
            // Apply staged D2ClientConfig, used to be done by parser commit
            D2ClientConfigPtr cfg;
            cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
            CfgMgr::instance().setD2ClientConfig(cfg);
828 829 830 831

            // 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.
832 833 834
            const HooksConfig& libraries =
                CfgMgr::instance().getStagingCfg()->getHooksConfig();
            libraries.loadLibraries();
835 836
        }
        catch (const isc::Exception& ex) {
837
            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
838
            answer = isc::config::createAnswer(2, ex.what());
Francis Dupont's avatar
Francis Dupont committed
839
            // An error occurred, so make sure to restore the original data.
840 841 842
            rollback = true;
        } catch (...) {
            // for things like bad_cast in boost::lexical_cast
843
            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_EXCEPTION);
844 845
            answer = isc::config::createAnswer(2, "undefined configuration"
                                               " parsing error");
Francis Dupont's avatar
Francis Dupont committed
846
            // An error occurred, so make sure to restore the original data.
847
            rollback = true;
848
        }
849
    }
850

851
    // Rollback changes as the configuration parsing failed.
852
    if (rollback) {
853 854 855
        // Revert to original configuration of runtime option definitions
        // in the libdhcp++.
        LibDHCP::revertRuntimeOptionDefs();
856
        return (answer);
857
    }
858

859
    LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE)
860
        .arg(CfgMgr::instance().getStagingCfg()->
861
             getConfigSummary(SrvConfig::CFGSEL_ALL6));
862

863
    // Everything was fine. Configuration is successful.
864
    answer = isc::config::createAnswer(0, "Configuration successful.");
865
    return (answer);
866 867 868 869
}

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