json_config_parser.cc 41.7 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
34
#include <log/logger_support.h>
#include <util/encode/hex.h>
35
#include <util/strutil.h>
36
#include <defaults.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
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
74
75
/// 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.
76
    ///
77
78
    /// @param addr is the IPv6 prefix of the pool.
    /// @param len is the prefix length.
79
80
    /// @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
81
    /// polymorphic interface.
82
    /// @return returns a PoolPtr to the new Pool4 object.
83
84
    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
    {
85
        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
86
                                  (ptype), addr, len)));
87
88
    }

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

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

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

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

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

    /// @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.
    ///
154
    /// @param pools storage container in which to store the parsed pool.
155
156
157
158
    /// @param pd_pool_ pointer to an element that holds configuration entries
    /// that define a prefix delegation pool.
    ///
    /// @throw DhcpConfigError if configuration parsing fails.
159
160
  void parse(PoolStoragePtr pools,
             ConstElementPtr pd_pool_) {
161
162
        std::string addr_str;
        std::string excluded_prefix_str = "::";
163
164
165
        uint8_t prefix_len = 0;
        uint8_t delegated_len = 0;
        uint8_t excluded_prefix_len = 0;
166

167
168
169
        // Parse the elements that make up the option definition.
        BOOST_FOREACH(ConfigPair param, pd_pool_->mapValue()) {
            std::string entry(param.first);
170
171
172
173
174
175
176
            ConstElementPtr value(param.second);
            try {
                if (entry == "prefix") {
                    addr_str = value->stringValue();
                } else if (entry == "excluded-prefix") {
                    excluded_prefix_str = value->stringValue();
                } else if (entry == "prefix-len") {
177
                    prefix_len = getUint8(entry, value);
178
                } else if (entry == "delegated-len") {
179
                    delegated_len = getUint8(entry, value);
180
                } else if (entry == "excluded-prefix-len") {
181
                    excluded_prefix_len = getUint8(entry, value);
182
183
184
185
186
187
                } else if (entry == "option-data") {
                    OptionDataListParser opts_parser(AF_INET6);
                    opts_parser.parse(options_, value);
                } else if (entry == "user-context") {
                    user_context_ = value;
                } else {
188
                    isc_throw(isc::dhcp::DhcpConfigError,
189
190
191
192
193
194
195
196
                              "unsupported parameter: " << entry
                              << " (" << value->getPosition() << ")");
                }
            } catch (const isc::data::TypeError&) {
                isc_throw(isc::dhcp::DhcpConfigError,
                          "invalid value type specified for parameter '"
                          << entry << "' ("
                          << value->getPosition() << ")");
197
198
199
            }
        }

200
        // Check the pool parameters. It will throw an exception if any
201
        // of the required parameters are not present or invalid.
202
203
204
        requireParam("prefix", pd_pool_);
        requireParam("prefix-len", pd_pool_);
        requireParam("delegated-len", pd_pool_);
205
206
        try {
            // Attempt to construct the local pool.
207
208
209
210
            pool_.reset(new Pool6(IOAddress(addr_str),
                                  prefix_len,
                                  delegated_len,
                                  IOAddress(excluded_prefix_str),
211
                                  excluded_prefix_len));
212
213
            // Merge options specified for a pool into pool configuration.
            options_->copyTo(*pool_->getCfgOption());
214
        } catch (const std::exception& ex) {
215
216
217
218
219
            // 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() << ")");
220
        }
221
222
223
224

        if (user_context_) {
            pool_->setUserContext(user_context_);
        }
225
226

        // Add the local pool to the external storage ptr.
227
        pools->push_back(pool_);
228
229
    }

230
private:
231

232
233
234
235
236
    /// @brief Require a mandatory element
    ///
    /// @param name Entry name
    /// @param config Pools configuration
    /// @throw isc::dhcp::DhcpConfigError if not present
