config_parser.cc 33.4 KB
Newer Older
1
// Copyright (C) 2012-2014 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
#include <asiolink/io_address.h>
16
#include <cc/data.h>
17
#include <config/ccsession.h>
18
#include <dhcp/libdhcp++.h>
19
#include <dhcp6/config_parser.h>
20
#include <dhcp6/dhcp6_log.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
21
#include <dhcp/iface_mgr.h>
22
#include <dhcpsrv/cfgmgr.h>
23
#include <dhcpsrv/dbaccess_parser.h>
24
#include <dhcpsrv/dhcp_config_parser.h>
25
#include <dhcpsrv/dhcp_parsers.h>
26
27
28
29
30
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/triplet.h>
#include <log/logger_support.h>
#include <util/encode/hex.h>
31
#include <util/strutil.h>
32
33
34
35
36
37
38
39
40
41
42
43

#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>
#include <map>
#include <vector>

#include <stdint.h>
44
45

using namespace std;
46
using namespace isc;
47
using namespace isc::data;
48
using namespace isc::dhcp;
49
50
using namespace isc::asiolink;

51
namespace {
52

53
54
55
56
57
// Pointers to various parser objects.
typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;

58
/// @brief Parser for DHCP6 option data value.
59
60
///
/// This parser parses configuration entries that specify value of
61
62
63
/// a single option specific to DHCP6.  It provides the DHCP6-specific
/// implementation of the abstract class OptionDataParser.
class Dhcp6OptionDataParser : public OptionDataParser {
64
public:
65
66
    /// @brief Constructor.
    ///
67
68
69
    /// @param dummy first param, option names are always "Dhcp6/option-data[n]"
    /// @param options is the option storage in which to store the parsed option
    /// upon "commit".
70
    /// @param global_context is a pointer to the global context which
71
    /// stores global scope parameters, options, option defintions.
72
73
    Dhcp6OptionDataParser(const std::string&, OptionStoragePtr options,
                         ParserContextPtr global_context)
74
        :OptionDataParser("", options, global_context) {
75
76
    }

77
    /// @brief static factory method for instantiating Dhcp4OptionDataParsers
78
    ///
79
80
    /// @param param_name name of the parameter to be parsed.
    /// @param options storage where the parameter value is to be stored.
81
    /// @param global_context is a pointer to the global context which
82
83
84
85
86
87
    /// stores global scope parameters, options, option defintions.
    /// @return returns a pointer to a new OptionDataParser. Caller is
    /// is responsible for deleting it when it is no longer needed.
    static OptionDataParser* factory(const std::string& param_name,
    OptionStoragePtr options, ParserContextPtr global_context) {
        return (new Dhcp6OptionDataParser(param_name, options, global_context));
88
89
    }

90
91
92

protected:
    /// @brief Finds an option definition within the server's option space
93
94
    ///
    /// Given an option space and an option code, find the correpsonding
95
    /// option defintion within the server's option defintion storage.
96
    ///
97
98
99
    /// @param option_space name of the parameter option space
    /// @param option_code numeric value of the parameter to find
    /// @return OptionDefintionPtr of the option defintion or an
100
    /// empty OptionDefinitionPtr if not found.
101
102
    /// @throw DhcpConfigError if the option space requested is not valid
    /// for this server.
103
104
    virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
                            std::string& option_space, uint32_t option_code) {
105
106
107
108
109
110
        OptionDefinitionPtr def;
        if (option_space == "dhcp6" &&
            LibDHCP::isStandardOption(Option::V6, option_code)) {
            def = LibDHCP::getOptionDef(Option::V6, option_code);
        } else if (option_space == "dhcp4") {
            isc_throw(DhcpConfigError, "'dhcp4' option space name is reserved"
111
                     << " for DHCPv4 server");
Tomek Mrugalski's avatar
Tomek Mrugalski committed
112
113
114
115
116
117
118
        } else {
            // Check if this is a vendor-option. If it is, get vendor-specific
            // definition.
            uint32_t vendor_id = SubnetConfigParser::optionSpaceToVendorId(option_space);
            if (vendor_id) {
                def = LibDHCP::getVendorOptionDef(Option::V6, vendor_id, option_code);
            }
119
        }
120

Tomek Mrugalski's avatar
Tomek Mrugalski committed
121
        return (def);
122
    }
123
124
};

