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