237
    void requireParam(const std::string& name, ConstElementPtr config) const {
238
239
240
241
242
243
        if (!config->contains(name)) {
            isc_throw(isc::dhcp::DhcpConfigError,
                      "Missing parameter '" << name << "' ("
                      << config->getPosition() << ")");
        }
    }
244

245
246
247
248
249
250
251
252
    /// @brief Get an uint8_t value
    ///
    /// @param name Entry name
    /// @param value Integer element value
    /// @return uint8_t value
    /// @throw isc::data::TypeError when it is not an integer
    /// isc::dhcp::DhcpConfigError when it does not fit in an uint8_t
    uint8_t getUint8(const std::string& name, ConstElementPtr value) const {
253
        return (extractInt<uint8_t, DhcpConfigError>(name, value));
254
    }
255
256
257
258

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

259
260
    /// A storage for pool specific option values.
    CfgOptionPtr options_;
261
262

    isc::data::ConstElementPtr user_context_;
263
264
265
266
267
268
269
};

/// @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.
270
class PdPoolsListParser : public PoolsListParser {
271
272
273
274
275
276
277
public:

    /// @brief Parse configuration entries.
    ///
    /// This function parses configuration entries and creates instances
    /// of prefix delegation pools .
    ///
278
    /// @param storage is the pool storage in which to store the parsed
279
280
281
282
    /// @param pd_pool_list pointer to an element that holds entries
    /// that define a prefix delegation pool.
    ///
    /// @throw DhcpConfigError if configuration parsing fails.
283
284
    void parse(PoolStoragePtr pools,
               isc::data::ConstElementPtr pd_pool_list) {
285
286
        // Loop through the list of pd pools.
        BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
287
288
            PdPoolParser parser;
            parser.parse(pools, pd_pool);
289
290
291
292
        }
    }
};

293
/// @anchor Subnet6ConfigParser
294
/// @brief This class parses a single IPv6 subnet.
295
///
296
297
/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
/// the whole subnet definition. It creates parsersfor received configuration
298
299
/// parameters as needed.
class Subnet6ConfigParser : public SubnetConfigParser {
300
301
public:

302
    /// @brief Constructor
303
    ///
304
    /// @param ignored first parameter
Francis Dupont's avatar
Francis Dupont committed
305
    /// stores global scope parameters, options, option definitions.
306
    Subnet6ConfigParser(const std::string&)
307
        :SubnetConfigParser("", globalContext(), IOAddress("::")) {
308
309
    }

310
    /// @brief Parses a single IPv6 subnet configuration and adds to the
311
312
313
314
    /// Configuration Manager.
    ///
    /// @param subnet A new subnet being configured.
    void build(ConstElementPtr subnet) {
315
316
317
        /// Parse all pools first.
        ConstElementPtr pools = subnet->get("pools");
        if (pools) {
318
319
            Pools6ListParser parser;
            parser.parse(pools_, pools);
320
321
322
        }
        ConstElementPtr pd_pools = subnet->get("pd-pools");
        if (pd_pools) {
323
324
            PdPoolsListParser parser;
            parser.parse(pools_, pd_pools);
325
326
        }

327
328
        SubnetConfigParser::build(subnet);

329
        if (subnet_) {
330
331
332
333
            Subnet6Ptr sub6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
            if (!sub6ptr) {
                // If we hit this, it is a programming error.
                isc_throw(Unexpected,
334
                          "Invalid cast in Subnet6ConfigParser::commit");
335
            }
336

Francis Dupont's avatar
Francis Dupont committed
337
            // Set relay information if it was provided
338
            if (relay_info_) {
339
                sub6ptr->setRelayInfo(*relay_info_);
340
341
            }

342
343
344
345
            // 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 {
346
                CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(sub6ptr);
347
348
349
350
351
            } catch (const std::exception& ex) {
                isc_throw(DhcpConfigError, ex.what() << " ("
                          << subnet->getPosition() << ")");
            }

352
353
354
            // Parse Host Reservations for this subnet if any.
            ConstElementPtr reservations = subnet->get("reservations");
            if (reservations) {
355
356
                HostReservationsListParser<HostReservationParser6> parser;
                parser.parse(subnet_->getID(), reservations);
357
            }
358
359
360
        }
    }

