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