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