125
/// @brief Parser for IPv4 pool definitions.
126
///
127
128
129
/// 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
130
/// PoolStorage container.
131
///
132
133
/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
class Pool6Parser : public PoolParser {
134
135
136
137
public:

    /// @brief Constructor.
    ///
138
139
140
141
142
143
    /// @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) {
144
    }
145

146
147
protected:
    /// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
148
    ///
149
150
    /// @param addr is the IPv6 prefix of the pool.
    /// @param len is the prefix length.
151
152
    /// @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
153
    /// polymorphic interface.
154
    /// @return returns a PoolPtr to the new Pool4 object.
155
156
    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
    {
157
        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
158
                                  (ptype), addr, len)));
159
160
    }

161
    /// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
162
    ///
163
164
    /// @param min is the first IPv6 address in the pool.
    /// @param max is the last IPv6 address in the pool.
165
166
    /// @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
167
    /// polymorphic interface.
168
    /// @return returns a PoolPtr to the new Pool4 object.
169
170
    PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
    {
171
        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Lease::Type>
172
                                  (ptype), min, max)));
173
174
175
    }
};

176
177
178
179
180
181
182
183
/// @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:
///
184
/// @code
185
186
187
188
189
190
///
/// {
///     "prefix": "2001:db8:1::",
///     "prefix-len": 64,
///     "delegated-len": 128
/// }
191
192
/// @endcode
///
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
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 {
234
235
                isc_throw(DhcpConfigError, "unsupported parameter: " << entry
                          << " (" << param.second->getPosition() << ")");
236
237
238
239
240
241
            }

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

242
243
244
245
246
        // 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;
247
        try {
248
249
250
            addr_str = string_values_->getParam("prefix");
            prefix_len = uint32_values_->getParam("prefix-len");
            delegated_len = uint32_values_->getParam("delegated-len");
251
252

            // Attempt to construct the local pool.
253
254
            pool_.reset(new Pool6(Lease::TYPE_PD, IOAddress(addr_str),
                                  prefix_len, delegated_len));
255
        } catch (const std::exception& ex) {
256
257
258
259
260
            // 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() << ")");
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
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
313
314
315
316
317
318
319
320
321
322
323
324
        }
    }

    // @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,
325
                      "PdPoolListParser: list of pool definitions is NULL");
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
        }

        // 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_;
};

361
/// @brief This class parses a single IPv6 subnet.
362
///
363
364
/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
/// the whole subnet definition. It creates parsersfor received configuration
365
366
/// parameters as needed.
class Subnet6ConfigParser : public SubnetConfigParser {
367
368
public:

369
    /// @brief Constructor
370
    ///
371
372
    /// @param ignored first parameter
    /// stores global scope parameters, options, option defintions.
373
    Subnet6ConfigParser(const std::string&)
374
        :SubnetConfigParser("", globalContext(), IOAddress("::")) {
375
376
    }

377
    /// @brief Adds the created subnet to a server's configuration.
378
    /// @throw throws Unexpected if dynamic cast fails.
379
    void commit() {
380
        if (subnet_) {
381
382
383
384
            Subnet6Ptr sub6ptr = boost::dynamic_pointer_cast<Subnet6>(subnet_);
            if (!sub6ptr) {
                // If we hit this, it is a programming error.
                isc_throw(Unexpected,
385
                          "Invalid cast in Subnet6ConfigParser::commit");
386
            }
387
388
389

            // Set relay infomation if it was provided
            if (relay_info_) {
390
                sub6ptr->setRelayInfo(*relay_info_);
391
392
            }

393
            isc::dhcp::CfgMgr::instance().addSubnet6(sub6ptr);
394
395
396
        }
    }

397
protected:
398

399
    /// @brief creates parsers for entries in subnet definition
400
    ///
401
    /// @param config_id name of the entry
402
    ///
403
404
405
406
407
408
409
410
411
    /// @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)  ||
412
413
            (config_id.compare("rebind-timer") == 0) ||
            (config_id.compare("id") == 0)) {
414
415
            parser = new Uint32Parser(config_id, uint32_values_);
        } else if ((config_id.compare("subnet") == 0) ||
416
                   (config_id.compare("interface") == 0) ||
417
                   (config_id.compare("client-class") == 0) ||
418
                   (config_id.compare("interface-id") == 0)) {
419
420
421
            parser = new StringParser(config_id, string_values_);
        } else if (config_id.compare("pool") == 0) {
            parser = new Pool6Parser(config_id, pools_);
422
        } else if (config_id.compare("relay") == 0) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
