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