Kea 2.7.3
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>
18#include <dhcp4/dhcp4_log.h>
19#include <dhcp4/dhcp4_srv.h>
21#include <dhcp/libdhcp++.h>
25#include <dhcpsrv/cfg_option.h>
26#include <dhcpsrv/cfgmgr.h>
28#include <dhcpsrv/db_type.h>
43#include <dhcpsrv/timer_mgr.h>
44#include <hooks/hooks_manager.h>
45#include <hooks/hooks_parser.h>
47#include <util/encode/encode.h>
49
50#ifdef HAVE_MYSQL
51#include <hooks/dhcp/mysql_lb/mysql_lease_mgr.h>
52#include <hooks/dhcp/mysql_hb/mysql_host_data_source.h>
53#endif
54
55#ifdef HAVE_PGSQL
56#include <hooks/dhcp/pgsql_lb/pgsql_lease_mgr.h>
57#include <hooks/dhcp/pgsql_hb/pgsql_host_data_source.h>
58#endif
59
60#include <boost/algorithm/string.hpp>
61#include <boost/lexical_cast.hpp>
62
63#include <iomanip>
64#include <iostream>
65#include <limits>
66#include <map>
67#include <netinet/in.h>
68#include <vector>
69
70using namespace isc::asiolink;
71using namespace isc::config;
72using namespace isc::data;
73using namespace isc::db;
74using namespace isc::dhcp;
75using namespace isc::hooks;
76using namespace isc::process;
77using namespace isc::util;
78using namespace isc;
79using namespace std;
80
81//
82// Register database backends
83//
84namespace {
85
86// Code will be moved to appropriate hook library.
87#ifdef HAVE_MYSQL
88// Database backend will be registered at object initialization
89MySqlLeaseMgrInit mysql_init_lease;
90MySqlHostDataSourceInit mysql_init_host;
91#endif
92
93// Code will be moved to appropriate hook library.
94#ifdef HAVE_PGSQL
95// Database backend will be registered at object initialization
96PgSqlLeaseMgrInit pgsql_init_lease;
97PgSqlHostDataSourceInit pgsql_init_host;
98#endif
99
108class Dhcp4ConfigParser : public isc::data::SimpleParser {
109public:
110
125 void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
126
127 // Set whether v4 server is supposed to echo back client-id
128 // (yes = RFC6842 compatible, no = backward compatibility)
129 bool echo_client_id = getBoolean(global, "echo-client-id");
130 cfg->setEchoClientId(echo_client_id);
131
132 // Set the probation period for decline handling.
133 uint32_t probation_period =
134 getUint32(global, "decline-probation-period");
135 cfg->setDeclinePeriod(probation_period);
136
137 // Set the DHCPv4-over-DHCPv6 interserver port.
138 uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
139 cfg->setDhcp4o6Port(dhcp4o6_port);
140
141 // Set the global user context.
142 ConstElementPtr user_context = global->get("user-context");
143 if (user_context) {
144 cfg->setContext(user_context);
145 }
146
147 // Set the server's logical name
148 std::string server_tag = getString(global, "server-tag");
149 cfg->setServerTag(server_tag);
150 }
151
163 void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
164 // Set ip-reservations-unique flag.
165 bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
166 cfg->setIPReservationsUnique(ip_reservations_unique);
167 }
168
175 void
176 copySubnets4(const CfgSubnets4Ptr& dest, const CfgSharedNetworks4Ptr& from) {
177
178 if (!dest || !from) {
179 isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null");
180 }
181
182 const SharedNetwork4Collection* networks = from->getAll();
183 if (!networks) {
184 // Nothing to copy. Technically, it should return a pointer to empty
185 // container, but let's handle null pointer as well.
186 return;
187 }
188
189 // Let's go through all the networks one by one
190 for (auto const& net : *networks) {
191
192 // For each network go through all the subnets in it.
193 const Subnet4SimpleCollection* subnets = net->getAllSubnets();
194 if (!subnets) {
195 // Shared network without subnets it weird, but we decided to
196 // accept such configurations.
197 continue;
198 }
199
200 // For each subnet, add it to a list of regular subnets.
201 for (auto const& subnet : *subnets) {
202 dest->add(subnet);
203 }
204 }
205 }
206
215 void
216 sanityChecks(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
217
219 cfg->sanityChecksLifetime("valid-lifetime");
220
222 const SharedNetwork4Collection* networks = cfg->getCfgSharedNetworks4()->getAll();
223 if (networks) {
224 sharedNetworksSanityChecks(*networks, global->get("shared-networks"));
225 }
226 }
227
234 void
235 sharedNetworksSanityChecks(const SharedNetwork4Collection& networks,
236 ConstElementPtr json) {
237
239 if (!json) {
240 // No json? That means that the shared-networks was never specified
241 // in the config.
242 return;
243 }
244
245 // Used for names uniqueness checks.
246 std::set<string> names;
247
248 // Let's go through all the networks one by one
249 for (auto const& net : networks) {
250 string txt;
251
252 // Let's check if all subnets have either the same interface
253 // or don't have the interface specified at all.
254 bool authoritative = net->getAuthoritative();
255 string iface = net->getIface();
256
257 const Subnet4SimpleCollection* subnets = net->getAllSubnets();
258 if (subnets) {
259 // For each subnet, add it to a list of regular subnets.
260 for (auto const& subnet : *subnets) {
261 if (subnet->getAuthoritative() != authoritative) {
262 isc_throw(DhcpConfigError, "Subnet " << boolalpha
263 << subnet->toText()
264 << " has different authoritative setting "
265 << subnet->getAuthoritative()
266 << " than the shared-network itself: "
267 << authoritative);
268 }
269
270 if (iface.empty()) {
271 iface = subnet->getIface();
272 continue;
273 }
274
275 if (subnet->getIface().empty()) {
276 continue;
277 }
278
279 if (subnet->getIface() != iface) {
280 isc_throw(DhcpConfigError, "Subnet " << subnet->toText()
281 << " has specified interface " << subnet->getIface()
282 << ", but earlier subnet in the same shared-network"
283 << " or the shared-network itself used " << iface);
284 }
285
286 // Let's collect the subnets in case we later find out the
287 // subnet doesn't have a mandatory name.
288 txt += subnet->toText() + " ";
289 }
290 }
291
292 // Next, let's check name of the shared network.
293 if (net->getName().empty()) {
294 isc_throw(DhcpConfigError, "Shared-network with subnets "
295 << txt << " is missing mandatory 'name' parameter");
296 }
297
298 // Is it unique?
299 if (names.find(net->getName()) != names.end()) {
300 isc_throw(DhcpConfigError, "A shared-network with "
301 "name " << net->getName() << " defined twice.");
302 }
303 names.insert(net->getName());
304
305 }
306 }
307};
308
309} // anonymous namespace
310
311namespace isc {
312namespace dhcp {
313
322 // Get new socket configuration.
323 ConstElementPtr sock_cfg =
324 CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
325
326 // Get current socket configuration.
327 ConstElementPtr current_sock_cfg =
328 CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();
329
330 // Determine if the socket configuration has changed. It has if
331 // both old and new configuration is specified but respective
332 // data elements aren't equal.
333 bool sock_changed = (sock_cfg && current_sock_cfg &&
334 !sock_cfg->equals(*current_sock_cfg));
335
336 // If the previous or new socket configuration doesn't exist or
337 // the new configuration differs from the old configuration we
338 // close the existing socket and open a new socket as appropriate.
339 // Note that closing an existing socket means the client will not
340 // receive the configuration result.
341 if (!sock_cfg || !current_sock_cfg || sock_changed) {
342 // Close the existing socket (if any).
343 CommandMgr::instance().closeCommandSocket();
344
345 if (sock_cfg) {
346 // This will create a control socket and install the external
347 // socket in IfaceMgr. That socket will be monitored when
348 // Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
349 // callback in CommandMgr will be called, if necessary.
350 CommandMgr::instance().openCommandSocket(sock_cfg);
351 }
352 }
353
354 // HTTP control socket is simpler: just (re)configure it.
355
356 // Get new config.
357 HttpCommandConfigPtr http_config =
358 CfgMgr::instance().getStagingCfg()->getHttpControlSocketInfo();
359 HttpCommandMgr::instance().configure(http_config);
360}
361
368 // Revert any runtime option definitions configured so far and not committed.
370 // Let's set empty container in case a user hasn't specified any configuration
371 // for option definitions. This is equivalent to committing empty container.
373
374 // Answer will hold the result.
375 ConstElementPtr answer;
376
377 // Global parameter name in case of an error.
378 string parameter_name;
379 ElementPtr mutable_cfg;
380 SrvConfigPtr srv_config;
381 try {
382 // Get the staging configuration.
383 srv_config = CfgMgr::instance().getStagingCfg();
384
385 // This is a way to convert ConstElementPtr to ElementPtr.
386 // We need a config that can be edited, because we will insert
387 // default values and will insert derived values as well.
388 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
389
390 // Set all default values if not specified by the user.
392
393 // And now derive (inherit) global parameters to subnets, if not specified.
395
396 // In principle we could have the following code structured as a series
397 // of long if else if clauses. That would give a marginal performance
398 // boost, but would make the code less readable. We had serious issues
399 // with the parser code debugability, so I decided to keep it as a
400 // series of independent ifs.
401
402 // This parser is used in several places.
403 Dhcp4ConfigParser global_parser;
404
405 // Apply global options in the staging config, e.g. ip-reservations-unique
406 global_parser.parseEarly(srv_config, mutable_cfg);
407
408 // We need definitions first
409 ConstElementPtr option_defs = mutable_cfg->get("option-def");
410 if (option_defs) {
411 parameter_name = "option-def";
412 OptionDefListParser parser(AF_INET);
413 CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
414 parser.parse(cfg_option_def, option_defs);
415 }
416
417 ConstElementPtr option_datas = mutable_cfg->get("option-data");
418 if (option_datas) {
419 parameter_name = "option-data";
420 OptionDataListParser parser(AF_INET);
421 CfgOptionPtr cfg_option = srv_config->getCfgOption();
422 parser.parse(cfg_option, option_datas);
423 }
424
425 ConstElementPtr control_socket = mutable_cfg->get("control-socket");
426 if (control_socket) {
427 mutable_cfg->remove("control-socket");
429 l->add(UserContext::toElement(control_socket));
430 mutable_cfg->set("control-sockets", l);
431 }
432
433 ConstElementPtr control_sockets = mutable_cfg->get("control-sockets");
434 if (control_sockets) {
435 parameter_name = "control-sockets";
437 parser.parse(*srv_config, control_sockets);
438 }
439
440 ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
441 if (multi_threading) {
442 parameter_name = "multi-threading";
444 parser.parse(*srv_config, multi_threading);
445 }
446
447 bool multi_threading_enabled = true;
448 uint32_t thread_count = 0;
449 uint32_t queue_size = 0;
450 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
451 multi_threading_enabled, thread_count, queue_size);
452
454 ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
455 if (queue_control) {
456 parameter_name = "dhcp-queue-control";
458 srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
459 }
460
462 ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
463 if (reservations_lookup_first) {
464 parameter_name = "reservations-lookup-first";
465 if (multi_threading_enabled) {
467 }
468 srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
469 }
470
471 ConstElementPtr hr_identifiers =
472 mutable_cfg->get("host-reservation-identifiers");
473 if (hr_identifiers) {
474 parameter_name = "host-reservation-identifiers";
476 parser.parse(hr_identifiers);
477 }
478
479 ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
480 if (sanity_checks) {
481 parameter_name = "sanity-checks";
482 SanityChecksParser parser;
483 parser.parse(*srv_config, sanity_checks);
484 }
485
486 ConstElementPtr expiration_cfg =
487 mutable_cfg->get("expired-leases-processing");
488 if (expiration_cfg) {
489 parameter_name = "expired-leases-processing";
491 parser.parse(expiration_cfg, CfgMgr::instance().getStagingCfg()->getCfgExpiration());
492 }
493
494 // The hooks-libraries configuration must be parsed after parsing
495 // multi-threading configuration so that libraries are checked
496 // for multi-threading compatibility.
497 ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
498 if (hooks_libraries) {
499 parameter_name = "hooks-libraries";
500 HooksLibrariesParser hooks_parser;
501 HooksConfig& libraries = srv_config->getHooksConfig();
502 hooks_parser.parse(libraries, hooks_libraries);
503 libraries.verifyLibraries(hooks_libraries->getPosition(),
504 multi_threading_enabled);
505 }
506
507 // D2 client configuration.
508 D2ClientConfigPtr d2_client_cfg;
509
510 // Legacy DhcpConfigParser stuff below.
511 ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
512 if (dhcp_ddns) {
513 parameter_name = "dhcp-ddns";
514 // Apply defaults
517 d2_client_cfg = parser.parse(dhcp_ddns);
518 }
519
520 ConstElementPtr client_classes = mutable_cfg->get("client-classes");
521 if (client_classes) {
522 parameter_name = "client-classes";
524 ClientClassDictionaryPtr dictionary =
525 parser.parse(client_classes, AF_INET);
526 srv_config->setClientClassDictionary(dictionary);
527 }
528
529 // Please move at the end when migration will be finished.
530 ConstElementPtr lease_database = mutable_cfg->get("lease-database");
531 if (lease_database) {
532 parameter_name = "lease-database";
533 db::DbAccessParser parser;
534 std::string access_string;
535 parser.parse(access_string, lease_database);
536 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
537 cfg_db_access->setLeaseDbAccessString(access_string);
538 }
539
540 ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
541 if (hosts_database) {
542 parameter_name = "hosts-database";
543 db::DbAccessParser parser;
544 std::string access_string;
545 parser.parse(access_string, hosts_database);
546 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
547 cfg_db_access->setHostDbAccessString(access_string);
548 }
549
550 ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
551 if (hosts_databases) {
552 parameter_name = "hosts-databases";
553 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
554 for (auto const& it : hosts_databases->listValue()) {
555 db::DbAccessParser parser;
556 std::string access_string;
557 parser.parse(access_string, it);
558 cfg_db_access->setHostDbAccessString(access_string);
559 }
560 }
561
562 // Keep relative orders of shared networks and subnets.
563 ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
564 if (shared_networks) {
565 parameter_name = "shared-networks";
572 CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4();
573 parser.parse(cfg, shared_networks);
574
575 // We also need to put the subnets it contains into normal
576 // subnets list.
577 global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg);
578 }
579
580 ConstElementPtr subnet4 = mutable_cfg->get("subnet4");
581 if (subnet4) {
582 parameter_name = "subnet4";
583 Subnets4ListConfigParser subnets_parser;
584 // parse() returns number of subnets parsed. We may log it one day.
585 subnets_parser.parse(srv_config, subnet4);
586 }
587
588 ConstElementPtr reservations = mutable_cfg->get("reservations");
589 if (reservations) {
590 parameter_name = "reservations";
591 HostCollection hosts;
592 HostReservationsListParser<HostReservationParser4> parser;
593 parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
594 for (auto const& h : hosts) {
595 srv_config->getCfgHosts()->add(h);
596 }
597 }
598
599 ConstElementPtr config_control = mutable_cfg->get("config-control");
600 if (config_control) {
601 parameter_name = "config-control";
602 ConfigControlParser parser;
603 ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
604 CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
605 }
606
607 ConstElementPtr compatibility = mutable_cfg->get("compatibility");
608 if (compatibility) {
609 CompatibilityParser parser;
610 parser.parse(compatibility, *CfgMgr::instance().getStagingCfg());
611 }
612
613 // Make parsers grouping.
614 const std::map<std::string, ConstElementPtr>& values_map =
615 mutable_cfg->mapValue();
616
617 for (auto const& config_pair : values_map) {
618 parameter_name = config_pair.first;
619
620 // These are converted to SimpleParser and are handled already above.
621 if ((config_pair.first == "option-def") ||
622 (config_pair.first == "option-data") ||
623 (config_pair.first == "control-socket") ||
624 (config_pair.first == "control-sockets") ||
625 (config_pair.first == "multi-threading") ||
626 (config_pair.first == "dhcp-queue-control") ||
627 (config_pair.first == "host-reservation-identifiers") ||
628 (config_pair.first == "interfaces-config") ||
629 (config_pair.first == "sanity-checks") ||
630 (config_pair.first == "expired-leases-processing") ||
631 (config_pair.first == "hooks-libraries") ||
632 (config_pair.first == "dhcp-ddns") ||
633 (config_pair.first == "client-classes") ||
634 (config_pair.first == "lease-database") ||
635 (config_pair.first == "hosts-database") ||
636 (config_pair.first == "hosts-databases") ||
637 (config_pair.first == "subnet4") ||
638 (config_pair.first == "shared-networks") ||
639 (config_pair.first == "reservations") ||
640 (config_pair.first == "config-control") ||
641 (config_pair.first == "loggers") ||
642 (config_pair.first == "compatibility")) {
643 continue;
644 }
645
646 // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
647 // The old method is used in JSON configuration parsers when the global
648 // parameters are derived into the subnets and shared networks and are
649 // being treated as explicitly specified. The new way used by the config
650 // backend is the dynamic inheritance whereby each subnet and shared
651 // network uses a callback function to return global parameter if it
652 // is not specified at lower level. This callback uses configured globals.
653 // We deliberately include both default and explicitly specified globals
654 // so as the callback can access the appropriate global values regardless
655 // whether they are set to a default or other value.
656 if ( (config_pair.first == "renew-timer") ||
657 (config_pair.first == "rebind-timer") ||
658 (config_pair.first == "valid-lifetime") ||
659 (config_pair.first == "min-valid-lifetime") ||
660 (config_pair.first == "max-valid-lifetime") ||
661 (config_pair.first == "decline-probation-period") ||
662 (config_pair.first == "dhcp4o6-port") ||
663 (config_pair.first == "echo-client-id") ||
664 (config_pair.first == "match-client-id") ||
665 (config_pair.first == "authoritative") ||
666 (config_pair.first == "next-server") ||
667 (config_pair.first == "server-hostname") ||
668 (config_pair.first == "boot-file-name") ||
669 (config_pair.first == "server-tag") ||
670 (config_pair.first == "reservations-global") ||
671 (config_pair.first == "reservations-in-subnet") ||
672 (config_pair.first == "reservations-out-of-pool") ||
673 (config_pair.first == "calculate-tee-times") ||
674 (config_pair.first == "t1-percent") ||
675 (config_pair.first == "t2-percent") ||
676 (config_pair.first == "cache-threshold") ||
677 (config_pair.first == "cache-max-age") ||
678 (config_pair.first == "hostname-char-set") ||
679 (config_pair.first == "hostname-char-replacement") ||
680 (config_pair.first == "ddns-send-updates") ||
681 (config_pair.first == "ddns-override-no-update") ||
682 (config_pair.first == "ddns-override-client-update") ||
683 (config_pair.first == "ddns-replace-client-name") ||
684 (config_pair.first == "ddns-generated-prefix") ||
685 (config_pair.first == "ddns-qualifying-suffix") ||
686 (config_pair.first == "ddns-update-on-renew") ||
687 (config_pair.first == "ddns-use-conflict-resolution") ||
688 (config_pair.first == "ddns-conflict-resolution-mode") ||
689 (config_pair.first == "ddns-ttl-percent") ||
690 (config_pair.first == "store-extended-info") ||
691 (config_pair.first == "statistic-default-sample-count") ||
692 (config_pair.first == "statistic-default-sample-age") ||
693 (config_pair.first == "early-global-reservations-lookup") ||
694 (config_pair.first == "ip-reservations-unique") ||
695 (config_pair.first == "reservations-lookup-first") ||
696 (config_pair.first == "parked-packet-limit") ||
697 (config_pair.first == "allocator") ||
698 (config_pair.first == "offer-lifetime") ||
699 (config_pair.first == "stash-agent-options") ) {
700 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
701 config_pair.second);
702 continue;
703 }
704
705 // Nothing to configure for the user-context.
706 if (config_pair.first == "user-context") {
707 continue;
708 }
709
710 // If we got here, no code handled this parameter, so we bail out.
712 "unsupported global configuration parameter: " << config_pair.first
713 << " (" << config_pair.second->getPosition() << ")");
714 }
715
716 // Reset parameter name.
717 parameter_name = "<post parsing>";
718
719 // Apply global options in the staging config.
720 global_parser.parse(srv_config, mutable_cfg);
721
722 // This method conducts final sanity checks and tweaks. In particular,
723 // it checks that there is no conflict between plain subnets and those
724 // defined as part of shared networks.
725 global_parser.sanityChecks(srv_config, mutable_cfg);
726
727 // Validate D2 client configuration.
728 if (!d2_client_cfg) {
729 d2_client_cfg.reset(new D2ClientConfig());
730 }
731 d2_client_cfg->validateContents();
732 srv_config->setD2ClientConfig(d2_client_cfg);
733 } catch (const isc::Exception& ex) {
735 .arg(parameter_name).arg(ex.what());
737 } catch (...) {
738 // For things like bad_cast in boost::lexical_cast
739 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
740 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
741 "processing error");
742 }
743
744 if (!answer) {
745 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
746 "Control-socket, hook-libraries, and D2 configuration "
747 "were sanity checked, but not applied.");
748 }
749
750 return (answer);
751}
752
755 bool check_only, bool extra_checks) {
756 if (!config_set) {
758 "Can't parse NULL config");
759 return (answer);
760 }
761
763 .arg(server.redactConfig(config_set)->str());
764
765 if (check_only) {
766 MultiThreadingMgr::instance().setTestMode(true);
767 }
768
769 auto answer = processDhcp4Config(config_set);
770
771 int status_code = CONTROL_RESULT_SUCCESS;
772 isc::config::parseAnswer(status_code, answer);
773
774 SrvConfigPtr srv_config;
775
776 if (status_code == CONTROL_RESULT_SUCCESS) {
777 if (check_only) {
778 if (extra_checks) {
779 // Re-open lease and host database with new parameters.
780 try {
781 // Get the staging configuration.
782 srv_config = CfgMgr::instance().getStagingCfg();
783
784 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
785 string params = "universe=4 persist=false";
786 cfg_db->setAppendedParameters(params);
787 cfg_db->createManagers();
788 } catch (const std::exception& ex) {
790 status_code = CONTROL_RESULT_ERROR;
791 }
792
793 if (status_code == CONTROL_RESULT_SUCCESS) {
794 std::ostringstream err;
795 // Configure DHCP packet queueing
796 try {
798 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
799 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
801 .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
802 }
803
804 } catch (const std::exception& ex) {
805 err << "Error setting packet queue controls after server reconfiguration: "
806 << ex.what();
808 status_code = CONTROL_RESULT_ERROR;
809 }
810 }
811 }
812 } else {
813 // disable multi-threading (it will be applied by new configuration)
814 // this must be done in order to properly handle MT to ST transition
815 // when 'multi-threading' structure is missing from new config and
816 // to properly drop any task items stored in the thread pool which
817 // might reference some handles to loaded hooks, preventing them
818 // from being unloaded.
819 MultiThreadingMgr::instance().apply(false, 0, 0);
820
821 // Close DHCP sockets and remove any existing timers.
822 IfaceMgr::instance().closeSockets();
823 TimerMgr::instance()->unregisterTimers();
824 server.discardPackets();
825 server.getCBControl()->reset();
826 }
827
828 if (status_code == CONTROL_RESULT_SUCCESS) {
829 string parameter_name;
830 ElementPtr mutable_cfg;
831 try {
832 // Get the staging configuration.
833 srv_config = CfgMgr::instance().getStagingCfg();
834
835 // This is a way to convert ConstElementPtr to ElementPtr.
836 // We need a config that can be edited, because we will insert
837 // default values and will insert derived values as well.
838 mutable_cfg = boost::const_pointer_cast<Element>(config_set);
839
840 ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
841 if (ifaces_config) {
842 parameter_name = "interfaces-config";
843 IfacesConfigParser parser(AF_INET, check_only);
844 CfgIfacePtr cfg_iface = srv_config->getCfgIface();
845 cfg_iface->reset();
846 parser.parse(cfg_iface, ifaces_config);
847 }
848 } catch (const isc::Exception& ex) {
850 .arg(parameter_name).arg(ex.what());
852 status_code = CONTROL_RESULT_ERROR;
853 } catch (...) {
854 // For things like bad_cast in boost::lexical_cast
855 LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
856 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
857 " processing error");
858 status_code = CONTROL_RESULT_ERROR;
859 }
860 }
861 }
862
863 // So far so good, there was no parsing error so let's commit the
864 // configuration. This will add created subnets and option values into
865 // the server's configuration.
866 // This operation should be exception safe but let's make sure.
867 if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
868 try {
869
870 // Setup the command channel.
872 } catch (const isc::Exception& ex) {
875 status_code = CONTROL_RESULT_ERROR;
876 } catch (...) {
877 // For things like bad_cast in boost::lexical_cast
879 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
880 " parsing error");
881 status_code = CONTROL_RESULT_ERROR;
882 }
883 }
884
885 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
886 try {
887 // No need to commit interface names as this is handled by the
888 // CfgMgr::commit() function.
889
890 // Apply the staged D2ClientConfig, used to be done by parser commit
892 cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
893 CfgMgr::instance().setD2ClientConfig(cfg);
894 } catch (const isc::Exception& ex) {
897 status_code = CONTROL_RESULT_ERROR;
898 } catch (...) {
899 // For things like bad_cast in boost::lexical_cast
901 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
902 " parsing error");
903 status_code = CONTROL_RESULT_ERROR;
904 }
905 }
906
907 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
908 try {
909 // This occurs last as if it succeeds, there is no easy way to
910 // revert it. As a result, the failure to commit a subsequent
911 // change causes problems when trying to roll back.
913 static_cast<void>(HooksManager::unloadLibraries());
914 IOServiceMgr::instance().clearIOServices();
915 const HooksConfig& libraries =
916 CfgMgr::instance().getStagingCfg()->getHooksConfig();
917 bool multi_threading_enabled = true;
918 uint32_t thread_count = 0;
919 uint32_t queue_size = 0;
920 CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
921 multi_threading_enabled, thread_count, queue_size);
922 libraries.loadLibraries(multi_threading_enabled);
923 } catch (const isc::Exception& ex) {
926 status_code = CONTROL_RESULT_ERROR;
927 } catch (...) {
928 // For things like bad_cast in boost::lexical_cast
930 answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
931 " parsing error");
932 status_code = CONTROL_RESULT_ERROR;
933 }
934 }
935
936 // Log the list of known backends.
938
939 // Log the list of known backends.
941
942 // Moved from the commit block to add the config backend indication.
943 if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
944 try {
945 // If there are config backends, fetch and merge into staging config
946 server.getCBControl()->databaseConfigFetch(srv_config,
948 } catch (const isc::Exception& ex) {
949 std::ostringstream err;
950 err << "during update from config backend database: " << ex.what();
953 status_code = CONTROL_RESULT_ERROR;
954 } catch (...) {
955 // For things like bad_cast in boost::lexical_cast
956 std::ostringstream err;
957 err << "during update from config backend database: "
958 << "undefined configuration parsing error";
961 status_code = CONTROL_RESULT_ERROR;
962 }
963 }
964
965 // Rollback changes as the configuration parsing failed.
966 if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
967 // Revert to original configuration of runtime option definitions
968 // in the libdhcp++.
970
971 if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
972 auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
973 if (notify_libraries) {
974 return (notify_libraries);
975 }
976
978 try {
979 // Handle events registered by hooks using external IOService objects.
980 IOServiceMgr::instance().pollIOServices();
981 } catch (const std::exception& ex) {
982 std::ostringstream err;
983 err << "Error initializing hooks: "
984 << ex.what();
986 }
987 }
988
989 return (answer);
990 }
991
993 .arg(CfgMgr::instance().getStagingCfg()->
994 getConfigSummary(SrvConfig::CFGSEL_ALL4));
995
996 // Also calculate SHA256 hash of the config that was just set and
997 // append it to the response.
998 ConstElementPtr config = CfgMgr::instance().getStagingCfg()->toElement();
999 string hash = BaseCommandMgr::getHash(config);
1000 ElementPtr hash_map = Element::createMap();
1001 hash_map->set("hash", Element::create(hash));
1002
1003 // Everything was fine. Configuration is successful.
1004 answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
1005 return (answer);
1006}
1007
1008} // namespace dhcp
1009} // 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 HttpCommandMgr & instance()
HttpCommandMgr 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: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: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: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:846
boost::multi_index_container< Subnet4Ptr, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetSubnetIdIndexTag >, boost::multi_index::const_mem_fun< Subnet, SubnetID, &Subnet::getID > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetPrefixIndexTag >, boost::multi_index::const_mem_fun< Subnet, std::string, &Subnet::toText > > > > Subnet4SimpleCollection
A simple collection of Subnet4 objects.
Definition subnet.h:807
const int DBG_DHCP4_COMMAND
Debug level used to log receiving commands.
Definition dhcp4_log.h:30
const isc::log::MessageID DHCP4_PARSER_COMMIT_FAIL
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
isc::data::ConstElementPtr configureDhcp4Server(Dhcpv4Srv &server, isc::data::ConstElementPtr config_set, bool check_only, bool extra_checks)
Configure DHCPv4 server (Dhcpv4Srv) with a set of configuration values.
boost::shared_ptr< CfgSubnets4 > CfgSubnets4Ptr
Non-const pointer.
const isc::log::MessageID DHCP4_CONFIG_COMPLETE
boost::multi_index_container< SharedNetwork4Ptr, boost::multi_index::indexed_by< boost::multi_index::random_access< boost::multi_index::tag< SharedNetworkRandomAccessIndexTag > >, boost::multi_index::hashed_non_unique< boost::multi_index::tag< SharedNetworkIdIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, uint64_t, &data::BaseStampedElement::getId > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SharedNetworkNameIndexTag >, boost::multi_index::const_mem_fun< SharedNetwork4, std::string, &SharedNetwork4::getName > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SharedNetworkServerIdIndexTag >, boost::multi_index::const_mem_fun< Network4, asiolink::IOAddress, &Network4::getServerId > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SharedNetworkModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > > > > SharedNetwork4Collection
Multi index container holding shared networks.
boost::shared_ptr< CfgSharedNetworks4 > CfgSharedNetworks4Ptr
Pointer to the configuration of IPv4 shared networks.
isc::log::Logger dhcp4_logger(DHCP4_APP_LOGGER_NAME)
Base logger for DHCPv4 server.
Definition dhcp4_log.h:90
boost::shared_ptr< ConfigControlInfo > ConfigControlInfoPtr
Defines a pointer to a ConfigControlInfo.
Defines the logger used by the top-level component of kea-lfc.
static data::ElementPtr toElement(data::ConstElementPtr map)
Copy an Element map.