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