361
362
363
364
365
366
    /// @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() { }

367
protected:
368

369
    /// @brief creates parsers for entries in subnet definition
370
    ///
371
    /// @param config_id name of the entry
372
    ///
373
374
375
376
377
378
379
380
381
    /// @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)  ||
382
383
            (config_id.compare("rebind-timer") == 0) ||
            (config_id.compare("id") == 0)) {
384
385
            parser = new Uint32Parser(config_id, uint32_values_);
        } else if ((config_id.compare("subnet") == 0) ||
386
                   (config_id.compare("interface") == 0) ||
387
                   (config_id.compare("client-class") == 0) ||
388
389
                   (config_id.compare("interface-id") == 0) ||
                   (config_id.compare("reservation-mode") == 0)) {
390
            parser = new StringParser(config_id, string_values_);
391
        // pools has been converted to SimpleParser.
392
        // relay has been converted to SimpleParser.
393
        // pd-pools has been converted to SimpleParser.
394
        // option-data was here, but it is now converted to SimpleParser
395
396
        } else if (config_id.compare("rapid-commit") == 0) {
            parser = new BooleanParser(config_id, boolean_values_);
397
        } else {
398
            isc_throw(NotImplemented, "unsupported parameter: " << config_id);
399
        }
400

401
        return (parser);
402
403
    }

404
    /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
405
406
    /// options.
    ///
407
408
409
410
    /// @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.
411
    virtual void duplicate_option_warning(uint32_t code,
412
413
414
415
                                         isc::asiolink::IOAddress& addr) {
        LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
            .arg(code).arg(addr.toText());
    }
416

417
    /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
418
419
    /// and prefix length.
    ///
420
    /// @param addr is IPv6 prefix of the subnet.
421
    /// @param len is the prefix length
422
    void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
423
424
        // Get all 'time' parameters using inheritance.
        // If the subnet-specific value is defined then use it, else
425
426
427
        // use the global value. The global value must always be
        // present. If it is not, it is an internal error and exception
        // is thrown.
428
429
430
431
        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");
432
433
434
435
        // 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));
436

437
        // Get interface-id option content. For now we support string
Francis Dupont's avatar
Francis Dupont committed
438
        // representation only
439
440
        std::string ifaceid;
        try {
441
442
            ifaceid = string_values_->getParam("interface-id");
        } catch (const DhcpConfigError &) {
443
444
445
            // interface-id is not mandatory
        }

446
447
        // Specifying both interface for locally reachable subnets and
        // interface id for relays is mutually exclusive. Need to test for
448
        // this condition.
449
450
451
452
453
        if (!ifaceid.empty()) {
            std::string iface;
            try {
                iface = string_values_->getParam("interface");
            } catch (const DhcpConfigError &) {
454
                // iface not mandatory
455
456
457
458
            }

            if (!iface.empty()) {
                isc_throw(isc::dhcp::DhcpConfigError,
459
460
461
                      "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 "
462
                      "subnet " << addr << "/" << (int)len);
463
            }
464
465
        }

466
        // Gather boolean parameters values.
467
        bool rapid_commit = boolean_values_->getOptionalParam("rapid-commit", false);
468

469
470
471
472
473
        std::ostringstream output;
        output << addr << "/" << static_cast<int>(len)
               << " with params t1=" << t1 << ", t2="
               << t2 << ", preferred-lifetime=" << pref
               << ", valid-lifetime=" << valid
474
               << ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled");
475

476
477

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

479
        // Create a new subnet.
480
481
        Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
                                       subnet_id);
482

483
484
485
486
        // 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));
487
            subnet6->setInterfaceId(opt);
488
489
        }

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

