Kea 1.5.0
ctrl_dhcp4_srv.cc
Go to the documentation of this file.
1// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8#include <cc/data.h>
10#include <config/command_mgr.h>
12#include <dhcp4/dhcp4_log.h>
13#include <dhcp4/dhcp4to6_ipc.h>
16#include <dhcpsrv/cfgmgr.h>
18#include <hooks/hooks.h>
19#include <hooks/hooks_manager.h>
20#include <stats/stats_mgr.h>
22#include <signal.h>
23#include <sstream>
24
25using namespace isc::data;
26using namespace isc::db;
27using namespace isc::dhcp;
28using namespace isc::hooks;
29using namespace isc::config;
30using namespace isc::stats;
31using namespace std;
32
33namespace {
34
36struct CtrlDhcp4Hooks {
37 int hooks_index_dhcp4_srv_configured_;
38
40 CtrlDhcp4Hooks() {
41 hooks_index_dhcp4_srv_configured_ = HooksManager::registerHook("dhcp4_srv_configured");
42 }
43
44};
45
46// Declare a Hooks object. As this is outside any function or method, it
47// will be instantiated (and the constructor run) when the module is loaded.
48// As a result, the hook indexes will be defined before any method in this
49// module is called.
50CtrlDhcp4Hooks Hooks;
51
61void signalHandler(int signo) {
62 // SIGHUP signals a request to reconfigure the server.
63 if (signo == SIGHUP) {
66 } else if ((signo == SIGTERM) || (signo == SIGINT)) {
69 }
70}
71
72}
73
74namespace isc {
75namespace dhcp {
76
77ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL;
78
79void
80ControlledDhcpv4Srv::init(const std::string& file_name) {
81 // Configure the server using JSON file.
82 ConstElementPtr result = loadConfigFile(file_name);
83 int rcode;
84 ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
85 if (rcode != 0) {
86 string reason = comment ? comment->stringValue() :
87 "no details available";
88 isc_throw(isc::BadValue, reason);
89 }
90
91 // We don't need to call openActiveSockets() or startD2() as these
92 // methods are called in processConfig() which is called by
93 // processCommand("config-set", ...)
94
95 // Set signal handlers. When the SIGHUP is received by the process
96 // the server reconfiguration will be triggered. When SIGTERM or
97 // SIGINT will be received, the server will start shutting down.
98 signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM));
99 // Set the pointer to the handler function.
100 signal_handler_ = signalHandler;
101}
102
104 // Nothing to do here. No need to disconnect from anything.
105}
106
118ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) {
119 // This is a configuration backend implementation that reads the
120 // configuration from a JSON file.
121
126
127 // Basic sanity check: file name must not be empty.
128 try {
129 if (file_name.empty()) {
130 // Basic sanity check: file name must not be empty.
131 isc_throw(isc::BadValue, "JSON configuration file not specified."
132 " Please use -c command line option.");
133 }
134
135 // Read contents of the file and parse it as JSON
136 Parser4Context parser;
137 json = parser.parseFile(file_name, Parser4Context::PARSER_DHCP4);
138 if (!json) {
139 isc_throw(isc::BadValue, "no configuration found");
140 }
141
142 // Let's do sanity check before we call json->get() which
143 // works only for map.
144 if (json->getType() != isc::data::Element::map) {
145 isc_throw(isc::BadValue, "Configuration file is expected to be "
146 "a map, i.e., start with { and end with } and contain "
147 "at least an entry called 'Dhcp4' that itself is a map. "
148 << file_name
149 << " is a valid JSON, but its top element is not a map."
150 " Did you forget to add { } around your configuration?");
151 }
152
153 // Use parsed JSON structures to configure the server
154 result = ControlledDhcpv4Srv::processCommand("config-set", json);
155 if (!result) {
156 // Undetermined status of the configuration. This should never
157 // happen, but as the configureDhcp4Server returns a pointer, it is
158 // theoretically possible that it will return NULL.
159 isc_throw(isc::BadValue, "undefined result of "
160 "processCommand(\"config-set\", json)");
161 }
162
163 // Now check is the returned result is successful (rcode=0) or not
164 // (see @ref isc::config::parseAnswer).
165 int rcode;
166 ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
167 if (rcode != 0) {
168 string reason = comment ? comment->stringValue() :
169 "no details available";
170 isc_throw(isc::BadValue, reason);
171 }
172 } catch (const std::exception& ex) {
173 // If configuration failed at any stage, we drop the staging
174 // configuration and continue to use the previous one.
176
177 LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
178 .arg(file_name).arg(ex.what());
179 isc_throw(isc::BadValue, "configuration error using file '"
180 << file_name << "': " << ex.what());
181 }
182
183 return (result);
184}
185
186
188ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) {
191 } else {
192 LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING);
194 "Shutdown failure.");
195 return (answer);
196 }
197 ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down.");
198 return (answer);
199}
200
202ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
203
207 bool status = HooksManager::loadLibraries(loaded);
208 if (!status) {
209 LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);
211 "Failed to reload hooks libraries.");
212 return (answer);
213 }
215 "Hooks libraries successfully reloaded.");
216 return (answer);
217}
218
220ControlledDhcpv4Srv::commandConfigReloadHandler(const string&,
221 ConstElementPtr /*args*/) {
222
223 // Get configuration file name.
224 std::string file = ControlledDhcpv4Srv::getInstance()->getConfigFile();
225 try {
226 LOG_INFO(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION).arg(file);
227 return (loadConfigFile(file));
228 } catch (const std::exception& ex) {
229 // Log the unsuccessful reconfiguration. The reason for failure
230 // should be already logged. Don't rethrow an exception so as
231 // the server keeps working.
232 LOG_ERROR(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION_FAIL)
233 .arg(file);
235 "Config reload failed:" + string(ex.what())));
236 }
237}
238
240ControlledDhcpv4Srv::commandConfigGetHandler(const string&,
241 ConstElementPtr /*args*/) {
242 ConstElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
243
244 return (createAnswer(0, config));
245}
246
248ControlledDhcpv4Srv::commandConfigWriteHandler(const string&,
249 ConstElementPtr args) {
250 string filename;
251
252 if (args) {
253 if (args->getType() != Element::map) {
254 return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
255 }
256 ConstElementPtr filename_param = args->get("filename");
257 if (filename_param) {
258 if (filename_param->getType() != Element::string) {
260 "passed parameter 'filename' is not a string"));
261 }
262 filename = filename_param->stringValue();
263 }
264 }
265
266 if (filename.empty()) {
267 // filename parameter was not specified, so let's use whatever we remember
268 filename = getConfigFile();
269 }
270
271 if (filename.empty()) {
272 return (createAnswer(CONTROL_RESULT_ERROR, "Unable to determine filename."
273 "Please specify filename explicitly."));
274 }
275
276 // Ok, it's time to write the file.
277 size_t size = 0;
278 try {
279 ConstElementPtr cfg = CfgMgr::instance().getCurrentCfg()->toElement();
280 size = writeConfigFile(filename, cfg);
281 } catch (const isc::Exception& ex) {
282 return (createAnswer(CONTROL_RESULT_ERROR, string("Error during write-config:")
283 + ex.what()));
284 }
285 if (size == 0) {
286 return (createAnswer(CONTROL_RESULT_ERROR, "Error writing configuration to "
287 + filename));
288 }
289
290 // Ok, it's time to return the successful response.
292 params->set("size", Element::create(static_cast<long long>(size)));
293 params->set("filename", Element::create(filename));
294
295 return (createAnswer(CONTROL_RESULT_SUCCESS, "Configuration written to "
296 + filename + " successful", params));
297}
298
300ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
301 ConstElementPtr args) {
302 const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
303 ConstElementPtr dhcp4;
304 string message;
305
306 // Command arguments are expected to be:
307 // { "Dhcp4": { ... }, "Logging": { ... } }
308 // The Logging component is technically optional. If it's not supplied
309 // logging will revert to default logging.
310 if (!args) {
311 message = "Missing mandatory 'arguments' parameter.";
312 } else {
313 dhcp4 = args->get("Dhcp4");
314 if (!dhcp4) {
315 message = "Missing mandatory 'Dhcp4' parameter.";
316 } else if (dhcp4->getType() != Element::map) {
317 message = "'Dhcp4' parameter expected to be a map.";
318 }
319 }
320
321 if (!message.empty()) {
322 // Something is amiss with arguments, return a failure response.
323 ConstElementPtr result = isc::config::createAnswer(status_code,
324 message);
325 return (result);
326 }
327
328 // We are starting the configuration process so we should remove any
329 // staging configuration that has been created during previous
330 // configuration attempts.
332
333 // Logging is a sibling element and must be parsed explicitly.
334 // The call to configureLogger parses the given Logging element if
335 // not null, into the staging config. Note this does not alter the
336 // current loggers, they remain in effect until we apply the
337 // logging config below. If no logging is supplied logging will
338 // revert to default logging.
339 Daemon::configureLogger(args->get("Logging"),
341
342 // Let's apply the new logging. We do it early, so we'll be able to print
343 // out what exactly is wrong with the new socnfig in case of problems.
344 CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
345
346 // Now we configure the server proper.
347 ConstElementPtr result = processConfig(dhcp4);
348
349 // If the configuration parsed successfully, apply the new logger
350 // configuration and the commit the new configuration. We apply
351 // the logging first in case there's a configuration failure.
352 int rcode = 0;
353 isc::config::parseAnswer(rcode, result);
354 if (rcode == 0) {
355 CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
356
357 // Use new configuration.
359 } else {
360 // Ok, we applied the logging from the upcoming configuration, but
361 // there were problems with the config. As such, we need to back off
362 // and revert to the previous logging configuration.
363 CfgMgr::instance().getCurrentCfg()->applyLoggingCfg();
364 }
365
366 return (result);
367}
368
370ControlledDhcpv4Srv::commandConfigTestHandler(const string&,
371 ConstElementPtr args) {
372 const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
373 ConstElementPtr dhcp4;
374 string message;
375
376 // Command arguments are expected to be:
377 // { "Dhcp4": { ... }, "Logging": { ... } }
378 // The Logging component is technically optional. If it's not supplied
379 // logging will revert to default logging.
380 if (!args) {
381 message = "Missing mandatory 'arguments' parameter.";
382 } else {
383 dhcp4 = args->get("Dhcp4");
384 if (!dhcp4) {
385 message = "Missing mandatory 'Dhcp4' parameter.";
386 } else if (dhcp4->getType() != Element::map) {
387 message = "'Dhcp4' parameter expected to be a map.";
388 }
389 }
390
391 if (!message.empty()) {
392 // Something is amiss with arguments, return a failure response.
393 ConstElementPtr result = isc::config::createAnswer(status_code,
394 message);
395 return (result);
396 }
397
398 // We are starting the configuration process so we should remove any
399 // staging configuration that has been created during previous
400 // configuration attempts.
402
403 // Now we check the server proper.
404 return (checkConfig(dhcp4));
405}
406
408ControlledDhcpv4Srv::commandDhcpDisableHandler(const std::string&,
409 ConstElementPtr args) {
410 std::ostringstream message;
411 int64_t max_period = 0;
412
413 // Parse arguments to see if the 'max-period' parameter has been specified.
414 if (args) {
415 // Arguments must be a map.
416 if (args->getType() != Element::map) {
417 message << "arguments for the 'dhcp-disable' command must be a map";
418
419 } else {
420 ConstElementPtr max_period_element = args->get("max-period");
421 // max-period is optional.
422 if (max_period_element) {
423 // It must be an integer, if specified.
424 if (max_period_element->getType() != Element::integer) {
425 message << "'max-period' argument must be a number";
426
427 } else {
428 // It must be positive integer.
429 max_period = max_period_element->intValue();
430 if (max_period <= 0) {
431 message << "'max-period' must be positive integer";
432 }
433
434 // The user specified that the DHCP service should resume not
435 // later than in max-period seconds. If the 'dhcp-enable' command
436 // is not sent, the DHCP service will resume automatically.
437 network_state_->delayedEnableAll(static_cast<unsigned>(max_period));
438 }
439 }
440 }
441 }
442
443 // No error occurred, so let's disable the service.
444 if (message.tellp() == 0) {
445 network_state_->disableService();
446
447 message << "DHCPv4 service disabled";
448 if (max_period > 0) {
449 message << " for " << max_period << " seconds";
450 }
451 // Success.
452 return (config::createAnswer(CONTROL_RESULT_SUCCESS, message.str()));
453 }
454
455 // Failure.
456 return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
457}
458
460ControlledDhcpv4Srv::commandDhcpEnableHandler(const std::string&, ConstElementPtr) {
461 network_state_->enableService();
462 return (config::createAnswer(CONTROL_RESULT_SUCCESS, "DHCP service successfully enabled"));
463}
464
466ControlledDhcpv4Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
468 ElementPtr arguments = Element::createMap();
469 arguments->set("extended", extended);
472 arguments);
473 return (answer);
474}
475
477ControlledDhcpv4Srv::commandBuildReportHandler(const string&,
479 ConstElementPtr answer =
481 return (answer);
482}
483
485ControlledDhcpv4Srv::commandLeasesReclaimHandler(const string&,
486 ConstElementPtr args) {
487 int status_code = CONTROL_RESULT_ERROR;
488 string message;
489
490 // args must be { "remove": <bool> }
491 if (!args) {
492 message = "Missing mandatory 'remove' parameter.";
493 } else {
494 ConstElementPtr remove_name = args->get("remove");
495 if (!remove_name) {
496 message = "Missing mandatory 'remove' parameter.";
497 } else if (remove_name->getType() != Element::boolean) {
498 message = "'remove' parameter expected to be a boolean.";
499 } else {
500 bool remove_lease = remove_name->boolValue();
501 server_->alloc_engine_->reclaimExpiredLeases4(0, 0, remove_lease);
502 status_code = 0;
503 message = "Reclamation of expired leases is complete.";
504 }
505 }
506 ConstElementPtr answer = isc::config::createAnswer(status_code, message);
507 return (answer);
508}
509
512 ConstElementPtr args) {
513 string txt = args ? args->str() : "(none)";
514
515 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED)
516 .arg(command).arg(txt);
517
519
520 if (!srv) {
522 "Server object not initialized, so can't process command '" +
523 command + "', arguments: '" + txt + "'.");
524 return (no_srv);
525 }
526
527 try {
528 if (command == "shutdown") {
529 return (srv->commandShutdownHandler(command, args));
530
531 } else if (command == "libreload") {
532 return (srv->commandLibReloadHandler(command, args));
533
534 } else if (command == "config-reload") {
535 return (srv->commandConfigReloadHandler(command, args));
536
537 } else if (command == "config-set") {
538 return (srv->commandConfigSetHandler(command, args));
539
540 } else if (command == "config-get") {
541 return (srv->commandConfigGetHandler(command, args));
542
543 } else if (command == "config-test") {
544 return (srv->commandConfigTestHandler(command, args));
545
546 } else if (command == "dhcp-disable") {
547 return (srv->commandDhcpDisableHandler(command, args));
548
549 } else if (command == "dhcp-enable") {
550 return (srv->commandDhcpEnableHandler(command, args));
551
552 } else if (command == "version-get") {
553 return (srv->commandVersionGetHandler(command, args));
554
555 } else if (command == "build-report") {
556 return (srv->commandBuildReportHandler(command, args));
557
558 } else if (command == "leases-reclaim") {
559 return (srv->commandLeasesReclaimHandler(command, args));
560
561 } else if (command == "config-write") {
562 return (srv->commandConfigWriteHandler(command, args));
563
564 }
566 "Unrecognized command:" + command);
567 return (answer);
568 } catch (const Exception& ex) {
569 return (isc::config::createAnswer(1, "Error while processing command '"
570 + command + "':" + ex.what() +
571 ", params: '" + txt + "'"));
572 }
573}
574
577
578 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED)
579 .arg(config->str());
580
582
583 // Single stream instance used in all error clauses
584 std::ostringstream err;
585
586 if (!srv) {
587 err << "Server object not initialized, can't process config.";
588 return (isc::config::createAnswer(1, err.str()));
589 }
590
591 ConstElementPtr answer = configureDhcp4Server(*srv, config);
592
593 // Check that configuration was successful. If not, do not reopen sockets
594 // and don't bother with DDNS stuff.
595 try {
596 int rcode = 0;
597 isc::config::parseAnswer(rcode, answer);
598 if (rcode != 0) {
599 return (answer);
600 }
601 } catch (const std::exception& ex) {
602 err << "Failed to process configuration:" << ex.what();
603 return (isc::config::createAnswer(1, err.str()));
604 }
605
606 // Re-open lease and host database with new parameters.
607 try {
609 boost::bind(&ControlledDhcpv4Srv::dbLostCallback, srv, _1);
610 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
611 cfg_db->setAppendedParameters("universe=4");
612 cfg_db->createManagers();
613 } catch (const std::exception& ex) {
614 err << "Unable to open database: " << ex.what();
615 return (isc::config::createAnswer(1, err.str()));
616 }
617
618 // Server will start DDNS communications if its enabled.
619 try {
620 srv->startD2();
621 } catch (const std::exception& ex) {
622 err << "Error starting DHCP_DDNS client after server reconfiguration: "
623 << ex.what();
624 return (isc::config::createAnswer(1, err.str()));
625 }
626
627 // Setup DHCPv4-over-DHCPv6 IPC
628 try {
630 } catch (const std::exception& ex) {
631 std::ostringstream err;
632 err << "error starting DHCPv4-over-DHCPv6 IPC "
633 " after server reconfiguration: " << ex.what();
634 return (isc::config::createAnswer(1, err.str()));
635 }
636
637 // Configure DHCP packet queueing
638 try {
640 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
641 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
642 LOG_INFO(dhcp4_logger, DHCP4_CONFIG_PACKET_QUEUE)
643 .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
644 }
645
646 } catch (const std::exception& ex) {
647 err << "Error setting packet queue controls after server reconfiguration: "
648 << ex.what();
649 return (isc::config::createAnswer(1, err.str()));
650 }
651
652 // Configuration may change active interfaces. Therefore, we have to reopen
653 // sockets according to new configuration. It is possible that this
654 // operation will fail for some interfaces but the openSockets function
655 // guards against exceptions and invokes a callback function to
656 // log warnings. Since we allow that this fails for some interfaces there
657 // is no need to rollback configuration if socket fails to open on any
658 // of the interfaces.
659 CfgMgr::instance().getStagingCfg()->getCfgIface()->
660 openSockets(AF_INET, srv->getPort(), getInstance()->useBroadcast());
661
662 // Install the timers for handling leases reclamation.
663 try {
664 CfgMgr::instance().getStagingCfg()->getCfgExpiration()->
665 setupTimers(&ControlledDhcpv4Srv::reclaimExpiredLeases,
666 &ControlledDhcpv4Srv::deleteExpiredReclaimedLeases,
667 server_);
668
669 } catch (const std::exception& ex) {
670 err << "unable to setup timers for periodically running the"
671 " reclamation of the expired leases: "
672 << ex.what() << ".";
673 return (isc::config::createAnswer(1, err.str()));
674 }
675
676 // This hook point notifies hooks libraries that the configuration of the
677 // DHCPv4 server has completed. It provides the hook library with the pointer
678 // to the common IO service object, new server configuration in the JSON
679 // format and with the pointer to the configuration storage where the
680 // parsed configuration is stored.
681 if (HooksManager::calloutsPresent(Hooks.hooks_index_dhcp4_srv_configured_)) {
683
684 callout_handle->setArgument("io_context", srv->getIOService());
685 callout_handle->setArgument("network_state", srv->getNetworkState());
686 callout_handle->setArgument("json_config", config);
687 callout_handle->setArgument("server_config", CfgMgr::instance().getStagingCfg());
688
689 HooksManager::callCallouts(Hooks.hooks_index_dhcp4_srv_configured_,
690 *callout_handle);
691
692 // Ignore status code as none of them would have an effect on further
693 // operation.
694 }
695
696 return (answer);
697}
698
701
702 LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED)
703 .arg(config->str());
704
706
707 // Single stream instance used in all error clauses
708 std::ostringstream err;
709
710 if (!srv) {
711 err << "Server object not initialized, can't process config.";
712 return (isc::config::createAnswer(1, err.str()));
713 }
714
715 return (configureDhcp4Server(*srv, config, true));
716}
717
718ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
719 : Dhcpv4Srv(port), io_service_(), timer_mgr_(TimerMgr::instance()) {
720 if (getInstance()) {
722 "There is another Dhcpv4Srv instance already.");
723 }
724 server_ = this; // remember this instance for later use in handlers
725
726 // TimerMgr uses IO service to run asynchronous timers.
727 TimerMgr::instance()->setIOService(getIOService());
728
729 // CommandMgr uses IO service to run asynchronous socket operations.
731
732 // These are the commands always supported by the DHCPv4 server.
733 // Please keep the list in alphabetic order.
734 CommandMgr::instance().registerCommand("build-report",
735 boost::bind(&ControlledDhcpv4Srv::commandBuildReportHandler, this, _1, _2));
736
738 boost::bind(&ControlledDhcpv4Srv::commandConfigGetHandler, this, _1, _2));
739
740 CommandMgr::instance().registerCommand("config-reload",
741 boost::bind(&ControlledDhcpv4Srv::commandConfigReloadHandler, this, _1, _2));
742
744 boost::bind(&ControlledDhcpv4Srv::commandConfigSetHandler, this, _1, _2));
745
747 boost::bind(&ControlledDhcpv4Srv::commandConfigTestHandler, this, _1, _2));
748
749 CommandMgr::instance().registerCommand("config-write",
750 boost::bind(&ControlledDhcpv4Srv::commandConfigWriteHandler, this, _1, _2));
751
753 boost::bind(&ControlledDhcpv4Srv::commandDhcpEnableHandler, this, _1, _2));
754
755 CommandMgr::instance().registerCommand("dhcp-disable",
756 boost::bind(&ControlledDhcpv4Srv::commandDhcpDisableHandler, this, _1, _2));
757
759 boost::bind(&ControlledDhcpv4Srv::commandLibReloadHandler, this, _1, _2));
760
761 CommandMgr::instance().registerCommand("leases-reclaim",
762 boost::bind(&ControlledDhcpv4Srv::commandLeasesReclaimHandler, this, _1, _2));
763
765 boost::bind(&ControlledDhcpv4Srv::commandShutdownHandler, this, _1, _2));
766
768 boost::bind(&ControlledDhcpv4Srv::commandVersionGetHandler, this, _1, _2));
769
770 // Register statistic related commands
771 CommandMgr::instance().registerCommand("statistic-get",
772 boost::bind(&StatsMgr::statisticGetHandler, _1, _2));
773
774 CommandMgr::instance().registerCommand("statistic-reset",
775 boost::bind(&StatsMgr::statisticResetHandler, _1, _2));
776
777 CommandMgr::instance().registerCommand("statistic-remove",
778 boost::bind(&StatsMgr::statisticRemoveHandler, _1, _2));
779
780 CommandMgr::instance().registerCommand("statistic-get-all",
781 boost::bind(&StatsMgr::statisticGetAllHandler, _1, _2));
782
783 CommandMgr::instance().registerCommand("statistic-reset-all",
784 boost::bind(&StatsMgr::statisticResetAllHandler, _1, _2));
785
786 CommandMgr::instance().registerCommand("statistic-remove-all",
787 boost::bind(&StatsMgr::statisticRemoveAllHandler, _1, _2));
788
789}
790
792 io_service_.stop(); // Stop ASIO transmissions
793 Dhcpv4Srv::shutdown(); // Initiate DHCPv4 shutdown procedure.
794}
795
797 try {
798 cleanup();
799
800 // The closure captures either a shared pointer (memory leak)
801 // or a raw pointer (pointing to a deleted object).
803
804 timer_mgr_->unregisterTimers();
805
806 // Close the command socket (if it exists).
808
809 // Deregister any registered commands (please keep in alphabetic order)
810 CommandMgr::instance().deregisterCommand("build-report");
812 CommandMgr::instance().deregisterCommand("config-reload");
814 CommandMgr::instance().deregisterCommand("config-write");
815 CommandMgr::instance().deregisterCommand("leases-reclaim");
818 CommandMgr::instance().deregisterCommand("dhcp-disable");
821 CommandMgr::instance().deregisterCommand("statistic-get");
822 CommandMgr::instance().deregisterCommand("statistic-get-all");
823 CommandMgr::instance().deregisterCommand("statistic-remove");
824 CommandMgr::instance().deregisterCommand("statistic-remove-all");
825 CommandMgr::instance().deregisterCommand("statistic-reset");
826 CommandMgr::instance().deregisterCommand("statistic-reset-all");
828
829 } catch (...) {
830 // Don't want to throw exceptions from the destructor. The server
831 // is shutting down anyway.
832 ;
833 }
834
835 server_ = NULL; // forget this instance. Noone should call any handlers at
836 // this stage.
837}
838
839void ControlledDhcpv4Srv::sessionReader(void) {
840 // Process one asio event. If there are more events, iface_mgr will call
841 // this callback more than once.
842 if (getInstance()) {
843 getInstance()->io_service_.run_one();
844 }
845}
846
847void
848ControlledDhcpv4Srv::reclaimExpiredLeases(const size_t max_leases,
849 const uint16_t timeout,
850 const bool remove_lease,
851 const uint16_t max_unwarned_cycles) {
852 server_->alloc_engine_->reclaimExpiredLeases4(max_leases, timeout,
853 remove_lease,
854 max_unwarned_cycles);
855 // We're using the ONE_SHOT timer so there is a need to re-schedule it.
857}
858
859void
860ControlledDhcpv4Srv::deleteExpiredReclaimedLeases(const uint32_t secs) {
861 server_->alloc_engine_->deleteExpiredReclaimedLeases4(secs);
862 // We're using the ONE_SHOT timer so there is a need to re-schedule it.
864}
865
866void
867ControlledDhcpv4Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
868 bool reopened = false;
869
870 // Re-open lease and host database with new parameters.
871 try {
872 CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
873 cfg_db->createManagers();
874 reopened = true;
875 } catch (const std::exception& ex) {
876 LOG_ERROR(dhcp4_logger, DHCP4_DB_RECONNECT_ATTEMPT_FAILED).arg(ex.what());
877 }
878
879 if (reopened) {
880 // Cancel the timer.
881 if (TimerMgr::instance()->isTimerRegistered("Dhcp4DbReconnectTimer")) {
882 TimerMgr::instance()->cancel("Dhcp4DbReconnectTimer");
883 }
884
885 // Set network state to service enabled
886 network_state_->enableService();
887
888 // Toss the reconnect control, we're done with it
889 db_reconnect_ctl.reset();
890 } else {
891 if (!db_reconnect_ctl->checkRetries()) {
892 LOG_ERROR(dhcp4_logger, DHCP4_DB_RECONNECT_RETRIES_EXHAUSTED)
893 .arg(db_reconnect_ctl->maxRetries());
894 shutdown();
895 return;
896 }
897
898 LOG_INFO(dhcp4_logger, DHCP4_DB_RECONNECT_ATTEMPT_SCHEDULE)
899 .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
900 .arg(db_reconnect_ctl->maxRetries())
901 .arg(db_reconnect_ctl->retryInterval());
902
903 if (!TimerMgr::instance()->isTimerRegistered("Dhcp4DbReconnectTimer")) {
904 TimerMgr::instance()->registerTimer("Dhcp4DbReconnectTimer",
905 boost::bind(&ControlledDhcpv4Srv::dbReconnect, this,
906 db_reconnect_ctl),
907 db_reconnect_ctl->retryInterval(),
909 }
910
911 TimerMgr::instance()->setup("Dhcp4DbReconnectTimer");
912 }
913}
914
915bool
916ControlledDhcpv4Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
917 // Disable service until we recover
918 network_state_->disableService();
919
920 if (!db_reconnect_ctl) {
921 // This shouldn't never happen
922 LOG_ERROR(dhcp4_logger, DHCP4_DB_RECONNECT_NO_DB_CTL);
923 return (false);
924 }
925
926 // If reconnect isn't enabled, log it and return false
927 if (!db_reconnect_ctl->retriesLeft() ||
928 !db_reconnect_ctl->retryInterval()) {
929 LOG_INFO(dhcp4_logger, DHCP4_DB_RECONNECT_DISABLED)
930 .arg(db_reconnect_ctl->retriesLeft())
931 .arg(db_reconnect_ctl->retryInterval());
932 return(false);
933 }
934
935 // Invoke reconnect method
936 dbReconnect(db_reconnect_ctl);
937
938 return(true);
939}
940
941}; // end of isc::dhcp namespace
942}; // end of isc namespace
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.
void registerCommand(const std::string &cmd, CommandHandler handler)
Registers specified command handler for a given command.
void deregisterCommand(const std::string &cmd)
Deregisters specified command handler.
void closeCommandSocket()
Shuts down any open control sockets.
Definition: command_mgr.cc:598
static CommandMgr & instance()
CommandMgr is a singleton class.
Definition: command_mgr.cc:620
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the command manager.
Definition: command_mgr.cc:626
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:223
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:268
static DbLostCallback db_lost_callback
Optional call back function to invoke if a successfully open connection subsequently fails.
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.
void rollback()
Removes staging configuration.
Definition: cfgmgr.cc:113
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition: cfgmgr.cc:160
void commit()
Commits the staging configuration.
Definition: cfgmgr.cc:87
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:154
Controlled version of the DHCPv4 server.
static isc::data::ConstElementPtr processConfig(isc::data::ConstElementPtr new_config)
Configuration processor.
void shutdown()
Initiates shutdown procedure for the whole DHCPv4 server.
isc::data::ConstElementPtr loadConfigFile(const std::string &file_name)
Loads specific config file.
void cleanup()
Performs cleanup, immediately before termination.
static isc::data::ConstElementPtr processCommand(const std::string &command, isc::data::ConstElementPtr args)
Command processor.
void init(const std::string &config_file)
Initializes the server.
ControlledDhcpv4Srv(uint16_t port=DHCP4_SERVER_PORT)
Constructor.
static ControlledDhcpv4Srv * getInstance()
Returns pointer to the sole instance of Dhcpv4Srv.
isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr new_config)
Configuration checker.
static Dhcp4to6Ipc & instance()
Returns pointer to the sole instance of Dhcp4to6Ipc.
Definition: dhcp4to6_ipc.cc:32
virtual void open()
Open communication socket.
Definition: dhcp4to6_ipc.cc:37
DHCPv4 server service.
Definition: dhcp4_srv.h:194
void shutdown()
Instructs the server to shut down.
Definition: dhcp4_srv.cc:511
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
Definition: dhcp4_srv.cc:3465
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition: dhcp4_srv.h:234
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition: dhcp4_srv.h:911
NetworkStatePtr & getNetworkState()
Returns pointer to the network state used by the server.
Definition: dhcp4_srv.h:239
static std::string getVersion(bool extended)
returns Kea version on stdout and exit.
Definition: dhcp4_srv.cc:3501
bool useBroadcast() const
Return bool value indicating that broadcast flags should be set on sockets.
Definition: dhcp4_srv.h:296
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition: dhcp4_srv.h:937
uint16_t getPort() const
Get UDP port on which server should listen.
Definition: dhcp4_srv.h:288
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
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.
@ PARSER_DHCP4
This parser will parse the content as Dhcp4 config wrapped in a map (that's the regular config file)
Manages a pool of asynchronous interval timers.
Definition: timer_mgr.h:54
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition: timer_mgr.cc:319
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static bool loadLibraries(const HookLibsCollection &libraries)
Load and reload libraries.
static boost::shared_ptr< CalloutHandle > createCalloutHandle()
Return callout handle.
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
static HookLibsCollection getLibraryInfo()
Return list of loaded libraries with its parameters.
std::string getConfigFile() const
Returns config file name.
Definition: daemon.cc:105
virtual size_t writeConfigFile(const std::string &config_file, isc::data::ConstElementPtr cfg=isc::data::ConstElementPtr()) const
Writes current configuration to specified file.
Definition: daemon.cc:229
isc::util::SignalSetPtr signal_set_
A pointer to the object installing custom signal handlers.
Definition: daemon.h:238
isc::util::SignalHandler signal_handler_
Pointer to the common signal handler invoked by the handleSignal function.
Definition: daemon.h:246
Represents a collection of signals handled in a customized way.
Definition: signal_set.h:87
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.
Dhcp4Hooks Hooks
Definition: dhcp4_srv.cc:117
Defines the Dhcp4o6Ipc class.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
static isc::data::ConstElementPtr statisticResetHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-reset command.
Definition: stats_mgr.cc:155
static isc::data::ConstElementPtr statisticResetAllHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-reset-all command.
Definition: stats_mgr.cc:204
static isc::data::ConstElementPtr statisticRemoveHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-remove command.
Definition: stats_mgr.cc:172
static isc::data::ConstElementPtr statisticRemoveAllHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-remove-all command.
Definition: stats_mgr.cc:189
static isc::data::ConstElementPtr statisticGetHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-get command.
Definition: stats_mgr.cc:144
static isc::data::ConstElementPtr statisticGetAllHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-get-all command.
Definition: stats_mgr.cc:197
#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_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
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:23
boost::shared_ptr< Element > ElementPtr
Definition: data.h:22
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
std::string getConfigReport()
Definition: cfgrpt.cc:20
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
isc::data::ConstElementPtr configureDhcp4Server(Dhcpv4Srv &server, isc::data::ConstElementPtr config_set, bool check_only)
Configure DHCPv4 server (Dhcpv4Srv) with a set of configuration values.
const int DBG_DHCP4_COMMAND
Debug level used to log receiving commands.
Definition: dhcp4_log.h:30
isc::log::Logger dhcp4_logger(DHCP4_APP_LOGGER_NAME)
Base logger for DHCPv4 server.
Definition: dhcp4_log.h:90
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.
Defines the logger used by the top-level component of kea-dhcp-ddns.