Kea  2.3.9
dhcp6/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 
9 #include <asiolink/io_address.h>
10 #include <cc/data.h>
11 #include <cc/command_interpreter.h>
12 #include <config/command_mgr.h>
14 #include <dhcp6/ctrl_dhcp6_srv.h>
15 #include <dhcp6/dhcp6_log.h>
16 #include <dhcp6/dhcp6_srv.h>
18 #include <dhcp/libdhcp++.h>
19 #include <dhcp/iface_mgr.h>
20 #include <dhcpsrv/cb_ctl_dhcp4.h>
22 #include <dhcpsrv/cfg_option.h>
23 #include <dhcpsrv/cfgmgr.h>
24 #include <dhcpsrv/db_type.h>
39 #include <dhcpsrv/pool.h>
40 #include <dhcpsrv/subnet.h>
41 #include <dhcpsrv/timer_mgr.h>
42 #include <hooks/hooks_manager.h>
43 #include <hooks/hooks_parser.h>
44 #include <log/logger_support.h>
46 #include <util/encode/hex.h>
48 #include <util/strutil.h>
49 #include <util/triplet.h>
50 
51 #include <boost/algorithm/string.hpp>
52 #include <boost/foreach.hpp>
53 #include <boost/lexical_cast.hpp>
54 #include <boost/scoped_ptr.hpp>
55 #include <boost/shared_ptr.hpp>
56 
57 #include <iostream>
58 #include <limits>
59 #include <map>
60 #include <netinet/in.h>
61 #include <vector>
62 
63 #include <stdint.h>
64 
65 using namespace std;
66 using namespace isc;
67 using namespace isc::data;
68 using namespace isc::dhcp;
69 using namespace isc::asiolink;
70 using namespace isc::hooks;
71 using namespace isc::process;
72 using namespace isc::config;
73 using namespace isc::util;
74 
75 namespace {
76 
81 void dirExists(const string& dir_path) {
82  struct stat statbuf;
83  if (stat(dir_path.c_str(), &statbuf) < 0) {
84  isc_throw(BadValue, "Bad directory '" << dir_path
85  << "': " << strerror(errno));
86  }
87  if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
88  isc_throw(BadValue, "'" << dir_path << "' is not a directory");
89  }
90 }
91 
100 class RSOOListConfigParser : public isc::data::SimpleParser {
101 public:
102 
110  void parse(const SrvConfigPtr& cfg, const isc::data::ConstElementPtr& value) {
111  try {
112  BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
113  std::string option_str = source_elem->stringValue();
114  // This option can be either code (integer) or name. Let's try code first
115  int64_t code = 0;
116  try {
117  code = boost::lexical_cast<int64_t>(option_str);
118  // Protect against the negative value and too high value.
119  if (code < 0) {
120  isc_throw(BadValue, "invalid option code value specified '"
121  << option_str << "', the option code must be a"
122  " non-negative value");
123 
124  } else if (code > std::numeric_limits<uint16_t>::max()) {
125  isc_throw(BadValue, "invalid option code value specified '"
126  << option_str << "', the option code must not be"
127  " greater than '" << std::numeric_limits<uint16_t>::max()
128  << "'");
129  }
130 
131  } catch (const boost::bad_lexical_cast &) {
132  // Oh well, it's not a number
133  }
134 
135  if (!code) {
136  const OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE,
137  option_str);
138  if (def) {
139  code = def->getCode();
140  } else {
141  isc_throw(BadValue, "unable to find option code for the "
142  " specified option name '" << option_str << "'"
143  " while parsing the list of enabled"
144  " relay-supplied-options");
145  }
146  }
147  cfg->getCfgRSOO()->enable(code);
148  }
149  } catch (const std::exception& ex) {
150  // Rethrow exception with the appended position of the parsed
151  // element.
152  isc_throw(DhcpConfigError, ex.what() << " (" << value->getPosition() << ")");
153  }
154  }
155 };
156 
165 class Dhcp6ConfigParser : public isc::data::SimpleParser {
166 public:
167 
182  void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
183 
184  // Set the data directory for server id file.
185  if (global->contains("data-directory")) {
186  CfgMgr::instance().setDataDir(getString(global, "data-directory"),
187  false);
188  }
189 
190  // Set the probation period for decline handling.
191  uint32_t probation_period =
192  getUint32(global, "decline-probation-period");
193  cfg->setDeclinePeriod(probation_period);
194 
195  // Set the DHCPv4-over-DHCPv6 interserver port.
196  uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
197  cfg->setDhcp4o6Port(dhcp4o6_port);
198 
199  // Set the global user context.
200  ConstElementPtr user_context = global->get("user-context");
201  if (user_context) {
202  cfg->setContext(user_context);
203  }
204 
205  // Set the server's logical name
206  std::string server_tag = getString(global, "server-tag");
207  cfg->setServerTag(server_tag);
208  }
209 
221  void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
222  // Set ip-reservations-unique flag.
223  bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
224  cfg->setIPReservationsUnique(ip_reservations_unique);
225  }
226 
233  void
234  copySubnets6(const CfgSubnets6Ptr& dest, const CfgSharedNetworks6Ptr& from) {
235 
236  if (!dest || !from) {
237  isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null");
238  }
239 
240  const SharedNetwork6Collection* networks = from->getAll();
241  if (!networks) {
242  // Nothing to copy. Technically, it should return a pointer to empty
243  // container, but let's handle null pointer as well.
244  return;
245  }
246 
247  // Let's go through all the networks one by one
248  for (auto net = networks->begin(); net != networks->end(); ++net) {
249 
250  // For each network go through all the subnets in it.
251  const Subnet6SimpleCollection* subnets = (*net)->getAllSubnets();
252  if (!subnets) {
253  // Shared network without subnets it weird, but we decided to
254  // accept such configurations.
255  continue;
256  }
257 
258  // For each subnet, add it to a list of regular subnets.
259  for (auto subnet = subnets->begin(); subnet != subnets->end(); ++subnet) {
260  dest->add(*subnet);
261  }
262  }
263  }
264 
273  void
274  sanityChecks(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
275 
277  cfg->sanityChecksLifetime("preferred-lifetime");
278  cfg->sanityChecksLifetime("valid-lifetime");
279 
281  const SharedNetwork6Collection* networks = cfg->getCfgSharedNetworks6()->getAll();
282  if (networks) {
283  sharedNetworksSanityChecks(*networks, global->get("shared-networks"));
284  }
285  }
286 
293  void
294  sharedNetworksSanityChecks(const SharedNetwork6Collection& networks,
295  ConstElementPtr json) {
296 
298  if (!json) {
299  // No json? That means that the shared-networks was never specified
300  // in the config.
301  return;
302  }
303 
304  // Used for names uniqueness checks.
305  std::set<string> names;
306 
307  // Let's go through all the networks one by one
308  for (auto net = networks.begin(); net != networks.end(); ++net) {
309  string txt;
310 
311  // Let's check if all subnets have either the same interface
312  // or don't have the interface specified at all.
313  string iface = (*net)->getIface();
314 
315  const Subnet6SimpleCollection* subnets = (*net)->getAllSubnets();
316  if (subnets) {
317 
318  bool rapid_commit = false;
319 
320  // For each subnet, add it to a list of regular subnets.
321  for (auto subnet = subnets->begin(); subnet != subnets->end(); ++subnet) {
322 
323  // Rapid commit must either be enabled or disabled in all subnets
324  // in the shared network.
325  if (subnet == subnets->begin()) {
326  // If this is the first subnet, remember the value.
327  rapid_commit = (*subnet)->getRapidCommit();
328  } else {
329  // Ok, this is the second or following subnets. The value
330  // must match what was set in the first subnet.
331  if (rapid_commit != (*subnet)->getRapidCommit()) {
332  isc_throw(DhcpConfigError, "All subnets in a shared network "
333  "must have the same rapid-commit value. Subnet "
334  << (*subnet)->toText()
335  << " has specified rapid-commit "
336  << ( (*subnet)->getRapidCommit() ? "true" : "false")
337  << ", but earlier subnet in the same shared-network"
338  << " or the shared-network itself used rapid-commit "
339  << (rapid_commit ? "true" : "false"));
340  }
341  }
342 
343  if (iface.empty()) {
344  iface = (*subnet)->getIface();
345  continue;
346  }
347 
348  if ((*subnet)->getIface().empty()) {
349  continue;
350  }
351 
352  if ((*subnet)->getIface() != iface) {
353  isc_throw(DhcpConfigError, "Subnet " << (*subnet)->toText()
354  << " has specified interface " << (*subnet)->getIface()
355  << ", but earlier subnet in the same shared-network"
356  << " or the shared-network itself used " << iface);
357  }
358 
359  // Let's collect the subnets in case we later find out the
360  // subnet doesn't have a mandatory name.
361  txt += (*subnet)->toText() + " ";
362  }
363  }
364 
365  // Next, let's check name of the shared network.
366  if ((*net)->getName().empty()) {
367  isc_throw(DhcpConfigError, "Shared-network with subnets "
368  << txt << " is missing mandatory 'name' parameter");
369  }
370 
371  // Is it unique?
372  if (names.find((*net)->getName()) != names.end()) {
373  isc_throw(DhcpConfigError, "A shared-network with "
374  "name " << (*net)->getName() << " defined twice.");
375  }
376  names.insert((*net)->getName());
377 
378  }
379  }
380 };
381 
382 } // anonymous namespace
383 
384 namespace isc {
385 namespace dhcp {
386 
395  // Get new socket configuration.
396  ConstElementPtr sock_cfg =
397  CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
398 
399  // Get current socket configuration.
400  ConstElementPtr current_sock_cfg =
401  CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();
402 
403  // Determine if the socket configuration has changed. It has if
404  // both old and new configuration is specified but respective
405  // data elements aren't equal.
406  bool sock_changed = (sock_cfg && current_sock_cfg &&
407  !sock_cfg->equals(*current_sock_cfg));
408 
409  // If the previous or new socket configuration doesn't exist or
410  // the new configuration differs from the old configuration we
411  // close the existing socket and open a new socket as appropriate.
412  // Note that closing an existing socket means the client will not
413  // receive the configuration result.
414  if (!sock_cfg || !current_sock_cfg || sock_changed) {
415  // Close the existing socket (if any).
417 
418  if (sock_cfg) {
419  // This will create a control socket and install the external
420  // socket in IfaceMgr. That socket will be monitored when
421  // Dhcp6Srv::receivePacket() calls IfaceMgr::receive6() and
422  // callback in CommandMgr will be called, if necessary.
424  }
425  }
426 }
427 
430  // Before starting any subnet operations, let's reset the subnet-id counter,
431  // so newly recreated configuration starts with first subnet-id equal 1.
432  Subnet::resetSubnetID();
433 
434  // Revert any runtime option definitions configured so far and not committed.
435  LibDHCP::revertRuntimeOptionDefs();
436  // Let's set empty container in case a user hasn't specified any configuration
437  // for option definitions. This is equivalent to committing empty container.
438  LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
439 
440  // Print the list of known backends.
441  HostDataSourceFactory::printRegistered();
442 
443  // Answer will hold the result.
444  ConstElementPtr answer;
445 
446  // Global parameter name in case of an error.
447  string parameter_name;
448  ElementPtr mutable_cfg;
449  SrvConfigPtr srv_config;
450  try {
451  // Get the staging configuration.
452  srv_config = CfgMgr::instance().getStagingCfg();
453 
454  // This is a way to convert ConstElementPtr to ElementPtr.
455  // We need a config that can be edited, because we will insert
456  // default values and will insert derived values as well.
457  mutable_cfg = boost::const_pointer_cast<Element>(config_set);
458 
459  // Relocate dhcp-ddns parameters that have moved to global scope.
460  // Rule is that a global value overrides the dhcp-ddns value, so
461  // we need to do this before we apply global defaults.
462  // Note this is done for backward compatibility.
463  srv_config->moveDdnsParams(mutable_cfg);
464 
465  // Move from reservation mode to new reservations flags.
466  // @todo add warning
467  BaseNetworkParser::moveReservationMode(mutable_cfg);
468 
469  // Set all default values if not specified by the user.
470  SimpleParser6::setAllDefaults(mutable_cfg);
471 
472  // And now derive (inherit) global parameters to subnets, if not specified.
473  SimpleParser6::deriveParameters(mutable_cfg);
474 
475  // In principle we could have the following code structured as a series
476  // of long if else if clauses. That would give a marginal performance
477  // boost, but would make the code less readable. We had serious issues
478  // with the parser code debugability, so I decided to keep it as a
479  // series of independent ifs.
480 
481  // This parser is used in several places.
482  Dhcp6ConfigParser global_parser;
483 
484  // Apply global options in the staging config, e.g. ip-reservations-unique
485  global_parser.parseEarly(srv_config, mutable_cfg);
486 
487  // Specific check for this global parameter.
488  ConstElementPtr data_directory = mutable_cfg->get("data-directory");
489  if (data_directory) {
490  parameter_name = "data-directory";
491  dirExists(data_directory->stringValue());
492  }
493 
494  // We need definitions first
495  ConstElementPtr option_defs = mutable_cfg->get("option-def");
496  if (option_defs) {
497  parameter_name = "option-def";
498  OptionDefListParser parser(AF_INET6);
499  CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
500  parser.parse(cfg_option_def, option_defs);
501  }
502 
503  ConstElementPtr option_datas = mutable_cfg->get("option-data");
504  if (option_datas) {
505  parameter_name = "option-data";
506  OptionDataListParser parser(AF_INET6);
507  CfgOptionPtr cfg_option = srv_config->getCfgOption();
508  parser.parse(cfg_option, option_datas);
509  }
510 
511  ConstElementPtr mac_sources = mutable_cfg->get("mac-sources");
512  if (mac_sources) {
513  parameter_name = "mac-sources";
515  CfgMACSource& mac_source = srv_config->getMACSources();
516  parser.parse(mac_source, mac_sources);
517  }
518 
519  ConstElementPtr control_socket = mutable_cfg->get("control-socket");
520  if (control_socket) {
521  parameter_name = "control-socket";
522  ControlSocketParser parser;
523  parser.parse(*srv_config, control_socket);
524  }
525 
526  ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
527  if (multi_threading) {
528  parameter_name = "multi-threading";
530  parser.parse(*srv_config, multi_threading);
531  }
532 
533  bool multi_threading_enabled = true;
534  uint32_t thread_count = 0;
535  uint32_t queue_size = 0;
536  CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
537  multi_threading_enabled, thread_count, queue_size);
538 
540  ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
541  if (queue_control) {
542  parameter_name = "dhcp-queue-control";
543  DHCPQueueControlParser parser;
544  srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
545  }
546 
548  ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
549  if (reservations_lookup_first) {
550  parameter_name = "reservations-lookup-first";
551  if (multi_threading_enabled) {
553  }
554  srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
555  }
556 
557  ConstElementPtr hr_identifiers =
558  mutable_cfg->get("host-reservation-identifiers");
559  if (hr_identifiers) {
560  parameter_name = "host-reservation-identifiers";
562  parser.parse(hr_identifiers);
563  }
564 
565  ConstElementPtr server_id = mutable_cfg->get("server-id");
566  if (server_id) {
567  parameter_name = "server-id";
568  DUIDConfigParser parser;
569  const CfgDUIDPtr& cfg = srv_config->getCfgDUID();
570  parser.parse(cfg, server_id);
571  }
572 
573  ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
574  if (ifaces_config) {
575  parameter_name = "interfaces-config";
576  IfacesConfigParser parser(AF_INET6, true);
577  CfgIfacePtr cfg_iface = srv_config->getCfgIface();
578  parser.parse(cfg_iface, ifaces_config);
579  }
580 
581  ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
582  if (sanity_checks) {
583  parameter_name = "sanity-checks";
584  SanityChecksParser parser;
585  parser.parse(*srv_config, sanity_checks);
586  }
587 
588  ConstElementPtr expiration_cfg =
589  mutable_cfg->get("expired-leases-processing");
590  if (expiration_cfg) {
591  parameter_name = "expired-leases-processing";
592  ExpirationConfigParser parser;
593  parser.parse(expiration_cfg);
594  }
595 
596  // The hooks-libraries configuration must be parsed after parsing
597  // multi-threading configuration so that libraries are checked
598  // for multi-threading compatibility.
599  ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
600  if (hooks_libraries) {
601  parameter_name = "hooks-libraries";
602  HooksLibrariesParser hooks_parser;
603  HooksConfig& libraries = srv_config->getHooksConfig();
604  hooks_parser.parse(libraries, hooks_libraries);
605  libraries.verifyLibraries(hooks_libraries->getPosition(),
606  multi_threading_enabled);
607  }
608 
609  // D2 client configuration.
610  D2ClientConfigPtr d2_client_cfg;
611 
612  // Legacy DhcpConfigParser stuff below.
613  ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
614  if (dhcp_ddns) {
615  parameter_name = "dhcp-ddns";
616  // Apply defaults
617  D2ClientConfigParser::setAllDefaults(dhcp_ddns);
618  D2ClientConfigParser parser;
619  d2_client_cfg = parser.parse(dhcp_ddns);
620  }
621 
622  ConstElementPtr client_classes = mutable_cfg->get("client-classes");
623  if (client_classes) {
624  parameter_name = "client-classes";
626  ClientClassDictionaryPtr dictionary =
627  parser.parse(client_classes, AF_INET6);
628  srv_config->setClientClassDictionary(dictionary);
629  }
630 
631  // Please move at the end when migration will be finished.
632  ConstElementPtr lease_database = mutable_cfg->get("lease-database");
633  if (lease_database) {
634  parameter_name = "lease-database";
635  db::DbAccessParser parser;
636  std::string access_string;
637  parser.parse(access_string, lease_database);
638  CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
639  cfg_db_access->setLeaseDbAccessString(access_string);
640  }
641 
642  ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
643  if (hosts_database) {
644  parameter_name = "hosts-database";
645  db::DbAccessParser parser;
646  std::string access_string;
647  parser.parse(access_string, hosts_database);
648  CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
649  cfg_db_access->setHostDbAccessString(access_string);
650  }
651 
652  ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
653  if (hosts_databases) {
654  parameter_name = "hosts-databases";
655  CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
656  for (auto it : hosts_databases->listValue()) {
657  db::DbAccessParser parser;
658  std::string access_string;
659  parser.parse(access_string, it);
660  cfg_db_access->setHostDbAccessString(access_string);
661  }
662  }
663 
664  // Keep relative orders of shared networks and subnets.
665  ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
666  if (shared_networks) {
667  parameter_name = "shared-networks";
674  CfgSharedNetworks6Ptr cfg = srv_config->getCfgSharedNetworks6();
675  parser.parse(cfg, shared_networks);
676 
677  // We also need to put the subnets it contains into normal
678  // subnets list.
679  global_parser.copySubnets6(srv_config->getCfgSubnets6(), cfg);
680  }
681 
682  ConstElementPtr subnet6 = mutable_cfg->get("subnet6");
683  if (subnet6) {
684  parameter_name = "subnet6";
685  Subnets6ListConfigParser subnets_parser;
686  // parse() returns number of subnets parsed. We may log it one day.
687  subnets_parser.parse(srv_config, subnet6);
688  }
689 
690  ConstElementPtr reservations = mutable_cfg->get("reservations");
691  if (reservations) {
692  parameter_name = "reservations";
693  HostCollection hosts;
695  parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
696  for (auto h = hosts.begin(); h != hosts.end(); ++h) {
697  srv_config->getCfgHosts()->add(*h);
698  }
699  }
700 
701  ConstElementPtr config_control = mutable_cfg->get("config-control");
702  if (config_control) {
703  parameter_name = "config-control";
704  ConfigControlParser parser;
705  ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
706  CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
707  }
708 
709  ConstElementPtr rsoo_list = mutable_cfg->get("relay-supplied-options");
710  if (rsoo_list) {
711  parameter_name = "relay-supplied-options";
712  RSOOListConfigParser parser;
713  parser.parse(srv_config, rsoo_list);
714  }
715 
716  ConstElementPtr compatibility = mutable_cfg->get("compatibility");
717  if (compatibility) {
718  for (auto kv : compatibility->mapValue()) {
719  if (!kv.second || (kv.second->getType() != Element::boolean)) {
721  "compatibility parameter values must be "
722  << "boolean (" << kv.first << " at "
723  << kv.second->getPosition() << ")");
724  }
725  if (kv.first == "lenient-option-parsing") {
726  CfgMgr::instance().getStagingCfg()->setLenientOptionParsing(
727  kv.second->boolValue());
728  } else {
730  "unsupported compatibility parameter: "
731  << kv.first << " (" << kv.second->getPosition()
732  << ")");
733  }
734  }
735  }
736 
737  // Make parsers grouping.
738  ConfigPair config_pair;
739  const std::map<std::string, ConstElementPtr>& values_map =
740  mutable_cfg->mapValue();
741 
742  BOOST_FOREACH(config_pair, values_map) {
743 
744  parameter_name = config_pair.first;
745 
746  // These are converted to SimpleParser and are handled already above.
747  if ((config_pair.first == "data-directory") ||
748  (config_pair.first == "option-def") ||
749  (config_pair.first == "option-data") ||
750  (config_pair.first == "mac-sources") ||
751  (config_pair.first == "control-socket") ||
752  (config_pair.first == "multi-threading") ||
753  (config_pair.first == "dhcp-queue-control") ||
754  (config_pair.first == "host-reservation-identifiers") ||
755  (config_pair.first == "server-id") ||
756  (config_pair.first == "interfaces-config") ||
757  (config_pair.first == "sanity-checks") ||
758  (config_pair.first == "expired-leases-processing") ||
759  (config_pair.first == "hooks-libraries") ||
760  (config_pair.first == "dhcp-ddns") ||
761  (config_pair.first == "client-classes") ||
762  (config_pair.first == "lease-database") ||
763  (config_pair.first == "hosts-database") ||
764  (config_pair.first == "hosts-databases") ||
765  (config_pair.first == "subnet6") ||
766  (config_pair.first == "shared-networks") ||
767  (config_pair.first == "reservations") ||
768  (config_pair.first == "config-control") ||
769  (config_pair.first == "relay-supplied-options") ||
770  (config_pair.first == "loggers") ||
771  (config_pair.first == "compatibility")) {
772  continue;
773  }
774 
775  // As of Kea 1.6.0 we have two ways of inheriting the global parameters.
776  // The old method is used in JSON configuration parsers when the global
777  // parameters are derived into the subnets and shared networks and are
778  // being treated as explicitly specified. The new way used by the config
779  // backend is the dynamic inheritance whereby each subnet and shared
780  // network uses a callback function to return global parameter if it
781  // is not specified at lower level. This callback uses configured globals.
782  // We deliberately include both default and explicitly specified globals
783  // so as the callback can access the appropriate global values regardless
784  // whether they are set to a default or other value.
785  if ( (config_pair.first == "renew-timer") ||
786  (config_pair.first == "rebind-timer") ||
787  (config_pair.first == "preferred-lifetime") ||
788  (config_pair.first == "min-preferred-lifetime") ||
789  (config_pair.first == "max-preferred-lifetime") ||
790  (config_pair.first == "valid-lifetime") ||
791  (config_pair.first == "min-valid-lifetime") ||
792  (config_pair.first == "max-valid-lifetime") ||
793  (config_pair.first == "decline-probation-period") ||
794  (config_pair.first == "dhcp4o6-port") ||
795  (config_pair.first == "server-tag") ||
796  (config_pair.first == "reservation-mode") ||
797  (config_pair.first == "reservations-global") ||
798  (config_pair.first == "reservations-in-subnet") ||
799  (config_pair.first == "reservations-out-of-pool") ||
800  (config_pair.first == "calculate-tee-times") ||
801  (config_pair.first == "t1-percent") ||
802  (config_pair.first == "t2-percent") ||
803  (config_pair.first == "cache-threshold") ||
804  (config_pair.first == "cache-max-age") ||
805  (config_pair.first == "hostname-char-set") ||
806  (config_pair.first == "hostname-char-replacement") ||
807  (config_pair.first == "ddns-send-updates") ||
808  (config_pair.first == "ddns-override-no-update") ||
809  (config_pair.first == "ddns-override-client-update") ||
810  (config_pair.first == "ddns-replace-client-name") ||
811  (config_pair.first == "ddns-generated-prefix") ||
812  (config_pair.first == "ddns-qualifying-suffix") ||
813  (config_pair.first == "ddns-update-on-renew") ||
814  (config_pair.first == "ddns-use-conflict-resolution") ||
815  (config_pair.first == "ddns-ttl-percent") ||
816  (config_pair.first == "store-extended-info") ||
817  (config_pair.first == "statistic-default-sample-count") ||
818  (config_pair.first == "statistic-default-sample-age") ||
819  (config_pair.first == "early-global-reservations-lookup") ||
820  (config_pair.first == "ip-reservations-unique") ||
821  (config_pair.first == "reservations-lookup-first") ||
822  (config_pair.first == "parked-packet-limit") ||
823  (config_pair.first == "allocator") ||
824  (config_pair.first == "pd-allocator") ) {
825  CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
826  config_pair.second);
827  continue;
828  }
829 
830  // Nothing to configure for the user-context.
831  if (config_pair.first == "user-context") {
832  continue;
833  }
834 
835  // If we got here, no code handled this parameter, so we bail out.
837  "unsupported global configuration parameter: " << config_pair.first
838  << " (" << config_pair.second->getPosition() << ")");
839  }
840 
841  // Reset parameter name.
842  parameter_name = "<post parsing>";
843 
844  // Apply global options in the staging config.
845  global_parser.parse(srv_config, mutable_cfg);
846 
847  // This method conducts final sanity checks and tweaks. In particular,
848  // it checks that there is no conflict between plain subnets and those
849  // defined as part of shared networks.
850  global_parser.sanityChecks(srv_config, mutable_cfg);
851 
852  // Validate D2 client configuration.
853  if (!d2_client_cfg) {
854  d2_client_cfg.reset(new D2ClientConfig());
855  }
856  d2_client_cfg->validateContents();
857  srv_config->setD2ClientConfig(d2_client_cfg);
858  } catch (const isc::Exception& ex) {
860  .arg(parameter_name).arg(ex.what());
862  } catch (...) {
863  // For things like bad_cast in boost::lexical_cast
864  LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(parameter_name);
865  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
866  "processing error");
867  }
868 
869  if (!answer) {
870  answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
871  "Control-socket, hook-libraries, and D2 configuration "
872  "were sanity checked, but not applied.");
873  }
874 
875  return (answer);
876 }
877 
880  bool check_only, bool extra_checks) {
881  if (!config_set) {
883  "Can't parse NULL config");
884  return (answer);
885  }
886 
888  .arg(server.redactConfig(config_set)->str());
889 
890  auto answer = processDhcp6Config(config_set);
891 
892  int status_code = CONTROL_RESULT_SUCCESS;
893  isc::config::parseAnswer(status_code, answer);
894 
895  SrvConfigPtr srv_config;
896 
897  if (status_code == CONTROL_RESULT_SUCCESS) {
898  if (check_only) {
899  if (extra_checks) {
900  // Re-open lease and host database with new parameters.
901  try {
902  // Get the staging configuration.
903  srv_config = CfgMgr::instance().getStagingCfg();
904 
905  CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
906  string params = "universe=6 persist=false";
907  if (cfg_db->getExtendedInfoTablesEnabled()) {
908  params += " extended-info-tables=true";
909  }
910  cfg_db->setAppendedParameters(params);
911  cfg_db->createManagers();
912  } catch (const std::exception& ex) {
913  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
914  status_code = CONTROL_RESULT_ERROR;
915  }
916 
917  if (status_code == CONTROL_RESULT_SUCCESS) {
918  std::ostringstream err;
919  // Configure DHCP packet queueing
920  try {
922  qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
923  if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) {
925  .arg(IfaceMgr::instance().getPacketQueue6()->getInfoStr());
926  }
927 
928  } catch (const std::exception& ex) {
929  err << "Error setting packet queue controls after server reconfiguration: "
930  << ex.what();
931  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
932  status_code = CONTROL_RESULT_ERROR;
933  }
934  }
935  }
936  } else {
937  string parameter_name;
938  ElementPtr mutable_cfg;
939 
940  // disable multi-threading (it will be applied by new configuration)
941  // this must be done in order to properly handle MT to ST transition
942  // when 'multi-threading' structure is missing from new config and
943  // to properly drop any task items stored in the thread pool which
944  // might reference some handles to loaded hooks, preventing them
945  // from being unloaded.
946  MultiThreadingMgr::instance().apply(false, 0, 0);
947 
948  // Close DHCP sockets and remove any existing timers.
949  IfaceMgr::instance().closeSockets();
950  TimerMgr::instance()->unregisterTimers();
951  server.discardPackets();
952  server.getCBControl()->reset();
953 
954  try {
955 
956  // Get the staging configuration.
957  srv_config = CfgMgr::instance().getStagingCfg();
958 
959  // This is a way to convert ConstElementPtr to ElementPtr.
960  // We need a config that can be edited, because we will insert
961  // default values and will insert derived values as well.
962  mutable_cfg = boost::const_pointer_cast<Element>(config_set);
963 
964  ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
965  if (ifaces_config) {
966  parameter_name = "interfaces-config";
967  IfacesConfigParser parser(AF_INET6, false);
968  CfgIfacePtr cfg_iface = srv_config->getCfgIface();
969  cfg_iface->reset();
970  parser.parse(cfg_iface, ifaces_config);
971  }
972  } catch (const isc::Exception& ex) {
974  .arg(parameter_name).arg(ex.what());
976  status_code = CONTROL_RESULT_ERROR;
977  } catch (...) {
978  // For things like bad_cast in boost::lexical_cast
979  LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(parameter_name);
980  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
981  " processing error");
982  status_code = CONTROL_RESULT_ERROR;
983  }
984  }
985  }
986 
987  // So far so good, there was no parsing error so let's commit the
988  // configuration. This will add created subnets and option values into
989  // the server's configuration.
990  // This operation should be exception safe but let's make sure.
991  if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
992  try {
993 
994  // Setup the command channel.
996  } catch (const isc::Exception& ex) {
999  status_code = CONTROL_RESULT_ERROR;
1000  } catch (...) {
1001  // For things like bad_cast in boost::lexical_cast
1003  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
1004  " parsing error");
1005  status_code = CONTROL_RESULT_ERROR;
1006  }
1007  }
1008 
1009  if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
1010  try {
1011  // No need to commit interface names as this is handled by the
1012  // CfgMgr::commit() function.
1013 
1014  // Apply the staged D2ClientConfig, used to be done by parser commit
1015  D2ClientConfigPtr cfg;
1016  cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
1017  CfgMgr::instance().setD2ClientConfig(cfg);
1018  } catch (const isc::Exception& ex) {
1021  status_code = CONTROL_RESULT_ERROR;
1022  } catch (...) {
1023  // For things like bad_cast in boost::lexical_cast
1025  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
1026  " parsing error");
1027  status_code = CONTROL_RESULT_ERROR;
1028  }
1029  }
1030 
1031  if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
1032  try {
1033  // This occurs last as if it succeeds, there is no easy way to
1034  // revert it. As a result, the failure to commit a subsequent
1035  // change causes problems when trying to roll back.
1036  HooksManager::prepareUnloadLibraries();
1037  static_cast<void>(HooksManager::unloadLibraries());
1038  const HooksConfig& libraries =
1039  CfgMgr::instance().getStagingCfg()->getHooksConfig();
1040  bool multi_threading_enabled = true;
1041  uint32_t thread_count = 0;
1042  uint32_t queue_size = 0;
1043  CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
1044  multi_threading_enabled, thread_count, queue_size);
1045  libraries.loadLibraries(multi_threading_enabled);
1046  } catch (const isc::Exception& ex) {
1049  status_code = CONTROL_RESULT_ERROR;
1050  } catch (...) {
1051  // For things like bad_cast in boost::lexical_cast
1053  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
1054  " parsing error");
1055  status_code = CONTROL_RESULT_ERROR;
1056  }
1057  }
1058 
1059  // Moved from the commit block to add the config backend indication.
1060  if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
1061  try {
1062  // If there are config backends, fetch and merge into staging config
1063  server.getCBControl()->databaseConfigFetch(srv_config,
1064  CBControlDHCPv6::FetchMode::FETCH_ALL);
1065  } catch (const isc::Exception& ex) {
1066  std::ostringstream err;
1067  err << "during update from config backend database: " << ex.what();
1069  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
1070  status_code = CONTROL_RESULT_ERROR;
1071  } catch (...) {
1072  // For things like bad_cast in boost::lexical_cast
1073  std::ostringstream err;
1074  err << "during update from config backend database: "
1075  << "undefined configuration parsing error";
1077  answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
1078  status_code = CONTROL_RESULT_ERROR;
1079  }
1080  }
1081 
1082  // Rollback changes as the configuration parsing failed.
1083  if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
1084  // Revert to original configuration of runtime option definitions
1085  // in the libdhcp++.
1086  LibDHCP::revertRuntimeOptionDefs();
1087 
1088  if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
1089  auto notify_libraries = ControlledDhcpv6Srv::finishConfigHookLibraries(config_set);
1090  if (notify_libraries) {
1091  return (notify_libraries);
1092  }
1093  }
1094 
1095  return (answer);
1096  }
1097 
1099  .arg(CfgMgr::instance().getStagingCfg()->
1100  getConfigSummary(SrvConfig::CFGSEL_ALL6));
1101 
1102  // Everything was fine. Configuration is successful.
1103  answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.");
1104  return (answer);
1105 }
1106 
1107 } // namespace dhcp
1108 } // 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.
Wrapper class that holds MAC/hardware address sources.
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".
Parser for server DUID configuration.
void parse(const CfgDUIDPtr &cfg, isc::data::ConstElementPtr duid_configuration)
Parses DUID configuration.
To be removed. Please use ConfigError instead.
DHCPv6 server service.
Definition: dhcp6_srv.h:66
CBControlDHCPv6Ptr getCBControl() const
Returns an object which controls access to the configuration backends.
Definition: dhcp6_srv.h:124
void discardPackets()
Discards parked packets Clears the packet parking lots of all packets.
Definition: dhcp6_srv.cc:4666
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 DHCPv6.
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".
parser for MAC/hardware acquisition sources
Definition: dhcp_parsers.h:195
void parse(CfgMACSource &mac_sources, isc::data::ConstElementPtr value)
parses parameters value
Definition: dhcp_parsers.cc:47
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 a list of DHCP6 subnets
Definition: dhcp_parsers.h:929
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 ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Logging initialization functions.
#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.
boost::shared_ptr< CfgDUID > CfgDUIDPtr
Pointer to the Non-const object.
Definition: cfg_duid.h:161
std::pair< std::string, isc::data::ConstElementPtr > ConfigPair
Combination of parameter name and configuration contents.
Definition: dhcp_parsers.h:175
const isc::log::MessageID DHCP6_PARSER_FAIL
const isc::log::MessageID DHCP6_PARSER_EXCEPTION
boost::shared_ptr< D2ClientConfig > D2ClientConfigPtr
Defines a pointer for D2ClientConfig instances.
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:780
boost::multi_index_container< SharedNetwork6Ptr, 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< SharedNetwork6, std::string, &SharedNetwork6::getName > >, 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 > > >> SharedNetwork6Collection
Multi index container holding shared networks.
isc::data::ConstElementPtr configureDhcp6Server(Dhcpv6Srv &server, isc::data::ConstElementPtr config_set, bool check_only, bool extra_checks)
Configure DHCPv6 server (Dhcpv6Srv) with a set of configuration values.
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
isc::data::ConstElementPtr processDhcp6Config(isc::data::ConstElementPtr config_set)
const int DBG_DHCP6_COMMAND
Debug level used to log receiving commands.
Definition: dhcp6_log.h:28
const isc::log::MessageID DHCP6_CONFIG_COMPLETE
boost::shared_ptr< CfgIface > CfgIfacePtr
A pointer to the CfgIface .
Definition: cfg_iface.h:501
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
Definition: srv_config.h:1234
boost::shared_ptr< CfgSubnets6 > CfgSubnets6Ptr
Non-const pointer.
Definition: cfg_subnets6.h:349
std::vector< HostPtr > HostCollection
Collection of the Host objects.
Definition: host.h:810
const isc::log::MessageID DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
boost::shared_ptr< CfgSharedNetworks6 > CfgSharedNetworks6Ptr
Pointer to the configuration of IPv6 shared networks.
boost::multi_index_container< Subnet6Ptr, 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 > > >> Subnet6SimpleCollection
A simple collection of Subnet6 objects.
Definition: subnet.h:930
const isc::log::MessageID DHCP6_PARSER_COMMIT_EXCEPTION
const isc::log::MessageID DHCP6_CONFIG_START
const isc::log::MessageID DHCP6_PARSER_COMMIT_FAIL
isc::log::Logger dhcp6_logger(DHCP6_APP_LOGGER_NAME)
Base logger for DHCPv6 server.
Definition: dhcp6_log.h:88
const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE
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.
#define DHCP6_OPTION_SPACE