Kea 2.7.5
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 const SharedNetwork4Collection* networks = cfg->getCfgSharedNetworks4()->getAll();
200 if (networks) {
201 sharedNetworksSanityChecks(*networks, global->get("shared-networks"));
202 }
203 }
204
211 void
212 sharedNetworksSanityChecks(const SharedNetwork4Collection& networks,
213 ConstElementPtr json) {
214
216 if (!json) {
217 // No json? That means that the shared-networks was never specified
218 // in the config.
219 return;
220 }
221
222 // Used for names uniqueness checks.
223 std::set<string> names;
224
225 // Let's go through all the networks one by one
226 for (auto const& net : networks) {
227 string txt;
228
229 // Let's check if all subnets have either the same interface
230 // or don't have the interface specified at all.
231 bool authoritative = net->getAuthoritative();
232 string iface = net->getIface();
233
234 const Subnet4SimpleCollection* subnets = net->getAllSubnets();
235 if (subnets) {
236 // For each subnet, add it to a list of regular subnets.
237 for (auto const& subnet : *subnets) {
238 if (subnet->getAuthoritative() != authoritative) {
239 isc_throw(DhcpConfigError, "Subnet " << boolalpha
240 << subnet->toText()
241 << " has different authoritative setting "
242 << subnet->getAuthoritative()
243 << " than the shared-network itself: "
244 << authoritative);
245 }
246
247 if (iface.empty()) {
248 iface = subnet->getIface();
249 continue;
250 }
251
252 if (subnet->getIface().empty()) {
253 continue;
254 }
255
256 if (subnet->getIface() != iface) {
257 isc_throw(DhcpConfigError, "Subnet " << subnet->toText()
258 << " has specified interface " << subnet->getIface()
259 << ", but earlier subnet in the same shared-network"
260 << " or the shared-network itself used " << iface);
261 }
262
263 // Let's collect the subnets in case we later find out the
264 // subnet doesn't have a mandatory name.
265 txt += subnet->toText() + " ";
266 }
267 }
268
269 // Next, let's check name of the shared network.
270 if (net->getName().empty()) {
271 isc_throw(DhcpConfigError, "Shared-network with subnets "
272 << txt << " is missing mandatory 'name' parameter");
273 }
274
275 // Is it unique?
276 if (names.find(net->getName()) != names.end()) {
277 isc_throw(DhcpConfigError, "A shared-network with "
278 "name " << net->getName() << " defined twice.");
279 }
280 names.insert(net->getName());
281
282 }
283 }
284};
285
286} // anonymous namespace
287
288namespace isc {
289namespace dhcp {
290
299 // Get new UNIX socket configuration.
300 ConstElementPtr sock_cfg =
301 CfgMgr::instance().getStagingCfg()->getUnixControlSocketInfo();
302
303 // Get current UNIX socket configuration.
304 ConstElementPtr current_sock_cfg =
305 CfgMgr::instance().getCurrentCfg()->getUnixControlSocketInfo();
306
307 // Determine if the socket configuration has changed. It has if
308 // both old and new configuration is specified but respective
309 // data elements aren't equal.
310 bool sock_changed = (sock_cfg && current_sock_cfg &&
311 !sock_cfg->equals(*current_sock_cfg));
312
313 // If the previous or new socket configuration doesn't exist or
314 // the new configuration differs from the old configuration we
315 // close the existing socket and open a new socket as appropriate.
316 // Note that closing an existing socket means the client will not
317 // receive the configuration result.
318 if (!sock_cfg || !current_sock_cfg || sock_changed) {
319 // Close the existing socket (if any).
320 UnixCommandMgr::instance().closeCommandSocket();
321
322 if (sock_cfg) {
323 // This will create a control socket and install the external
324 // socket in IfaceMgr. That socket will be monitored when
325 // Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
326 // callback in CommandMgr will be called, if necessary.
327 UnixCommandMgr::instance().openCommandSocket(sock_cfg);
328 }
329 }
330
331 // HTTP control socket is simpler: just (re)configure it.
332
333 // Get new config.
334 HttpCommandConfigPtr http_config =
335 CfgMgr::instance().getStagingCfg()->getHttpControlSocketInfo();
336 HttpCommandMgr::instance().configure(http_config);
337}
338
345 // Revert any runtime option definitions configured so far and not committed.
347 // Let's set empty container in case a user hasn't specified any configuration
348 // for option definitions. This is equivalent to committing empty container.
350
351 // Answer will hold the result.
352 ConstElementPtr answer;
353
354 // Global parameter name in case of an error.
355 string parameter_name;
356 ElementPtr mutable_cfg;
357 SrvConfigPtr srv_config;
358 try {
359 // Get the staging configuration.
360 srv_config = CfgMgr::instance().getStagingCfg();
361
362 // This is a way to convert ConstElementPtr to ElementPtr.
363 // We need a config that can be edited, because we will insert
364 // default values and will insert derived values as well.
365 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
366
367 // Set all default values if not specified by the user.
369
370 // And now derive (inherit) global parameters to subnets, if not specified.
372
373 // In principle we could have the following code structured as a series
374 // of long if else if clauses. That would give a marginal performance
375 // boost, but would make the code less readable. We had serious issues
376 // with the parser code debugability, so I decided to keep it as a
377 // series of independent ifs.
378
379 // This parser is used in several places.
380 Dhcp4ConfigParser global_parser;
381
382 // Apply global options in the staging config, e.g. ip-reservations-unique
383 global_parser.parseEarly(srv_config, mutable_cfg);
384
385 // We need definitions first
386 ConstElementPtr option_defs = mutable_cfg->get("option-def");
387 if (option_defs) {
388 parameter_name = "option-def";
389 OptionDefListParser parser(AF_INET);
390 CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
391 parser.parse(cfg_option_def, option_defs);
392 }
393
394 ConstElementPtr option_datas = mutable_cfg->get("option-data");
395 if (option_datas) {
396 parameter_name = "option-data";
397 OptionDataListParser parser(AF_INET);
398 CfgOptionPtr cfg_option = srv_config->getCfgOption();
399 parser.parse(cfg_option, option_datas);
400 }
401
402 ConstElementPtr control_socket = mutable_cfg->get("control-socket");
403 if (control_socket) {
404 mutable_cfg->remove("control-socket");
406 l->add(UserContext::toElement(control_socket));
407 mutable_cfg->set("control-sockets", l);
408 }
409
410 ConstElementPtr control_sockets = mutable_cfg->get("control-sockets");
411 if (control_sockets) {
412 parameter_name = "control-sockets";
414 parser.parse(*srv_config, control_sockets);
415 }
416
417 ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
418 if (multi_threading) {
419 parameter_name = "multi-threading";
421 parser.parse(*srv_config, multi_threading);
422 }
423
424 bool multi_threading_enabled = true;
425 uint32_t thread_count = 0;
426 uint32_t queue_size = 0;
427 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
428 multi_threading_enabled, thread_count, queue_size);
429
431 ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
432 if (queue_control) {
433 parameter_name = "dhcp-queue-control";
435 srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
436 }
437
439 ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
440 if (reservations_lookup_first) {
441 parameter_name = "reservations-lookup-first";
442 if (multi_threading_enabled) {
444 }
445 srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
446 }
447
448 ConstElementPtr hr_identifiers =
449 mutable_cfg->get("host-reservation-identifiers");
450 if (hr_identifiers) {
451 parameter_name = "host-reservation-identifiers";
453 parser.parse(hr_identifiers);
454 }
455
456 ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
457 if (sanity_checks) {
458 parameter_name = "sanity-checks";
459 SanityChecksParser parser;
460 parser.parse(*srv_config, sanity_checks);
461 }
462
463 ConstElementPtr expiration_cfg =
464 mutable_cfg->get("expired-leases-processing");
465 if (expiration_cfg) {
466 parameter_name = "expired-leases-processing";
468 parser.parse(expiration_cfg, CfgMgr::instance().getStagingCfg()->getCfgExpiration());
469 }
470
471 // The hooks-libraries configuration must be parsed after parsing
472 // multi-threading configuration so that libraries are checked
473 // for multi-threading compatibility.
474 ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
475 if (hooks_libraries) {
476 parameter_name = "hooks-libraries";
477 HooksLibrariesParser hooks_parser;
478 HooksConfig& libraries = srv_config->getHooksConfig();
479 hooks_parser.parse(libraries, hooks_libraries);
480 libraries.verifyLibraries(hooks_libraries->getPosition(),
481 multi_threading_enabled);
482 }
483
484 // D2 client configuration.
485 D2ClientConfigPtr d2_client_cfg;
486
487 // Legacy DhcpConfigParser stuff below.
488 ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
489 if (dhcp_ddns) {
490 parameter_name = "dhcp-ddns";
491 // Apply defaults
494 d2_client_cfg = parser.parse(dhcp_ddns);
495 }
496
497 ConstElementPtr client_classes = mutable_cfg->get("client-classes");
498 if (client_classes) {
499 parameter_name = "client-classes";
501 ClientClassDictionaryPtr dictionary =
502 parser.parse(client_classes, AF_INET);
503 srv_config->setClientClassDictionary(dictionary);
504 }
505
506 // Please move at the end when migration will be finished.
507 ConstElementPtr lease_database = mutable_cfg->get("lease-database");
508 if (lease_database) {
509 parameter_name = "lease-database";
510 db::DbAccessParser parser;
511 std::string access_string;
512 parser.parse(access_string, lease_database);
513 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
514 cfg_db_access->setLeaseDbAccessString(access_string);
515 }
516
517 ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
518 if (hosts_database) {
519 parameter_name = "hosts-database";
520 db::DbAccessParser parser;
521 std::string access_string;
522 parser.parse(access_string, hosts_database);
523 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
524 cfg_db_access->setHostDbAccessString(access_string);
525 }
526
527 ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
528 if (hosts_databases) {
529 parameter_name = "hosts-databases";
530 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
531 for (auto const& it : hosts_databases->listValue()) {
532 db::DbAccessParser parser;
533 std::string access_string;
534 parser.parse(access_string, it);
535 cfg_db_access->setHostDbAccessString(access_string);
536 }
537 }
538
539 // Keep relative orders of shared networks and subnets.
540 ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
541 if (shared_networks) {
542 parameter_name = "shared-networks";
549 CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4();
550 parser.parse(cfg, shared_networks);
551
552 // We also need to put the subnets it contains into normal
553 // subnets list.
554 global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg);
555 }
556
557 ConstElementPtr subnet4 = mutable_cfg->get("subnet4");
558 if (subnet4) {
559 parameter_name = "subnet4";
560 Subnets4ListConfigParser subnets_parser;
561 // parse() returns number of subnets parsed. We may log it one day.
562 subnets_parser.parse(srv_config, subnet4);
563 }
564
565 ConstElementPtr reservations = mutable_cfg->get("reservations");
566 if (reservations) {
567 parameter_name = "reservations";
568 HostCollection hosts;
569 HostReservationsListParser<HostReservationParser4> parser;
570 parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
571 for (auto const& h : hosts) {
572 srv_config->getCfgHosts()->add(h);
573 }
574 }
575
576 ConstElementPtr config_control = mutable_cfg->get("config-control");
577 if (config_control) {
578 parameter_name = "config-control";
579 ConfigControlParser parser;
580 ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
581 CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
582 }
583
584 ConstElementPtr compatibility = mutable_cfg->get("compatibility");
585 if (compatibility) {
586 CompatibilityParser parser;
587 parser.parse(compatibility, *CfgMgr::instance().getStagingCfg());
588 }
589
590 // Make parsers grouping.
591 const std::map<std::string, ConstElementPtr>& values_map =
592 mutable_cfg->mapValue();
593
594 for (auto const& config_pair : values_map) {
595 parameter_name = config_pair.first;
596
597 // These are converted to SimpleParser and are handled already above.
598 if ((config_pair.first == "option-def") ||
599 (config_pair.first == "option-data") ||
600 (config_pair.first == "control-socket") ||
601 (config_pair.first == "control-sockets") ||
602 (config_pair.first == "multi-threading") ||
603 (config_pair.first == "dhcp-queue-control") ||
604 (config_pair.first == "host-reservation-identifiers") ||
605 (config_pair.first == "interfaces-config") ||
606 (config_pair.first == "sanity-checks") ||
607 (config_pair.first == "expired-leases-processing") ||
608 (config_pair.first == "hooks-libraries") ||
609 (config_pair.first == "dhcp-ddns") ||
610 (config_pair.first == "client-classes") ||
611 (config_pair.first == "lease-database") ||
612 (config_pair.first == "hosts-database") ||
613 (config_pair.first == "hosts-databases") ||
614 (config_pair.first == "subnet4") ||
615 (config_pair.first == "shared-networks") ||
616 (config_pair.first == "reservations") ||
617 (config_pair.first == "config-control") ||
618 (config_pair.first == "loggers") ||
619 (config_pair.first == "compatibility")) {
620 continue;
621 }
622
623 // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
624 // The old method is used in JSON configuration parsers when the global
625 // parameters are derived into the subnets and shared networks and are
626 // being treated as explicitly specified. The new way used by the config
627 // backend is the dynamic inheritance whereby each subnet and shared
628 // network uses a callback function to return global parameter if it
629 // is not specified at lower level. This callback uses configured globals.
630 // We deliberately include both default and explicitly specified globals
631 // so as the callback can access the appropriate global values regardless
632 // whether they are set to a default or other value.
633 if ( (config_pair.first == "renew-timer") ||
634 (config_pair.first == "rebind-timer") ||
635 (config_pair.first == "valid-lifetime") ||
636 (config_pair.first == "min-valid-lifetime") ||
637 (config_pair.first == "max-valid-lifetime") ||
638 (config_pair.first == "decline-probation-period") ||
639 (config_pair.first == "dhcp4o6-port") ||
640 (config_pair.first == "echo-client-id") ||
641 (config_pair.first == "match-client-id") ||
642 (config_pair.first == "authoritative") ||
643 (config_pair.first == "next-server") ||
644 (config_pair.first == "server-hostname") ||
645 (config_pair.first == "boot-file-name") ||
646 (config_pair.first == "server-tag") ||
647 (config_pair.first == "reservations-global") ||
648 (config_pair.first == "reservations-in-subnet") ||
649 (config_pair.first == "reservations-out-of-pool") ||
650 (config_pair.first == "calculate-tee-times") ||
651 (config_pair.first == "t1-percent") ||
652 (config_pair.first == "t2-percent") ||
653 (config_pair.first == "cache-threshold") ||
654 (config_pair.first == "cache-max-age") ||
655 (config_pair.first == "hostname-char-set") ||
656 (config_pair.first == "hostname-char-replacement") ||
657 (config_pair.first == "ddns-send-updates") ||
658 (config_pair.first == "ddns-override-no-update") ||
659 (config_pair.first == "ddns-override-client-update") ||
660 (config_pair.first == "ddns-replace-client-name") ||
661 (config_pair.first == "ddns-generated-prefix") ||
662 (config_pair.first == "ddns-qualifying-suffix") ||
663 (config_pair.first == "ddns-update-on-renew") ||
664 (config_pair.first == "ddns-use-conflict-resolution") ||
665 (config_pair.first == "ddns-conflict-resolution-mode") ||
666 (config_pair.first == "ddns-ttl-percent") ||
667 (config_pair.first == "store-extended-info") ||
668 (config_pair.first == "statistic-default-sample-count") ||
669 (config_pair.first == "statistic-default-sample-age") ||
670 (config_pair.first == "early-global-reservations-lookup") ||
671 (config_pair.first == "ip-reservations-unique") ||
672 (config_pair.first == "reservations-lookup-first") ||
673 (config_pair.first == "parked-packet-limit") ||
674 (config_pair.first == "allocator") ||
675 (config_pair.first == "offer-lifetime") ||
676 (config_pair.first == "stash-agent-options") ) {
677 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
678 config_pair.second);
679 continue;
680 }
681
682 // Nothing to configure for the user-context.
683 if (config_pair.first == "user-context") {
684 continue;
685 }
686
687 // If we got here, no code handled this parameter, so we bail out.
689 "unsupported global configuration parameter: " << config_pair.first
690 << " (" << config_pair.second->getPosition() << ")");
691 }
692
693 // Reset parameter name.
694 parameter_name = "<post parsing>";
695
696 // Apply global options in the staging config.
697 global_parser.parse(srv_config, mutable_cfg);
698
699 // This method conducts final sanity checks and tweaks. In particular,
700 // it checks that there is no conflict between plain subnets and those
701 // defined as part of shared networks.
702 global_parser.sanityChecks(srv_config, mutable_cfg);
703
704 // Validate D2 client configuration.
705 if (!d2_client_cfg) {
706 d2_client_cfg.reset(new D2ClientConfig());
707 }
708 d2_client_cfg->validateContents();
709 srv_config->setD2ClientConfig(d2_client_cfg);
710 } catch (const isc::Exception& ex) {
712 .arg(parameter_name).arg(ex.what());
714 } catch (...) {
715 // For things like bad_cast in boost::lexical_cast
716 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
717 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
718 "processing error");
719 }
720
721 if (!answer) {
722 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
723 "Control-socket, hook-libraries, and D2 configuration "
724 "were sanity checked, but not applied.");
725 }
726
727 return (answer);
728}
729
732 bool check_only, bool extra_checks) {
733 if (!config_set) {
735 "Can't parse NULL config");
736 return (answer);
737 }
738
740 .arg(server.redactConfig(config_set)->str());
741
742 if (check_only) {
743 MultiThreadingMgr::instance().setTestMode(true);
744 }
745
746 auto answer = processDhcp4Config(config_set);
747
748 int status_code = CONTROL_RESULT_SUCCESS;
749 isc::config::parseAnswer(status_code, answer);
750
751 SrvConfigPtr srv_config;
752
753 if (status_code == CONTROL_RESULT_SUCCESS) {
754 if (check_only) {
755 if (extra_checks) {
756 std::ostringstream err;
757 // Configure DHCP packet queueing
758 try {
760 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
761 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
763 .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
764 }
765
766 } catch (const std::exception& ex) {
767 err << "Error setting packet queue controls after server reconfiguration: "
768 << ex.what();
770 status_code = CONTROL_RESULT_ERROR;
771 }
772 }
773 } else {
774 // disable multi-threading (it will be applied by new configuration)
775 // this must be done in order to properly handle MT to ST transition
776 // when 'multi-threading' structure is missing from new config and
777 // to properly drop any task items stored in the thread pool which
778 // might reference some handles to loaded hooks, preventing them
779 // from being unloaded.
780 MultiThreadingMgr::instance().apply(false, 0, 0);
781
782 // Close DHCP sockets and remove any existing timers.
783 IfaceMgr::instance().closeSockets();
784 TimerMgr::instance()->unregisterTimers();
785 server.discardPackets();
786 server.getCBControl()->reset();
787 }
788
789 if (status_code == CONTROL_RESULT_SUCCESS) {
790 string parameter_name;
791 ElementPtr mutable_cfg;
792 try {
793 // Get the staging configuration.
794 srv_config = CfgMgr::instance().getStagingCfg();
795
796 // This is a way to convert ConstElementPtr to ElementPtr.
797 // We need a config that can be edited, because we will insert
798 // default values and will insert derived values as well.
799 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
800
801 ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
802 if (ifaces_config) {
803 parameter_name = "interfaces-config";
804 IfacesConfigParser parser(AF_INET, check_only);
805 CfgIfacePtr cfg_iface = srv_config->getCfgIface();
806 cfg_iface->reset();
807 parser.parse(cfg_iface, ifaces_config);
808 }
809 } catch (const isc::Exception& ex) {
811 .arg(parameter_name).arg(ex.what());
813 status_code = CONTROL_RESULT_ERROR;
814 } catch (...) {
815 // For things like bad_cast in boost::lexical_cast
816 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
817 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
818 " processing error");
819 status_code = CONTROL_RESULT_ERROR;
820 }
821 }
822 }
823
824 // So far so good, there was no parsing error so let's commit the
825 // configuration. This will add created subnets and option values into
826 // the server's configuration.
827 // This operation should be exception safe but let's make sure.
828 if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
829 try {
830
831 // Setup the command channel.
833 } catch (const isc::Exception& ex) {
836 status_code = CONTROL_RESULT_ERROR;
837 } catch (...) {
838 // For things like bad_cast in boost::lexical_cast
840 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
841 " parsing error");
842 status_code = CONTROL_RESULT_ERROR;
843 }
844 }
845
846 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
847 try {
848 // No need to commit interface names as this is handled by the
849 // CfgMgr::commit() function.
850
851 // Apply the staged D2ClientConfig, used to be done by parser commit
853 cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
854 CfgMgr::instance().setD2ClientConfig(cfg);
855 } catch (const isc::Exception& ex) {
858 status_code = CONTROL_RESULT_ERROR;
859 } catch (...) {
860 // For things like bad_cast in boost::lexical_cast
862 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
863 " parsing error");
864 status_code = CONTROL_RESULT_ERROR;
865 }
866 }
867
868 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
869 try {
870 // This occurs last as if it succeeds, there is no easy way to
871 // revert it. As a result, the failure to commit a subsequent
872 // change causes problems when trying to roll back.
874 static_cast<void>(HooksManager::unloadLibraries());
875 IOServiceMgr::instance().clearIOServices();
876 const HooksConfig& libraries =
877 CfgMgr::instance().getStagingCfg()->getHooksConfig();
878 bool multi_threading_enabled = true;
879 uint32_t thread_count = 0;
880 uint32_t queue_size = 0;
881 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
882 multi_threading_enabled, thread_count, queue_size);
883 libraries.loadLibraries(multi_threading_enabled);
884 } catch (const isc::Exception& ex) {
887 status_code = CONTROL_RESULT_ERROR;
888 } catch (...) {
889 // For things like bad_cast in boost::lexical_cast
891 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
892 " parsing error");
893 status_code = CONTROL_RESULT_ERROR;
894 }
895
896 if (extra_checks && status_code == CONTROL_RESULT_SUCCESS) {
897 // Re-open lease and host database with new parameters.
898 try {
899 // Get the staging configuration.
900 srv_config = CfgMgr::instance().getStagingCfg();
901
902 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
903 string params = "universe=4 persist=false";
904 cfg_db->setAppendedParameters(params);
905 cfg_db->createManagers();
906 } catch (const std::exception& ex) {
908 status_code = CONTROL_RESULT_ERROR;
909 }
910 }
911 }
912
913 // Log the list of known backends.
915
916 // Log the list of known backends.
918
919 // Moved from the commit block to add the config backend indication.
920 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
921 try {
922 // If there are config backends, fetch and merge into staging config
923 server.getCBControl()->databaseConfigFetch(srv_config,
925 } catch (const isc::Exception& ex) {
926 std::ostringstream err;
927 err << "during update from config backend database: " << ex.what();
930 status_code = CONTROL_RESULT_ERROR;
931 } catch (...) {
932 // For things like bad_cast in boost::lexical_cast
933 std::ostringstream err;
934 err << "during update from config backend database: "
935 << "undefined configuration parsing error";
938 status_code = CONTROL_RESULT_ERROR;
939 }
940 }
941
942 // Rollback changes as the configuration parsing failed.
943 if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
944 // Revert to original configuration of runtime option definitions
945 // in the libdhcp++.
947
948 if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
949 auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
950 if (notify_libraries) {
951 return (notify_libraries);
952 }
953
955 try {
956 // Handle events registered by hooks using external IOService objects.
957 IOServiceMgr::instance().pollIOServices();
958 } catch (const std::exception& ex) {
959 std::ostringstream err;
960 err << "Error initializing hooks: "
961 << ex.what();
963 }
964 }
965
966 return (answer);
967 }
968
970 .arg(CfgMgr::instance().getStagingCfg()->
971 getConfigSummary(SrvConfig::CFGSEL_ALL4));
972
973 // Also calculate SHA256 hash of the config that was just set and
974 // append it to the response.
975 ConstElementPtr config = CfgMgr::instance().getStagingCfg()->toElement();
976 string hash = BaseCommandMgr::getHash(config);
977 ElementPtr hash_map = Element::createMap();
978 hash_map->set("hash", Element::create(hash));
979
980 // Everything was fine. Configuration is successful.
981 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
982 return (answer);
983}
984
985} // namespace dhcp
986} // 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.
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.
static UnixCommandMgr & instance()
UnixCommandMgr is a singleton class.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
Parse Database Parameters.
void parse(std::string &access_string, isc::data::ConstElementPtr database_config)
Parse configuration value.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:25
static void extract(data::ConstElementPtr value, bool &enabled, uint32_t &thread_count, uint32_t &queue_size)
Extract multi-threading parameters from a given configuration.
Parser for a list of client class definitions.
ClientClassDictionaryPtr parse(isc::data::ConstElementPtr class_def_list, uint16_t family, bool check_dependencies=true)
Parse configuration entries.
void parse(isc::data::ConstElementPtr cfg, isc::dhcp::SrvConfig &srv_cfg)
Parse compatibility flags.
Parser for the control-sockets structure.
void parse(SrvConfig &srv_cfg, isc::data::ConstElementPtr value)
"Parses" control-sockets structure
static isc::data::ConstElementPtr finishConfigHookLibraries(isc::data::ConstElementPtr config)
Configuration checker for hook libraries.
Parser for D2ClientConfig.
D2ClientConfigPtr parse(isc::data::ConstElementPtr d2_client_cfg)
Parses a given dhcp-ddns element into D2ClientConfig.
static size_t setAllDefaults(isc::data::ConstElementPtr d2_config)
Sets all defaults for D2 client configuration.
Acts as a storage vault for D2 client configuration.
Parser for the configuration of DHCP packet queue controls.
data::ElementPtr parse(const isc::data::ConstElementPtr &control_elem, bool multi_threading_enabled)
Parses content of the "dhcp-queue-control".
To be removed. Please use ConfigError instead.
DHCPv4 server service.
Definition dhcp4_srv.h:268
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.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
Parser for the configuration of interfaces.
void parse(const CfgIfacePtr &config, const isc::data::ConstElementPtr &values)
Parses content of the "interfaces-config".
static void logRegistered()
Logs out all registered backends.
static void setRuntimeOptionDefs(const OptionDefSpaceContainer &defs)
Copies option definitions created at runtime.
Definition libdhcp++.cc: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
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:209
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.
Parser for hooks library list.
static bool unloadLibraries()
Unload libraries.
static void prepareUnloadLibraries()
Prepare the unloading of libraries.
Implements parser for config control information, "config-control".
ConfigControlInfoPtr parse(const data::ConstElementPtr &config_control)
Parses a configuration control Element.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
Parsers for client class definitions.
This file contains several functions and constants that are used for handling commands and responses ...
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)
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
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 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.