Kea 2.7.3
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>
18#include <dhcp6/dhcp6_log.h>
19#include <dhcp6/dhcp6_srv.h>
21#include <dhcp/libdhcp++.h>
22#include <dhcp/iface_mgr.h>
25#include <dhcpsrv/cfg_option.h>
26#include <dhcpsrv/cfgmgr.h>
27#include <dhcpsrv/db_type.h>
43#include <dhcpsrv/pool.h>
44#include <dhcpsrv/subnet.h>
45#include <dhcpsrv/timer_mgr.h>
46#include <hooks/hooks_manager.h>
47#include <hooks/hooks_parser.h>
48#include <log/logger_support.h>
50#include <util/encode/encode.h>
52#include <util/triplet.h>
53
54#ifdef HAVE_MYSQL
55#include <hooks/dhcp/mysql_lb/mysql_lease_mgr.h>
56#include <hooks/dhcp/mysql_hb/mysql_host_data_source.h>
57#endif
58
59#ifdef HAVE_PGSQL
60#include <hooks/dhcp/pgsql_lb/pgsql_lease_mgr.h>
61#include <hooks/dhcp/pgsql_hb/pgsql_host_data_source.h>
62#endif
63
64#include <boost/algorithm/string.hpp>
65#include <boost/lexical_cast.hpp>
66#include <boost/scoped_ptr.hpp>
67#include <boost/shared_ptr.hpp>
68
69#include <iostream>
70#include <limits>
71#include <map>
72#include <netinet/in.h>
73#include <vector>
74
75#include <stdint.h>
76
77using namespace isc::asiolink;
78using namespace isc::config;
79using namespace isc::data;
80using namespace isc::db;
81using namespace isc::dhcp;
82using namespace isc::hooks;
83using namespace isc::process;
84using namespace isc::util;
85using namespace isc;
86using namespace std;
87
88//
89// Register database backends
90//
91namespace {
92
93// Code will be moved to appropriate hook library.
94#ifdef HAVE_MYSQL
95// Database backend will be registered at object initialization
96MySqlLeaseMgrInit mysql_init_lease;
97MySqlHostDataSourceInit mysql_init_host;
98#endif
99
100// Code will be moved to appropriate hook library.
101#ifdef HAVE_PGSQL
102// Database backend will be registered at object initialization
103PgSqlLeaseMgrInit pgsql_init_lease;
104PgSqlHostDataSourceInit pgsql_init_host;
105#endif
106
111void dirExists(const string& dir_path) {
112 struct stat statbuf;
113 if (stat(dir_path.c_str(), &statbuf) < 0) {
114 isc_throw(BadValue, "Bad directory '" << dir_path
115 << "': " << strerror(errno));
116 }
117 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
118 isc_throw(BadValue, "'" << dir_path << "' is not a directory");
119 }
120}
121
130class RSOOListConfigParser : public isc::data::SimpleParser {
131public:
132
140 void parse(const SrvConfigPtr& cfg, const isc::data::ConstElementPtr& value) {
141 try {
142 for (auto const& source_elem : value->listValue()) {
143 std::string option_str = source_elem->stringValue();
144 // This option can be either code (integer) or name. Let's try code first
145 int64_t code = 0;
146 try {
147 code = boost::lexical_cast<int64_t>(option_str);
148 // Protect against the negative value and too high value.
149 if (code < 0) {
150 isc_throw(BadValue, "invalid option code value specified '"
151 << option_str << "', the option code must be a"
152 " non-negative value");
153
154 } else if (code > std::numeric_limits<uint16_t>::max()) {
155 isc_throw(BadValue, "invalid option code value specified '"
156 << option_str << "', the option code must not be"
157 " greater than '" << std::numeric_limits<uint16_t>::max()
158 << "'");
159 }
160
161 } catch (const boost::bad_lexical_cast &) {
162 // Oh well, it's not a number
163 }
164
165 if (!code) {
167 option_str);
168 if (def) {
169 code = def->getCode();
170 } else {
171 isc_throw(BadValue, "unable to find option code for the "
172 " specified option name '" << option_str << "'"
173 " while parsing the list of enabled"
174 " relay-supplied-options");
175 }
176 }
177 cfg->getCfgRSOO()->enable(code);
178 }
179 } catch (const std::exception& ex) {
180 // Rethrow exception with the appended position of the parsed
181 // element.
182 isc_throw(DhcpConfigError, ex.what() << " (" << value->getPosition() << ")");
183 }
184 }
185};
186
195class Dhcp6ConfigParser : public isc::data::SimpleParser {
196public:
197
212 void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
213
214 // Set the data directory for server id file.
215 if (global->contains("data-directory")) {
216 CfgMgr::instance().setDataDir(getString(global, "data-directory"),
217 false);
218 }
219
220 // Set the probation period for decline handling.
221 uint32_t probation_period =
222 getUint32(global, "decline-probation-period");
223 cfg->setDeclinePeriod(probation_period);
224
225 // Set the DHCPv4-over-DHCPv6 interserver port.
226 uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
227 cfg->setDhcp4o6Port(dhcp4o6_port);
228
229 // Set the global user context.
230 ConstElementPtr user_context = global->get("user-context");
231 if (user_context) {
232 cfg->setContext(user_context);
233 }
234
235 // Set the server's logical name
236 std::string server_tag = getString(global, "server-tag");
237 cfg->setServerTag(server_tag);
238 }
239
251 void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
252 // Set ip-reservations-unique flag.
253 bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
254 cfg->setIPReservationsUnique(ip_reservations_unique);
255 }
256
263 void
264 copySubnets6(const CfgSubnets6Ptr& dest, const CfgSharedNetworks6Ptr& from) {
265
266 if (!dest || !from) {
267 isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null");
268 }
269
270 const SharedNetwork6Collection* networks = from->getAll();
271 if (!networks) {
272 // Nothing to copy. Technically, it should return a pointer to empty
273 // container, but let's handle null pointer as well.
274 return;
275 }
276
277 // Let's go through all the networks one by one
278 for (auto const& net : *networks) {
279
280 // For each network go through all the subnets in it.
281 const Subnet6SimpleCollection* subnets = net->getAllSubnets();
282 if (!subnets) {
283 // Shared network without subnets it weird, but we decided to
284 // accept such configurations.
285 continue;
286 }
287
288 // For each subnet, add it to a list of regular subnets.
289 for (auto const& subnet : *subnets) {
290 dest->add(subnet);
291 }
292 }
293 }
294
303 void
304 sanityChecks(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
305
307 cfg->sanityChecksLifetime("preferred-lifetime");
308 cfg->sanityChecksLifetime("valid-lifetime");
309
311 const SharedNetwork6Collection* networks = cfg->getCfgSharedNetworks6()->getAll();
312 if (networks) {
313 sharedNetworksSanityChecks(*networks, global->get("shared-networks"));
314 }
315 }
316
323 void
324 sharedNetworksSanityChecks(const SharedNetwork6Collection& networks,
325 ConstElementPtr json) {
326
328 if (!json) {
329 // No json? That means that the shared-networks was never specified
330 // in the config.
331 return;
332 }
333
334 // Used for names uniqueness checks.
335 std::set<string> names;
336
337 // Let's go through all the networks one by one
338 for (auto const& net : networks) {
339 string txt;
340
341 // Let's check if all subnets have either the same interface
342 // or don't have the interface specified at all.
343 string iface = net->getIface();
344
345 const Subnet6SimpleCollection* subnets = net->getAllSubnets();
346 if (subnets) {
347
348 bool rapid_commit = false;
349
350 // Rapid commit must either be enabled or disabled in all subnets
351 // in the shared network.
352 if (subnets->size()) {
353 // If this is the first subnet, remember the value.
354 rapid_commit = (*subnets->begin())->getRapidCommit();
355 }
356
357 // For each subnet, add it to a list of regular subnets.
358 for (auto const& subnet : *subnets) {
359 // Ok, this is the second or following subnets. The value
360 // must match what was set in the first subnet.
361 if (rapid_commit != subnet->getRapidCommit()) {
362 isc_throw(DhcpConfigError, "All subnets in a shared network "
363 "must have the same rapid-commit value. Subnet "
364 << subnet->toText()
365 << " has specified rapid-commit "
366 << (subnet->getRapidCommit() ? "true" : "false")
367 << ", but earlier subnet in the same shared-network"
368 << " or the shared-network itself used rapid-commit "
369 << (rapid_commit ? "true" : "false"));
370 }
371
372 if (iface.empty()) {
373 iface = subnet->getIface();
374 continue;
375 }
376
377 if (subnet->getIface().empty()) {
378 continue;
379 }
380
381 if (subnet->getIface() != iface) {
382 isc_throw(DhcpConfigError, "Subnet " << subnet->toText()
383 << " has specified interface " << subnet->getIface()
384 << ", but earlier subnet in the same shared-network"
385 << " or the shared-network itself used " << iface);
386 }
387
388 // Let's collect the subnets in case we later find out the
389 // subnet doesn't have a mandatory name.
390 txt += subnet->toText() + " ";
391 }
392 }
393
394 // Next, let's check name of the shared network.
395 if (net->getName().empty()) {
396 isc_throw(DhcpConfigError, "Shared-network with subnets "
397 << txt << " is missing mandatory 'name' parameter");
398 }
399
400 // Is it unique?
401 if (names.find(net->getName()) != names.end()) {
402 isc_throw(DhcpConfigError, "A shared-network with "
403 "name " << net->getName() << " defined twice.");
404 }
405 names.insert(net->getName());
406
407 }
408 }
409};
410
411} // anonymous namespace
412
413namespace isc {
414namespace dhcp {
415
424 // Get new socket configuration.
425 ConstElementPtr sock_cfg =
426 CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
427
428 // Get current socket configuration.
429 ConstElementPtr current_sock_cfg =
430 CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();
431
432 // Determine if the socket configuration has changed. It has if
433 // both old and new configuration is specified but respective
434 // data elements aren't equal.
435 bool sock_changed = (sock_cfg && current_sock_cfg &&
436 !sock_cfg->equals(*current_sock_cfg));
437
438 // If the previous or new socket configuration doesn't exist or
439 // the new configuration differs from the old configuration we
440 // close the existing socket and open a new socket as appropriate.
441 // Note that closing an existing socket means the client will not
442 // receive the configuration result.
443 if (!sock_cfg || !current_sock_cfg || sock_changed) {
444 // Close the existing socket (if any).
445 CommandMgr::instance().closeCommandSocket();
446
447 if (sock_cfg) {
448 // This will create a control socket and install the external
449 // socket in IfaceMgr. That socket will be monitored when
450 // Dhcp6Srv::receivePacket() calls IfaceMgr::receive6() and
451 // callback in CommandMgr will be called, if necessary.
452 CommandMgr::instance().openCommandSocket(sock_cfg);
453 }
454 }
455
456 // HTTP control socket is simpler: just (re)configure it.
457
458 // Get new config.
459 HttpCommandConfigPtr http_config =
460 CfgMgr::instance().getStagingCfg()->getHttpControlSocketInfo();
461 HttpCommandMgr::instance().configure(http_config);
462}
463
470 // Revert any runtime option definitions configured so far and not committed.
472 // Let's set empty container in case a user hasn't specified any configuration
473 // for option definitions. This is equivalent to committing empty container.
475
476 // Answer will hold the result.
477 ConstElementPtr answer;
478
479 // Global parameter name in case of an error.
480 string parameter_name;
481 ElementPtr mutable_cfg;
482 SrvConfigPtr srv_config;
483 try {
484 // Get the staging configuration.
485 srv_config = CfgMgr::instance().getStagingCfg();
486
487 // This is a way to convert ConstElementPtr to ElementPtr.
488 // We need a config that can be edited, because we will insert
489 // default values and will insert derived values as well.
490 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
491
492 // Set all default values if not specified by the user.
494
495 // And now derive (inherit) global parameters to subnets, if not specified.
497
498 // In principle we could have the following code structured as a series
499 // of long if else if clauses. That would give a marginal performance
500 // boost, but would make the code less readable. We had serious issues
501 // with the parser code debugability, so I decided to keep it as a
502 // series of independent ifs.
503
504 // This parser is used in several places.
505 Dhcp6ConfigParser global_parser;
506
507 // Apply global options in the staging config, e.g. ip-reservations-unique
508 global_parser.parseEarly(srv_config, mutable_cfg);
509
510 // Specific check for this global parameter.
511 ConstElementPtr data_directory = mutable_cfg->get("data-directory");
512 if (data_directory) {
513 parameter_name = "data-directory";
514 dirExists(data_directory->stringValue());
515 }
516
517 // We need definitions first
518 ConstElementPtr option_defs = mutable_cfg->get("option-def");
519 if (option_defs) {
520 parameter_name = "option-def";
521 OptionDefListParser parser(AF_INET6);
522 CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
523 parser.parse(cfg_option_def, option_defs);
524 }
525
526 ConstElementPtr option_datas = mutable_cfg->get("option-data");
527 if (option_datas) {
528 parameter_name = "option-data";
529 OptionDataListParser parser(AF_INET6);
530 CfgOptionPtr cfg_option = srv_config->getCfgOption();
531 parser.parse(cfg_option, option_datas);
532 }
533
534 ConstElementPtr mac_sources = mutable_cfg->get("mac-sources");
535 if (mac_sources) {
536 parameter_name = "mac-sources";
538 CfgMACSource& mac_source = srv_config->getMACSources();
539 parser.parse(mac_source, mac_sources);
540 }
541
542 ConstElementPtr control_socket = mutable_cfg->get("control-socket");
543 if (control_socket) {
544 mutable_cfg->remove("control-socket");
546 l->add(UserContext::toElement(control_socket));
547 mutable_cfg->set("control-sockets", l);
548 }
549
550 ConstElementPtr control_sockets = mutable_cfg->get("control-sockets");
551 if (control_sockets) {
552 parameter_name = "control-sockets";
554 parser.parse(*srv_config, control_sockets);
555 }
556
557 ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
558 if (multi_threading) {
559 parameter_name = "multi-threading";
561 parser.parse(*srv_config, multi_threading);
562 }
563
564 bool multi_threading_enabled = true;
565 uint32_t thread_count = 0;
566 uint32_t queue_size = 0;
567 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
568 multi_threading_enabled, thread_count, queue_size);
569
571 ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
572 if (queue_control) {
573 parameter_name = "dhcp-queue-control";
575 srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
576 }
577
579 ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
580 if (reservations_lookup_first) {
581 parameter_name = "reservations-lookup-first";
582 if (multi_threading_enabled) {
584 }
585 srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
586 }
587
588 ConstElementPtr hr_identifiers =
589 mutable_cfg->get("host-reservation-identifiers");
590 if (hr_identifiers) {
591 parameter_name = "host-reservation-identifiers";
593 parser.parse(hr_identifiers);
594 }
595
596 ConstElementPtr server_id = mutable_cfg->get("server-id");
597 if (server_id) {
598 parameter_name = "server-id";
599 DUIDConfigParser parser;
600 const CfgDUIDPtr& cfg = srv_config->getCfgDUID();
601 parser.parse(cfg, server_id);
602 }
603
604 ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
605 if (sanity_checks) {
606 parameter_name = "sanity-checks";
607 SanityChecksParser parser;
608 parser.parse(*srv_config, sanity_checks);
609 }
610
611 ConstElementPtr expiration_cfg =
612 mutable_cfg->get("expired-leases-processing");
613 if (expiration_cfg) {
614 parameter_name = "expired-leases-processing";
616 parser.parse(expiration_cfg, CfgMgr::instance().getStagingCfg()->getCfgExpiration());
617 }
618
619 // The hooks-libraries configuration must be parsed after parsing
620 // multi-threading configuration so that libraries are checked
621 // for multi-threading compatibility.
622 ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
623 if (hooks_libraries) {
624 parameter_name = "hooks-libraries";
625 HooksLibrariesParser hooks_parser;
626 HooksConfig& libraries = srv_config->getHooksConfig();
627 hooks_parser.parse(libraries, hooks_libraries);
628 libraries.verifyLibraries(hooks_libraries->getPosition(),
629 multi_threading_enabled);
630 }
631
632 // D2 client configuration.
633 D2ClientConfigPtr d2_client_cfg;
634
635 // Legacy DhcpConfigParser stuff below.
636 ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
637 if (dhcp_ddns) {
638 parameter_name = "dhcp-ddns";
639 // Apply defaults
642 d2_client_cfg = parser.parse(dhcp_ddns);
643 }
644
645 ConstElementPtr client_classes = mutable_cfg->get("client-classes");
646 if (client_classes) {
647 parameter_name = "client-classes";
649 ClientClassDictionaryPtr dictionary =
650 parser.parse(client_classes, AF_INET6);
651 srv_config->setClientClassDictionary(dictionary);
652 }
653
654 // Please move at the end when migration will be finished.
655 ConstElementPtr lease_database = mutable_cfg->get("lease-database");
656 if (lease_database) {
657 parameter_name = "lease-database";
658 db::DbAccessParser parser;
659 std::string access_string;
660 parser.parse(access_string, lease_database);
661 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
662 cfg_db_access->setLeaseDbAccessString(access_string);
663 }
664
665 ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
666 if (hosts_database) {
667 parameter_name = "hosts-database";
668 db::DbAccessParser parser;
669 std::string access_string;
670 parser.parse(access_string, hosts_database);
671 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
672 cfg_db_access->setHostDbAccessString(access_string);
673 }
674
675 ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
676 if (hosts_databases) {
677 parameter_name = "hosts-databases";
678 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
679 for (auto const& it : hosts_databases->listValue()) {
680 db::DbAccessParser parser;
681 std::string access_string;
682 parser.parse(access_string, it);
683 cfg_db_access->setHostDbAccessString(access_string);
684 }
685 }
686
687 // Keep relative orders of shared networks and subnets.
688 ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
689 if (shared_networks) {
690 parameter_name = "shared-networks";
697 CfgSharedNetworks6Ptr cfg = srv_config->getCfgSharedNetworks6();
698 parser.parse(cfg, shared_networks);
699
700 // We also need to put the subnets it contains into normal
701 // subnets list.
702 global_parser.copySubnets6(srv_config->getCfgSubnets6(), cfg);
703 }
704
705 ConstElementPtr subnet6 = mutable_cfg->get("subnet6");
706 if (subnet6) {
707 parameter_name = "subnet6";
708 Subnets6ListConfigParser subnets_parser;
709 // parse() returns number of subnets parsed. We may log it one day.
710 subnets_parser.parse(srv_config, subnet6);
711 }
712
713 ConstElementPtr reservations = mutable_cfg->get("reservations");
714 if (reservations) {
715 parameter_name = "reservations";
716 HostCollection hosts;
717 HostReservationsListParser<HostReservationParser6> parser;
718 parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
719 for (auto const& h : hosts) {
720 srv_config->getCfgHosts()->add(h);
721 }
722 }
723
724 ConstElementPtr config_control = mutable_cfg->get("config-control");
725 if (config_control) {
726 parameter_name = "config-control";
727 ConfigControlParser parser;
728 ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
729 CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
730 }
731
732 ConstElementPtr rsoo_list = mutable_cfg->get("relay-supplied-options");
733 if (rsoo_list) {
734 parameter_name = "relay-supplied-options";
735 RSOOListConfigParser parser;
736 parser.parse(srv_config, rsoo_list);
737 }
738
739 ConstElementPtr compatibility = mutable_cfg->get("compatibility");
740 if (compatibility) {
741 CompatibilityParser parser;
742 parser.parse(compatibility, *CfgMgr::instance().getStagingCfg());
743 }
744
745 // Make parsers grouping.
746 const std::map<std::string, ConstElementPtr>& values_map =
747 mutable_cfg->mapValue();
748
749 for (auto const& config_pair : values_map) {
750 parameter_name = config_pair.first;
751
752 // These are converted to SimpleParser and are handled already above.
753 if ((config_pair.first == "data-directory") ||
754 (config_pair.first == "option-def") ||
755 (config_pair.first == "option-data") ||
756 (config_pair.first == "mac-sources") ||
757 (config_pair.first == "control-socket") ||
758 (config_pair.first == "control-sockets") ||
759 (config_pair.first == "multi-threading") ||
760 (config_pair.first == "dhcp-queue-control") ||
761 (config_pair.first == "host-reservation-identifiers") ||
762 (config_pair.first == "server-id") ||
763 (config_pair.first == "interfaces-config") ||
764 (config_pair.first == "sanity-checks") ||
765 (config_pair.first == "expired-leases-processing") ||
766 (config_pair.first == "hooks-libraries") ||
767 (config_pair.first == "dhcp-ddns") ||
768 (config_pair.first == "client-classes") ||
769 (config_pair.first == "lease-database") ||
770 (config_pair.first == "hosts-database") ||
771 (config_pair.first == "hosts-databases") ||
772 (config_pair.first == "subnet6") ||
773 (config_pair.first == "shared-networks") ||
774 (config_pair.first == "reservations") ||
775 (config_pair.first == "config-control") ||
776 (config_pair.first == "relay-supplied-options") ||
777 (config_pair.first == "loggers") ||
778 (config_pair.first == "compatibility")) {
779 continue;
780 }
781
782 // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
783 // The old method is used in JSON configuration parsers when the global
784 // parameters are derived into the subnets and shared networks and are
785 // being treated as explicitly specified. The new way used by the config
786 // backend is the dynamic inheritance whereby each subnet and shared
787 // network uses a callback function to return global parameter if it
788 // is not specified at lower level. This callback uses configured globals.
789 // We deliberately include both default and explicitly specified globals
790 // so as the callback can access the appropriate global values regardless
791 // whether they are set to a default or other value.
792 if ( (config_pair.first == "renew-timer") ||
793 (config_pair.first == "rebind-timer") ||
794 (config_pair.first == "preferred-lifetime") ||
795 (config_pair.first == "min-preferred-lifetime") ||
796 (config_pair.first == "max-preferred-lifetime") ||
797 (config_pair.first == "valid-lifetime") ||
798 (config_pair.first == "min-valid-lifetime") ||
799 (config_pair.first == "max-valid-lifetime") ||
800 (config_pair.first == "decline-probation-period") ||
801 (config_pair.first == "dhcp4o6-port") ||
802 (config_pair.first == "server-tag") ||
803 (config_pair.first == "reservations-global") ||
804 (config_pair.first == "reservations-in-subnet") ||
805 (config_pair.first == "reservations-out-of-pool") ||
806 (config_pair.first == "calculate-tee-times") ||
807 (config_pair.first == "t1-percent") ||
808 (config_pair.first == "t2-percent") ||
809 (config_pair.first == "cache-threshold") ||
810 (config_pair.first == "cache-max-age") ||
811 (config_pair.first == "hostname-char-set") ||
812 (config_pair.first == "hostname-char-replacement") ||
813 (config_pair.first == "ddns-send-updates") ||
814 (config_pair.first == "ddns-override-no-update") ||
815 (config_pair.first == "ddns-override-client-update") ||
816 (config_pair.first == "ddns-replace-client-name") ||
817 (config_pair.first == "ddns-generated-prefix") ||
818 (config_pair.first == "ddns-qualifying-suffix") ||
819 (config_pair.first == "ddns-update-on-renew") ||
820 (config_pair.first == "ddns-use-conflict-resolution") ||
821 (config_pair.first == "ddns-conflict-resolution-mode") ||
822 (config_pair.first == "ddns-ttl-percent") ||
823 (config_pair.first == "store-extended-info") ||
824 (config_pair.first == "statistic-default-sample-count") ||
825 (config_pair.first == "statistic-default-sample-age") ||
826 (config_pair.first == "early-global-reservations-lookup") ||
827 (config_pair.first == "ip-reservations-unique") ||
828 (config_pair.first == "reservations-lookup-first") ||
829 (config_pair.first == "parked-packet-limit") ||
830 (config_pair.first == "allocator") ||
831 (config_pair.first == "pd-allocator") ) {
832 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
833 config_pair.second);
834 continue;
835 }
836
837 // Nothing to configure for the user-context.
838 if (config_pair.first == "user-context") {
839 continue;
840 }
841
842 // If we got here, no code handled this parameter, so we bail out.
844 "unsupported global configuration parameter: " << config_pair.first
845 << " (" << config_pair.second->getPosition() << ")");
846 }
847
848 // Reset parameter name.
849 parameter_name = "<post parsing>";
850
851 // Apply global options in the staging config.
852 global_parser.parse(srv_config, mutable_cfg);
853
854 // This method conducts final sanity checks and tweaks. In particular,
855 // it checks that there is no conflict between plain subnets and those
856 // defined as part of shared networks.
857 global_parser.sanityChecks(srv_config, mutable_cfg);
858
859 // Validate D2 client configuration.
860 if (!d2_client_cfg) {
861 d2_client_cfg.reset(new D2ClientConfig());
862 }
863 d2_client_cfg->validateContents();
864 srv_config->setD2ClientConfig(d2_client_cfg);
865 } catch (const isc::Exception& ex) {
867 .arg(parameter_name).arg(ex.what());
869 } catch (...) {
870 // For things like bad_cast in boost::lexical_cast
871 LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(parameter_name);
872 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
873 "processing error");
874 }
875
876 if (!answer) {
877 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
878 "Control-socket, hook-libraries, and D2 configuration "
879 "were sanity checked, but not applied.");
880 }
881
882 return (answer);
883}
884
887 bool check_only, bool extra_checks) {
888 if (!config_set) {
890 "Can't parse NULL config");
891 return (answer);
892 }
893
895 .arg(server.redactConfig(config_set)->str());
896
897 if (check_only) {
898 MultiThreadingMgr::instance().setTestMode(true);
899 }
900
901 auto answer = processDhcp6Config(config_set);
902
903 int status_code = CONTROL_RESULT_SUCCESS;
904 isc::config::parseAnswer(status_code, answer);
905
906 SrvConfigPtr srv_config;
907
908 if (status_code == CONTROL_RESULT_SUCCESS) {
909 if (check_only) {
910 if (extra_checks) {
911 // Re-open lease and host database with new parameters.
912 try {
913 // Get the staging configuration.
914 srv_config = CfgMgr::instance().getStagingCfg();
915
916 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
917 string params = "universe=6 persist=false";
918 // The "extended-info-tables" has no effect on -T command
919 // line parameter so it is omitted on purpose.
920 // Note that in this case, the current code creates managers
921 // before hooks are loaded, so it can not be activated by
922 // the BLQ hook.
923 cfg_db->setAppendedParameters(params);
924 cfg_db->createManagers();
925 } catch (const std::exception& ex) {
927 status_code = CONTROL_RESULT_ERROR;
928 }
929
930 if (status_code == CONTROL_RESULT_SUCCESS) {
931 std::ostringstream err;
932 // Configure DHCP packet queueing
933 try {
935 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
936 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) {
938 .arg(IfaceMgr::instance().getPacketQueue6()->getInfoStr());
939 }
940
941 } catch (const std::exception& ex) {
942 err << "Error setting packet queue controls after server reconfiguration: "
943 << ex.what();
945 status_code = CONTROL_RESULT_ERROR;
946 }
947 }
948 }
949 } else {
950 // disable multi-threading (it will be applied by new configuration)
951 // this must be done in order to properly handle MT to ST transition
952 // when 'multi-threading' structure is missing from new config and
953 // to properly drop any task items stored in the thread pool which
954 // might reference some handles to loaded hooks, preventing them
955 // from being unloaded.
956 MultiThreadingMgr::instance().apply(false, 0, 0);
957
958 // Close DHCP sockets and remove any existing timers.
959 IfaceMgr::instance().closeSockets();
960 TimerMgr::instance()->unregisterTimers();
961 server.discardPackets();
962 server.getCBControl()->reset();
963 }
964
965 if (status_code == CONTROL_RESULT_SUCCESS) {
966 string parameter_name;
967 ElementPtr mutable_cfg;
968 try {
969 // Get the staging configuration.
970 srv_config = CfgMgr::instance().getStagingCfg();
971
972 // This is a way to convert ConstElementPtr to ElementPtr.
973 // We need a config that can be edited, because we will insert
974 // default values and will insert derived values as well.
975 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
976
977 ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
978 if (ifaces_config) {
979 parameter_name = "interfaces-config";
980 IfacesConfigParser parser(AF_INET6, check_only);
981 CfgIfacePtr cfg_iface = srv_config->getCfgIface();
982 cfg_iface->reset();
983 parser.parse(cfg_iface, ifaces_config);
984 }
985 } catch (const isc::Exception& ex) {
987 .arg(parameter_name).arg(ex.what());
989 status_code = CONTROL_RESULT_ERROR;
990 } catch (...) {
991 // For things like bad_cast in boost::lexical_cast
992 LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(parameter_name);
993 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
994 " processing error");
995 status_code = CONTROL_RESULT_ERROR;
996 }
997 }
998 }
999
1000 // So far so good, there was no parsing error so let's commit the
1001 // configuration. This will add created subnets and option values into
1002 // the server's configuration.
1003 // This operation should be exception safe but let's make sure.
1004 if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
1005 try {
1006
1007 // Setup the command channel.
1009 } catch (const isc::Exception& ex) {
1012 status_code = CONTROL_RESULT_ERROR;
1013 } catch (...) {
1014 // For things like bad_cast in boost::lexical_cast
1016 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
1017 " parsing error");
1018 status_code = CONTROL_RESULT_ERROR;
1019 }
1020 }
1021
1022 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
1023 try {
1024 // No need to commit interface names as this is handled by the
1025 // CfgMgr::commit() function.
1026
1027 // Apply the staged D2ClientConfig, used to be done by parser commit
1029 cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
1030 CfgMgr::instance().setD2ClientConfig(cfg);
1031 } catch (const isc::Exception& ex) {
1034 status_code = CONTROL_RESULT_ERROR;
1035 } catch (...) {
1036 // For things like bad_cast in boost::lexical_cast
1038 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
1039 " parsing error");
1040 status_code = CONTROL_RESULT_ERROR;
1041 }
1042 }
1043
1044 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
1045 try {
1046 // This occurs last as if it succeeds, there is no easy way to
1047 // revert it. As a result, the failure to commit a subsequent
1048 // change causes problems when trying to roll back.
1050 static_cast<void>(HooksManager::unloadLibraries());
1051 IOServiceMgr::instance().clearIOServices();
1052 const HooksConfig& libraries =
1053 CfgMgr::instance().getStagingCfg()->getHooksConfig();
1054 bool multi_threading_enabled = true;
1055 uint32_t thread_count = 0;
1056 uint32_t queue_size = 0;
1057 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
1058 multi_threading_enabled, thread_count, queue_size);
1059 libraries.loadLibraries(multi_threading_enabled);
1060 } catch (const isc::Exception& ex) {
1063 status_code = CONTROL_RESULT_ERROR;
1064 } catch (...) {
1065 // For things like bad_cast in boost::lexical_cast
1067 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
1068 " parsing error");
1069 status_code = CONTROL_RESULT_ERROR;
1070 }
1071 }
1072
1073 // Log the list of known backends.
1075
1076 // Log the list of known backends.
1078
1079 // Moved from the commit block to add the config backend indication.
1080 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
1081 try {
1082 // If there are config backends, fetch and merge into staging config
1083 server.getCBControl()->databaseConfigFetch(srv_config,
1085 } catch (const isc::Exception& ex) {
1086 std::ostringstream err;
1087 err << "during update from config backend database: " << ex.what();
1090 status_code = CONTROL_RESULT_ERROR;
1091 } catch (...) {
1092 // For things like bad_cast in boost::lexical_cast
1093 std::ostringstream err;
1094 err << "during update from config backend database: "
1095 << "undefined configuration parsing error";
1098 status_code = CONTROL_RESULT_ERROR;
1099 }
1100 }
1101
1102 // Rollback changes as the configuration parsing failed.
1103 if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
1104 // Revert to original configuration of runtime option definitions
1105 // in the libdhcp++.
1107
1108 if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
1109 auto notify_libraries = ControlledDhcpv6Srv::finishConfigHookLibraries(config_set);
1110 if (notify_libraries) {
1111 return (notify_libraries);
1112 }
1113
1115 try {
1116 // Handle events registered by hooks using external IOService objects.
1117 IOServiceMgr::instance().pollIOServices();
1118 } catch (const std::exception& ex) {
1119 std::ostringstream err;
1120 err << "Error initializing hooks: "
1121 << ex.what();
1123 }
1124 }
1125
1126 return (answer);
1127 }
1128
1130 .arg(CfgMgr::instance().getStagingCfg()->
1131 getConfigSummary(SrvConfig::CFGSEL_ALL6));
1132
1133 // Also calculate SHA256 hash of the config that was just set and
1134 // append it to the response.
1135 ConstElementPtr config = CfgMgr::instance().getStagingCfg()->toElement();
1136 string hash = BaseCommandMgr::getHash(config);
1137 ElementPtr hash_map = Element::createMap();
1138 hash_map->set("hash", Element::create(hash));
1139
1140 // Everything was fine. Configuration is successful.
1141 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
1142 return (answer);
1143}
1144
1145} // namespace dhcp
1146} // namespace isc
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
static CommandMgr & instance()
CommandMgr is a singleton class.
static HttpCommandMgr & instance()
HttpCommandMgr is a singleton class.
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
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
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.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:25
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-sockets structure.
void parse(SrvConfig &srv_cfg, isc::data::ConstElementPtr value)
"Parses" control-sockets 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 logRegistered()
Logs 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.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
Parser for the configuration of interfaces.
void parse(const CfgIfacePtr &config, const isc::data::ConstElementPtr &values)
Parses content of the "interfaces-config".
static void logRegistered()
Logs out all registered backends.
static void setRuntimeOptionDefs(const OptionDefSpaceContainer &defs)
Copies option definitions created at runtime.
Definition libdhcp++.cc:220
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition libdhcp++.cc:128
static void revertRuntimeOptionDefs()
Reverts uncommitted changes to runtime option definitions.
Definition libdhcp++.cc:239
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
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:211
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.
Parser for hooks library list.
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.
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
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
boost::shared_ptr< HttpCommandConfig > HttpCommandConfigPtr
Pointer to a HttpCommandConfig object.
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:846
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
static data::ElementPtr toElement(data::ConstElementPtr map)
Copy an Element map.