493
494
495
496
497
498
499
500
        // 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.
        }

501
        subnet_.reset(subnet6);
502
    }
503

504
505
};

506
507

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

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

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

    }

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

    }

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

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

559
/// @brief Parser for list of RSOO options
560
///
561
/// This parser handles a Dhcp6/relay-supplied-options entry. It contains a
562
563
/// list of RSOO-enabled options which should be sent back to the client.
///
564
/// The options on this list can be specified using an option code or option
565
566
/// name. Therefore, the values on the list should always be enclosed in
/// "quotes".
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
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) {
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
        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
                }
614

615
                if (!code) {
616
617
                    const OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE,
                                                                          option_str);
618
619
620
621
622
623
624
625
                    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
Francis Dupont's avatar
Francis Dupont committed
656
657
DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
                                                ConstElementPtr element) {
658
    DhcpConfigParser* parser = NULL;
659
660
661
    if ((config_id.compare("preferred-lifetime") == 0)  ||
        (config_id.compare("valid-lifetime") == 0)  ||
        (config_id.compare("renew-timer") == 0)  ||
662
        (config_id.compare("rebind-timer") == 0) ||
663
664
        (config_id.compare("decline-probation-period") == 0) ||
        (config_id.compare("dhcp4o6-port") == 0) )  {
665
        parser = new Uint32Parser(config_id,
666
                                 globalContext()->uint32_values_);
667
    } else if (config_id.compare("subnet6") == 0) {
668
        parser = new Subnets6ListConfigParser(config_id);
669
    // option-data and option-def are no longer needed here. They're now
670
671
    // converted to SimpleParser and are handled in configureDhcp6Server.
    // interfaces-config has been converted to SimpleParser.
672
    // version was removed - it was a leftover from bindctrl.
673
    // hooks-libraries is now converted to SimpleParser.
674
    // lease-database and hosts-database have been converted to SimpleParser already.
675
    // mac-source has been converted to SimpleParser.
676
    // dhcp-ddns has been converted to SimpleParser
677
678
    } else if (config_id.compare("relay-supplied-options") == 0) {
        parser = new RSOOListConfigParser(config_id);
679
    // control-socket has been converted to SimpleParser.
Francis Dupont's avatar
Francis Dupont committed
680
    // expired-leases-processing has been converted to SimpleParser.
681
    // client-classes has been converted to SimpleParser.
682
    // host-reservation-identifiers have been converted to SimpleParser already.
683
    // server-id has been migrated to SimpleParser
684
    } else {
685
686
687
        isc_throw(DhcpConfigError,
                "unsupported global configuration parameter: "
                  << config_id << " (" << element->getPosition() << ")");
688
    }
689
690

    return (parser);
691
692
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
693
/// @brief Sets global parameters in the staging configuration
694
695
696
697
///
/// Currently this method sets the following global parameters:
///
/// - decline-probation-period
698
/// - dhcp4o6-port
Tomek Mrugalski's avatar
Tomek Mrugalski committed
699
void setGlobalParameters6() {
700
701
702
703
704
705
706
707
708
709

    // Set the probation period for decline handling.
    try {
        uint32_t probation_period = globalContext()->uint32_values_
            ->getOptionalParam("decline-probation-period",
                               DEFAULT_DECLINE_PROBATION_PERIOD);
        CfgMgr::instance().getStagingCfg()->setDeclinePeriod(probation_period);
    } catch (...) {
        // That's not really needed.
    }
710
711
712
713
714
715
716
717
718

    // Set the DHCPv4-over-DHCPv6 interserver port.
    try {
        uint32_t dhcp4o6_port = globalContext()->uint32_values_
            ->getOptionalParam("dhcp4o6-port", 0);
        CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(dhcp4o6_port);
    } catch (...) {
        // Ignore errors. This flag is optional
    }
719
720
}

721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
/// @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);
        }
    }
}

