Kea  2.3.9
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>
14 #include <dhcp4/ctrl_dhcp4_srv.h>
15 #include <dhcp4/dhcp4_log.h>
16 #include <dhcp4/dhcp4_srv.h>
18 #include <dhcp/libdhcp++.h>
19 #include <dhcp/option_definition.h>
20 #include <dhcpsrv/cb_ctl_dhcp4.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 
58 using namespace std;
59 using namespace isc;
60 using namespace isc::data;
61 using namespace isc::dhcp;
62 using namespace isc::asiolink;
63 using namespace isc::hooks;
64 using namespace isc::process;
65 using namespace isc::config;
66 using namespace isc::util;
67 
68 namespace {
69 
78 class Dhcp4ConfigParser : public isc::data::SimpleParser {
79 public:
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 
281 namespace isc {
282 namespace 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 
327  // Before starting any subnet operations, let's reset the subnet-id counter,
328  // so newly recreated configuration starts with first subnet-id equal 1.
329  Subnet::resetSubnetID();
330 
331  // Revert any runtime option definitions configured so far and not committed.
332  LibDHCP::revertRuntimeOptionDefs();
333  // Let's set empty container in case a user hasn't specified any configuration
334  // for option definitions. This is equivalent to committing empty container.
335  LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
336 
337  // Print the list of known backends.
338  HostDataSourceFactory::printRegistered();
339 
340  // Answer will hold the result.
341  ConstElementPtr answer;
342 
343  // Global parameter name in case of an error.
344  string parameter_name;
345  ElementPtr mutable_cfg;
346  SrvConfigPtr srv_config;
347  try {
348  // Get the staging configuration.
349  srv_config = CfgMgr::instance().getStagingCfg();
350 
351  // This is a way to convert ConstElementPtr to ElementPtr.
352  // We need a config that can be edited, because we will insert
353  // default values and will insert derived values as well.
354  mutable_cfg = boost::const_pointer_cast<Element>(config_set);
355 
356  // Relocate dhcp-ddns parameters that have moved to global scope.
357  // Rule is that a global value overrides the dhcp-ddns value, so
358  // we need to do this before we apply global defaults.
359  // Note this is done for backward compatibility.
360  srv_config->moveDdnsParams(mutable_cfg);
361 
362  // Move from reservation mode to new reservations flags.
363  // @todo add warning
364  BaseNetworkParser::moveReservationMode(mutable_cfg);
365 
366  // Set all default values if not specified by the user.
367  SimpleParser4::setAllDefaults(mutable_cfg);
368 
369  // And now derive (inherit) global parameters to subnets, if not specified.
370  SimpleParser4::deriveParameters(mutable_cfg);
371 
372  // In principle we could have the following code structured as a series
373  // of long if else if clauses. That would give a marginal performance
374  // boost, but would make the code less readable. We had serious issues
375  // with the parser code debugability, so I decided to keep it as a
376  // series of independent ifs.
377 
378  // This parser is used in several places.
379  Dhcp4ConfigParser global_parser;
380 
381  // Apply global options in the staging config, e.g. ip-reservations-unique
382  global_parser.parseEarly(srv_config, mutable_cfg);
383 
384  // We need definitions first
385  ConstElementPtr option_defs = mutable_cfg->get("option-def");
386  if (option_defs) {
387  parameter_name = "option-def";
388  OptionDefListParser parser(AF_INET);
389  CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
390  parser.parse(cfg_option_def, option_defs);
391  }
392 
393  ConstElementPtr option_datas = mutable_cfg->get("option-data");
394  if (option_datas) {
395  parameter_name = "option-data";
396  OptionDataListParser parser(AF_INET);
397  CfgOptionPtr cfg_option = srv_config->getCfgOption();
398  parser.parse(cfg_option, option_datas);
399  }
400 
401  ConstElementPtr control_socket = mutable_cfg->get("control-socket");
402  if (control_socket) {
403  parameter_name = "control-socket";
404  ControlSocketParser parser;
405  parser.parse(*srv_config, control_socket);
406  }
407 
408  ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
409  if (multi_threading) {
410  parameter_name = "multi-threading";
412  parser.parse(*srv_config, multi_threading);
413  }
414 
415  bool multi_threading_enabled = true;
416  uint32_t thread_count = 0;
417  uint32_t queue_size = 0;
418  CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
419  multi_threading_enabled, thread_count, queue_size);
420 
422  ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
423  if (queue_control) {
424  parameter_name = "dhcp-queue-control";
425  DHCPQueueControlParser parser;
426  srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
427  }
428 
430  ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
431  if (reservations_lookup_first) {
432  parameter_name = "reservations-lookup-first";
433  if (multi_threading_enabled) {
435  }
436  srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
437  }
438 
439  ConstElementPtr hr_identifiers =
440  mutable_cfg->get("host-reservation-identifiers");
441  if (hr_identifiers) {
442  parameter_name = "host-reservation-identifiers";
444  parser.parse(hr_identifiers);
445  }
446 
447  ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
448  if (ifaces_config) {
449  parameter_name = "interfaces-config";
450  IfacesConfigParser parser(AF_INET, true);
451  CfgIfacePtr cfg_iface = srv_config->getCfgIface();
452  parser.parse(cfg_iface, ifaces_config);
453  }
454 
455  ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
456  if (sanity_checks) {
457  parameter_name = "sanity-checks";
458  SanityChecksParser parser;
459  parser.parse(*srv_config, sanity_checks);
460  }
461 
462  ConstElementPtr expiration_cfg =
463  mutable_cfg->get("expired-leases-processing");
464  if (expiration_cfg) {
465  parameter_name = "expired-leases-processing";
466  ExpirationConfigParser parser;
467  parser.parse(expiration_cfg);
468  }
469 
470  // The hooks-libraries configuration must be parsed after parsing
471  // multi-threading configuration so that libraries are checked
472  // for multi-threading compatibility.
473  ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
474  if (hooks_libraries) {
475  parameter_name = "hooks-libraries";
476  HooksLibrariesParser hooks_parser;
477  HooksConfig& libraries = srv_config->getHooksConfig();
478  hooks_parser.parse(libraries, hooks_libraries);
479  libraries.verifyLibraries(hooks_libraries->getPosition(),
480  multi_threading_enabled);
481  }
482 
483  // D2 client configuration.
484  D2ClientConfigPtr d2_client_cfg;
485 
486  // Legacy DhcpConfigParser stuff below.
487  ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
488  if (dhcp_ddns) {
489  parameter_name = "dhcp-ddns";
490  // Apply defaults
491  D2ClientConfigParser::setAllDefaults(dhcp_ddns);
492  D2ClientConfigParser parser;
493  d2_client_cfg = parser.parse(dhcp_ddns);
494  }
495 
496  ConstElementPtr client_classes = mutable_cfg->get("client-classes");
497  if (client_classes) {
498  parameter_name = "client-classes";
500  ClientClassDictionaryPtr dictionary =
501  parser.parse(client_classes, AF_INET);
502  srv_config->setClientClassDictionary(dictionary);
503  }
504 
505  // Please move at the end when migration will be finished.
506  ConstElementPtr lease_database = mutable_cfg->get("lease-database");
507  if (lease_database) {
508  parameter_name = "lease-database";
509  db::DbAccessParser parser;
510  std::string access_string;
511  parser.parse(access_string, lease_database);
512  CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
513  cfg_db_access->setLeaseDbAccessString(access_string);
514  }
515 
516  ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
517  if (hosts_database) {
518  parameter_name = "hosts-database";
519  db::DbAccessParser parser;
520  std::string access_string;
521  parser.parse(access_string, hosts_database);
522  CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
523  cfg_db_access->setHostDbAccessString(access_string);
524  }
525 
526  ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
527  if (hosts_databases) {
528  parameter_name = "hosts-databases";
529  CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
530  for (auto it : hosts_databases->listValue()) {
531  db::DbAccessParser parser;
532  std::string access_string;
533  parser.parse(access_string, it);
534  cfg_db_access->setHostDbAccessString(access_string);
535  }
536  }
537 
538  // Keep relative orders of shared networks and subnets.
539  ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
540  if (shared_networks) {
541  parameter_name = "shared-networks";
548  CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4();
549  parser.parse(cfg, shared_networks);
550 
551  // We also need to put the subnets it contains into normal
552  // subnets list.
553  global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg);
554  }
555 
556  ConstElementPtr subnet4 = mutable_cfg->get("subnet4");
557  if (subnet4) {
558  parameter_name = "subnet4";
559  Subnets4ListConfigParser subnets_parser;
560  // parse() returns number of subnets parsed. We may log it one day.
561  subnets_parser.parse(srv_config, subnet4);
562  }
563 
564  ConstElementPtr reservations = mutable_cfg->get("reservations");
565  if (reservations) {
566  parameter_name = "reservations";
567  HostCollection hosts;
569  parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
570  for (auto h = hosts.begin(); h != hosts.end(); ++h) {
571  srv_config->getCfgHosts()->add(*h);
572  }
573  }
574 
575  ConstElementPtr config_control = mutable_cfg->get("config-control");
576  if (config_control) {
577  parameter_name = "config-control";
578  ConfigControlParser parser;
579  ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
580  CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
581  }
582 
583  ConstElementPtr compatibility = mutable_cfg->get("compatibility");
584  if (compatibility) {
585  for (auto kv : compatibility->mapValue()) {
586  if (!kv.second || (kv.second->getType() != Element::boolean)) {
588  "compatibility parameter values must be "
589  << "boolean (" << kv.first << " at "
590  << kv.second->getPosition() << ")");
591  }
592  if (kv.first == "lenient-option-parsing") {
593  CfgMgr::instance().getStagingCfg()->setLenientOptionParsing(
594  kv.second->boolValue());
595  } else if (kv.first == "ignore-dhcp-server-identifier") {
596  CfgMgr::instance().getStagingCfg()->setIgnoreServerIdentifier(
597  kv.second->boolValue());
598  } else if (kv.first == "ignore-rai-link-selection") {
599  CfgMgr::instance().getStagingCfg()->setIgnoreRAILinkSelection(
600  kv.second->boolValue());
601  } else if (kv.first == "exclude-first-last-24") {
602  CfgMgr::instance().getStagingCfg()->setExcludeFirstLast24(
603  kv.second->boolValue());
604  } else {
606  "unsupported compatibility parameter: "
607  << kv.first << " (" << kv.second->getPosition()
608  << ")");
609  }
610  }
611  }
612 
613  // Make parsers grouping.
614  ConfigPair config_pair;
615  const std::map<std::string, ConstElementPtr>& values_map =
616  mutable_cfg->mapValue();
617 
618  BOOST_FOREACH(config_pair, values_map) {
619 
620  parameter_name = config_pair.first;
621 
622  // These are converted to SimpleParser and are handled already above.
623  if ((config_pair.first == "option-def") ||
624  (config_pair.first == "option-data") ||
625  (config_pair.first == "control-socket") ||
626  (config_pair.first == "multi-threading") ||
627  (config_pair.first == "dhcp-queue-control") ||
628  (config_pair.first == "host-reservation-identifiers") ||
629  (config_pair.first == "interfaces-config") ||
630  (config_pair.first == "sanity-checks") ||
631  (config_pair.first == "expired-leases-processing") ||
632  (config_pair.first == "hooks-libraries") ||
633  (config_pair.first == "dhcp-ddns") ||
634  (config_pair.first == "client-classes") ||
635  (config_pair.first == "lease-database") ||
636  (config_pair.first == "hosts-database") ||
637  (config_pair.first == "hosts-databases") ||
638  (config_pair.first == "subnet4") ||
639  (config_pair.first == "shared-networks") ||
640  (config_pair.first == "reservations") ||
641  (config_pair.first == "config-control") ||
642  (config_pair.first == "loggers") ||
643  (config_pair.first == "compatibility")) {
644  continue;
645  }
646 
647  // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
648  // The old method is used in JSON configuration parsers when the global
649  // parameters are derived into the subnets and shared networks and are
650  // being treated as explicitly specified. The new way used by the config
651  // backend is the dynamic inheritance whereby each subnet and shared
652  // network uses a callback function to return global parameter if it
653  // is not specified at lower level. This callback uses configured globals.
654  // We deliberately include both default and explicitly specified globals
655  // so as the callback can access the appropriate global values regardless
656  // whether they are set to a default or other value.
657  if ( (config_pair.first == "renew-timer") ||
658  (config_pair.first == "rebind-timer") ||
659  (config_pair.first == "valid-lifetime") ||
660  (config_pair.first == "min-valid-lifetime") ||
661  (config_pair.first == "max-valid-lifetime") ||
662  (config_pair.first == "decline-probation-period") ||
663  (config_pair.first == "dhcp4o6-port") ||
664  (config_pair.first == "echo-client-id") ||
665  (config_pair.first == "match-client-id") ||
666  (config_pair.first == "authoritative") ||
667  (config_pair.first == "next-server") ||
668  (config_pair.first == "server-hostname") ||
669  (config_pair.first == "boot-file-name") ||
670  (config_pair.first == "server-tag") ||
671  (config_pair.first == "reservation-mode") ||
672  (config_pair.first == "reservations-global") ||
673  (config_pair.first == "reservations-in-subnet") ||
674  (config_pair.first == "reservations-out-of-pool") ||
675  (config_pair.first == "calculate-tee-times") ||
676  (config_pair.first == "t1-percent") ||
677  (config_pair.first == "t2-percent") ||
678  (config_pair.first == "cache-threshold") ||
679  (config_pair.first == "cache-max-age") ||
680  (config_pair.first == "hostname-char-set") ||
681  (config_pair.first == "hostname-char-replacement") ||
682  (config_pair.first == "ddns-send-updates") ||
683  (config_pair.first == "ddns-override-no-update") ||
684  (config_pair.first == "ddns-override-client-update") ||
685  (config_pair.first == "ddns-replace-client-name") ||
686  (config_pair.first == "ddns-generated-prefix") ||
687  (config_pair.first == "ddns-qualifying-suffix") ||
688  (config_pair.first == "ddns-update-on-renew") ||
689  (config_pair.first == "ddns-use-conflict-resolution") ||
690  (config_pair.first == "ddns-ttl-percent") ||
691  (config_pair.first == "store-extended-info") ||
692  (config_pair.first == "statistic-default-sample-count") ||
693  (config_pair.first == "statistic-default-sample-age") ||
694  (config_pair.first == "early-global-reservations-lookup") ||
695  (config_pair.first == "ip-reservations-unique") ||
696  (config_pair.first == "reservations-lookup-first") ||
697  (config_pair.first == "parked-packet-limit") ||
698  (config_pair.first == "allocator") ||
699  (config_pair.first == "offer-lifetime") ) {
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  auto answer = processDhcp4Config(config_set);
766 
767  int status_code = CONTROL_RESULT_SUCCESS;
768  isc::config::parseAnswer(status_code, answer);
769 
770  SrvConfigPtr srv_config;
771 
772  if (status_code == CONTROL_RESULT_SUCCESS) {
773  if (check_only) {
774  if (extra_checks) {
775  // Re-open lease and host database with new parameters.
776  try {
777  // Get the staging configuration.
778  srv_config = CfgMgr::instance().getStagingCfg();
779 
780  CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
781  string params = "universe=4 persist=false";
782  if (cfg_db->getExtendedInfoTablesEnabled()) {
783  params += " extended-info-tables=true";
784  }
785  cfg_db->setAppendedParameters(params);
786  cfg_db->createManagers();
787  } catch (const std::exception& ex) {
788  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
789  status_code = CONTROL_RESULT_ERROR;
790  }
791 
792  if (status_code == CONTROL_RESULT_SUCCESS) {
793  std::ostringstream err;
794  // Configure DHCP packet queueing
795  try {
797  qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
798  if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
800  .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
801  }
802 
803  } catch (const std::exception& ex) {
804  err << "Error setting packet queue controls after server reconfiguration: "
805  << ex.what();
806  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
807  status_code = CONTROL_RESULT_ERROR;
808  }
809  }
810  }
811  } else {
812  string parameter_name;
813  ElementPtr mutable_cfg;
814 
815  // disable multi-threading (it will be applied by new configuration)
816  // this must be done in order to properly handle MT to ST transition
817  // when 'multi-threading' structure is missing from new config and
818  // to properly drop any task items stored in the thread pool which
819  // might reference some handles to loaded hooks, preventing them
820  // from being unloaded.
821  MultiThreadingMgr::instance().apply(false, 0, 0);
822 
823  // Close DHCP sockets and remove any existing timers.
824  IfaceMgr::instance().closeSockets();
825  TimerMgr::instance()->unregisterTimers();
826  server.discardPackets();
827  server.getCBControl()->reset();
828 
829  try {
830 
831  // Get the staging configuration.
832  srv_config = CfgMgr::instance().getStagingCfg();
833 
834  // This is a way to convert ConstElementPtr to ElementPtr.
835  // We need a config that can be edited, because we will insert
836  // default values and will insert derived values as well.
837  mutable_cfg = boost::const_pointer_cast<Element>(config_set);
838 
839  ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
840  if (ifaces_config) {
841  parameter_name = "interfaces-config";
842  IfacesConfigParser parser(AF_INET, false);
843  CfgIfacePtr cfg_iface = srv_config->getCfgIface();
844  cfg_iface->reset();
845  parser.parse(cfg_iface, ifaces_config);
846  }
847  } catch (const isc::Exception& ex) {
849  .arg(parameter_name).arg(ex.what());
851  status_code = CONTROL_RESULT_ERROR;
852  } catch (...) {
853  // For things like bad_cast in boost::lexical_cast
854  LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
855  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
856  " processing error");
857  status_code = CONTROL_RESULT_ERROR;
858  }
859  }
860  }
861 
862  // So far so good, there was no parsing error so let's commit the
863  // configuration. This will add created subnets and option values into
864  // the server's configuration.
865  // This operation should be exception safe but let's make sure.
866  if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
867  try {
868 
869  // Setup the command channel.
871  } catch (const isc::Exception& ex) {
874  status_code = CONTROL_RESULT_ERROR;
875  } catch (...) {
876  // For things like bad_cast in boost::lexical_cast
878  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
879  " parsing error");
880  status_code = CONTROL_RESULT_ERROR;
881  }
882  }
883 
884  if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
885  try {
886  // No need to commit interface names as this is handled by the
887  // CfgMgr::commit() function.
888 
889  // Apply the staged D2ClientConfig, used to be done by parser commit
890  D2ClientConfigPtr cfg;
891  cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
892  CfgMgr::instance().setD2ClientConfig(cfg);
893  } catch (const isc::Exception& ex) {
896  status_code = CONTROL_RESULT_ERROR;
897  } catch (...) {
898  // For things like bad_cast in boost::lexical_cast
900  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
901  " parsing error");
902  status_code = CONTROL_RESULT_ERROR;
903  }
904  }
905 
906  if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
907  try {
908  // This occurs last as if it succeeds, there is no easy way to
909  // revert it. As a result, the failure to commit a subsequent
910  // change causes problems when trying to roll back.
911  HooksManager::prepareUnloadLibraries();
912  static_cast<void>(HooksManager::unloadLibraries());
913  const HooksConfig& libraries =
914  CfgMgr::instance().getStagingCfg()->getHooksConfig();
915  bool multi_threading_enabled = true;
916  uint32_t thread_count = 0;
917  uint32_t queue_size = 0;
918  CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
919  multi_threading_enabled, thread_count, queue_size);
920  libraries.loadLibraries(multi_threading_enabled);
921  } catch (const isc::Exception& ex) {
924  status_code = CONTROL_RESULT_ERROR;
925  } catch (...) {
926  // For things like bad_cast in boost::lexical_cast
928  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
929  " parsing error");
930  status_code = CONTROL_RESULT_ERROR;
931  }
932  }
933 
934  // Moved from the commit block to add the config backend indication.
935  if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
936  try {
937  // If there are config backends, fetch and merge into staging config
938  server.getCBControl()->databaseConfigFetch(srv_config,
939  CBControlDHCPv4::FetchMode::FETCH_ALL);
940  } catch (const isc::Exception& ex) {
941  std::ostringstream err;
942  err << "during update from config backend database: " << ex.what();
944  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
945  status_code = CONTROL_RESULT_ERROR;
946  } catch (...) {
947  // For things like bad_cast in boost::lexical_cast
948  std::ostringstream err;
949  err << "during update from config backend database: "
950  << "undefined configuration parsing error";
952  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
953  status_code = CONTROL_RESULT_ERROR;
954  }
955  }
956 
957  // Rollback changes as the configuration parsing failed.
958  if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
959  // Revert to original configuration of runtime option definitions
960  // in the libdhcp++.
961  LibDHCP::revertRuntimeOptionDefs();
962 
963  if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
964  auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
965  if (notify_libraries) {
966  return (notify_libraries);
967  }
968  }
969 
970  return (answer);
971  }
972 
974  .arg(CfgMgr::instance().getStagingCfg()->
975  getConfigSummary(SrvConfig::CFGSEL_ALL4));
976 
977  // Everything was fine. Configuration is successful.
978  answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.");
979  return (answer);
980 }
981 
982 } // namespace dhcp
983 } // 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
Parse Database Parameters.
void parse(std::string &access_string, isc::data::ConstElementPtr database_config)
Parse configuration value.
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
Parser for D2ClientConfig.
Definition: dhcp_parsers.h:981
D2ClientConfigPtr parse(isc::data::ConstElementPtr d2_client_cfg)
Parses a given dhcp-ddns element into D2ClientConfig.
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:4549
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.
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.
Parser for the configuration of interfaces.
void parse(const CfgIfacePtr &config, const isc::data::ConstElementPtr &values)
Parses content of the "interfaces-config".
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)
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.
this class parses list of DHCP4 subnets
Definition: dhcp_parsers.h:641
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list)
parses contents of the list
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:26
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:780
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)
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:1234
const isc::log::MessageID DHCP4_PARSER_FAIL
std::vector< HostPtr > HostCollection
Collection of the Host objects.
Definition: host.h:810
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:850
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.