Kea  1.5.0
ctrl_dhcp6_srv.cc
Go to the documentation of this file.
1 // Copyright (C) 2014-2018 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 #include <cc/data.h>
10 #include <config/command_mgr.h>
11 #include <dhcp/libdhcp++.h>
12 #include <dhcpsrv/cfgmgr.h>
13 #include <dhcpsrv/cfg_db_access.h>
14 #include <dhcp6/ctrl_dhcp6_srv.h>
15 #include <dhcp6/dhcp6to4_ipc.h>
16 #include <dhcp6/dhcp6_log.h>
18 #include <dhcp6/parser_context.h>
19 #include <hooks/hooks_manager.h>
20 #include <stats/stats_mgr.h>
21 #include <cfgrpt/config_report.h>
22 #include <signal.h>
23 #include <sstream>
24 
25 using namespace isc::config;
26 using namespace isc::db;
27 using namespace isc::dhcp;
28 using namespace isc::data;
29 using namespace isc::hooks;
30 using namespace isc::stats;
31 using namespace std;
32 
33 namespace {
34 
36 struct CtrlDhcp6Hooks {
37  int hooks_index_dhcp6_srv_configured_;
38 
40  CtrlDhcp6Hooks() {
41  hooks_index_dhcp6_srv_configured_ = HooksManager::registerHook("dhcp6_srv_configured");
42  }
43 
44 };
45 
46 // Declare a Hooks object. As this is outside any function or method, it
47 // will be instantiated (and the constructor run) when the module is loaded.
48 // As a result, the hook indexes will be defined before any method in this
49 // module is called.
50 CtrlDhcp6Hooks Hooks;
51 
52 // Name of the file holding server identifier.
53 static const char* SERVER_DUID_FILE = "kea-dhcp6-serverid";
54 
64 void signalHandler(int signo) {
65  // SIGHUP signals a request to reconfigure the server.
66  if (signo == SIGHUP) {
67  ControlledDhcpv6Srv::processCommand("config-reload",
68  ConstElementPtr());
69  } else if ((signo == SIGTERM) || (signo == SIGINT)) {
70  ControlledDhcpv6Srv::processCommand("shutdown",
71  ConstElementPtr());
72  }
73 }
74 
75 }
76 
77 namespace isc {
78 namespace dhcp {
79 
80 ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
81 
93 ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
94  // This is a configuration backend implementation that reads the
95  // configuration from a JSON file.
96 
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;
142  isc::config::parseAnswer(rcode, result);
143  if (rcode != 0) {
144  string reason = comment ? comment->stringValue() :
145  "no details available";
146  isc_throw(isc::BadValue, reason);
147  }
148  } catch (const std::exception& ex) {
149  // If configuration failed at any stage, we drop the staging
150  // configuration and continue to use the previous one.
151  CfgMgr::instance().rollback();
152 
153  LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL)
154  .arg(file_name).arg(ex.what());
155  isc_throw(isc::BadValue, "configuration error using file '"
156  << file_name << "': " << ex.what());
157  }
158 
159  return (result);
160 }
161 
162 
163 void
164 ControlledDhcpv6Srv::init(const std::string& file_name) {
165  // Configure the server using JSON file.
166  ConstElementPtr result = loadConfigFile(file_name);
167  int rcode;
168  ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
169  if (rcode != 0) {
170  string reason = comment ? comment->stringValue() :
171  "no details available";
172  isc_throw(isc::BadValue, reason);
173  }
174 
175  // We don't need to call openActiveSockets() or startD2() as these
176  // methods are called in processConfig() which is called by
177  // processCommand("config-set", ...)
178 
179  // Set signal handlers. When the SIGHUP is received by the process
180  // the server reconfiguration will be triggered. When SIGTERM or
181  // SIGINT will be received, the server will start shutting down.
182  signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM));
183  // Set the pointer to the handler function.
184  signal_handler_ = signalHandler;
185 }
186 
187 void ControlledDhcpv6Srv::cleanup() {
188  // Nothing to do here. No need to disconnect from anything.
189 }
190 
191 
193 ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr) {
194  if (ControlledDhcpv6Srv::server_) {
195  ControlledDhcpv6Srv::server_->shutdown();
196  } else {
197  LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING);
198  ConstElementPtr answer = isc::config::createAnswer(1, "Shutdown failure.");
199  return (answer);
200  }
201  ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down.");
202  return (answer);
203 }
204 
206 ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
209  HookLibsCollection loaded = HooksManager::getLibraryInfo();
210  bool status = HooksManager::loadLibraries(loaded);
211  if (!status) {
212  LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL);
214  "Failed to reload hooks libraries.");
215  return (answer);
216  }
218  "Hooks libraries successfully reloaded.");
219  return (answer);
220 }
221 
223 ControlledDhcpv6Srv::commandConfigReloadHandler(const string&,
224  ConstElementPtr /*args*/) {
225  // Get configuration file name.
226  std::string file = ControlledDhcpv6Srv::getInstance()->getConfigFile();
227  try {
228  LOG_INFO(dhcp6_logger, DHCP6_DYNAMIC_RECONFIGURATION).arg(file);
229  return (loadConfigFile(file));
230  } catch (const std::exception& ex) {
231  // Log the unsuccessful reconfiguration. The reason for failure
232  // should be already logged. Don't rethrow an exception so as
233  // the server keeps working.
234  LOG_ERROR(dhcp6_logger, DHCP6_DYNAMIC_RECONFIGURATION_FAIL)
235  .arg(file);
237  "Config reload failed:" + string(ex.what())));
238  }
239 }
240 
242 ControlledDhcpv6Srv::commandConfigGetHandler(const string&,
243  ConstElementPtr /*args*/) {
244  ConstElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
245 
246  return (createAnswer(0, config));
247 }
248 
250 ControlledDhcpv6Srv::commandConfigWriteHandler(const string&, ConstElementPtr args) {
251  string filename;
252 
253  if (args) {
254  if (args->getType() != Element::map) {
255  return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
256  }
257  ConstElementPtr filename_param = args->get("filename");
258  if (filename_param) {
259  if (filename_param->getType() != Element::string) {
261  "passed parameter 'filename' is not a string"));
262  }
263  filename = filename_param->stringValue();
264  }
265  }
266 
267  if (filename.empty()) {
268  // filename parameter was not specified, so let's use whatever we remember
269  // from the command-line
270  filename = getConfigFile();
271  }
272 
273  if (filename.empty()) {
274  return (createAnswer(CONTROL_RESULT_ERROR, "Unable to determine filename."
275  "Please specify filename explicitly."));
276  }
277 
278  // Ok, it's time to write the file.
279  size_t size = 0;
280  try {
281  ConstElementPtr cfg = CfgMgr::instance().getCurrentCfg()->toElement();
282  size = writeConfigFile(filename, cfg);
283  } catch (const isc::Exception& ex) {
284  return (createAnswer(CONTROL_RESULT_ERROR, string("Error during write-config:")
285  + ex.what()));
286  }
287  if (size == 0) {
288  return (createAnswer(CONTROL_RESULT_ERROR, "Error writing configuration to "
289  + filename));
290  }
291 
292  // Ok, it's time to return the successful response.
293  ElementPtr params = Element::createMap();
294  params->set("size", Element::create(static_cast<long long>(size)));
295  params->set("filename", Element::create(filename));
296 
297  return (createAnswer(CONTROL_RESULT_SUCCESS, "Configuration written to "
298  + filename + " successful", params));
299 }
300 
302 ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
303  ConstElementPtr args) {
304  const int status_code = CONTROL_RESULT_ERROR;
305  ConstElementPtr dhcp6;
306  string message;
307 
308  // Command arguments are expected to be:
309  // { "Dhcp6": { ... }, "Logging": { ... } }
310  // The Logging component is technically optional. If it's not supplied
311  // logging will revert to default logging.
312  if (!args) {
313  message = "Missing mandatory 'arguments' parameter.";
314  } else {
315  dhcp6 = args->get("Dhcp6");
316  if (!dhcp6) {
317  message = "Missing mandatory 'Dhcp6' parameter.";
318  } else if (dhcp6->getType() != Element::map) {
319  message = "'Dhcp6' parameter expected to be a map.";
320  }
321  }
322 
323  if (!message.empty()) {
324  // Something is amiss with arguments, return a failure response.
325  ConstElementPtr result = isc::config::createAnswer(status_code,
326  message);
327  return (result);
328  }
329 
330  // We are starting the configuration process so we should remove any
331  // staging configuration that has been created during previous
332  // configuration attempts.
333  CfgMgr::instance().rollback();
334 
335  // Logging is a sibling element and must be parsed explicitly.
336  // The call to configureLogger parses the given Logging element if
337  // not null, into the staging config. Note this does not alter the
338  // current loggers, they remain in effect until we apply the
339  // logging config below. If no logging is supplied logging will
340  // revert to default logging.
341  Daemon::configureLogger(args->get("Logging"),
342  CfgMgr::instance().getStagingCfg());
343 
344  // Let's apply the new logging. We do it early, so we'll be able to print
345  // out what exactly is wrong with the new socnfig in case of problems.
346  CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
347 
348  // Now we configure the server proper.
349  ConstElementPtr result = processConfig(dhcp6);
350 
351  // If the configuration parsed successfully, apply the new logger
352  // configuration and the commit the new configuration. We apply
353  // the logging first in case there's a configuration failure.
354  int rcode = 0;
355  isc::config::parseAnswer(rcode, result);
356  if (rcode == CONTROL_RESULT_SUCCESS) {
357  CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
358 
359  // Use new configuration.
360  CfgMgr::instance().commit();
361  } else {
362  // Ok, we applied the logging from the upcoming configuration, but
363  // there were problems with the config. As such, we need to back off
364  // and revert to the previous logging configuration.
365  CfgMgr::instance().getCurrentCfg()->applyLoggingCfg();
366  }
367 
368  return (result);
369 }
370 
372 ControlledDhcpv6Srv::commandConfigTestHandler(const string&,
373  ConstElementPtr args) {
374  const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
375  ConstElementPtr dhcp6;
376  string message;
377 
378  // Command arguments are expected to be:
379  // { "Dhcp6": { ... }, "Logging": { ... } }
380  // The Logging component is technically optional. If it's not supplied
381  // logging will revert to default logging.
382  if (!args) {
383  message = "Missing mandatory 'arguments' parameter.";
384  } else {
385  dhcp6 = args->get("Dhcp6");
386  if (!dhcp6) {
387  message = "Missing mandatory 'Dhcp6' parameter.";
388  } else if (dhcp6->getType() != Element::map) {
389  message = "'Dhcp6' parameter expected to be a map.";
390  }
391  }
392 
393  if (!message.empty()) {
394  // Something is amiss with arguments, return a failure response.
395  ConstElementPtr result = isc::config::createAnswer(status_code,
396  message);
397  return (result);
398  }
399 
400  // We are starting the configuration process so we should remove any
401  // staging configuration that has been created during previous
402  // configuration attempts.
403  CfgMgr::instance().rollback();
404 
405  // Now we check the server proper.
406  return (checkConfig(dhcp6));
407 }
408 
410 ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
411  ConstElementPtr args) {
412  std::ostringstream message;
413  int64_t max_period = 0;
414 
415  // Parse arguments to see if the 'max-period' parameter has been specified.
416  if (args) {
417  // Arguments must be a map.
418  if (args->getType() != Element::map) {
419  message << "arguments for the 'dhcp-disable' command must be a map";
420 
421  } else {
422  ConstElementPtr max_period_element = args->get("max-period");
423  // max-period is optional.
424  if (max_period_element) {
425  // It must be an integer, if specified.
426  if (max_period_element->getType() != Element::integer) {
427  message << "'max-period' argument must be a number";
428 
429  } else {
430  // It must be positive integer.
431  max_period = max_period_element->intValue();
432  if (max_period <= 0) {
433  message << "'max-period' must be positive integer";
434  }
435 
436  // The user specified that the DHCP service should resume not
437  // later than in max-period seconds. If the 'dhcp-enable' command
438  // is not sent, the DHCP service will resume automatically.
439  network_state_->delayedEnableAll(static_cast<unsigned>(max_period));
440  }
441  }
442  }
443  }
444 
445  // No error occurred, so let's disable the service.
446  if (message.tellp() == 0) {
447  network_state_->disableService();
448 
449  message << "DHCPv6 service disabled";
450  if (max_period > 0) {
451  message << " for " << max_period << " seconds";
452  }
453  // Success.
454  return (config::createAnswer(CONTROL_RESULT_SUCCESS, message.str()));
455  }
456 
457  // Failure.
458  return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
459 }
460 
462 ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&, ConstElementPtr) {
463  network_state_->enableService();
464  return (config::createAnswer(CONTROL_RESULT_SUCCESS, "DHCP service successfully enabled"));
465 }
466 
468 ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
469  ElementPtr extended = Element::create(Dhcpv6Srv::getVersion(true));
470  ElementPtr arguments = Element::createMap();
471  arguments->set("extended", extended);
473  Dhcpv6Srv::getVersion(false),
474  arguments);
475  return (answer);
476 }
477 
479 ControlledDhcpv6Srv::commandBuildReportHandler(const string&, ConstElementPtr) {
480  ConstElementPtr answer =
482  return (answer);
483 }
484 
486 ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&,
487  ConstElementPtr args) {
488  int status_code = 1;
489  string message;
490 
491  // args must be { "remove": <bool> }
492  if (!args) {
493  message = "Missing mandatory 'remove' parameter.";
494  } else {
495  ConstElementPtr remove_name = args->get("remove");
496  if (!remove_name) {
497  message = "Missing mandatory 'remove' parameter.";
498  } else if (remove_name->getType() != Element::boolean) {
499  message = "'remove' parameter expected to be a boolean.";
500  } else {
501  bool remove_lease = remove_name->boolValue();
502  server_->alloc_engine_->reclaimExpiredLeases6(0, 0, remove_lease);
503  status_code = 0;
504  message = "Reclamation of expired leases is complete.";
505  }
506  }
507  ConstElementPtr answer = isc::config::createAnswer(status_code, message);
508  return (answer);
509 }
510 
512 ControlledDhcpv6Srv::processCommand(const std::string& command,
514  string txt = args ? args->str() : "(none)";
515 
516  LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED)
517  .arg(command).arg(txt);
518 
519  ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
520 
521  if (!srv) {
523  "Server object not initialized, can't process command '" +
524  command + "', arguments: '" + txt + "'.");
525  return (no_srv);
526  }
527 
528  try {
529  if (command == "shutdown") {
530  return (srv->commandShutdownHandler(command, args));
531 
532  } else if (command == "libreload") {
533  return (srv->commandLibReloadHandler(command, args));
534 
535  } else if (command == "config-reload") {
536  return (srv->commandConfigReloadHandler(command, args));
537 
538  } else if (command == "config-set") {
539  return (srv->commandConfigSetHandler(command, args));
540 
541  } else if (command == "config-get") {
542  return (srv->commandConfigGetHandler(command, args));
543 
544  } else if (command == "config-test") {
545  return (srv->commandConfigTestHandler(command, args));
546 
547  } else if (command == "dhcp-disable") {
548  return (srv->commandDhcpDisableHandler(command, args));
549 
550  } else if (command == "dhcp-enable") {
551  return (srv->commandDhcpEnableHandler(command, args));
552 
553  } else if (command == "version-get") {
554  return (srv->commandVersionGetHandler(command, args));
555 
556  } else if (command == "build-report") {
557  return (srv->commandBuildReportHandler(command, args));
558 
559  } else if (command == "leases-reclaim") {
560  return (srv->commandLeasesReclaimHandler(command, args));
561 
562  } else if (command == "config-write") {
563  return (srv->commandConfigWriteHandler(command, args));
564 
565  }
566 
567  return (isc::config::createAnswer(1, "Unrecognized command:"
568  + command));
569 
570  } catch (const Exception& ex) {
571  return (isc::config::createAnswer(1, "Error while processing command '"
572  + command + "':" + ex.what()));
573  }
574 }
575 
577 ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
578 
579  LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_RECEIVED)
580  .arg(config->str());
581 
582  ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
583 
584  if (!srv) {
587  "Server object not initialized, can't process config.");
588  return (no_srv);
589  }
590 
591  ConstElementPtr answer = configureDhcp6Server(*srv, config);
592 
593  // Check that configuration was successful. If not, do not reopen sockets
594  // and don't bother with DDNS stuff.
595  try {
596  int rcode = 0;
597  isc::config::parseAnswer(rcode, answer);
598  if (rcode != 0) {
599  return (answer);
600  }
601  } catch (const std::exception& ex) {
602  return (isc::config::createAnswer(1, "Failed to process configuration:"
603  + string(ex.what())));
604  }
605 
606  // Re-open lease and host database with new parameters.
607  try {
608  DatabaseConnection::db_lost_callback =
609  boost::bind(&ControlledDhcpv6Srv::dbLostCallback, srv, _1);
610  CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
611  cfg_db->setAppendedParameters("universe=6");
612  cfg_db->createManagers();
613  } catch (const std::exception& ex) {
614  return (isc::config::createAnswer(1, "Unable to open database: "
615  + std::string(ex.what())));
616  }
617 
618  // Regenerate server identifier if needed.
619  try {
620  const std::string duid_file = CfgMgr::instance().getDataDir() + "/" +
621  std::string(SERVER_DUID_FILE);
622  DuidPtr duid = CfgMgr::instance().getStagingCfg()->getCfgDUID()->create(duid_file);
623  server_->serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
624  if (duid) {
625  LOG_INFO(dhcp6_logger, DHCP6_USING_SERVERID)
626  .arg(duid->toText())
627  .arg(duid_file);
628  }
629 
630  } catch (const std::exception& ex) {
631  std::ostringstream err;
632  err << "unable to configure server identifier: " << ex.what();
633  return (isc::config::createAnswer(1, err.str()));
634  }
635 
636  // Server will start DDNS communications if its enabled.
637  try {
638  srv->startD2();
639  } catch (const std::exception& ex) {
640  std::ostringstream err;
641  err << "error starting DHCP_DDNS client "
642  " after server reconfiguration: " << ex.what();
643  return (isc::config::createAnswer(1, err.str()));
644  }
645 
646  // Setup DHCPv4-over-DHCPv6 IPC
647  try {
648  Dhcp6to4Ipc::instance().open();
649  } catch (const std::exception& ex) {
650  std::ostringstream err;
651  err << "error starting DHCPv4-over-DHCPv6 IPC "
652  " after server reconfiguration: " << ex.what();
653  return (isc::config::createAnswer(1, err.str()));
654  }
655 
656  // Configure DHCP packet queueing
657  try {
659  qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
660  if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) {
661  LOG_INFO(dhcp6_logger, DHCP6_CONFIG_PACKET_QUEUE)
662  .arg(IfaceMgr::instance().getPacketQueue6()->getInfoStr());
663  }
664 
665  } catch (const std::exception& ex) {
666  std::ostringstream err;
667  err << "Error setting packet queue controls after server reconfiguration: "
668  << ex.what();
669  return (isc::config::createAnswer(1, err.str()));
670  }
671 
672  // Configuration may change active interfaces. Therefore, we have to reopen
673  // sockets according to new configuration. It is possible that this
674  // operation will fail for some interfaces but the openSockets function
675  // guards against exceptions and invokes a callback function to
676  // log warnings. Since we allow that this fails for some interfaces there
677  // is no need to rollback configuration if socket fails to open on any
678  // of the interfaces.
679  CfgMgr::instance().getStagingCfg()->getCfgIface()->openSockets(AF_INET6, srv->getPort());
680 
681  // Install the timers for handling leases reclamation.
682  try {
683  CfgMgr::instance().getStagingCfg()->getCfgExpiration()->
684  setupTimers(&ControlledDhcpv6Srv::reclaimExpiredLeases,
685  &ControlledDhcpv6Srv::deleteExpiredReclaimedLeases,
686  server_);
687 
688  } catch (const std::exception& ex) {
689  std::ostringstream err;
690  err << "unable to setup timers for periodically running the"
691  " reclamation of the expired leases: "
692  << ex.what() << ".";
693  return (isc::config::createAnswer(1, err.str()));
694  }
695 
696  // Finally, we can commit runtime option definitions in libdhcp++. This is
697  // exception free.
698  LibDHCP::commitRuntimeOptionDefs();
699 
700  // This hook point notifies hooks libraries that the configuration of the
701  // DHCPv6 server has completed. It provides the hook library with the pointer
702  // to the common IO service object, new server configuration in the JSON
703  // format and with the pointer to the configuration storage where the
704  // parsed configuration is stored.
705  if (HooksManager::calloutsPresent(Hooks.hooks_index_dhcp6_srv_configured_)) {
706  CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
707 
708  callout_handle->setArgument("io_context", srv->getIOService());
709  callout_handle->setArgument("network_state", srv->getNetworkState());
710  callout_handle->setArgument("json_config", config);
711  callout_handle->setArgument("server_config", CfgMgr::instance().getStagingCfg());
712 
713  HooksManager::callCallouts(Hooks.hooks_index_dhcp6_srv_configured_,
714  *callout_handle);
715 
716  // Ignore status code as none of them would have an effect on further
717  // operation.
718  }
719 
720  return (answer);
721 }
722 
724 ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) {
725 
726  LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_RECEIVED)
727  .arg(config->str());
728 
729  ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
730 
731  if (!srv) {
733  "Server object not initialized, can't process config.");
734  return (no_srv);
735  }
736 
737  return (configureDhcp6Server(*srv, config, true));
738 }
739 
740 ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port)
741  : Dhcpv6Srv(port), io_service_(), timer_mgr_(TimerMgr::instance()) {
742  if (server_) {
744  "There is another Dhcpv6Srv instance already.");
745  }
746  server_ = this; // remember this instance for use in callback
747 
748  // TimerMgr uses IO service to run asynchronous timers.
749  TimerMgr::instance()->setIOService(getIOService());
750 
751  // CommandMgr uses IO service to run asynchronous socket operations.
752  CommandMgr::instance().setIOService(getIOService());
753 
754  // These are the commands always supported by the DHCPv6 server.
755  // Please keep the list in alphabetic order.
756  CommandMgr::instance().registerCommand("build-report",
757  boost::bind(&ControlledDhcpv6Srv::commandBuildReportHandler, this, _1, _2));
758 
759  CommandMgr::instance().registerCommand("config-get",
760  boost::bind(&ControlledDhcpv6Srv::commandConfigGetHandler, this, _1, _2));
761 
762  CommandMgr::instance().registerCommand("config-reload",
763  boost::bind(&ControlledDhcpv6Srv::commandConfigReloadHandler, this, _1, _2));
764 
765  CommandMgr::instance().registerCommand("config-test",
766  boost::bind(&ControlledDhcpv6Srv::commandConfigTestHandler, this, _1, _2));
767 
768  CommandMgr::instance().registerCommand("config-write",
769  boost::bind(&ControlledDhcpv6Srv::commandConfigWriteHandler, this, _1, _2));
770 
771  CommandMgr::instance().registerCommand("dhcp-disable",
772  boost::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, _1, _2));
773 
774  CommandMgr::instance().registerCommand("dhcp-enable",
775  boost::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, this, _1, _2));
776 
777  CommandMgr::instance().registerCommand("leases-reclaim",
778  boost::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, _1, _2));
779 
780  CommandMgr::instance().registerCommand("libreload",
781  boost::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, _1, _2));
782 
783  CommandMgr::instance().registerCommand("config-set",
784  boost::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, _1, _2));
785 
786  CommandMgr::instance().registerCommand("shutdown",
787  boost::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, _1, _2));
788 
789  CommandMgr::instance().registerCommand("version-get",
790  boost::bind(&ControlledDhcpv6Srv::commandVersionGetHandler, this, _1, _2));
791 
792  // Register statistic related commands
793  CommandMgr::instance().registerCommand("statistic-get",
794  boost::bind(&StatsMgr::statisticGetHandler, _1, _2));
795 
796  CommandMgr::instance().registerCommand("statistic-get-all",
797  boost::bind(&StatsMgr::statisticGetAllHandler, _1, _2));
798 
799  CommandMgr::instance().registerCommand("statistic-reset",
800  boost::bind(&StatsMgr::statisticResetHandler, _1, _2));
801 
802  CommandMgr::instance().registerCommand("statistic-reset-all",
803  boost::bind(&StatsMgr::statisticResetAllHandler, _1, _2));
804 
805  CommandMgr::instance().registerCommand("statistic-remove",
806  boost::bind(&StatsMgr::statisticRemoveHandler, _1, _2));
807 
808  CommandMgr::instance().registerCommand("statistic-remove-all",
809  boost::bind(&StatsMgr::statisticRemoveAllHandler, _1, _2));
810 }
811 
813  io_service_.stop(); // Stop ASIO transmissions
814  Dhcpv6Srv::shutdown(); // Initiate DHCPv6 shutdown procedure.
815 }
816 
818  try {
819  cleanup();
820 
821  // The closure captures either a shared pointer (memory leak)
822  // or a raw pointer (pointing to a deleted object).
823  DatabaseConnection::db_lost_callback = 0;
824 
825  timer_mgr_->unregisterTimers();
826 
827  // Close the command socket (if it exists).
828  CommandMgr::instance().closeCommandSocket();
829 
830  // Deregister any registered commands (please keep in alphabetic order)
831  CommandMgr::instance().deregisterCommand("build-report");
832  CommandMgr::instance().deregisterCommand("config-get");
833  CommandMgr::instance().deregisterCommand("config-set");
834  CommandMgr::instance().deregisterCommand("config-reload");
835  CommandMgr::instance().deregisterCommand("config-test");
836  CommandMgr::instance().deregisterCommand("config-write");
837  CommandMgr::instance().deregisterCommand("dhcp-disable");
838  CommandMgr::instance().deregisterCommand("dhcp-enable");
839  CommandMgr::instance().deregisterCommand("leases-reclaim");
840  CommandMgr::instance().deregisterCommand("libreload");
841  CommandMgr::instance().deregisterCommand("shutdown");
842  CommandMgr::instance().deregisterCommand("statistic-get");
843  CommandMgr::instance().deregisterCommand("statistic-get-all");
844  CommandMgr::instance().deregisterCommand("statistic-remove");
845  CommandMgr::instance().deregisterCommand("statistic-remove-all");
846  CommandMgr::instance().deregisterCommand("statistic-reset");
847  CommandMgr::instance().deregisterCommand("statistic-reset-all");
848  CommandMgr::instance().deregisterCommand("version-get");
849 
850  } catch (...) {
851  // Don't want to throw exceptions from the destructor. The server
852  // is shutting down anyway.
853  ;
854  }
855 
856  server_ = NULL; // forget this instance. There should be no callback anymore
857  // at this stage anyway.
858 }
859 
860 void ControlledDhcpv6Srv::sessionReader(void) {
861  // Process one asio event. If there are more events, iface_mgr will call
862  // this callback more than once.
863  if (server_) {
864  server_->io_service_.run_one();
865  }
866 }
867 
868 void
869 ControlledDhcpv6Srv::reclaimExpiredLeases(const size_t max_leases,
870  const uint16_t timeout,
871  const bool remove_lease,
872  const uint16_t max_unwarned_cycles) {
873  server_->alloc_engine_->reclaimExpiredLeases6(max_leases, timeout,
874  remove_lease,
875  max_unwarned_cycles);
876  // We're using the ONE_SHOT timer so there is a need to re-schedule it.
878 }
879 
880 void
881 ControlledDhcpv6Srv::deleteExpiredReclaimedLeases(const uint32_t secs) {
882  server_->alloc_engine_->deleteExpiredReclaimedLeases6(secs);
883  // We're using the ONE_SHOT timer so there is a need to re-schedule it.
885 }
886 
887 void
888 ControlledDhcpv6Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
889  bool reopened = false;
890 
891  // Re-open lease and host database with new parameters.
892  try {
893  CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
894  cfg_db->createManagers();
895  reopened = true;
896  } catch (const std::exception& ex) {
897  LOG_ERROR(dhcp6_logger, DHCP6_DB_RECONNECT_ATTEMPT_FAILED).arg(ex.what());
898  }
899 
900  if (reopened) {
901  // Cancel the timer.
902  if (TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) {
903  TimerMgr::instance()->cancel("Dhcp6DbReconnectTimer"); }
904 
905  // Set network state to service enabled
906  network_state_->enableService();
907 
908  // Toss the reconnct control, we're done with it
909  db_reconnect_ctl.reset();
910  } else {
911  if (!db_reconnect_ctl->checkRetries()) {
912  LOG_ERROR(dhcp6_logger, DHCP6_DB_RECONNECT_RETRIES_EXHAUSTED)
913  .arg(db_reconnect_ctl->maxRetries());
914  shutdown();
915  return;
916  }
917 
918  LOG_INFO(dhcp6_logger, DHCP6_DB_RECONNECT_ATTEMPT_SCHEDULE)
919  .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
920  .arg(db_reconnect_ctl->maxRetries())
921  .arg(db_reconnect_ctl->retryInterval());
922 
923  if (!TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) {
924  TimerMgr::instance()->registerTimer("Dhcp6DbReconnectTimer",
925  boost::bind(&ControlledDhcpv6Srv::dbReconnect, this,
926  db_reconnect_ctl),
927  db_reconnect_ctl->retryInterval(),
929  }
930 
931  TimerMgr::instance()->setup("Dhcp6DbReconnectTimer");
932  }
933 }
934 
935 bool
936 ControlledDhcpv6Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
937  // Disable service until we recover
938  network_state_->disableService();
939 
940  if (!db_reconnect_ctl) {
941  // This shouldn't never happen
942  LOG_ERROR(dhcp6_logger, DHCP6_DB_RECONNECT_NO_DB_CTL);
943  return (false);
944  }
945 
946  // If reconnect isn't enabled, log it and return false
947  if (!db_reconnect_ctl->retriesLeft() ||
948  !db_reconnect_ctl->retryInterval()) {
949  LOG_INFO(dhcp6_logger, DHCP6_DB_RECONNECT_DISABLED)
950  .arg(db_reconnect_ctl->retriesLeft())
951  .arg(db_reconnect_ctl->retryInterval());
952  return(false);
953  }
954 
955  // Invoke reconnect method
956  dbReconnect(db_reconnect_ctl);
957 
958  return(true);
959 }
960 
961 }; // end of isc::dhcp namespace
962 }; // end of isc namespace
dhcp6to4_ipc.h
command_mgr.h
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
isc::dhcp::CfgMgr::instance
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
isc::config::CONTROL_RESULT_SUCCESS
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
Definition: command_interpreter.h:39
isc::db
Definition: cql_connection.cc:29
json_config_parser.h
libdhcp++.h
isc::hooks::CalloutHandlePtr
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
Definition: callout_handle.h:416
isc::dhcp::DuidPtr
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
isc::config::createAnswer
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
Definition: command_interpreter.cc:33
config_report.h
isc::dhcp::Parser6Context
Evaluation context, an interface to the expression evaluation.
Definition: dhcp6/parser_context.h:37
isc::config
Definition: command_interpreter.cc:23
isc::dhcp::dhcp6_logger
isc::log::Logger dhcp6_logger(DHCP6_APP_LOGGER_NAME)
Base logger for DHCPv6 server.
Definition: dhcp6_log.h:87
hooks_manager.h
isc::config::CONTROL_RESULT_ERROR
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
Definition: command_interpreter.h:42
isc::data
Definition: cfg_to_element.h:25
isc::dhcp::Dhcpv6Srv::shutdown
void shutdown()
Instructs the server to shut down.
Definition: dhcp6_srv.cc:245
isc::dhcp::DBG_DHCP6_COMMAND
const int DBG_DHCP6_COMMAND
Debug level used to log receiving commands.
Definition: dhcp6_log.h:27
Hooks
Dhcp4Hooks Hooks
Definition: dhcp4_srv.cc:117
isc::dhcp::CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME
static const std::string FLUSH_RECLAIMED_TIMER_NAME
Name of the timer for flushing reclaimed leases.
Definition: cfg_expiration.h:117
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition: exceptions/exceptions.h:23
isc::dhcp::Dhcpv6Srv::alloc_engine_
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition: dhcp6_srv.h:985
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::dhcp::TimerMgr
Manages a pool of asynchronous interval timers.
Definition: timer_mgr.h:54
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition: exceptions/exceptions.cc:32
isc::dhcp::Parser6Context::parseFile
isc::data::ElementPtr parseFile(const std::string &filename, ParserType parser_type)
Run the parser on the file specified.
Definition: dhcp6/parser_context.cc:37
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
isc::InvalidOperation
A generic exception that is thrown if a function is called in a prohibited way.
Definition: exceptions/exceptions.h:143
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
command_interpreter.h
isc::dhcp::TimerMgr::instance
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition: timer_mgr.cc:319
isc::asiodns::logger
isc::log::Logger logger("asiodns")
Use the ASIO logger.
Definition: asiodns/logger.h:15
dhcp6_log.h
D6O_SERVERID
@ D6O_SERVERID
Definition: dhcp6.h:22
isc::dhcp
Definition: ctrl_dhcp4_srv.cc:75
isc::dhcp::ControlledDhcpv6Srv::~ControlledDhcpv6Srv
virtual ~ControlledDhcpv6Srv()
Destructor.
Definition: ctrl_dhcp6_srv.cc:817
isc::dhcp::ControlledDhcpv6Srv
Controlled version of the DHCPv6 server.
Definition: ctrl_dhcp6_srv.h:25
isc::dhcp::Dhcpv6Srv::network_state_
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition: dhcp6_srv.h:993
isc::dhcp::Dhcpv6Srv::startD2
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp6_srv.cc:3618
isc::stats
Definition: context.cc:13
cfg_db_access.h
LOG_WARN
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
stats_mgr.h
isc::dhcp::CfgExpiration::RECLAIM_EXPIRED_TIMER_NAME
static const std::string RECLAIM_EXPIRED_TIMER_NAME
Name of the timer for reclaiming expired leases.
Definition: cfg_expiration.h:114
isc::dhcp::ControlledDhcpv6Srv::shutdown
void shutdown()
Initiates shutdown procedure for the whole DHCPv6 server.
Definition: ctrl_dhcp6_srv.cc:812
parser_context.h
isc::dhcp::ControlledDhcpv6Srv::cleanup
void cleanup()
Performs cleanup, immediately before termination.
Definition: ctrl_dhcp6_srv.cc:187
isc::dhcp::Dhcpv6Srv::getIOService
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition: dhcp6_srv.h:90
isc::config::parseAnswer
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Definition: command_interpreter.cc:68
cfgmgr.h
isc::dhcp::Dhcpv6Srv
DHCPv6 server service.
Definition: dhcp6_srv.h:59
isc::dhcp::CfgMgr::getCurrentCfg
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:154
isc::data::Element::map
@ map
Definition: data.h:151
data.h
isc::detail::getConfigReport
std::string getConfigReport()
Definition: cfgrpt.cc:20
isc::hooks
Definition: callout_handle.cc:21
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
isc::db::ReconnectCtlPtr
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
Definition: database_connection.h:130
isc::dhcp::CfgDbAccessPtr
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
Definition: cfg_db_access.h:101
isc::hooks::HookLibsCollection
std::vector< HookLibInfo > HookLibsCollection
A storage for information about hook libraries.
Definition: libinfo.h:31
isc::dhcp::Option
Definition: option.h:58
isc::dhcp::Dhcpv6Srv::getPort
uint16_t getPort() const
Get UDP port on which server should listen.
Definition: dhcp6_srv.h:141
isc::util::SignalSet
Represents a collection of signals handled in a customized way.
Definition: signal_set.h:87
ctrl_dhcp6_srv.h
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
isc::dhcp::Dhcpv6Srv::getNetworkState
NetworkStatePtr & getNetworkState()
Returns pointer to the network state used by the server.
Definition: dhcp6_srv.h:95
isc::dhcp::configureDhcp6Server
isc::data::ConstElementPtr configureDhcp6Server(Dhcpv6Srv &server, isc::data::ConstElementPtr config_set, bool check_only)
Configures DHCPv6 server.
Definition: dhcp6/json_config_parser.cc:380