Kea 2.7.5
ctrl_dhcp6_srv.cc
Go to the documentation of this file.
1// Copyright (C) 2014-2024 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>
17#include <dhcp/libdhcp++.h>
19#include <dhcp6/dhcp6_log.h>
20#include <dhcp6/dhcp6to4_ipc.h>
25#include <dhcpsrv/cfgmgr.h>
26#include <dhcpsrv/db_type.h>
27#include <dhcpsrv/host_mgr.h>
29#include <hooks/hooks.h>
30#include <hooks/hooks_manager.h>
32#include <stats/stats_mgr.h>
33#include <util/encode/encode.h>
35
36#include <signal.h>
37
38#include <sstream>
39
40using namespace isc::asiolink;
41using namespace isc::config;
42using namespace isc::data;
43using namespace isc::db;
44using namespace isc::dhcp;
45using namespace isc::hooks;
46using namespace isc::stats;
47using namespace isc::util;
48using namespace std;
49namespace ph = std::placeholders;
50
51namespace {
52
54struct CtrlDhcp6Hooks {
55 int hooks_index_dhcp6_srv_configured_;
56
58 CtrlDhcp6Hooks() {
59 hooks_index_dhcp6_srv_configured_ = HooksManager::registerHook("dhcp6_srv_configured");
60 }
61
62};
63
64// Declare a Hooks object. As this is outside any function or method, it
65// will be instantiated (and the constructor run) when the module is loaded.
66// As a result, the hook indexes will be defined before any method in this
67// module is called.
68CtrlDhcp6Hooks Hooks;
69
70// Name of the file holding server identifier.
71static const char* SERVER_DUID_FILE = "kea-dhcp6-serverid";
72
82void signalHandler(int signo) {
83 // SIGHUP signals a request to reconfigure the server.
84 if (signo == SIGHUP) {
85 CommandMgr::instance().processCommand(createCommand("config-reload"));
86 } else if ((signo == SIGTERM) || (signo == SIGINT)) {
87 CommandMgr::instance().processCommand(createCommand("shutdown"));
88 }
89}
90
91}
92
93namespace isc {
94namespace dhcp {
95
96ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
97
98void
99ControlledDhcpv6Srv::init(const std::string& file_name) {
100 // Keep the call timestamp.
101 start_ = boost::posix_time::second_clock::universal_time();
102
103 // Configure the server using JSON file.
104 ConstElementPtr result = loadConfigFile(file_name);
105
106 int rcode;
107 ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
108 if (rcode != CONTROL_RESULT_SUCCESS) {
109 string reason = comment ? comment->stringValue() :
110 "no details available";
111 isc_throw(isc::BadValue, reason);
112 }
113
114 // Set signal handlers. When the SIGHUP is received by the process
115 // the server reconfiguration will be triggered. When SIGTERM or
116 // SIGINT will be received, the server will start shutting down.
117 signal_set_.reset(new IOSignalSet(getIOService(), signalHandler));
118
119 signal_set_->add(SIGINT);
120 signal_set_->add(SIGHUP);
121 signal_set_->add(SIGTERM);
122}
123
125 signal_set_.reset();
126 getIOService()->poll();
127}
128
130ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
131 // This is a configuration backend implementation that reads the
132 // configuration from a JSON file.
133
136
137 // Basic sanity check: file name must not be empty.
138 try {
139 if (file_name.empty()) {
140 // Basic sanity check: file name must not be empty.
141 isc_throw(isc::BadValue, "JSON configuration file not specified."
142 " Please use -c command line option.");
143 }
144
145 // Read contents of the file and parse it as JSON
146 Parser6Context parser;
147 json = parser.parseFile(file_name, Parser6Context::PARSER_DHCP6);
148 if (!json) {
149 isc_throw(isc::BadValue, "no configuration found");
150 }
151
152 // Let's do sanity check before we call json->get() which
153 // works only for map.
154 if (json->getType() != isc::data::Element::map) {
155 isc_throw(isc::BadValue, "Configuration file is expected to be "
156 "a map, i.e., start with { and end with } and contain "
157 "at least an entry called 'Dhcp6' that itself is a map. "
158 << file_name
159 << " is a valid JSON, but its top element is not a map."
160 " Did you forget to add { } around your configuration?");
161 }
162
163 // Use parsed JSON structures to configure the server
164 result = CommandMgr::instance().processCommand(createCommand("config-set", json));
165 if (!result) {
166 // Undetermined status of the configuration. This should never
167 // happen, but as the configureDhcp6Server returns a pointer, it is
168 // theoretically possible that it will return NULL.
169 isc_throw(isc::BadValue, "undefined result of "
170 "process command \"config-set\"");
171 }
172
173 // Now check is the returned result is successful (rcode=0) or not
174 // (see @ref isc::config::parseAnswer).
175 int rcode;
176 ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
177 if (rcode != CONTROL_RESULT_SUCCESS) {
178 string reason = comment ? comment->stringValue() :
179 "no details available";
180 isc_throw(isc::BadValue, reason);
181 }
182 } catch (const std::exception& ex) {
183 // If configuration failed at any stage, we drop the staging
184 // configuration and continue to use the previous one.
185 CfgMgr::instance().rollback();
186
188 .arg(file_name).arg(ex.what());
189 isc_throw(isc::BadValue, "configuration error using file '"
190 << file_name << "': " << ex.what());
191 }
192
194 .arg(MultiThreadingMgr::instance().getMode() ? "yes" : "no")
195 .arg(MultiThreadingMgr::instance().getThreadPoolSize())
196 .arg(MultiThreadingMgr::instance().getPacketQueueSize());
197
198 return (result);
199}
200
202ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr args) {
205 return (createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure."));
206 }
207
208 int exit_value = 0;
209 if (args) {
210 // @todo Should we go ahead and shutdown even if the args are invalid?
211 if (args->getType() != Element::map) {
212 return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
213 }
214
215 ConstElementPtr param = args->get("exit-value");
216 if (param) {
217 if (param->getType() != Element::integer) {
219 "parameter 'exit-value' is not an integer"));
220 }
221
222 exit_value = param->intValue();
223 }
224 }
225
226 ControlledDhcpv6Srv::getInstance()->shutdownServer(exit_value);
227 return (createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down."));
228}
229
231ControlledDhcpv6Srv::commandConfigReloadHandler(const string&,
232 ConstElementPtr /*args*/) {
233 // Get configuration file name.
234 string file = ControlledDhcpv6Srv::getInstance()->getConfigFile();
235 try {
237 auto result = loadConfigFile(file);
239 return (result);
240 } catch (const std::exception& ex) {
241 // Log the unsuccessful reconfiguration. The reason for failure
242 // should be already logged. Don't rethrow an exception so as
243 // the server keeps working.
245 .arg(file);
247 "Config reload failed: " + string(ex.what())));
248 }
249}
250
252ControlledDhcpv6Srv::commandConfigGetHandler(const string&,
253 ConstElementPtr /*args*/) {
254 ElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
255 string hash = BaseCommandMgr::getHash(config);
256 config->set("hash", Element::create(hash));
257
258 return (createAnswer(CONTROL_RESULT_SUCCESS, config));
259}
260
262ControlledDhcpv6Srv::commandConfigHashGetHandler(const string&,
263 ConstElementPtr /*args*/) {
264 ConstElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
265
266 string hash = BaseCommandMgr::getHash(config);
267
269 params->set("hash", Element::create(hash));
270 return (createAnswer(CONTROL_RESULT_SUCCESS, params));
271}
272
274ControlledDhcpv6Srv::commandConfigWriteHandler(const string&,
275 ConstElementPtr args) {
276 string filename;
277
278 if (args) {
279 if (args->getType() != Element::map) {
280 return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
281 }
282 ConstElementPtr filename_param = args->get("filename");
283 if (filename_param) {
284 if (filename_param->getType() != Element::string) {
286 "passed parameter 'filename' is not a string"));
287 }
288 filename = filename_param->stringValue();
289 }
290 }
291
292 if (filename.empty()) {
293 // filename parameter was not specified, so let's use whatever we remember
294 // from the command-line
295 filename = getConfigFile();
296 }
297
298 if (filename.empty()) {
299 return (createAnswer(CONTROL_RESULT_ERROR, "Unable to determine filename."
300 "Please specify filename explicitly."));
301 }
302
303 // Ok, it's time to write the file.
304 size_t size = 0;
305 try {
306 ConstElementPtr cfg = CfgMgr::instance().getCurrentCfg()->toElement();
307 size = writeConfigFile(filename, cfg);
308 } catch (const isc::Exception& ex) {
309 return (createAnswer(CONTROL_RESULT_ERROR, string("Error during config-write: ")
310 + ex.what()));
311 }
312 if (size == 0) {
313 return (createAnswer(CONTROL_RESULT_ERROR, "Error writing configuration to "
314 + filename));
315 }
316
317 // Ok, it's time to return the successful response.
319 params->set("size", Element::create(static_cast<long long>(size)));
320 params->set("filename", Element::create(filename));
321
322 return (createAnswer(CONTROL_RESULT_SUCCESS, "Configuration written to "
323 + filename + " successful", params));
324}
325
327ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
328 ConstElementPtr args) {
329 const int status_code = CONTROL_RESULT_ERROR;
330 ConstElementPtr dhcp6;
331 string message;
332
333 // Command arguments are expected to be:
334 // { "Dhcp6": { ... } }
335 if (!args) {
336 message = "Missing mandatory 'arguments' parameter.";
337 } else {
338 dhcp6 = args->get("Dhcp6");
339 if (!dhcp6) {
340 message = "Missing mandatory 'Dhcp6' parameter.";
341 } else if (dhcp6->getType() != Element::map) {
342 message = "'Dhcp6' parameter expected to be a map.";
343 }
344 }
345
346 // Check unsupported objects.
347 if (message.empty()) {
348 for (auto const& obj : args->mapValue()) {
349 const string& obj_name = obj.first;
350 if (obj_name != "Dhcp6") {
352 .arg(obj_name);
353 if (message.empty()) {
354 message = "Unsupported '" + obj_name + "' parameter";
355 } else {
356 message += " (and '" + obj_name + "')";
357 }
358 }
359 }
360 if (!message.empty()) {
361 message += ".";
362 }
363 }
364
365 if (!message.empty()) {
366 // Something is amiss with arguments, return a failure response.
367 ConstElementPtr result = isc::config::createAnswer(status_code,
368 message);
369 return (result);
370 }
371
372 // stop thread pool (if running)
374
375 // We are starting the configuration process so we should remove any
376 // staging configuration that has been created during previous
377 // configuration attempts.
378 CfgMgr::instance().rollback();
379
380 // Parse the logger configuration explicitly into the staging config.
381 // Note this does not alter the current loggers, they remain in
382 // effect until we apply the logging config below. If no logging
383 // is supplied logging will revert to default logging.
384 Daemon::configureLogger(dhcp6, CfgMgr::instance().getStagingCfg());
385
386 // Let's apply the new logging. We do it early, so we'll be able to print
387 // out what exactly is wrong with the new config in case of problems.
388 CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
389
390 // Now we configure the server proper.
391 ConstElementPtr result = processConfig(dhcp6);
392
393 // If the configuration parsed successfully, apply the new logger
394 // configuration and the commit the new configuration. We apply
395 // the logging first in case there's a configuration failure.
396 int rcode = 0;
397 isc::config::parseAnswer(rcode, result);
398 if (rcode == CONTROL_RESULT_SUCCESS) {
399 CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
400
401 // Use new configuration.
402 CfgMgr::instance().commit();
403 } else if (CfgMgr::instance().getCurrentCfg()->getSequence() != 0) {
404 // Ok, we applied the logging from the upcoming configuration, but
405 // there were problems with the config. As such, we need to back off
406 // and revert to the previous logging configuration. This is not done if
407 // sequence == 0, because that would mean always reverting to stdout by
408 // default, and it is arguably more helpful to have the error in a
409 // potential file or syslog configured in the upcoming configuration.
410 CfgMgr::instance().getCurrentCfg()->applyLoggingCfg();
411
412 // Not initial configuration so someone can believe we reverted
413 // to the previous configuration. It is not the case so be clear
414 // about this.
416 }
417
419 try {
420 // Handle events registered by hooks using external IOService objects.
421 IOServiceMgr::instance().pollIOServices();
422 } catch (const std::exception& ex) {
423 std::ostringstream err;
424 err << "Error initializing hooks: "
425 << ex.what();
427 }
428
429 return (result);
430}
431
433ControlledDhcpv6Srv::commandConfigTestHandler(const string&,
434 ConstElementPtr args) {
435 const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
436 ConstElementPtr dhcp6;
437 string message;
438
439 // Command arguments are expected to be:
440 // { "Dhcp6": { ... } }
441 if (!args) {
442 message = "Missing mandatory 'arguments' parameter.";
443 } else {
444 dhcp6 = args->get("Dhcp6");
445 if (!dhcp6) {
446 message = "Missing mandatory 'Dhcp6' parameter.";
447 } else if (dhcp6->getType() != Element::map) {
448 message = "'Dhcp6' parameter expected to be a map.";
449 }
450 }
451
452 // Check unsupported objects.
453 if (message.empty()) {
454 for (auto const& obj : args->mapValue()) {
455 const string& obj_name = obj.first;
456 if (obj_name != "Dhcp6") {
458 .arg(obj_name);
459 if (message.empty()) {
460 message = "Unsupported '" + obj_name + "' parameter";
461 } else {
462 message += " (and '" + obj_name + "')";
463 }
464 }
465 }
466 if (!message.empty()) {
467 message += ".";
468 }
469 }
470
471 if (!message.empty()) {
472 // Something is amiss with arguments, return a failure response.
473 ConstElementPtr result = isc::config::createAnswer(status_code,
474 message);
475 return (result);
476 }
477
478 // stop thread pool (if running)
480
481 // We are starting the configuration process so we should remove any
482 // staging configuration that has been created during previous
483 // configuration attempts.
484 CfgMgr::instance().rollback();
485
486 // Now we check the server proper.
487 return (checkConfig(dhcp6));
488}
489
491ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
492 ConstElementPtr args) {
493 std::ostringstream message;
494 int64_t max_period = 0;
495 std::string origin;
496
497 // If the args map does not contain 'origin' parameter, the default type
498 // will be used (user command).
499 auto type = NetworkState::USER_COMMAND;
500
501 // Parse arguments to see if the 'max-period' or 'origin' parameters have
502 // been specified.
503 if (args) {
504 // Arguments must be a map.
505 if (args->getType() != Element::map) {
506 message << "arguments for the 'dhcp-disable' command must be a map";
507
508 } else {
509 ConstElementPtr max_period_element = args->get("max-period");
510 // max-period is optional.
511 if (max_period_element) {
512 // It must be an integer, if specified.
513 if (max_period_element->getType() != Element::integer) {
514 message << "'max-period' argument must be a number";
515
516 } else {
517 // It must be positive integer.
518 max_period = max_period_element->intValue();
519 if (max_period <= 0) {
520 message << "'max-period' must be positive integer";
521 }
522 }
523 }
524 // 'origin-id' replaces the older parameter 'origin' since Kea 2.5.8
525 // stable release. However, the 'origin' is kept for backward compatibility
526 // with Kea versions before 2.5.8. It is common to receive both parameters
527 // because HA hook library sends both in case the partner server hasn't been
528 // upgraded to the new version. The 'origin-id' takes precedence over the
529 // 'origin'.
530 ConstElementPtr origin_id_element = args->get("origin-id");
531 ConstElementPtr origin_element = args->get("origin");
532 // The 'origin-id' and 'origin' arguments are optional.
533 if (origin_id_element) {
534 if (origin_id_element->getType() == Element::integer) {
535 type = origin_id_element->intValue();
536 } else {
537 message << "'origin-id' argument must be a number";
538 }
539 } else if (origin_element) {
540 switch (origin_element->getType()) {
541 case Element::string:
542 origin = origin_element->stringValue();
543 if (origin == "ha-partner") {
545 } else if (origin != "user") {
546 if (origin.empty()) {
547 origin = "(empty string)";
548 }
549 message << "invalid value used for 'origin' parameter: "
550 << origin;
551 }
552 break;
553 case Element::integer:
554 type = origin_element->intValue();
555 break;
556 default:
557 // It must be a string or a number, if specified.
558 message << "'origin' argument must be a string or a number";
559 }
560 }
561 }
562 }
563
564 // No error occurred, so let's disable the service.
565 if (message.tellp() == 0) {
566 message << "DHCPv6 service disabled";
567 if (max_period > 0) {
568 message << " for " << max_period << " seconds";
569
570 // The user specified that the DHCP service should resume not
571 // later than in max-period seconds. If the 'dhcp-enable' command
572 // is not sent, the DHCP service will resume automatically.
573 network_state_->delayedEnableService(static_cast<unsigned>(max_period),
574 type);
575 }
576 network_state_->disableService(type);
577
578 // Success.
579 return (config::createAnswer(CONTROL_RESULT_SUCCESS, message.str()));
580 }
581
582 // Failure.
583 return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
584}
585
587ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&,
588 ConstElementPtr args) {
589 std::ostringstream message;
590 std::string origin;
591
592 // If the args map does not contain 'origin' parameter, the default type
593 // will be used (user command).
594 auto type = NetworkState::USER_COMMAND;
595
596 // Parse arguments to see if the 'origin' parameter has been specified.
597 if (args) {
598 // Arguments must be a map.
599 if (args->getType() != Element::map) {
600 message << "arguments for the 'dhcp-enable' command must be a map";
601
602 } else {
603 // 'origin-id' replaces the older parameter 'origin' since Kea 2.5.8
604 // stable release. However, the 'origin' is kept for backward compatibility
605 // with Kea versions before 2.5.8. It is common to receive both parameters
606 // because HA hook library sends both in case the partner server hasn't been
607 // upgraded to the new version. The 'origin-id' takes precedence over the
608 // 'origin'.
609 ConstElementPtr origin_id_element = args->get("origin-id");
610 ConstElementPtr origin_element = args->get("origin");
611 // The 'origin-id' and 'origin' arguments are optional.
612 if (origin_id_element) {
613 if (origin_id_element->getType() == Element::integer) {
614 type = origin_id_element->intValue();
615 } else {
616 message << "'origin-id' argument must be a number";
617 }
618 } else if (origin_element) {
619 switch (origin_element->getType()) {
620 case Element::string:
621 origin = origin_element->stringValue();
622 if (origin == "ha-partner") {
624 } else if (origin != "user") {
625 if (origin.empty()) {
626 origin = "(empty string)";
627 }
628 message << "invalid value used for 'origin' parameter: "
629 << origin;
630 }
631 break;
632 case Element::integer:
633 type = origin_element->intValue();
634 break;
635 default:
636 // It must be a string or a number, if specified.
637 message << "'origin' argument must be a string or a number";
638 }
639 }
640 }
641 }
642
643 // No error occurred, so let's enable the service.
644 if (message.tellp() == 0) {
645 network_state_->enableService(type);
646
647 // Success.
649 "DHCP service successfully enabled"));
650 }
651
652 // Failure.
653 return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
654}
655
657ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
659 ElementPtr arguments = Element::createMap();
660 arguments->set("extended", extended);
663 arguments);
664 return (answer);
665}
666
668ControlledDhcpv6Srv::commandBuildReportHandler(const string&,
670 ConstElementPtr answer =
672 return (answer);
673}
674
676ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&,
677 ConstElementPtr args) {
678 int status_code = CONTROL_RESULT_ERROR;
679 string message;
680
681 // args must be { "remove": <bool> }
682 if (!args) {
683 message = "Missing mandatory 'remove' parameter.";
684 } else {
685 ConstElementPtr remove_name = args->get("remove");
686 if (!remove_name) {
687 message = "Missing mandatory 'remove' parameter.";
688 } else if (remove_name->getType() != Element::boolean) {
689 message = "'remove' parameter expected to be a boolean.";
690 } else {
691 bool remove_lease = remove_name->boolValue();
692 server_->alloc_engine_->reclaimExpiredLeases6(0, 0, remove_lease);
693 status_code = 0;
694 message = "Reclamation of expired leases is complete.";
695 }
696 }
697 ConstElementPtr answer = isc::config::createAnswer(status_code, message);
698 return (answer);
699}
700
702ControlledDhcpv6Srv::commandSubnet6SelectTestHandler(const string&,
703 ConstElementPtr args) {
704 if (!args) {
705 return (createAnswer(CONTROL_RESULT_ERROR, "empty arguments"));
706 }
707 if (args->getType() != Element::map) {
708 return (createAnswer(CONTROL_RESULT_ERROR, "arguments must be a map"));
709 }
710 SubnetSelector selector;
712 for (auto const& entry : args->mapValue()) {
713 ostringstream errmsg;
714 if (entry.first == "interface") {
715 if (entry.second->getType() != Element::string) {
716 errmsg << "'interface' entry must be a string";
717 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
718 }
719 selector.iface_name_ = entry.second->stringValue();
720 continue;
721 } if (entry.first == "interface-id") {
722 if (entry.second->getType() != Element::string) {
723 errmsg << "'interface-id' entry must be a string";
724 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
725 }
726 try {
727 string str = entry.second->stringValue();
728 vector<uint8_t> id = util::str::quotedStringToBinary(str);
729 if (id.empty()) {
731 }
732 if (id.empty()) {
733 errmsg << "'interface-id' must be not empty";
734 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
735 }
736 selector.interface_id_ = OptionPtr(new Option(Option::V6,
738 id));
739 continue;
740 } catch (...) {
741 errmsg << "value of 'interface-id' was not recognized";
742 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
743 }
744 } else if (entry.first == "remote") {
745 if (entry.second->getType() != Element::string) {
746 errmsg << "'remote' entry must be a string";
747 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
748 }
749 try {
750 IOAddress addr(entry.second->stringValue());
751 if (!addr.isV6()) {
752 errmsg << "bad 'remote' entry: not IPv6";
753 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
754 }
755 selector.remote_address_ = addr;
756 continue;
757 } catch (const exception& ex) {
758 errmsg << "bad 'remote' entry: " << ex.what();
759 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
760 }
761 } else if (entry.first == "link") {
762 if (entry.second->getType() != Element::string) {
763 errmsg << "'link' entry must be a string";
764 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
765 }
766 try {
767 IOAddress addr(entry.second->stringValue());
768 if (!addr.isV6()) {
769 errmsg << "bad 'link' entry: not IPv6";
770 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
771 }
772 selector.first_relay_linkaddr_ = addr;
773 continue;
774 } catch (const exception& ex) {
775 errmsg << "bad 'link' entry: " << ex.what();
776 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
777 }
778 } else if (entry.first == "classes") {
779 if (entry.second->getType() != Element::list) {
781 "'classes' entry must be a list"));
782 }
783 for (auto const& item : entry.second->listValue()) {
784 if (!item || (item->getType() != Element::string)) {
785 errmsg << "'classes' entry must be a list of strings";
786 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
787 }
788 // Skip empty client classes.
789 if (!item->stringValue().empty()) {
790 selector.client_classes_.insert(item->stringValue());
791 }
792 }
793 continue;
794 } else {
795 errmsg << "unknown entry '" << entry.first << "'";
796 return (createAnswer(CONTROL_RESULT_ERROR, errmsg.str()));
797 }
798 }
799 ConstSubnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()->
800 getCfgSubnets6()->selectSubnet(selector);
801 if (!subnet) {
802 return (createAnswer(CONTROL_RESULT_EMPTY, "no subnet selected"));
803 }
804 SharedNetwork6Ptr network;
805 subnet->getSharedNetwork(network);
806 ostringstream msg;
807 if (network) {
808 msg << "selected shared network '" << network->getName()
809 << "' starting with subnet '" << subnet->toText()
810 << "' id " << subnet->getID();
811 } else {
812 msg << "selected subnet '" << subnet->toText()
813 << "' id " << subnet->getID();
814 }
815 return (createAnswer(CONTROL_RESULT_SUCCESS, msg.str()));
816}
817
819ControlledDhcpv6Srv::commandServerTagGetHandler(const std::string&,
821 const std::string& tag =
822 CfgMgr::instance().getCurrentCfg()->getServerTag();
823 ElementPtr response = Element::createMap();
824 response->set("server-tag", Element::create(tag));
825
826 return (createAnswer(CONTROL_RESULT_SUCCESS, response));
827}
828
830ControlledDhcpv6Srv::commandConfigBackendPullHandler(const std::string&,
832 auto ctl_info = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo();
833 if (!ctl_info) {
834 return (createAnswer(CONTROL_RESULT_EMPTY, "No config backend."));
835 }
836
837 // stop thread pool (if running)
839
840 // Reschedule the periodic CB fetch.
841 if (TimerMgr::instance()->isTimerRegistered("Dhcp6CBFetchTimer")) {
842 TimerMgr::instance()->cancel("Dhcp6CBFetchTimer");
843 TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
844 }
845
846 // Code from cbFetchUpdates.
847 // The configuration to use is the current one because this is called
848 // after the configuration manager commit.
849 try {
850 auto srv_cfg = CfgMgr::instance().getCurrentCfg();
852 server_->getCBControl()->databaseConfigFetch(srv_cfg, mode);
853 } catch (const std::exception& ex) {
855 .arg(ex.what());
857 "On demand configuration update failed: " +
858 string(ex.what())));
859 }
861 "On demand configuration update successful."));
862}
863
865ControlledDhcpv6Srv::commandStatusGetHandler(const string&,
866 ConstElementPtr /*args*/) {
868 status->set("pid", Element::create(static_cast<int>(getpid())));
869
870 auto now = boost::posix_time::second_clock::universal_time();
871 // Sanity check: start_ is always initialized.
872 if (!start_.is_not_a_date_time()) {
873 auto uptime = now - start_;
874 status->set("uptime", Element::create(uptime.total_seconds()));
875 }
876
877 auto last_commit = CfgMgr::instance().getCurrentCfg()->getLastCommitTime();
878 if (!last_commit.is_not_a_date_time()) {
879 auto reload = now - last_commit;
880 status->set("reload", Element::create(reload.total_seconds()));
881 }
882
883 auto& mt_mgr = MultiThreadingMgr::instance();
884 if (mt_mgr.getMode()) {
885 status->set("multi-threading-enabled", Element::create(true));
886 status->set("thread-pool-size", Element::create(static_cast<int32_t>(
887 MultiThreadingMgr::instance().getThreadPoolSize())));
888 status->set("packet-queue-size", Element::create(static_cast<int32_t>(
889 MultiThreadingMgr::instance().getPacketQueueSize())));
890 ElementPtr queue_stats = Element::createList();
891 queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(10)));
892 queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(100)));
893 queue_stats->add(Element::create(mt_mgr.getThreadPool().getQueueStat(1000)));
894 status->set("packet-queue-statistics", queue_stats);
895
896 } else {
897 status->set("multi-threading-enabled", Element::create(false));
898 }
899
900 status->set("extended-info-tables", Element::create(
901 CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getExtendedInfoTablesEnabled()));
902
903 // Iterate through the interfaces and get all the errors.
904 ElementPtr socket_errors(Element::createList());
905 for (IfacePtr const& interface : IfaceMgr::instance().getIfaces()) {
906 for (std::string const& error : interface->getErrors()) {
907 socket_errors->add(Element::create(error));
908 }
909 }
910
911 // Abstract the information from all sockets into a single status.
913 if (socket_errors->empty()) {
914 sockets->set("status", Element::create("ready"));
915 } else {
916 ReconnectCtlPtr const reconnect_ctl(
917 CfgMgr::instance().getCurrentCfg()->getCfgIface()->getReconnectCtl());
918 if (reconnect_ctl && reconnect_ctl->retriesLeft()) {
919 sockets->set("status", Element::create("retrying"));
920 } else {
921 sockets->set("status", Element::create("failed"));
922 }
923 sockets->set("errors", socket_errors);
924 }
925 status->set("sockets", sockets);
926
927 status->set("dhcp-state", network_state_->toElement());
928
929 return (createAnswer(CONTROL_RESULT_SUCCESS, status));
930}
931
933ControlledDhcpv6Srv::commandStatisticSetMaxSampleCountAllHandler(const string&,
934 ConstElementPtr args) {
935 StatsMgr& stats_mgr = StatsMgr::instance();
936 ConstElementPtr answer = stats_mgr.statisticSetMaxSampleCountAllHandler(args);
937 // Update the default parameter.
938 long max_samples = stats_mgr.getMaxSampleCountDefault();
939 CfgMgr::instance().getCurrentCfg()->addConfiguredGlobal(
940 "statistic-default-sample-count", Element::create(max_samples));
941 return (answer);
942}
943
945ControlledDhcpv6Srv::commandStatisticSetMaxSampleAgeAllHandler(const string&,
946 ConstElementPtr args) {
947 StatsMgr& stats_mgr = StatsMgr::instance();
948 ConstElementPtr answer = stats_mgr.statisticSetMaxSampleAgeAllHandler(args);
949 // Update the default parameter.
950 auto duration = stats_mgr.getMaxSampleAgeDefault();
951 long max_age = toSeconds(duration);
952 CfgMgr::instance().getCurrentCfg()->addConfiguredGlobal(
953 "statistic-default-sample-age", Element::create(max_age));
954 return (answer);
955}
956
960
961 // Allow DB reconnect on startup. The database connection parameters specify
962 // respective details.
964
965 // Single stream instance used in all error clauses
966 std::ostringstream err;
967
968 if (!srv) {
969 err << "Server object not initialized, can't process config.";
971 }
972
974 .arg(srv->redactConfig(config)->str());
975
976 // Destroy lease manager before hooks unload.
978
979 // Destroy host manager before hooks unload.
981
982 ConstElementPtr answer = configureDhcp6Server(*srv, config);
983
984 // Check that configuration was successful. If not, do not reopen sockets
985 // and don't bother with DDNS stuff.
986 try {
987 int rcode = 0;
988 isc::config::parseAnswer(rcode, answer);
989 if (rcode != 0) {
990 return (answer);
991 }
992 } catch (const std::exception& ex) {
993 err << "Failed to process configuration:" << ex.what();
995 }
996
997 // Re-open lease and host database with new parameters.
998 try {
1000 std::bind(&ControlledDhcpv6Srv::dbLostCallback, srv, ph::_1);
1001
1003 std::bind(&ControlledDhcpv6Srv::dbRecoveredCallback, srv, ph::_1);
1004
1006 std::bind(&ControlledDhcpv6Srv::dbFailedCallback, srv, ph::_1);
1007
1008 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
1009 string params = "universe=6";
1010 if (cfg_db->getExtendedInfoTablesEnabled()) {
1011 params += " extended-info-tables=true";
1012 }
1013 cfg_db->setAppendedParameters(params);
1014 cfg_db->createManagers();
1015 // Reset counters related to connections as all managers have been recreated.
1016 srv->getNetworkState()->resetForDbConnection();
1017 srv->getNetworkState()->resetForLocalCommands();
1018 srv->getNetworkState()->resetForRemoteCommands();
1019 } catch (const std::exception& ex) {
1020 err << "Unable to open database: " << ex.what();
1022 }
1023
1024 // Regenerate server identifier if needed.
1025 try {
1026 const std::string duid_file =
1027 std::string(CfgMgr::instance().getDataDir()) + "/" +
1028 std::string(SERVER_DUID_FILE);
1029 DuidPtr duid = CfgMgr::instance().getStagingCfg()->getCfgDUID()->create(duid_file);
1030 server_->serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
1031 if (duid) {
1033 .arg(duid->toText())
1034 .arg(duid_file);
1035 }
1036
1037 } catch (const std::exception& ex) {
1038 err << "unable to configure server identifier: " << ex.what();
1040 }
1041
1042 // Server will start DDNS communications if its enabled.
1043 try {
1044 srv->startD2();
1045 } catch (const std::exception& ex) {
1046 err << "Error starting DHCP_DDNS client after server reconfiguration: "
1047 << ex.what();
1049 }
1050
1051 // Setup DHCPv4-over-DHCPv6 IPC
1052 try {
1053 Dhcp6to4Ipc::instance().open();
1054 } catch (const std::exception& ex) {
1055 err << "error starting DHCPv4-over-DHCPv6 IPC "
1056 " after server reconfiguration: " << ex.what();
1058 }
1059
1060 // Configure DHCP packet queueing
1061 try {
1063 qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
1064 if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) {
1066 .arg(IfaceMgr::instance().getPacketQueue6()->getInfoStr());
1067 }
1068
1069 } catch (const std::exception& ex) {
1070 err << "Error setting packet queue controls after server reconfiguration: "
1071 << ex.what();
1073 }
1074
1075 // Configure a callback to shut down the server when the bind socket
1076 // attempts exceeded.
1078 std::bind(&ControlledDhcpv6Srv::openSocketsFailedCallback, srv, ph::_1);
1079
1080 // Configuration may change active interfaces. Therefore, we have to reopen
1081 // sockets according to new configuration. It is possible that this
1082 // operation will fail for some interfaces but the openSockets function
1083 // guards against exceptions and invokes a callback function to
1084 // log warnings. Since we allow that this fails for some interfaces there
1085 // is no need to rollback configuration if socket fails to open on any
1086 // of the interfaces.
1087 CfgMgr::instance().getStagingCfg()->getCfgIface()->
1088 openSockets(AF_INET6, srv->getServerPort());
1089
1090 // Install the timers for handling leases reclamation.
1091 try {
1092 CfgMgr::instance().getStagingCfg()->getCfgExpiration()->
1093 setupTimers(&ControlledDhcpv6Srv::reclaimExpiredLeases,
1094 &ControlledDhcpv6Srv::deleteExpiredReclaimedLeases,
1095 server_);
1096
1097 } catch (const std::exception& ex) {
1098 err << "unable to setup timers for periodically running the"
1099 " reclamation of the expired leases: "
1100 << ex.what() << ".";
1102 }
1103
1104 // Setup config backend polling, if configured for it.
1105 auto ctl_info = CfgMgr::instance().getStagingCfg()->getConfigControlInfo();
1106 if (ctl_info) {
1107 long fetch_time = static_cast<long>(ctl_info->getConfigFetchWaitTime());
1108 // Only schedule the CB fetch timer if the fetch wait time is greater
1109 // than 0.
1110 if (fetch_time > 0) {
1111 // When we run unit tests, we want to use milliseconds unit for the
1112 // specified interval. Otherwise, we use seconds. Note that using
1113 // milliseconds as a unit in unit tests prevents us from waiting 1
1114 // second on more before the timer goes off. Instead, we wait one
1115 // millisecond which significantly reduces the test time.
1116 if (!server_->inTestMode()) {
1117 fetch_time = 1000 * fetch_time;
1118 }
1119
1120 boost::shared_ptr<unsigned> failure_count(new unsigned(0));
1122 registerTimer("Dhcp6CBFetchTimer",
1123 std::bind(&ControlledDhcpv6Srv::cbFetchUpdates,
1124 server_, CfgMgr::instance().getStagingCfg(),
1125 failure_count),
1126 fetch_time,
1128 TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
1129 }
1130 }
1131
1132 // Finally, we can commit runtime option definitions in libdhcp++. This is
1133 // exception free.
1135
1136 auto notify_libraries = ControlledDhcpv6Srv::finishConfigHookLibraries(config);
1137 if (notify_libraries) {
1138 return (notify_libraries);
1139 }
1140
1141 // Initialize the allocators. If the user selected a Free Lease Queue Allocator
1142 // for any of the subnets, the server will now populate free leases to the queue.
1143 // It may take a while!
1144 try {
1145 CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->initAllocatorsAfterConfigure();
1146
1147 } catch (const std::exception& ex) {
1148 err << "Error initializing the lease allocators: "
1149 << ex.what();
1151 }
1152
1153 // Apply multi threading settings.
1154 // @note These settings are applied/updated only if no errors occur while
1155 // applying the new configuration.
1156 // @todo This should be fixed.
1157 try {
1158 CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
1159 } catch (const std::exception& ex) {
1160 err << "Error applying multi threading settings: "
1161 << ex.what();
1163 }
1164
1165 return (answer);
1166}
1167
1171 // This hook point notifies hooks libraries that the configuration of the
1172 // DHCPv6 server has completed. It provides the hook library with the pointer
1173 // to the common IO service object, new server configuration in the JSON
1174 // format and with the pointer to the configuration storage where the
1175 // parsed configuration is stored.
1176 if (HooksManager::calloutsPresent(Hooks.hooks_index_dhcp6_srv_configured_)) {
1178
1179 callout_handle->setArgument("io_context", srv->getIOService());
1180 callout_handle->setArgument("network_state", srv->getNetworkState());
1181 callout_handle->setArgument("json_config", config);
1182 callout_handle->setArgument("server_config", CfgMgr::instance().getStagingCfg());
1183
1184 HooksManager::callCallouts(Hooks.hooks_index_dhcp6_srv_configured_,
1185 *callout_handle);
1186
1187 // If next step is DROP, report a configuration error.
1188 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1189 string error;
1190 try {
1191 callout_handle->getArgument("error", error);
1192 } catch (NoSuchArgument const& ex) {
1193 error = "unknown error";
1194 }
1196 }
1197 }
1198
1199 return (ConstElementPtr());
1200}
1201
1205
1206 if (!srv) {
1208 "Server object not initialized, can't process config.");
1209 return (no_srv);
1210 }
1211
1213 .arg(srv->redactConfig(config)->str());
1214
1215 return (configureDhcp6Server(*srv, config, true));
1216}
1217
1218ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port /*= DHCP6_SERVER_PORT*/,
1219 uint16_t client_port /*= 0*/)
1220 : Dhcpv6Srv(server_port, client_port), timer_mgr_(TimerMgr::instance()) {
1221 if (getInstance()) {
1223 "There is another Dhcpv6Srv instance already.");
1224 }
1225 server_ = this; // remember this instance for later use in handlers
1226
1227 // ProcessSpawn uses IO service to handle signal set events.
1229
1230 // TimerMgr uses IO service to run asynchronous timers.
1231 TimerMgr::instance()->setIOService(getIOService());
1232
1233 // Command managers use IO service to run asynchronous socket operations.
1234 UnixCommandMgr::instance().setIOService(getIOService());
1235 HttpCommandMgr::instance().setIOService(getIOService());
1236
1237 // Set the HTTP default socket address to the IPv6 (vs IPv4) loopback.
1239
1240 // Set the HTTP authentication default realm.
1242
1243 // DatabaseConnection uses IO service to run asynchronous timers.
1245
1246 // These are the commands always supported by the DHCPv6 server.
1247 // Please keep the list in alphabetic order.
1248 CommandMgr::instance().registerCommand("build-report",
1249 std::bind(&ControlledDhcpv6Srv::commandBuildReportHandler, this, ph::_1, ph::_2));
1250
1251 CommandMgr::instance().registerCommand("config-backend-pull",
1252 std::bind(&ControlledDhcpv6Srv::commandConfigBackendPullHandler, this, ph::_1, ph::_2));
1253
1254 CommandMgr::instance().registerCommand("config-get",
1255 std::bind(&ControlledDhcpv6Srv::commandConfigGetHandler, this, ph::_1, ph::_2));
1256
1257 CommandMgr::instance().registerCommand("config-hash-get",
1258 std::bind(&ControlledDhcpv6Srv::commandConfigHashGetHandler, this, ph::_1, ph::_2));
1259
1260 CommandMgr::instance().registerCommand("config-reload",
1261 std::bind(&ControlledDhcpv6Srv::commandConfigReloadHandler, this, ph::_1, ph::_2));
1262
1263 CommandMgr::instance().registerCommand("config-set",
1264 std::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, ph::_1, ph::_2));
1265
1266 CommandMgr::instance().registerCommand("config-test",
1267 std::bind(&ControlledDhcpv6Srv::commandConfigTestHandler, this, ph::_1, ph::_2));
1268
1269 CommandMgr::instance().registerCommand("config-write",
1270 std::bind(&ControlledDhcpv6Srv::commandConfigWriteHandler, this, ph::_1, ph::_2));
1271
1272 CommandMgr::instance().registerCommand("dhcp-enable",
1273 std::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, this, ph::_1, ph::_2));
1274
1275 CommandMgr::instance().registerCommand("dhcp-disable",
1276 std::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, ph::_1, ph::_2));
1277
1278 CommandMgr::instance().registerCommand("leases-reclaim",
1279 std::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, ph::_1, ph::_2));
1280
1281 CommandMgr::instance().registerCommand("subnet6-select-test",
1282 std::bind(&ControlledDhcpv6Srv::commandSubnet6SelectTestHandler, this, ph::_1, ph::_2));
1283
1284 CommandMgr::instance().registerCommand("server-tag-get",
1285 std::bind(&ControlledDhcpv6Srv::commandServerTagGetHandler, this, ph::_1, ph::_2));
1286
1287 CommandMgr::instance().registerCommand("shutdown",
1288 std::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, ph::_1, ph::_2));
1289
1290 CommandMgr::instance().registerCommand("status-get",
1291 std::bind(&ControlledDhcpv6Srv::commandStatusGetHandler, this, ph::_1, ph::_2));
1292
1293 CommandMgr::instance().registerCommand("version-get",
1294 std::bind(&ControlledDhcpv6Srv::commandVersionGetHandler, this, ph::_1, ph::_2));
1295
1296 // Register statistic related commands
1297 CommandMgr::instance().registerCommand("statistic-get",
1298 std::bind(&StatsMgr::statisticGetHandler, ph::_1, ph::_2));
1299
1300 CommandMgr::instance().registerCommand("statistic-reset",
1301 std::bind(&StatsMgr::statisticResetHandler, ph::_1, ph::_2));
1302
1303 CommandMgr::instance().registerCommand("statistic-remove",
1304 std::bind(&StatsMgr::statisticRemoveHandler, ph::_1, ph::_2));
1305
1306 CommandMgr::instance().registerCommand("statistic-get-all",
1307 std::bind(&StatsMgr::statisticGetAllHandler, ph::_1, ph::_2));
1308
1309 CommandMgr::instance().registerCommand("statistic-reset-all",
1310 std::bind(&StatsMgr::statisticResetAllHandler, ph::_1, ph::_2));
1311
1312 CommandMgr::instance().registerCommand("statistic-remove-all",
1313 std::bind(&StatsMgr::statisticRemoveAllHandler, ph::_1, ph::_2));
1314
1315 CommandMgr::instance().registerCommand("statistic-sample-age-set",
1316 std::bind(&StatsMgr::statisticSetMaxSampleAgeHandler, ph::_1, ph::_2));
1317
1318 CommandMgr::instance().registerCommand("statistic-sample-age-set-all",
1319 std::bind(&ControlledDhcpv6Srv::commandStatisticSetMaxSampleAgeAllHandler, this, ph::_1, ph::_2));
1320
1321 CommandMgr::instance().registerCommand("statistic-sample-count-set",
1322 std::bind(&StatsMgr::statisticSetMaxSampleCountHandler, ph::_1, ph::_2));
1323
1324 CommandMgr::instance().registerCommand("statistic-sample-count-set-all",
1325 std::bind(&ControlledDhcpv6Srv::commandStatisticSetMaxSampleCountAllHandler, this, ph::_1, ph::_2));
1326}
1327
1329 setExitValue(exit_value);
1330 getIOService()->stop(); // Stop ASIO transmissions
1331 shutdown(); // Initiate DHCPv6 shutdown procedure.
1332}
1333
1335 try {
1336 MultiThreadingMgr::instance().apply(false, 0, 0);
1339
1340 // The closure captures either a shared pointer (memory leak)
1341 // or a raw pointer (pointing to a deleted object).
1345
1346 timer_mgr_->unregisterTimers();
1347
1348 cleanup();
1349
1350 // Close command sockets.
1351 UnixCommandMgr::instance().closeCommandSocket();
1352 HttpCommandMgr::instance().close();
1353
1354 // Deregister any registered commands (please keep in alphabetic order)
1355 CommandMgr::instance().deregisterCommand("build-report");
1356 CommandMgr::instance().deregisterCommand("config-backend-pull");
1357 CommandMgr::instance().deregisterCommand("config-get");
1358 CommandMgr::instance().deregisterCommand("config-hash-get");
1359 CommandMgr::instance().deregisterCommand("config-reload");
1360 CommandMgr::instance().deregisterCommand("config-set");
1361 CommandMgr::instance().deregisterCommand("config-test");
1362 CommandMgr::instance().deregisterCommand("config-write");
1363 CommandMgr::instance().deregisterCommand("dhcp-disable");
1364 CommandMgr::instance().deregisterCommand("dhcp-enable");
1365 CommandMgr::instance().deregisterCommand("leases-reclaim");
1366 CommandMgr::instance().deregisterCommand("subnet6-select-test");
1367 CommandMgr::instance().deregisterCommand("server-tag-get");
1368 CommandMgr::instance().deregisterCommand("shutdown");
1369 CommandMgr::instance().deregisterCommand("statistic-get");
1370 CommandMgr::instance().deregisterCommand("statistic-get-all");
1371 CommandMgr::instance().deregisterCommand("statistic-remove");
1372 CommandMgr::instance().deregisterCommand("statistic-remove-all");
1373 CommandMgr::instance().deregisterCommand("statistic-reset");
1374 CommandMgr::instance().deregisterCommand("statistic-reset-all");
1375 CommandMgr::instance().deregisterCommand("statistic-sample-age-set");
1376 CommandMgr::instance().deregisterCommand("statistic-sample-age-set-all");
1377 CommandMgr::instance().deregisterCommand("statistic-sample-count-set");
1378 CommandMgr::instance().deregisterCommand("statistic-sample-count-set-all");
1379 CommandMgr::instance().deregisterCommand("status-get");
1380 CommandMgr::instance().deregisterCommand("version-get");
1381
1382 // Reset DatabaseConnection IO service.
1384 } catch (...) {
1385 // Don't want to throw exceptions from the destructor. The server
1386 // is shutting down anyway.
1387 }
1388
1389 server_ = NULL; // forget this instance. There should be no callback anymore
1390 // at this stage anyway.
1391}
1392
1393void
1394ControlledDhcpv6Srv::reclaimExpiredLeases(const size_t max_leases,
1395 const uint16_t timeout,
1396 const bool remove_lease,
1397 const uint16_t max_unwarned_cycles) {
1398 try {
1399 server_->alloc_engine_->reclaimExpiredLeases6(max_leases, timeout,
1400 remove_lease,
1401 max_unwarned_cycles);
1402 } catch (const std::exception& ex) {
1404 .arg(ex.what());
1405 }
1406 // We're using the ONE_SHOT timer so there is a need to re-schedule it.
1408}
1409
1410void
1411ControlledDhcpv6Srv::deleteExpiredReclaimedLeases(const uint32_t secs) {
1412 server_->alloc_engine_->deleteExpiredReclaimedLeases6(secs);
1413 // We're using the ONE_SHOT timer so there is a need to re-schedule it.
1415}
1416
1417bool
1418ControlledDhcpv6Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
1419 if (!db_reconnect_ctl) {
1420 // This should never happen
1422 return (false);
1423 }
1424
1425 // Disable service until the connection is recovered.
1426 if (db_reconnect_ctl->retriesLeft() == db_reconnect_ctl->maxRetries() &&
1427 db_reconnect_ctl->alterServiceState()) {
1429 }
1430
1432
1433 // If reconnect isn't enabled log it, initiate a shutdown if needed and
1434 // return false.
1435 if (!db_reconnect_ctl->retriesLeft() ||
1436 !db_reconnect_ctl->retryInterval()) {
1438 .arg(db_reconnect_ctl->retriesLeft())
1439 .arg(db_reconnect_ctl->retryInterval());
1440 if (db_reconnect_ctl->exitOnFailure()) {
1441 shutdownServer(EXIT_FAILURE);
1442 }
1443 return (false);
1444 }
1445
1446 return (true);
1447}
1448
1449bool
1450ControlledDhcpv6Srv::dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
1451 if (!db_reconnect_ctl) {
1452 // This should never happen
1454 return (false);
1455 }
1456
1457 // Enable service after the connection is recovered.
1458 if (db_reconnect_ctl->alterServiceState()) {
1460 }
1461
1463
1464 db_reconnect_ctl->resetRetries();
1465
1466 return (true);
1467}
1468
1469bool
1470ControlledDhcpv6Srv::dbFailedCallback(ReconnectCtlPtr db_reconnect_ctl) {
1471 if (!db_reconnect_ctl) {
1472 // This should never happen
1474 return (false);
1475 }
1476
1478 .arg(db_reconnect_ctl->maxRetries());
1479
1480 if (db_reconnect_ctl->exitOnFailure()) {
1481 shutdownServer(EXIT_FAILURE);
1482 }
1483
1484 return (true);
1485}
1486
1487void
1488ControlledDhcpv6Srv::openSocketsFailedCallback(ReconnectCtlPtr reconnect_ctl) {
1489 if (!reconnect_ctl) {
1490 // This should never happen
1492 return;
1493 }
1494
1496 .arg(reconnect_ctl->maxRetries());
1497
1498 if (reconnect_ctl->exitOnFailure()) {
1499 shutdownServer(EXIT_FAILURE);
1500 }
1501}
1502
1503void
1504ControlledDhcpv6Srv::cbFetchUpdates(const SrvConfigPtr& srv_cfg,
1505 boost::shared_ptr<unsigned> failure_count) {
1506 // stop thread pool (if running)
1508
1509 try {
1510 // Fetch any configuration backend updates since our last fetch.
1511 server_->getCBControl()->databaseConfigFetch(srv_cfg,
1513 (*failure_count) = 0;
1514
1515 } catch (const std::exception& ex) {
1517 .arg(ex.what());
1518
1519 // We allow at most 10 consecutive failures after which we stop
1520 // making further attempts to fetch the configuration updates.
1521 // Let's return without re-scheduling the timer.
1522 if (++(*failure_count) > 10) {
1525 return;
1526 }
1527 }
1528
1529 // Reschedule the timer to fetch new updates or re-try if
1530 // the previous attempt resulted in an error.
1531 if (TimerMgr::instance()->isTimerRegistered("Dhcp6CBFetchTimer")) {
1532 TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
1533 }
1534}
1535
1536} // namespace dhcp
1537} // namespace isc
CtrlAgentHooks Hooks
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
static std::string getHash(const isc::data::ConstElementPtr &config)
returns a hash of a given Element structure
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).
static HttpCommandMgr & instance()
HttpCommandMgr is a singleton class.
static UnixCommandMgr & instance()
UnixCommandMgr is a singleton class.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
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:25
static void apply(data::ConstElementPtr value)
apply multi threading configuration
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.
static Dhcp6to4Ipc & instance()
Returns pointer to the sole instance of Dhcp6to4Ipc.
DHCPv6 server service.
Definition dhcp6_srv.h:66
void shutdown() override
Instructs the server to shut down.
Definition dhcp6_srv.cc:355
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition dhcp6_srv.h:1246
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
static void create()
Creates new instance of the HostMgr.
Definition host_mgr.cc:52
Handles network interfaces, transmission and reception.
Definition iface_mgr.h:656
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
static void destroy()
Destroy lease manager.
static void commitRuntimeOptionDefs()
Commits runtime option definitions.
Definition libdhcp++.cc:248
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:104
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:228
isc::asiolink::IOSignalSetPtr signal_set_
A pointer to the object installing custom signal handlers.
Definition daemon.h:257
boost::posix_time::ptime start_
Timestamp of the start of the daemon.
Definition daemon.h:263
void setExitValue(int value)
Sets the exit value.
Definition daemon.h:227
Statistics Manager class.
static StatsMgr & instance()
Statistics Manager accessor method.
RAII class creating a critical section.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
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.
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 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.
#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 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()
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:29
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
@ 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.
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition iface_mgr.h:487
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
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.
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.
Subnet selector used to specify parameters used to select a subnet.
asiolink::IOAddress remote_address_
Source address of the message.