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