762
763
isc::data::ConstElementPtr
configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
764
    if (!config_set) {
765
766
767
        ConstElementPtr answer = isc::config::createAnswer(1,
                                 string("Can't parse NULL config"));
        return (answer);
768
769
    }

770
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
771
              DHCP6_CONFIG_START).arg(config_set->str());
772

773
774
775
    // Reset global context.
    globalContext().reset(new ParserContext(Option::V6));

776
777
778
779
    // 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();

780
781
782
    // Remove any existing timers.
    TimerMgr::instance()->unregisterTimers();

783
784
    // Revert any runtime option definitions configured so far and not committed.
    LibDHCP::revertRuntimeOptionDefs();
785
786
787
    // 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());
788

789
    // Some of the values specified in the configuration depend on
790
    // other values. Typically, the values in the subnet6 structure
791
792
    // depend on the global values. Also, option values configuration
    // must be performed after the option definitions configurations.
793
    // Thus we group parsers and will fire them in the right order:
794
795
796
797
    // 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!
798
    ParserCollection independent_parsers;
799
    ParserPtr subnet_parser;
800

801
802
803
    // 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.)
804
    HooksLibrariesParser hooks_parser;
805

806
    // The subnet parsers implement data inheritance by directly
807
    // accessing global storage. For this reason the global data
808
    // parsers must store the parsed data into global storages
809
810
811
812
    // 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.
813
    ParserContext original_context(*globalContext());
814
815

    // answer will hold the result.
816
    ConstElementPtr answer;
Francis Dupont's avatar
Francis Dupont committed
817
    // rollback informs whether error occurred and original data
818
    // have to be restored to global storages.
819
    bool rollback = false;
Francis Dupont's avatar
Francis Dupont committed
820
    // config_pair holds the details of the current parser when iterating over
821
822
    // 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.
823
    ConfigPair config_pair;
824
    try {
825

826
827
        SrvConfigPtr srv_config = CfgMgr::instance().getStagingCfg();

828
829
830
        // 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.
831
        ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(config_set);
832

833
        SimpleParser6::setAllDefaults(mutable_cfg);
834

835
836
        // Make parsers grouping.
        const std::map<std::string, ConstElementPtr>& values_map =
837
838
839
840
841
            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
842
            OptionDefListParser parser;
843
            CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
844
845
846
            parser.parse(cfg_option_def, option_defs);
        }

847
        BOOST_FOREACH(config_pair, values_map) {
848
849
850
            // 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
851
            // with the parser code debugability, so I decided to keep it as a
852
            // series of independent ifs.
853
854
855
856
857
858
859
860

            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);
861
                CfgOptionPtr cfg_option = srv_config->getCfgOption();
862
863
864
865
                parser.parse(cfg_option, config_pair.second);
                continue;
            }

866
867
            if (config_pair.first == "mac-sources") {
                MACSourcesListConfigParser parser;
868
                CfgMACSource& mac_source = srv_config->getMACSources();
869
870
871
872
873
874
875
876
877
878
                parser.parse(mac_source, config_pair.second);
                continue;
            }

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

879
880
881
882
883
884
            if (config_pair.first == "host-reservation-identifiers") {
                HostReservationIdsParser6 parser;
                parser.parse(config_pair.second);
                continue;
            }

885
886
            if (config_pair.first == "server-id") {
                DUIDConfigParser parser;
887
                const CfgDUIDPtr& cfg = srv_config->getCfgDUID();
888
889
890
891
                parser.parse(cfg, config_pair.second);
                continue;
            }

892
893
            if (config_pair.first == "interfaces-config") {
                IfacesConfigParser parser(AF_INET6);
894
                CfgIfacePtr cfg_iface = srv_config->getCfgIface();
895
896
897
898
                parser.parse(cfg_iface, config_pair.second);
                continue;
            }

Francis Dupont's avatar
Francis Dupont committed
899
900
901
902
903
904
            if (config_pair.first == "expired-leases-processing") {
                ExpirationConfigParser parser;
                parser.parse(config_pair.second);
                continue;
            }

