Kea 2.7.8
dhcp4/json_config_parser.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2025 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 <config/command_mgr.h>
19#include <dhcp4/dhcp4_log.h>
20#include <dhcp4/dhcp4_srv.h>
22#include <dhcp/libdhcp++.h>
27#include <dhcpsrv/cfg_option.h>
28#include <dhcpsrv/cfgmgr.h>
30#include <dhcpsrv/db_type.h>
45#include <dhcpsrv/host_mgr.h>
46#include <dhcpsrv/timer_mgr.h>
47#include <hooks/hooks_manager.h>
48#include <hooks/hooks_parser.h>
50#include <util/encode/encode.h>
52#include <boost/algorithm/string.hpp>
53#include <boost/lexical_cast.hpp>
54
55#include <iomanip>
56#include <iostream>
57#include <limits>
58#include <map>
59#include <netinet/in.h>
60#include <vector>
61
62using namespace isc::asiolink;
63using namespace isc::config;
64using namespace isc::data;
65using namespace isc::db;
66using namespace isc::dhcp;
67using namespace isc::hooks;
68using namespace isc::process;
69using namespace isc::util;
70using namespace isc;
71using namespace std;
72
73//
74// Register database backends
75//
76namespace {
77
86class Dhcp4ConfigParser : public isc::data::SimpleParser {
87public:
88
103 void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
104
105 // Set whether v4 server is supposed to echo back client-id
106 // (yes = RFC6842 compatible, no = backward compatibility)
107 bool echo_client_id = getBoolean(global, "echo-client-id");
108 cfg->setEchoClientId(echo_client_id);
109
110 // Set the probation period for decline handling.
111 uint32_t probation_period =
112 getUint32(global, "decline-probation-period");
113 cfg->setDeclinePeriod(probation_period);
114
115 // Set the DHCPv4-over-DHCPv6 interserver port.
116 uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
117 cfg->setDhcp4o6Port(dhcp4o6_port);
118
119 // Set the global user context.
120 ConstElementPtr user_context = global->get("user-context");
121 if (user_context) {
122 cfg->setContext(user_context);
123 }
124
125 // Set the server's logical name
126 std::string server_tag = getString(global, "server-tag");
127 cfg->setServerTag(server_tag);
128 }
129
141 void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
142 // Set ip-reservations-unique flag.
143 bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
144 cfg->setIPReservationsUnique(ip_reservations_unique);
145 }
146
153 void
154 copySubnets4(const CfgSubnets4Ptr& dest, const CfgSharedNetworks4Ptr& from) {
155
156 if (!dest || !from) {
157 isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null");
158 }
159
160 const SharedNetwork4Collection* networks = from->getAll();
161 if (!networks) {
162 // Nothing to copy. Technically, it should return a pointer to empty
163 // container, but let's handle null pointer as well.
164 return;
165 }
166
167 // Let's go through all the networks one by one
168 for (auto const& net : *networks) {
169
170 // For each network go through all the subnets in it.
171 const Subnet4SimpleCollection* subnets = net->getAllSubnets();
172 if (!subnets) {
173 // Shared network without subnets it weird, but we decided to
174 // accept such configurations.
175 continue;
176 }
177
178 // For each subnet, add it to a list of regular subnets.
179 for (auto const& subnet : *subnets) {
180 dest->add(subnet);
181 }
182 }
183 }
184
193 void
194 sanityChecks(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
195
197 cfg->sanityChecksLifetime("valid-lifetime");
198
200 cfg->sanityChecksDdnsTtlParameters();
201
203 const SharedNetwork4Collection* networks = cfg->getCfgSharedNetworks4()->getAll();
204 if (networks) {
205 sharedNetworksSanityChecks(*networks, global->get("shared-networks"));
206 }
207 }
208
215 void
216 sharedNetworksSanityChecks(const SharedNetwork4Collection& networks,
217 ConstElementPtr json) {
218
220 if (!json) {
221 // No json? That means that the shared-networks was never specified
222 // in the config.
223 return;
224 }
225
226 // Used for names uniqueness checks.
227 std::set<string> names;
228
229 // Let's go through all the networks one by one
230 for (auto const& net : networks) {
231 string txt;
232
233 // Let's check if all subnets have either the same interface
234 // or don't have the interface specified at all.
235 bool authoritative = net->getAuthoritative();
236 string iface = net->getIface();
237
238 const Subnet4SimpleCollection* subnets = net->getAllSubnets();
239 if (subnets) {
240 // For each subnet, add it to a list of regular subnets.
241 for (auto const& subnet : *subnets) {
242 if (subnet->getAuthoritative() != authoritative) {
243 isc_throw(DhcpConfigError, "Subnet " << boolalpha
244 << subnet->toText()
245 << " has different authoritative setting "
246 << subnet->getAuthoritative()
247 << " than the shared-network itself: "
248 << authoritative);
249 }
250
251 if (iface.empty()) {
252 iface = subnet->getIface();
253 continue;
254 }
255
256 if (subnet->getIface().empty()) {
257 continue;
258 }
259
260 if (subnet->getIface() != iface) {
261 isc_throw(DhcpConfigError, "Subnet " << subnet->toText()
262 << " has specified interface " << subnet->getIface()
263 << ", but earlier subnet in the same shared-network"
264 << " or the shared-network itself used " << iface);
265 }
266
267 // Let's collect the subnets in case we later find out the
268 // subnet doesn't have a mandatory name.
269 txt += subnet->toText() + " ";
270 }
271 }
272
273 // Next, let's check name of the shared network.
274 if (net->getName().empty()) {
275 isc_throw(DhcpConfigError, "Shared-network with subnets "
276 << txt << " is missing mandatory 'name' parameter");
277 }
278
279 // Is it unique?
280 if (names.find(net->getName()) != names.end()) {
281 isc_throw(DhcpConfigError, "A shared-network with "
282 "name " << net->getName() << " defined twice.");
283 }
284 names.insert(net->getName());
285
286 }
287 }
288};
289
290} // anonymous namespace
291
292namespace isc {
293namespace dhcp {
294
303 // Get new UNIX socket configuration.
304 ConstElementPtr unix_config =
305 CfgMgr::instance().getStagingCfg()->getUnixControlSocketInfo();
306
307 // Get current UNIX socket configuration.
308 ConstElementPtr current_unix_config =
309 CfgMgr::instance().getCurrentCfg()->getUnixControlSocketInfo();
310
311 // Determine if the socket configuration has changed. It has if
312 // both old and new configuration is specified but respective
313 // data elements aren't equal.
314 bool sock_changed = (unix_config && current_unix_config &&
315 !unix_config->equals(*current_unix_config));
316
317 // If the previous or new socket configuration doesn't exist or
318 // the new configuration differs from the old configuration we
319 // close the existing socket and open a new socket as appropriate.
320 // Note that closing an existing socket means the client will not
321 // receive the configuration result.
322 if (!unix_config || !current_unix_config || sock_changed) {
323 if (unix_config) {
324 // This will create a control socket and install the external
325 // socket in IfaceMgr. That socket will be monitored when
326 // Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
327 // callback in CommandMgr will be called, if necessary.
329 } else if (current_unix_config) {
331 }
332 }
333
334 // Get new HTTP/HTTPS socket configuration.
335 ConstElementPtr http_config =
336 CfgMgr::instance().getStagingCfg()->getHttpControlSocketInfo();
337
338 // Get current HTTP/HTTPS socket configuration.
339 ConstElementPtr current_http_config =
340 CfgMgr::instance().getCurrentCfg()->getHttpControlSocketInfo();
341
342 if (http_config) {
344 } else if (current_http_config) {
346 }
347}
348
355 // Revert any runtime option definitions configured so far and not committed.
357 // Let's set empty container in case a user hasn't specified any configuration
358 // for option definitions. This is equivalent to committing empty container.
360
361 // Answer will hold the result.
362 ConstElementPtr answer;
363
364 // Global parameter name in case of an error.
365 string parameter_name;
366 ElementPtr mutable_cfg;
367 SrvConfigPtr srv_config;
368 try {
369 // Get the staging configuration.
370 srv_config = CfgMgr::instance().getStagingCfg();
371
372 // This is a way to convert ConstElementPtr to ElementPtr.
373 // We need a config that can be edited, because we will insert
374 // default values and will insert derived values as well.
375 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
376
377 // Set all default values if not specified by the user.
379
380 // And now derive (inherit) global parameters to subnets, if not specified.
382
383 // In principle we could have the following code structured as a series
384 // of long if else if clauses. That would give a marginal performance
385 // boost, but would make the code less readable. We had serious issues
386 // with the parser code debugability, so I decided to keep it as a
387 // series of independent ifs.
388
389 // This parser is used in several places.
390 Dhcp4ConfigParser global_parser;
391
392 // Apply global options in the staging config, e.g. ip-reservations-unique
393 global_parser.parseEarly(srv_config, mutable_cfg);
394
395 // We need definitions first
396 ConstElementPtr option_defs = mutable_cfg->get("option-def");
397 if (option_defs) {
398 parameter_name = "option-def";
399 OptionDefListParser parser(AF_INET);
400 CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
401 parser.parse(cfg_option_def, option_defs);
402 }
403
404 ConstElementPtr option_datas = mutable_cfg->get("option-data");
405 if (option_datas) {
406 parameter_name = "option-data";
407 OptionDataListParser parser(AF_INET);
408 CfgOptionPtr cfg_option = srv_config->getCfgOption();
409 parser.parse(cfg_option, option_datas);
410 }
411
412 ConstElementPtr control_socket = mutable_cfg->get("control-socket");
413 if (control_socket) {
414 mutable_cfg->remove("control-socket");
416 l->add(UserContext::toElement(control_socket));
417 mutable_cfg->set("control-sockets", l);
418 }
419
420 ConstElementPtr control_sockets = mutable_cfg->get("control-sockets");
421 if (control_sockets) {
422 parameter_name = "control-sockets";
424 parser.parse(*srv_config, control_sockets);
425 }
426
427 ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
428 if (multi_threading) {
429 parameter_name = "multi-threading";
431 parser.parse(*srv_config, multi_threading);
432 }
433
434 bool multi_threading_enabled = true;
435 uint32_t thread_count = 0;
436 uint32_t queue_size = 0;
437 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
438 multi_threading_enabled, thread_count, queue_size);
439
441 ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
442 if (queue_control) {
443 parameter_name = "dhcp-queue-control";
445 srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
446 }
447
449 ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
450 if (reservations_lookup_first) {
451 parameter_name = "reservations-lookup-first";
452 if (multi_threading_enabled) {
454 }
455 srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
456 }
457
458 ConstElementPtr hr_identifiers =
459 mutable_cfg->get("host-reservation-identifiers");
460 if (hr_identifiers) {
461 parameter_name = "host-reservation-identifiers";
463 parser.parse(hr_identifiers);
464 }
465
466 ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
467 if (sanity_checks) {
468 parameter_name = "sanity-checks";
469 SanityChecksParser parser;
470 parser.parse(*srv_config, sanity_checks);
471 }
472
473 ConstElementPtr expiration_cfg =
474 mutable_cfg->get("expired-leases-processing");
475 if (expiration_cfg) {
476 parameter_name = "expired-leases-processing";
478 parser.parse(expiration_cfg, CfgMgr::instance().getStagingCfg()->getCfgExpiration());
479 }
480
481 // The hooks-libraries configuration must be parsed after parsing
482 // multi-threading configuration so that libraries are checked
483 // for multi-threading compatibility.
484 ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
485 if (hooks_libraries) {
486 parameter_name = "hooks-libraries";
487 HooksLibrariesParser hooks_parser;
488 HooksConfig& libraries = srv_config->getHooksConfig();
489 hooks_parser.parse(libraries, hooks_libraries);
490 libraries.verifyLibraries(hooks_libraries->getPosition(),
491 multi_threading_enabled);
492 }
493
494 // D2 client configuration.
495 D2ClientConfigPtr d2_client_cfg;
496
497 // Legacy DhcpConfigParser stuff below.
498 ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
499 if (dhcp_ddns) {
500 parameter_name = "dhcp-ddns";
501 // Apply defaults
504 d2_client_cfg = parser.parse(dhcp_ddns);
505 }
506
507 ConstElementPtr client_classes = mutable_cfg->get("client-classes");
508 if (client_classes) {
509 parameter_name = "client-classes";
511 ClientClassDictionaryPtr dictionary =
512 parser.parse(client_classes, AF_INET);
513 srv_config->setClientClassDictionary(dictionary);
514 }
515
516 // Please move at the end when migration will be finished.
517 ConstElementPtr lease_database = mutable_cfg->get("lease-database");
518 if (lease_database) {
519 parameter_name = "lease-database";
520 db::DbAccessParser parser;
521 std::string access_string;
522 parser.parse(access_string, lease_database);
523 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
524 cfg_db_access->setLeaseDbAccessString(access_string);
525 }
526
527 ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
528 if (hosts_database) {
529 parameter_name = "hosts-database";
530 db::DbAccessParser parser;
531 std::string access_string;
532 parser.parse(access_string, hosts_database);
533 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
534 cfg_db_access->setHostDbAccessString(access_string);
535 }
536
537 ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
538 if (hosts_databases) {
539 parameter_name = "hosts-databases";
540 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
541 for (auto const& it : hosts_databases->listValue()) {
542 db::DbAccessParser parser;
543 std::string access_string;
544 parser.parse(access_string, it);
545 cfg_db_access->setHostDbAccessString(access_string);
546 }
547 }
548
549 // Keep relative orders of shared networks and subnets.
550 ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
551 if (shared_networks) {
552 parameter_name = "shared-networks";
559 CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4();
560 parser.parse(cfg, shared_networks);
561
562 // We also need to put the subnets it contains into normal
563 // subnets list.
564 global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg);
565 }
566
567 ConstElementPtr subnet4 = mutable_cfg->get("subnet4");
568 if (subnet4) {
569 parameter_name = "subnet4";
570 Subnets4ListConfigParser subnets_parser;
571 // parse() returns number of subnets parsed. We may log it one day.
572 subnets_parser.parse(srv_config, subnet4);
573 }
574
575 ConstElementPtr reservations = mutable_cfg->get("reservations");
576 if (reservations) {
577 parameter_name = "reservations";
578 HostCollection hosts;
580 parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
581 for (auto const& h : hosts) {
582 srv_config->getCfgHosts()->add(h);
583 }
584 }
585
586 ConstElementPtr config_control = mutable_cfg->get("config-control");
587 if (config_control) {
588 parameter_name = "config-control";
589 ConfigControlParser parser;
590 ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
591 CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
592 }
593
594 ConstElementPtr compatibility = mutable_cfg->get("compatibility");
595 if (compatibility) {
596 CompatibilityParser parser;
597 parser.parse(compatibility, *CfgMgr::instance().getStagingCfg());
598 }
599
600 // Make parsers grouping.
601 const std::map<std::string, ConstElementPtr>& values_map =
602 mutable_cfg->mapValue();
603
604 for (auto const& config_pair : values_map) {
605 parameter_name = config_pair.first;
606
607 // These are converted to SimpleParser and are handled already above.
608 if ((config_pair.first == "option-def") ||
609 (config_pair.first == "option-data") ||
610 (config_pair.first == "control-socket") ||
611 (config_pair.first == "control-sockets") ||
612 (config_pair.first == "multi-threading") ||
613 (config_pair.first == "dhcp-queue-control") ||
614 (config_pair.first == "host-reservation-identifiers") ||
615 (config_pair.first == "interfaces-config") ||
616 (config_pair.first == "sanity-checks") ||
617 (config_pair.first == "expired-leases-processing") ||
618 (config_pair.first == "hooks-libraries") ||
619 (config_pair.first == "dhcp-ddns") ||
620 (config_pair.first == "client-classes") ||
621 (config_pair.first == "lease-database") ||
622 (config_pair.first == "hosts-database") ||
623 (config_pair.first == "hosts-databases") ||
624 (config_pair.first == "subnet4") ||
625 (config_pair.first == "shared-networks") ||
626 (config_pair.first == "reservations") ||
627 (config_pair.first == "config-control") ||
628 (config_pair.first == "loggers") ||
629 (config_pair.first == "compatibility")) {
630 continue;
631 }
632
633 // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
634 // The old method is used in JSON configuration parsers when the global
635 // parameters are derived into the subnets and shared networks and are
636 // being treated as explicitly specified. The new way used by the config
637 // backend is the dynamic inheritance whereby each subnet and shared
638 // network uses a callback function to return global parameter if it
639 // is not specified at lower level. This callback uses configured globals.
640 // We deliberately include both default and explicitly specified globals
641 // so as the callback can access the appropriate global values regardless
642 // whether they are set to a default or other value.
643 if ( (config_pair.first == "renew-timer") ||
644 (config_pair.first == "rebind-timer") ||
645 (config_pair.first == "valid-lifetime") ||
646 (config_pair.first == "min-valid-lifetime") ||
647 (config_pair.first == "max-valid-lifetime") ||
648 (config_pair.first == "decline-probation-period") ||
649 (config_pair.first == "dhcp4o6-port") ||
650 (config_pair.first == "echo-client-id") ||
651 (config_pair.first == "match-client-id") ||
652 (config_pair.first == "authoritative") ||
653 (config_pair.first == "next-server") ||
654 (config_pair.first == "server-hostname") ||
655 (config_pair.first == "boot-file-name") ||
656 (config_pair.first == "server-tag") ||
657 (config_pair.first == "reservations-global") ||
658 (config_pair.first == "reservations-in-subnet") ||
659 (config_pair.first == "reservations-out-of-pool") ||
660 (config_pair.first == "calculate-tee-times") ||
661 (config_pair.first == "t1-percent") ||
662 (config_pair.first == "t2-percent") ||
663 (config_pair.first == "cache-threshold") ||
664 (config_pair.first == "cache-max-age") ||
665 (config_pair.first == "hostname-char-set") ||
666 (config_pair.first == "hostname-char-replacement") ||
667 (config_pair.first == "ddns-send-updates") ||
668 (config_pair.first == "ddns-override-no-update") ||
669 (config_pair.first == "ddns-override-client-update") ||
670 (config_pair.first == "ddns-replace-client-name") ||
671 (config_pair.first == "ddns-generated-prefix") ||
672 (config_pair.first == "ddns-qualifying-suffix") ||
673 (config_pair.first == "ddns-update-on-renew") ||
674 (config_pair.first == "ddns-use-conflict-resolution") ||
675 (config_pair.first == "ddns-conflict-resolution-mode") ||
676 (config_pair.first == "ddns-ttl-percent") ||
677 (config_pair.first == "store-extended-info") ||
678 (config_pair.first == "statistic-default-sample-count") ||
679 (config_pair.first == "statistic-default-sample-age") ||
680 (config_pair.first == "early-global-reservations-lookup") ||
681 (config_pair.first == "ip-reservations-unique") ||
682 (config_pair.first == "reservations-lookup-first") ||
683 (config_pair.first == "parked-packet-limit") ||
684 (config_pair.first == "allocator") ||
685 (config_pair.first == "offer-lifetime") ||
686 (config_pair.first == "ddns-ttl") ||
687 (config_pair.first == "ddns-ttl-min") ||
688 (config_pair.first == "ddns-ttl-max") ||
689 (config_pair.first == "stash-agent-options")) {
690 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
691 config_pair.second);
692 continue;
693 }
694
695 // Nothing to configure for the user-context.
696 if (config_pair.first == "user-context") {
697 continue;
698 }
699
700 // If we got here, no code handled this parameter, so we bail out.
702 "unsupported global configuration parameter: " << config_pair.first
703 << " (" << config_pair.second->getPosition() << ")");
704 }
705
706 // Reset parameter name.
707 parameter_name = "<post parsing>";
708
709 // Apply global options in the staging config.
710 global_parser.parse(srv_config, mutable_cfg);
711
712 // This method conducts final sanity checks and tweaks. In particular,
713 // it checks that there is no conflict between plain subnets and those
714 // defined as part of shared networks.
715 global_parser.sanityChecks(srv_config, mutable_cfg);
716
717 // Validate D2 client configuration.
718 if (!d2_client_cfg) {
719 d2_client_cfg.reset(new D2ClientConfig());
720 }
721 d2_client_cfg->validateContents();
722 srv_config->setD2ClientConfig(d2_client_cfg);
723 } catch (const isc::Exception& ex) {
725 .arg(parameter_name).arg(ex.what());
727 } catch (...) {
728 // For things like bad_cast in boost::lexical_cast
729 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
730 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
731 "processing error");
732 }
733
734 if (!answer) {
735 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
736 "Control-socket, hook-libraries, and D2 configuration "
737 "were sanity checked, but not applied.");
738 }
739
740 return (answer);
741}
742
745 bool check_only, bool extra_checks) {
746 if (!config_set) {
748 "Can't parse NULL config");
749 return (answer);
750 }
751
753 .arg(server.redactConfig(config_set)->str());
754
755 if (check_only) {
757 }
758
759 auto answer = processDhcp4Config(config_set);
760
761 int status_code = CONTROL_RESULT_SUCCESS;
762 isc::config::parseAnswer(status_code, answer);
763
764 SrvConfigPtr srv_config;
765
766 if (status_code == CONTROL_RESULT_SUCCESS) {
767 if (check_only) {
768 if (extra_checks) {
769 std::ostringstream err;
770 // Configure DHCP packet queueing
771 try {
773 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
774 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
776 .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
777 }
778
779 } catch (const std::exception& ex) {
780 err << "Error setting packet queue controls after server reconfiguration: "
781 << ex.what();
783 status_code = CONTROL_RESULT_ERROR;
784 }
785 }
786 } else {
787 // disable multi-threading (it will be applied by new configuration)
788 // this must be done in order to properly handle MT to ST transition
789 // when 'multi-threading' structure is missing from new config and
790 // to properly drop any task items stored in the thread pool which
791 // might reference some handles to loaded hooks, preventing them
792 // from being unloaded.
793 MultiThreadingMgr::instance().apply(false, 0, 0);
794
795 // Close DHCP sockets and remove any existing timers.
797 TimerMgr::instance()->unregisterTimers();
798 server.discardPackets();
799 server.getCBControl()->reset();
800 }
801
802 if (status_code == CONTROL_RESULT_SUCCESS) {
803 string parameter_name;
804 ElementPtr mutable_cfg;
805 try {
806 // Get the staging configuration.
807 srv_config = CfgMgr::instance().getStagingCfg();
808
809 // This is a way to convert ConstElementPtr to ElementPtr.
810 // We need a config that can be edited, because we will insert
811 // default values and will insert derived values as well.
812 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
813
814 ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
815 if (ifaces_config) {
816 parameter_name = "interfaces-config";
817 IfacesConfigParser parser(AF_INET, check_only);
818 CfgIfacePtr cfg_iface = srv_config->getCfgIface();
819 cfg_iface->reset();
820 parser.parse(cfg_iface, ifaces_config);
821 }
822 } catch (const isc::Exception& ex) {
824 .arg(parameter_name).arg(ex.what());
826 status_code = CONTROL_RESULT_ERROR;
827 } catch (...) {
828 // For things like bad_cast in boost::lexical_cast
829 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
830 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
831 " processing error");
832 status_code = CONTROL_RESULT_ERROR;
833 }
834 }
835 }
836
837 // So far so good, there was no parsing error so let's commit the
838 // configuration. This will add created subnets and option values into
839 // the server's configuration.
840 // This operation should be exception safe but let's make sure.
841 if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
842 try {
843
844 // Setup the command channel.
846 } catch (const isc::Exception& ex) {
849 status_code = CONTROL_RESULT_ERROR;
850 } catch (...) {
851 // For things like bad_cast in boost::lexical_cast
853 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
854 " parsing error");
855 status_code = CONTROL_RESULT_ERROR;
856 }
857 }
858
859 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
860 try {
861 // No need to commit interface names as this is handled by the
862 // CfgMgr::commit() function.
863
864 // Apply the staged D2ClientConfig, used to be done by parser commit
866 cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
868 } catch (const isc::Exception& ex) {
871 status_code = CONTROL_RESULT_ERROR;
872 } catch (...) {
873 // For things like bad_cast in boost::lexical_cast
875 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
876 " parsing error");
877 status_code = CONTROL_RESULT_ERROR;
878 }
879 }
880
881 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
882 try {
883 // This occurs last as if it succeeds, there is no easy way to
884 // revert it. As a result, the failure to commit a subsequent
885 // change causes problems when trying to roll back.
887 static_cast<void>(HooksManager::unloadLibraries());
889 const HooksConfig& libraries =
890 CfgMgr::instance().getStagingCfg()->getHooksConfig();
891 bool multi_threading_enabled = true;
892 uint32_t thread_count = 0;
893 uint32_t queue_size = 0;
894 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
895 multi_threading_enabled, thread_count, queue_size);
896 libraries.loadLibraries(multi_threading_enabled);
897 } catch (const isc::Exception& ex) {
900 status_code = CONTROL_RESULT_ERROR;
901 } catch (...) {
902 // For things like bad_cast in boost::lexical_cast
904 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
905 " parsing error");
906 status_code = CONTROL_RESULT_ERROR;
907 }
908
909 if (extra_checks && status_code == CONTROL_RESULT_SUCCESS) {
910 // Re-open lease and host database with new parameters.
911 try {
912 // Get the staging configuration.
913 srv_config = CfgMgr::instance().getStagingCfg();
914
915 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
916 string params = "universe=4 persist=false";
917 cfg_db->setAppendedParameters(params);
918 cfg_db->createManagers();
919 } catch (const std::exception& ex) {
921 status_code = CONTROL_RESULT_ERROR;
922 }
923 }
924 }
925
926 // Log the list of known backends.
928
929 // Log the list of known backends.
931
932 // Log the list of known backends.
934
935 // Log the list of known backends.
937
938 // Moved from the commit block to add the config backend indication.
939 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
940 try {
941 // If there are config backends, fetch and merge into staging config
942 server.getCBControl()->databaseConfigFetch(srv_config,
943 CBControlDHCPv4::FetchMode::FETCH_ALL);
944 } catch (const isc::Exception& ex) {
945 std::ostringstream err;
946 err << "during update from config backend database: " << ex.what();
949 status_code = CONTROL_RESULT_ERROR;
950 } catch (...) {
951 // For things like bad_cast in boost::lexical_cast
952 std::ostringstream err;
953 err << "during update from config backend database: "
954 << "undefined configuration parsing error";
957 status_code = CONTROL_RESULT_ERROR;
958 }
959 }
960
961 // Rollback changes as the configuration parsing failed.
962 if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
963 // Revert to original configuration of runtime option definitions
964 // in the libdhcp++.
966
967 if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
968 auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
969 if (notify_libraries) {
970 return (notify_libraries);
971 }
972
974 try {
975 // Handle events registered by hooks using external IOService objects.
977 } catch (const std::exception& ex) {
978 std::ostringstream err;
979 err << "Error initializing hooks: "
980 << ex.what();
982 }
983 }
984
985 return (answer);
986 }
987
989 .arg(CfgMgr::instance().getStagingCfg()->
990 getConfigSummary(SrvConfig::CFGSEL_ALL4));
991
992 // Also calculate SHA256 hash of the config that was just set and
993 // append it to the response.
994 ConstElementPtr config = CfgMgr::instance().getStagingCfg()->toElement();
995 string hash = BaseCommandMgr::getHash(config);
996 ElementPtr hash_map = Element::createMap();
997 hash_map->set("hash", Element::create(hash));
998
999 // Everything was fine. Configuration is successful.
1000 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
1001 return (answer);
1002}
1003
1004} // namespace dhcp
1005} // 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.
void logRegistered()
Logs out all registered backends.
static std::string getHash(const isc::data::ConstElementPtr &config)
returns a hash of a given Element structure
void closeCommandSockets()
Close http control sockets.
static HttpCommandMgr & instance()
HttpCommandMgr is a singleton class.
void openCommandSockets(const isc::data::ConstElementPtr config)
Open http control sockets using configuration.
static UnixCommandMgr & instance()
UnixCommandMgr is a singleton class.
void openCommandSockets(const isc::data::ConstElementPtr config)
Opens unix control socket with parameters specified in socket_info (required parameters: socket-type:...
void closeCommandSockets()
Shuts down any open unix control sockets.
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.
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
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.
static ConfigBackendDHCPv4Mgr & instance()
Returns a sole instance of the ConfigBackendDHCPv4Mgr.
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".
To be removed. Please use ConfigError instead.
DHCPv4 server service.
Definition dhcp4_srv.h:268
CBControlDHCPv4Ptr getCBControl() const
Returns an object which controls access to the configuration backends.
Definition dhcp4_srv.h:333
void discardPackets()
Discards parked packets Clears the packet parking lots of all packets.
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 DHCPv4.
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 logRegistered()
Logs out all registered backends.
static void logRegistered()
Logs out all registered backends.
static void setRuntimeOptionDefs(const OptionDefSpaceContainer &defs)
Copies option definitions created at runtime.
Definition libdhcp++.cc:224
static void revertRuntimeOptionDefs()
Reverts uncommitted changes to runtime option definitions.
Definition libdhcp++.cc:243
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
Parser for a list of shared networks.
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 DHCPv4 configuration.
static const uint32_t CFGSEL_ALL4
IPv4 related config.
Definition srv_config.h:74
this class parses list of DHCP4 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.
isc::data::ConstElementPtr redactConfig(isc::data::ConstElementPtr const &config)
Redact a configuration.
Definition daemon.cc:259
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
void setTestMode(const bool test_mode)
Sets or clears the test mode for MultiThreadingMgr.
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 ...
Contains declarations for loggers used by the DHCPv4 server component.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#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()
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.
const isc::log::MessageID DHCP4_CONFIG_START
boost::shared_ptr< D2ClientConfig > D2ClientConfigPtr
Defines a pointer for D2ClientConfig instances.
const isc::log::MessageID DHCP4_PARSER_EXCEPTION
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition cfg_option.h:892
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
const isc::log::MessageID DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED
isc::data::ConstElementPtr processDhcp4Config(isc::data::ConstElementPtr config_set)
Process a DHCPv4 confguration and return an answer stating if the configuration is valid,...
boost::shared_ptr< CfgIface > CfgIfacePtr
A pointer to the CfgIface .
Definition cfg_iface.h:501
const isc::log::MessageID DHCP4_CONFIG_PACKET_QUEUE
const isc::log::MessageID DHCP4_PARSER_COMMIT_EXCEPTION
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
const isc::log::MessageID DHCP4_PARSER_FAIL
std::vector< HostPtr > HostCollection
Collection of the Host objects.
Definition host.h:846
boost::multi_index_container< Subnet4Ptr, 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 > > > > Subnet4SimpleCollection
A simple collection of Subnet4 objects.
Definition subnet.h:810
const int DBG_DHCP4_COMMAND
Debug level used to log receiving commands.
Definition dhcp4_log.h:30
const isc::log::MessageID DHCP4_PARSER_COMMIT_FAIL
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
isc::data::ConstElementPtr configureDhcp4Server(Dhcpv4Srv &server, isc::data::ConstElementPtr config_set, bool check_only, bool extra_checks)
Configure DHCPv4 server (Dhcpv4Srv) with a set of configuration values.
boost::shared_ptr< CfgSubnets4 > CfgSubnets4Ptr
Non-const pointer.
const isc::log::MessageID DHCP4_CONFIG_COMPLETE
boost::multi_index_container< SharedNetwork4Ptr, 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< SharedNetwork4, std::string, &SharedNetwork4::getName > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SharedNetworkServerIdIndexTag >, boost::multi_index::const_mem_fun< Network4, asiolink::IOAddress, &Network4::getServerId > >, 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 > > > > SharedNetwork4Collection
Multi index container holding shared networks.
boost::shared_ptr< CfgSharedNetworks4 > CfgSharedNetworks4Ptr
Pointer to the configuration of IPv4 shared networks.
isc::log::Logger dhcp4_logger(DHCP4_APP_LOGGER_NAME)
Base logger for DHCPv4 server.
Definition dhcp4_log.h:90
boost::shared_ptr< ConfigControlInfo > ConfigControlInfoPtr
Defines a pointer to a ConfigControlInfo.
Defines the logger used by the top-level component of kea-lfc.
static data::ElementPtr toElement(data::ConstElementPtr map)
Copy an Element map.