Kea 2.6.2
dhcp6/json_config_parser.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
11#include <cc/data.h>
13#include <config/command_mgr.h>
16#include <dhcp6/dhcp6_log.h>
17#include <dhcp6/dhcp6_srv.h>
19#include <dhcp/libdhcp++.h>
20#include <dhcp/iface_mgr.h>
23#include <dhcpsrv/cfg_option.h>
24#include <dhcpsrv/cfgmgr.h>
25#include <dhcpsrv/db_type.h>
40#include <dhcpsrv/pool.h>
41#include <dhcpsrv/subnet.h>
42#include <dhcpsrv/timer_mgr.h>
43#include <hooks/hooks_manager.h>
44#include <hooks/hooks_parser.h>
45#include <log/logger_support.h>
47#include <util/encode/encode.h>
49#include <util/triplet.h>
50
51#include <boost/algorithm/string.hpp>
52#include <boost/lexical_cast.hpp>
53#include <boost/scoped_ptr.hpp>
54#include <boost/shared_ptr.hpp>
55
56#include <iostream>
57#include <limits>
58#include <map>
59#include <netinet/in.h>
60#include <vector>
61
62#include <stdint.h>
63
64using namespace std;
65using namespace isc;
66using namespace isc::data;
67using namespace isc::dhcp;
68using namespace isc::asiolink;
69using namespace isc::hooks;
70using namespace isc::process;
71using namespace isc::config;
72using namespace isc::util;
73
74namespace {
75
80void dirExists(const string& dir_path) {
81 struct stat statbuf;
82 if (stat(dir_path.c_str(), &statbuf) < 0) {
83 isc_throw(BadValue, "Bad directory '" << dir_path
84 << "': " << strerror(errno));
85 }
86 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
87 isc_throw(BadValue, "'" << dir_path << "' is not a directory");
88 }
89}
90
99class RSOOListConfigParser : public isc::data::SimpleParser {
100public:
101
109 void parse(const SrvConfigPtr& cfg, const isc::data::ConstElementPtr& value) {
110 try {
111 for (auto const& source_elem : value->listValue()) {
112 std::string option_str = source_elem->stringValue();
113 // This option can be either code (integer) or name. Let's try code first
114 int64_t code = 0;
115 try {
116 code = boost::lexical_cast<int64_t>(option_str);
117 // Protect against the negative value and too high value.
118 if (code < 0) {
119 isc_throw(BadValue, "invalid option code value specified '"
120 << option_str << "', the option code must be a"
121 " non-negative value");
122
123 } else if (code > std::numeric_limits<uint16_t>::max()) {
124 isc_throw(BadValue, "invalid option code value specified '"
125 << option_str << "', the option code must not be"
126 " greater than '" << std::numeric_limits<uint16_t>::max()
127 << "'");
128 }
129
130 } catch (const boost::bad_lexical_cast &) {
131 // Oh well, it's not a number
132 }
133
134 if (!code) {
136 option_str);
137 if (def) {
138 code = def->getCode();
139 } else {
140 isc_throw(BadValue, "unable to find option code for the "
141 " specified option name '" << option_str << "'"
142 " while parsing the list of enabled"
143 " relay-supplied-options");
144 }
145 }
146 cfg->getCfgRSOO()->enable(code);
147 }
148 } catch (const std::exception& ex) {
149 // Rethrow exception with the appended position of the parsed
150 // element.
151 isc_throw(DhcpConfigError, ex.what() << " (" << value->getPosition() << ")");
152 }
153 }
154};
155
164class Dhcp6ConfigParser : public isc::data::SimpleParser {
165public:
166
181 void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
182
183 // Set the data directory for server id file.
184 if (global->contains("data-directory")) {
185 CfgMgr::instance().setDataDir(getString(global, "data-directory"),
186 false);
187 }
188
189 // Set the probation period for decline handling.
190 uint32_t probation_period =
191 getUint32(global, "decline-probation-period");
192 cfg->setDeclinePeriod(probation_period);
193
194 // Set the DHCPv4-over-DHCPv6 interserver port.
195 uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
196 cfg->setDhcp4o6Port(dhcp4o6_port);
197
198 // Set the global user context.
199 ConstElementPtr user_context = global->get("user-context");
200 if (user_context) {
201 cfg->setContext(user_context);
202 }
203
204 // Set the server's logical name
205 std::string server_tag = getString(global, "server-tag");
206 cfg->setServerTag(server_tag);
207 }
208
220 void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
221 // Set ip-reservations-unique flag.
222 bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
223 cfg->setIPReservationsUnique(ip_reservations_unique);
224 }
225
232 void
233 copySubnets6(const CfgSubnets6Ptr& dest, const CfgSharedNetworks6Ptr& from) {
234
235 if (!dest || !from) {
236 isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null");
237 }
238
239 const SharedNetwork6Collection* networks = from->getAll();
240 if (!networks) {
241 // Nothing to copy. Technically, it should return a pointer to empty
242 // container, but let's handle null pointer as well.
243 return;
244 }
245
246 // Let's go through all the networks one by one
247 for (auto const& net : *networks) {
248
249 // For each network go through all the subnets in it.
250 const Subnet6SimpleCollection* subnets = net->getAllSubnets();
251 if (!subnets) {
252 // Shared network without subnets it weird, but we decided to
253 // accept such configurations.
254 continue;
255 }
256
257 // For each subnet, add it to a list of regular subnets.
258 for (auto const& subnet : *subnets) {
259 dest->add(subnet);
260 }
261 }
262 }
263
272 void
273 sanityChecks(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
274
276 cfg->sanityChecksLifetime("preferred-lifetime");
277 cfg->sanityChecksLifetime("valid-lifetime");
278
280 const SharedNetwork6Collection* networks = cfg->getCfgSharedNetworks6()->getAll();
281 if (networks) {
282 sharedNetworksSanityChecks(*networks, global->get("shared-networks"));
283 }
284 }
285
292 void
293 sharedNetworksSanityChecks(const SharedNetwork6Collection& networks,
294 ConstElementPtr json) {
295
297 if (!json) {
298 // No json? That means that the shared-networks was never specified
299 // in the config.
300 return;
301 }
302
303 // Used for names uniqueness checks.
304 std::set<string> names;
305
306 // Let's go through all the networks one by one
307 for (auto const& net : networks) {
308 string txt;
309
310 // Let's check if all subnets have either the same interface
311 // or don't have the interface specified at all.
312 string iface = net->getIface();
313
314 const Subnet6SimpleCollection* subnets = net->getAllSubnets();
315 if (subnets) {
316
317 bool rapid_commit = false;
318
319 // Rapid commit must either be enabled or disabled in all subnets
320 // in the shared network.
321 if (subnets->size()) {
322 // If this is the first subnet, remember the value.
323 rapid_commit = (*subnets->begin())->getRapidCommit();
324 }
325
326 // For each subnet, add it to a list of regular subnets.
327 for (auto const& subnet : *subnets) {
328 // Ok, this is the second or following subnets. The value
329 // must match what was set in the first subnet.
330 if (rapid_commit != subnet->getRapidCommit()) {
331 isc_throw(DhcpConfigError, "All subnets in a shared network "
332 "must have the same rapid-commit value. Subnet "
333 << subnet->toText()
334 << " has specified rapid-commit "
335 << (subnet->getRapidCommit() ? "true" : "false")
336 << ", but earlier subnet in the same shared-network"
337 << " or the shared-network itself used rapid-commit "
338 << (rapid_commit ? "true" : "false"));
339 }
340
341 if (iface.empty()) {
342 iface = subnet->getIface();
343 continue;
344 }
345
346 if (subnet->getIface().empty()) {
347 continue;
348 }
349
350 if (subnet->getIface() != iface) {
351 isc_throw(DhcpConfigError, "Subnet " << subnet->toText()
352 << " has specified interface " << subnet->getIface()
353 << ", but earlier subnet in the same shared-network"
354 << " or the shared-network itself used " << iface);
355 }
356
357 // Let's collect the subnets in case we later find out the
358 // subnet doesn't have a mandatory name.
359 txt += subnet->toText() + " ";
360 }
361 }
362
363 // Next, let's check name of the shared network.
364 if (net->getName().empty()) {
365 isc_throw(DhcpConfigError, "Shared-network with subnets "
366 << txt << " is missing mandatory 'name' parameter");
367 }
368
369 // Is it unique?
370 if (names.find(net->getName()) != names.end()) {
371 isc_throw(DhcpConfigError, "A shared-network with "
372 "name " << net->getName() << " defined twice.");
373 }
374 names.insert(net->getName());
375
376 }
377 }
378};
379
380} // anonymous namespace
381
382namespace isc {
383namespace dhcp {
384
393 // Get new socket configuration.
394 ConstElementPtr sock_cfg =
395 CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
396
397 // Get current socket configuration.
398 ConstElementPtr current_sock_cfg =
399 CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();
400
401 // Determine if the socket configuration has changed. It has if
402 // both old and new configuration is specified but respective
403 // data elements aren't equal.
404 bool sock_changed = (sock_cfg && current_sock_cfg &&
405 !sock_cfg->equals(*current_sock_cfg));
406
407 // If the previous or new socket configuration doesn't exist or
408 // the new configuration differs from the old configuration we
409 // close the existing socket and open a new socket as appropriate.
410 // Note that closing an existing socket means the client will not
411 // receive the configuration result.
412 if (!sock_cfg || !current_sock_cfg || sock_changed) {
413 // Close the existing socket (if any).
415
416 if (sock_cfg) {
417 // This will create a control socket and install the external
418 // socket in IfaceMgr. That socket will be monitored when
419 // Dhcp6Srv::receivePacket() calls IfaceMgr::receive6() and
420 // callback in CommandMgr will be called, if necessary.
422 }
423 }
424}
425
432 // Revert any runtime option definitions configured so far and not committed.
434 // Let's set empty container in case a user hasn't specified any configuration
435 // for option definitions. This is equivalent to committing empty container.
437
438 // Print the list of known backends.
440
441 // Answer will hold the result.
443
444 // Global parameter name in case of an error.
445 string parameter_name;
446 ElementPtr mutable_cfg;
447 SrvConfigPtr srv_config;
448 try {
449 // Get the staging configuration.
450 srv_config = CfgMgr::instance().getStagingCfg();
451
452 // This is a way to convert ConstElementPtr to ElementPtr.
453 // We need a config that can be edited, because we will insert
454 // default values and will insert derived values as well.
455 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
456
457 // Set all default values if not specified by the user.
459
460 // And now derive (inherit) global parameters to subnets, if not specified.
462
463 // In principle we could have the following code structured as a series
464 // of long if else if clauses. That would give a marginal performance
465 // boost, but would make the code less readable. We had serious issues
466 // with the parser code debugability, so I decided to keep it as a
467 // series of independent ifs.
468
469 // This parser is used in several places.
470 Dhcp6ConfigParser global_parser;
471
472 // Apply global options in the staging config, e.g. ip-reservations-unique
473 global_parser.parseEarly(srv_config, mutable_cfg);
474
475 // Specific check for this global parameter.
476 ConstElementPtr data_directory = mutable_cfg->get("data-directory");
477 if (data_directory) {
478 parameter_name = "data-directory";
479 dirExists(data_directory->stringValue());
480 }
481
482 // We need definitions first
483 ConstElementPtr option_defs = mutable_cfg->get("option-def");
484 if (option_defs) {
485 parameter_name = "option-def";
486 OptionDefListParser parser(AF_INET6);
487 CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
488 parser.parse(cfg_option_def, option_defs);
489 }
490
491 ConstElementPtr option_datas = mutable_cfg->get("option-data");
492 if (option_datas) {
493 parameter_name = "option-data";
494 OptionDataListParser parser(AF_INET6);
495 CfgOptionPtr cfg_option = srv_config->getCfgOption();
496 parser.parse(cfg_option, option_datas);
497 }
498
499 ConstElementPtr mac_sources = mutable_cfg->get("mac-sources");
500 if (mac_sources) {
501 parameter_name = "mac-sources";
503 CfgMACSource& mac_source = srv_config->getMACSources();
504 parser.parse(mac_source, mac_sources);
505 }
506
507 ConstElementPtr control_socket = mutable_cfg->get("control-socket");
508 if (control_socket) {
509 parameter_name = "control-socket";
510 ControlSocketParser parser;
511 parser.parse(*srv_config, control_socket);
512 }
513
514 ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
515 if (multi_threading) {
516 parameter_name = "multi-threading";
518 parser.parse(*srv_config, multi_threading);
519 }
520
521 bool multi_threading_enabled = true;
522 uint32_t thread_count = 0;
523 uint32_t queue_size = 0;
524 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
525 multi_threading_enabled, thread_count, queue_size);
526
528 ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
529 if (queue_control) {
530 parameter_name = "dhcp-queue-control";
532 srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
533 }
534
536 ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
537 if (reservations_lookup_first) {
538 parameter_name = "reservations-lookup-first";
539 if (multi_threading_enabled) {
541 }
542 srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
543 }
544
545 ConstElementPtr hr_identifiers =
546 mutable_cfg->get("host-reservation-identifiers");
547 if (hr_identifiers) {
548 parameter_name = "host-reservation-identifiers";
550 parser.parse(hr_identifiers);
551 }
552
553 ConstElementPtr server_id = mutable_cfg->get("server-id");
554 if (server_id) {
555 parameter_name = "server-id";
556 DUIDConfigParser parser;
557 const CfgDUIDPtr& cfg = srv_config->getCfgDUID();
558 parser.parse(cfg, server_id);
559 }
560
561 ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
562 if (sanity_checks) {
563 parameter_name = "sanity-checks";
564 SanityChecksParser parser;
565 parser.parse(*srv_config, sanity_checks);
566 }
567
568 ConstElementPtr expiration_cfg =
569 mutable_cfg->get("expired-leases-processing");
570 if (expiration_cfg) {
571 parameter_name = "expired-leases-processing";
573 parser.parse(expiration_cfg, CfgMgr::instance().getStagingCfg()->getCfgExpiration());
574 }
575
576 // The hooks-libraries configuration must be parsed after parsing
577 // multi-threading configuration so that libraries are checked
578 // for multi-threading compatibility.
579 ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
580 if (hooks_libraries) {
581 parameter_name = "hooks-libraries";
582 HooksLibrariesParser hooks_parser;
583 HooksConfig& libraries = srv_config->getHooksConfig();
584 hooks_parser.parse(libraries, hooks_libraries);
585 libraries.verifyLibraries(hooks_libraries->getPosition(),
586 multi_threading_enabled);
587 }
588
589 // D2 client configuration.
590 D2ClientConfigPtr d2_client_cfg;
591
592 // Legacy DhcpConfigParser stuff below.
593 ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
594 if (dhcp_ddns) {
595 parameter_name = "dhcp-ddns";
596 // Apply defaults
599 d2_client_cfg = parser.parse(dhcp_ddns);
600 }
601
602 ConstElementPtr client_classes = mutable_cfg->get("client-classes");
603 if (client_classes) {
604 parameter_name = "client-classes";
606 ClientClassDictionaryPtr dictionary =
607 parser.parse(client_classes, AF_INET6);
608 srv_config->setClientClassDictionary(dictionary);
609 }
610
611 // Please move at the end when migration will be finished.
612 ConstElementPtr lease_database = mutable_cfg->get("lease-database");
613 if (lease_database) {
614 parameter_name = "lease-database";
615 db::DbAccessParser parser;
616 std::string access_string;
617 parser.parse(access_string, lease_database);
618 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
619 cfg_db_access->setLeaseDbAccessString(access_string);
620 }
621
622 ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
623 if (hosts_database) {
624 parameter_name = "hosts-database";
625 db::DbAccessParser parser;
626 std::string access_string;
627 parser.parse(access_string, hosts_database);
628 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
629 cfg_db_access->setHostDbAccessString(access_string);
630 }
631
632 ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
633 if (hosts_databases) {
634 parameter_name = "hosts-databases";
635 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
636 for (auto const& it : hosts_databases->listValue()) {
637 db::DbAccessParser parser;
638 std::string access_string;
639 parser.parse(access_string, it);
640 cfg_db_access->setHostDbAccessString(access_string);
641 }
642 }
643
644 // Keep relative orders of shared networks and subnets.
645 ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
646 if (shared_networks) {
647 parameter_name = "shared-networks";
654 CfgSharedNetworks6Ptr cfg = srv_config->getCfgSharedNetworks6();
655 parser.parse(cfg, shared_networks);
656
657 // We also need to put the subnets it contains into normal
658 // subnets list.
659 global_parser.copySubnets6(srv_config->getCfgSubnets6(), cfg);
660 }
661
662 ConstElementPtr subnet6 = mutable_cfg->get("subnet6");
663 if (subnet6) {
664 parameter_name = "subnet6";
665 Subnets6ListConfigParser subnets_parser;
666 // parse() returns number of subnets parsed. We may log it one day.
667 subnets_parser.parse(srv_config, subnet6);
668 }
669
670 ConstElementPtr reservations = mutable_cfg->get("reservations");
671 if (reservations) {
672 parameter_name = "reservations";
673 HostCollection hosts;
675 parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
676 for (auto const& h : hosts) {
677 srv_config->getCfgHosts()->add(h);
678 }
679 }
680
681 ConstElementPtr config_control = mutable_cfg->get("config-control");
682 if (config_control) {
683 parameter_name = "config-control";
684 ConfigControlParser parser;
685 ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
686 CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
687 }
688
689 ConstElementPtr rsoo_list = mutable_cfg->get("relay-supplied-options");
690 if (rsoo_list) {
691 parameter_name = "relay-supplied-options";
692 RSOOListConfigParser parser;
693 parser.parse(srv_config, rsoo_list);
694 }
695
696 ConstElementPtr compatibility = mutable_cfg->get("compatibility");
697 if (compatibility) {
698 CompatibilityParser parser;
699 parser.parse(compatibility, *CfgMgr::instance().getStagingCfg());
700 }
701
702 // Make parsers grouping.
703 const std::map<std::string, ConstElementPtr>& values_map =
704 mutable_cfg->mapValue();
705
706 for (auto const& config_pair : values_map) {
707 parameter_name = config_pair.first;
708
709 // These are converted to SimpleParser and are handled already above.
710 if ((config_pair.first == "data-directory") ||
711 (config_pair.first == "option-def") ||
712 (config_pair.first == "option-data") ||
713 (config_pair.first == "mac-sources") ||
714 (config_pair.first == "control-socket") ||
715 (config_pair.first == "multi-threading") ||
716 (config_pair.first == "dhcp-queue-control") ||
717 (config_pair.first == "host-reservation-identifiers") ||
718 (config_pair.first == "server-id") ||
719 (config_pair.first == "interfaces-config") ||
720 (config_pair.first == "sanity-checks") ||
721 (config_pair.first == "expired-leases-processing") ||
722 (config_pair.first == "hooks-libraries") ||
723 (config_pair.first == "dhcp-ddns") ||
724 (config_pair.first == "client-classes") ||
725 (config_pair.first == "lease-database") ||
726 (config_pair.first == "hosts-database") ||
727 (config_pair.first == "hosts-databases") ||
728 (config_pair.first == "subnet6") ||
729 (config_pair.first == "shared-networks") ||
730 (config_pair.first == "reservations") ||
731 (config_pair.first == "config-control") ||
732 (config_pair.first == "relay-supplied-options") ||
733 (config_pair.first == "loggers") ||
734 (config_pair.first == "compatibility")) {
735 continue;
736 }
737
738 // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
739 // The old method is used in JSON configuration parsers when the global
740 // parameters are derived into the subnets and shared networks and are
741 // being treated as explicitly specified. The new way used by the config
742 // backend is the dynamic inheritance whereby each subnet and shared
743 // network uses a callback function to return global parameter if it
744 // is not specified at lower level. This callback uses configured globals.
745 // We deliberately include both default and explicitly specified globals
746 // so as the callback can access the appropriate global values regardless
747 // whether they are set to a default or other value.
748 if ( (config_pair.first == "renew-timer") ||
749 (config_pair.first == "rebind-timer") ||
750 (config_pair.first == "preferred-lifetime") ||
751 (config_pair.first == "min-preferred-lifetime") ||
752 (config_pair.first == "max-preferred-lifetime") ||
753 (config_pair.first == "valid-lifetime") ||
754 (config_pair.first == "min-valid-lifetime") ||
755 (config_pair.first == "max-valid-lifetime") ||
756 (config_pair.first == "decline-probation-period") ||
757 (config_pair.first == "dhcp4o6-port") ||
758 (config_pair.first == "server-tag") ||
759 (config_pair.first == "reservations-global") ||
760 (config_pair.first == "reservations-in-subnet") ||
761 (config_pair.first == "reservations-out-of-pool") ||
762 (config_pair.first == "calculate-tee-times") ||
763 (config_pair.first == "t1-percent") ||
764 (config_pair.first == "t2-percent") ||
765 (config_pair.first == "cache-threshold") ||
766 (config_pair.first == "cache-max-age") ||
767 (config_pair.first == "hostname-char-set") ||
768 (config_pair.first == "hostname-char-replacement") ||
769 (config_pair.first == "ddns-send-updates") ||
770 (config_pair.first == "ddns-override-no-update") ||
771 (config_pair.first == "ddns-override-client-update") ||
772 (config_pair.first == "ddns-replace-client-name") ||
773 (config_pair.first == "ddns-generated-prefix") ||
774 (config_pair.first == "ddns-qualifying-suffix") ||
775 (config_pair.first == "ddns-update-on-renew") ||
776 (config_pair.first == "ddns-use-conflict-resolution") ||
777 (config_pair.first == "ddns-conflict-resolution-mode") ||
778 (config_pair.first == "ddns-ttl-percent") ||
779 (config_pair.first == "store-extended-info") ||
780 (config_pair.first == "statistic-default-sample-count") ||
781 (config_pair.first == "statistic-default-sample-age") ||
782 (config_pair.first == "early-global-reservations-lookup") ||
783 (config_pair.first == "ip-reservations-unique") ||
784 (config_pair.first == "reservations-lookup-first") ||
785 (config_pair.first == "parked-packet-limit") ||
786 (config_pair.first == "allocator") ||
787 (config_pair.first == "pd-allocator") ) {
788 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
789 config_pair.second);
790 continue;
791 }
792
793 // Nothing to configure for the user-context.
794 if (config_pair.first == "user-context") {
795 continue;
796 }
797
798 // If we got here, no code handled this parameter, so we bail out.
800 "unsupported global configuration parameter: " << config_pair.first
801 << " (" << config_pair.second->getPosition() << ")");
802 }
803
804 // Reset parameter name.
805 parameter_name = "<post parsing>";
806
807 // Apply global options in the staging config.
808 global_parser.parse(srv_config, mutable_cfg);
809
810 // This method conducts final sanity checks and tweaks. In particular,
811 // it checks that there is no conflict between plain subnets and those
812 // defined as part of shared networks.
813 global_parser.sanityChecks(srv_config, mutable_cfg);
814
815 // Validate D2 client configuration.
816 if (!d2_client_cfg) {
817 d2_client_cfg.reset(new D2ClientConfig());
818 }
819 d2_client_cfg->validateContents();
820 srv_config->setD2ClientConfig(d2_client_cfg);
821 } catch (const isc::Exception& ex) {
823 .arg(parameter_name).arg(ex.what());
825 } catch (...) {
826 // For things like bad_cast in boost::lexical_cast
827 LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(parameter_name);
828 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
829 "processing error");
830 }
831
832 if (!answer) {
833 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
834 "Control-socket, hook-libraries, and D2 configuration "
835 "were sanity checked, but not applied.");
836 }
837
838 return (answer);
839}
840
843 bool check_only, bool extra_checks) {
844 if (!config_set) {
846 "Can't parse NULL config");
847 return (answer);
848 }
849
851 .arg(server.redactConfig(config_set)->str());
852
853 auto answer = processDhcp6Config(config_set);
854
855 int status_code = CONTROL_RESULT_SUCCESS;
856 isc::config::parseAnswer(status_code, answer);
857
858 SrvConfigPtr srv_config;
859
860 if (status_code == CONTROL_RESULT_SUCCESS) {
861 if (check_only) {
862 if (extra_checks) {
863 // Re-open lease and host database with new parameters.
864 try {
865 // Get the staging configuration.
866 srv_config = CfgMgr::instance().getStagingCfg();
867
868 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
869 string params = "universe=6 persist=false";
870 // The "extended-info-tables" has no effect on -T command
871 // line parameter so it is omitted on purpose.
872 // Note that in this case, the current code creates managers
873 // before hooks are loaded, so it can not be activated by
874 // the BLQ hook.
875 cfg_db->setAppendedParameters(params);
876 cfg_db->createManagers();
877 } catch (const std::exception& ex) {
879 status_code = CONTROL_RESULT_ERROR;
880 }
881
882 if (status_code == CONTROL_RESULT_SUCCESS) {
883 std::ostringstream err;
884 // Configure DHCP packet queueing
885 try {
887 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
888 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) {
890 .arg(IfaceMgr::instance().getPacketQueue6()->getInfoStr());
891 }
892
893 } catch (const std::exception& ex) {
894 err << "Error setting packet queue controls after server reconfiguration: "
895 << ex.what();
897 status_code = CONTROL_RESULT_ERROR;
898 }
899 }
900 }
901 } else {
902 // disable multi-threading (it will be applied by new configuration)
903 // this must be done in order to properly handle MT to ST transition
904 // when 'multi-threading' structure is missing from new config and
905 // to properly drop any task items stored in the thread pool which
906 // might reference some handles to loaded hooks, preventing them
907 // from being unloaded.
908 MultiThreadingMgr::instance().apply(false, 0, 0);
909
910 // Close DHCP sockets and remove any existing timers.
912 TimerMgr::instance()->unregisterTimers();
913 server.discardPackets();
914 server.getCBControl()->reset();
915 }
916
917 if (status_code == CONTROL_RESULT_SUCCESS) {
918 string parameter_name;
919 ElementPtr mutable_cfg;
920 try {
921 // Get the staging configuration.
922 srv_config = CfgMgr::instance().getStagingCfg();
923
924 // This is a way to convert ConstElementPtr to ElementPtr.
925 // We need a config that can be edited, because we will insert
926 // default values and will insert derived values as well.
927 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
928
929 ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
930 if (ifaces_config) {
931 parameter_name = "interfaces-config";
932 IfacesConfigParser parser(AF_INET6, check_only);
933 CfgIfacePtr cfg_iface = srv_config->getCfgIface();
934 cfg_iface->reset();
935 parser.parse(cfg_iface, ifaces_config);
936 }
937 } catch (const isc::Exception& ex) {
939 .arg(parameter_name).arg(ex.what());
941 status_code = CONTROL_RESULT_ERROR;
942 } catch (...) {
943 // For things like bad_cast in boost::lexical_cast
944 LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(parameter_name);
945 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
946 " processing error");
947 status_code = CONTROL_RESULT_ERROR;
948 }
949 }
950 }
951
952 // So far so good, there was no parsing error so let's commit the
953 // configuration. This will add created subnets and option values into
954 // the server's configuration.
955 // This operation should be exception safe but let's make sure.
956 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
957 try {
958
959 // Setup the command channel.
961 } catch (const isc::Exception& ex) {
964 status_code = CONTROL_RESULT_ERROR;
965 } catch (...) {
966 // For things like bad_cast in boost::lexical_cast
968 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
969 " parsing error");
970 status_code = CONTROL_RESULT_ERROR;
971 }
972 }
973
974 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
975 try {
976 // No need to commit interface names as this is handled by the
977 // CfgMgr::commit() function.
978
979 // Apply the staged D2ClientConfig, used to be done by parser commit
981 cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
983 } catch (const isc::Exception& ex) {
986 status_code = CONTROL_RESULT_ERROR;
987 } catch (...) {
988 // For things like bad_cast in boost::lexical_cast
990 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
991 " parsing error");
992 status_code = CONTROL_RESULT_ERROR;
993 }
994 }
995
996 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
997 try {
998 // This occurs last as if it succeeds, there is no easy way to
999 // revert it. As a result, the failure to commit a subsequent
1000 // change causes problems when trying to roll back.
1002 static_cast<void>(HooksManager::unloadLibraries());
1004 const HooksConfig& libraries =
1005 CfgMgr::instance().getStagingCfg()->getHooksConfig();
1006 bool multi_threading_enabled = true;
1007 uint32_t thread_count = 0;
1008 uint32_t queue_size = 0;
1009 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
1010 multi_threading_enabled, thread_count, queue_size);
1011 libraries.loadLibraries(multi_threading_enabled);
1012 } catch (const isc::Exception& ex) {
1015 status_code = CONTROL_RESULT_ERROR;
1016 } catch (...) {
1017 // For things like bad_cast in boost::lexical_cast
1019 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
1020 " parsing error");
1021 status_code = CONTROL_RESULT_ERROR;
1022 }
1023 }
1024
1025 // Moved from the commit block to add the config backend indication.
1026 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
1027 try {
1028 // If there are config backends, fetch and merge into staging config
1029 server.getCBControl()->databaseConfigFetch(srv_config,
1030 CBControlDHCPv6::FetchMode::FETCH_ALL);
1031 } catch (const isc::Exception& ex) {
1032 std::ostringstream err;
1033 err << "during update from config backend database: " << ex.what();
1036 status_code = CONTROL_RESULT_ERROR;
1037 } catch (...) {
1038 // For things like bad_cast in boost::lexical_cast
1039 std::ostringstream err;
1040 err << "during update from config backend database: "
1041 << "undefined configuration parsing error";
1044 status_code = CONTROL_RESULT_ERROR;
1045 }
1046 }
1047
1048 // Rollback changes as the configuration parsing failed.
1049 if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
1050 // Revert to original configuration of runtime option definitions
1051 // in the libdhcp++.
1053
1054 if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
1055 auto notify_libraries = ControlledDhcpv6Srv::finishConfigHookLibraries(config_set);
1056 if (notify_libraries) {
1057 return (notify_libraries);
1058 }
1059
1061 try {
1062 // Handle events registered by hooks using external IOService objects.
1064 } catch (const std::exception& ex) {
1065 std::ostringstream err;
1066 err << "Error initializing hooks: "
1067 << ex.what();
1069 }
1070 }
1071
1072 return (answer);
1073 }
1074
1076 .arg(CfgMgr::instance().getStagingCfg()->
1077 getConfigSummary(SrvConfig::CFGSEL_ALL6));
1078
1079 // Also calculate SHA256 hash of the config that was just set and
1080 // append it to the response.
1082 string hash = BaseCommandMgr::getHash(config);
1083 ElementPtr hash_map = Element::createMap();
1084 hash_map->set("hash", Element::create(hash));
1085
1086 // Everything was fine. Configuration is successful.
1087 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
1088 return (answer);
1089}
1090
1091} // namespace dhcp
1092} // namespace isc
it forwards queries to a single upstream resolver and passes the answers back to the client It is constructed with the address of the forward server Queries are initiated with the question to ask the forward server
Definition asiodns.dox:60
it forwards queries to a single upstream resolver and passes the answers back to the client It is constructed with the address of the forward server Queries are initiated with the question to ask the forward a buffer into which to write the answer
Definition asiodns.dox:60
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
static std::string getHash(const isc::data::ConstElementPtr &config)
returns a hash of a given Element structure
void closeCommandSocket()
Shuts down any open control sockets.
static CommandMgr & instance()
CommandMgr is a singleton class.
void openCommandSocket(const isc::data::ConstElementPtr &socket_info)
Opens control socket with parameters specified in socket_info.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
Parse Database Parameters.
void parse(std::string &access_string, isc::data::ConstElementPtr database_config)
Parse configuration value.
Wrapper class that holds MAC/hardware address sources.
void setD2ClientConfig(D2ClientConfigPtr &new_config)
Updates the DHCP-DDNS client configuration to the given value.
Definition cfgmgr.cc:44
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:28
void setDataDir(const std::string &datadir, bool unspecified=true)
Sets new data directory.
Definition cfgmgr.cc:39
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition cfgmgr.cc:120
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:115
static void extract(data::ConstElementPtr value, bool &enabled, uint32_t &thread_count, uint32_t &queue_size)
Extract multi-threading parameters from a given configuration.
Parser for a list of client class definitions.
ClientClassDictionaryPtr parse(isc::data::ConstElementPtr class_def_list, uint16_t family, bool check_dependencies=true)
Parse configuration entries.
void parse(isc::data::ConstElementPtr cfg, isc::dhcp::SrvConfig &srv_cfg)
Parse compatibility flags.
Parser for the control-socket structure.
void parse(SrvConfig &srv_cfg, isc::data::ConstElementPtr value)
"Parses" control-socket structure
static isc::data::ConstElementPtr finishConfigHookLibraries(isc::data::ConstElementPtr config)
Configuration checker for hook libraries.
Parser for D2ClientConfig.
D2ClientConfigPtr parse(isc::data::ConstElementPtr d2_client_cfg)
Parses a given dhcp-ddns element into D2ClientConfig.
static size_t setAllDefaults(isc::data::ConstElementPtr d2_config)
Sets all defaults for D2 client configuration.
Acts as a storage vault for D2 client configuration.
Parser for the configuration of DHCP packet queue controls.
data::ElementPtr parse(const isc::data::ConstElementPtr &control_elem, bool multi_threading_enabled)
Parses content of the "dhcp-queue-control".
Parser for server DUID configuration.
void parse(const CfgDUIDPtr &cfg, isc::data::ConstElementPtr duid_configuration)
Parses DUID configuration.
To be removed. Please use ConfigError instead.
DHCPv6 server service.
Definition dhcp6_srv.h:66
Parser for the configuration parameters pertaining to the processing of expired leases.
void parse(isc::data::ConstElementPtr expiration_config, isc::dhcp::CfgExpirationPtr expiration)
Parses parameters in the JSON map, pertaining to the processing of the expired leases.
static void printRegistered()
Prints out all registered backends.
Parser for a list of host identifiers for DHCPv6.
void parse(isc::data::ConstElementPtr ids_list)
Parses a list of host identifiers.
Parser for a list of host reservations for a subnet.
void parse(const SubnetID &subnet_id, isc::data::ConstElementPtr hr_list, HostCollection &hosts_list)
Parses a list of host reservation entries for a subnet.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
void closeSockets()
Closes all open sockets.
Definition iface_mgr.cc:286
Parser for the configuration of interfaces.
void parse(const CfgIfacePtr &config, const isc::data::ConstElementPtr &values)
Parses content of the "interfaces-config".
static void setRuntimeOptionDefs(const OptionDefSpaceContainer &defs)
Copies option definitions created at runtime.
Definition libdhcp++.cc:218
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition libdhcp++.cc:126
static void revertRuntimeOptionDefs()
Reverts uncommitted changes to runtime option definitions.
Definition libdhcp++.cc:237
parser for MAC/hardware acquisition sources
void parse(CfgMACSource &mac_sources, isc::data::ConstElementPtr value)
parses parameters value
Simple parser for multi-threading structure.
void parse(SrvConfig &srv_cfg, const isc::data::ConstElementPtr &value)
parses JSON structure.
Parser for option data values within a subnet.
void parse(const CfgOptionPtr &cfg, isc::data::ConstElementPtr option_data_list, bool encapsulate=true)
Parses a list of options, instantiates them and stores in cfg.
Parser for a list of option definitions.
void parse(CfgOptionDefPtr cfg, isc::data::ConstElementPtr def_list)
Parses a list of option definitions, create them and store in cfg.
Class of option definition space container.
Simple parser for sanity-checks structure.
void parse(SrvConfig &srv_cfg, const isc::data::ConstElementPtr &value)
parses JSON structure
void parse(CfgSharedNetworksTypePtr &cfg, const data::ConstElementPtr &shared_networks_list_data)
Parses a list of shared networks.
static size_t deriveParameters(isc::data::ElementPtr global)
Derives (inherits) all parameters from global to more specific scopes.
static size_t setAllDefaults(isc::data::ElementPtr global)
Sets all defaults for DHCPv6 configuration.
static const uint32_t CFGSEL_ALL6
IPv6 related config.
Definition srv_config.h:210
this class parses a list of DHCP6 subnets
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list, bool encapsulate_options=true)
parses contents of the list
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition timer_mgr.cc:446
Wrapper class that holds hooks libraries configuration.
void verifyLibraries(const isc::data::Element::Position &position, bool multi_threading_enabled) const
Verifies that libraries stored in libraries_ are valid.
void loadLibraries(bool multi_threading_enabled) const
Commits hooks libraries configuration.
Parser for hooks library list.
void parse(HooksConfig &libraries, isc::data::ConstElementPtr value)
Parses parameters value.
static bool unloadLibraries()
Unload libraries.
static void prepareUnloadLibraries()
Prepare the unloading of libraries.
Implements parser for config control information, "config-control".
ConfigControlInfoPtr parse(const data::ConstElementPtr &config_control)
Parses a configuration control Element.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
void apply(bool enabled, uint32_t thread_count, uint32_t queue_size)
Apply the multi-threading related settings.
Parsers for client class definitions.
This file contains several functions and constants that are used for handling commands and responses ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Logging initialization functions.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns arguments or text status code.
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
Creates a standard config/command level answer message.
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
void configureCommandChannel()
Initialize the command channel based on the staging configuration.
boost::shared_ptr< CfgDUID > CfgDUIDPtr
Pointer to the Non-const object.
Definition cfg_duid.h:161
const isc::log::MessageID DHCP6_PARSER_FAIL
const isc::log::MessageID DHCP6_PARSER_EXCEPTION
boost::shared_ptr< D2ClientConfig > D2ClientConfigPtr
Defines a pointer for D2ClientConfig instances.
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition cfg_option.h:803
boost::multi_index_container< SharedNetwork6Ptr, boost::multi_index::indexed_by< boost::multi_index::random_access< boost::multi_index::tag< SharedNetworkRandomAccessIndexTag > >, boost::multi_index::hashed_non_unique< boost::multi_index::tag< SharedNetworkIdIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, uint64_t, &data::BaseStampedElement::getId > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SharedNetworkNameIndexTag >, boost::multi_index::const_mem_fun< SharedNetwork6, std::string, &SharedNetwork6::getName > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SharedNetworkModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > > > > SharedNetwork6Collection
Multi index container holding shared networks.
isc::data::ConstElementPtr configureDhcp6Server(Dhcpv6Srv &server, isc::data::ConstElementPtr config_set, bool check_only, bool extra_checks)
Configure DHCPv6 server (Dhcpv6Srv) with a set of configuration values.
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
isc::data::ConstElementPtr processDhcp6Config(isc::data::ConstElementPtr config_set)
Process a DHCPv6 confguration and return an answer stating if the configuration is valid,...
const int DBG_DHCP6_COMMAND
Debug level used to log receiving commands.
Definition dhcp6_log.h:28
const isc::log::MessageID DHCP6_CONFIG_COMPLETE
boost::shared_ptr< CfgIface > CfgIfacePtr
A pointer to the CfgIface .
Definition cfg_iface.h:501
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
boost::shared_ptr< CfgSubnets6 > CfgSubnets6Ptr
Non-const pointer.
std::vector< HostPtr > HostCollection
Collection of the Host objects.
Definition host.h:816
const isc::log::MessageID DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
boost::shared_ptr< CfgSharedNetworks6 > CfgSharedNetworks6Ptr
Pointer to the configuration of IPv6 shared networks.
boost::multi_index_container< Subnet6Ptr, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetSubnetIdIndexTag >, boost::multi_index::const_mem_fun< Subnet, SubnetID, &Subnet::getID > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetPrefixIndexTag >, boost::multi_index::const_mem_fun< Subnet, std::string, &Subnet::toText > > > > Subnet6SimpleCollection
A simple collection of Subnet6 objects.
Definition subnet.h:887
const isc::log::MessageID DHCP6_PARSER_COMMIT_EXCEPTION
const isc::log::MessageID DHCP6_CONFIG_START
SharedNetworksListParser< SharedNetwork6Parser > SharedNetworks6ListParser
Type of the shared networks list parser for IPv6.
const isc::log::MessageID DHCP6_PARSER_COMMIT_FAIL
isc::log::Logger dhcp6_logger(DHCP6_APP_LOGGER_NAME)
Base logger for DHCPv6 server.
Definition dhcp6_log.h:88
const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE
boost::shared_ptr< ConfigControlInfo > ConfigControlInfoPtr
Defines a pointer to a ConfigControlInfo.
Defines the logger used by the top-level component of kea-lfc.
#define DHCP6_OPTION_SPACE