905
906
907
908
909
910
            if (config_pair.first == "hooks-libraries") {
                hooks_parser.parse(config_pair.second);
                hooks_parser.verifyLibraries();
                continue;
            }

911
            if (config_pair.first == "dhcp-ddns") {
912
913
914
915
                // Apply defaults if not in short cut
                if (!D2ClientConfigParser::isShortCutDisabled(config_pair.second)) {
                    D2ClientConfigParser::setAllDefaults(config_pair.second);
                }
916
917
                D2ClientConfigParser parser;
                D2ClientConfigPtr cfg = parser.parse(config_pair.second);
918
                srv_config->setD2ClientConfig(cfg);
919
920
921
                continue;
            }

922
923
924
925
            if (config_pair.first =="client-classes") {
                ClientClassDefListParser parser;
                ClientClassDictionaryPtr dictionary =
                    parser.parse(config_pair.second, AF_INET6);
926
927
928
929
                srv_config->setClientClassDictionary(dictionary);
                continue;
            }

930
931
932
933
934
935
936
937
            // 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;
            }

938
            if (config_pair.first == "hosts-database") {
939
940
941
                DbAccessParser parser(DbAccessParser::HOSTS_DB);
                CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
                parser.parse(cfg_db_access, config_pair.second);
942
943
                continue;
            }
944

945
946
            ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first,
                                                           config_pair.second));
947
948
            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
                      .arg(config_pair.first);
949
950
951
952
953
            if (config_pair.first == "subnet6") {
                subnet_parser = parser;
            } else {
                // Those parsers should be started before other
                // parsers so we can call build straight away.
954
955
                independent_parsers.push_back(parser);
                parser->build(config_pair.second);
956
957
958
                // The commit operation here may modify the global storage
                // but we need it so as the subnet6 parser can access the
                // parsed data.
959
960
961
                parser->commit();
            }
        }
962

963
        // The subnet parser is the next one to be run.
964
965
966
        std::map<std::string, ConstElementPtr>::const_iterator subnet_config =
            values_map.find("subnet6");
        if (subnet_config != values_map.end()) {
967
            config_pair.first = "subnet6";
968
            subnet_parser->build(subnet_config->second);
969
        }
970

971
972
        // Setup the command channel.
        configureCommandChannel();
973

974
    } catch (const isc::Exception& ex) {
975
        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL)
976
                  .arg(config_pair.first).arg(ex.what());
977
        answer = isc::config::createAnswer(1, ex.what());
Francis Dupont's avatar
Francis Dupont committed
978
        // An error occurred, so make sure that we restore original data.
979
980
        rollback = true;

981
982
    } catch (...) {
        // for things like bad_cast in boost::lexical_cast
983
        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(config_pair.first);
984
985
        answer = isc::config::createAnswer(1, "undefined configuration"
                                           " processing error");
Francis Dupont's avatar
Francis Dupont committed
986
        // An error occurred, so make sure that we restore original data.
987
        rollback = true;
988
989
    }

990
991
992
993
    // 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.
994
995
    if (!rollback) {
        try {
996
997
            if (subnet_parser) {
                subnet_parser->commit();
998
            }
999

Tomek Mrugalski's avatar
Tomek Mrugalski committed
1000
1001
            // Apply global options in the staging config.
            setGlobalParameters6();
1002

1003
1004
            // No need to commit interface names as this is handled by the
            // CfgMgr::commit() function.
1005
1006
1007
1008

            // 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.
1009
            hooks_parser.loadLibraries();
1010
1011
1012
1013
1014

            // Apply staged D2ClientConfig, used to be done by parser commit
            D2ClientConfigPtr cfg;
            cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
            CfgMgr::instance().setD2ClientConfig(cfg);
1015
1016
        }
        catch (const isc::Exception& ex) {
1017
            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
1018
            answer = isc::config::createAnswer(2, ex.what());