Kea 2.5.8
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>
16#include <dhcp4/dhcp4_log.h>
17#include <dhcp4/dhcp4_srv.h>
19#include <dhcp/libdhcp++.h>
23#include <dhcpsrv/cfg_option.h>
24#include <dhcpsrv/cfgmgr.h>
26#include <dhcpsrv/db_type.h>
40#include <dhcpsrv/timer_mgr.h>
41#include <hooks/hooks_manager.h>
42#include <hooks/hooks_parser.h>
44#include <util/encode/encode.h>
46
47#include <boost/algorithm/string.hpp>
48#include <boost/lexical_cast.hpp>
49
50#include <iomanip>
51#include <iostream>
52#include <limits>
53#include <map>
54#include <netinet/in.h>
55#include <vector>
56
57using namespace std;
58using namespace isc;
59using namespace isc::data;
60using namespace isc::dhcp;
61using namespace isc::asiolink;
62using namespace isc::hooks;
63using namespace isc::process;
64using namespace isc::config;
65using namespace isc::util;
66
67namespace {
68
77class Dhcp4ConfigParser : public isc::data::SimpleParser {
78public:
79
94 void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
95
96 // Set whether v4 server is supposed to echo back client-id
97 // (yes = RFC6842 compatible, no = backward compatibility)
98 bool echo_client_id = getBoolean(global, "echo-client-id");
99 cfg->setEchoClientId(echo_client_id);
100
101 // Set the probation period for decline handling.
102 uint32_t probation_period =
103 getUint32(global, "decline-probation-period");
104 cfg->setDeclinePeriod(probation_period);
105
106 // Set the DHCPv4-over-DHCPv6 interserver port.
107 uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
108 cfg->setDhcp4o6Port(dhcp4o6_port);
109
110 // Set the global user context.
111 ConstElementPtr user_context = global->get("user-context");
112 if (user_context) {
113 cfg->setContext(user_context);
114 }
115
116 // Set the server's logical name
117 std::string server_tag = getString(global, "server-tag");
118 cfg->setServerTag(server_tag);
119 }
120
132 void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
133 // Set ip-reservations-unique flag.
134 bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
135 cfg->setIPReservationsUnique(ip_reservations_unique);
136 }
137
144 void
145 copySubnets4(const CfgSubnets4Ptr& dest, const CfgSharedNetworks4Ptr& from) {
146
147 if (!dest || !from) {
148 isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null");
149 }
150
151 const SharedNetwork4Collection* networks = from->getAll();
152 if (!networks) {
153 // Nothing to copy. Technically, it should return a pointer to empty
154 // container, but let's handle null pointer as well.
155 return;
156 }
157
158 // Let's go through all the networks one by one
159 for (auto const& net : *networks) {
160
161 // For each network go through all the subnets in it.
162 const Subnet4SimpleCollection* subnets = net->getAllSubnets();
163 if (!subnets) {
164 // Shared network without subnets it weird, but we decided to
165 // accept such configurations.
166 continue;
167 }
168
169 // For each subnet, add it to a list of regular subnets.
170 for (auto const& subnet : *subnets) {
171 dest->add(subnet);
172 }
173 }
174 }
175
184 void
185 sanityChecks(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
186
188 cfg->sanityChecksLifetime("valid-lifetime");
189
191 const SharedNetwork4Collection* networks = cfg->getCfgSharedNetworks4()->getAll();
192 if (networks) {
193 sharedNetworksSanityChecks(*networks, global->get("shared-networks"));
194 }
195 }
196
203 void
204 sharedNetworksSanityChecks(const SharedNetwork4Collection& networks,
205 ConstElementPtr json) {
206
208 if (!json) {
209 // No json? That means that the shared-networks was never specified
210 // in the config.
211 return;
212 }
213
214 // Used for names uniqueness checks.
215 std::set<string> names;
216
217 // Let's go through all the networks one by one
218 for (auto const& net : networks) {
219 string txt;
220
221 // Let's check if all subnets have either the same interface
222 // or don't have the interface specified at all.
223 bool authoritative = net->getAuthoritative();
224 string iface = net->getIface();
225
226 const Subnet4SimpleCollection* subnets = net->getAllSubnets();
227 if (subnets) {
228 // For each subnet, add it to a list of regular subnets.
229 for (auto const& subnet : *subnets) {
230 if (subnet->getAuthoritative() != authoritative) {
231 isc_throw(DhcpConfigError, "Subnet " << boolalpha
232 << subnet->toText()
233 << " has different authoritative setting "
234 << subnet->getAuthoritative()
235 << " than the shared-network itself: "
236 << authoritative);
237 }
238
239 if (iface.empty()) {
240 iface = subnet->getIface();
241 continue;
242 }
243
244 if (subnet->getIface().empty()) {
245 continue;
246 }
247
248 if (subnet->getIface() != iface) {
249 isc_throw(DhcpConfigError, "Subnet " << subnet->toText()
250 << " has specified interface " << subnet->getIface()
251 << ", but earlier subnet in the same shared-network"
252 << " or the shared-network itself used " << iface);
253 }
254
255 // Let's collect the subnets in case we later find out the
256 // subnet doesn't have a mandatory name.
257 txt += subnet->toText() + " ";
258 }
259 }
260
261 // Next, let's check name of the shared network.
262 if (net->getName().empty()) {
263 isc_throw(DhcpConfigError, "Shared-network with subnets "
264 << txt << " is missing mandatory 'name' parameter");
265 }
266
267 // Is it unique?
268 if (names.find(net->getName()) != names.end()) {
269 isc_throw(DhcpConfigError, "A shared-network with "
270 "name " << net->getName() << " defined twice.");
271 }
272 names.insert(net->getName());
273
274 }
275 }
276};
277
278} // anonymous namespace
279
280namespace isc {
281namespace dhcp {
282
291 // Get new socket configuration.
292 ConstElementPtr sock_cfg =
293 CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
294
295 // Get current socket configuration.
296 ConstElementPtr current_sock_cfg =
297 CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();
298
299 // Determine if the socket configuration has changed. It has if
300 // both old and new configuration is specified but respective
301 // data elements aren't equal.
302 bool sock_changed = (sock_cfg && current_sock_cfg &&
303 !sock_cfg->equals(*current_sock_cfg));
304
305 // If the previous or new socket configuration doesn't exist or
306 // the new configuration differs from the old configuration we
307 // close the existing socket and open a new socket as appropriate.
308 // Note that closing an existing socket means the client will not
309 // receive the configuration result.
310 if (!sock_cfg || !current_sock_cfg || sock_changed) {
311 // Close the existing socket (if any).
313
314 if (sock_cfg) {
315 // This will create a control socket and install the external
316 // socket in IfaceMgr. That socket will be monitored when
317 // Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
318 // callback in CommandMgr will be called, if necessary.
320 }
321 }
322}
323
330 // Before starting any subnet operations, let's reset the subnet-id counter,
331 // so newly recreated configuration starts with first subnet-id equal 1.
333
334 // Revert any runtime option definitions configured so far and not committed.
336 // Let's set empty container in case a user hasn't specified any configuration
337 // for option definitions. This is equivalent to committing empty container.
339
340 // Print the list of known backends.
342
343 // Answer will hold the result.
344 ConstElementPtr answer;
345
346 // Global parameter name in case of an error.
347 string parameter_name;
348 ElementPtr mutable_cfg;
349 SrvConfigPtr srv_config;
350 try {
351 // Get the staging configuration.
352 srv_config = CfgMgr::instance().getStagingCfg();
353
354 // This is a way to convert ConstElementPtr to ElementPtr.
355 // We need a config that can be edited, because we will insert
356 // default values and will insert derived values as well.
357 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
358
359 // Relocate dhcp-ddns parameters that have moved to global scope.
360 // Rule is that a global value overrides the dhcp-ddns value, so
361 // we need to do this before we apply global defaults.
362 // Note this is done for backward compatibility.
363 srv_config->moveDdnsParams(mutable_cfg);
364
365 // Move from reservation mode to new reservations flags.
366 // @todo add warning
368
369 // Set all default values if not specified by the user.
371
372 // And now derive (inherit) global parameters to subnets, if not specified.
374
375 // In principle we could have the following code structured as a series
376 // of long if else if clauses. That would give a marginal performance
377 // boost, but would make the code less readable. We had serious issues
378 // with the parser code debugability, so I decided to keep it as a
379 // series of independent ifs.
380
381 // This parser is used in several places.
382 Dhcp4ConfigParser global_parser;
383
384 // Apply global options in the staging config, e.g. ip-reservations-unique
385 global_parser.parseEarly(srv_config, mutable_cfg);
386
387 // We need definitions first
388 ConstElementPtr option_defs = mutable_cfg->get("option-def");
389 if (option_defs) {
390 parameter_name = "option-def";
391 OptionDefListParser parser(AF_INET);
392 CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
393 parser.parse(cfg_option_def, option_defs);
394 }
395
396 ConstElementPtr option_datas = mutable_cfg->get("option-data");
397 if (option_datas) {
398 parameter_name = "option-data";
399 OptionDataListParser parser(AF_INET);
400 CfgOptionPtr cfg_option = srv_config->getCfgOption();
401 parser.parse(cfg_option, option_datas);
402 }
403
404 ConstElementPtr control_socket = mutable_cfg->get("control-socket");
405 if (control_socket) {
406 parameter_name = "control-socket";
407 ControlSocketParser parser;
408 parser.parse(*srv_config, control_socket);
409 }
410
411 ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
412 if (multi_threading) {
413 parameter_name = "multi-threading";
415 parser.parse(*srv_config, multi_threading);
416 }
417
418 bool multi_threading_enabled = true;
419 uint32_t thread_count = 0;
420 uint32_t queue_size = 0;
421 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
422 multi_threading_enabled, thread_count, queue_size);
423
425 ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
426 if (queue_control) {
427 parameter_name = "dhcp-queue-control";
429 srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
430 }
431
433 ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
434 if (reservations_lookup_first) {
435 parameter_name = "reservations-lookup-first";
436 if (multi_threading_enabled) {
438 }
439 srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
440 }
441
442 ConstElementPtr hr_identifiers =
443 mutable_cfg->get("host-reservation-identifiers");
444 if (hr_identifiers) {
445 parameter_name = "host-reservation-identifiers";
447 parser.parse(hr_identifiers);
448 }
449
450 ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
451 if (sanity_checks) {
452 parameter_name = "sanity-checks";
453 SanityChecksParser parser;
454 parser.parse(*srv_config, sanity_checks);
455 }
456
457 ConstElementPtr expiration_cfg =
458 mutable_cfg->get("expired-leases-processing");
459 if (expiration_cfg) {
460 parameter_name = "expired-leases-processing";
462 parser.parse(expiration_cfg, CfgMgr::instance().getStagingCfg()->getCfgExpiration());
463 }
464
465 // The hooks-libraries configuration must be parsed after parsing
466 // multi-threading configuration so that libraries are checked
467 // for multi-threading compatibility.
468 ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
469 if (hooks_libraries) {
470 parameter_name = "hooks-libraries";
471 HooksLibrariesParser hooks_parser;
472 HooksConfig& libraries = srv_config->getHooksConfig();
473 hooks_parser.parse(libraries, hooks_libraries);
474 libraries.verifyLibraries(hooks_libraries->getPosition(),
475 multi_threading_enabled);
476 }
477
478 // D2 client configuration.
479 D2ClientConfigPtr d2_client_cfg;
480
481 // Legacy DhcpConfigParser stuff below.
482 ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
483 if (dhcp_ddns) {
484 parameter_name = "dhcp-ddns";
485 // Apply defaults
488 d2_client_cfg = parser.parse(dhcp_ddns);
489 }
490
491 ConstElementPtr client_classes = mutable_cfg->get("client-classes");
492 if (client_classes) {
493 parameter_name = "client-classes";
495 ClientClassDictionaryPtr dictionary =
496 parser.parse(client_classes, AF_INET);
497 srv_config->setClientClassDictionary(dictionary);
498 }
499
500 // Please move at the end when migration will be finished.
501 ConstElementPtr lease_database = mutable_cfg->get("lease-database");
502 if (lease_database) {
503 parameter_name = "lease-database";
504 db::DbAccessParser parser;
505 std::string access_string;
506 parser.parse(access_string, lease_database);
507 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
508 cfg_db_access->setLeaseDbAccessString(access_string);
509 }
510
511 ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
512 if (hosts_database) {
513 parameter_name = "hosts-database";
514 db::DbAccessParser parser;
515 std::string access_string;
516 parser.parse(access_string, hosts_database);
517 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
518 cfg_db_access->setHostDbAccessString(access_string);
519 }
520
521 ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
522 if (hosts_databases) {
523 parameter_name = "hosts-databases";
524 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
525 for (auto const& it : hosts_databases->listValue()) {
526 db::DbAccessParser parser;
527 std::string access_string;
528 parser.parse(access_string, it);
529 cfg_db_access->setHostDbAccessString(access_string);
530 }
531 }
532
533 // Keep relative orders of shared networks and subnets.
534 ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
535 if (shared_networks) {
536 parameter_name = "shared-networks";
543 CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4();
544 parser.parse(cfg, shared_networks);
545
546 // We also need to put the subnets it contains into normal
547 // subnets list.
548 global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg);
549 }
550
551 ConstElementPtr subnet4 = mutable_cfg->get("subnet4");
552 if (subnet4) {
553 parameter_name = "subnet4";
554 Subnets4ListConfigParser subnets_parser;
555 // parse() returns number of subnets parsed. We may log it one day.
556 subnets_parser.parse(srv_config, subnet4);
557 }
558
559 ConstElementPtr reservations = mutable_cfg->get("reservations");
560 if (reservations) {
561 parameter_name = "reservations";
562 HostCollection hosts;
564 parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
565 for (auto const& h : hosts) {
566 srv_config->getCfgHosts()->add(h);
567 }
568 }
569
570 ConstElementPtr config_control = mutable_cfg->get("config-control");
571 if (config_control) {
572 parameter_name = "config-control";
573 ConfigControlParser parser;
574 ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
575 CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
576 }
577
578 ConstElementPtr compatibility = mutable_cfg->get("compatibility");
579 if (compatibility) {
580 CompatibilityParser parser;
581 parser.parse(compatibility, *CfgMgr::instance().getStagingCfg());
582 }
583
584 // Make parsers grouping.
585 const std::map<std::string, ConstElementPtr>& values_map =
586 mutable_cfg->mapValue();
587
588 for (auto const& config_pair : values_map) {
589 parameter_name = config_pair.first;
590
591 // These are converted to SimpleParser and are handled already above.
592 if ((config_pair.first == "option-def") ||
593 (config_pair.first == "option-data") ||
594 (config_pair.first == "control-socket") ||
595 (config_pair.first == "multi-threading") ||
596 (config_pair.first == "dhcp-queue-control") ||
597 (config_pair.first == "host-reservation-identifiers") ||
598 (config_pair.first == "interfaces-config") ||
599 (config_pair.first == "sanity-checks") ||
600 (config_pair.first == "expired-leases-processing") ||
601 (config_pair.first == "hooks-libraries") ||
602 (config_pair.first == "dhcp-ddns") ||
603 (config_pair.first == "client-classes") ||
604 (config_pair.first == "lease-database") ||
605 (config_pair.first == "hosts-database") ||
606 (config_pair.first == "hosts-databases") ||
607 (config_pair.first == "subnet4") ||
608 (config_pair.first == "shared-networks") ||
609 (config_pair.first == "reservations") ||
610 (config_pair.first == "config-control") ||
611 (config_pair.first == "loggers") ||
612 (config_pair.first == "compatibility")) {
613 continue;
614 }
615
616 // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
617 // The old method is used in JSON configuration parsers when the global
618 // parameters are derived into the subnets and shared networks and are
619 // being treated as explicitly specified. The new way used by the config
620 // backend is the dynamic inheritance whereby each subnet and shared
621 // network uses a callback function to return global parameter if it
622 // is not specified at lower level. This callback uses configured globals.
623 // We deliberately include both default and explicitly specified globals
624 // so as the callback can access the appropriate global values regardless
625 // whether they are set to a default or other value.
626 if ( (config_pair.first == "renew-timer") ||
627 (config_pair.first == "rebind-timer") ||
628 (config_pair.first == "valid-lifetime") ||
629 (config_pair.first == "min-valid-lifetime") ||
630 (config_pair.first == "max-valid-lifetime") ||
631 (config_pair.first == "decline-probation-period") ||
632 (config_pair.first == "dhcp4o6-port") ||
633 (config_pair.first == "echo-client-id") ||
634 (config_pair.first == "match-client-id") ||
635 (config_pair.first == "authoritative") ||
636 (config_pair.first == "next-server") ||
637 (config_pair.first == "server-hostname") ||
638 (config_pair.first == "boot-file-name") ||
639 (config_pair.first == "server-tag") ||
640 (config_pair.first == "reservation-mode") ||
641 (config_pair.first == "reservations-global") ||
642 (config_pair.first == "reservations-in-subnet") ||
643 (config_pair.first == "reservations-out-of-pool") ||
644 (config_pair.first == "calculate-tee-times") ||
645 (config_pair.first == "t1-percent") ||
646 (config_pair.first == "t2-percent") ||
647 (config_pair.first == "cache-threshold") ||
648 (config_pair.first == "cache-max-age") ||
649 (config_pair.first == "hostname-char-set") ||
650 (config_pair.first == "hostname-char-replacement") ||
651 (config_pair.first == "ddns-send-updates") ||
652 (config_pair.first == "ddns-override-no-update") ||
653 (config_pair.first == "ddns-override-client-update") ||
654 (config_pair.first == "ddns-replace-client-name") ||
655 (config_pair.first == "ddns-generated-prefix") ||
656 (config_pair.first == "ddns-qualifying-suffix") ||
657 (config_pair.first == "ddns-update-on-renew") ||
658 (config_pair.first == "ddns-use-conflict-resolution") ||
659 (config_pair.first == "ddns-conflict-resolution-mode") ||
660 (config_pair.first == "ddns-ttl-percent") ||
661 (config_pair.first == "store-extended-info") ||
662 (config_pair.first == "statistic-default-sample-count") ||
663 (config_pair.first == "statistic-default-sample-age") ||
664 (config_pair.first == "early-global-reservations-lookup") ||
665 (config_pair.first == "ip-reservations-unique") ||
666 (config_pair.first == "reservations-lookup-first") ||
667 (config_pair.first == "parked-packet-limit") ||
668 (config_pair.first == "allocator") ||
669 (config_pair.first == "offer-lifetime") ) {
670 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
671 config_pair.second);
672 continue;
673 }
674
675 // Nothing to configure for the user-context.
676 if (config_pair.first == "user-context") {
677 continue;
678 }
679
680 // If we got here, no code handled this parameter, so we bail out.
682 "unsupported global configuration parameter: " << config_pair.first
683 << " (" << config_pair.second->getPosition() << ")");
684 }
685
686 // Reset parameter name.
687 parameter_name = "<post parsing>";
688
689 // Apply global options in the staging config.
690 global_parser.parse(srv_config, mutable_cfg);
691
692 // This method conducts final sanity checks and tweaks. In particular,
693 // it checks that there is no conflict between plain subnets and those
694 // defined as part of shared networks.
695 global_parser.sanityChecks(srv_config, mutable_cfg);
696
697 // Validate D2 client configuration.
698 if (!d2_client_cfg) {
699 d2_client_cfg.reset(new D2ClientConfig());
700 }
701 d2_client_cfg->validateContents();
702 srv_config->setD2ClientConfig(d2_client_cfg);
703 } catch (const isc::Exception& ex) {
705 .arg(parameter_name).arg(ex.what());
707 } catch (...) {
708 // For things like bad_cast in boost::lexical_cast
709 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
710 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
711 "processing error");
712 }
713
714 if (!answer) {
715 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
716 "Control-socket, hook-libraries, and D2 configuration "
717 "were sanity checked, but not applied.");
718 }
719
720 return (answer);
721}
722
725 bool check_only, bool extra_checks) {
726 if (!config_set) {
728 "Can't parse NULL config");
729 return (answer);
730 }
731
733 .arg(server.redactConfig(config_set)->str());
734
735 auto answer = processDhcp4Config(config_set);
736
737 int status_code = CONTROL_RESULT_SUCCESS;
738 isc::config::parseAnswer(status_code, answer);
739
740 SrvConfigPtr srv_config;
741
742 if (status_code == CONTROL_RESULT_SUCCESS) {
743 if (check_only) {
744 if (extra_checks) {
745 // Re-open lease and host database with new parameters.
746 try {
747 // Get the staging configuration.
748 srv_config = CfgMgr::instance().getStagingCfg();
749
750 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
751 string params = "universe=4 persist=false";
752 if (cfg_db->getExtendedInfoTablesEnabled()) {
753 params += " extended-info-tables=true";
754 }
755 cfg_db->setAppendedParameters(params);
756 cfg_db->createManagers();
757 } catch (const std::exception& ex) {
759 status_code = CONTROL_RESULT_ERROR;
760 }
761
762 if (status_code == CONTROL_RESULT_SUCCESS) {
763 std::ostringstream err;
764 // Configure DHCP packet queueing
765 try {
767 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
768 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
770 .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
771 }
772
773 } catch (const std::exception& ex) {
774 err << "Error setting packet queue controls after server reconfiguration: "
775 << ex.what();
777 status_code = CONTROL_RESULT_ERROR;
778 }
779 }
780 }
781 } else {
782 // disable multi-threading (it will be applied by new configuration)
783 // this must be done in order to properly handle MT to ST transition
784 // when 'multi-threading' structure is missing from new config and
785 // to properly drop any task items stored in the thread pool which
786 // might reference some handles to loaded hooks, preventing them
787 // from being unloaded.
788 MultiThreadingMgr::instance().apply(false, 0, 0);
789
790 // Close DHCP sockets and remove any existing timers.
792 TimerMgr::instance()->unregisterTimers();
793 server.discardPackets();
794 server.getCBControl()->reset();
795 }
796
797 if (status_code == CONTROL_RESULT_SUCCESS) {
798 string parameter_name;
799 ElementPtr mutable_cfg;
800 try {
801 // Get the staging configuration.
802 srv_config = CfgMgr::instance().getStagingCfg();
803
804 // This is a way to convert ConstElementPtr to ElementPtr.
805 // We need a config that can be edited, because we will insert
806 // default values and will insert derived values as well.
807 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
808
809 ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
810 if (ifaces_config) {
811 parameter_name = "interfaces-config";
812 IfacesConfigParser parser(AF_INET, check_only);
813 CfgIfacePtr cfg_iface = srv_config->getCfgIface();
814 cfg_iface->reset();
815 parser.parse(cfg_iface, ifaces_config);
816 }
817 } catch (const isc::Exception& ex) {
819 .arg(parameter_name).arg(ex.what());
821 status_code = CONTROL_RESULT_ERROR;
822 } catch (...) {
823 // For things like bad_cast in boost::lexical_cast
824 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
825 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
826 " processing error");
827 status_code = CONTROL_RESULT_ERROR;
828 }
829 }
830 }
831
832 // So far so good, there was no parsing error so let's commit the
833 // configuration. This will add created subnets and option values into
834 // the server's configuration.
835 // This operation should be exception safe but let's make sure.
836 if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
837 try {
838
839 // Setup the command channel.
841 } catch (const isc::Exception& ex) {
844 status_code = CONTROL_RESULT_ERROR;
845 } catch (...) {
846 // For things like bad_cast in boost::lexical_cast
848 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
849 " parsing error");
850 status_code = CONTROL_RESULT_ERROR;
851 }
852 }
853
854 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
855 try {
856 // No need to commit interface names as this is handled by the
857 // CfgMgr::commit() function.
858
859 // Apply the staged D2ClientConfig, used to be done by parser commit
861 cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
863 } catch (const isc::Exception& ex) {
866 status_code = CONTROL_RESULT_ERROR;
867 } catch (...) {
868 // For things like bad_cast in boost::lexical_cast
870 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
871 " parsing error");
872 status_code = CONTROL_RESULT_ERROR;
873 }
874 }
875
876 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
877 try {
878 // This occurs last as if it succeeds, there is no easy way to
879 // revert it. As a result, the failure to commit a subsequent
880 // change causes problems when trying to roll back.
882 static_cast<void>(HooksManager::unloadLibraries());
884 const HooksConfig& libraries =
885 CfgMgr::instance().getStagingCfg()->getHooksConfig();
886 bool multi_threading_enabled = true;
887 uint32_t thread_count = 0;
888 uint32_t queue_size = 0;
889 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
890 multi_threading_enabled, thread_count, queue_size);
891 libraries.loadLibraries(multi_threading_enabled);
892 } catch (const isc::Exception& ex) {
895 status_code = CONTROL_RESULT_ERROR;
896 } catch (...) {
897 // For things like bad_cast in boost::lexical_cast
899 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
900 " parsing error");
901 status_code = CONTROL_RESULT_ERROR;
902 }
903 }
904
905 // Moved from the commit block to add the config backend indication.
906 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
907 try {
908 // If there are config backends, fetch and merge into staging config
909 server.getCBControl()->databaseConfigFetch(srv_config,
911 } catch (const isc::Exception& ex) {
912 std::ostringstream err;
913 err << "during update from config backend database: " << ex.what();
916 status_code = CONTROL_RESULT_ERROR;
917 } catch (...) {
918 // For things like bad_cast in boost::lexical_cast
919 std::ostringstream err;
920 err << "during update from config backend database: "
921 << "undefined configuration parsing error";
924 status_code = CONTROL_RESULT_ERROR;
925 }
926 }
927
928 // Rollback changes as the configuration parsing failed.
929 if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
930 // Revert to original configuration of runtime option definitions
931 // in the libdhcp++.
933
934 if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
935 auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
936 if (notify_libraries) {
937 return (notify_libraries);
938 }
939
941 try {
942 // Handle events registered by hooks using external IOService objects.
944 } catch (const std::exception& ex) {
945 std::ostringstream err;
946 err << "Error initializing hooks: "
947 << ex.what();
949 }
950 }
951
952 return (answer);
953 }
954
956 .arg(CfgMgr::instance().getStagingCfg()->
957 getConfigSummary(SrvConfig::CFGSEL_ALL4));
958
959 // Also calculate SHA256 hash of the config that was just set and
960 // append it to the response.
961 ConstElementPtr config = CfgMgr::instance().getStagingCfg()->toElement();
962 string hash = BaseCommandMgr::getHash(config);
963 ElementPtr hash_map = Element::createMap();
964 hash_map->set("hash", Element::create(hash));
965
966 // Everything was fine. Configuration is successful.
967 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
968 return (answer);
969}
970
971} // namespace dhcp
972} // 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
void closeCommandSocket()
Shuts down any open control sockets.
Definition: command_mgr.cc:624
static CommandMgr & instance()
CommandMgr is a singleton class.
Definition: command_mgr.cc:646
void openCommandSocket(const isc::data::ConstElementPtr &socket_info)
Opens control socket with parameters specified in socket_info.
Definition: command_mgr.cc:620
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 std::string getString(isc::data::ConstElementPtr scope, const std::string &name)
Returns a string parameter from a scope.
uint32_t getUint32(isc::data::ConstElementPtr scope, const std::string &name)
Returns a value converted to uint32_t.
static bool getBoolean(isc::data::ConstElementPtr scope, const std::string &name)
Returns a boolean parameter from a scope.
uint16_t getUint16(isc::data::ConstElementPtr scope, const std::string &name)
Returns a value converted to uint16_t.
Parse Database Parameters.
void parse(std::string &access_string, isc::data::ConstElementPtr database_config)
Parse configuration value.
static void moveReservationMode(isc::data::ElementPtr config)
Moves deprecated reservation-mode parameter to new reservations flags.
void setD2ClientConfig(D2ClientConfigPtr &new_config)
Updates the DHCP-DDNS client configuration to the given value.
Definition: cfgmgr.cc:41
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition: cfgmgr.cc:167
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
static void extract(data::ConstElementPtr value, bool &enabled, uint32_t &thread_count, uint32_t &queue_size)
Extract multi-threading parameters from a given configuration.
Parser for a list of client class definitions.
ClientClassDictionaryPtr parse(isc::data::ConstElementPtr class_def_list, uint16_t family, bool check_dependencies=true)
Parse configuration entries.
void parse(isc::data::ConstElementPtr cfg, isc::dhcp::SrvConfig &srv_cfg)
Parse compatibility flags.
Parser for the control-socket structure.
Definition: dhcp_parsers.h:211
void parse(SrvConfig &srv_cfg, isc::data::ConstElementPtr value)
"Parses" control-socket structure
Definition: dhcp_parsers.cc:77
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.
Definition: d2_client_cfg.h:57
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:253
CBControlDHCPv4Ptr getCBControl() const
Returns an object which controls access to the configuration backends.
Definition: dhcp4_srv.h:318
void discardPackets()
Discards parked packets Clears the packet parking lots of all packets.
Definition: dhcp4_srv.cc:4958
Parser for the configuration parameters pertaining to the processing of expired leases.
void parse(isc::data::ConstElementPtr expiration_config, isc::dhcp::CfgExpirationPtr expiration)
Parses parameters in the JSON map, pertaining to the processing of the expired leases.
static void printRegistered()
Prints out all registered backends.
Parser for a list of host identifiers for 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 setRuntimeOptionDefs(const OptionDefSpaceContainer &defs)
Copies option definitions created at runtime.
Definition: libdhcp++.cc:218
static void revertRuntimeOptionDefs()
Reverts uncommitted changes to runtime option definitions.
Definition: libdhcp++.cc:237
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.
Definition: dhcp_parsers.h:254
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:208
static void resetSubnetID()
Resets subnet-id counter to its initial value (1).
Definition: subnet.h:219
this class parses list of DHCP4 subnets
Definition: dhcp_parsers.h:653
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.
Definition: hooks_config.h:36
void verifyLibraries(const isc::data::Element::Position &position, bool multi_threading_enabled) const
Verifies that libraries stored in libraries_ are valid.
Definition: hooks_config.cc:20
void loadLibraries(bool multi_threading_enabled) const
Commits hooks libraries configuration.
Definition: hooks_config.cc:57
Parser for hooks library list.
Definition: hooks_parser.h:21
void parse(HooksConfig &libraries, isc::data::ConstElementPtr value)
Parses parameters value.
Definition: hooks_parser.cc:27
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:256
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
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
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:803
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.
Definition: srv_config.h:1267
const isc::log::MessageID DHCP4_PARSER_FAIL
std::vector< HostPtr > HostCollection
Collection of the Host objects.
Definition: host.h:816
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:847
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.
Definition: cfg_subnets4.h:350
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.