Kea  2.3.5-git
ctrl_dhcp6_srv.cc
Go to the documentation of this file.
1 // Copyright (C) 2014-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 <cc/data.h>
11 #include <cfgrpt/config_report.h>
12 #include <config/command_mgr.h>
13 #include <dhcp/libdhcp++.h>
14 #include <dhcp6/ctrl_dhcp6_srv.h>
15 #include <dhcp6/dhcp6_log.h>
16 #include <dhcp6/dhcp6to4_ipc.h>
18 #include <dhcp6/parser_context.h>
19 #include <dhcpsrv/cfg_db_access.h>
21 #include <dhcpsrv/cfgmgr.h>
22 #include <dhcpsrv/db_type.h>
24 #include <dhcpsrv/host_mgr.h>
25 #include <hooks/hooks.h>
26 #include <hooks/hooks_manager.h>
27 #include <stats/stats_mgr.h>
29 
30 #include <signal.h>
31 
32 #include <sstream>
33 
34 using namespace isc::asiolink;
35 using namespace isc::config;
36 using namespace isc::data;
37 using namespace isc::db;
38 using namespace isc::dhcp;
39 using namespace isc::hooks;
40 using namespace isc::stats;
41 using namespace isc::util;
42 using namespace std;
43 namespace ph = std::placeholders;
44 
45 namespace {
46 
48 struct CtrlDhcp6Hooks {
49  int hooks_index_dhcp6_srv_configured_;
50 
52  CtrlDhcp6Hooks() {
53  hooks_index_dhcp6_srv_configured_ = HooksManager::registerHook("dhcp6_srv_configured");
54  }
55 
56 };
57 
58 // Declare a Hooks object. As this is outside any function or method, it
59 // will be instantiated (and the constructor run) when the module is loaded.
60 // As a result, the hook indexes will be defined before any method in this
61 // module is called.
62 CtrlDhcp6Hooks Hooks;
63 
64 // Name of the file holding server identifier.
65 static const char* SERVER_DUID_FILE = "kea-dhcp6-serverid";
66 
76 void signalHandler(int signo) {
77  // SIGHUP signals a request to reconfigure the server.
78  if (signo == SIGHUP) {
79  ControlledDhcpv6Srv::processCommand("config-reload",
80  ConstElementPtr());
81  } else if ((signo == SIGTERM) || (signo == SIGINT)) {
82  ControlledDhcpv6Srv::processCommand("shutdown",
83  ConstElementPtr());
84  }
85 }
86 
87 }
88 
89 namespace isc {
90 namespace dhcp {
91 
92 ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
93 
95 ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
96  // This is a configuration backend implementation that reads the
97  // configuration from a JSON file.
98 
101 
102  // Basic sanity check: file name must not be empty.
103  try {
104  if (file_name.empty()) {
105  // Basic sanity check: file name must not be empty.
106  isc_throw(isc::BadValue, "JSON configuration file not specified. Please "
107  "use -c command line option.");
108  }
109 
110  // Read contents of the file and parse it as JSON
111  Parser6Context parser;
112  json = parser.parseFile(file_name, Parser6Context::PARSER_DHCP6);
113  if (!json) {
114  isc_throw(isc::BadValue, "no configuration found");
115  }
116 
117  // Let's do sanity check before we call json->get() which
118  // works only for map.
119  if (json->getType() != isc::data::Element::map) {
120  isc_throw(isc::BadValue, "Configuration file is expected to be "
121  "a map, i.e., start with { and end with } and contain "
122  "at least an entry called 'Dhcp6' that itself is a map. "
123  << file_name
124  << " is a valid JSON, but its top element is not a map."
125  " Did you forget to add { } around your configuration?");
126  }
127 
128  // Use parsed JSON structures to configure the server
129  result = ControlledDhcpv6Srv::processCommand("config-set", json);
130  if (!result) {
131  // Undetermined status of the configuration. This should never
132  // happen, but as the configureDhcp6Server returns a pointer, it is
133  // theoretically possible that it will return NULL.
134  isc_throw(isc::BadValue, "undefined result of "
135  "processCommand(\"config-set\", json)");
136  }
137 
138  // Now check is the returned result is successful (rcode=0) or not
139  // (see @ref isc::config::parseAnswer).
140  int rcode;
141  ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
142  if (rcode != CONTROL_RESULT_SUCCESS) {
143  string reason = comment ? comment->stringValue() :
144  "no details available";
145  isc_throw(isc::BadValue, reason);
146  }
147  } catch (const std::exception& ex) {
148  // If configuration failed at any stage, we drop the staging
149  // configuration and continue to use the previous one.
150  CfgMgr::instance().rollback();
151 
153  .arg(file_name).arg(ex.what());
154  isc_throw(isc::BadValue, "configuration error using file '"
155  << file_name << "': " << ex.what());
156  }
157 
159  .arg(MultiThreadingMgr::instance().getMode() ? "yes" : "no")
160  .arg(MultiThreadingMgr::instance().getThreadPoolSize())
161  .arg(MultiThreadingMgr::instance().getPacketQueueSize());
162 
163  return (result);
164 }
165 
166 void
167 ControlledDhcpv6Srv::init(const std::string& file_name) {
168  // Keep the call timestamp.
169  start_ = boost::posix_time::second_clock::universal_time();
170 
171  // Configure the server using JSON file.
172  ConstElementPtr result = loadConfigFile(file_name);
173 
174  int rcode;
175  ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
176  if (rcode != CONTROL_RESULT_SUCCESS) {
177  string reason = comment ? comment->stringValue() :
178  "no details available";
179  isc_throw(isc::BadValue, reason);
180  }
181 
182  // We don't need to call openActiveSockets() or startD2() as these
183  // methods are called in processConfig() which is called by
184  // processCommand("config-set", ...)
185 
186  // Set signal handlers. When the SIGHUP is received by the process
187  // the server reconfiguration will be triggered. When SIGTERM or
188  // SIGINT will be received, the server will start shutting down.
189  signal_set_.reset(new IOSignalSet(getIOService(), signalHandler));
190 
191  signal_set_->add(SIGINT);
192  signal_set_->add(SIGHUP);
193  signal_set_->add(SIGTERM);
194 }
195 
196 void ControlledDhcpv6Srv::cleanup() {
197  // Nothing to do here. No need to disconnect from anything.
198 }
199 
201 ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr args) {
202  if (!ControlledDhcpv6Srv::getInstance()) {
204  return (createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure."));
205  }
206 
207  int exit_value = 0;
208  if (args) {
209  // @todo Should we go ahead and shutdown even if the args are invalid?
210  if (args->getType() != Element::map) {
211  return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
212  }
213 
214  ConstElementPtr param = args->get("exit-value");
215  if (param) {
216  if (param->getType() != Element::integer) {
218  "parameter 'exit-value' is not an integer"));
219  }
220 
221  exit_value = param->intValue();
222  }
223  }
224 
225  ControlledDhcpv6Srv::getInstance()->shutdownServer(exit_value);
226  return (createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down."));
227 }
228 
230 ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
231  LOG_WARN(dhcp6_logger, DHCP6_DEPRECATED).arg("libreload command");
232 
233  // stop thread pool (if running)
235 
236  // Clear the packet queue.
237  MultiThreadingMgr::instance().getThreadPool().reset();
238 
239  try {
241  HookLibsCollection loaded = HooksManager::getLibraryInfo();
242  HooksManager::prepareUnloadLibraries();
243  static_cast<void>(HooksManager::unloadLibraries());
244  bool status = HooksManager::loadLibraries(loaded);
245  if (!status) {
246  isc_throw(Unexpected, "Failed to reload hooks libraries"
247  " (WARNING: libreload is deprecated).");
248  }
249  } catch (const std::exception& ex) {
251  ConstElementPtr answer = isc::config::createAnswer(1, ex.what());
252  return (answer);
253  }
255  "Hooks libraries successfully reloaded "
256  "(WARNING: libreload is deprecated).");
257  return (answer);
258 }
259 
261 ControlledDhcpv6Srv::commandConfigReloadHandler(const string&,
262  ConstElementPtr /*args*/) {
263  // Get configuration file name.
264  std::string file = ControlledDhcpv6Srv::getInstance()->getConfigFile();
265  try {
267  auto result = loadConfigFile(file);
269  return (result);
270  } catch (const std::exception& ex) {
271  // Log the unsuccessful reconfiguration. The reason for failure
272  // should be already logged. Don't rethrow an exception so as
273  // the server keeps working.
275  .arg(file);
277  "Config reload failed: " + string(ex.what())));
278  }
279 }
280 
282 ControlledDhcpv6Srv::commandConfigGetHandler(const string&,
283  ConstElementPtr /*args*/) {
284  ConstElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
285 
286  return (createAnswer(0, config));
287 }
288 
290 ControlledDhcpv6Srv::commandConfigWriteHandler(const string&,
291  ConstElementPtr args) {
292  string filename;
293 
294  if (args) {
295  if (args->getType() != Element::map) {
296  return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
297  }
298  ConstElementPtr filename_param = args->get("filename");
299  if (filename_param) {
300  if (filename_param->getType() != Element::string) {
302  "passed parameter 'filename' is not a string"));
303  }
304  filename = filename_param->stringValue();
305  }
306  }
307 
308  if (filename.empty()) {
309  // filename parameter was not specified, so let's use whatever we remember
310  // from the command-line
311  filename = getConfigFile();
312  }
313 
314  if (filename.empty()) {
315  return (createAnswer(CONTROL_RESULT_ERROR, "Unable to determine filename."
316  "Please specify filename explicitly."));
317  }
318 
319  // Ok, it's time to write the file.
320  size_t size = 0;
321  try {
322  ConstElementPtr cfg = CfgMgr::instance().getCurrentCfg()->toElement();
323  size = writeConfigFile(filename, cfg);
324  } catch (const isc::Exception& ex) {
325  return (createAnswer(CONTROL_RESULT_ERROR, string("Error during write-config:")
326  + ex.what()));
327  }
328  if (size == 0) {
329  return (createAnswer(CONTROL_RESULT_ERROR, "Error writing configuration to "
330  + filename));
331  }
332 
333  // Ok, it's time to return the successful response.
334  ElementPtr params = Element::createMap();
335  params->set("size", Element::create(static_cast<long long>(size)));
336  params->set("filename", Element::create(filename));
337 
338  return (createAnswer(CONTROL_RESULT_SUCCESS, "Configuration written to "
339  + filename + " successful", params));
340 }
341 
343 ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
344  ConstElementPtr args) {
345  const int status_code = CONTROL_RESULT_ERROR;
346  ConstElementPtr dhcp6;
347  string message;
348 
349  // Command arguments are expected to be:
350  // { "Dhcp6": { ... } }
351  if (!args) {
352  message = "Missing mandatory 'arguments' parameter.";
353  } else {
354  dhcp6 = args->get("Dhcp6");
355  if (!dhcp6) {
356  message = "Missing mandatory 'Dhcp6' parameter.";
357  } else if (dhcp6->getType() != Element::map) {
358  message = "'Dhcp6' parameter expected to be a map.";
359  }
360  }
361 
362  // Check unsupported objects.
363  if (message.empty()) {
364  for (auto obj : args->mapValue()) {
365  const string& obj_name = obj.first;
366  if (obj_name != "Dhcp6") {
368  .arg(obj_name);
369  if (message.empty()) {
370  message = "Unsupported '" + obj_name + "' parameter";
371  } else {
372  message += " (and '" + obj_name + "')";
373  }
374  }
375  }
376  if (!message.empty()) {
377  message += ".";
378  }
379  }
380 
381  if (!message.empty()) {
382  // Something is amiss with arguments, return a failure response.
383  ConstElementPtr result = isc::config::createAnswer(status_code,
384  message);
385  return (result);
386  }
387 
388  // stop thread pool (if running)
390 
391  // disable multi-threading (it will be applied by new configuration)
392  // this must be done in order to properly handle MT to ST transition
393  // when 'multi-threading' structure is missing from new config
394  MultiThreadingMgr::instance().apply(false, 0, 0);
395 
396  // We are starting the configuration process so we should remove any
397  // staging configuration that has been created during previous
398  // configuration attempts.
399  CfgMgr::instance().rollback();
400 
401  // Parse the logger configuration explicitly into the staging config.
402  // Note this does not alter the current loggers, they remain in
403  // effect until we apply the logging config below. If no logging
404  // is supplied logging will revert to default logging.
405  Daemon::configureLogger(dhcp6, CfgMgr::instance().getStagingCfg());
406 
407  // Let's apply the new logging. We do it early, so we'll be able to print
408  // out what exactly is wrong with the new config in case of problems.
409  CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
410 
411  // Now we configure the server proper.
412  ConstElementPtr result = processConfig(dhcp6);
413 
414  // If the configuration parsed successfully, apply the new logger
415  // configuration and the commit the new configuration. We apply
416  // the logging first in case there's a configuration failure.
417  int rcode = 0;
418  isc::config::parseAnswer(rcode, result);
419  if (rcode == CONTROL_RESULT_SUCCESS) {
420  CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
421 
422  // Use new configuration.
423  CfgMgr::instance().commit();
424  } else if (CfgMgr::instance().getCurrentCfg()->getSequence() != 0) {
425  // Ok, we applied the logging from the upcoming configuration, but
426  // there were problems with the config. As such, we need to back off
427  // and revert to the previous logging configuration. This is not done if
428  // sequence == 0, because that would mean always reverting to stdout by
429  // default, and it is arguably more helpful to have the error in a
430  // potential file or syslog configured in the upcoming configuration.
431  CfgMgr::instance().getCurrentCfg()->applyLoggingCfg();
432 
433  // Not initial configuration so someone can believe we reverted
434  // to the previous configuration. It is not the case so be clear
435  // about this.
437  }
438 
439  return (result);
440 }
441 
443 ControlledDhcpv6Srv::commandConfigTestHandler(const string&,
444  ConstElementPtr args) {
445  const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
446  ConstElementPtr dhcp6;
447  string message;
448 
449  // Command arguments are expected to be:
450  // { "Dhcp6": { ... } }
451  if (!args) {
452  message = "Missing mandatory 'arguments' parameter.";
453  } else {
454  dhcp6 = args->get("Dhcp6");
455  if (!dhcp6) {
456  message = "Missing mandatory 'Dhcp6' parameter.";
457  } else if (dhcp6->getType() != Element::map) {
458  message = "'Dhcp6' parameter expected to be a map.";
459  }
460  }
461 
462  // Check unsupported objects.
463  if (message.empty()) {
464  for (auto obj : args->mapValue()) {
465  const string& obj_name = obj.first;
466  if (obj_name != "Dhcp6") {
468  .arg(obj_name);
469  if (message.empty()) {
470  message = "Unsupported '" + obj_name + "' parameter";
471  } else {
472  message += " (and '" + obj_name + "')";
473  }
474  }
475  }
476  if (!message.empty()) {
477  message += ".";
478  }
479  }
480 
481  if (!message.empty()) {
482  // Something is amiss with arguments, return a failure response.
483  ConstElementPtr result = isc::config::createAnswer(status_code,
484  message);
485  return (result);
486  }
487 
488  // stop thread pool (if running)
490 
491  // We are starting the configuration process so we should remove any
492  // staging configuration that has been created during previous
493  // configuration attempts.
494  CfgMgr::instance().rollback();
495 
496  // Now we check the server proper.
497  return (checkConfig(dhcp6));
498 }
499 
501 ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
502  ConstElementPtr args) {
503  std::ostringstream message;
504  int64_t max_period = 0;
505  std::string origin;
506 
507  // If the args map does not contain 'origin' parameter, the default type
508  // will be used (user command).
509  NetworkState::Origin type = NetworkState::Origin::USER_COMMAND;
510 
511  // Parse arguments to see if the 'max-period' or 'origin' parameters have
512  // been specified.
513  if (args) {
514  // Arguments must be a map.
515  if (args->getType() != Element::map) {
516  message << "arguments for the 'dhcp-disable' command must be a map";
517 
518  } else {
519  ConstElementPtr max_period_element = args->get("max-period");
520  // max-period is optional.
521  if (max_period_element) {
522  // It must be an integer, if specified.
523  if (max_period_element->getType() != Element::integer) {
524  message << "'max-period' argument must be a number";
525 
526  } else {
527  // It must be positive integer.
528  max_period = max_period_element->intValue();
529  if (max_period <= 0) {
530  message << "'max-period' must be positive integer";
531  }
532  }
533  }
534  ConstElementPtr origin_element = args->get("origin");
535  // The 'origin' parameter is optional.
536  if (origin_element) {
537  // It must be a string, if specified.
538  if (origin_element->getType() != Element::string) {
539  message << "'origin' argument must be a string";
540 
541  } else {
542  origin = origin_element->stringValue();
543  if (origin == "ha-partner") {
544  type = NetworkState::Origin::HA_COMMAND;
545  } else if (origin != "user") {
546  if (origin.empty()) {
547  origin = "(empty string)";
548  }
549  message << "invalid value used for 'origin' parameter: "
550  << origin;
551  }
552  }
553  }
554  }
555  }
556 
557  // No error occurred, so let's disable the service.
558  if (message.tellp() == 0) {
559  message << "DHCPv6 service disabled";
560  if (max_period > 0) {
561  message << " for " << max_period << " seconds";
562 
563  // The user specified that the DHCP service should resume not
564  // later than in max-period seconds. If the 'dhcp-enable' command
565  // is not sent, the DHCP service will resume automatically.
566  network_state_->delayedEnableAll(static_cast<unsigned>(max_period),
567  type);
568  }
569  network_state_->disableService(type);
570 
571  // Success.
572  return (config::createAnswer(CONTROL_RESULT_SUCCESS, message.str()));
573  }
574 
575  // Failure.
576  return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
577 }
578 
580 ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&,
581  ConstElementPtr args) {
582  std::ostringstream message;
583  std::string origin;
584 
585  // If the args map does not contain 'origin' parameter, the default type
586  // will be used (user command).
587  NetworkState::Origin type = NetworkState::Origin::USER_COMMAND;
588 
589  // Parse arguments to see if the 'origin' parameter has been specified.
590  if (args) {
591  // Arguments must be a map.
592  if (args->getType() != Element::map) {
593  message << "arguments for the 'dhcp-enable' command must be a map";
594 
595  } else {
596  ConstElementPtr origin_element = args->get("origin");
597  // The 'origin' parameter is optional.
598  if (origin_element) {
599  // It must be a string, if specified.
600  if (origin_element->getType() != Element::string) {
601  message << "'origin' argument must be a string";
602 
603  } else {
604  origin = origin_element->stringValue();
605  if (origin == "ha-partner") {
606  type = NetworkState::Origin::HA_COMMAND;
607  } else if (origin != "user") {
608  if (origin.empty()) {
609  origin = "(empty string)";
610  }
611  message << "invalid value used for 'origin' parameter: "
612  << origin;
613  }
614  }
615  }
616  }
617  }
618 
619  // No error occurred, so let's enable the service.
620  if (message.tellp() == 0) {
621  network_state_->enableService(type);
622 
623  // Success.
625  "DHCP service successfully enabled"));
626  }
627 
628  // Failure.
629  return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
630 }
631 
633 ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
634  ElementPtr extended = Element::create(Dhcpv6Srv::getVersion(true));
635  ElementPtr arguments = Element::createMap();
636  arguments->set("extended", extended);
638  Dhcpv6Srv::getVersion(false),
639  arguments);
640  return (answer);
641 }
642 
644 ControlledDhcpv6Srv::commandBuildReportHandler(const string&,
645  ConstElementPtr) {
646  ConstElementPtr answer =
648  return (answer);
649 }
650 
652 ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&,
653  ConstElementPtr args) {
654  int status_code = CONTROL_RESULT_ERROR;
655  string message;
656 
657  // args must be { "remove": <bool> }
658  if (!args) {
659  message = "Missing mandatory 'remove' parameter.";
660  } else {
661  ConstElementPtr remove_name = args->get("remove");
662  if (!remove_name) {
663  message = "Missing mandatory 'remove' parameter.";
664  } else if (remove_name->getType() != Element::boolean) {
665  message = "'remove' parameter expected to be a boolean.";
666  } else {
667  bool remove_lease = remove_name->boolValue();
668  server_->alloc_engine_->reclaimExpiredLeases6(0, 0, remove_lease);
669  status_code = 0;
670  message = "Reclamation of expired leases is complete.";
671  }
672  }
673  ConstElementPtr answer = isc::config::createAnswer(status_code, message);
674  return (answer);
675 }
676 
678 ControlledDhcpv6Srv::commandServerTagGetHandler(const std::string&,
679  ConstElementPtr) {
680  const std::string& tag =
681  CfgMgr::instance().getCurrentCfg()->getServerTag();
682  ElementPtr response = Element::createMap();
683  response->set("server-tag", Element::create(tag));
684 
685  return (createAnswer(CONTROL_RESULT_SUCCESS, response));
686 }
687 
689 ControlledDhcpv6Srv::commandConfigBackendPullHandler(const std::string&,
690  ConstElementPtr) {
691  auto ctl_info = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo();
692  if (!ctl_info) {
693  return (createAnswer(CONTROL_RESULT_EMPTY, "No config backend."));
694  }
695 
696  // stop thread pool (if running)
698 
699  // Reschedule the periodic CB fetch.
700  if (TimerMgr::instance()->isTimerRegistered("Dhcp6CBFetchTimer")) {
701  TimerMgr::instance()->cancel("Dhcp6CBFetchTimer");
702  TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
703  }
704 
705  // Code from cbFetchUpdates.
706  // The configuration to use is the current one because this is called
707  // after the configuration manager commit.
708  try {
709  auto srv_cfg = CfgMgr::instance().getCurrentCfg();
710  auto mode = CBControlDHCPv6::FetchMode::FETCH_UPDATE;
711  server_->getCBControl()->databaseConfigFetch(srv_cfg, mode);
712  } catch (const std::exception& ex) {
714  .arg(ex.what());
716  "On demand configuration update failed: " +
717  string(ex.what())));
718  }
720  "On demand configuration update successful."));
721 }
722 
724 ControlledDhcpv6Srv::commandStatusGetHandler(const string&,
725  ConstElementPtr /*args*/) {
726  ElementPtr status = Element::createMap();
727  status->set("pid", Element::create(static_cast<int>(getpid())));
728 
729  auto now = boost::posix_time::second_clock::universal_time();
730  // Sanity check: start_ is always initialized.
731  if (!start_.is_not_a_date_time()) {
732  auto uptime = now - start_;
733  status->set("uptime", Element::create(uptime.total_seconds()));
734  }
735 
736  auto last_commit = CfgMgr::instance().getCurrentCfg()->getLastCommitTime();
737  if (!last_commit.is_not_a_date_time()) {
738  auto reload = now - last_commit;
739  status->set("reload", Element::create(reload.total_seconds()));
740  }
741 
742  auto& mt_mgr = MultiThreadingMgr::instance();
743  if (mt_mgr.getMode()) {
744  status->set("multi-threading-enabled", Element::create(true));
745  status->set("thread-pool-size", Element::create(static_cast<int32_t>(
746  MultiThreadingMgr::instance().getThreadPoolSize())));
747  status->set("packet-queue-size", Element::create(static_cast<int32_t>(
748  MultiThreadingMgr::instance().getPacketQueueSize())));
749  ElementPtr queue_stats = Element::createList();
750  queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(10)));
751  queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(100)));
752  queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(1000)));
753  status->set("packet-queue-statistics", queue_stats);
754 
755  } else {
756  status->set("multi-threading-enabled", Element::create(false));
757  }
758 
759  // Iterate through the interfaces and get all the errors.
760  ElementPtr socket_errors(Element::createList());
761  for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
762  for (std::string const& error : interface->getErrors()) {
763  socket_errors->add(Element::create(error));
764  }
765  }
766 
767  // Abstract the information from all sockets into a single status.
768  ElementPtr sockets(Element::createMap());
769  if (socket_errors->empty()) {
770  sockets->set("status", Element::create("ready"));
771  } else {
772  ReconnectCtlPtr const reconnect_ctl(
773  CfgMgr::instance().getCurrentCfg()->getCfgIface()->getReconnectCtl());
774  if (reconnect_ctl && reconnect_ctl->retriesLeft()) {
775  sockets->set("status", Element::create("retrying"));
776  } else {
777  sockets->set("status", Element::create("failed"));
778  }
779  sockets->set("errors", socket_errors);
780  }
781  status->set("sockets", sockets);
782 
783  return (createAnswer(0, status));
784 }
785 
787 ControlledDhcpv6Srv::commandStatisticSetMaxSampleCountAllHandler(const string&,
788  ConstElementPtr args) {
789  StatsMgr& stats_mgr = StatsMgr::instance();
790  ConstElementPtr answer = stats_mgr.statisticSetMaxSampleCountAllHandler(args);
791  // Update the default parameter.
792  long max_samples = stats_mgr.getMaxSampleCountDefault();
793  CfgMgr::instance().getCurrentCfg()->addConfiguredGlobal(
794  "statistic-default-sample-count", Element::create(max_samples));
795  return (answer);
796 }
797 
799 ControlledDhcpv6Srv::commandStatisticSetMaxSampleAgeAllHandler(const string&,
800  ConstElementPtr args) {
801  StatsMgr& stats_mgr = StatsMgr::instance();
802  ConstElementPtr answer = stats_mgr.statisticSetMaxSampleAgeAllHandler(args);
803  // Update the default parameter.
804  auto duration = stats_mgr.getMaxSampleAgeDefault();
805  long max_age = toSeconds(duration);
806  CfgMgr::instance().getCurrentCfg()->addConfiguredGlobal(
807  "statistic-default-sample-age", Element::create(max_age));
808  return (answer);
809 }
810 
812 ControlledDhcpv6Srv::processCommand(const string& command,
813  ConstElementPtr args) {
814  string txt = args ? args->str() : "(none)";
815 
817  .arg(command).arg(txt);
818 
819  ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
820 
821  if (!srv) {
823  "Server object not initialized, so can't process command '" +
824  command + "', arguments: '" + txt + "'.");
825  return (no_srv);
826  }
827 
828  try {
829  if (command == "shutdown") {
830  return (srv->commandShutdownHandler(command, args));
831 
832  } else if (command == "libreload") {
833  return (srv->commandLibReloadHandler(command, args));
834 
835  } else if (command == "config-reload") {
836  return (srv->commandConfigReloadHandler(command, args));
837 
838  } else if (command == "config-set") {
839  return (srv->commandConfigSetHandler(command, args));
840 
841  } else if (command == "config-get") {
842  return (srv->commandConfigGetHandler(command, args));
843 
844  } else if (command == "config-test") {
845  return (srv->commandConfigTestHandler(command, args));
846 
847  } else if (command == "dhcp-disable") {
848  return (srv->commandDhcpDisableHandler(command, args));
849 
850  } else if (command == "dhcp-enable") {
851  return (srv->commandDhcpEnableHandler(command, args));
852 
853  } else if (command == "version-get") {
854  return (srv->commandVersionGetHandler(command, args));
855 
856  } else if (command == "build-report") {
857  return (srv->commandBuildReportHandler(command, args));
858 
859  } else if (command == "leases-reclaim") {
860  return (srv->commandLeasesReclaimHandler(command, args));
861 
862  } else if (command == "config-write") {
863  return (srv->commandConfigWriteHandler(command, args));
864 
865  } else if (command == "server-tag-get") {
866  return (srv->commandServerTagGetHandler(command, args));
867 
868  } else if (command == "config-backend-pull") {
869  return (srv->commandConfigBackendPullHandler(command, args));
870 
871  } else if (command == "status-get") {
872  return (srv->commandStatusGetHandler(command, args));
873  }
874 
875  return (isc::config::createAnswer(1, "Unrecognized command:"
876  + command));
877 
878  } catch (const isc::Exception& ex) {
879  return (isc::config::createAnswer(1, "Error while processing command '"
880  + command + "': " + ex.what() +
881  ", params: '" + txt + "'"));
882  }
883 }
884 
886 ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
887 
888  ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
889 
890  // Single stream instance used in all error clauses
891  std::ostringstream err;
892 
893  if (!srv) {
894  err << "Server object not initialized, can't process config.";
895  return (isc::config::createAnswer(1, err.str()));
896  }
897 
899  .arg(srv->redactConfig(config)->str());
900 
901  ConstElementPtr answer = configureDhcp6Server(*srv, config);
902 
903  // Check that configuration was successful. If not, do not reopen sockets
904  // and don't bother with DDNS stuff.
905  try {
906  int rcode = 0;
907  isc::config::parseAnswer(rcode, answer);
908  if (rcode != 0) {
909  return (answer);
910  }
911  } catch (const std::exception& ex) {
912  err << "Failed to process configuration:" << ex.what();
913  return (isc::config::createAnswer(1, err.str()));
914  }
915 
916  // Re-open lease and host database with new parameters.
917  try {
918  DatabaseConnection::db_lost_callback_ =
919  std::bind(&ControlledDhcpv6Srv::dbLostCallback, srv, ph::_1);
920 
921  DatabaseConnection::db_recovered_callback_ =
922  std::bind(&ControlledDhcpv6Srv::dbRecoveredCallback, srv, ph::_1);
923 
924  DatabaseConnection::db_failed_callback_ =
925  std::bind(&ControlledDhcpv6Srv::dbFailedCallback, srv, ph::_1);
926 
927  CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
928  string params = "universe=6";
929  if (cfg_db->getExtendedInfoTablesEnabled()) {
930  params += " extended-info-tables=true";
931  }
932  cfg_db->setAppendedParameters(params);
933  cfg_db->createManagers();
934  // Reset counters related to connections as all managers have been recreated.
935  srv->getNetworkState()->reset(NetworkState::Origin::DB_CONNECTION);
936  } catch (const std::exception& ex) {
937  err << "Unable to open database: " << ex.what();
938  return (isc::config::createAnswer(1, err.str()));
939  }
940 
941  // Regenerate server identifier if needed.
942  try {
943  const std::string duid_file =
944  std::string(CfgMgr::instance().getDataDir()) + "/" +
945  std::string(SERVER_DUID_FILE);
946  DuidPtr duid = CfgMgr::instance().getStagingCfg()->getCfgDUID()->create(duid_file);
947  server_->serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
948  if (duid) {
950  .arg(duid->toText())
951  .arg(duid_file);
952  }
953 
954  } catch (const std::exception& ex) {
955  err << "unable to configure server identifier: " << ex.what();
956  return (isc::config::createAnswer(1, err.str()));
957  }
958 
959  // Server will start DDNS communications if its enabled.
960  try {
961  srv->startD2();
962  } catch (const std::exception& ex) {
963  err << "Error starting DHCP_DDNS client after server reconfiguration: "
964  << ex.what();
965  return (isc::config::createAnswer(1, err.str()));
966  }
967 
968  // Setup DHCPv4-over-DHCPv6 IPC
969  try {
970  Dhcp6to4Ipc::instance().open();
971  } catch (const std::exception& ex) {
972  err << "error starting DHCPv4-over-DHCPv6 IPC "
973  " after server reconfiguration: " << ex.what();
974  return (isc::config::createAnswer(1, err.str()));
975  }
976 
977  // Configure DHCP packet queueing
978  try {
980  qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
981  if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) {
983  .arg(IfaceMgr::instance().getPacketQueue6()->getInfoStr());
984  }
985 
986  } catch (const std::exception& ex) {
987  err << "Error setting packet queue controls after server reconfiguration: "
988  << ex.what();
989  return (isc::config::createAnswer(1, err.str()));
990  }
991 
992  // Configure a callback to shut down the server when the bind socket
993  // attempts exceeded.
994  CfgIface::open_sockets_failed_callback_ =
995  std::bind(&ControlledDhcpv6Srv::openSocketsFailedCallback, srv, ph::_1);
996 
997  // Configuration may change active interfaces. Therefore, we have to reopen
998  // sockets according to new configuration. It is possible that this
999  // operation will fail for some interfaces but the openSockets function
1000  // guards against exceptions and invokes a callback function to
1001  // log warnings. Since we allow that this fails for some interfaces there
1002  // is no need to rollback configuration if socket fails to open on any
1003  // of the interfaces.
1004  CfgMgr::instance().getStagingCfg()->getCfgIface()->
1005  openSockets(AF_INET6, srv->getServerPort());
1006 
1007  // Install the timers for handling leases reclamation.
1008  try {
1009  CfgMgr::instance().getStagingCfg()->getCfgExpiration()->
1010  setupTimers(&ControlledDhcpv6Srv::reclaimExpiredLeases,
1011  &ControlledDhcpv6Srv::deleteExpiredReclaimedLeases,
1012  server_);
1013 
1014  } catch (const std::exception& ex) {
1015  err << "unable to setup timers for periodically running the"
1016  " reclamation of the expired leases: "
1017  << ex.what() << ".";
1018  return (isc::config::createAnswer(1, err.str()));
1019  }
1020 
1021  // Setup config backend polling, if configured for it.
1022  auto ctl_info = CfgMgr::instance().getStagingCfg()->getConfigControlInfo();
1023  if (ctl_info) {
1024  long fetch_time = static_cast<long>(ctl_info->getConfigFetchWaitTime());
1025  // Only schedule the CB fetch timer if the fetch wait time is greater
1026  // than 0.
1027  if (fetch_time > 0) {
1028  // When we run unit tests, we want to use milliseconds unit for the
1029  // specified interval. Otherwise, we use seconds. Note that using
1030  // milliseconds as a unit in unit tests prevents us from waiting 1
1031  // second on more before the timer goes off. Instead, we wait one
1032  // millisecond which significantly reduces the test time.
1033  if (!server_->inTestMode()) {
1034  fetch_time = 1000 * fetch_time;
1035  }
1036 
1037  boost::shared_ptr<unsigned> failure_count(new unsigned(0));
1038  TimerMgr::instance()->
1039  registerTimer("Dhcp6CBFetchTimer",
1040  std::bind(&ControlledDhcpv6Srv::cbFetchUpdates,
1041  server_, CfgMgr::instance().getStagingCfg(),
1042  failure_count),
1043  fetch_time,
1045  TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
1046  }
1047  }
1048 
1049  // Finally, we can commit runtime option definitions in libdhcp++. This is
1050  // exception free.
1051  LibDHCP::commitRuntimeOptionDefs();
1052 
1053  // This hook point notifies hooks libraries that the configuration of the
1054  // DHCPv6 server has completed. It provides the hook library with the pointer
1055  // to the common IO service object, new server configuration in the JSON
1056  // format and with the pointer to the configuration storage where the
1057  // parsed configuration is stored.
1058  if (HooksManager::calloutsPresent(Hooks.hooks_index_dhcp6_srv_configured_)) {
1059  CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
1060 
1061  callout_handle->setArgument("io_context", srv->getIOService());
1062  callout_handle->setArgument("network_state", srv->getNetworkState());
1063  callout_handle->setArgument("json_config", config);
1064  callout_handle->setArgument("server_config", CfgMgr::instance().getStagingCfg());
1065 
1066  HooksManager::callCallouts(Hooks.hooks_index_dhcp6_srv_configured_,
1067  *callout_handle);
1068 
1069  // If next step is DROP, report a configuration error.
1070  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1071  string error;
1072  try {
1073  callout_handle->getArgument("error", error);
1074  } catch (NoSuchArgument const& ex) {
1075  error = "unknown error";
1076  }
1078  }
1079  }
1080 
1081  // Apply multi threading settings.
1082  // @note These settings are applied/updated only if no errors occur while
1083  // applying the new configuration.
1084  // @todo This should be fixed.
1085  try {
1086  CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
1087  } catch (const std::exception& ex) {
1088  err << "Error applying multi threading settings: "
1089  << ex.what();
1090  return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
1091  }
1092 
1093  return (answer);
1094 }
1095 
1097 ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) {
1098 
1100  .arg(redactConfig(config)->str());
1101 
1102  ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
1103 
1104  if (!srv) {
1106  "Server object not initialized, can't process config.");
1107  return (no_srv);
1108  }
1109 
1110  return (configureDhcp6Server(*srv, config, true));
1111 }
1112 
1113 ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port,
1114  uint16_t client_port)
1115  : Dhcpv6Srv(server_port, client_port), timer_mgr_(TimerMgr::instance()) {
1116  if (getInstance()) {
1118  "There is another Dhcpv6Srv instance already.");
1119  }
1120  server_ = this; // remember this instance for later use in handlers
1121 
1122  // TimerMgr uses IO service to run asynchronous timers.
1123  TimerMgr::instance()->setIOService(getIOService());
1124 
1125  // CommandMgr uses IO service to run asynchronous socket operations.
1126  CommandMgr::instance().setIOService(getIOService());
1127 
1128  // LeaseMgr uses IO service to run asynchronous timers.
1130 
1131  // HostMgr uses IO service to run asynchronous timers.
1133 
1134  // These are the commands always supported by the DHCPv6 server.
1135  // Please keep the list in alphabetic order.
1136  CommandMgr::instance().registerCommand("build-report",
1137  std::bind(&ControlledDhcpv6Srv::commandBuildReportHandler, this, ph::_1, ph::_2));
1138 
1139  CommandMgr::instance().registerCommand("config-backend-pull",
1140  std::bind(&ControlledDhcpv6Srv::commandConfigBackendPullHandler, this, ph::_1, ph::_2));
1141 
1142  CommandMgr::instance().registerCommand("config-get",
1143  std::bind(&ControlledDhcpv6Srv::commandConfigGetHandler, this, ph::_1, ph::_2));
1144 
1145  CommandMgr::instance().registerCommand("config-reload",
1146  std::bind(&ControlledDhcpv6Srv::commandConfigReloadHandler, this, ph::_1, ph::_2));
1147 
1148  CommandMgr::instance().registerCommand("config-test",
1149  std::bind(&ControlledDhcpv6Srv::commandConfigTestHandler, this, ph::_1, ph::_2));
1150 
1151  CommandMgr::instance().registerCommand("config-write",
1152  std::bind(&ControlledDhcpv6Srv::commandConfigWriteHandler, this, ph::_1, ph::_2));
1153 
1154  CommandMgr::instance().registerCommand("dhcp-disable",
1155  std::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, ph::_1, ph::_2));
1156 
1157  CommandMgr::instance().registerCommand("dhcp-enable",
1158  std::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, this, ph::_1, ph::_2));
1159 
1160  CommandMgr::instance().registerCommand("leases-reclaim",
1161  std::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, ph::_1, ph::_2));
1162 
1163  CommandMgr::instance().registerCommand("server-tag-get",
1164  std::bind(&ControlledDhcpv6Srv::commandServerTagGetHandler, this, ph::_1, ph::_2));
1165 
1166  CommandMgr::instance().registerCommand("libreload",
1167  std::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, ph::_1, ph::_2));
1168 
1169  CommandMgr::instance().registerCommand("config-set",
1170  std::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, ph::_1, ph::_2));
1171 
1172  CommandMgr::instance().registerCommand("shutdown",
1173  std::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, ph::_1, ph::_2));
1174 
1175  CommandMgr::instance().registerCommand("status-get",
1176  std::bind(&ControlledDhcpv6Srv::commandStatusGetHandler, this, ph::_1, ph::_2));
1177 
1178  CommandMgr::instance().registerCommand("version-get",
1179  std::bind(&ControlledDhcpv6Srv::commandVersionGetHandler, this, ph::_1, ph::_2));
1180 
1181  // Register statistic related commands
1182  CommandMgr::instance().registerCommand("statistic-get",
1183  std::bind(&StatsMgr::statisticGetHandler, ph::_1, ph::_2));
1184 
1185  CommandMgr::instance().registerCommand("statistic-get-all",
1186  std::bind(&StatsMgr::statisticGetAllHandler, ph::_1, ph::_2));
1187 
1188  CommandMgr::instance().registerCommand("statistic-reset",
1189  std::bind(&StatsMgr::statisticResetHandler, ph::_1, ph::_2));
1190 
1191  CommandMgr::instance().registerCommand("statistic-reset-all",
1192  std::bind(&StatsMgr::statisticResetAllHandler, ph::_1, ph::_2));
1193 
1194  CommandMgr::instance().registerCommand("statistic-remove",
1195  std::bind(&StatsMgr::statisticRemoveHandler, ph::_1, ph::_2));
1196 
1197  CommandMgr::instance().registerCommand("statistic-remove-all",
1198  std::bind(&StatsMgr::statisticRemoveAllHandler, ph::_1, ph::_2));
1199 
1200  CommandMgr::instance().registerCommand("statistic-sample-age-set",
1201  std::bind(&StatsMgr::statisticSetMaxSampleAgeHandler, ph::_1, ph::_2));
1202 
1203  CommandMgr::instance().registerCommand("statistic-sample-age-set-all",
1204  std::bind(&ControlledDhcpv6Srv::commandStatisticSetMaxSampleAgeAllHandler, this, ph::_1, ph::_2));
1205 
1206  CommandMgr::instance().registerCommand("statistic-sample-count-set",
1207  std::bind(&StatsMgr::statisticSetMaxSampleCountHandler, ph::_1, ph::_2));
1208 
1209  CommandMgr::instance().registerCommand("statistic-sample-count-set-all",
1210  std::bind(&ControlledDhcpv6Srv::commandStatisticSetMaxSampleCountAllHandler, this, ph::_1, ph::_2));
1211 }
1212 
1214  setExitValue(exit_value);
1215  getIOService()->stop(); // Stop ASIO transmissions
1216  shutdown(); // Initiate DHCPv6 shutdown procedure.
1217 }
1218 
1220  try {
1222  HostMgr::create();
1223  cleanup();
1224 
1225  // The closure captures either a shared pointer (memory leak)
1226  // or a raw pointer (pointing to a deleted object).
1230 
1231  timer_mgr_->unregisterTimers();
1232 
1233  // Close the command socket (if it exists).
1234  CommandMgr::instance().closeCommandSocket();
1235 
1236  // Deregister any registered commands (please keep in alphabetic order)
1237  CommandMgr::instance().deregisterCommand("build-report");
1238  CommandMgr::instance().deregisterCommand("config-backend-pull");
1239  CommandMgr::instance().deregisterCommand("config-get");
1240  CommandMgr::instance().deregisterCommand("config-reload");
1241  CommandMgr::instance().deregisterCommand("config-set");
1242  CommandMgr::instance().deregisterCommand("config-test");
1243  CommandMgr::instance().deregisterCommand("config-write");
1244  CommandMgr::instance().deregisterCommand("dhcp-disable");
1245  CommandMgr::instance().deregisterCommand("dhcp-enable");
1246  CommandMgr::instance().deregisterCommand("leases-reclaim");
1247  CommandMgr::instance().deregisterCommand("libreload");
1248  CommandMgr::instance().deregisterCommand("server-tag-get");
1249  CommandMgr::instance().deregisterCommand("shutdown");
1250  CommandMgr::instance().deregisterCommand("statistic-get");
1251  CommandMgr::instance().deregisterCommand("statistic-get-all");
1252  CommandMgr::instance().deregisterCommand("statistic-remove");
1253  CommandMgr::instance().deregisterCommand("statistic-remove-all");
1254  CommandMgr::instance().deregisterCommand("statistic-reset");
1255  CommandMgr::instance().deregisterCommand("statistic-reset-all");
1256  CommandMgr::instance().deregisterCommand("statistic-sample-age-set");
1257  CommandMgr::instance().deregisterCommand("statistic-sample-age-set-all");
1258  CommandMgr::instance().deregisterCommand("statistic-sample-count-set");
1259  CommandMgr::instance().deregisterCommand("statistic-sample-count-set-all");
1260  CommandMgr::instance().deregisterCommand("status-get");
1261  CommandMgr::instance().deregisterCommand("version-get");
1262 
1263  // LeaseMgr uses IO service to run asynchronous timers.
1265 
1266  // HostMgr uses IO service to run asynchronous timers.
1268  } catch (...) {
1269  // Don't want to throw exceptions from the destructor. The server
1270  // is shutting down anyway.
1271  ;
1272  }
1273 
1274  server_ = NULL; // forget this instance. There should be no callback anymore
1275  // at this stage anyway.
1276 }
1277 
1278 void
1279 ControlledDhcpv6Srv::reclaimExpiredLeases(const size_t max_leases,
1280  const uint16_t timeout,
1281  const bool remove_lease,
1282  const uint16_t max_unwarned_cycles) {
1283  try {
1284  server_->alloc_engine_->reclaimExpiredLeases6(max_leases, timeout,
1285  remove_lease,
1286  max_unwarned_cycles);
1287  } catch (const std::exception& ex) {
1289  .arg(ex.what());
1290  }
1291  // We're using the ONE_SHOT timer so there is a need to re-schedule it.
1293 }
1294 
1295 void
1296 ControlledDhcpv6Srv::deleteExpiredReclaimedLeases(const uint32_t secs) {
1297  server_->alloc_engine_->deleteExpiredReclaimedLeases6(secs);
1298  // We're using the ONE_SHOT timer so there is a need to re-schedule it.
1300 }
1301 
1302 bool
1303 ControlledDhcpv6Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
1304  if (!db_reconnect_ctl) {
1305  // This should never happen
1307  return (false);
1308  }
1309 
1310  // Disable service until the connection is recovered.
1311  if (db_reconnect_ctl->retriesLeft() == db_reconnect_ctl->maxRetries() &&
1312  db_reconnect_ctl->alterServiceState()) {
1314  }
1315 
1317 
1318  // If reconnect isn't enabled log it, initiate a shutdown if needed and
1319  // return false.
1320  if (!db_reconnect_ctl->retriesLeft() ||
1321  !db_reconnect_ctl->retryInterval()) {
1323  .arg(db_reconnect_ctl->retriesLeft())
1324  .arg(db_reconnect_ctl->retryInterval());
1325  if (db_reconnect_ctl->exitOnFailure()) {
1326  shutdownServer(EXIT_FAILURE);
1327  }
1328  return (false);
1329  }
1330 
1331  return (true);
1332 }
1333 
1334 bool
1335 ControlledDhcpv6Srv::dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
1336  if (!db_reconnect_ctl) {
1337  // This should never happen
1339  return (false);
1340  }
1341 
1342  // Enable service after the connection is recovered.
1343  if (db_reconnect_ctl->alterServiceState()) {
1345  }
1346 
1348 
1349  db_reconnect_ctl->resetRetries();
1350 
1351  return (true);
1352 }
1353 
1354 bool
1355 ControlledDhcpv6Srv::dbFailedCallback(ReconnectCtlPtr db_reconnect_ctl) {
1356  if (!db_reconnect_ctl) {
1357  // This should never happen
1359  return (false);
1360  }
1361 
1363  .arg(db_reconnect_ctl->maxRetries());
1364 
1365  if (db_reconnect_ctl->exitOnFailure()) {
1366  shutdownServer(EXIT_FAILURE);
1367  }
1368 
1369  return (true);
1370 }
1371 
1372 void
1373 ControlledDhcpv6Srv::openSocketsFailedCallback(ReconnectCtlPtr reconnect_ctl) {
1374  if (!reconnect_ctl) {
1375  // This should never happen
1377  return;
1378  }
1379 
1381  .arg(reconnect_ctl->maxRetries());
1382 
1383  if (reconnect_ctl->exitOnFailure()) {
1384  shutdownServer(EXIT_FAILURE);
1385  }
1386 }
1387 
1388 void
1389 ControlledDhcpv6Srv::cbFetchUpdates(const SrvConfigPtr& srv_cfg,
1390  boost::shared_ptr<unsigned> failure_count) {
1391  // stop thread pool (if running)
1393 
1394  try {
1395  // Fetch any configuration backend updates since our last fetch.
1396  server_->getCBControl()->databaseConfigFetch(srv_cfg,
1397  CBControlDHCPv6::FetchMode::FETCH_UPDATE);
1398  (*failure_count) = 0;
1399 
1400  } catch (const std::exception& ex) {
1402  .arg(ex.what());
1403 
1404  // We allow at most 10 consecutive failures after which we stop
1405  // making further attempts to fetch the configuration updates.
1406  // Let's return without re-scheduling the timer.
1407  if (++(*failure_count) > 10) {
1410  return;
1411  }
1412  }
1413 
1414  // Reschedule the timer to fetch new updates or re-try if
1415  // the previous attempt resulted in an error.
1416  if (TimerMgr::instance()->isTimerRegistered("Dhcp6CBFetchTimer")) {
1417  TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
1418  }
1419 }
1420 
1421 } // namespace dhcp
1422 } // namespace isc
RAII class creating a critical section.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
uint16_t getServerPort() const
Get UDP port on which server should listen.
Definition: dhcp6_srv.h:216
virtual void shutdownServer(int exit_value)
Initiates shutdown procedure for the whole DHCPv6 server.
static ControlledDhcpv6Srv * getInstance()
Returns pointer to the sole instance of Dhcpv6Srv.
virtual ~ControlledDhcpv6Srv()
Destructor.
static DbCallback db_lost_callback_
Optional callback function to invoke if an opened connection is lost.
const isc::log::MessageID DHCP6_USING_SERVERID
isc::data::ConstElementPtr configureDhcp6Server(Dhcpv6Srv &server, isc::data::ConstElementPtr config_set, bool check_only)
Configures DHCPv6 server.
const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
isc::data::ElementPtr parseFile(const std::string &filename, ParserType parser_type)
Run the parser on the file specified.
const isc::log::MessageID DHCP6_CB_PERIODIC_FETCH_UPDATES_RETRIES_EXHAUSTED
void shutdown() override
Instructs the server to shut down.
Definition: dhcp6_srv.cc:305
static void setIOService(const isc::asiolink::IOServicePtr &io_service)
Sets IO service to be used by the Host Manager.
Definition: host_mgr.h:638
CBControlDHCPv6Ptr getCBControl() const
Returns an object which controls access to the configuration backends.
Definition: dhcp6_srv.h:124
isc::data::ConstElementPtr statisticSetMaxSampleCountAllHandler(const isc::data::ConstElementPtr &params)
Handles statistic-sample-count-set-all command.
const isc::log::MessageID DHCP6_DYNAMIC_RECONFIGURATION_SUCCESS
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
static void destroy()
Destroy lease manager.
const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_FAIL
const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition: iface_mgr.h:487
Manages a pool of asynchronous interval timers.
Definition: timer_mgr.h:62
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
Definition: srv_config.h:1191
The network state is being altered by the DB connection recovery mechanics.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
STL namespace.
Evaluation context, an interface to the expression evaluation.
const isc::log::MessageID DHCP6_DYNAMIC_RECONFIGURATION
No such argument.
const isc::log::MessageID DHCP6_CB_ON_DEMAND_FETCH_UPDATES_FAIL
const isc::log::MessageID DHCP6_OPEN_SOCKETS_NO_RECONNECT_CTL
const isc::log::MessageID DHCP6_DB_RECONNECT_NO_DB_CTL
const isc::log::MessageID DHCP6_COMMAND_RECEIVED
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
static DbCallback db_failed_callback_
Optional callback function to invoke if an opened connection recovery failed.
Statistics Manager class.
std::vector< HookLibInfo > HookLibsCollection
A storage for information about hook libraries.
Definition: libinfo.h:31
const isc::log::MessageID DHCP6_DB_RECONNECT_DISABLED
const int CONTROL_RESULT_EMPTY
Status code indicating that the specified command was completed correctly, but failed to produce any ...
const isc::log::MessageID DHCP6_OPEN_SOCKETS_FAILED
NetworkStatePtr & getNetworkState()
Returns pointer to the network state used by the server.
Definition: dhcp6_srv.h:115
Origin
Origin of the network state transition.
Definition: network_state.h:84
const isc::log::MessageID DHCP6_DYNAMIC_RECONFIGURATION_FAIL
const isc::log::MessageID DHCP6_DEPRECATED
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
static const std::string FLUSH_RECLAIMED_TIMER_NAME
Name of the timer for flushing reclaimed leases.
const isc::log::MessageID DHCP6_CONFIG_UNSUPPORTED_OBJECT
const isc::log::MessageID DHCP6_CB_PERIODIC_FETCH_UPDATES_FAIL
Definition: edns.h:19
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
const int DBG_DHCP6_COMMAND
Debug level used to log receiving commands.
Definition: dhcp6_log.h:28
static void create()
Creates new instance of the HostMgr.
Definition: host_mgr.cc:43
A generic exception that is thrown when an unexpected error condition occurs.
static DbCallback db_recovered_callback_
Optional callback function to invoke if an opened connection recovery succeeded.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
Defines the Dhcp6to4Ipc class.
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition: dhcp6_srv.h:1190
const isc::log::MessageID DHCP6_DB_RECONNECT_LOST_CONNECTION
isc::data::ConstElementPtr redactConfig(isc::data::ConstElementPtr const &config)
Redact a configuration.
Definition: daemon.cc:256
uint32_t getMaxSampleCountDefault() const
Get default count limit.
ConstElementPtr redactConfig(ConstElementPtr const &element, list< string > const &json_path)
Redact a configuration.
This is a base class for exceptions thrown from the DNS library module.
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Defines the logger used by the top-level component of kea-lfc.
const isc::log::MessageID DHCP6_DB_RECONNECT_SUCCEEDED
void setExitValue(int value)
Sets the exit value.
Definition: daemon.h:227
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp6_srv.cc:4356
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
static const std::string RECLAIM_EXPIRED_TIMER_NAME
Name of the timer for reclaiming expired leases.
This file contains several functions and constants that are used for handling commands and responses ...
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition: dhcp6_srv.h:110
CtrlAgentHooks Hooks
const isc::log::MessageID DHCP6_CONFIG_UNRECOVERABLE_ERROR
A generic exception that is thrown if a function is called in a prohibited way.
const isc::log::MessageID DHCP6_MULTI_THREADING_INFO
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
void cleanup()
Performs cleanup, immediately before termination.
const isc::log::MessageID DHCP6_CONFIG_RECEIVED
isc::data::ConstElementPtr statisticSetMaxSampleAgeAllHandler(const isc::data::ConstElementPtr &params)
Handles statistic-sample-age-set-all command.
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition: dhcp6_srv.h:1182
std::string getConfigReport()
Definition: cfgrpt.cc:20
static void setIOService(const isc::asiolink::IOServicePtr &io_service)
Sets IO service to be used by the Lease Manager.
Definition: lease_mgr.h:788
const isc::log::MessageID DHCP6_DB_RECONNECT_FAILED
#define LOG_FATAL(LOGGER, MESSAGE)
Macro to conveniently test fatal output and log it.
Definition: macros.h:38
Controlled version of the DHCPv6 server.
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition: timer_mgr.cc:449
const StatsDuration & getMaxSampleAgeDefault() const
Get default duration limit.
DHCPv6 server service.
Definition: dhcp6_srv.h:66
const isc::log::MessageID DHCP6_NOT_RUNNING
isc::log::Logger dhcp6_logger(DHCP6_APP_LOGGER_NAME)
Base logger for DHCPv6 server.
Definition: dhcp6_log.h:88
long toSeconds(const StatsDuration &dur)
Returns the number of seconds in a duration.
Definition: observation.h:45
const isc::log::MessageID DHCP6_HOOKS_LIBS_RELOAD_FAIL