423
            parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
424
425
        } else if (config_id.compare("pd-pools") == 0) {
            parser = new PdPoolListParser(config_id, pools_);
426
        } else if (config_id.compare("option-data") == 0) {
427
           parser = new OptionDataListParser(config_id, options_,
428
429
                                             global_context_,
                                             Dhcp6OptionDataParser::factory);
430
        } else {
431
            isc_throw(NotImplemented, "unsupported parameter: " << config_id);
432
        }
433

434
        return (parser);
435
436
437
    }


438
    /// @brief Determines if the given option space name and code describe
439
    /// a standard option for the DHCP6 server.
440
    ///
441
442
443
444
445
    /// @param option_space is the name of the option space to consider
    /// @param code is the numeric option code to consider
    /// @return returns true if the space and code are part of the server's
    /// standard options.
    bool isServerStdOption(std::string option_space, uint32_t code) {
446
        return ((option_space.compare("dhcp6") == 0)
447
                && LibDHCP::isStandardOption(Option::V6, code));
448
449
    }

450
451
452
453
454
455
    /// @brief Returns the option definition for a given option code from
    /// the DHCP6 server's standard set of options.
    /// @param code is the numeric option code of the desired option definition.
    /// @return returns a pointer the option definition
    OptionDefinitionPtr getServerStdOptionDefinition (uint32_t code) {
        return (LibDHCP::getOptionDef(Option::V6, code));
456
457
    }

458
    /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
459
460
    /// options.
    ///
461
462
463
464
    /// @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.
465
    virtual void duplicate_option_warning(uint32_t code,
466
467
468
469
                                         isc::asiolink::IOAddress& addr) {
        LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
            .arg(code).arg(addr.toText());
    }
470

471
    /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
472
473
    /// and prefix length.
    ///
474
    /// @param addr is IPv6 prefix of the subnet.
475
    /// @param len is the prefix length
476
    void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
477
478
        // Get all 'time' parameters using inheritance.
        // If the subnet-specific value is defined then use it, else
479
480
481
        // use the global value. The global value must always be
        // present. If it is not, it is an internal error and exception
        // is thrown.
482
483
484
485
        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");
486
487
488
489
        // 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));
490

491
492
493
494
        // Get interface-id option content. For now we support string
        // represenation only
        std::string ifaceid;
        try {
495
496
            ifaceid = string_values_->getParam("interface-id");
        } catch (const DhcpConfigError &) {
497
498
499
            // interface-id is not mandatory
        }

500
501
        // Specifying both interface for locally reachable subnets and
        // interface id for relays is mutually exclusive. Need to test for
502
        // this condition.
503
504
505
506
507
        if (!ifaceid.empty()) {
            std::string iface;
            try {
                iface = string_values_->getParam("interface");
            } catch (const DhcpConfigError &) {
508
                // iface not mandatory
509
510
511
512
            }

            if (!iface.empty()) {
                isc_throw(isc::dhcp::DhcpConfigError,
513
514
515
                      "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 "
516
                      "subnet " << addr << "/" << (int)len);
517
            }
518
519
        }

520
        stringstream tmp;
521
        tmp << addr << "/" << static_cast<int>(len)
522
523
            << " with params t1=" << t1 << ", t2=" << t2 << ", pref="
            << pref << ", valid=" << valid;
524

525
        LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
526

527
        // Create a new subnet.
528
529
        Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
                                       subnet_id);
530

531
532
533
534
        // 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));
535
            subnet6->setInterfaceId(opt);
536
537
        }

538
539
540
541
542
543
544
545
        // 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.
        }

546
        subnet_.reset(subnet6);
547
    }
548

549
550
};

551
552

/// @brief this class parses a list of DHCP6 subnets
553
554
555
556
///
/// This is a wrapper parser that handles the whole list of Subnet6
/// definitions. It iterates over all entries and creates Subnet6ConfigParser
/// for each entry.
557
class Subnets6ListConfigParser : public DhcpConfigParser {
558
public:
559
560
561

