Kea 2.7.1
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).
312 isc::config::CommandMgr::instance().closeCommandSocket();
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.
319 isc::config::CommandMgr::instance().openCommandSocket(sock_cfg);
320 }
321 }
322}
323
330 // Revert any runtime option definitions configured so far and not committed.
332 // Let's set empty container in case a user hasn't specified any configuration
333 // for option definitions. This is equivalent to committing empty container.
335
336 // Print the list of known backends.
338
339 // Answer will hold the result.
340 ConstElementPtr answer;
341
342 // Global parameter name in case of an error.
343 string parameter_name;
344 ElementPtr mutable_cfg;
345 SrvConfigPtr srv_config;
346 try {
347 // Get the staging configuration.
348 srv_config = CfgMgr::instance().getStagingCfg();
349
350 // This is a way to convert ConstElementPtr to ElementPtr.
351 // We need a config that can be edited, because we will insert
352 // default values and will insert derived values as well.
353 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
354
355 // Set all default values if not specified by the user.
357
358 // And now derive (inherit) global parameters to subnets, if not specified.
360
361 // In principle we could have the following code structured as a series
362 // of long if else if clauses. That would give a marginal performance
363 // boost, but would make the code less readable. We had serious issues
364 // with the parser code debugability, so I decided to keep it as a
365 // series of independent ifs.
366
367 // This parser is used in several places.
368 Dhcp4ConfigParser global_parser;
369
370 // Apply global options in the staging config, e.g. ip-reservations-unique
371 global_parser.parseEarly(srv_config, mutable_cfg);
372
373 // We need definitions first
374 ConstElementPtr option_defs = mutable_cfg->get("option-def");
375 if (option_defs) {
376 parameter_name = "option-def";
377 OptionDefListParser parser(AF_INET);
378 CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
379 parser.parse(cfg_option_def, option_defs);
380 }
381
382 ConstElementPtr option_datas = mutable_cfg->get("option-data");
383 if (option_datas) {
384 parameter_name = "option-data";
385 OptionDataListParser parser(AF_INET);
386 CfgOptionPtr cfg_option = srv_config->getCfgOption();
387 parser.parse(cfg_option, option_datas);
388 }
389
390 ConstElementPtr control_socket = mutable_cfg->get("control-socket");
391 if (control_socket) {
392 parameter_name = "control-socket";
393 ControlSocketParser parser;
394 parser.parse(*srv_config, control_socket);
395 }
396
397 ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
398 if (multi_threading) {
399 parameter_name = "multi-threading";
401 parser.parse(*srv_config, multi_threading);
402 }
403
404 bool multi_threading_enabled = true;
405 uint32_t thread_count = 0;
406 uint32_t queue_size = 0;
407 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
408 multi_threading_enabled, thread_count, queue_size);
409
411 ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
412 if (queue_control) {
413 parameter_name = "dhcp-queue-control";
415 srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
416 }
417
419 ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
420 if (reservations_lookup_first) {
421 parameter_name = "reservations-lookup-first";
422 if (multi_threading_enabled) {
424 }
425 srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
426 }
427
428 ConstElementPtr hr_identifiers =
429 mutable_cfg->get("host-reservation-identifiers");
430 if (hr_identifiers) {
431 parameter_name = "host-reservation-identifiers";
433 parser.parse(hr_identifiers);
434 }
435
436 ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
437 if (sanity_checks) {
438 parameter_name = "sanity-checks";
439 SanityChecksParser parser;
440 parser.parse(*srv_config, sanity_checks);
441 }
442
443 ConstElementPtr expiration_cfg =
444 mutable_cfg->get("expired-leases-processing");
445 if (expiration_cfg) {
446 parameter_name = "expired-leases-processing";
448 parser.parse(expiration_cfg, CfgMgr::instance().getStagingCfg()->getCfgExpiration());
449 }
450
451 // The hooks-libraries configuration must be parsed after parsing
452 // multi-threading configuration so that libraries are checked
453 // for multi-threading compatibility.
454 ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
455 if (hooks_libraries) {
456 parameter_name = "hooks-libraries";
457 HooksLibrariesParser hooks_parser;
458 HooksConfig& libraries = srv_config->getHooksConfig();
459 hooks_parser.parse(libraries, hooks_libraries);
460 libraries.verifyLibraries(hooks_libraries->getPosition(),
461 multi_threading_enabled);
462 }
463
464 // D2 client configuration.
465 D2ClientConfigPtr d2_client_cfg;
466
467 // Legacy DhcpConfigParser stuff below.
468 ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
469 if (dhcp_ddns) {
470 parameter_name = "dhcp-ddns";
471 // Apply defaults
474 d2_client_cfg = parser.parse(dhcp_ddns);
475 }
476
477 ConstElementPtr client_classes = mutable_cfg->get("client-classes");
478 if (client_classes) {
479 parameter_name = "client-classes";
481 ClientClassDictionaryPtr dictionary =
482 parser.parse(client_classes, AF_INET);
483 srv_config->setClientClassDictionary(dictionary);
484 }
485
486 // Please move at the end when migration will be finished.
487 ConstElementPtr lease_database = mutable_cfg->get("lease-database");
488 if (lease_database) {
489 parameter_name = "lease-database";
490 db::DbAccessParser parser;
491 std::string access_string;
492 parser.parse(access_string, lease_database);
493 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
494 cfg_db_access->setLeaseDbAccessString(access_string);
495 }
496
497 ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
498 if (hosts_database) {
499 parameter_name = "hosts-database";
500 db::DbAccessParser parser;
501 std::string access_string;
502 parser.parse(access_string, hosts_database);
503 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
504 cfg_db_access->setHostDbAccessString(access_string);
505 }
506
507 ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
508 if (hosts_databases) {
509 parameter_name = "hosts-databases";
510 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
511 for (auto const& it : hosts_databases->listValue()) {
512 db::DbAccessParser parser;
513 std::string access_string;
514 parser.parse(access_string, it);
515 cfg_db_access->setHostDbAccessString(access_string);
516 }
517 }
518
519 // Keep relative orders of shared networks and subnets.
520 ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
521 if (shared_networks) {
522 parameter_name = "shared-networks";
529 CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4();
530 parser.parse(cfg, shared_networks);
531
532 // We also need to put the subnets it contains into normal
533 // subnets list.
534 global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg);
535 }
536
537 ConstElementPtr subnet4 = mutable_cfg->get("subnet4");
538 if (subnet4) {
539 parameter_name = "subnet4";
540 Subnets4ListConfigParser subnets_parser;
541 // parse() returns number of subnets parsed. We may log it one day.
542 subnets_parser.parse(srv_config, subnet4);
543 }
544
545 ConstElementPtr reservations = mutable_cfg->get("reservations");
546 if (reservations) {
547 parameter_name = "reservations";
548 HostCollection hosts;
549 HostReservationsListParser<HostReservationParser4> parser;
550 parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
551 for (auto const& h : hosts) {
552 srv_config->getCfgHosts()->add(h);
553 }
554 }
555
556 ConstElementPtr config_control = mutable_cfg->get("config-control");
557 if (config_control) {
558 parameter_name = "config-control";
559 ConfigControlParser parser;
560 ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
561 CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
562 }
563
564 ConstElementPtr compatibility = mutable_cfg->get("compatibility");
565 if (compatibility) {
566 CompatibilityParser parser;
567 parser.parse(compatibility, *CfgMgr::instance().getStagingCfg());
568 }
569
570 // Make parsers grouping.
571 const std::map<std::string, ConstElementPtr>& values_map =
572 mutable_cfg->mapValue();
573
574 for (auto const& config_pair : values_map) {
575 parameter_name = config_pair.first;
576
577 // These are converted to SimpleParser and are handled already above.
578 if ((config_pair.first == "option-def") ||
579 (config_pair.first == "option-data") ||
580 (config_pair.first == "control-socket") ||
581 (config_pair.first == "multi-threading") ||
582 (config_pair.first == "dhcp-queue-control") ||
583 (config_pair.first == "host-reservation-identifiers") ||
584 (config_pair.first == "interfaces-config") ||
585 (config_pair.first == "sanity-checks") ||
586 (config_pair.first == "expired-leases-processing") ||
587 (config_pair.first == "hooks-libraries") ||
588 (config_pair.first == "dhcp-ddns") ||
589 (config_pair.first == "client-classes") ||
590 (config_pair.first == "lease-database") ||
591 (config_pair.first == "hosts-database") ||
592 (config_pair.first == "hosts-databases") ||
593 (config_pair.first == "subnet4") ||
594 (config_pair.first == "shared-networks") ||
595 (config_pair.first == "reservations") ||
596 (config_pair.first == "config-control") ||
597 (config_pair.first == "loggers") ||
598 (config_pair.first == "compatibility")) {
599 continue;
600 }
601
602 // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
603 // The old method is used in JSON configuration parsers when the global
604 // parameters are derived into the subnets and shared networks and are
605 // being treated as explicitly specified. The new way used by the config
606 // backend is the dynamic inheritance whereby each subnet and shared
607 // network uses a callback function to return global parameter if it
608 // is not specified at lower level. This callback uses configured globals.
609 // We deliberately include both default and explicitly specified globals
610 // so as the callback can access the appropriate global values regardless
611 // whether they are set to a default or other value.
612 if ( (config_pair.first == "renew-timer") ||
613 (config_pair.first == "rebind-timer") ||
614 (config_pair.first == "valid-lifetime") ||
615 (config_pair.first == "min-valid-lifetime") ||
616 (config_pair.first == "max-valid-lifetime") ||
617 (config_pair.first == "decline-probation-period") ||
618 (config_pair.first == "dhcp4o6-port") ||
619 (config_pair.first == "echo-client-id") ||
620 (config_pair.first == "match-client-id") ||
621 (config_pair.first == "authoritative") ||
622 (config_pair.first == "next-server") ||
623 (config_pair.first == "server-hostname") ||
624 (config_pair.first == "boot-file-name") ||
625 (config_pair.first == "server-tag") ||
626 (config_pair.first == "reservations-global") ||
627 (config_pair.first == "reservations-in-subnet") ||
628 (config_pair.first == "reservations-out-of-pool") ||
629 (config_pair.first == "calculate-tee-times") ||
630 (config_pair.first == "t1-percent") ||
631 (config_pair.first == "t2-percent") ||
632 (config_pair.first == "cache-threshold") ||
633 (config_pair.first == "cache-max-age") ||
634 (config_pair.first == "hostname-char-set") ||
635 (config_pair.first == "hostname-char-replacement") ||
636 (config_pair.first == "ddns-send-updates") ||
637 (config_pair.first == "ddns-override-no-update") ||
638 (config_pair.first == "ddns-override-client-update") ||
639 (config_pair.first == "ddns-replace-client-name") ||
640 (config_pair.first == "ddns-generated-prefix") ||
641 (config_pair.first == "ddns-qualifying-suffix") ||
642 (config_pair.first == "ddns-update-on-renew") ||
643 (config_pair.first == "ddns-use-conflict-resolution") ||
644 (config_pair.first == "ddns-conflict-resolution-mode") ||
645 (config_pair.first == "ddns-ttl-percent") ||
646 (config_pair.first == "store-extended-info") ||
647 (config_pair.first == "statistic-default-sample-count") ||
648 (config_pair.first == "statistic-default-sample-age") ||
649 (config_pair.first == "early-global-reservations-lookup") ||
650 (config_pair.first == "ip-reservations-unique") ||
651 (config_pair.first == "reservations-lookup-first") ||
652 (config_pair.first == "parked-packet-limit") ||
653 (config_pair.first == "allocator") ||
654 (config_pair.first == "offer-lifetime") ||
655 (config_pair.first == "stash-agent-options") ) {
656 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
657 config_pair.second);
658 continue;
659 }
660
661 // Nothing to configure for the user-context.
662 if (config_pair.first == "user-context") {
663 continue;
664 }
665
666 // If we got here, no code handled this parameter, so we bail out.
668 "unsupported global configuration parameter: " << config_pair.first
669 << " (" << config_pair.second->getPosition() << ")");
670 }
671
672 // Reset parameter name.
673 parameter_name = "<post parsing>";
674
675 // Apply global options in the staging config.
676 global_parser.parse(srv_config, mutable_cfg);
677
678 // This method conducts final sanity checks and tweaks. In particular,
679 // it checks that there is no conflict between plain subnets and those
680 // defined as part of shared networks.
681 global_parser.sanityChecks(srv_config, mutable_cfg);
682
683 // Validate D2 client configuration.
684 if (!d2_client_cfg) {
685 d2_client_cfg.reset(new D2ClientConfig());
686 }
687 d2_client_cfg->validateContents();
688 srv_config->setD2ClientConfig(d2_client_cfg);
689 } catch (const isc::Exception& ex) {
691 .arg(parameter_name).arg(ex.what());
693 } catch (...) {
694 // For things like bad_cast in boost::lexical_cast
695 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
696 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
697 "processing error");
698 }
699
700 if (!answer) {
701 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
702 "Control-socket, hook-libraries, and D2 configuration "
703 "were sanity checked, but not applied.");
704 }
705
706 return (answer);
707}
708
711 bool check_only, bool extra_checks) {
712 if (!config_set) {
714 "Can't parse NULL config");
715 return (answer);
716 }
717
719 .arg(server.redactConfig(config_set)->str());
720
721 auto answer = processDhcp4Config(config_set);
722
723 int status_code = CONTROL_RESULT_SUCCESS;
724 isc::config::parseAnswer(status_code, answer);
725
726 SrvConfigPtr srv_config;
727
728 if (status_code == CONTROL_RESULT_SUCCESS) {
729 if (check_only) {
730 if (extra_checks) {
731 // Re-open lease and host database with new parameters.
732 try {
733 // Get the staging configuration.
734 srv_config = CfgMgr::instance().getStagingCfg();
735
736 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
737 string params = "universe=4 persist=false";
738 cfg_db->setAppendedParameters(params);
739 cfg_db->createManagers();
740 } catch (const std::exception& ex) {
742 status_code = CONTROL_RESULT_ERROR;
743 }
744
745 if (status_code == CONTROL_RESULT_SUCCESS) {
746 std::ostringstream err;
747 // Configure DHCP packet queueing
748 try {
750 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
751 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
753 .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
754 }
755
756 } catch (const std::exception& ex) {
757 err << "Error setting packet queue controls after server reconfiguration: "
758 << ex.what();
760 status_code = CONTROL_RESULT_ERROR;
761 }
762 }
763 }
764 } else {
765 // disable multi-threading (it will be applied by new configuration)
766 // this must be done in order to properly handle MT to ST transition
767 // when 'multi-threading' structure is missing from new config and
768 // to properly drop any task items stored in the thread pool which
769 // might reference some handles to loaded hooks, preventing them
770 // from being unloaded.
771 MultiThreadingMgr::instance().apply(false, 0, 0);
772
773 // Close DHCP sockets and remove any existing timers.
774 IfaceMgr::instance().closeSockets();
775 TimerMgr::instance()->unregisterTimers();
776 server.discardPackets();
777 server.getCBControl()->reset();
778 }
779
780 if (status_code == CONTROL_RESULT_SUCCESS) {
781 string parameter_name;
782 ElementPtr mutable_cfg;
783 try {
784 // Get the staging configuration.
785 srv_config = CfgMgr::instance().getStagingCfg();
786
787 // This is a way to convert ConstElementPtr to ElementPtr.
788 // We need a config that can be edited, because we will insert
789 // default values and will insert derived values as well.
790 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
791
792 ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
793 if (ifaces_config) {
794 parameter_name = "interfaces-config";
795 IfacesConfigParser parser(AF_INET, check_only);
796 CfgIfacePtr cfg_iface = srv_config->getCfgIface();
797 cfg_iface->reset();
798 parser.parse(cfg_iface, ifaces_config);
799 }
800 } catch (const isc::Exception& ex) {
802 .arg(parameter_name).arg(ex.what());
804 status_code = CONTROL_RESULT_ERROR;
805 } catch (...) {
806 // For things like bad_cast in boost::lexical_cast
807 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
808 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
809 " processing error");
810 status_code = CONTROL_RESULT_ERROR;
811 }
812 }
813 }
814
815 // So far so good, there was no parsing error so let's commit the
816 // configuration. This will add created subnets and option values into
817 // the server's configuration.
818 // This operation should be exception safe but let's make sure.
819 if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
820 try {
821
822 // Setup the command channel.
824 } catch (const isc::Exception& ex) {
827 status_code = CONTROL_RESULT_ERROR;
828 } catch (...) {
829 // For things like bad_cast in boost::lexical_cast
831 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
832 " parsing error");
833 status_code = CONTROL_RESULT_ERROR;
834 }
835 }
836
837 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
838 try {
839 // No need to commit interface names as this is handled by the
840 // CfgMgr::commit() function.
841
842 // Apply the staged D2ClientConfig, used to be done by parser commit
844 cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
845 CfgMgr::instance().setD2ClientConfig(cfg);
846 } catch (const isc::Exception& ex) {
849 status_code = CONTROL_RESULT_ERROR;
850 } catch (...) {
851 // For things like bad_cast in boost::lexical_cast
853 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
854 " parsing error");
855 status_code = CONTROL_RESULT_ERROR;
856 }
857 }
858
859 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
860 try {
861 // This occurs last as if it succeeds, there is no easy way to
862 // revert it. As a result, the failure to commit a subsequent
863 // change causes problems when trying to roll back.
865 static_cast<void>(HooksManager::unloadLibraries());
866 IOServiceMgr::instance().clearIOServices();
867 const HooksConfig& libraries =
868 CfgMgr::instance().getStagingCfg()->getHooksConfig();
869 bool multi_threading_enabled = true;
870 uint32_t thread_count = 0;
871 uint32_t queue_size = 0;
872 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
873 multi_threading_enabled, thread_count, queue_size);
874 libraries.loadLibraries(multi_threading_enabled);
875 } catch (const isc::Exception& ex) {
878 status_code = CONTROL_RESULT_ERROR;
879 } catch (...) {
880 // For things like bad_cast in boost::lexical_cast
882 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
883 " parsing error");
884 status_code = CONTROL_RESULT_ERROR;
885 }
886 }
887
888 // Moved from the commit block to add the config backend indication.
889 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
890 try {
891 // If there are config backends, fetch and merge into staging config
892 server.getCBControl()->databaseConfigFetch(srv_config,
894 } catch (const isc::Exception& ex) {
895 std::ostringstream err;
896 err << "during update from config backend database: " << ex.what();
899 status_code = CONTROL_RESULT_ERROR;
900 } catch (...) {
901 // For things like bad_cast in boost::lexical_cast
902 std::ostringstream err;
903 err << "during update from config backend database: "
904 << "undefined configuration parsing error";
907 status_code = CONTROL_RESULT_ERROR;
908 }
909 }
910
911 // Rollback changes as the configuration parsing failed.
912 if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
913 // Revert to original configuration of runtime option definitions
914 // in the libdhcp++.
916
917 if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
918 auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
919 if (notify_libraries) {
920 return (notify_libraries);
921 }
922
924 try {
925 // Handle events registered by hooks using external IOService objects.
926 IOServiceMgr::instance().pollIOServices();
927 } catch (const std::exception& ex) {
928 std::ostringstream err;
929 err << "Error initializing hooks: "
930 << ex.what();
932 }
933 }
934
935 return (answer);
936 }
937
939 .arg(CfgMgr::instance().getStagingCfg()->
940 getConfigSummary(SrvConfig::CFGSEL_ALL4));
941
942 // Also calculate SHA256 hash of the config that was just set and
943 // append it to the response.
944 ConstElementPtr config = CfgMgr::instance().getStagingCfg()->toElement();
945 string hash = BaseCommandMgr::getHash(config);
946 ElementPtr hash_map = Element::createMap();
947 hash_map->set("hash", Element::create(hash));
948
949 // Everything was fine. Configuration is successful.
950 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
951 return (answer);
952}
953
954} // namespace dhcp
955} // 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 CommandMgr & instance()
CommandMgr 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
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-socket structure.
void parse(SrvConfig &srv_cfg, isc::data::ConstElementPtr value)
"Parses" control-socket 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 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.
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 setRuntimeOptionDefs(const OptionDefSpaceContainer &defs)
Copies option definitions created at runtime.
Definition libdhcp++.cc:220
static void revertRuntimeOptionDefs()
Reverts uncommitted changes to runtime option definitions.
Definition libdhcp++.cc:239
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:208
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)
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
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: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: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.