Kea 3.2.0
dhcp4/json_config_parser.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2026 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 // Sanity check use of SFLQ allocator.
209 cfg->sanityChecksSflqAllocator();
210 }
211
218 void
219 sharedNetworksSanityChecks(const SharedNetwork4Collection& networks,
220 ConstElementPtr json) {
221
223 if (!json) {
224 // No json? That means that the shared-networks was never specified
225 // in the config.
226 return;
227 }
228
229 // Used for names uniqueness checks.
230 std::set<string> names;
231
232 // Let's go through all the networks one by one
233 for (auto const& net : networks) {
234 string txt;
235
236 // Let's check if all subnets have either the same interface
237 // or don't have the interface specified at all.
238 bool authoritative = net->getAuthoritative();
239 string iface = net->getIface();
240
241 const Subnet4SimpleCollection* subnets = net->getAllSubnets();
242 if (subnets) {
243 // For each subnet, add it to a list of regular subnets.
244 for (auto const& subnet : *subnets) {
245 if (subnet->getAuthoritative() != authoritative) {
246 isc_throw(DhcpConfigError, "Subnet " << boolalpha
247 << subnet->toText()
248 << " has different authoritative setting "
249 << subnet->getAuthoritative()
250 << " than the shared-network itself: "
251 << authoritative);
252 }
253
254 if (iface.empty()) {
255 iface = subnet->getIface();
256 continue;
257 }
258
259 if (subnet->getIface().empty()) {
260 continue;
261 }
262
263 if (subnet->getIface() != iface) {
264 isc_throw(DhcpConfigError, "Subnet " << subnet->toText()
265 << " has specified interface " << subnet->getIface()
266 << ", but earlier subnet in the same shared-network"
267 << " or the shared-network itself used " << iface);
268 }
269
270 // Let's collect the subnets in case we later find out the
271 // subnet doesn't have a mandatory name.
272 txt += subnet->toText() + " ";
273 }
274 }
275
276 // Next, let's check name of the shared network.
277 if (net->getName().empty()) {
278 isc_throw(DhcpConfigError, "Shared-network with subnets "
279 << txt << " is missing mandatory 'name' parameter");
280 }
281
282 // Is it unique?
283 if (names.find(net->getName()) != names.end()) {
284 isc_throw(DhcpConfigError, "A shared-network with "
285 "name " << net->getName() << " defined twice.");
286 }
287 names.insert(net->getName());
288
289 }
290 }
291};
292
293} // anonymous namespace
294
295namespace isc {
296namespace dhcp {
297
306 // Get new UNIX socket configuration.
307 ConstElementPtr unix_config =
308 CfgMgr::instance().getStagingCfg()->getUnixControlSocketInfo();
309
310 // Get current UNIX socket configuration.
311 ConstElementPtr current_unix_config =
312 CfgMgr::instance().getCurrentCfg()->getUnixControlSocketInfo();
313
314 // Determine if the socket configuration has changed. It has if
315 // both old and new configuration is specified but respective
316 // data elements aren't equal.
317 bool sock_changed = (unix_config && current_unix_config &&
318 !unix_config->equals(*current_unix_config));
319
320 // If the previous or new socket configuration doesn't exist or
321 // the new configuration differs from the old configuration we
322 // close the existing socket and open a new socket as appropriate.
323 // Note that closing an existing socket means the client will not
324 // receive the configuration result.
325 if (!unix_config || !current_unix_config || sock_changed) {
326 if (unix_config) {
327 // This will create a control socket and install the external
328 // socket in IfaceMgr. That socket will be monitored when
329 // Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
330 // callback in CommandMgr will be called, if necessary.
332 } else if (current_unix_config) {
334 }
335 }
336
337 // Get new HTTP/HTTPS socket configuration.
338 ConstElementPtr http_config =
339 CfgMgr::instance().getStagingCfg()->getHttpControlSocketInfo();
340
341 // Get current HTTP/HTTPS socket configuration.
342 ConstElementPtr current_http_config =
343 CfgMgr::instance().getCurrentCfg()->getHttpControlSocketInfo();
344
345 if (http_config) {
347 } else if (current_http_config) {
349 }
350}
351
358 // Revert any runtime option definitions configured so far and not committed.
360 // Let's set empty container in case a user hasn't specified any configuration
361 // for option definitions. This is equivalent to committing empty container.
363
365
367
368 // Answer will hold the result.
369 ConstElementPtr answer;
370
371 // Global parameter name in case of an error.
372 string parameter_name;
373 ElementPtr mutable_cfg;
374 SrvConfigPtr srv_config;
375 try {
376 // Get the staging configuration.
377 srv_config = CfgMgr::instance().getStagingCfg();
378
379 // This is a way to convert ConstElementPtr to ElementPtr.
380 // We need a config that can be edited, because we will insert
381 // default values and will insert derived values as well.
382 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
383
384 // Set all default values if not specified by the user.
386
387 // And now derive (inherit) global parameters to subnets, if not specified.
389
390 // In principle we could have the following code structured as a series
391 // of long if else if clauses. That would give a marginal performance
392 // boost, but would make the code less readable. We had serious issues
393 // with the parser code debugability, so I decided to keep it as a
394 // series of independent ifs.
395
396 // This parser is used in several places.
397 Dhcp4ConfigParser global_parser;
398
399 // Apply global options in the staging config, e.g. ip-reservations-unique
400 global_parser.parseEarly(srv_config, mutable_cfg);
401
402 // We need definitions first
403 ConstElementPtr option_defs = mutable_cfg->get("option-def");
404 if (option_defs) {
405 parameter_name = "option-def";
406 OptionDefListParser parser(AF_INET);
407 CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
408 parser.parse(cfg_option_def, option_defs);
409 }
410
411 ConstElementPtr option_datas = mutable_cfg->get("option-data");
412 if (option_datas) {
413 parameter_name = "option-data";
414 OptionDataListParser parser(AF_INET);
415 CfgOptionPtr cfg_option = srv_config->getCfgOption();
416 parser.parse(cfg_option, option_datas);
417 }
418
419 ConstElementPtr control_socket = mutable_cfg->get("control-socket");
420 if (control_socket) {
421 mutable_cfg->remove("control-socket");
422 ElementPtr l = Element::createList(control_socket->getPosition());
423 l->add(UserContext::toElement(control_socket));
424 mutable_cfg->set("control-sockets", l);
425 }
426
427 ConstElementPtr control_sockets = mutable_cfg->get("control-sockets");
428 if (control_sockets) {
429 parameter_name = "control-sockets";
431 parser.parse(*srv_config, control_sockets);
432 }
433
434 ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
435 if (multi_threading) {
436 parameter_name = "multi-threading";
438 parser.parse(*srv_config, multi_threading);
439 }
440
441 bool multi_threading_enabled = true;
442 uint32_t thread_count = 0;
443 uint32_t queue_size = 0;
444 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
445 multi_threading_enabled, thread_count, queue_size);
446
448 ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
449 if (queue_control) {
450 parameter_name = "dhcp-queue-control";
452 srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
453 }
454
456 ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
457 if (reservations_lookup_first) {
458 parameter_name = "reservations-lookup-first";
459 if (multi_threading_enabled) {
461 }
462 srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
463 }
464
465 ConstElementPtr hr_identifiers =
466 mutable_cfg->get("host-reservation-identifiers");
467 if (hr_identifiers) {
468 parameter_name = "host-reservation-identifiers";
469 HostReservationIdsParser4 parser(srv_config->getCfgHostOperations4());
470 parser.parse(hr_identifiers);
471 }
472
473 ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
474 if (sanity_checks) {
475 parameter_name = "sanity-checks";
476 SanityChecksParser parser;
477 parser.parse(*srv_config, sanity_checks);
478 }
479
480 ConstElementPtr expiration_cfg =
481 mutable_cfg->get("expired-leases-processing");
482 if (expiration_cfg) {
483 parameter_name = "expired-leases-processing";
485 parser.parse(expiration_cfg, CfgMgr::instance().getStagingCfg()->getCfgExpiration());
486 }
487
488 // The hooks-libraries configuration must be parsed after parsing
489 // multi-threading configuration so that libraries are checked
490 // for multi-threading compatibility.
491 ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
492 if (hooks_libraries) {
493 parameter_name = "hooks-libraries";
494 HooksLibrariesParser hooks_parser;
495 HooksConfig& libraries = srv_config->getHooksConfig();
496 hooks_parser.parse(libraries, hooks_libraries);
497 libraries.verifyLibraries(hooks_libraries->getPosition(),
498 multi_threading_enabled);
499 }
500
501 // D2 client configuration.
502 D2ClientConfigPtr d2_client_cfg;
503
504 // Legacy DhcpConfigParser stuff below.
505 ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
506 if (dhcp_ddns) {
507 parameter_name = "dhcp-ddns";
508 // Apply defaults
511 d2_client_cfg = parser.parse(dhcp_ddns);
512 }
513
514 ConstElementPtr client_classes = mutable_cfg->get("client-classes");
515 if (client_classes) {
516 parameter_name = "client-classes";
518 ClientClassDictionaryPtr dictionary =
519 parser.parse(client_classes, AF_INET);
520 srv_config->setClientClassDictionary(dictionary);
521 }
522
523 // Please move at the end when migration will be finished.
524 ConstElementPtr lease_database = mutable_cfg->get("lease-database");
525 if (lease_database) {
526 parameter_name = "lease-database";
527 db::DbAccessParser parser;
528 std::string access_string;
529 parser.parse(access_string, lease_database);
530 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
531 cfg_db_access->setLeaseDbAccessString(access_string);
532 }
533
534 ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
535 if (hosts_database) {
536 parameter_name = "hosts-database";
537 db::DbAccessParser parser;
538 std::string access_string;
539 parser.parse(access_string, hosts_database);
540 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
541 cfg_db_access->setHostDbAccessString(access_string);
542 }
543
544 ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
545 if (hosts_databases) {
546 parameter_name = "hosts-databases";
547 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
548 for (auto const& it : hosts_databases->listValue()) {
549 db::DbAccessParser parser;
550 std::string access_string;
551 parser.parse(access_string, it);
552 cfg_db_access->setHostDbAccessString(access_string);
553 }
554 }
555
556 // Keep relative orders of shared networks and subnets.
557 ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
558 if (shared_networks) {
559 parameter_name = "shared-networks";
566 CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4();
567 parser.parse(cfg, shared_networks);
568
569 // We also need to put the subnets it contains into normal
570 // subnets list.
571 global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg);
572 }
573
574 ConstElementPtr subnet4 = mutable_cfg->get("subnet4");
575 if (subnet4) {
576 parameter_name = "subnet4";
577 Subnets4ListConfigParser subnets_parser;
578 // parse() returns number of subnets parsed. We may log it one day.
579 subnets_parser.parse(srv_config, subnet4);
580 }
581
582 ConstElementPtr reservations = mutable_cfg->get("reservations");
583 if (reservations) {
584 parameter_name = "reservations";
585 HostCollection hosts;
587 parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
588 for (auto const& h : hosts) {
589 srv_config->getCfgHosts()->add(h);
590 }
591 }
592
593 ConstElementPtr config_control = mutable_cfg->get("config-control");
594 if (config_control) {
595 parameter_name = "config-control";
596 ConfigControlParser parser;
597 ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
598 CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
599 }
600
601 ConstElementPtr compatibility = mutable_cfg->get("compatibility");
602 if (compatibility) {
603 CompatibilityParser parser;
604 parser.parse(compatibility, *CfgMgr::instance().getStagingCfg());
605 }
606
607 // Make parsers grouping.
608 const std::map<std::string, ConstElementPtr>& values_map =
609 mutable_cfg->mapValue();
610
611 for (auto const& config_pair : values_map) {
612 parameter_name = config_pair.first;
613
614 // These are converted to SimpleParser and are handled already above.
615 if ((config_pair.first == "option-def") ||
616 (config_pair.first == "option-data") ||
617 (config_pair.first == "control-socket") ||
618 (config_pair.first == "control-sockets") ||
619 (config_pair.first == "multi-threading") ||
620 (config_pair.first == "dhcp-queue-control") ||
621 (config_pair.first == "host-reservation-identifiers") ||
622 (config_pair.first == "interfaces-config") ||
623 (config_pair.first == "sanity-checks") ||
624 (config_pair.first == "expired-leases-processing") ||
625 (config_pair.first == "hooks-libraries") ||
626 (config_pair.first == "dhcp-ddns") ||
627 (config_pair.first == "client-classes") ||
628 (config_pair.first == "lease-database") ||
629 (config_pair.first == "hosts-database") ||
630 (config_pair.first == "hosts-databases") ||
631 (config_pair.first == "subnet4") ||
632 (config_pair.first == "shared-networks") ||
633 (config_pair.first == "reservations") ||
634 (config_pair.first == "config-control") ||
635 (config_pair.first == "loggers") ||
636 (config_pair.first == "compatibility")) {
637 continue;
638 }
639
640 // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
641 // The old method is used in JSON configuration parsers when the global
642 // parameters are derived into the subnets and shared networks and are
643 // being treated as explicitly specified. The new way used by the config
644 // backend is the dynamic inheritance whereby each subnet and shared
645 // network uses a callback function to return global parameter if it
646 // is not specified at lower level. This callback uses configured globals.
647 // We deliberately include both default and explicitly specified globals
648 // so as the callback can access the appropriate global values regardless
649 // whether they are set to a default or other value.
650 if ( (config_pair.first == "renew-timer") ||
651 (config_pair.first == "rebind-timer") ||
652 (config_pair.first == "valid-lifetime") ||
653 (config_pair.first == "min-valid-lifetime") ||
654 (config_pair.first == "max-valid-lifetime") ||
655 (config_pair.first == "decline-probation-period") ||
656 (config_pair.first == "dhcp4o6-port") ||
657 (config_pair.first == "echo-client-id") ||
658 (config_pair.first == "match-client-id") ||
659 (config_pair.first == "authoritative") ||
660 (config_pair.first == "next-server") ||
661 (config_pair.first == "server-hostname") ||
662 (config_pair.first == "boot-file-name") ||
663 (config_pair.first == "server-tag") ||
664 (config_pair.first == "reservations-global") ||
665 (config_pair.first == "reservations-in-subnet") ||
666 (config_pair.first == "reservations-out-of-pool") ||
667 (config_pair.first == "calculate-tee-times") ||
668 (config_pair.first == "t1-percent") ||
669 (config_pair.first == "t2-percent") ||
670 (config_pair.first == "cache-threshold") ||
671 (config_pair.first == "cache-max-age") ||
672 (config_pair.first == "adaptive-lease-time-threshold") ||
673 (config_pair.first == "hostname-char-set") ||
674 (config_pair.first == "hostname-char-replacement") ||
675 (config_pair.first == "ddns-send-updates") ||
676 (config_pair.first == "ddns-override-no-update") ||
677 (config_pair.first == "ddns-override-client-update") ||
678 (config_pair.first == "ddns-replace-client-name") ||
679 (config_pair.first == "ddns-generated-prefix") ||
680 (config_pair.first == "ddns-qualifying-suffix") ||
681 (config_pair.first == "ddns-update-on-renew") ||
682 (config_pair.first == "ddns-use-conflict-resolution") ||
683 (config_pair.first == "ddns-conflict-resolution-mode") ||
684 (config_pair.first == "ddns-ttl-percent") ||
685 (config_pair.first == "store-extended-info") ||
686 (config_pair.first == "statistic-default-sample-count") ||
687 (config_pair.first == "statistic-default-sample-age") ||
688 (config_pair.first == "early-global-reservations-lookup") ||
689 (config_pair.first == "ip-reservations-unique") ||
690 (config_pair.first == "reservations-lookup-first") ||
691 (config_pair.first == "parked-packet-limit") ||
692 (config_pair.first == "allocator") ||
693 (config_pair.first == "offer-lifetime") ||
694 (config_pair.first == "ddns-ttl") ||
695 (config_pair.first == "ddns-ttl-min") ||
696 (config_pair.first == "ddns-ttl-max") ||
697 (config_pair.first == "stash-agent-options")) {
698 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
699 config_pair.second);
700 continue;
701 }
702
703 // Nothing to configure for the user-context.
704 if (config_pair.first == "user-context") {
705 continue;
706 }
707
708 // If we got here, no code handled this parameter, so we bail out.
710 "unsupported global configuration parameter: " << config_pair.first
711 << " (" << config_pair.second->getPosition() << ")");
712 }
713
714 // Reset parameter name.
715 parameter_name = "<post parsing>";
716
717 // Apply global options in the staging config.
718 global_parser.parse(srv_config, mutable_cfg);
719
720 // This method conducts final sanity checks and tweaks. In particular,
721 // it checks that there is no conflict between plain subnets and those
722 // defined as part of shared networks.
723 global_parser.sanityChecks(srv_config, mutable_cfg);
724
725 // Validate D2 client configuration.
726 if (!d2_client_cfg) {
727 d2_client_cfg.reset(new D2ClientConfig());
728 }
729 d2_client_cfg->validateContents();
730 srv_config->setD2ClientConfig(d2_client_cfg);
731 } catch (const isc::Exception& ex) {
733 .arg(parameter_name).arg(ex.what());
735 } catch (...) {
736 // For things like bad_cast in boost::lexical_cast
737 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
738 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
739 "processing error");
740 }
741
742 if (!answer) {
743 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
744 "Control-socket, hook-libraries, and D2 configuration "
745 "were sanity checked, but not applied.");
746 }
747
748 return (answer);
749}
750
753 bool check_only, bool extra_checks) {
754 if (!config_set) {
756 "Can't parse NULL config");
757 return (answer);
758 }
759
761 .arg(server.redactConfig(config_set)->str());
762
763 // Resource managers usually check for @ref MultiThreadingMgr::isTestMode
764 // value and do not open UNIX or TCP/UDP sockets, lock files, nor do they open
765 // or rotate files, as any of these actions could interfere with a running
766 // process on the same machine.
767 std::unique_ptr<MtTestMode> mt_test_mode;
768 if (check_only) {
769 mt_test_mode.reset(new MtTestMode());
770 }
771
772 auto answer = processDhcp4Config(config_set);
773
774 int status_code = CONTROL_RESULT_SUCCESS;
775 isc::config::parseAnswer(status_code, answer);
776
777 SrvConfigPtr srv_config;
778
779 if (status_code == CONTROL_RESULT_SUCCESS) {
780 if (check_only) {
781 if (extra_checks) {
782 std::ostringstream err;
783 // Configure DHCP packet queueing
784 try {
786 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
787 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
789 .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
790 }
791
792 } catch (const std::exception& ex) {
793 err << "Error setting packet queue controls after server reconfiguration: "
794 << ex.what();
796 status_code = CONTROL_RESULT_ERROR;
797 }
798 }
799 } else {
800
801 // Usually unit tests create managers before calling configureDhcp4Server and
802 // do not call ControlledDhcpv4Srv::processConfig.
803 // Runtime code path creates the managers after calling configureDhcp4Server
804 // and they need to be reset just after successful configuration parsing.
805 if (!IfaceMgr::instance().isTestMode()) {
806 // Destroy lease manager before hooks unload.
808
809 // Destroy host manager before hooks unload.
811 }
812
813 // disable multi-threading (it will be applied by new configuration)
814 // this must be done in order to properly handle MT to ST transition
815 // when 'multi-threading' structure is missing from new config and
816 // to properly drop any task items stored in the thread pool which
817 // might reference some handles to loaded hooks, preventing them
818 // from being unloaded.
819 MultiThreadingMgr::instance().apply(false, 0, 0);
820
821 // Close DHCP sockets and remove any existing timers.
823 TimerMgr::instance()->unregisterTimers();
824 server.discardPackets();
825 server.getCBControl()->reset();
826 }
827 }
828
829 // Parsing stage is complete. The current configuration has not been altered.
830 // From this stage on, every error might have irreversible consequences and the
831 // configuration might not be restored to a working state.
832 if (status_code == CONTROL_RESULT_SUCCESS) {
833 string parameter_name;
834 ElementPtr mutable_cfg;
835 try {
836 // Get the staging configuration.
837 srv_config = CfgMgr::instance().getStagingCfg();
838
839 // This is a way to convert ConstElementPtr to ElementPtr.
840 // We need a config that can be edited, because we will insert
841 // default values and will insert derived values as well.
842 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
843
844 ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
845 if (ifaces_config) {
846 parameter_name = "interfaces-config";
847 IfacesConfigParser parser(AF_INET, check_only);
848 CfgIfacePtr cfg_iface = srv_config->getCfgIface();
849 cfg_iface->reset();
850 parser.parse(cfg_iface, ifaces_config);
851 }
852 } catch (const isc::Exception& ex) {
854 .arg(parameter_name).arg(ex.what());
855 if (!check_only || extra_checks) {
856 status_code = CONTROL_RESULT_FATAL_ERROR;
857 } else {
858 status_code = CONTROL_RESULT_ERROR;
859 }
860 answer = isc::config::createAnswer(status_code, ex.what());
861 } catch (...) {
862 // For things like bad_cast in boost::lexical_cast
863 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
864 if (!check_only || extra_checks) {
865 status_code = CONTROL_RESULT_FATAL_ERROR;
866 } else {
867 status_code = CONTROL_RESULT_ERROR;
868 }
869 answer = isc::config::createAnswer(status_code, "undefined configuration"
870 " processing error");
871 }
872 }
873
874 // So far so good, there was no parsing error so let's commit the
875 // configuration. This will add created subnets and option values into
876 // the server's configuration.
877 // This operation should be exception safe but let's make sure.
878 if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
879 try {
880
881 // Setup the command channel.
883 } catch (const isc::Exception& ex) {
885 status_code = CONTROL_RESULT_FATAL_ERROR;
886 answer = isc::config::createAnswer(status_code, ex.what());
887 } catch (...) {
888 // For things like bad_cast in boost::lexical_cast
890 status_code = CONTROL_RESULT_FATAL_ERROR;
891 answer = isc::config::createAnswer(status_code, "undefined configuration"
892 " parsing error");
893 }
894 }
895
896 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
897 try {
898 // No need to commit interface names as this is handled by the
899 // CfgMgr::commit() function.
900
901 // Apply the staged D2ClientConfig, used to be done by parser commit
903 cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
905 } catch (const isc::Exception& ex) {
907 status_code = CONTROL_RESULT_FATAL_ERROR;
908 answer = isc::config::createAnswer(status_code, ex.what());
909 } catch (...) {
910 // For things like bad_cast in boost::lexical_cast
912 status_code = CONTROL_RESULT_FATAL_ERROR;
913 answer = isc::config::createAnswer(status_code, "undefined configuration"
914 " parsing error");
915 }
916 }
917
918 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
919 try {
920 // This occurs last as if it succeeds, there is no easy way to
921 // revert it. As a result, the failure to commit a subsequent
922 // change causes problems when trying to roll back.
924 static_cast<void>(HooksManager::unloadLibraries());
926 const HooksConfig& libraries =
927 CfgMgr::instance().getStagingCfg()->getHooksConfig();
928 bool multi_threading_enabled = true;
929 uint32_t thread_count = 0;
930 uint32_t queue_size = 0;
931 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
932 multi_threading_enabled, thread_count, queue_size);
933 libraries.loadLibraries(multi_threading_enabled);
934 } catch (const isc::Exception& ex) {
936 status_code = CONTROL_RESULT_FATAL_ERROR;
937 answer = isc::config::createAnswer(status_code, ex.what());
938 } catch (...) {
939 // For things like bad_cast in boost::lexical_cast
941 status_code = CONTROL_RESULT_FATAL_ERROR;
942 answer = isc::config::createAnswer(status_code, "undefined configuration"
943 " parsing error");
944 }
945
946 if (extra_checks && status_code == CONTROL_RESULT_SUCCESS) {
947 // Re-open lease and host database with new parameters.
948 try {
949 // Get the staging configuration.
950 srv_config = CfgMgr::instance().getStagingCfg();
951
952 // Create managers like @ref ControlledDhcpv4Srv::processConfig
953 // does. No need to change "persist" value here because
954 // @ref MultiThreadingMgr::isTestMode is used instead.
955 // This will also make log messages consistent with the checked
956 // config values.
957 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
958 string params = "universe=4";
959 cfg_db->setAppendedParameters(params);
960 cfg_db->createManagers();
961 } catch (const std::exception& ex) {
962 status_code = CONTROL_RESULT_FATAL_ERROR;
963 answer = isc::config::createAnswer(status_code, ex.what());
964 }
965 }
966 }
967
968 // Log the list of known backends.
970
971 // Log the list of known backends.
973
974 // Log the list of known backends.
976
977 // Log the list of known backends.
979
980 // Moved from the commit block to add the config backend indication.
981 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
982 try {
983 // If there are config backends, fetch and merge into staging config
984 server.getCBControl()->databaseConfigFetch(srv_config,
985 CBControlDHCPv4::FetchMode::FETCH_ALL);
986 } catch (const isc::Exception& ex) {
987 std::ostringstream err;
988 err << "during update from config backend database: " << ex.what();
990 status_code = CONTROL_RESULT_FATAL_ERROR;
991 answer = isc::config::createAnswer(status_code, err.str());
992 } catch (...) {
993 // For things like bad_cast in boost::lexical_cast
994 std::ostringstream err;
995 err << "during update from config backend database: "
996 << "undefined configuration parsing error";
998 status_code = CONTROL_RESULT_FATAL_ERROR;
999 answer = isc::config::createAnswer(status_code, err.str());
1000 }
1001 }
1002
1003 // Rollback changes as the configuration parsing failed.
1004 if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
1005 // Revert to original configuration of runtime option definitions
1006 // in the libdhcp++.
1008
1009 if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
1010 auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
1011 if (notify_libraries) {
1012 return (notify_libraries);
1013 }
1014
1016 try {
1017 // Handle events registered by hooks using external IOService objects.
1019 } catch (const std::exception& ex) {
1020 std::ostringstream err;
1021 err << "Error initializing hooks: "
1022 << ex.what();
1024 }
1025 }
1026
1027 return (answer);
1028 }
1029
1031 .arg(CfgMgr::instance().getStagingCfg()->
1032 getConfigSummary(SrvConfig::CFGSEL_ALL4));
1033
1034 // Also calculate SHA256 hash of the config that was just set and
1035 // append it to the response.
1037 string hash = BaseCommandMgr::getHash(config);
1038 ElementPtr hash_map = Element::createMap();
1039 hash_map->set("hash", Element::create(hash));
1040
1041 // Everything was fine. Configuration is successful.
1042 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
1043 return (answer);
1044}
1045
1046} // namespace dhcp
1047} // namespace isc
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())
Create a NullElement.
Definition data.cc:299
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:354
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:349
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:45
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition cfgmgr.cc:121
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:116
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:267
CBControlDHCPv4Ptr getCBControl() const
Returns an object which controls access to the configuration backends.
Definition dhcp4_srv.h:332
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.
static void create()
Creates new instance of the HostMgr.
Definition host_mgr.cc:52
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.
void detectIfaces(bool update_only=false)
Detects network interfaces.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:52
void closeSockets()
Closes all open sockets.
Definition iface_mgr.cc:313
Parser for the configuration of interfaces.
void parse(const CfgIfacePtr &config, const isc::data::ConstElementPtr &values)
Parses content of the "interfaces-config".
static void destroy()
Destroy lease manager.
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.
RAII class creating a critical section for the receiver thread.
Definition iface_mgr.h:1867
Simple parser for sanity-checks structure.
void parse(SrvConfig &srv_cfg, const isc::data::ConstElementPtr &value)
parses JSON structure
void parse(CfgSharedNetworksTypePtr &cfg, const data::ConstElementPtr &shared_networks_list_data)
Parses a list of shared networks.
static size_t deriveParameters(isc::data::ElementPtr global)
Derives (inherits) all parameters from global to more specific scopes.
static size_t setAllDefaults(isc::data::ElementPtr global)
Sets all defaults for 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:297
RAII wrapper for MT test mode.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
void apply(bool enabled, uint32_t thread_count, uint32_t queue_size)
Apply the multi-threading related settings.
Parsers for client class definitions.
This file contains several functions and constants that are used for handling commands and responses ...
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(const int status_code, const std::string &text, const ConstElementPtr &arg)
Creates a standard config/command level answer message.
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
const int CONTROL_RESULT_FATAL_ERROR
Status code indicating that the command was unsuccessful and the configuration could not be reverted ...
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
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:973
SharedNetworksListParser< SharedNetwork4Parser > SharedNetworks4ListParser
Type of the shared networks list parser for IPv4.
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 configuration and return an answer stating if the configuration is valid,...
boost::shared_ptr< CfgIface > CfgIfacePtr
A pointer to the CfgIface .
Definition cfg_iface.h:522
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:807
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.