    /// @brief constructor
    ///
562
    /// @param dummy first argument, always ignored. All parsers accept a
563
    /// string parameter "name" as their first argument.
564
    Subnets6ListConfigParser(const std::string&) {
565
566
    }

567
568
    /// @brief parses contents of the list
    ///
569
    /// Iterates over all entries on the list and creates a Subnet6ConfigParser
570
571
572
    /// for each entry.
    ///
    /// @param subnets_list pointer to a list of IPv6 subnets
573
574
575
576
577
578
579
580
581
    void build(ConstElementPtr subnets_list) {
        BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
            ParserPtr parser(new Subnet6ConfigParser("subnet"));
            parser->build(subnet);
            subnets_.push_back(parser);
        }

    }

582
583
    /// @brief commits subnets definitions.
    ///
584
585
    /// Iterates over all Subnet6 parsers. Each parser contains definitions of
    /// a single subnet and its parameters and commits each subnet separately.
586
    void commit() {
587
588
589
590
        // @todo: Implement more subtle reconfiguration than toss
        // the old one and replace with the new one.

        // remove old subnets
591
        isc::dhcp::CfgMgr::instance().deleteSubnets6();
592

593
594
595
596
597
598
        BOOST_FOREACH(ParserPtr subnet, subnets_) {
            subnet->commit();
        }

    }

599
600
601
    /// @brief Returns Subnet6ListConfigParser object
    /// @param param_name name of the parameter
    /// @return Subnets6ListConfigParser object
602
    static DhcpConfigParser* factory(const std::string& param_name) {
603
604
605
        return (new Subnets6ListConfigParser(param_name));
    }

606
    /// @brief collection of subnet parsers.
607
608
609
    ParserCollection subnets_;
};

610
611
612
613
614
} // anonymous namespace

namespace isc {
namespace dhcp {

615
616
617
618
/// @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.
619
620
621
///
/// @param config_id pointer to received global configuration entry
/// @return parser for specified global DHCPv6 parameter
622
/// @throw NotImplemented if trying to create a parser for unknown config
623
624
625
/// element
DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id) {
    DhcpConfigParser* parser = NULL;
626
627
628
629
    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))  {
630
        parser = new Uint32Parser(config_id,
631
                                 globalContext()->uint32_values_);
632
    } else if (config_id.compare("interfaces") == 0) {
633
        parser = new InterfaceListConfigParser(config_id);
634
    } else if (config_id.compare("subnet6") == 0) {
635
        parser = new Subnets6ListConfigParser(config_id);
636
    } else if (config_id.compare("option-data") == 0) {
637
638
        parser = new OptionDataListParser(config_id,
                                          globalContext()->options_,
639
                                          globalContext(),
640
                                          Dhcp6OptionDataParser::factory);
641
    } else if (config_id.compare("option-def") == 0) {
642
        parser  = new OptionDefListParser(config_id,
643
                                          globalContext()->option_defs_);
644
    } else if (config_id.compare("version") == 0) {
645
        parser  = new StringParser(config_id,
646
                                   globalContext()->string_values_);
647
    } else if (config_id.compare("lease-database") == 0) {
648
        parser = new DbAccessParser(config_id, *globalContext());
649
650
    } else if (config_id.compare("hooks-libraries") == 0) {
        parser = new HooksLibrariesParser(config_id);
651
652
    } else if (config_id.compare("dhcp-ddns") == 0) {
        parser = new D2ClientConfigParser(config_id);
653
    } else {
654
        isc_throw(NotImplemented,
655
656
                "Parser error: Global configuration parameter not supported: "
                << config_id);
657
    }
658
659

    return (parser);
660
661
}

662
663
isc::data::ConstElementPtr
configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
664
    if (!config_set) {
665
666
667
        ConstElementPtr answer = isc::config::createAnswer(1,
                                 string("Can't parse NULL config"));
        return (answer);
668
669
    }

670
    /// @todo: Append most essential info here (like "2 new subnets configured")
671
672
    string config_details;

673
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
674
              DHCP6_CONFIG_START).arg(config_set->str());
675

676
677
678
679
    // 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();

680
    // Some of the values specified in the configuration depend on
681
    // other values. Typically, the values in the subnet6 structure
682
683
    // depend on the global values. Also, option values configuration
    // must be performed after the option definitions configurations.
684
    // Thus we group parsers and will fire them in the right order:
685
686
    // all parsers other than subnet6 and option-data parser,
    // option-data parser, subnet6 parser.
687
    ParserCollection independent_parsers;
688
689
    ParserPtr subnet_parser;
    ParserPtr option_parser;
690
    ParserPtr iface_parser;
691

692
693
694
695
696
    // 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;

697
    // The subnet parsers implement data inheritance by directly
698
    // accessing global storage. For this reason the global data
699
    // parsers must store the parsed data into global storages
700
701
702
703
    // 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.
704
    ParserContext original_context(*globalContext());
705
706

    // answer will hold the result.
707
    ConstElementPtr answer;
708
709
    // rollback informs whether error occured and original data
    // have to be restored to global storages.
710
    bool rollback = false;
711
712
713
    // 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.
714
    ConfigPair config_pair;
715
    try {
716
717
718
719

        // Make parsers grouping.
        const std::map<std::string, ConstElementPtr>& values_map =
            config_set->mapValue();
720
        BOOST_FOREACH(config_pair, values_map) {
721
            ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first));
722
723
            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
                      .arg(config_pair.first);
724
725
726
727
            if (config_pair.first == "subnet6") {
                subnet_parser = parser;
            } else if (config_pair.first == "option-data") {
                option_parser = parser;
728
729
730
731
732
733
734
            } 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);
735
736
737
738
739
            } else if (config_pair.first == "interfaces") {
                // 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;
740
741
742
            } else {
                // Those parsers should be started before other
                // parsers so we can call build straight away.
743
744
                independent_parsers.push_back(parser);
                parser->build(config_pair.second);
745
746
747
                // The commit operation here may modify the global storage
                // but we need it so as the subnet6 parser can access the
                // parsed data.
748
749
750
                parser->commit();
            }
        }
751

752
753
754
755
756
757
758
759
760
761
762
763
764
        // 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()) {
            option_parser->build(option_config->second);
            option_parser->commit();
        }

        // The subnet parser is the last one to be run.
        std::map<std::string, ConstElementPtr>::const_iterator subnet_config =
            values_map.find("subnet6");
        if (subnet_config != values_map.end()) {
            subnet_parser->build(subnet_config->second);
765
        }
766

767
    } catch (const isc::Exception& ex) {
768
        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL)
769
770
771
                  .arg(config_pair.first).arg(ex.what());
        answer = isc::config::createAnswer(1,
                     string("Configuration parsing failed: ") + ex.what());
772
        // An error occured, so make sure that we restore original data.
773
774
        rollback = true;

775
776
    } catch (...) {
        // for things like bad_cast in boost::lexical_cast
777
        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(config_pair.first);
778
779
        answer = isc::config::createAnswer(1,
                     string("Configuration parsing failed"));
780
        // An error occured, so make sure that we restore original data.
781
        rollback = true;
782
783
    }

784
785
786
787
    // 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.
788
789
    if (!rollback) {
        try {
790
791
            if (subnet_parser) {
                subnet_parser->commit();
792
            }
793
794
795
796

            if (iface_parser) {
                iface_parser->commit();
            }
797
798
799
800
801
802
803

            // 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();
            }
804
805
        }
        catch (const isc::Exception& ex) {
806
            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
807
808
            answer = isc::config::createAnswer(2,
                         string("Configuration commit failed:") + ex.what());
809
            // An error occured, so make sure to restore the original data.
810
811
812
            rollback = true;
        } catch (...) {
            // for things like bad_cast in boost::lexical_cast
813
            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_EXCEPTION);
814
815
            answer = isc::config::createAnswer(2,
                         string("Configuration commit failed"));
816
            // An error occured, so make sure to restore the original data.
817
            rollback = true;
818
        }
819
    }
820

821
    // Rollback changes as the configuration parsing failed.
822
    if (rollback) {
823
        globalContext().reset(new ParserContext(original_context));
824
        return (answer);
825
    }
826
827
828

    LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details);

829
    // Everything was fine. Configuration is successful.
Shane Kerr's avatar
Shane Kerr committed
830
    answer = isc::config::createAnswer(0, "Configuration committed.");
831
    return (answer);
832
833
}

834
ParserContextPtr& globalContext() {
835
836
    static ParserContextPtr global_context_ptr(new ParserContext(Option::V6));
    return (global_context_ptr);
837
838
}

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