Kea 3.1.7
ctrl_dhcp6_srv.cc
Go to the documentation of this file.
1// Copyright (C) 2014-2025 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
12#include <cc/data.h>
13#include <config/command_mgr.h>
18#include <dhcp/libdhcp++.h>
20#include <dhcp6/dhcp6_log.h>
21#include <dhcp6/dhcp6to4_ipc.h>
26#include <dhcpsrv/cfgmgr.h>
27#include <dhcpsrv/db_type.h>
28#include <dhcpsrv/host_mgr.h>
31#include <hooks/hooks.h>
32#include <hooks/hooks_manager.h>
34#include <stats/stats_mgr.h>
35#include <util/encode/encode.h>
37
38#include <signal.h>
39
40#include <sstream>
41
42using namespace isc::asiolink;
43using namespace isc::config;
44using namespace isc::data;
45using namespace isc::db;
46using namespace isc::dhcp;
47using namespace isc::hooks;
48using namespace isc::stats;
49using namespace isc::util;
50using namespace std;
51namespace ph = std::placeholders;
52
53namespace {
54
56struct CtrlDhcp6Hooks {
57 int hooks_index_dhcp6_srv_configured_;
58
60 CtrlDhcp6Hooks() {
61 hooks_index_dhcp6_srv_configured_ = HooksManager::registerHook("dhcp6_srv_configured");
62 }
63
64};
65
66// Declare a Hooks object. As this is outside any function or method, it
67// will be instantiated (and the constructor run) when the module is loaded.
68// As a result, the hook indexes will be defined before any method in this
69// module is called.
70CtrlDhcp6Hooks Hooks;
71
72// Name of the file holding server identifier.
73static const char* SERVER_DUID_FILE = "kea-dhcp6-serverid";
74
84void signalHandler(int signo) {
85 // SIGHUP signals a request to reconfigure the server.
86 if (signo == SIGHUP) {
88 } else if ((signo == SIGTERM) || (signo == SIGINT)) {
90 }
91}
92
93}
94
95namespace isc {
96namespace dhcp {
97
98ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
99
100void
101ControlledDhcpv6Srv::init(const std::string& file_name) {
102 // Keep the call timestamp.
103 start_ = boost::posix_time::second_clock::universal_time();
104
105 // Configure the server using JSON file.
106 ConstElementPtr result = loadConfigFile(file_name);
107
108 int rcode;
109 ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
110 if (rcode != CONTROL_RESULT_SUCCESS) {
111 string reason = comment ? comment->stringValue() :
112 "no details available";
113 isc_throw(isc::BadValue, reason);
114 }
115
116 // Set signal handlers. When the SIGHUP is received by the process
117 // the server reconfiguration will be triggered. When SIGTERM or
118 // SIGINT will be received, the server will start shutting down.
119 signal_set_.reset(new IOSignalSet(getIOService(), signalHandler));
120
121 signal_set_->add(SIGINT);
122 signal_set_->add(SIGHUP);
123 signal_set_->add(SIGTERM);
124}
125
127 signal_set_.reset();
128 getIOService()->poll();
129}
130
132ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
133 // This is a configuration backend implementation that reads the
134 // configuration from a JSON file.
135
138
139 // Basic sanity check: file name must not be empty.
140 try {
141 if (file_name.empty()) {
142 // Basic sanity check: file name must not be empty.
143 isc_throw(isc::BadValue, "JSON configuration file not specified."
144 " Please use -c command line option.");
145 }
146
147 // Read contents of the file and parse it as JSON
148 Parser6Context parser;
149 json = parser.parseFile(file_name, Parser6Context::PARSER_DHCP6);
150 if (!json) {
151 isc_throw(isc::BadValue, "no configuration found");
152 }
153
154 // Let's do sanity check before we call json->get() which
155 // works only for map.
156 if (json->getType() != isc::data::Element::map) {
157 isc_throw(isc::BadValue, "Configuration file is expected to be "
158 "a map, i.e., start with { and end with } and contain "
159 "at least an entry called 'Dhcp6' that itself is a map. "
160 << file_name
161 << " is a valid JSON, but its top element is not a map."
162 " Did you forget to add { } around your configuration?");
163 }
164
165 // Use parsed JSON structures to configure the server
166 result = CommandMgr::instance().processCommand(createCommand("config-set", json));
167 if (!result) {
168 // Undetermined status of the configuration. This should never
169 // happen, but as the configureDhcp6Server returns a pointer, it is
170 // theoretically possible that it will return NULL.
171 isc_throw(isc::BadValue, "undefined result of "
172 "process command \"config-set\"");
173 }
174
175 // Now check is the returned result is successful (rcode=0) or not
176 // (see @ref isc::config::parseAnswer).
177 int rcode;
178 ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
179 if (rcode != CONTROL_RESULT_SUCCESS) {
180 string reason = comment ? comment->stringValue() :
181 "no details available";
182 isc_throw(isc::BadValue, reason);
183 }
184 } catch (const std::exception& ex) {
185 // If configuration failed at any stage, we drop the staging
186 // configuration and continue to use the previous one.
188
190 .arg(file_name).arg(ex.what());
191 isc_throw(isc::BadValue, "configuration error using file '"
192 << file_name << "': " << ex.what());
193 }
194
196 .arg(MultiThreadingMgr::instance().getMode() ? "yes" : "no")
197 .arg(MultiThreadingMgr::instance().getThreadPoolSize())
198 .arg(MultiThreadingMgr::instance().getPacketQueueSize());
199
200 return (result);
201}
202
204ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr args) {
207 return (createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure."));
208 }
209
210 int exit_value = 0;
211 if (args) {
212 // @todo Should we go ahead and shutdown even if the args are invalid?
213 if (args->getType() != Element::map) {
214 return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
215 }
216
217 ConstElementPtr param = args->get("exit-value");
218 if (param) {
219 if (param->getType() != Element::integer) {
221 "parameter 'exit-value' is not an integer"));
222 }
223
224 exit_value = param->intValue();
225 }
226 }
227
229 return (createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down."));
230}
231
233ControlledDhcpv6Srv::commandConfigReloadHandler(const string&,
234 ConstElementPtr /*args*/) {
235 // Get configuration file name.
237 try {
239 auto result = loadConfigFile(file);
241 return (result);
242 } catch (const std::exception& ex) {
243 // Log the unsuccessful reconfiguration. The reason for failure
244 // should be already logged. Don't rethrow an exception so as
245 // the server keeps working.
247 .arg(file);
249 "Config reload failed: " + string(ex.what())));
250 }
251}
252
254ControlledDhcpv6Srv::commandConfigGetHandler(const string&,
255 ConstElementPtr /*args*/) {
256 ElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
257 string hash = BaseCommandMgr::getHash(config);
258 config->set("hash", Element::create(hash));
259
260 return (createAnswer(CONTROL_RESULT_SUCCESS, config));
261}
262
264ControlledDhcpv6Srv::commandConfigHashGetHandler(const string&,
265 ConstElementPtr /*args*/) {
266 ConstElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
267
268 string hash = BaseCommandMgr::getHash(config);
269
271 params->set("hash", Element::create(hash));
272 return (createAnswer(CONTROL_RESULT_SUCCESS, params));
273}
274
276ControlledDhcpv6Srv::commandConfigWriteHandler(const string&,
277 ConstElementPtr args) {
278 string filename;
279
280 if (args) {
281 if (args->getType() != Element::map) {
282 return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
283 }
284 ConstElementPtr filename_param = args->get("filename");
285 if (filename_param) {
286 if (filename_param->getType() != Element::string) {
288 "passed parameter 'filename' is not a string"));
289 }
290 filename = filename_param->stringValue();
291 }
292 }
293
294 if (filename.empty()) {
295 // filename parameter was not specified, so let's use whatever we remember
296 // from the command-line
297 filename = getConfigFile();
298 if (filename.empty()) {
299 return (createAnswer(CONTROL_RESULT_ERROR, "Unable to determine filename."
300 "Please specify filename explicitly."));
301 }
302 } else {
303 try {
304 checkWriteConfigFile(filename);
305 } catch (const isc::Exception& ex) {
306 std::ostringstream msg;
307 msg << "not allowed to write config into " << filename
308 << ": " << ex.what();
309 return (createAnswer(CONTROL_RESULT_ERROR, msg.str()));
310 }
311 }
312
313 // Ok, it's time to write the file.
314 size_t size = 0;
315 try {
316 ConstElementPtr cfg = CfgMgr::instance().getCurrentCfg()->toElement();
317 size = writeConfigFile(filename, cfg);
318 } catch (const isc::Exception& ex) {
319 return (createAnswer(CONTROL_RESULT_ERROR, string("Error during config-write: ")
320 + ex.what()));
321 }
322 if (size == 0) {
323 return (createAnswer(CONTROL_RESULT_ERROR, "Error writing configuration to "
324 + filename));
325 }
326
327 // Ok, it's time to return the successful response.
329 params->set("size", Element::create(static_cast<long long>(size)));
330 params->set("filename", Element::create(filename));
331
332 return (createAnswer(CONTROL_RESULT_SUCCESS, "Configuration written to "
333 + filename + " successful", params));
334}
335
337ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
338 ConstElementPtr args) {
339 const int status_code = CONTROL_RESULT_ERROR;
340 ConstElementPtr dhcp6;
341 string message;
342
343 // Command arguments are expected to be:
344 // { "Dhcp6": { ... } }
345 if (!args) {
346 message = "Missing mandatory 'arguments' parameter.";
347 } else {
348 dhcp6 = args->get("Dhcp6");
349 if (!dhcp6) {
350 message = "Missing mandatory 'Dhcp6' parameter.";
351 } else if (dhcp6->getType() != Element::map) {
352 message = "'Dhcp6' parameter expected to be a map.";
353 }
354 }
355
356 // Check unsupported objects.
357 if (message.empty()) {
358 for (auto const& obj : args->mapValue()) {
359 const string& obj_name = obj.first;
360 if (obj_name != "Dhcp6") {
362 .arg(obj_name);
363 if (message.empty()) {
364 message = "Unsupported '" + obj_name + "' parameter";
365 } else {
366 message += " (and '" + obj_name + "')";
367 }
368 }
369 }
370 if (!message.empty()) {
371 message += ".";
372 }
373 }
374
375 if (!message.empty()) {
376 // Something is amiss with arguments, return a failure response.
377 ConstElementPtr result = isc::config::createAnswer(status_code,
378 message);
379 return (result);
380 }
381
383 (LeaseMgrFactory::instance().getType() == "memfile")) {
384 Memfile_LeaseMgr& mgr = dynamic_cast<Memfile_LeaseMgr&>(LeaseMgrFactory::instance());
385 auto file_name = mgr.getLeaseFilePath(Memfile_LeaseMgr::V6);
388 "Can not update configuration while lease file cleanup process is running."));
389 }
390 }
391
392 // stop thread pool (if running)
393 MultiThreadingCriticalSection cs;
394
395 // We are starting the configuration process so we should remove any
396 // staging configuration that has been created during previous
397 // configuration attempts.
399
400 // Parse the logger configuration explicitly into the staging config.
401 // Note this does not alter the current loggers, they remain in
402 // effect until we apply the logging config below. If no logging
403 // is supplied logging will revert to default logging.
404 Daemon::configureLogger(dhcp6, CfgMgr::instance().getStagingCfg());
405
406 // Let's apply the new logging. We do it early, so we'll be able to print
407 // out what exactly is wrong with the new config in case of problems.
408 CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
409
410 // Now we configure the server proper.
411 ConstElementPtr result = processConfig(dhcp6);
412
413 // If the configuration parsed successfully, apply the new logger
414 // configuration and then commit the new configuration. We apply
415 // the logging first in case there's a configuration failure.
416 int rcode = 0;
417 isc::config::parseAnswer(rcode, result);
418 if (rcode == CONTROL_RESULT_SUCCESS) {
419 CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
420
421 // Use new configuration.
423 } else if (CfgMgr::instance().getCurrentCfg()->getSequence() != 0) {
424 // Ok, we applied the logging from the upcoming configuration, but
425 // there were problems with the config. As such, we need to back off
426 // and revert to the previous logging configuration. This is not done if
427 // sequence == 0, because that would mean always reverting to stdout by
428 // default, and it is arguably more helpful to have the error in a
429 // potential file or syslog configured in the upcoming configuration.
430 CfgMgr::instance().getCurrentCfg()->applyLoggingCfg();
431
432 // Not initial configuration so someone can believe we reverted
433 // to the previous configuration. It is not the case so be clear
434 // about this.
436 }
437
439 try {
440 // Handle events registered by hooks using external IOService objects.
442 } catch (const std::exception& ex) {
443 std::ostringstream err;
444 err << "Error initializing hooks: "
445 << ex.what();
447 }
448
449 return (result);
450}
451
453ControlledDhcpv6Srv::commandConfigTestHandler(const string&,
454 ConstElementPtr args) {
455 const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
456 ConstElementPtr dhcp6;
457 string message;
458
459 // Command arguments are expected to be:
460 // { "Dhcp6": { ... } }
461 if (!args) {
462 message = "Missing mandatory 'arguments' parameter.";
463 } else {
464 dhcp6 = args->get("Dhcp6");
465 if (!dhcp6) {
466 message = "Missing mandatory 'Dhcp6' parameter.";
467 } else if (dhcp6->getType() != Element::map) {
468 message = "'Dhcp6' parameter expected to be a map.";
469 }
470 }
471
472 // Check unsupported objects.
473 if (message.empty()) {
474 for (auto const& obj : args->mapValue()) {
475 const string& obj_name = obj.first;
476 if (obj_name != "Dhcp6") {
478 .arg(obj_name);
479 if (message.empty()) {
480 message = "Unsupported '" + obj_name + "' parameter";
481 } else {
482 message += " (and '" + obj_name + "')";
483 }
484 }
485 }
486 if (!message.empty()) {
487 message += ".";
488 }
489 }
490
491 if (!message.empty()) {
492 // Something is amiss with arguments, return a failure response.
493 ConstElementPtr result = isc::config::createAnswer(status_code,
494 message);
495 return (result);
496 }
497
498 // stop thread pool (if running)
499 MultiThreadingCriticalSection cs;
500
501 // We are starting the configuration process so we should remove any
502 // staging configuration that has been created during previous
503 // configuration attempts.
505
506 // Now we check the server proper.
507 return (checkConfig(dhcp6));
508}
509
511ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
512 ConstElementPtr args) {
513 std::ostringstream message;
514 int64_t max_period = 0;
515 std::string origin;
516
517 // If the args map does not contain 'origin' parameter, the default type
518 // will be used (user command).
519 auto type = NetworkState::USER_COMMAND;
520
521 // Parse arguments to see if the 'max-period' or 'origin' parameters have
522 // been specified.
523 if (args) {
524 // Arguments must be a map.
525 if (args->getType() != Element::map) {
526 message << "arguments for the 'dhcp-disable' command must be a map";
527
528 } else {
529 ConstElementPtr max_period_element = args->get("max-period");
530 // max-period is optional.
531 if (max_period_element) {
532 // It must be an integer, if specified.
533 if (max_period_element->getType() != Element::integer) {
534 message << "'max-period' argument must be a number";
535
536 } else {
537 // It must be positive integer.
538 max_period = max_period_element->intValue();
539 if (max_period <= 0) {
540 message << "'max-period' must be positive integer";
541 }
542 }
543 }
544 // 'origin-id' replaces the older parameter 'origin' since Kea 2.5.8
545 // stable release. However, the 'origin' is kept for backward compatibility
546 // with Kea versions before 2.5.8. It is common to receive both parameters
547 // because HA hook library sends both in case the partner server hasn't been
548 // upgraded to the new version. The 'origin-id' takes precedence over the
549 // 'origin'.
550 ConstElementPtr origin_id_element = args->get("origin-id");
551 ConstElementPtr origin_element = args->get("origin");
552 // The 'origin-id' and 'origin' arguments are optional.
553 if (origin_id_element) {
554 if (origin_id_element->getType() == Element::integer) {
555 type = origin_id_element->intValue();
556 } else {
557 message << "'origin-id' argument must be a number";
558 }
559 } else if (origin_element) {
560 switch (origin_element->getType()) {
561 case Element::string:
562 origin = origin_element->stringValue();
563 if (origin == "ha-partner") {
565 } else if (origin != "user") {
566 if (origin.empty()) {
567 origin = "(empty string)";
568 }
569 message << "invalid value used for 'origin' parameter: "
570 << origin;
571 }
572 break;
573 case Element::integer:
574 type = origin_element->intValue();
575 break;
576 default:
577 // It must be a string or a number, if specified.
578 message << "'origin' argument must be a string or a number";
579 }
580 }
581 }
582 }
583
584 // No error occurred, so let's disable the service.
585 if (message.tellp() == 0) {
586 message << "DHCPv6 service disabled";
587 if (max_period > 0) {
588 message << " for " << max_period << " seconds";
589
590 // The user specified that the DHCP service should resume not
591 // later than in max-period seconds. If the 'dhcp-enable' command
592 // is not sent, the DHCP service will resume automatically.
593 network_state_->delayedEnableService(static_cast<unsigned>(max_period),
594 type);
595 }
596 network_state_->disableService(type);
597
598 // Success.
599 return (config::createAnswer(CONTROL_RESULT_SUCCESS, message.str()));
600 }
601
602 // Failure.
603 return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
604}
605
607ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&,
608 ConstElementPtr args) {
609 std::ostringstream message;
610 std::string origin;
611
612 // If the args map does not contain 'origin' parameter, the default type
613 // will be used (user command).
614 auto type = NetworkState::USER_COMMAND;
615
616 // Parse arguments to see if the 'origin' parameter has been specified.
617 if (args) {
618 // Arguments must be a map.
619 if (args->getType() != Element::map) {
620 message << "arguments for the 'dhcp-enable' command must be a map";
621
622 } else {
623 // 'origin-id' replaces the older parameter 'origin' since Kea 2.5.8
624 // stable release. However, the 'origin' is kept for backward compatibility
625 // with Kea versions before 2.5.8. It is common to receive both parameters
626 // because HA hook library sends both in case the partner server hasn't been
627 // upgraded to the new version. The 'origin-id' takes precedence over the
628 // 'origin'.
629 ConstElementPtr origin_id_element = args->get("origin-id");
630 ConstElementPtr origin_element = args->get("origin");
631 // The 'origin-id' and 'origin' arguments are optional.
632 if (origin_id_element) {
633 if (origin_id_element->getType() == Element::integer) {
634 type = origin_id_element->intValue();
635 } else {
636 message << "'origin-id' argument must be a number";
637 }
638 } else if (origin_element) {
639 switch (origin_element->getType()) {
640 case Element::string:
641 origin = origin_element->stringValue();
642 if (origin == "ha-partner") {
644 } else if (origin != "user") {
645 if (origin.empty()) {
646 origin = "(empty string)";
647 }
648 message << "invalid value used for 'origin' parameter: "
649 << origin;
650 }
651 break;
652 case Element::integer:
653 type = origin_element->intValue();
654 break;
655 default:
656 // It must be a string or a number, if specified.
657 message << "'origin' argument must be a string or a number";
658 }
659 }
660 }
661 }
662
663 // No error occurred, so let's enable the service.
664 if (message.tellp() == 0) {
665 network_state_->enableService(type);
666
667 // Success.
669 "DHCP service successfully enabled"));
670 }
671
672 // Failure.
673 return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
674}
675
677ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
679 ElementPtr arguments = Element::createMap();
680 arguments->set("extended", extended);
683 arguments);
684 return (answer);
685}
686
688ControlledDhcpv6Srv::commandBuildReportHandler(const string&,
690 ConstElementPtr answer =
692 return (answer);
693}
694
696ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&,
697 ConstElementPtr args) {
698 int status_code = CONTROL_RESULT_ERROR;
699 string message;
700
701 // args must be { "remove": <bool> }
702 if (!args) {
703 message = "Missing mandatory 'remove' parameter.";
704 } else {
705 ConstElementPtr remove_name = args->get("remove");
706 if (!remove_name) {
707 message = "Missing mandatory 'remove' parameter.";
708 } else if (remove_name->getType() != Element::boolean) {
709 message = "'remove' parameter expected to be a boolean.";
710 } else {
711 bool remove_lease = remove_name->boolValue();
712 server_->alloc_engine_->reclaimExpiredLeases6(0, 0, remove_lease);
713 status_code = 0;
714 message = "Reclamation of expired leases is complete.";
715 }
716 }
717 ConstElementPtr answer = isc::config::createAnswer(status_code, message);
718 return (answer);
719}
720
722ControlledDhcpv6Srv::commandSubnet6SelectTestHandler(const string&,
723 ConstElementPtr args) {
724 if (!args) {
725 return (createAnswer(CONTROL_RESULT_ERROR, "empty arguments"));
726 }
727 if (args->getType() != Element::map) {
728 return (createAnswer(CONTROL_RESULT_ERROR, "arguments must be a map"));
729 }
730 SubnetSelector selector;
732 for (auto const& entry : args->mapValue()) {
733 ostringstream errmsg;
734 if (entry.first == "interface") {
735 if (entry.second->getType() != Element::string) {
736 errmsg << "'interface' entry must be a string";
737 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
738 }
739 selector.iface_name_ = entry.second->stringValue();
740 continue;
741 } if (entry.first == "interface-id") {
742 if (entry.second->getType() != Element::string) {
743 errmsg << "'interface-id' entry must be a string";
744 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
745 }
746 try {
747 string str = entry.second->stringValue();
748 vector<uint8_t> id = util::str::quotedStringToBinary(str);
749 if (id.empty()) {
751 }
752 if (id.empty()) {
753 errmsg << "'interface-id' must be not empty";
754 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
755 }
756 selector.interface_id_ = OptionPtr(new Option(Option::V6,
758 id));
759 continue;
760 } catch (...) {
761 errmsg << "value of 'interface-id' was not recognized";
762 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
763 }
764 } else if (entry.first == "remote") {
765 if (entry.second->getType() != Element::string) {
766 errmsg << "'remote' entry must be a string";
767 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
768 }
769 try {
770 IOAddress addr(entry.second->stringValue());
771 if (!addr.isV6()) {
772 errmsg << "bad 'remote' entry: not IPv6";
773 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
774 }
775 selector.remote_address_ = addr;
776 continue;
777 } catch (const exception& ex) {
778 errmsg << "bad 'remote' entry: " << ex.what();
779 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
780 }
781 } else if (entry.first == "link") {
782 if (entry.second->getType() != Element::string) {
783 errmsg << "'link' entry must be a string";
784 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
785 }
786 try {
787 IOAddress addr(entry.second->stringValue());
788 if (!addr.isV6()) {
789 errmsg << "bad 'link' entry: not IPv6";
790 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
791 }
792 selector.first_relay_linkaddr_ = addr;
793 continue;
794 } catch (const exception& ex) {
795 errmsg << "bad 'link' entry: " << ex.what();
796 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
797 }
798 } else if (entry.first == "classes") {
799 if (entry.second->getType() != Element::list) {
801 "'classes' entry must be a list"));
802 }
803 for (auto const& item : entry.second->listValue()) {
804 if (!item || (item->getType() != Element::string)) {
805 errmsg << "'classes' entry must be a list of strings";
806 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
807 }
808 // Skip empty client classes.
809 if (!item->stringValue().empty()) {
810 selector.client_classes_.insert(item->stringValue());
811 }
812 }
813 continue;
814 } else {
815 errmsg << "unknown entry '" << entry.first << "'";
816 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
817 }
818 }
820 getCfgSubnets6()->selectSubnet(selector);
821 if (!subnet) {
822 return (createAnswer(CONTROL_RESULT_EMPTY, "no subnet selected"));
823 }
824 SharedNetwork6Ptr network;
825 subnet->getSharedNetwork(network);
826 ostringstream msg;
827 if (network) {
828 msg << "selected shared network '" << network->getName()
829 << "' starting with subnet '" << subnet->toText()
830 << "' id " << subnet->getID();
831 } else {
832 msg << "selected subnet '" << subnet->toText()
833 << "' id " << subnet->getID();
834 }
835 return (createAnswer(CONTROL_RESULT_SUCCESS, msg.str()));
836}
837
839ControlledDhcpv6Srv::commandServerTagGetHandler(const std::string&,
841 const std::string& tag =
842 CfgMgr::instance().getCurrentCfg()->getServerTag();
843 ElementPtr response = Element::createMap();
844 response->set("server-tag", Element::create(tag));
845
846 return (createAnswer(CONTROL_RESULT_SUCCESS, response));
847}
848
850ControlledDhcpv6Srv::commandConfigBackendPullHandler(const std::string&,
852 auto ctl_info = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo();
853 if (!ctl_info) {
854 return (createAnswer(CONTROL_RESULT_EMPTY, "No config backend."));
855 }
856
857 // stop thread pool (if running)
858 MultiThreadingCriticalSection cs;
859
860 // Reschedule the periodic CB fetch.
861 if (TimerMgr::instance()->isTimerRegistered("Dhcp6CBFetchTimer")) {
862 TimerMgr::instance()->cancel("Dhcp6CBFetchTimer");
863 TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
864 }
865
866 // Code from cbFetchUpdates.
867 // The configuration to use is the current one because this is called
868 // after the configuration manager commit.
869 try {
870 auto srv_cfg = CfgMgr::instance().getCurrentCfg();
871 auto mode = CBControlDHCPv6::FetchMode::FETCH_UPDATE;
872 server_->getCBControl()->databaseConfigFetch(srv_cfg, mode);
873 } catch (const std::exception& ex) {
875 .arg(ex.what());
877 "On demand configuration update failed: " +
878 string(ex.what())));
879 }
881 "On demand configuration update successful."));
882}
883
885ControlledDhcpv6Srv::commandStatusGetHandler(const string&,
886 ConstElementPtr /*args*/) {
888 status->set("pid", Element::create(static_cast<int>(getpid())));
889
890 auto now = boost::posix_time::second_clock::universal_time();
891 // Sanity check: start_ is always initialized.
892 if (!start_.is_not_a_date_time()) {
893 auto uptime = now - start_;
894 status->set("uptime", Element::create(uptime.total_seconds()));
895 }
896
897 auto last_commit = CfgMgr::instance().getCurrentCfg()->getLastCommitTime();
898 if (!last_commit.is_not_a_date_time()) {
899 auto reload = now - last_commit;
900 status->set("reload", Element::create(reload.total_seconds()));
901 }
902
903 auto& mt_mgr = MultiThreadingMgr::instance();
904 if (mt_mgr.getMode()) {
905 status->set("multi-threading-enabled", Element::create(true));
906 status->set("thread-pool-size", Element::create(static_cast<int32_t>(
907 MultiThreadingMgr::instance().getThreadPoolSize())));
908 status->set("packet-queue-size", Element::create(static_cast<int32_t>(
909 MultiThreadingMgr::instance().getPacketQueueSize())));
910 ElementPtr queue_stats = Element::createList();
911 queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(10)));
912 queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(100)));
913 queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(1000)));
914 status->set("packet-queue-statistics", queue_stats);
915
916 } else {
917 status->set("multi-threading-enabled", Element::create(false));
918 }
919
920 // Merge lease manager status.
921 ElementPtr lm_info;
924 }
925 if (lm_info && (lm_info->getType() == Element::map)) {
926 for (auto const& entry : lm_info->mapValue()) {
927 status->set(entry.first, entry.second);
928 }
929 }
930
931 status->set("extended-info-tables", Element::create(
932 CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getExtendedInfoTablesEnabled()));
933
934 // Iterate through the interfaces and get all the errors.
935 ElementPtr socket_errors(Element::createList());
936 for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
937 for (std::string const& error : interface->getErrors()) {
938 socket_errors->add(Element::create(error));
939 }
940 }
941
942 // Abstract the information from all sockets into a single status.
944 if (socket_errors->empty()) {
945 sockets->set("status", Element::create("ready"));
946 } else {
947 ReconnectCtlPtr const reconnect_ctl(
948 CfgMgr::instance().getCurrentCfg()->getCfgIface()->getReconnectCtl());
949 if (reconnect_ctl && reconnect_ctl->retriesLeft()) {
950 sockets->set("status", Element::create("retrying"));
951 } else {
952 sockets->set("status", Element::create("failed"));
953 }
954 sockets->set("errors", socket_errors);
955 }
956 status->set("sockets", sockets);
957
958 status->set("dhcp-state", network_state_->toElement());
959
960 return (createAnswer(CONTROL_RESULT_SUCCESS, status));
961}
962
964ControlledDhcpv6Srv::commandStatisticSetMaxSampleCountAllHandler(const string&,
965 ConstElementPtr args) {
966 StatsMgr& stats_mgr = StatsMgr::instance();
968 // Update the default parameter.
969 long max_samples = stats_mgr.getMaxSampleCountDefault();
970 CfgMgr::instance().getCurrentCfg()->addConfiguredGlobal(
971 "statistic-default-sample-count", Element::create(max_samples));
972 return (answer);
973}
974
976ControlledDhcpv6Srv::commandStatisticSetMaxSampleAgeAllHandler(const string&,
977 ConstElementPtr args) {
978 StatsMgr& stats_mgr = StatsMgr::instance();
980 // Update the default parameter.
981 auto duration = stats_mgr.getMaxSampleAgeDefault();
982 long max_age = toSeconds(duration);
983 CfgMgr::instance().getCurrentCfg()->addConfiguredGlobal(
984 "statistic-default-sample-age", Element::create(max_age));
985 return (answer);
986}
987
989ControlledDhcpv6Srv::commandLfcStartHandler(const string&, ConstElementPtr) {
991 return (LeaseMgrFactory::instance().lfcStartHandler());
992 }
994 "no lease backend"));
995}
996
1000
1001 // Allow DB reconnect on startup. The database connection parameters specify
1002 // respective details.
1004
1005 // Single stream instance used in all error clauses
1006 std::ostringstream err;
1007
1008 if (!srv) {
1009 err << "Server object not initialized, can't process config.";
1011 }
1012
1014 .arg(srv->redactConfig(config)->str());
1015
1016 // Destroy lease manager before hooks unload.
1018
1019 // Destroy host manager before hooks unload.
1021
1023
1024 // Check that configuration was successful. If not, do not reopen sockets
1025 // and don't bother with DDNS stuff.
1026 try {
1027 int rcode = 0;
1028 isc::config::parseAnswer(rcode, answer);
1029 if (rcode != 0) {
1030 return (answer);
1031 }
1032 } catch (const std::exception& ex) {
1033 err << "Failed to process configuration:" << ex.what();
1035 }
1036
1037 // Re-open lease and host database with new parameters.
1038 try {
1040 std::bind(&ControlledDhcpv6Srv::dbLostCallback, srv, ph::_1);
1041
1043 std::bind(&ControlledDhcpv6Srv::dbRecoveredCallback, srv, ph::_1);
1044
1046 std::bind(&ControlledDhcpv6Srv::dbFailedCallback, srv, ph::_1);
1047
1048 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
1049 string params = "universe=6";
1050 if (cfg_db->getExtendedInfoTablesEnabled()) {
1051 params += " extended-info-tables=true";
1052 }
1053 cfg_db->setAppendedParameters(params);
1054 cfg_db->createManagers();
1055 // Reset counters related to connections as all managers have been recreated.
1056 srv->getNetworkState()->resetForDbConnection();
1057 srv->getNetworkState()->resetForLocalCommands();
1058 srv->getNetworkState()->resetForRemoteCommands();
1059 } catch (const std::exception& ex) {
1060 err << "Unable to open database: " << ex.what();
1062 }
1063
1064 // Regenerate server identifier if needed.
1065 try {
1066 const std::string duid_file =
1067 std::string(CfgMgr::instance().getDataDir()) + "/" +
1068 std::string(SERVER_DUID_FILE);
1069 DuidPtr duid = CfgMgr::instance().getStagingCfg()->getCfgDUID()->create(duid_file);
1070 server_->serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
1071 if (duid) {
1073 .arg(duid->toText())
1074 .arg(duid_file);
1075 }
1076
1077 } catch (const std::exception& ex) {
1078 err << "unable to configure server identifier: " << ex.what();
1080 }
1081
1082 // Server will start DDNS communications if its enabled.
1083 try {
1084 srv->startD2();
1085 } catch (const std::exception& ex) {
1086 err << "Error starting DHCP_DDNS client after server reconfiguration: "
1087 << ex.what();
1089 }
1090
1091 // Setup DHCPv4-over-DHCPv6 IPC
1092 try {
1094 } catch (const std::exception& ex) {
1095 err << "error starting DHCPv4-over-DHCPv6 IPC "
1096 " after server reconfiguration: " << ex.what();
1098 }
1099
1100 // Configure DHCP packet queueing
1101 try {
1103 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
1104 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) {
1106 .arg(IfaceMgr::instance().getPacketQueue6()->getInfoStr());
1107 }
1108
1109 } catch (const std::exception& ex) {
1110 err << "Error setting packet queue controls after server reconfiguration: "
1111 << ex.what();
1113 }
1114
1115 // Configure a callback to shut down the server when the bind socket
1116 // attempts exceeded.
1118 std::bind(&ControlledDhcpv6Srv::openSocketsFailedCallback, srv, ph::_1);
1119
1120 // Configuration may change active interfaces. Therefore, we have to reopen
1121 // sockets according to new configuration. It is possible that this
1122 // operation will fail for some interfaces but the openSockets function
1123 // guards against exceptions and invokes a callback function to
1124 // log warnings. Since we allow that this fails for some interfaces there
1125 // is no need to rollback configuration if socket fails to open on any
1126 // of the interfaces.
1127 CfgMgr::instance().getStagingCfg()->getCfgIface()->
1128 openSockets(AF_INET6, srv->getServerPort());
1129
1130 // Install the timers for handling leases reclamation.
1131 try {
1132 CfgMgr::instance().getStagingCfg()->getCfgExpiration()->
1133 setupTimers(&ControlledDhcpv6Srv::reclaimExpiredLeases,
1134 &ControlledDhcpv6Srv::deleteExpiredReclaimedLeases,
1135 server_);
1136
1137 } catch (const std::exception& ex) {
1138 err << "unable to setup timers for periodically running the"
1139 " reclamation of the expired leases: "
1140 << ex.what() << ".";
1142 }
1143
1144 // Setup config backend polling, if configured for it.
1145 auto ctl_info = CfgMgr::instance().getStagingCfg()->getConfigControlInfo();
1146 if (ctl_info) {
1147 long fetch_time = static_cast<long>(ctl_info->getConfigFetchWaitTime());
1148 // Only schedule the CB fetch timer if the fetch wait time is greater
1149 // than 0.
1150 if (fetch_time > 0) {
1151 // When we run unit tests, we want to use milliseconds unit for the
1152 // specified interval. Otherwise, we use seconds. Note that using
1153 // milliseconds as a unit in unit tests prevents us from waiting 1
1154 // second on more before the timer goes off. Instead, we wait one
1155 // millisecond which significantly reduces the test time.
1156 if (!server_->inTestMode()) {
1157 fetch_time = 1000 * fetch_time;
1158 }
1159
1160 boost::shared_ptr<unsigned> failure_count(new unsigned(0));
1162 registerTimer("Dhcp6CBFetchTimer",
1163 std::bind(&ControlledDhcpv6Srv::cbFetchUpdates,
1164 server_, CfgMgr::instance().getStagingCfg(),
1165 failure_count),
1166 fetch_time,
1168 TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
1169 }
1170 }
1171
1172 // Finally, we can commit runtime option definitions in libdhcp++. This is
1173 // exception free.
1175
1177 if (notify_libraries) {
1178 return (notify_libraries);
1179 }
1180
1181 // Initialize the allocators. If the user selected a Free Lease Queue Allocator
1182 // for any of the subnets, the server will now populate free leases to the queue.
1183 // It may take a while!
1184 try {
1185 CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->initAllocatorsAfterConfigure();
1186
1187 } catch (const std::exception& ex) {
1188 err << "Error initializing the lease allocators: "
1189 << ex.what();
1191 }
1192
1193 // Apply multi threading settings.
1194 // @note These settings are applied/updated only if no errors occur while
1195 // applying the new configuration.
1196 // @todo This should be fixed.
1197 try {
1198 CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
1199 } catch (const std::exception& ex) {
1200 err << "Error applying multi threading settings: "
1201 << ex.what();
1203 }
1204
1205 return (answer);
1206}
1207
1211 // This hook point notifies hooks libraries that the configuration of the
1212 // DHCPv6 server has completed. It provides the hook library with the pointer
1213 // to the common IO service object, new server configuration in the JSON
1214 // format and with the pointer to the configuration storage where the
1215 // parsed configuration is stored.
1216 if (HooksManager::calloutsPresent(Hooks.hooks_index_dhcp6_srv_configured_)) {
1218
1219 callout_handle->setArgument("io_context", srv->getIOService());
1220 callout_handle->setArgument("network_state", srv->getNetworkState());
1221 callout_handle->setArgument("json_config", config);
1222 callout_handle->setArgument("server_config", CfgMgr::instance().getStagingCfg());
1223
1224 HooksManager::callCallouts(Hooks.hooks_index_dhcp6_srv_configured_,
1225 *callout_handle);
1226
1227 // If next step is DROP, report a configuration error.
1228 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1229 string error;
1230 try {
1231 callout_handle->getArgument("error", error);
1232 } catch (NoSuchArgument const& ex) {
1233 error = "unknown error";
1234 }
1236 }
1237 }
1238
1239 return (ConstElementPtr());
1240}
1241
1245
1246 if (!srv) {
1248 "Server object not initialized, can't process config.");
1249 return (no_srv);
1250 }
1251
1253 .arg(srv->redactConfig(config)->str());
1254
1255 return (configureDhcp6Server(*srv, config, true));
1256}
1257
1258ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port /*= DHCP6_SERVER_PORT*/,
1259 uint16_t client_port /*= 0*/)
1260 : Dhcpv6Srv(server_port, client_port), timer_mgr_(TimerMgr::instance()) {
1261 if (getInstance()) {
1263 "There is another Dhcpv6Srv instance already.");
1264 }
1265 server_ = this; // remember this instance for later use in handlers
1266
1267 // ProcessSpawn uses IO service to handle signal set events.
1269
1270 // TimerMgr uses IO service to run asynchronous timers.
1271 TimerMgr::instance()->setIOService(getIOService());
1272
1273 // Command managers use IO service to run asynchronous socket operations.
1276
1277 // Set the HTTP default socket address to the IPv6 (vs IPv4) loopback.
1279
1280 // Set the HTTP authentication default realm.
1282
1283 // DatabaseConnection uses IO service to run asynchronous timers.
1285
1286 // These are the commands always supported by the DHCPv6 server.
1287 // Please keep the list in alphabetic order.
1288 CommandMgr::instance().registerCommand("build-report",
1289 std::bind(&ControlledDhcpv6Srv::commandBuildReportHandler, this, ph::_1, ph::_2));
1290
1291 CommandMgr::instance().registerCommand("config-backend-pull",
1292 std::bind(&ControlledDhcpv6Srv::commandConfigBackendPullHandler, this, ph::_1, ph::_2));
1293
1295 std::bind(&ControlledDhcpv6Srv::commandConfigGetHandler, this, ph::_1, ph::_2));
1296
1297 CommandMgr::instance().registerCommand("config-hash-get",
1298 std::bind(&ControlledDhcpv6Srv::commandConfigHashGetHandler, this, ph::_1, ph::_2));
1299
1300 CommandMgr::instance().registerCommand("config-reload",
1301 std::bind(&ControlledDhcpv6Srv::commandConfigReloadHandler, this, ph::_1, ph::_2));
1302
1304 std::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, ph::_1, ph::_2));
1305
1306 CommandMgr::instance().registerCommand("config-test",
1307 std::bind(&ControlledDhcpv6Srv::commandConfigTestHandler, this, ph::_1, ph::_2));
1308
1309 CommandMgr::instance().registerCommand("config-write",
1310 std::bind(&ControlledDhcpv6Srv::commandConfigWriteHandler, this, ph::_1, ph::_2));
1311
1312 CommandMgr::instance().registerCommand("dhcp-enable",
1313 std::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, this, ph::_1, ph::_2));
1314
1315 CommandMgr::instance().registerCommand("dhcp-disable",
1316 std::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, ph::_1, ph::_2));
1317
1318 CommandMgr::instance().registerCommand("kea-lfc-start",
1319 std::bind(&ControlledDhcpv6Srv::commandLfcStartHandler, this, ph::_1, ph::_2));
1320
1321 CommandMgr::instance().registerCommand("leases-reclaim",
1322 std::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, ph::_1, ph::_2));
1323
1324 CommandMgr::instance().registerCommand("subnet6-select-test",
1325 std::bind(&ControlledDhcpv6Srv::commandSubnet6SelectTestHandler, this, ph::_1, ph::_2));
1326
1327 CommandMgr::instance().registerCommand("server-tag-get",
1328 std::bind(&ControlledDhcpv6Srv::commandServerTagGetHandler, this, ph::_1, ph::_2));
1329
1331 std::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, ph::_1, ph::_2));
1332
1334 std::bind(&ControlledDhcpv6Srv::commandStatusGetHandler, this, ph::_1, ph::_2));
1335
1336 CommandMgr::instance().registerCommand("version-get",
1337 std::bind(&ControlledDhcpv6Srv::commandVersionGetHandler, this, ph::_1, ph::_2));
1338
1339 // Register statistic related commands
1340 CommandMgr::instance().registerCommand("statistic-get",
1341 std::bind(&StatsMgr::statisticGetHandler, ph::_1, ph::_2));
1342
1343 CommandMgr::instance().registerCommand("statistic-reset",
1344 std::bind(&StatsMgr::statisticResetHandler, ph::_1, ph::_2));
1345
1346 CommandMgr::instance().registerCommand("statistic-remove",
1347 std::bind(&StatsMgr::statisticRemoveHandler, ph::_1, ph::_2));
1348
1349 CommandMgr::instance().registerCommand("statistic-get-all",
1350 std::bind(&StatsMgr::statisticGetAllHandler, ph::_1, ph::_2));
1351
1352 CommandMgr::instance().registerCommand("statistic-global-get-all",
1353 std::bind(&StatsMgr::statisticGlobalGetAllHandler, ph::_1, ph::_2));
1354
1355 CommandMgr::instance().registerCommand("statistic-reset-all",
1356 std::bind(&StatsMgr::statisticResetAllHandler, ph::_1, ph::_2));
1357
1358 CommandMgr::instance().registerCommand("statistic-remove-all",
1359 std::bind(&StatsMgr::statisticRemoveAllHandler, ph::_1, ph::_2));
1360
1361 CommandMgr::instance().registerCommand("statistic-sample-age-set",
1362 std::bind(&StatsMgr::statisticSetMaxSampleAgeHandler, ph::_1, ph::_2));
1363
1364 CommandMgr::instance().registerCommand("statistic-sample-age-set-all",
1365 std::bind(&ControlledDhcpv6Srv::commandStatisticSetMaxSampleAgeAllHandler, this, ph::_1, ph::_2));
1366
1367 CommandMgr::instance().registerCommand("statistic-sample-count-set",
1368 std::bind(&StatsMgr::statisticSetMaxSampleCountHandler, ph::_1, ph::_2));
1369
1370 CommandMgr::instance().registerCommand("statistic-sample-count-set-all",
1371 std::bind(&ControlledDhcpv6Srv::commandStatisticSetMaxSampleCountAllHandler, this, ph::_1, ph::_2));
1372}
1373
1375 setExitValue(exit_value);
1376 getIOService()->stop(); // Stop ASIO transmissions
1377 shutdown(); // Initiate DHCPv6 shutdown procedure.
1378}
1379
1381 try {
1382 MultiThreadingMgr::instance().apply(false, 0, 0);
1385
1386 // The closure captures either a shared pointer (memory leak)
1387 // or a raw pointer (pointing to a deleted object).
1391
1392 timer_mgr_->unregisterTimers();
1393
1394 cleanup();
1395
1396 // Close command sockets.
1399
1400 // Deregister any registered commands (please keep in alphabetic order)
1401 CommandMgr::instance().deregisterCommand("build-report");
1402 CommandMgr::instance().deregisterCommand("config-backend-pull");
1404 CommandMgr::instance().deregisterCommand("config-hash-get");
1405 CommandMgr::instance().deregisterCommand("config-reload");
1407 CommandMgr::instance().deregisterCommand("config-test");
1408 CommandMgr::instance().deregisterCommand("config-write");
1409 CommandMgr::instance().deregisterCommand("dhcp-disable");
1410 CommandMgr::instance().deregisterCommand("dhcp-enable");
1411 CommandMgr::instance().deregisterCommand("kea-lfc-start");
1412 CommandMgr::instance().deregisterCommand("leases-reclaim");
1413 CommandMgr::instance().deregisterCommand("subnet6-select-test");
1414 CommandMgr::instance().deregisterCommand("server-tag-get");
1416 CommandMgr::instance().deregisterCommand("statistic-get");
1417 CommandMgr::instance().deregisterCommand("statistic-get-all");
1418 CommandMgr::instance().deregisterCommand("statistic-global-get-all");
1419 CommandMgr::instance().deregisterCommand("statistic-remove");
1420 CommandMgr::instance().deregisterCommand("statistic-remove-all");
1421 CommandMgr::instance().deregisterCommand("statistic-reset");
1422 CommandMgr::instance().deregisterCommand("statistic-reset-all");
1423 CommandMgr::instance().deregisterCommand("statistic-sample-age-set");
1424 CommandMgr::instance().deregisterCommand("statistic-sample-age-set-all");
1425 CommandMgr::instance().deregisterCommand("statistic-sample-count-set");
1426 CommandMgr::instance().deregisterCommand("statistic-sample-count-set-all");
1428 CommandMgr::instance().deregisterCommand("version-get");
1429
1430 // Reset DatabaseConnection IO service.
1432 } catch (...) {
1433 // Don't want to throw exceptions from the destructor. The server
1434 // is shutting down anyway.
1435 }
1436
1437 server_ = NULL; // forget this instance. There should be no callback anymore
1438 // at this stage anyway.
1439}
1440
1441void
1442ControlledDhcpv6Srv::reclaimExpiredLeases(const size_t max_leases,
1443 const uint16_t timeout,
1444 const bool remove_lease,
1445 const uint16_t max_unwarned_cycles) {
1446 try {
1447 if (network_state_->isServiceEnabled()) {
1448 server_->alloc_engine_->reclaimExpiredLeases6(max_leases, timeout,
1449 remove_lease,
1450 max_unwarned_cycles);
1451 } else {
1453 .arg(CfgMgr::instance().getCurrentCfg()->
1454 getCfgExpiration()->getReclaimTimerWaitTime());
1455 }
1456 } catch (const std::exception& ex) {
1458 .arg(ex.what());
1459 }
1460 // We're using the ONE_SHOT timer so there is a need to re-schedule it.
1462}
1463
1464void
1465ControlledDhcpv6Srv::deleteExpiredReclaimedLeases(const uint32_t secs) {
1466 if (network_state_->isServiceEnabled()) {
1467 server_->alloc_engine_->deleteExpiredReclaimedLeases6(secs);
1468 }
1469
1470 // We're using the ONE_SHOT timer so there is a need to re-schedule it.
1472}
1473
1474bool
1475ControlledDhcpv6Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
1476 if (!db_reconnect_ctl) {
1477 // This should never happen
1479 return (false);
1480 }
1481
1482 // Disable service until the connection is recovered.
1483 if (db_reconnect_ctl->retriesLeft() == db_reconnect_ctl->maxRetries() &&
1484 db_reconnect_ctl->alterServiceState()) {
1485 network_state_->disableService(NetworkState::DB_CONNECTION + db_reconnect_ctl->id());
1486 }
1487
1489 .arg(db_reconnect_ctl->id())
1490 .arg(db_reconnect_ctl->timerName());
1491
1492 // If reconnect isn't enabled log it, initiate a shutdown if needed and
1493 // return false.
1494 if (!db_reconnect_ctl->retriesLeft() ||
1495 !db_reconnect_ctl->retryInterval()) {
1497 .arg(db_reconnect_ctl->retriesLeft())
1498 .arg(db_reconnect_ctl->retryInterval())
1499 .arg(db_reconnect_ctl->id())
1500 .arg(db_reconnect_ctl->timerName());
1501 if (db_reconnect_ctl->exitOnFailure()) {
1502 shutdownServer(EXIT_FAILURE);
1503 }
1504 return (false);
1505 }
1506
1507 return (true);
1508}
1509
1510bool
1511ControlledDhcpv6Srv::dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
1512 if (!db_reconnect_ctl) {
1513 // This should never happen
1515 return (false);
1516 }
1517
1518 // Enable service after the connection is recovered.
1519 if (db_reconnect_ctl->retriesLeft() != db_reconnect_ctl->maxRetries() &&
1520 db_reconnect_ctl->alterServiceState()) {
1521 network_state_->enableService(NetworkState::DB_CONNECTION + db_reconnect_ctl->id());
1522 }
1523
1525 .arg(db_reconnect_ctl->id())
1526 .arg(db_reconnect_ctl->timerName());
1527
1528 db_reconnect_ctl->resetRetries();
1529
1530 return (true);
1531}
1532
1533bool
1534ControlledDhcpv6Srv::dbFailedCallback(ReconnectCtlPtr db_reconnect_ctl) {
1535 if (!db_reconnect_ctl) {
1536 // This should never happen
1538 return (false);
1539 }
1540
1542 .arg(db_reconnect_ctl->maxRetries())
1543 .arg(db_reconnect_ctl->id())
1544 .arg(db_reconnect_ctl->timerName());
1545
1546 if (db_reconnect_ctl->exitOnFailure()) {
1547 shutdownServer(EXIT_FAILURE);
1548 }
1549
1550 return (true);
1551}
1552
1553void
1554ControlledDhcpv6Srv::openSocketsFailedCallback(ReconnectCtlPtr reconnect_ctl) {
1555 if (!reconnect_ctl) {
1556 // This should never happen
1558 return;
1559 }
1560
1562 .arg(reconnect_ctl->maxRetries());
1563
1564 if (reconnect_ctl->exitOnFailure()) {
1565 shutdownServer(EXIT_FAILURE);
1566 }
1567}
1568
1569void
1570ControlledDhcpv6Srv::cbFetchUpdates(const SrvConfigPtr& srv_cfg,
1571 boost::shared_ptr<unsigned> failure_count) {
1572 // stop thread pool (if running)
1573 MultiThreadingCriticalSection cs;
1574
1575 try {
1576 // Fetch any configuration backend updates since our last fetch.
1577 server_->getCBControl()->databaseConfigFetch(srv_cfg,
1578 CBControlDHCPv6::FetchMode::FETCH_UPDATE);
1579 (*failure_count) = 0;
1580
1581 } catch (const std::exception& ex) {
1583 .arg(ex.what());
1584
1585 // We allow at most 10 consecutive failures after which we stop
1586 // making further attempts to fetch the configuration updates.
1587 // Let's return without re-scheduling the timer.
1588 if (++(*failure_count) > 10) {
1591 return;
1592 }
1593 }
1594
1595 // Reschedule the timer to fetch new updates or re-try if
1596 // the previous attempt resulted in an error.
1597 if (TimerMgr::instance()->isTimerRegistered("Dhcp6CBFetchTimer")) {
1598 TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
1599 }
1600}
1601
1602} // namespace dhcp
1603} // namespace isc
CtrlAgentHooks Hooks
@ map
Definition data.h:160
@ integer
Definition data.h:153
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
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.
virtual isc::data::ConstElementPtr processCommand(const isc::data::ConstElementPtr &cmd)
Triggers command processing.
void registerCommand(const std::string &cmd, CommandHandler handler)
Registers specified command handler for a given command.
static std::string getHash(const isc::data::ConstElementPtr &config)
returns a hash of a given Element structure
void deregisterCommand(const std::string &cmd)
Deregisters specified command handler.
static CommandMgr & instance()
CommandMgr is a singleton class.
static std::string DEFAULT_AUTHENTICATION_REALM
Default HTTP authentication realm.
static isc::asiolink::IOAddress DEFAULT_SOCKET_ADDRESS
Default socket address (127.0.0.1).
void closeCommandSockets()
Close http control sockets.
static HttpCommandMgr & instance()
HttpCommandMgr is a singleton class.
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the http command manager.
static UnixCommandMgr & instance()
UnixCommandMgr is a singleton class.
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the unix command manager.
void closeCommandSockets()
Shuts down any open unix control sockets.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Create a NullElement.
Definition data.cc:299
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:354
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:349
static void setIOService(const isc::asiolink::IOServicePtr &io_service)
Sets IO service to be used by the database backends.
static DbCallback db_recovered_callback_
Optional callback function to invoke if an opened connection recovery succeeded.
static DbCallback db_failed_callback_
Optional callback function to invoke if an opened connection recovery failed.
static DbCallback db_lost_callback_
Optional callback function to invoke if an opened connection is lost.
RAII class to enable DB reconnect retries on server startup.
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.
static OpenSocketsFailedCallback open_sockets_failed_callback_
Optional callback function to invoke if all retries of the opening sockets fail.
Definition cfg_iface.h:361
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition cfgmgr.cc:121
void commit()
Commits the staging configuration.
Definition cfgmgr.cc:93
void clearStagingConfiguration()
Remove staging configuration.
Definition cfgmgr.cc:88
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:116
static void apply(data::ConstElementPtr value)
apply multi threading configuration
void insert(const ClientClass &class_name)
Insert an element.
Definition classify.h:160
Controlled version of the DHCPv6 server.
void init(const std::string &config_file)
Initializes the server.
void cleanup()
Performs cleanup, immediately before termination.
static isc::data::ConstElementPtr finishConfigHookLibraries(isc::data::ConstElementPtr config)
Configuration checker for hook libraries.
virtual ~ControlledDhcpv6Srv()
Destructor.
static isc::data::ConstElementPtr processConfig(isc::data::ConstElementPtr config)
Configuration processor.
virtual void shutdownServer(int exit_value)
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)
Configure DHCPv6 server using the configuration file specified.
static isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr config)
Configuration checker.
ControlledDhcpv6Srv(uint16_t server_port=DHCP6_SERVER_PORT, uint16_t client_port=0)
Constructor.
virtual void open()
Open communication socket.
static Dhcp6to4Ipc & instance()
Returns pointer to the sole instance of Dhcp6to4Ipc.
void shutdown() override
Instructs the server to shut down.
Definition dhcp6_srv.cc:371
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition dhcp6_srv.h:1245
uint16_t getServerPort() const
Get UDP port on which server should listen.
NetworkStatePtr & getNetworkState()
Returns pointer to the network state used by the server.
Definition dhcp6_srv.h:115
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition dhcp6_srv.h:1253
Dhcpv6Srv(uint16_t server_port=DHCP6_SERVER_PORT, uint16_t client_port=0)
Default constructor.
Definition dhcp6_srv.cc:272
static std::string getVersion(bool extended)
returns Kea version on stdout and exit.
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition dhcp6_srv.h:110
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
static void create()
Creates new instance of the HostMgr.
Definition host_mgr.cc:52
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:49
static TrackingLeaseMgr & instance()
Return current lease manager.
static void destroy()
Destroy lease manager.
static bool haveInstance()
Indicates if the lease manager has been instantiated.
virtual data::ElementPtr getStatus() const
Return status information.
static void commitRuntimeOptionDefs()
Commits runtime option definitions.
Definition libdhcp++.cc:248
static bool isLFCProcessRunning(const std::string file_name, Universe u)
Check if LFC is running.
static const unsigned int DB_CONNECTION
The network state is being altered by the DB connection recovery mechanics.
static const unsigned int USER_COMMAND
Origin of the network state transition.
static const unsigned int HA_REMOTE_COMMAND
The network state is being altered by a "dhcp-disable" or "dhcp-enable" command sent by a HA partner.
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:62
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition timer_mgr.cc:446
@ NEXT_STEP_DROP
drop the packet
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static boost::shared_ptr< CalloutHandle > createCalloutHandle()
Return callout handle.
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
std::string getConfigFile() const
Returns config file name.
Definition daemon.cc:107
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:269
isc::asiolink::IOSignalSetPtr signal_set_
A pointer to the object installing custom signal handlers.
Definition daemon.h:272
boost::posix_time::ptime start_
Timestamp of the start of the daemon.
Definition daemon.h:278
void checkWriteConfigFile(std::string &file)
Checks the to-be-written configuration file name.
Definition daemon.cc:132
void setExitValue(int value)
Sets the exit value.
Definition daemon.h:242
isc::data::ConstElementPtr redactConfig(isc::data::ConstElementPtr const &config)
Redact a configuration.
Definition daemon.cc:297
static StatsMgr & instance()
Statistics Manager accessor method.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
void apply(bool enabled, uint32_t thread_count, uint32_t queue_size)
Apply the multi-threading related settings.
This file contains several functions and constants that are used for handling commands and responses ...
@ D6O_INTERFACE_ID
Definition dhcp6.h:38
@ 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.
isc::data::ConstElementPtr statisticSetMaxSampleCountAllHandler(const isc::data::ConstElementPtr &params)
Handles statistic-sample-count-set-all command.
static isc::data::ConstElementPtr statisticResetHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-reset command.
static isc::data::ConstElementPtr statisticGetAllHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-get-all command.
static isc::data::ConstElementPtr statisticRemoveHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-remove command.
static isc::data::ConstElementPtr statisticGetHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-get command.
static isc::data::ConstElementPtr statisticGlobalGetAllHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-global-get-all command.
isc::data::ConstElementPtr statisticSetMaxSampleAgeAllHandler(const isc::data::ConstElementPtr &params)
Handles statistic-sample-age-set-all command.
static isc::data::ConstElementPtr statisticResetAllHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-reset-all command.
static isc::data::ConstElementPtr statisticSetMaxSampleAgeHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-sample-age-set command.
static isc::data::ConstElementPtr statisticRemoveAllHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-remove-all command.
static isc::data::ConstElementPtr statisticSetMaxSampleCountHandler(const std::string &name, const isc::data::ConstElementPtr &params)
Handles statistic-sample-count-set command.
uint32_t getMaxSampleCountDefault() const
Get default count limit.
const StatsDuration & getMaxSampleAgeDefault() const
Get default duration limit.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_FATAL(LOGGER, MESSAGE)
Macro to conveniently test fatal output and log it.
Definition macros.h:38
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const int CONTROL_RESULT_EMPTY
Status code indicating that the specified command was completed correctly, but failed to produce any ...
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns arguments or text status code.
ConstElementPtr createCommand(const std::string &command)
Creates a standard command message with no argument (of the form { "command": "my_command" }...
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
Creates a standard config/command level answer message.
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
const int CONTROL_RESULT_COMMAND_UNSUPPORTED
Status code indicating that the specified command is not supported.
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
@ error
Definition db_log.h:118
std::string getConfigReport()
Definition cfgrpt.cc:20
const isc::log::MessageID DHCP6_DB_RECONNECT_NO_DB_CTL
const isc::log::MessageID DHCP6_OPEN_SOCKETS_NO_RECONNECT_CTL
const isc::log::MessageID DHCP6_USING_SERVERID
const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:623
const isc::log::MessageID DHCP6_DB_RECONNECT_SUCCEEDED
isc::data::ConstElementPtr configureDhcp6Server(Dhcpv6Srv &server, isc::data::ConstElementPtr config_set, bool check_only, bool extra_checks)
Configure DHCPv6 server (Dhcpv6Srv) with a set of configuration values.
const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_SKIPPED
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition iface_mgr.h:514
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
const int DBG_DHCP6_COMMAND
Debug level used to log receiving commands.
Definition dhcp6_log.h:28
const isc::log::MessageID DHCP6_CB_PERIODIC_FETCH_UPDATES_FAIL
const isc::log::MessageID DHCP6_RECLAIM_EXPIRED_LEASES_FAIL
const isc::log::MessageID DHCP6_OPEN_SOCKETS_FAILED
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
const isc::log::MessageID DHCP6_DYNAMIC_RECONFIGURATION_SUCCESS
const isc::log::MessageID DHCP6_CB_ON_DEMAND_FETCH_UPDATES_FAIL
const isc::log::MessageID DHCP6_CB_PERIODIC_FETCH_UPDATES_RETRIES_EXHAUSTED
const isc::log::MessageID DHCP6_NOT_RUNNING
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
const isc::log::MessageID DHCP6_DYNAMIC_RECONFIGURATION_FAIL
const isc::log::MessageID DHCP6_CONFIG_UNSUPPORTED_OBJECT
const isc::log::MessageID DHCP6_CONFIG_UNRECOVERABLE_ERROR
const isc::log::MessageID DHCP6_CONFIG_RECEIVED
const isc::log::MessageID DHCP6_DB_RECONNECT_DISABLED
const isc::log::MessageID DHCP6_DYNAMIC_RECONFIGURATION
const isc::log::MessageID DHCP6_DB_RECONNECT_LOST_CONNECTION
const int DBG_DHCP6_BASIC
Debug level used to trace basic operations within the code.
Definition dhcp6_log.h:31
isc::log::Logger dhcp6_logger(DHCP6_APP_LOGGER_NAME)
Base logger for DHCPv6 server.
Definition dhcp6_log.h:88
const isc::log::MessageID DHCP6_MULTI_THREADING_INFO
const isc::log::MessageID DHCP6_DB_RECONNECT_FAILED
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
PerfMonMgrPtr mgr
PerfMonMgr singleton.
long toSeconds(const StatsDuration &dur)
Returns the number of seconds in a duration.
Definition observation.h:49
void decodeFormattedHexString(const string &hex_string, vector< uint8_t > &binary)
Converts a formatted string of hexadecimal digits into a vector.
Definition str.cc:212
vector< uint8_t > quotedStringToBinary(const string &quoted_string)
Converts a string in quotes into vector.
Definition str.cc:139
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
Defines the logger used by the top-level component of kea-lfc.
std::string iface_name_
Name of the interface on which the message was received.
ClientClasses client_classes_
Classes that the client belongs to.
asiolink::IOAddress remote_address_
Source address of the message.
OptionPtr interface_id_
Interface id option.
asiolink::IOAddress first_relay_linkaddr_
First relay link address.