Kea 2.7.5
dhcp6_srv.cc
Go to the documentation of this file.
1// Copyright (C) 2011-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#include <kea_version.h>
9
10#include <asiolink/io_address.h>
11#include <asiolink/io_service.h>
13#include <cc/data.h>
16#include <dhcp/classify.h>
17#include <dhcp/dhcp6.h>
19#include <dhcp/duid.h>
20#include <dhcp/duid_factory.h>
21#include <dhcp/hwaddr.h>
22#include <dhcp/iface_mgr.h>
23#include <dhcp/libdhcp++.h>
25#include <dhcp/option.h>
27#include <dhcp/option6_ia.h>
28#include <dhcp/option6_iaaddr.h>
32#include <dhcp/option_vendor.h>
34#include <dhcp/pkt.h>
35#include <dhcp/pkt6.h>
38#include <dhcp6/dhcp6_log.h>
39#include <dhcp6/dhcp6_srv.h>
40#include <dhcp6/dhcp6to4_ipc.h>
41#include <dhcp_ddns/ncr_io.h>
42#include <dhcp_ddns/ncr_msg.h>
47#include <dhcpsrv/cfg_globals.h>
50#include <dhcpsrv/cfg_option.h>
52#include <dhcpsrv/cfg_rsoo.h>
54#include <dhcpsrv/cfgmgr.h>
58#include <dhcpsrv/host.h>
60#include <dhcpsrv/host_mgr.h>
61#include <dhcpsrv/lease.h>
66#include <dhcpsrv/pool.h>
69#include <dhcpsrv/srv_config.h>
70#include <dhcpsrv/subnet.h>
71#include <dhcpsrv/subnet_id.h>
73#include <dhcpsrv/utils.h>
74#include <eval/evaluate.h>
75#include <eval/token.h>
78#include <hooks/hooks_log.h>
79#include <hooks/hooks_manager.h>
80#include <hooks/parking_lots.h>
81#include <hooks/server_hooks.h>
83#include <log/log_dbglevels.h>
84#include <log/log_formatter.h>
85#include <log/logger.h>
86#include <log/macros.h>
87#include <stats/stats_mgr.h>
88#include <util/buffer.h>
90#include <util/optional.h>
91#include <util/pointer_util.h>
93#include <util/thread_pool.h>
94#include <util/triplet.h>
95
96#include <algorithm>
97#include <cmath>
98#include <cstdint>
99#include <cstdlib>
100#include <exception>
101#include <functional>
102#include <iomanip>
103#include <limits>
104#include <list>
105#include <map>
106#include <memory>
107#include <set>
108#include <sstream>
109#include <string>
110#include <tuple>
111#include <utility>
112#include <vector>
113
114#include <boost/algorithm/string/erase.hpp>
115#include <boost/algorithm/string/join.hpp>
116#include <boost/algorithm/string/split.hpp>
117#include <boost/foreach.hpp>
118#include <boost/tokenizer.hpp>
119
120using namespace isc;
121using namespace isc::asiolink;
122using namespace isc::cryptolink;
123using namespace isc::data;
124using namespace isc::dhcp;
125using namespace isc::dhcp_ddns;
126using namespace isc::hooks;
127using namespace isc::log;
128using namespace isc::log::interprocess;
129using namespace isc::stats;
130using namespace isc::util;
131using namespace std;
132namespace ph = std::placeholders;
133
134namespace {
135
137struct Dhcp6Hooks {
138 int hook_index_buffer6_receive_;
139 int hook_index_pkt6_receive_;
140 int hook_index_subnet6_select_;
141 int hook_index_leases6_committed_;
142 int hook_index_lease6_release_;
143 int hook_index_pkt6_send_;
144 int hook_index_buffer6_send_;
145 int hook_index_lease6_decline_;
146 int hook_index_host6_identifier_;
147 int hook_index_ddns6_update_;
148
150 Dhcp6Hooks() {
151 hook_index_buffer6_receive_ = HooksManager::registerHook("buffer6_receive");
152 hook_index_pkt6_receive_ = HooksManager::registerHook("pkt6_receive");
153 hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
154 hook_index_leases6_committed_ = HooksManager::registerHook("leases6_committed");
155 hook_index_lease6_release_ = HooksManager::registerHook("lease6_release");
156 hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send");
157 hook_index_buffer6_send_ = HooksManager::registerHook("buffer6_send");
158 hook_index_lease6_decline_ = HooksManager::registerHook("lease6_decline");
159 hook_index_host6_identifier_ = HooksManager::registerHook("host6_identifier");
160 hook_index_ddns6_update_ = HooksManager::registerHook("ddns6_update");
161 }
162};
163
164// Declare a Hooks object. As this is outside any function or method, it
165// will be instantiated (and the constructor run) when the module is loaded.
166// As a result, the hook indexes will be defined before any method in this
167// module is called.
168Dhcp6Hooks Hooks;
169
182createStatusCode(const Pkt6& pkt, const uint16_t status_code,
183 const std::string& status_message) {
184 Option6StatusCodePtr option_status(new Option6StatusCode(status_code,
185 status_message));
187 .arg(pkt.getLabel())
188 .arg(option_status->dataToText());
189 return (option_status);
190}
191
207createStatusCode(const Pkt6& pkt, const Option6IA& ia, const uint16_t status_code,
208 const std::string& status_message) {
209 Option6StatusCodePtr option_status(new Option6StatusCode(status_code,
210 status_message));
212 .arg(pkt.getLabel())
213 .arg(ia.getIAID())
214 .arg(option_status->dataToText());
215 return (option_status);
216}
217
220std::set<std::string> dhcp6_statistics = {
221 "pkt6-received",
222 "pkt6-solicit-received",
223 "pkt6-advertise-received",
224 "pkt6-request-received",
225 "pkt6-reply-received",
226 "pkt6-renew-received",
227 "pkt6-rebind-received",
228 "pkt6-decline-received",
229 "pkt6-release-received",
230 "pkt6-infrequest-received",
231 "pkt6-dhcpv4-query-received",
232 "pkt6-dhcpv4-response-received",
233 "pkt6-unknown-received",
234 "pkt6-sent",
235 "pkt6-advertise-sent",
236 "pkt6-reply-sent",
237 "pkt6-dhcpv4-response-sent",
238 "pkt6-parse-failed",
239 "pkt6-receive-drop",
240 "v6-allocation-fail",
241 "v6-allocation-fail-shared-network",
242 "v6-allocation-fail-subnet",
243 "v6-allocation-fail-no-pools",
244 "v6-allocation-fail-classes",
245 "v6-ia-na-lease-reuses",
246 "v6-ia-pd-lease-reuses",
247};
248
249} // namespace
250
251namespace isc {
252namespace dhcp {
253
254const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
255
256Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port)
257 : io_service_(new IOService()), server_port_(server_port),
258 client_port_(client_port), serverid_(), shutdown_(true),
259 alloc_engine_(), name_change_reqs_(),
260 network_state_(new NetworkState()),
261 cb_control_(new CBControlDHCPv6()) {
263 .arg(server_port);
264
265 Dhcp6to4Ipc::instance().client_port = client_port;
266
267 // Initialize objects required for DHCP server operation.
268 try {
269 // Port 0 is used for testing purposes where in most cases we don't
270 // rely on the physical interfaces. Therefore, it should be possible
271 // to create an object even when there are no usable interfaces.
272 if ((server_port > 0) && (IfaceMgr::instance().countIfaces() == 0)) {
274 return;
275 }
276
277 // Create a DUID instance but do not store it into a file.
278 DUIDFactory duid_factory;
279 DuidPtr duid = duid_factory.get();
280 serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
281
282 // Instantiate allocation engine. The number of allocation attempts equal
283 // to zero indicates that the allocation engine will use the number of
284 // attempts depending on the pool size.
285 alloc_engine_.reset(new AllocEngine(0));
286
288
289 } catch (const std::exception &e) {
291 return;
292 }
293
294 // Initializing all observations with default value
296
297 // All done, so can proceed
298 shutdown_ = false;
299}
300
303
304 // Iterate over set of observed statistics
305 for (auto const& it : dhcp6_statistics) {
306 // Initialize them with default value 0
307 stats_mgr.setValue(it, static_cast<int64_t>(0));
308 }
309}
310
312 // Discard any parked packets
314
315 try {
316 stopD2();
317 } catch (const std::exception& ex) {
318 // Highly unlikely, but lets Report it but go on
320 }
321
322 try {
323 Dhcp6to4Ipc::instance().close();
324 } catch (const std::exception& ex) {
325 // Highly unlikely, but lets Report it but go on
326 // LOG_ERROR(dhcp6_logger, DHCP6_SRV_DHCP4O6_ERROR).arg(ex.what());
327 }
328
329 IfaceMgr::instance().closeSockets();
330
331 // The lease manager was instantiated during DHCPv6Srv configuration,
332 // so we should clean up after ourselves.
334
335 // Destroy the host manager before hooks unload.
337
338 // Explicitly unload hooks
341 auto names = HooksManager::getLibraryNames();
342 std::string msg;
343 if (!names.empty()) {
344 msg = names[0];
345 for (size_t i = 1; i < names.size(); ++i) {
346 msg += std::string(", ") + names[i];
347 }
348 }
350 }
351 IOServiceMgr::instance().clearIOServices();
352 io_service_->stopAndPoll();
353}
354
359
361 return (IfaceMgr::instance().receive6(timeout));
362}
363
364void Dhcpv6Srv::sendPacket(const Pkt6Ptr& packet) {
365 IfaceMgr::instance().send(packet);
366}
367
368bool
374 OptionPtr server_id = pkt->getOption(D6O_SERVERID);
375 if (server_id){
376 // Let us test received ServerID if it is same as ServerID
377 // which is being used by server
378 if (getServerID()->getData() != server_id->getData()){
380 .arg(pkt->getLabel())
381 .arg(duidToString(server_id))
382 .arg(duidToString(getServerID()));
383 return (false);
384 }
385 }
386 // return True if: no serverid received or ServerIDs matching
387 return (true);
388}
389
390bool
392 switch (pkt->getType()) {
393 case DHCPV6_SOLICIT:
394 case DHCPV6_CONFIRM:
395 case DHCPV6_REBIND:
397 if (pkt->relay_info_.empty() && !pkt->getLocalAddr().isV6Multicast()) {
399 .arg(pkt->getLabel())
400 .arg(pkt->getName());
401 return (false);
402 }
403 break;
404 default:
405 // do nothing
406 ;
407 }
408 return (true);
409}
410
411void
413 const ConstCfgHostOperationsPtr cfg =
414 CfgMgr::instance().getCurrentCfg()->getCfgHostOperations6();
415 for (auto const& id_type : cfg->getIdentifierTypes()) {
416 switch (id_type) {
417 case Host::IDENT_DUID:
418 if (ctx.duid_) {
419 ctx.addHostIdentifier(id_type, ctx.duid_->getDuid());
420 }
421 break;
422
424 if (ctx.hwaddr_) {
425 ctx.addHostIdentifier(id_type, ctx.hwaddr_->hwaddr_);
426 }
427 break;
428 case Host::IDENT_FLEX:
429 // At this point the information in the packet has been unpacked into
430 // the various packet fields and option objects has been created.
431 // Execute callouts registered for host6_identifier.
432 if (HooksManager::calloutsPresent(Hooks.hook_index_host6_identifier_)) {
433 CalloutHandlePtr callout_handle = getCalloutHandle(ctx.query_);
434
436 std::vector<uint8_t> id;
437
438 // Use the RAII wrapper to make sure that the callout handle state is
439 // reset when this object goes out of scope. All hook points must do
440 // it to prevent possible circular dependency between the callout
441 // handle and its arguments.
442 ScopedCalloutHandleState callout_handle_state(callout_handle);
443
444 // Pass incoming packet as argument
445 callout_handle->setArgument("query6", ctx.query_);
446 callout_handle->setArgument("id_type", type);
447 callout_handle->setArgument("id_value", id);
448
449 // Call callouts
450 HooksManager::callCallouts(Hooks.hook_index_host6_identifier_,
451 *callout_handle);
452
453 callout_handle->getArgument("id_type", type);
454 callout_handle->getArgument("id_value", id);
455
456 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_CONTINUE) &&
457 !id.empty()) {
458
460 .arg(ctx.query_->getLabel())
461 .arg(Host::getIdentifierAsText(type, &id[0], id.size()));
462
463 ctx.addHostIdentifier(type, id);
464 }
465 }
466 break;
467 default:
468 ;
469 }
470 }
471}
472
473void
476 // Pointer to client's query.
477 ctx.query_ = query;
478
479 // DUID.
480 ctx.duid_ = query->getClientId();
481
482 // Hardware address.
483 ctx.hwaddr_ = getMAC(query);
484}
485
486bool
489 // First part of context initialization.
490 initContext0(query, ctx);
491
492 // Get the early-global-reservations-lookup flag value.
493 data::ConstElementPtr egrl = CfgMgr::instance().getCurrentCfg()->
495 if (egrl) {
496 ctx.early_global_reservations_lookup_ = egrl->boolValue();
497 }
498
499 // Perform early global reservations lookup when wanted.
501 // Get the host identifiers.
503
504 // Check for global host reservations.
505 ConstHostPtr global_host = alloc_engine_->findGlobalReservation(ctx);
506
507 if (global_host && !global_host->getClientClasses6().empty()) {
508 // Remove dependent evaluated classes.
510
511 // Add classes from the global reservations.
512 const ClientClasses& classes = global_host->getClientClasses6();
513 for (auto const& cclass : classes) {
514 query->addClass(cclass);
515 }
516
517 // Evaluate classes before KNOWN.
518 evaluateClasses(query, false);
519 }
520
521 if (global_host) {
522 // Add the KNOWN class;
523 query->addClass("KNOWN");
525 .arg(query->getLabel())
526 .arg("KNOWN");
527
528 // Evaluate classes after KNOWN.
529 evaluateClasses(query, true);
530
531 // Check the DROP special class.
532 if (query->inClass("DROP")) {
535 .arg(query->makeLabel(query->getClientId(), nullptr))
536 .arg(query->toText());
537 StatsMgr::instance().addValue("pkt6-receive-drop",
538 static_cast<int64_t>(1));
539 return (false);
540 }
541
542 // Store the reservation.
543 ctx.hosts_[SUBNET_ID_GLOBAL] = global_host;
544 }
545 }
546
547 return (true);
548}
549
550void
552 // Sanity check.
553 if (!ctx.query_) {
554 drop = true;
555 return;
556 }
557 ctx.fwd_dns_update_ = false;
558 ctx.rev_dns_update_ = false;
559 ctx.hostname_ = "";
561
562 // Collect host identifiers if host reservations enabled. The identifiers
563 // are stored in order of preference. The server will use them in that
564 // order to search for host reservations.
566 if (ctx.subnet_) {
567 // Before we can check for static reservations, we need to prepare
568 // a set of identifiers to be used for this.
571 }
572
573 // Find host reservations using specified identifiers.
574 alloc_engine_->findReservation(ctx);
575
576 // Get shared network to see if it is set for a subnet.
577 ctx.subnet_->getSharedNetwork(sn);
578 }
579
580 // Global host reservations are independent of a selected subnet. If the
581 // global reservations contain client classes we should use them in case
582 // they are meant to affect pool selection. Also, if the subnet does not
583 // belong to a shared network we can use the reserved client classes
584 // because there is no way our subnet could change. Such classes may
585 // affect selection of a pool within the selected subnet.
586 auto global_host = ctx.globalHost();
587 auto current_host = ctx.currentHost();
589 global_host && !global_host->getClientClasses6().empty()) ||
590 (!sn && current_host && !current_host->getClientClasses6().empty())) {
591 // We have already evaluated client classes and some of them may
592 // be in conflict with the reserved classes. Suppose there are
593 // two classes defined in the server configuration: first_class
594 // and second_class and the test for the second_class it looks
595 // like this: "not member('first_class')". If the first_class
596 // initially evaluates to false, the second_class evaluates to
597 // true. If the first_class is now set within the hosts reservations
598 // and we don't remove the previously evaluated second_class we'd
599 // end up with both first_class and second_class evaluated to
600 // true. In order to avoid that, we have to remove the classes
601 // evaluated in the first pass and evaluate them again. As
602 // a result, the first_class set via the host reservation will
603 // replace the second_class because the second_class will this
604 // time evaluate to false as desired.
607 evaluateClasses(ctx.query_, false);
608 }
609
610 // Set KNOWN builtin class if something was found, UNKNOWN if not.
611 if (!ctx.hosts_.empty()) {
612 ctx.query_->addClass("KNOWN");
614 .arg(ctx.query_->getLabel())
615 .arg("KNOWN");
616 } else {
617 ctx.query_->addClass("UNKNOWN");
619 .arg(ctx.query_->getLabel())
620 .arg("UNKNOWN");
621 }
622
623 // Perform second pass of classification.
624 evaluateClasses(ctx.query_, true);
625
626 const ClientClasses& classes = ctx.query_->getClasses();
628 .arg(ctx.query_->getLabel())
629 .arg(classes.toText());
630
631 // Check the DROP special class.
632 if (ctx.query_->inClass("DROP")) {
634 .arg(ctx.query_->makeLabel(ctx.query_->getClientId(), 0))
635 .arg(ctx.query_->toText());
636 StatsMgr::instance().addValue("pkt6-receive-drop",
637 static_cast<int64_t>(1));
638 drop = true;
639 }
640}
641
642int
644#ifdef HAVE_AFL
645 // Get the values of the environment variables used to control the
646 // fuzzing.
647
648 // Specfies the interface to be used to pass packets from AFL to Kea.
649 const char* interface = getenv("KEA_AFL_INTERFACE");
650 if (!interface) {
651 isc_throw(FuzzInitFail, "no fuzzing interface has been set");
652 }
653
654 // The address on the interface to be used.
655 const char* address = getenv("KEA_AFL_ADDRESS");
656 if (!address) {
657 isc_throw(FuzzInitFail, "no fuzzing address has been set");
658 }
659
660 // Set up structures needed for fuzzing.
661 PacketFuzzer fuzzer(server_port_, interface, address);
662
663 // The next line is needed as a signature for AFL to recognize that we are
664 // running persistent fuzzing. This has to be in the main image file.
665 while (__AFL_LOOP(fuzzer.maxLoopCount())) {
666 // Read from stdin and put the data read into an address/port on which
667 // Kea is listening, read for Kea to read it via asynchronous I/O.
668 fuzzer.transfer();
669#else
670 while (!shutdown_) {
671#endif // HAVE_AFL
672 try {
673 runOne();
674 // Handle events registered by hooks using external IOService objects.
675 IOServiceMgr::instance().pollIOServices();
676 getIOService()->poll();
677 config::HttpCommandMgr::instance().garbageCollectListeners();
678 } catch (const std::exception& e) {
679 // General catch-all standard exceptions that are not caught by more
680 // specific catches.
682 .arg(e.what());
683
684 } catch (...) {
685 // General catch-all non-standard exception that are not caught
686 // by more specific catches.
688 }
689 }
690
691 // Stop everything before we change into single-threaded mode.
693
694 // destroying the thread pool
695 MultiThreadingMgr::instance().apply(false, 0, 0);
696
697 return (getExitValue());
698}
699
700void
702 // client's message and server's response
703 Pkt6Ptr query;
704
705 try {
706 // Set select() timeout to 1s. This value should not be modified
707 // because it is important that the select() returns control
708 // frequently so as the IOService can be polled for ready handlers.
709 uint32_t timeout = 1;
710 query = receivePacket(timeout);
711
712 // Log if packet has arrived. We can't log the detailed information
713 // about the DHCP message because it hasn't been unpacked/parsed
714 // yet, and it can't be parsed at this point because hooks will
715 // have to process it first. The only information available at this
716 // point are: the interface, source address and destination addresses
717 // and ports.
718 if (query) {
720 .arg(query->getRemoteAddr().toText())
721 .arg(query->getRemotePort())
722 .arg(query->getLocalAddr().toText())
723 .arg(query->getLocalPort())
724 .arg(query->getIface());
725
726 // Log reception of the packet. We need to increase it early, as
727 // any failures in unpacking will cause the packet to be dropped.
728 // we will increase type specific packets further down the road.
729 // See processStatsReceived().
730 StatsMgr::instance().addValue("pkt6-received", static_cast<int64_t>(1));
731 }
732
733 // We used to log that the wait was interrupted, but this is no longer
734 // the case. Our wait time is 1s now, so the lack of query packet more
735 // likely means that nothing new appeared within a second, rather than
736 // we were interrupted. And we don't want to print a message every
737 // second.
738
739 } catch (const SignalInterruptOnSelect&) {
740 // Packet reception interrupted because a signal has been received.
741 // This is not an error because we might have received a SIGTERM,
742 // SIGINT, SIGHUP or SIGCHLD which are handled by the server. For
743 // signals that are not handled by the server we rely on the default
744 // behavior of the system.
746 } catch (const std::exception& e) {
748 .arg(e.what());
749 }
750
751 // Timeout may be reached or signal received, which breaks select()
752 // with no packet received
753 if (!query) {
754 return;
755 }
756
757 // If the DHCP service has been globally disabled, drop the packet.
758 if (!network_state_->isServiceEnabled()) {
760 .arg(query->getLabel());
761 return;
762 } else {
763 if (MultiThreadingMgr::instance().getMode()) {
764 query->addPktEvent("mt_queued");
765 typedef function<void()> CallBack;
766 boost::shared_ptr<CallBack> call_back =
767 boost::make_shared<CallBack>(std::bind(&Dhcpv6Srv::processPacketAndSendResponseNoThrow,
768 this, query));
769 if (!MultiThreadingMgr::instance().getThreadPool().add(call_back)) {
771 }
772 } else {
774 }
775 }
776}
777
778void
780 try {
782 } catch (const std::exception& e) {
784 .arg(query->getLabel())
785 .arg(e.what());
786 } catch (...) {
788 .arg(query->getLabel());
789 }
790}
791
792void
794 Pkt6Ptr rsp = processPacket(query);
795 if (!rsp) {
796 return;
797 }
798
799 CalloutHandlePtr callout_handle = getCalloutHandle(query);
800 processPacketBufferSend(callout_handle, rsp);
801}
802
805 query->addPktEvent("process_started");
806
807 // All packets belong to ALL.
808 query->addClass("ALL");
809
810 bool skip_unpack = false;
811
812 // The packet has just been received so contains the uninterpreted wire
813 // data; execute callouts registered for buffer6_receive.
814 if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
815 CalloutHandlePtr callout_handle = getCalloutHandle(query);
816
817 // Use the RAII wrapper to make sure that the callout handle state is
818 // reset when this object goes out of scope. All hook points must do
819 // it to prevent possible circular dependency between the callout
820 // handle and its arguments.
821 ScopedCalloutHandleState callout_handle_state(callout_handle);
822
823 // Enable copying options from the packet within hook library.
824 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
825
826 // Pass incoming packet as argument
827 callout_handle->setArgument("query6", query);
828
829 // Call callouts
830 HooksManager::callCallouts(Hooks.hook_index_buffer6_receive_, *callout_handle);
831
832 // Callouts decided to skip the next processing step. The next
833 // processing step would be to parse the packet, so skip at this
834 // stage means that callouts did the parsing already, so server
835 // should skip parsing.
836 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
838 .arg(query->getRemoteAddr().toText())
839 .arg(query->getLocalAddr().toText())
840 .arg(query->getIface());
841 skip_unpack = true;
842 }
843
844 // Callouts decided to drop the received packet
845 // The response (rsp) is null so the caller (runOne) will
846 // immediately return too.
847 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
849 .arg(query->getRemoteAddr().toText())
850 .arg(query->getLocalAddr().toText())
851 .arg(query->getIface());
852
853 // Not increasing the statistics of the dropped packets because it
854 // is the callouts' responsibility to increase it. There are some
855 // cases when the callouts may elect to not increase the statistics.
856 // For example, packets dropped by the load-balancing algorithm must
857 // not increase the statistics.
858 return (Pkt6Ptr());
859 }
860
861 callout_handle->getArgument("query6", query);
862 if (!query) {
863 // Please use the status instead of resetting query!
864 return (Pkt6Ptr());
865 }
866 }
867
868 // Unpack the packet information unless the buffer6_receive callouts
869 // indicated they did it
870 if (!skip_unpack) {
871 try {
873 .arg(query->getRemoteAddr().toText())
874 .arg(query->getLocalAddr().toText())
875 .arg(query->getIface());
876 query->unpack();
877 } catch (const SkipRemainingOptionsError& e) {
878 // An option failed to unpack but we are to attempt to process it
879 // anyway. Log it and let's hope for the best.
882 .arg(query->getLabel())
883 .arg(e.what());
884 } catch (const std::exception &e) {
885 // Failed to parse the packet.
887 .arg(query->getLabel())
888 .arg(query->getRemoteAddr().toText())
889 .arg(query->getLocalAddr().toText())
890 .arg(query->getIface())
891 .arg(e.what())
892 .arg(query->makeLabel(query->getClientId(), nullptr));
893
894 // Increase the statistics of parse failures and dropped packets.
895 StatsMgr::instance().addValue("pkt6-parse-failed",
896 static_cast<int64_t>(1));
897 StatsMgr::instance().addValue("pkt6-receive-drop",
898 static_cast<int64_t>(1));
899 return (Pkt6Ptr());
900 }
901 }
902
903 // Classify can emit INFO logs so help to track the query.
905 .arg(query->getLabel());
906
907 // Update statistics accordingly for received packet.
908 processStatsReceived(query);
909
910 // Check if received query carries server identifier matching
911 // server identifier being used by the server.
912 if (!testServerID(query)) {
913
914 // Increase the statistic of dropped packets.
915 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
916 return (Pkt6Ptr());
917 }
918
919 // Check if the received query has been sent to unicast or multicast.
920 // The Solicit, Confirm, Rebind and Information Request will be
921 // discarded if sent to unicast address.
922 if (!testUnicast(query)) {
923
924 // Increase the statistic of dropped packets.
925 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
926 return (Pkt6Ptr());
927 }
928
929 // Assign this packet to a class, if possible
930 classifyPacket(query);
931
933 .arg(query->getLabel())
934 .arg(query->getName())
935 .arg(static_cast<int>(query->getType()))
936 .arg(query->getRemoteAddr())
937 .arg(query->getLocalAddr())
938 .arg(query->getIface());
940 .arg(query->getLabel())
941 .arg(query->toText());
942
943 // At this point the information in the packet has been unpacked into
944 // the various packet fields and option objects has been created.
945 // Execute callouts registered for packet6_receive.
946 if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
947 CalloutHandlePtr callout_handle = getCalloutHandle(query);
948
949 // Use the RAII wrapper to make sure that the callout handle state is
950 // reset when this object goes out of scope. All hook points must do
951 // it to prevent possible circular dependency between the callout
952 // handle and its arguments.
953 ScopedCalloutHandleState callout_handle_state(callout_handle);
954
955 // Enable copying options from the packet within hook library.
956 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
957
958 // Pass incoming packet as argument
959 callout_handle->setArgument("query6", query);
960
961 // Call callouts
962 HooksManager::callCallouts(Hooks.hook_index_pkt6_receive_, *callout_handle);
963
964 // Callouts decided to skip the next processing step. The next
965 // processing step would be to process the packet, so skip at this
966 // stage means drop.
967 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
968 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
970 .arg(query->getLabel());
971 // Not increasing the statistics of the dropped packets because it
972 // is the callouts' responsibility to increase it. There are some
973 // cases when the callouts may elect to not increase the statistics.
974 // For example, packets dropped by the load-balancing algorithm must
975 // not increase the statistics.
976 return (Pkt6Ptr());
977 }
978
979 callout_handle->getArgument("query6", query);
980 if (!query) {
981 // Please use the status instead of resetting query!
982 return (Pkt6Ptr());
983 }
984 }
985
986 // Reject the message if it doesn't pass the sanity check.
987 if (!sanityCheck(query)) {
988 return (Pkt6Ptr());
989 }
990
991 // Check the DROP special class.
992 if (query->inClass("DROP")) {
994 .arg(query->makeLabel(query->getClientId(), nullptr))
995 .arg(query->toText());
996 StatsMgr::instance().addValue("pkt6-receive-drop",
997 static_cast<int64_t>(1));
998 return (Pkt6Ptr());
999 }
1000
1001 return (processDhcp6Query(query));
1002}
1003
1004void
1006 try {
1007 Pkt6Ptr rsp = processDhcp6Query(query);
1008 if (!rsp) {
1009 return;
1010 }
1011
1012 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1013 processPacketBufferSend(callout_handle, rsp);
1014 } catch (const std::exception& e) {
1016 .arg(query->getLabel())
1017 .arg(e.what());
1018 } catch (...) {
1020 .arg(query->getLabel());
1021 }
1022}
1023
1024Pkt6Ptr
1026 // Create a client race avoidance RAII handler.
1027 ClientHandler client_handler;
1028
1029 // Check for lease modifier queries from the same client being processed.
1030 if (MultiThreadingMgr::instance().getMode() &&
1031 ((query->getType() == DHCPV6_SOLICIT) ||
1032 (query->getType() == DHCPV6_REQUEST) ||
1033 (query->getType() == DHCPV6_RENEW) ||
1034 (query->getType() == DHCPV6_REBIND) ||
1035 (query->getType() == DHCPV6_RELEASE) ||
1036 (query->getType() == DHCPV6_DECLINE))) {
1037 ContinuationPtr cont =
1039 this, query));
1040 if (!client_handler.tryLock(query, cont)) {
1041 return (Pkt6Ptr());
1042 }
1043 }
1044
1045 // Let's create a simplified client context here.
1047 if (!earlyGHRLookup(query, ctx)) {
1048 return (Pkt6Ptr());
1049 }
1050
1051 if (query->getType() == DHCPV6_DHCPV4_QUERY) {
1052 // This call never throws. Should this change, this section must be
1053 // enclosed in try-catch.
1054 processDhcp4Query(query);
1055 return (Pkt6Ptr());
1056 }
1057
1058 // Complete the client context initialization.
1059 bool drop = false;
1060 ctx.subnet_ = selectSubnet(query, drop);
1061 if (drop) {
1062 // Caller will immediately drop the packet so simply return now.
1063 return (Pkt6Ptr());
1064 }
1065
1066 return (processLocalizedQuery6(ctx));
1067}
1068
1069
1070void
1073 try {
1075 if (!rsp) {
1076 return;
1077 }
1078
1079 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1080 processPacketBufferSend(callout_handle, rsp);
1081 } catch (const std::exception& e) {
1083 .arg(query->getLabel())
1084 .arg(e.what());
1085 } catch (...) {
1087 .arg(query->getLabel());
1088 }
1089}
1090
1091void
1093 // Initialize context.
1095 initContext0(query, ctx);
1096
1097 // Subnet is cached in the callout context associated to the query.
1098 try {
1099 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1100 callout_handle->getContext("subnet6", ctx.subnet_);
1101 } catch (const Exception&) {
1102 // No subnet, leave it to null...
1103 }
1104
1106}
1107
1108Pkt6Ptr
1110 Pkt6Ptr query = ctx.query_;
1111 bool drop = false;
1112 initContext(ctx, drop);
1113 // Stop here if initContext decided to drop the packet.
1114 if (drop) {
1115 return (Pkt6Ptr());
1116 }
1117
1118 Pkt6Ptr rsp;
1119 try {
1120 switch (query->getType()) {
1121 case DHCPV6_SOLICIT:
1122 rsp = processSolicit(ctx);
1123 break;
1124
1125 case DHCPV6_REQUEST:
1126 rsp = processRequest(ctx);
1127 break;
1128
1129 case DHCPV6_RENEW:
1130 rsp = processRenew(ctx);
1131 break;
1132
1133 case DHCPV6_REBIND:
1134 rsp = processRebind(ctx);
1135 break;
1136
1137 case DHCPV6_CONFIRM:
1138 rsp = processConfirm(ctx);
1139 break;
1140
1141 case DHCPV6_RELEASE:
1142 rsp = processRelease(ctx);
1143 break;
1144
1145 case DHCPV6_DECLINE:
1146 rsp = processDecline(ctx);
1147 break;
1148
1150 rsp = processInfRequest(ctx);
1151 break;
1152
1153 default:
1154 return (rsp);
1155 }
1156
1157 } catch (const std::exception& e) {
1158
1159 // Catch-all exception (at least for ones based on the isc Exception
1160 // class, which covers more or less all that are explicitly raised
1161 // in the Kea code), but also the standard one, which may possibly be
1162 // thrown from boost code. Just log the problem and ignore the packet.
1163 // (The problem is logged as a debug message because debug is
1164 // disabled by default - it prevents a DDOS attack based on the
1165 // sending of problem packets.)
1167 .arg(query->getLabel())
1168 .arg(query->getName())
1169 .arg(query->getRemoteAddr().toText())
1170 .arg(e.what());
1171
1172 // Increase the statistic of dropped packets.
1173 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
1174 }
1175
1176 if (!rsp) {
1177 return (rsp);
1178 }
1179
1180 // Process relay-supplied options. It is important to call this very
1181 // late in the process, because we now have all the options the
1182 // server wanted to send already set. This is important, because
1183 // RFC6422, section 6 states:
1184 //
1185 // The server SHOULD discard any options that appear in the RSOO
1186 // for which it already has one or more candidates.
1187 //
1188 // So we ignore any RSOO options if there's an option with the same
1189 // code already present.
1190 processRSOO(query, rsp);
1191
1192 rsp->setRemoteAddr(query->getRemoteAddr());
1193 rsp->setLocalAddr(query->getLocalAddr());
1194
1195 if (client_port_) {
1196 // A command line option enforces a specific client port
1197 rsp->setRemotePort(client_port_);
1198 } else if (rsp->relay_info_.empty()) {
1199 // Direct traffic, send back to the client directly
1200 rsp->setRemotePort(DHCP6_CLIENT_PORT);
1201 } else {
1202 // Relayed traffic, send back to the relay agent
1203 uint16_t relay_port = checkRelaySourcePort(query);
1204 rsp->setRemotePort(relay_port ? relay_port : DHCP6_SERVER_PORT);
1205 }
1206
1207 if (server_port_) {
1208 rsp->setLocalPort(server_port_);
1209 } else {
1210 rsp->setLocalPort(DHCP6_SERVER_PORT);
1211 }
1212 rsp->setIndex(query->getIndex());
1213 rsp->setIface(query->getIface());
1214
1215 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1216 if (!ctx.fake_allocation_ && (ctx.query_->getType() != DHCPV6_CONFIRM) &&
1217 (ctx.query_->getType() != DHCPV6_INFORMATION_REQUEST) &&
1218 HooksManager::calloutsPresent(Hooks.hook_index_leases6_committed_)) {
1219 // The ScopedCalloutHandleState class which guarantees that the task
1220 // is added to the thread pool after the response is reset (if needed)
1221 // and CalloutHandle state is reset. In ST it does nothing.
1222 // A smart pointer is used to store the ScopedCalloutHandleState so that
1223 // a copy of the pointer is created by the lambda and only on the
1224 // destruction of the last reference the task is added.
1225 // In MT there are 2 cases:
1226 // 1. packet is unparked before current thread smart pointer to
1227 // ScopedCalloutHandleState is destroyed:
1228 // - the lambda uses the smart pointer to set the callout which adds the
1229 // task, but the task is added after ScopedCalloutHandleState is
1230 // destroyed, on the destruction of the last reference which is held
1231 // by the current thread.
1232 // 2. packet is unparked after the current thread smart pointer to
1233 // ScopedCalloutHandleState is destroyed:
1234 // - the current thread reference to ScopedCalloutHandleState is
1235 // destroyed, but the reference in the lambda keeps it alive until
1236 // the lambda is called and the last reference is released, at which
1237 // time the task is actually added.
1238 // Use the RAII wrapper to make sure that the callout handle state is
1239 // reset when this object goes out of scope. All hook points must do
1240 // it to prevent possible circular dependency between the callout
1241 // handle and its arguments.
1242 std::shared_ptr<ScopedCalloutHandleState> callout_handle_state =
1243 std::make_shared<ScopedCalloutHandleState>(callout_handle);
1244
1245 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
1246
1247 // Also pass the corresponding query packet as argument
1248 callout_handle->setArgument("query6", query);
1249
1250 Lease6CollectionPtr new_leases(new Lease6Collection());
1251 if (!ctx.new_leases_.empty()) {
1252 // Filter out reused leases as they were not committed.
1253 for (auto const& new_lease : ctx.new_leases_) {
1254 if (new_lease->reuseable_valid_lft_ == 0) {
1255 new_leases->push_back(new_lease);
1256 }
1257 }
1258 }
1259 callout_handle->setArgument("leases6", new_leases);
1260
1261 Lease6CollectionPtr deleted_leases(new Lease6Collection());
1262
1263 // Do per IA lists
1264 for (auto const& iac : ctx.ias_) {
1265 if (!iac.old_leases_.empty()) {
1266 for (auto const& old_lease : iac.old_leases_) {
1267 if (ctx.new_leases_.empty()) {
1268 deleted_leases->push_back(old_lease);
1269 continue;
1270 }
1271 bool in_new = false;
1272 for (auto const& new_lease : ctx.new_leases_) {
1273 if ((new_lease->addr_ == old_lease->addr_) &&
1274 ((new_lease->type_ != Lease::TYPE_PD) ||
1275 (new_lease->prefixlen_ == old_lease->prefixlen_))) {
1276 in_new = true;
1277 break;
1278 }
1279 }
1280 if (!in_new) {
1281 deleted_leases->push_back(old_lease);
1282 }
1283 }
1284 }
1285 }
1286 callout_handle->setArgument("deleted_leases6", deleted_leases);
1287
1288 // Get the parking limit. Parsing should ensure the value is present.
1289 uint32_t parked_packet_limit = 0;
1290 data::ConstElementPtr ppl = CfgMgr::instance().getCurrentCfg()->
1291 getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT);
1292 if (ppl) {
1293 parked_packet_limit = ppl->intValue();
1294 }
1295
1296 if (parked_packet_limit) {
1297 auto const& parking_lot = ServerHooks::getServerHooks().
1298 getParkingLotPtr("leases6_committed");
1299 if (parking_lot && (parking_lot->size() >= parked_packet_limit)) {
1300 // We can't park it so we're going to throw it on the floor.
1303 .arg(parked_packet_limit)
1304 .arg(query->getLabel());
1305 isc::stats::StatsMgr::instance().addValue("pkt6-receive-drop",
1306 static_cast<int64_t>(1));
1307 rsp.reset();
1308 return (rsp);
1309 }
1310 }
1311
1312 // We proactively park the packet. We'll unpark it without invoking
1313 // the callback (i.e. drop) unless the callout status is set to
1314 // NEXT_STEP_PARK. Otherwise the callback we bind here will be
1315 // executed when the hook library unparks the packet.
1316 ConstSubnet6Ptr subnet = ctx.subnet_;
1317 HooksManager::park("leases6_committed", query,
1318 [this, callout_handle, query, rsp, callout_handle_state, subnet]() mutable {
1319 if (MultiThreadingMgr::instance().getMode()) {
1320 typedef function<void()> CallBack;
1321 boost::shared_ptr<CallBack> call_back =
1322 boost::make_shared<CallBack>(std::bind(&Dhcpv6Srv::sendResponseNoThrow,
1323 this, callout_handle, query, rsp, subnet));
1324 callout_handle_state->on_completion_ = [call_back]() {
1325 MultiThreadingMgr::instance().getThreadPool().add(call_back);
1326 };
1327 } else {
1328 processPacketPktSend(callout_handle, query, rsp, subnet);
1329 processPacketBufferSend(callout_handle, rsp);
1330 }
1331 });
1332
1333 try {
1334 // Call all installed callouts
1335 HooksManager::callCallouts(Hooks.hook_index_leases6_committed_,
1336 *callout_handle);
1337 } catch (...) {
1338 // Make sure we don't orphan a parked packet.
1339 HooksManager::drop("leases6_committed", query);
1340 throw;
1341 }
1342
1343 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
1345 .arg(query->getLabel());
1346 // Since the hook library(ies) are going to do the unparking, then
1347 // reset the pointer to the response to indicate to the caller that
1348 // it should return, as the packet processing will continue via
1349 // the callback.
1350 rsp.reset();
1351 } else {
1352 // Drop the park job on the packet, it isn't needed.
1353 HooksManager::drop("leases6_committed", query);
1354 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1356 .arg(query->getLabel());
1357 rsp.reset();
1358 }
1359 }
1360 }
1361
1362 // If we have a response prep it for shipment.
1363 if (rsp) {
1364 processPacketPktSend(callout_handle, query, rsp, ctx.subnet_);
1365 }
1366
1367 return (rsp);
1368}
1369
1370void
1372 Pkt6Ptr query, Pkt6Ptr& rsp,
1373 ConstSubnet6Ptr& subnet) {
1374 try {
1375 processPacketPktSend(callout_handle, query, rsp, subnet);
1376 processPacketBufferSend(callout_handle, rsp);
1377 } catch (const std::exception& e) {
1379 .arg(query->getLabel())
1380 .arg(e.what());
1381 } catch (...) {
1383 .arg(query->getLabel());
1384 }
1385}
1386
1387void
1389 Pkt6Ptr& query, Pkt6Ptr& rsp,
1390 ConstSubnet6Ptr& subnet) {
1391 query->addPktEvent("process_completed");
1392 if (!rsp) {
1393 return;
1394 }
1395
1396 // Specifies if server should do the packing
1397 bool skip_pack = false;
1398
1399 // Server's reply packet now has all options and fields set.
1400 // Options are represented by individual objects, but the
1401 // output wire data has not been prepared yet.
1402 // Execute all callouts registered for packet6_send
1403 if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
1404
1405 // Use the RAII wrapper to make sure that the callout handle state is
1406 // reset when this object goes out of scope. All hook points must do
1407 // it to prevent possible circular dependency between the callout
1408 // handle and its arguments.
1409 ScopedCalloutHandleState callout_handle_state(callout_handle);
1410
1411 // Enable copying options from the packets within hook library.
1412 ScopedEnableOptionsCopy<Pkt6> query_resp_options_copy(query, rsp);
1413
1414 // Pass incoming packet as argument
1415 callout_handle->setArgument("query6", query);
1416
1417 // Set our response
1418 callout_handle->setArgument("response6", rsp);
1419
1420 // Pass the selected subnet as an argument.
1421 callout_handle->setArgument("subnet6", subnet);
1422
1423 // Call all installed callouts
1424 HooksManager::callCallouts(Hooks.hook_index_pkt6_send_, *callout_handle);
1425
1426 // Callouts decided to skip the next processing step. The next
1427 // processing step would be to pack the packet (create wire data).
1428 // That step will be skipped if any callout sets skip flag.
1429 // It essentially means that the callout already did packing,
1430 // so the server does not have to do it again.
1431 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
1433 .arg(rsp->getLabel());
1434 skip_pack = true;
1435 }
1436
1438 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1440 .arg(rsp->getLabel());
1441 rsp.reset();
1442 return;
1443 }
1444 }
1445
1446 if (!skip_pack) {
1447 try {
1448 LibDHCP::splitNtpServerOptions6(rsp->options_);
1449 rsp->pack();
1450 } catch (const std::exception& e) {
1452 .arg(query->getLabel())
1453 .arg(e.what());
1454 return;
1455 }
1456
1457 }
1458}
1459
1460void
1462 Pkt6Ptr& rsp) {
1463 if (!rsp) {
1464 return;
1465 }
1466
1467 try {
1468 // Now all fields and options are constructed into output wire buffer.
1469 // Option objects modification does not make sense anymore. Hooks
1470 // can only manipulate wire buffer at this stage.
1471 // Let's execute all callouts registered for buffer6_send
1472 if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
1473
1474 // Use the RAII wrapper to make sure that the callout handle state is
1475 // reset when this object goes out of scope. All hook points must do
1476 // it to prevent possible circular dependency between the callout
1477 // handle and its arguments.
1478 ScopedCalloutHandleState callout_handle_state(callout_handle);
1479
1480 // Enable copying options from the packet within hook library.
1481 ScopedEnableOptionsCopy<Pkt6> response6_options_copy(rsp);
1482
1483 // Pass incoming packet as argument
1484 callout_handle->setArgument("response6", rsp);
1485
1486 // Call callouts
1487 HooksManager::callCallouts(Hooks.hook_index_buffer6_send_,
1488 *callout_handle);
1489
1490 // Callouts decided to skip the next processing step. The next
1491 // processing step would be to parse the packet, so skip at this
1492 // stage means drop.
1493 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1494 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1497 .arg(rsp->getLabel());
1498 return;
1499 }
1500
1501 callout_handle->getArgument("response6", rsp);
1502 }
1503
1505 .arg(rsp->getLabel())
1506 .arg(rsp->getName())
1507 .arg(static_cast<int>(rsp->getType()))
1508 .arg(rsp->getLocalAddr().isV6Zero() ? "*" : rsp->getLocalAddr().toText())
1509 .arg(rsp->getLocalPort())
1510 .arg(rsp->getRemoteAddr())
1511 .arg(rsp->getRemotePort())
1512 .arg(rsp->getIface());
1513
1515 .arg(rsp->getLabel())
1516 .arg(rsp->getName())
1517 .arg(static_cast<int>(rsp->getType()))
1518 .arg(rsp->toText());
1519 sendPacket(rsp);
1520
1521 // Update statistics accordingly for sent packet.
1522 processStatsSent(rsp);
1523
1524 } catch (const std::exception& e) {
1526 .arg(rsp->getLabel())
1527 .arg(e.what());
1528 }
1529}
1530
1531std::string
1533 stringstream tmp;
1534
1535 OptionBuffer data = opt->getData();
1536
1537 bool colon = false;
1538 for (auto const& it : data) {
1539 if (colon) {
1540 tmp << ":";
1541 }
1542 tmp << hex << setw(2) << setfill('0') << static_cast<uint16_t>(it);
1543 if (!colon) {
1544 colon = true;
1545 }
1546 }
1547
1548 return tmp.str();
1549}
1550
1551void
1553 // Add client-id.
1554 OptionPtr clientid = question->getOption(D6O_CLIENTID);
1555 if (clientid) {
1556 answer->addOption(clientid);
1557 }
1559
1560 // If this is a relayed message, we need to copy relay information
1561 if (!question->relay_info_.empty()) {
1562 answer->copyRelayInfo(question);
1563 }
1564
1565}
1566
1567void
1569 const CfgOptionList&) {
1570 // add server-id
1571 answer->addOption(getServerID());
1572}
1573
1574void
1577 CfgOptionList& co_list) {
1578 // Firstly, host specific options.
1579 if (ctx.currentHost() && !ctx.currentHost()->getCfgOption6()->empty()) {
1580 co_list.push_back(ctx.currentHost()->getCfgOption6());
1581 }
1582
1583 // Secondly, pool specific options. Pools are defined within a subnet, so
1584 // if there is no subnet, there is nothing to do.
1585 if (ctx.subnet_) {
1586 for (auto const& resource : ctx.allocated_resources_) {
1587 PoolPtr pool =
1588 ctx.subnet_->getPool(resource.getPrefixLength() == 128 ?
1590 resource.getAddress(),
1591 false);
1592 if (pool && !pool->getCfgOption()->empty()) {
1593 co_list.push_back(pool->getCfgOption());
1594 }
1595 }
1596
1597 // Thirdly, subnet configured options.
1598 if (!ctx.subnet_->getCfgOption()->empty()) {
1599 co_list.push_back(ctx.subnet_->getCfgOption());
1600 }
1601
1602 // Fourthly, shared network specific options.
1603 SharedNetwork6Ptr network;
1604 ctx.subnet_->getSharedNetwork(network);
1605 if (network && !network->getCfgOption()->empty()) {
1606 co_list.push_back(network->getCfgOption());
1607 }
1608 }
1609
1610 // Each class in the incoming packet
1611 const ClientClasses& classes = question->getClasses();
1612 for (auto const& cclass : classes) {
1613 // Find the client class definition for this class
1614 const ClientClassDefPtr& ccdef = CfgMgr::instance().getCurrentCfg()->
1615 getClientClassDictionary()->findClass(cclass);
1616 if (!ccdef) {
1617 // Not found: the class is built-in or not configured
1618 if (!isClientClassBuiltIn(cclass)) {
1620 .arg(question->getLabel())
1621 .arg(cclass);
1622 }
1623 // Skip it
1624 continue;
1625 }
1626
1627 if (ccdef->getCfgOption()->empty()) {
1628 // Skip classes which don't configure options
1629 continue;
1630 }
1631
1632 co_list.push_back(ccdef->getCfgOption());
1633 }
1634
1635 // Last global options
1636 if (!CfgMgr::instance().getCurrentCfg()->getCfgOption()->empty()) {
1637 co_list.push_back(CfgMgr::instance().getCurrentCfg()->getCfgOption());
1638 }
1639}
1640
1641void
1643 const CfgOptionList& co_list) {
1644 // Unlikely short cut
1645 if (co_list.empty()) {
1646 return;
1647 }
1648
1649 set<uint16_t> requested_opts;
1650
1651 // Client requests some options using ORO option. Try to
1652 // get this option from client's message.
1653 OptionUint16ArrayPtr option_oro = boost::dynamic_pointer_cast<
1654 OptionUint16Array>(question->getOption(D6O_ORO));
1655
1656 // Get the list of options that client requested.
1657 if (option_oro) {
1658 for (uint16_t code : option_oro->getValues()) {
1659 static_cast<void>(requested_opts.insert(code));
1660 }
1661 }
1662
1663 set<uint16_t> cancelled_opts;
1664
1665 // Iterate on the configured option list to add persistent and
1666 // cancelled options.
1667 for (auto const& copts : co_list) {
1668 const OptionContainerPtr& opts = copts->getAll(DHCP6_OPTION_SPACE);
1669 if (!opts) {
1670 continue;
1671 }
1672 // Get persistent options.
1673 const OptionContainerPersistIndex& pidx = opts->get<2>();
1674 const OptionContainerPersistRange& prange = pidx.equal_range(true);
1675 BOOST_FOREACH(auto const& desc, prange) {
1676 // Add the persistent option code to requested options.
1677 if (desc.option_) {
1678 uint16_t code = desc.option_->getType();
1679 static_cast<void>(requested_opts.insert(code));
1680 }
1681 }
1682 // Get cancelled options.
1683 const OptionContainerCancelIndex& cidx = opts->get<5>();
1684 const OptionContainerCancelRange& crange = cidx.equal_range(true);
1685 BOOST_FOREACH(auto const& desc, crange) {
1686 // Add the cancelled option code to the cancelled options.
1687 if (desc.option_) {
1688 uint16_t code = desc.option_->getType();
1689 static_cast<void>(cancelled_opts.insert(code));
1690 }
1691 }
1692 }
1693
1694 const auto& cclasses = question->getClasses();
1695 // For each requested option code get the first instance of the option
1696 // to be returned to the client.
1697 for (uint16_t opt : requested_opts) {
1698 // Skip if cancelled.
1699 if (cancelled_opts.count(opt) > 0) {
1700 continue;
1701 }
1702 // Add nothing when it is already there.
1703 // Skip special cases: D6O_VENDOR_OPTS
1704 if (opt == D6O_VENDOR_OPTS) {
1705 continue;
1706 }
1707 if (!answer->getOption(opt)) {
1708 // Iterate on the configured option list
1709 for (auto const& copts : co_list) {
1710 OptionDescriptor desc = copts->get(DHCP6_OPTION_SPACE, opt);
1711 // Got it: if allowed add it and jump to the outer loop.
1712 if (desc.option_ && desc.allowedForClientClasses(cclasses)) {
1713 answer->addOption(desc.option_);
1714 break;
1715 }
1716 }
1717 }
1718 }
1719
1720 // Special cases for vendor class and options which are identified
1721 // by the code/type and the vendor/enterprise id vs. the code/type only.
1722 if ((requested_opts.count(D6O_VENDOR_CLASS) > 0) &&
1723 (cancelled_opts.count(D6O_VENDOR_CLASS) == 0)) {
1724 // Keep vendor ids which are already in the response to insert
1725 // D6O_VENDOR_CLASS options at most once per vendor.
1726 set<uint32_t> vendor_ids;
1727 // Get what already exists in the response.
1728 for (auto const& opt : answer->getOptions(D6O_VENDOR_CLASS)) {
1729 OptionVendorClassPtr vendor_class;
1730 vendor_class = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
1731 if (vendor_class) {
1732 uint32_t vendor_id = vendor_class->getVendorId();
1733 static_cast<void>(vendor_ids.insert(vendor_id));
1734 }
1735 }
1736 // Iterate on the configured option list.
1737 for (auto const& copts : co_list) {
1738 for (auto const& desc : copts->getList(DHCP6_OPTION_SPACE, D6O_VENDOR_CLASS)) {
1739 // Empty or not allowed, skip i.
1740 if (!desc.option_ || !desc.allowedForClientClasses(cclasses)) {
1741 continue;
1742 }
1743 OptionVendorClassPtr vendor_class =
1744 boost::dynamic_pointer_cast<OptionVendorClass>(desc.option_);
1745 if (!vendor_class) {
1746 continue;
1747 }
1748 // Is the vendor id already in the response?
1749 uint32_t vendor_id = vendor_class->getVendorId();
1750 if (vendor_ids.count(vendor_id) > 0) {
1751 continue;
1752 }
1753 // Got it: add it.
1754 answer->addOption(desc.option_);
1755 static_cast<void>(vendor_ids.insert(vendor_id));
1756 }
1757 }
1758 }
1759
1760 if ((requested_opts.count(D6O_VENDOR_OPTS) > 0) &&
1761 (cancelled_opts.count(D6O_VENDOR_OPTS) == 0)) {
1762 // Keep vendor ids which are already in the response to insert
1763 // D6O_VENDOR_OPTS options at most once per vendor.
1764 set<uint32_t> vendor_ids;
1765 // Get what already exists in the response.
1766 for (auto const& opt : answer->getOptions(D6O_VENDOR_OPTS)) {
1767 OptionVendorPtr vendor_opts;
1768 vendor_opts = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1769 if (vendor_opts) {
1770 uint32_t vendor_id = vendor_opts->getVendorId();
1771 static_cast<void>(vendor_ids.insert(vendor_id));
1772 }
1773 }
1774 // Iterate on the configured option list
1775 for (auto const& copts : co_list) {
1776 for (auto const& desc : copts->getList(DHCP6_OPTION_SPACE, D6O_VENDOR_OPTS)) {
1777 // Empty or not allowed, skip i.
1778 if (!desc.option_ || !desc.allowedForClientClasses(cclasses)) {
1779 continue;
1780 }
1781 OptionVendorPtr vendor_opts =
1782 boost::dynamic_pointer_cast<OptionVendor>(desc.option_);
1783 if (!vendor_opts) {
1784 continue;
1785 }
1786 // Is the vendor id already in the response?
1787 uint32_t vendor_id = vendor_opts->getVendorId();
1788 if (vendor_ids.count(vendor_id) > 0) {
1789 continue;
1790 }
1791 // Append a fresh vendor option as the next method should
1792 // add suboptions to it.
1793 vendor_opts.reset(new OptionVendor(Option::V6, vendor_id));
1794 answer->addOption(vendor_opts);
1795 static_cast<void>(vendor_ids.insert(vendor_id));
1796 }
1797 }
1798 }
1799}
1800
1801void
1803 Pkt6Ptr& answer,
1805 const CfgOptionList& co_list) {
1806
1807 // Leave if there is no subnet matching the incoming packet.
1808 // There is no need to log the error message here because
1809 // it will be logged in the assignLease() when it fails to
1810 // pick the suitable subnet. We don't want to duplicate
1811 // error messages in such case.
1812 //
1813 // Also, if there's no options to possibly assign, give up.
1814 if (!ctx.subnet_ || co_list.empty()) {
1815 return;
1816 }
1817
1818 set<uint32_t> vendor_ids;
1819
1820 // The server could have provided the option using client classification or
1821 // hooks. If there're vendor info options in the response already, use them.
1822 map<uint32_t, OptionVendorPtr> vendor_rsps;
1823 for (auto const& opt : answer->getOptions(D6O_VENDOR_OPTS)) {
1824 OptionVendorPtr vendor_rsp;
1825 vendor_rsp = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1826 if (vendor_rsp) {
1827 uint32_t vendor_id = vendor_rsp->getVendorId();
1828 vendor_rsps[vendor_id] = vendor_rsp;
1829 static_cast<void>(vendor_ids.insert(vendor_id));
1830 }
1831 }
1832
1833 // Next, try to get the vendor-id from the client packet's
1834 // vendor-specific information option (17).
1835 map<uint32_t, OptionVendorPtr> vendor_reqs;
1836 for (auto const& opt : question->getOptions(D6O_VENDOR_OPTS)) {
1837 OptionVendorPtr vendor_req;
1838 vendor_req = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1839 if (vendor_req) {
1840 uint32_t vendor_id = vendor_req->getVendorId();
1841 vendor_reqs[vendor_id] = vendor_req;
1842 static_cast<void>(vendor_ids.insert(vendor_id));
1843 }
1844 }
1845
1846 // Finally, try to get the vendor-id from the client packet's vendor-class
1847 // option (16).
1848 for (auto const& opt : question->getOptions(D6O_VENDOR_CLASS)) {
1849 OptionVendorClassPtr vendor_class;
1850 vendor_class = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
1851 if (vendor_class) {
1852 uint32_t vendor_id = vendor_class->getVendorId();
1853 static_cast<void>(vendor_ids.insert(vendor_id));
1854 }
1855 }
1856
1857 // If there's no vendor option in either request or response, then there's no way
1858 // to figure out what the vendor-id values are and we give up.
1859 if (vendor_ids.empty()) {
1860 return;
1861 }
1862
1863 map<uint32_t, set<uint16_t> > requested_opts;
1864
1865 // Let's try to get ORO within that vendor-option.
1866 // This is specific to vendor-id=4491 (Cable Labs). Other vendors may have
1867 // different policies.
1869 if (vendor_reqs.count(VENDOR_ID_CABLE_LABS) > 0) {
1870 OptionVendorPtr vendor_req = vendor_reqs[VENDOR_ID_CABLE_LABS];
1871 OptionPtr oro_generic = vendor_req->getOption(DOCSIS3_V6_ORO);
1872 if (oro_generic) {
1873 // Vendor ID 4491 makes Kea look at DOCSIS3_V6_OPTION_DEFINITIONS
1874 // when parsing options. Based on that, oro_generic will have been
1875 // created as an OptionUint16Array, but might not be for other
1876 // vendor IDs.
1877 oro = boost::dynamic_pointer_cast<OptionUint16Array>(oro_generic);
1878 }
1879 if (oro) {
1880 set<uint16_t> oro_req_opts;
1881 for (uint16_t code : oro->getValues()) {
1882 static_cast<void>(oro_req_opts.insert(code));
1883 }
1884 requested_opts[VENDOR_ID_CABLE_LABS] = oro_req_opts;
1885 }
1886 }
1887
1888 map<uint32_t, set<uint16_t> > cancelled_opts;
1889 const auto& cclasses = question->getClasses();
1890
1891 // Iterate on the configured option list to add persistent and
1892 // cancelled options.
1893 for (uint32_t vendor_id : vendor_ids) {
1894 for (auto const& copts : co_list) {
1895 const OptionContainerPtr& opts = copts->getAll(vendor_id);
1896 if (!opts) {
1897 continue;
1898 }
1899 // Get persistent options.
1900 const OptionContainerPersistIndex& pidx = opts->get<2>();
1901 const OptionContainerPersistRange& prange = pidx.equal_range(true);
1902 BOOST_FOREACH(auto const& desc, prange) {
1903 if (!desc.option_) {
1904 continue;
1905 }
1906 // Add the persistent option code to requested options
1907 uint16_t code = desc.option_->getType();
1908 static_cast<void>(requested_opts[vendor_id].insert(code));
1909 }
1910 // Get cancelled options.
1911 const OptionContainerCancelIndex& cidx = opts->get<5>();
1912 const OptionContainerCancelRange& crange = cidx.equal_range(true);
1913 BOOST_FOREACH(auto const& desc, crange) {
1914 if (!desc.option_) {
1915 continue;
1916 }
1917 // Add the cancelled option code to cancelled options
1918 uint16_t code = desc.option_->getType();
1919 static_cast<void>(cancelled_opts[vendor_id].insert(code));
1920 }
1921 }
1922
1923 // If there is nothing to add don't do anything with this vendor.
1924 // This will explicitly not echo back vendor options from the request
1925 // that either correspond to a vendor not known to Kea even if the
1926 // option encapsulates data or there are no persistent options
1927 // configured for this vendor so Kea does not send any option back.
1928 if (requested_opts[vendor_id].empty()) {
1929 continue;
1930 }
1931
1932 // It's possible that the vendor opts option was inserted already
1933 // by client class or a hook. If that is so, let's use it.
1934 OptionVendorPtr vendor_rsp;
1935 if (vendor_rsps.count(vendor_id) > 0) {
1936 vendor_rsp = vendor_rsps[vendor_id];
1937 } else {
1938 vendor_rsp.reset(new OptionVendor(Option::V6, vendor_id));
1939 }
1940
1941 // Get the list of options that client requested.
1942 bool added = false;
1943
1944 for (uint16_t opt : requested_opts[vendor_id]) {
1945 if (cancelled_opts[vendor_id].count(opt) > 0) {
1946 continue;
1947 }
1948 if (!vendor_rsp->getOption(opt)) {
1949 for (auto const& copts : co_list) {
1950 OptionDescriptor desc = copts->get(vendor_id, opt);
1951 // Got it: if allowed add it and jump to outer loop.
1952 if (desc.option_ && desc.allowedForClientClasses(cclasses)) {
1953 vendor_rsp->addOption(desc.option_);
1954 added = true;
1955 break;
1956 }
1957 }
1958 }
1959 }
1960
1961 // If we added some sub-options and the vendor opts option is not in
1962 // the response already, then add it.
1963 if (added && (vendor_rsps.count(vendor_id) == 0)) {
1964 answer->addOption(vendor_rsp);
1965 }
1966 }
1967}
1968
1969bool
1971 try {
1972 switch (pkt->getType()) {
1973 case DHCPV6_SOLICIT:
1974 case DHCPV6_REBIND:
1975 case DHCPV6_CONFIRM:
1977 return (true);
1978
1979 case DHCPV6_REQUEST:
1980 case DHCPV6_RENEW:
1981 case DHCPV6_RELEASE:
1982 case DHCPV6_DECLINE:
1984 return (true);
1985
1989 return (true);
1990
1991 default:
1994 .arg(pkt->getLabel())
1995 .arg(static_cast<int>(pkt->getType()))
1996 .arg(pkt->getIface());
1997 }
1998
1999 } catch (const RFCViolation& e) {
2001 .arg(pkt->getLabel())
2002 .arg(pkt->getName())
2003 .arg(pkt->getRemoteAddr().toText())
2004 .arg(e.what());
2005 }
2006
2007 // Increase the statistic of dropped packets.
2008 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
2009 return (false);
2010}
2011
2012void
2014 RequirementLevel serverid) {
2015 OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
2016 switch (clientid) {
2017 case MANDATORY: {
2018 if (client_ids.size() != 1) {
2019 isc_throw(RFCViolation, "Exactly 1 client-id option expected in "
2020 << pkt->getName() << ", but " << client_ids.size()
2021 << " received");
2022 }
2023 sanityCheckDUID(client_ids.begin()->second, "client-id");
2024 break;
2025 }
2026 case OPTIONAL:
2027 if (client_ids.size() > 1) {
2028 isc_throw(RFCViolation, "Too many (" << client_ids.size()
2029 << ") client-id options received in " << pkt->getName());
2030 }
2031 if (!client_ids.empty()) {
2032 sanityCheckDUID(client_ids.begin()->second, "client-id");
2033 }
2034 break;
2035
2036 case FORBIDDEN:
2037 // doesn't make sense - client-id is always allowed
2038 break;
2039 }
2040
2041 OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
2042 switch (serverid) {
2043 case FORBIDDEN:
2044 if (!server_ids.empty()) {
2045 isc_throw(RFCViolation, "Server-id option was not expected, but "
2046 << server_ids.size() << " received in " << pkt->getName());
2047 }
2048 break;
2049
2050 case MANDATORY:
2051 if (server_ids.size() != 1) {
2052 isc_throw(RFCViolation, "Invalid number of server-id options received ("
2053 << server_ids.size() << "), exactly 1 expected in message "
2054 << pkt->getName());
2055 }
2056 sanityCheckDUID(server_ids.begin()->second, "server-id");
2057 break;
2058
2059 case OPTIONAL:
2060 if (server_ids.size() > 1) {
2061 isc_throw(RFCViolation, "Too many (" << server_ids.size()
2062 << ") server-id options received in " << pkt->getName());
2063 }
2064 if (!server_ids.empty()) {
2065 sanityCheckDUID(server_ids.begin()->second, "server-id");
2066 }
2067 }
2068}
2069
2070void Dhcpv6Srv::sanityCheckDUID(const OptionPtr& opt, const std::string& opt_name) {
2071 if (!opt) {
2072 isc_throw(RFCViolation, "Unable to find expected option " << opt_name);
2073 }
2074
2075 // The client-id or server-id has to have at least 3 bytes of useful data:
2076 // two for duid type and one more for actual duid value.
2077 uint16_t len = opt->len() - opt->getHeaderLen();
2078 if (len < DUID::MIN_DUID_LEN || len > DUID::MAX_DUID_LEN) {
2079 isc_throw(RFCViolation, "Received invalid DUID for " << opt_name << ", received "
2080 << len << " byte(s). It must be at least " << DUID::MIN_DUID_LEN
2081 << " and no more than " << DUID::MAX_DUID_LEN);
2082 }
2083}
2084
2086Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question, bool& drop) {
2087 const SubnetSelector& selector = CfgSubnets6::initSelector(question);
2088
2089 ConstSubnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()->
2090 getCfgSubnets6()->selectSubnet(selector);
2091
2092 // Let's execute all callouts registered for subnet6_receive
2093 if (HooksManager::calloutsPresent(Hooks.hook_index_subnet6_select_)) {
2094 CalloutHandlePtr callout_handle = getCalloutHandle(question);
2095
2096 // Use the RAII wrapper to make sure that the callout handle state is
2097 // reset when this object goes out of scope. All hook points must do
2098 // it to prevent possible circular dependency between the callout
2099 // handle and its arguments.
2100 shared_ptr<ScopedCalloutHandleState> callout_handle_state(
2101 std::make_shared<ScopedCalloutHandleState>(callout_handle));
2102
2103 // Enable copying options from the packet within hook library.
2104 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(question);
2105
2106 // Set new arguments
2107 callout_handle->setArgument("query6", question);
2108 callout_handle->setArgument("subnet6", subnet);
2109
2110 // We pass pointer to const collection for performance reasons.
2111 // Otherwise we would get a non-trivial performance penalty each
2112 // time subnet6_select is called.
2113 callout_handle->setArgument("subnet6collection",
2114 CfgMgr::instance().getCurrentCfg()->
2115 getCfgSubnets6()->getAll());
2116
2117 auto const tpl(parkingLimitExceeded("subnet6_select"));
2118 bool const exceeded(get<0>(tpl));
2119 if (exceeded) {
2120 uint32_t const limit(get<1>(tpl));
2121 // We can't park it so we're going to throw it on the floor.
2124 .arg(limit)
2125 .arg(question->getLabel());
2126 return (ConstSubnet6Ptr());
2127 }
2128
2129 // We proactively park the packet.
2130 // Not MT compatible because the unparking callback can be called
2131 // before the current thread exists from this block.
2132 HooksManager::park("subnet6_select", question, [this, question, callout_handle_state]() {
2133 if (MultiThreadingMgr::instance().getMode()) {
2134 boost::shared_ptr<function<void()>> callback(
2135 boost::make_shared<function<void()>>([this, question]() mutable {
2137 }));
2138 callout_handle_state->on_completion_ = [callback]() {
2139 MultiThreadingMgr::instance().getThreadPool().add(callback);
2140 };
2141 } else {
2143 }
2144 });
2145
2146 // Call user (and server-side) callouts
2147 try {
2148 HooksManager::callCallouts(Hooks.hook_index_subnet6_select_,
2149 *callout_handle);
2150 } catch (...) {
2151 // Make sure we don't orphan a parked packet.
2152 HooksManager::drop("subnet6_select", question);
2153 throw;
2154 }
2155
2156 // Callouts parked the packet. Same as drop but callouts will resume
2157 // processing or drop the packet later.
2158 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
2160 .arg(question->getLabel());
2161 drop = true;
2162 return (ConstSubnet6Ptr());
2163 } else {
2164 HooksManager::drop("subnet6_select", question);
2165 }
2166
2167 // Callouts decided to skip this step. This means that no
2168 // subnet will be selected. Packet processing will continue,
2169 // but it will be severely limited (i.e. only global options
2170 // will be assigned)
2171 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2173 .arg(question->getLabel());
2174 return (ConstSubnet6Ptr());
2175 }
2176
2177 // Callouts decided to drop the packet. It is a superset of the
2178 // skip case so no subnet will be selected.
2179 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
2181 .arg(question->getLabel());
2182 drop = true;
2183 return (ConstSubnet6Ptr());
2184 }
2185
2186 // Use whatever subnet was specified by the callout
2187 callout_handle->getArgument("subnet6", subnet);
2188 }
2189
2190 if (subnet) {
2191 // Log at higher debug level that subnet has been found.
2193 .arg(question->getLabel())
2194 .arg(subnet->getID());
2195 // Log detailed information about the selected subnet at the
2196 // lower debug level.
2198 .arg(question->getLabel())
2199 .arg(subnet->toText());
2200
2201 } else {
2203 .arg(question->getLabel());
2204 }
2205
2206 return (subnet);
2207}
2208
2209void
2210Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
2212 // Save the originally selected subnet.
2213 ConstSubnet6Ptr orig_subnet = ctx.subnet_;
2214
2215 // We need to allocate addresses for all IA_NA options in the client's
2216 // question (i.e. SOLICIT or REQUEST) message.
2217 // @todo add support for IA_TA
2218
2219 // For the lease allocation it is critical that the client has sent
2220 // DUID. There is no need to check for the presence of the DUID here
2221 // because we have already checked it in the sanityCheck().
2222
2223 // Now that we have all information about the client, let's iterate over all
2224 // received options and handle IA_NA options one by one and store our
2225 // responses in answer message (ADVERTISE or REPLY).
2226 //
2227 // @todo: IA_TA once we implement support for temporary addresses.
2228 for (auto const& opt : question->options_) {
2229 switch (opt.second->getType()) {
2230 case D6O_IA_NA: {
2231 OptionPtr answer_opt = assignIA_NA(question, ctx,
2232 boost::dynamic_pointer_cast<
2233 Option6IA>(opt.second));
2234 if (answer_opt) {
2235 answer->addOption(answer_opt);
2236 }
2237 break;
2238 }
2239 case D6O_IA_PD: {
2240 OptionPtr answer_opt = assignIA_PD(question, ctx,
2241 boost::dynamic_pointer_cast<
2242 Option6IA>(opt.second));
2243 if (answer_opt) {
2244 answer->addOption(answer_opt);
2245 }
2246 break;
2247 }
2248 default:
2249 break;
2250 }
2251 }
2252
2253 // Subnet may be modified by the allocation engine, there are things
2254 // we need to do when that happens.
2255 checkDynamicSubnetChange(question, answer, ctx, orig_subnet);
2256}
2257
2258void
2259Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
2261 D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
2262 DdnsParamsPtr ddns_params = ctx.getDdnsParams();
2263
2264 // Get Client FQDN Option from the client's message. If this option hasn't
2265 // been included, do nothing.
2266 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
2267 Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
2268 if (!fqdn) {
2269 if (ddns_params->getEnableUpdates() &&
2270 (ddns_params->getReplaceClientNameMode() == D2ClientConfig::RCM_ALWAYS ||
2271 ddns_params->getReplaceClientNameMode() == D2ClientConfig::RCM_WHEN_NOT_PRESENT)) {
2272 // Fabricate an empty "client" FQDN with flags requesting
2273 // the server do all the updates. The flags will get modified
2274 // below according the configuration options, the name will
2275 // be supplied later on.
2279 .arg(question->getLabel());
2280 } else {
2281 // No FQDN so get the lease hostname from the host reservation if
2282 // there is one.
2283 if (ctx.currentHost()) {
2284 ctx.hostname_ = ctx.currentHost()->getHostname();
2285 }
2286
2287 return;
2288 }
2289 }
2290
2292 .arg(question->getLabel())
2293 .arg(fqdn->toText());
2294
2295 // Create the DHCPv6 Client FQDN Option to be included in the server's
2296 // response to a client.
2297 Option6ClientFqdnPtr fqdn_resp(new Option6ClientFqdn(*fqdn));
2298
2299 // Set the server S, N, and O flags based on client's flags and
2300 // current configuration.
2301 d2_mgr.adjustFqdnFlags<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
2302
2303 // Get DDNS update direction flags
2304 CfgMgr::instance().getD2ClientMgr().getUpdateDirections(*fqdn_resp, ctx.fwd_dns_update_,
2305 ctx.rev_dns_update_);
2306
2307 // If there's a reservation and it has a hostname specified, use it!
2308 if (ctx.currentHost() && !ctx.currentHost()->getHostname().empty()) {
2309 // Add the qualifying suffix.
2310 // After #3765, this will only occur if the suffix is not empty.
2311 fqdn_resp->setDomainName(d2_mgr.qualifyName(ctx.currentHost()->getHostname(),
2312 *ddns_params, true),
2314 } else {
2315 // Adjust the domain name based on domain name value and type sent by
2316 // the client and current configuration.
2317 d2_mgr.adjustDomainName<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
2318 }
2319
2320 // Once we have the FQDN setup to use it for the lease hostname. This
2321 // only gets replaced later if the FQDN is to be generated from the address.
2322 ctx.hostname_ = fqdn_resp->getDomainName();
2323
2324 // The FQDN has been processed successfully. Let's append it to the
2325 // response to be sent to a client. Note that the Client FQDN option is
2326 // always sent back to the client if Client FQDN was included in the
2327 // client's message.
2329 .arg(question->getLabel())
2330 .arg(fqdn_resp->toText());
2331 answer->addOption(fqdn_resp);
2332
2333 // Optionally, call a hook that may override the decisions made
2334 // earlier.
2335 if (HooksManager::calloutsPresent(Hooks.hook_index_ddns6_update_)) {
2336 CalloutHandlePtr callout_handle = getCalloutHandle(question);
2337
2338 // Use the RAII wrapper to make sure that the callout handle state is
2339 // reset when this object goes out of scope. All hook points must do
2340 // it to prevent possible circular dependency between the callout
2341 // handle and its arguments.
2342 ScopedCalloutHandleState callout_handle_state(callout_handle);
2343
2344 // Setup the callout arguments.
2345 ConstSubnet6Ptr subnet = ctx.subnet_;
2346 callout_handle->setArgument("query6", question);
2347 callout_handle->setArgument("response6", answer);
2348 callout_handle->setArgument("subnet6", subnet);
2349 callout_handle->setArgument("hostname", ctx.hostname_);
2350 callout_handle->setArgument("fwd-update", ctx.fwd_dns_update_);
2351 callout_handle->setArgument("rev-update", ctx.rev_dns_update_);
2352 callout_handle->setArgument("ddns-params", ddns_params);
2353
2354 // Call callouts
2355 HooksManager::callCallouts(Hooks.hook_index_ddns6_update_, *callout_handle);
2356
2357 // Let's get the parameters returned by hook.
2358 string hook_hostname;
2359 bool hook_fwd_dns_update;
2360 bool hook_rev_dns_update;
2361 callout_handle->getArgument("hostname", hook_hostname);
2362 callout_handle->getArgument("fwd-update", hook_fwd_dns_update);
2363 callout_handle->getArgument("rev-update", hook_rev_dns_update);
2364
2365 // If there's anything changed by the hook, log it and then update the parameters
2366 if ((ctx.hostname_ != hook_hostname) || (ctx.fwd_dns_update_!= hook_fwd_dns_update) ||
2367 (ctx.rev_dns_update_ != hook_rev_dns_update)) {
2369 .arg(ctx.hostname_).arg(hook_hostname)
2370 .arg(ctx.fwd_dns_update_).arg(hook_fwd_dns_update)
2371 .arg(ctx.rev_dns_update_).arg(hook_rev_dns_update);
2372
2373 // Update the FQDN option in the response.
2374 fqdn_resp = boost::dynamic_pointer_cast<Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
2375 if (fqdn) {
2376 fqdn_resp->setDomainName(hook_hostname, Option6ClientFqdn::FULL);
2377 if (!(hook_fwd_dns_update || hook_rev_dns_update)) {
2378 // Hook disabled updates, Set flags back to client accordingly.
2379 fqdn_resp->setFlag(Option6ClientFqdn::FLAG_S, 0);
2380 fqdn_resp->setFlag(Option6ClientFqdn::FLAG_N, 1);
2381 }
2382 }
2383
2384 ctx.hostname_ = hook_hostname;
2385 ctx.fwd_dns_update_ = hook_fwd_dns_update;
2386 ctx.rev_dns_update_ = hook_rev_dns_update;
2387 }
2388 }
2389}
2390
2391void
2394 // Don't create NameChangeRequests if DNS updates are disabled.
2395 if (!(ctx.getDdnsParams()->getEnableUpdates())) {
2396 return;
2397 }
2398
2399 // The response message instance is always required. For instance it
2400 // holds the Client Identifier. It is a programming error if supplied
2401 // message is NULL.
2402 if (!answer) {
2403 isc_throw(isc::Unexpected, "an instance of the object"
2404 << " encapsulating server's message must not be"
2405 << " NULL when creating DNS NameChangeRequest");
2406 }
2407
2408 // It is likely that client haven't included the FQDN option. In such case,
2409 // FQDN option will be NULL. This is valid state, so we simply return.
2410 Option6ClientFqdnPtr opt_fqdn = boost::dynamic_pointer_cast<
2411 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
2412 if (!opt_fqdn) {
2413 return;
2414 }
2415
2416 // Get the update directions that should be performed based on our
2417 // response FQDN flags.
2418 bool do_fwd = false;
2419 bool do_rev = false;
2420 CfgMgr::instance().getD2ClientMgr().getUpdateDirections(*opt_fqdn,
2421 do_fwd, do_rev);
2422
2423 // Get the Client Id. It is mandatory and a function creating a response
2424 // would have thrown an exception if it was missing. Thus throwing
2425 // Unexpected if it is missing as it is a programming error.
2426 OptionPtr opt_duid = answer->getOption(D6O_CLIENTID);
2427 if (!opt_duid) {
2429 "client identifier is required when creating a new"
2430 " DNS NameChangeRequest");
2431 }
2432 DuidPtr duid = DuidPtr(new DUID(opt_duid->getData()));
2433
2434 // Get the FQDN in the on-wire format. It will be needed to compute
2435 // DHCID.
2436 OutputBuffer name_buf(1);
2437 opt_fqdn->packDomainName(name_buf);
2438 const std::vector<uint8_t>& buf_vec = name_buf.getVector();
2439 // Compute DHCID from Client Identifier and FQDN.
2440 isc::dhcp_ddns::D2Dhcid dhcid(*duid, buf_vec);
2441
2442 // Iterate over the IAContexts (there should be one per client IA processed).
2443 // For the first address in each IA_NA, create the appropriate NCR(s).
2444 for (auto& ia_ctx : ctx.getIAContexts()) {
2445 if ((ia_ctx.type_ != Lease::TYPE_NA) || !ia_ctx.ia_rsp_) {
2446 continue;
2447 }
2448
2451 Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
2452 Option6IAAddr>(ia_ctx.ia_rsp_->getOption(D6O_IAADDR));
2453
2454 // We need an address to create a name-to-address mapping.
2455 // If address is missing for any reason, go to the next IA.
2456 if (!iaaddr) {
2457 continue;
2458 }
2459
2460 bool extended_only = false;
2461 // If the lease is being reused (i.e. lease caching in effect) skip it.
2462 IOAddress ia_address = iaaddr->getAddress();
2463 for (auto const& l : ia_ctx.reused_leases_) {
2464 if (l->addr_ == ia_address) {
2465 extended_only = true;
2466 break;
2467 }
2468 }
2469
2470 if (extended_only) {
2471 continue;
2472 }
2473
2474 // If the lease for iaaddr is in the list of changed leases, we need
2475 // to determine if the changes included changes to the FQDN. If so
2476 // then we may need to do a CHG_REMOVE.
2477 for (auto const& l : ia_ctx.changed_leases_) {
2478
2479 if (l->addr_ == ia_address) {
2480 // The address is the same so this must be renewal. If we're not
2481 // always updating on renew, then we only renew if DNS info has
2482 // changed.
2483 if ((l->reuseable_valid_lft_ > 0) ||
2484 (!ctx.getDdnsParams()->getUpdateOnRenew() &&
2485 (l->hostname_ == opt_fqdn->getDomainName() &&
2486 l->fqdn_fwd_ == do_fwd && l->fqdn_rev_ == do_rev))) {
2487 extended_only = true;
2488 } else {
2489 // Queue a CHG_REMOVE of the old data.
2490 // NCR will only be created if the lease hostname is not
2491 // empty and at least one of the direction flags is true
2492 queueNCR(CHG_REMOVE, l);
2493 }
2494
2495 break;
2496 }
2497 }
2498
2499 if (!(do_fwd || do_rev) || (extended_only)) {
2500 // Flags indicate no updates needed or it was an extension of
2501 // an existing lease with no FQDN changes. In the case of the
2502 // former, the most likely scenario is that we are honoring the
2503 // client's request that no updates be done.
2504 continue;
2505 }
2506
2507 // Create new NameChangeRequest. Use the domain name from the FQDN.
2508 // This is an FQDN included in the response to the client, so it
2509 // holds a fully qualified domain-name already (not partial).
2510 // Get the IP address from the lease.
2512 auto cr_mode = StringToConflictResolutionMode(ctx.getDdnsParams()->getConflictResolutionMode());
2514 do_fwd, do_rev,
2515 opt_fqdn->getDomainName(),
2516 iaaddr->getAddress().toText(),
2517 dhcid, 0,
2518 calculateDdnsTtl(iaaddr->getValid(),
2519 ctx.getDdnsParams()->getTtlPercent()),
2520 cr_mode));
2522 .arg(answer->getLabel())
2523 .arg(ncr->toText());
2524
2525 // Post the NCR to the D2ClientMgr.
2526 CfgMgr::instance().getD2ClientMgr().sendRequest(ncr);
2527
2532 return;
2533 }
2534}
2535
2538 CfgMACSources mac_sources = CfgMgr::instance().getCurrentCfg()->
2539 getMACSources().get();
2540 HWAddrPtr hwaddr;
2541 for (auto const& it : mac_sources) {
2542 hwaddr = pkt->getMAC(it);
2543 if (hwaddr) {
2544 return (hwaddr);
2545 }
2546 }
2547 return (hwaddr);
2548}
2549
2552 const Lease6Ptr& lease) {
2553
2554 // Search the reservation the prefix is from.
2555 ConstHostPtr host = ctx.currentHost();
2556 if (host) {
2557 IPv6ResrvRange resvs = host->getIPv6Reservations(IPv6Resrv::TYPE_PD);
2558 BOOST_FOREACH(auto const& resv, resvs) {
2559 if ((resv.second.getPrefix() == lease->addr_) &&
2560 (resv.second.getPrefixLen() == lease->prefixlen_)) {
2561 return (resv.second.getPDExclude());
2562 }
2563 }
2564 }
2565
2566 // Search the pool the address is from.
2567 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2568 Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
2569 subnet->getPool(Lease::TYPE_PD, lease->addr_));
2570 if (pool) {
2571 return (pool->getPrefixExcludeOption());
2572 }
2573 return (OptionPtr());
2574}
2575
2579 boost::shared_ptr<Option6IA> ia) {
2580
2581 // Check if the client sent us a hint in his IA_NA. Clients may send an
2582 // address in their IA_NA options as a suggestion (e.g. the last address
2583 // they used before).
2584 Option6IAAddrPtr hint_opt =
2585 boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
2587 if (hint_opt) {
2588 hint = hint_opt->getAddress();
2589 }
2590
2591 if (ctx.fake_allocation_) {
2593 .arg(query->getLabel())
2594 .arg(ia->getIAID())
2595 .arg(hint_opt ? hint.toText() : "(no hint)");
2596 } else {
2598 .arg(query->getLabel())
2599 .arg(ia->getIAID())
2600 .arg(hint_opt ? hint.toText() : "(no hint)");
2601 }
2602
2603 // convenience values
2604 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2605
2606 // If there is no subnet selected for handling this IA_NA, the only thing left to do is
2607 // to say that we are sorry, but the user won't get an address. As a convenience, we
2608 // use a different status text to indicate that (compare to the same status code,
2609 // but different wording below)
2610 if (!subnet) {
2611 // Create an empty IA_NA option with IAID matching the request.
2612 // Note that we don't use OptionDefinition class to create this option.
2613 // This is because we prefer using a constructor of Option6IA that
2614 // initializes IAID. Otherwise we would have to use setIAID() after
2615 // creation of the option which has some performance implications.
2616 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2617
2618 // Insert status code NoAddrsAvail.
2619 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoAddrsAvail,
2620 "Server could not select subnet for"
2621 " this client"));
2622 return (ia_rsp);
2623 }
2624
2625 // Set per-IA context values.
2626 ctx.createIAContext();
2627 ctx.currentIA().iaid_ = ia->getIAID();
2628 if (hint_opt) {
2629 ctx.currentIA().addHint(hint_opt);
2630 } else {
2631 ctx.currentIA().addHint(hint);
2632 }
2634
2635 // Use allocation engine to pick a lease for this client. Allocation engine
2636 // will try to honor the hint, but it is just a hint - some other address
2637 // may be used instead. If fake_allocation is set to false, the lease will
2638 // be inserted into the LeaseMgr as well.
2639 Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
2640
2642 Lease6Ptr lease;
2643 if (!leases.empty()) {
2644 lease = *leases.begin();
2645 }
2646
2647 // Create IA_NA that we will put in the response.
2648 // Do not use OptionDefinition to create option's instance so
2649 // as we can initialize IAID using a constructor.
2650 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2651 ctx.currentIA().ia_rsp_ = ia_rsp;
2652
2653 if (lease) {
2654 // We have a lease! Let's wrap its content into IA_NA option
2655 // with IAADDR suboption.
2656 if (ctx.fake_allocation_) {
2658 .arg(query->getLabel())
2659 .arg(lease->addr_.toText())
2660 .arg(ia->getIAID());
2661 } else if (lease->reuseable_valid_lft_ == 0) {
2663 .arg(query->getLabel())
2664 .arg(lease->addr_.toText())
2665 .arg(ia->getIAID())
2666 .arg(Lease::lifetimeToText(lease->valid_lft_));
2667 } else {
2668 lease->valid_lft_ = lease->reuseable_valid_lft_;
2669 lease->preferred_lft_ = lease->reuseable_preferred_lft_;
2670 ctx.currentIA().reused_leases_.push_back(lease);
2672 .arg(query->getLabel())
2673 .arg(lease->addr_.toText())
2674 .arg(ia->getIAID())
2675 .arg(Lease::lifetimeToText(lease->valid_lft_));
2676
2677 // Increment the reuse statistics.
2678 StatsMgr::instance().addValue("v6-ia-na-lease-reuses", int64_t(1));
2679 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
2680 "v6-ia-na-lease-reuses"),
2681 int64_t(1));
2682 }
2684 .arg(query->getLabel())
2685 .arg(ia->getIAID())
2686 .arg(lease->toText());
2687
2688 // Set the values for T1 and T2.
2689 setTeeTimes(lease->preferred_lft_, subnet, ia_rsp);
2690
2691 Option6IAAddrPtr addr(new Option6IAAddr(D6O_IAADDR, lease->addr_,
2692 lease->preferred_lft_,
2693 lease->valid_lft_));
2694 ia_rsp->addOption(addr);
2695
2696 // It would be possible to insert status code=0(success) as well,
2697 // but this is considered waste of bandwidth as absence of status
2698 // code is considered a success.
2699
2700 } else {
2701 // Allocation engine did not allocate a lease. The engine logged
2702 // cause of that failure. The only thing left is to insert
2703 // status code to pass the sad news to the client.
2704
2707 .arg(query->getLabel())
2708 .arg(ia->getIAID());
2709
2710 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2712 "Sorry, no address could be"
2713 " allocated."));
2714 }
2715 return (ia_rsp);
2716}
2717
2721 boost::shared_ptr<Option6IA> ia) {
2722
2723 // Check if the client sent us a hint in his IA_PD. Clients may send an
2724 // address in their IA_PD options as a suggestion (e.g. the last address
2725 // they used before). While the hint consists of a full prefix (prefix +
2726 // length), getting just the prefix is sufficient to identify a lease.
2727 Option6IAPrefixPtr hint_opt =
2728 boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
2730 if (hint_opt) {
2731 hint = hint_opt->getAddress();
2732 }
2733
2734 if (ctx.fake_allocation_) {
2736 .arg(query->getLabel())
2737 .arg(ia->getIAID())
2738 .arg(hint_opt ? hint.toText() : "(no hint)");
2739 } else {
2741 .arg(query->getLabel())
2742 .arg(ia->getIAID())
2743 .arg(hint_opt ? hint.toText() : "(no hint)");
2744 }
2745
2746 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2747
2748 // Create IA_PD that we will put in the response.
2749 // Do not use OptionDefinition to create option's instance so
2750 // as we can initialize IAID using a constructor.
2751 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
2752
2753 // If there is no subnet selected for handling this IA_PD, the only thing
2754 // left to do is to say that we are sorry, but the user won't get an address.
2755 // As a convenience, we use a different status text to indicate that
2756 // (compare to the same status code, but different wording below)
2757 if (!subnet) {
2758
2759 // Insert status code NoAddrsAvail.
2760 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoPrefixAvail,
2761 "Sorry, no subnet available."));
2762 return (ia_rsp);
2763 }
2764
2765 // Set per-IA context values.
2766 ctx.createIAContext();
2767 ctx.currentIA().iaid_ = ia->getIAID();
2768 if (hint_opt) {
2769 ctx.currentIA().addHint(hint_opt);
2770 } else {
2771 ctx.currentIA().addHint(hint, 0);
2772 }
2774 ctx.currentIA().ia_rsp_ = ia_rsp;
2775
2776 // Use allocation engine to pick a lease for this client. Allocation engine
2777 // will try to honor the hint, but it is just a hint - some other address
2778 // may be used instead. If fake_allocation is set to false, the lease will
2779 // be inserted into the LeaseMgr as well.
2780 Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
2781
2782 if (!leases.empty()) {
2783
2784 // Need to retain the shortest preferred lease time to use
2785 // for calculating T1 and T2.
2786 uint32_t min_preferred_lft = (*leases.begin())->preferred_lft_;
2787
2788 const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
2789 for (auto const& l : leases) {
2790
2791 // We have a lease! Let's wrap its content into IA_PD option
2792 // with IAADDR suboption.
2793 if (ctx.fake_allocation_) {
2795 .arg(query->getLabel())
2796 .arg(l->addr_.toText())
2797 .arg(static_cast<int>(l->prefixlen_))
2798 .arg(ia->getIAID());
2799 } else if (l->reuseable_valid_lft_ == 0) {
2801 .arg(query->getLabel())
2802 .arg(l->addr_.toText())
2803 .arg(static_cast<int>(l->prefixlen_))
2804 .arg(ia->getIAID())
2805 .arg(Lease::lifetimeToText(l->valid_lft_));
2806 } else {
2807 l->valid_lft_ = l->reuseable_valid_lft_;
2808 l->preferred_lft_ = l->reuseable_preferred_lft_;
2810 .arg(query->getLabel())
2811 .arg(l->addr_.toText())
2812 .arg(static_cast<int>(l->prefixlen_))
2813 .arg(ia->getIAID())
2814 .arg(Lease::lifetimeToText(l->valid_lft_));
2815
2816 // Increment the reuse statistics.
2817 StatsMgr::instance().addValue("v6-ia-pd-lease-reuses", int64_t(1));
2818 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
2819 "v6-ia-pd-lease-reuses"),
2820 int64_t(1));
2821 }
2822
2823 // Check for new minimum lease time
2824 if ((l->preferred_lft_ > 0) && (min_preferred_lft > l->preferred_lft_)) {
2825 min_preferred_lft = l->preferred_lft_;
2826 }
2827
2828 boost::shared_ptr<Option6IAPrefix>
2829 addr(new Option6IAPrefix(D6O_IAPREFIX, l->addr_,
2830 l->prefixlen_, l->preferred_lft_,
2831 l->valid_lft_));
2832 ia_rsp->addOption(addr);
2833
2834 if (pd_exclude_requested) {
2835 OptionPtr pd_exclude_option = getPDExclude(ctx, l);
2836 if (pd_exclude_option) {
2837 addr->addOption(pd_exclude_option);
2838 }
2839 }
2840 }
2841
2842 // Set T1 and T2, using the shortest preferred lifetime among the leases.
2843 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
2844
2845 // It would be possible to insert status code=0(success) as well,
2846 // but this is considered waste of bandwidth as absence of status
2847 // code is considered a success.
2848
2849 } else {
2850 // Allocation engine did not allocate a lease. The engine logged
2851 // cause of that failure. The only thing left is to insert
2852 // status code to pass the sad news to the client.
2853
2856 .arg(query->getLabel())
2857 .arg(ia->getIAID());
2858
2859 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2861 "Sorry, no prefixes could"
2862 " be allocated."));
2863 }
2864 return (ia_rsp);
2865}
2866
2870 boost::shared_ptr<Option6IA> ia) {
2871
2873 .arg(query->getLabel())
2874 .arg(ia->getIAID());
2875
2876 // convenience values
2877 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2878
2879 // Create empty IA_NA option with IAID matching the request.
2880 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2881
2882 if (!subnet) {
2892 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2893 "Sorry, no known leases for this duid/iaid."));
2894 return (ia_rsp);
2895 }
2896
2897 // Set per-IA context values.
2898 ctx.createIAContext();
2899 ctx.currentIA().iaid_ = ia->getIAID();
2901 ctx.currentIA().ia_rsp_ = ia_rsp;
2902
2903 // Extract the addresses that the client is trying to obtain.
2904 OptionCollection addrs = ia->getOptions();
2905 for (auto const& it : addrs) {
2906 if (it.second->getType() != D6O_IAADDR) {
2907 continue;
2908 }
2909 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(it.second);
2910 if (!iaaddr) {
2911 // That's weird. Option code was ok, but the object type was not.
2912 // This should never happen. The only case would be with badly
2913 // mis-implemented hook libraries that insert invalid option objects.
2914 // There's no way to protect against this.
2915 continue;
2916 }
2917 ctx.currentIA().addHint(iaaddr);
2918 }
2919
2920 Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
2921
2922 // Ok, now we have the leases extended. We have:
2923 // - what the client tried to renew in ctx.hints_
2924 // - what we actually assigned in leases
2925 // - old leases that are no longer valid in ctx.old_leases_
2926
2927 // For each IA inserted by the client we have to determine what to do
2928 // about included addresses and notify the client. We will iterate over
2929 // those prefixes and remove those that we have already processed. We
2930 // don't want to remove them from the context, so we need to copy them
2931 // into temporary container.
2933
2934 // Retains the shortest valid lease time to use
2935 // for calculating T1 and T2.
2936 uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
2937
2938 // For all leases we have now, add the IAADDR with non-zero lifetimes.
2939 for (auto const& l : leases) {
2940 if (l->reuseable_valid_lft_ == 0) {
2942 .arg(query->getLabel())
2943 .arg(l->addr_.toText())
2944 .arg(ia->getIAID());
2945 } else {
2946 l->valid_lft_ = l->reuseable_valid_lft_;
2947 l->preferred_lft_ = l->reuseable_preferred_lft_;
2949 .arg(query->getLabel())
2950 .arg(l->addr_.toText())
2951 .arg(ia->getIAID())
2952 .arg(Lease::lifetimeToText(l->valid_lft_));
2953
2954 // Increment the reuse statistics.
2955 StatsMgr::instance().addValue("v6-ia-na-lease-reuses", int64_t(1));
2956 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
2957 "v6-ia-na-lease-reuses"),
2958 int64_t(1));
2959 }
2960
2962 l->addr_, l->preferred_lft_, l->valid_lft_));
2963 ia_rsp->addOption(iaaddr);
2964
2965 // Check for new minimum lease time
2966 if ((l->preferred_lft_ > 0) && (min_preferred_lft > l->preferred_lft_)) {
2967 min_preferred_lft = l->preferred_lft_;
2968 }
2969
2970 // Now remove this address from the hints list.
2971 AllocEngine::Resource hint_type(l->addr_);
2972 hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
2973 hints.end());
2974 }
2975
2976 // For the leases that we just retired, send the addresses with 0 lifetimes.
2977 for (auto const& l : ctx.currentIA().old_leases_) {
2978
2979 // Send an address with zero lifetimes only when this lease belonged to
2980 // this client. Do not send it when we're reusing an old lease that belonged
2981 // to someone else.
2982 if (equalValues(query->getClientId(), l->duid_)) {
2984 l->addr_, 0, 0));
2985 ia_rsp->addOption(iaaddr);
2986 }
2987
2988 // Now remove this address from the hints list.
2989 AllocEngine::Resource hint_type(l->addr_);
2990 hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
2991
2992 // If the new FQDN settings have changed for the lease, we need to
2993 // delete any existing FQDN records for this lease.
2994 if ((l->hostname_ != ctx.hostname_) || (l->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2995 (l->fqdn_rev_ != ctx.rev_dns_update_)) {
2998 .arg(query->getLabel())
2999 .arg(l->toText())
3000 .arg(ctx.hostname_)
3001 .arg(ctx.rev_dns_update_ ? "true" : "false")
3002 .arg(ctx.fwd_dns_update_ ? "true" : "false");
3003
3004 queueNCR(CHG_REMOVE, l);
3005 }
3006 }
3007
3008 // Finally, if there are any addresses requested that we haven't dealt with
3009 // already, inform the client that he can't have them.
3010 for (auto const& hint : hints) {
3012 hint.getAddress(), 0, 0));
3013 ia_rsp->addOption(iaaddr);
3014 }
3015
3016 if (!leases.empty()) {
3017 // We allocated leases so we need to update T1 and T2.
3018 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
3019 } else {
3020 // The server wasn't able allocate new lease and renew an existing
3021 // lease. In that case, the server sends NoAddrsAvail per RFC 8415.
3022 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
3024 "Sorry, no addresses could be"
3025 " assigned at this time."));
3026 }
3027
3028 return (ia_rsp);
3029}
3030
3034 boost::shared_ptr<Option6IA> ia) {
3035
3037 .arg(query->getLabel())
3038 .arg(ia->getIAID());
3039
3040 const ConstSubnet6Ptr& subnet = ctx.subnet_;
3041 const DuidPtr& duid = ctx.duid_;
3042
3043 // Let's create a IA_PD response and fill it in later
3044 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
3045
3046 // If there is no subnet for the particular client, we can't retrieve
3047 // information about client's leases from lease database. We treat this
3048 // as no binding for the client.
3049 if (!subnet) {
3050 // Per RFC 8415, section 18.3.4, if there is no binding and we are
3051 // processing a Renew, the NoBinding status code should be returned.
3052 if (query->getType() == DHCPV6_RENEW) {
3053 // Insert status code NoBinding
3054 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3055 "Sorry, no known PD leases"
3056 " for this duid/iaid."));
3057 return (ia_rsp);
3058
3059 // Per RFC 8415, section 18.3.5, if there is no binding and we are
3060 // processing Rebind, the message has to be discarded (assuming that
3061 // the server doesn't know if the prefix in the IA_PD option is
3062 // appropriate for the client's link). The exception being thrown
3063 // here should propagate to the main loop and cause the message to
3064 // be discarded.
3065 } else {
3066
3075 isc_throw(DHCPv6DiscardMessageError, "no subnet found for the"
3076 " client sending Rebind to extend lifetime of the"
3077 " prefix (DUID=" << duid->toText() << ", IAID="
3078 << ia->getIAID() << ")");
3079 }
3080 }
3081
3082 // Set per-IA context values.
3083 ctx.createIAContext();
3084 ctx.currentIA().iaid_ = ia->getIAID();
3086 ctx.currentIA().ia_rsp_ = ia_rsp;
3087
3088 // Extract prefixes that the client is trying to renew.
3089 OptionCollection addrs = ia->getOptions();
3090 for (auto const& it : addrs) {
3091 if (it.second->getType() != D6O_IAPREFIX) {
3092 continue;
3093 }
3094 Option6IAPrefixPtr prf = boost::dynamic_pointer_cast<Option6IAPrefix>(it.second);
3095 if (!prf) {
3096 // That's weird. Option code was ok, but the object type was not.
3097 // This should never happen. The only case would be with badly
3098 // mis-implemented hook libraries that insert invalid option objects.
3099 // There's no way to protect against this.
3100 continue;
3101 }
3102
3103 // Put the client's prefix into the hints list.
3104 ctx.currentIA().addHint(prf);
3105 }
3106
3107 // Call Allocation Engine and attempt to renew leases. Number of things
3108 // may happen. Leases may be extended, revoked (if the lease is no longer
3109 // valid or reserved for someone else), or new leases may be added.
3110 // Important parameters are:
3111 // - returned container - current valid leases
3112 // - old_leases - leases that used to be, but are no longer valid
3113 // - changed_leases - leases that have FQDN changed (not really important
3114 // in PD context)
3115 Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
3116
3117 // For each IA inserted by the client we have to determine what to do
3118 // about included prefixes and notify the client. We will iterate over
3119 // those prefixes and remove those that we have already processed. We
3120 // don't want to remove them from the context, so we need to copy them
3121 // into temporary container.
3123
3124 const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
3125
3126 // Retains the shortest valid lease time to use
3127 // for calculating T1 and T2.
3128 uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
3129
3130 for (auto const& l : leases) {
3131 if (l->reuseable_valid_lft_ == 0) {
3133 .arg(query->getLabel())
3134 .arg(l->addr_.toText())
3135 .arg(static_cast<int>(l->prefixlen_))
3136 .arg(ia->getIAID());
3137 } else {
3138 l->valid_lft_ = l->reuseable_valid_lft_;
3139 l->preferred_lft_ = l->reuseable_preferred_lft_;
3141 .arg(query->getLabel())
3142 .arg(l->addr_.toText())
3143 .arg(static_cast<int>(l->prefixlen_))
3144 .arg(ia->getIAID())
3145 .arg(Lease::lifetimeToText(l->valid_lft_));
3146
3147 // Increment the reuse statistics.
3148 StatsMgr::instance().addValue("v6-ia-pd-lease-reuses", int64_t(1));
3149 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
3150 "v6-ia-pd-lease-reuses"),
3151 int64_t(1));
3152 }
3153
3155 l->addr_, l->prefixlen_,
3156 l->preferred_lft_, l->valid_lft_));
3157 ia_rsp->addOption(prf);
3158
3159 if (pd_exclude_requested) {
3160 OptionPtr pd_exclude_option = getPDExclude(ctx, l);
3161 if (pd_exclude_option) {
3162 prf->addOption(pd_exclude_option);
3163 }
3164 }
3165
3166 // Check for new minimum lease time
3167 if ((l->preferred_lft_ > 0) && (l->preferred_lft_ < min_preferred_lft)) {
3168 min_preferred_lft = l->preferred_lft_;
3169 }
3170
3171 // Now remove this prefix from the hints list.
3172 AllocEngine::Resource hint_type(l->addr_, l->prefixlen_);
3173 hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
3174 hints.end());
3175 }
3176
3178 for (auto const& l : ctx.currentIA().old_leases_) {
3179
3180 // Send a prefix with zero lifetimes only when this lease belonged to
3181 // this client. Do not send it when we're reusing an old lease that belonged
3182 // to someone else.
3183 if (equalValues(query->getClientId(), l->duid_)) {
3184 Option6IAPrefixPtr prefix(new Option6IAPrefix(D6O_IAPREFIX, l->addr_,
3185 l->prefixlen_, 0, 0));
3186 ia_rsp->addOption(prefix);
3187 }
3188
3189 // Now remove this prefix from the hints list.
3190 AllocEngine::Resource hint_type(l->addr_, l->prefixlen_);
3191 hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
3192 }
3193
3194 // Finally, if there are any prefixes requested that we haven't dealt with
3195 // already, inform the client that he can't have them.
3196 for (auto const& prefix : hints) {
3197
3198 // Send the prefix with the zero lifetimes only if the prefix
3199 // contains non-zero value. A zero value indicates that the hint was
3200 // for the prefix length.
3201 if (!prefix.getAddress().isV6Zero()) {
3202 OptionPtr prefix_opt(new Option6IAPrefix(D6O_IAPREFIX,
3203 prefix.getAddress(),
3204 prefix.getPrefixLength(),
3205 0, 0));
3206 ia_rsp->addOption(prefix_opt);
3207 }
3208 }
3209
3210 if (!leases.empty()) {
3211 // We allocated leases so we need to update T1 and T2.
3212 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
3213 } else {
3214 // All is left is to insert the status code.
3215 // The server wasn't able allocate new lease and renew an existing
3216 // lease. In that case, the server sends NoPrefixAvail per RFC 8415.
3217 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
3219 "Sorry, no prefixes could be"
3220 " assigned at this time."));
3221 }
3222
3223 return (ia_rsp);
3224}
3225
3226void
3229
3230 // We will try to extend lease lifetime for all IA options in the client's
3231 // Renew or Rebind message.
3233
3234 // For the lease extension it is critical that the client has sent
3235 // DUID. There is no need to check for the presence of the DUID here
3236 // because we have already checked it in the sanityCheck().
3237
3238 // Save the originally selected subnet.
3239 ConstSubnet6Ptr orig_subnet = ctx.subnet_;
3240
3241 for (auto const& opt : query->options_) {
3242 switch (opt.second->getType()) {
3243 case D6O_IA_NA: {
3244 OptionPtr answer_opt = extendIA_NA(query, ctx,
3245 boost::dynamic_pointer_cast<
3246 Option6IA>(opt.second));
3247 if (answer_opt) {
3248 reply->addOption(answer_opt);
3249 }
3250 break;
3251 }
3252
3253 case D6O_IA_PD: {
3254 OptionPtr answer_opt = extendIA_PD(query, ctx,
3255 boost::dynamic_pointer_cast<
3256 Option6IA>(opt.second));
3257 if (answer_opt) {
3258 reply->addOption(answer_opt);
3259 }
3260 break;
3261 }
3262
3263 default:
3264 break;
3265 }
3266 }
3267
3268 // Subnet may be modified by the allocation engine, there are things
3269 // we need to do when that happens.
3270 checkDynamicSubnetChange(query, reply, ctx, orig_subnet);
3271}
3272
3273void
3276
3277 // We need to release addresses for all IA options in the client's
3278 // RELEASE message.
3279
3286
3287 // Let's set the status to be success by default. We can override it with
3288 // error status if needed. The important thing to understand here is that
3289 // the global status code may be set to success only if all IA options were
3290 // handled properly. Therefore the releaseIA_NA and releaseIA_PD options
3291 // may turn the status code to some error, but can't turn it back to success.
3292 int general_status = STATUS_Success;
3293 for (auto const& opt : release->options_) {
3294 Lease6Ptr old_lease;
3295 switch (opt.second->getType()) {
3296 case D6O_IA_NA: {
3297 OptionPtr answer_opt = releaseIA_NA(ctx.duid_, release, general_status,
3298 boost::dynamic_pointer_cast<Option6IA>(opt.second),
3299 old_lease);
3300 if (answer_opt) {
3301 reply->addOption(answer_opt);
3302 }
3303 break;
3304 }
3305 case D6O_IA_PD: {
3306 OptionPtr answer_opt = releaseIA_PD(ctx.duid_, release, general_status,
3307 boost::dynamic_pointer_cast<Option6IA>(opt.second),
3308 old_lease);
3309 if (answer_opt) {
3310 reply->addOption(answer_opt);
3311 }
3312 break;
3313 }
3314 // @todo: add support for IA_TA
3315 default:
3316 // remaining options are stateless and thus ignored in this context
3317 ;
3318 }
3319
3320 // Store the old lease.
3321 if (old_lease) {
3322 ctx.currentIA().old_leases_.push_back(old_lease);
3323 }
3324 }
3325
3326 // Include top-level status code as well.
3327 reply->addOption(createStatusCode(*release, general_status,
3328 "Summary status for all processed IA_NAs"));
3329}
3330
3332Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
3333 int& general_status, boost::shared_ptr<Option6IA> ia,
3334 Lease6Ptr& old_lease) {
3335
3337 .arg(query->getLabel())
3338 .arg(ia->getIAID());
3339
3340 // Release can be done in one of two ways:
3341 // Approach 1: extract address from client's IA_NA and see if it belongs
3342 // to this particular client.
3343 // Approach 2: find a subnet for this client, get a lease for
3344 // this subnet/duid/iaid and check if its content matches to what the
3345 // client is asking us to release.
3346 //
3347 // This method implements approach 1.
3348
3349 // That's our response
3350 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
3351
3352 Option6IAAddrPtr release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
3353 (ia->getOption(D6O_IAADDR));
3354 if (!release_addr) {
3355 ia_rsp->addOption(createStatusCode(*query, STATUS_NoBinding,
3356 "You did not include an address in your RELEASE"));
3357 general_status = STATUS_NoBinding;
3358 return (ia_rsp);
3359 }
3360
3362 release_addr->getAddress());
3363
3364 if (!lease) {
3365 // client releasing a lease that we don't know about.
3366
3367 // Insert status code NoBinding.
3368 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3369 "Sorry, no known leases for this duid/iaid, can't release."));
3370 general_status = STATUS_NoBinding;
3371
3372 return (ia_rsp);
3373 }
3374
3375 if (!lease->duid_) {
3376 // Something is gravely wrong here. We do have a lease, but it does not
3377 // have mandatory DUID information attached. Someone was messing with our
3378 // database.
3379
3381 .arg(query->getLabel())
3382 .arg(release_addr->getAddress().toText());
3383
3384 general_status = STATUS_UnspecFail;
3385 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3386 "Database consistency check failed when trying to RELEASE"));
3387 return (ia_rsp);
3388 }
3389
3390 if (*duid != *(lease->duid_)) {
3391
3392 // Sorry, it's not your address. You can't release it.
3394 .arg(query->getLabel())
3395 .arg(release_addr->getAddress().toText())
3396 .arg(lease->duid_->toText());
3397
3398 general_status = STATUS_NoBinding;
3399 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3400 "This address does not belong to you, you can't release it"));
3401 return (ia_rsp);
3402 }
3403
3404 if (ia->getIAID() != lease->iaid_) {
3405 // This address belongs to this client, but to a different IA
3407 .arg(query->getLabel())
3408 .arg(release_addr->getAddress().toText())
3409 .arg(lease->iaid_)
3410 .arg(ia->getIAID());
3411 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3412 "This is your address, but you used wrong IAID"));
3413 general_status = STATUS_NoBinding;
3414 return (ia_rsp);
3415 }
3416
3417 // It is not necessary to check if the address matches as we used
3418 // getLease6(addr) method that is supposed to return a proper lease.
3419
3420 bool skip = false;
3421 // Execute all callouts registered for packet6_send
3422 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
3423 CalloutHandlePtr callout_handle = getCalloutHandle(query);
3424
3425 // Use the RAII wrapper to make sure that the callout handle state is
3426 // reset when this object goes out of scope. All hook points must do
3427 // it to prevent possible circular dependency between the callout
3428 // handle and its arguments.
3429 ScopedCalloutHandleState callout_handle_state(callout_handle);
3430
3431 // Enable copying options from the packet within hook library.
3432 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
3433
3434 // Delete all previous arguments
3435 callout_handle->deleteAllArguments();
3436
3437 // Pass the original packet
3438 callout_handle->setArgument("query6", query);
3439
3440 // Pass the lease to be updated
3441 callout_handle->setArgument("lease6", lease);
3442
3443 // Call all installed callouts
3444 HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
3445
3446 // Callouts decided to skip the next processing step. The next
3447 // processing step would be to send the packet, so skip at this
3448 // stage means "drop response".
3449 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3450 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3451 skip = true;
3453 .arg(query->getLabel());
3454 }
3455 }
3456
3457 // Ok, we've passed all checks. Let's release this address.
3458 bool success = false; // was the removal operation successful?
3459 bool expired = false; // explicitly expired instead of removed?
3460 auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3461
3462 // Callout didn't indicate to skip the release process. Let's release
3463 // the lease.
3464 if (!skip) {
3465 // Delete lease only if affinity is disabled.
3466 if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3467 expiration_cfg->getHoldReclaimedTime() &&
3468 lease->valid_lft_ != Lease::INFINITY_LFT) {
3469 // Expire the lease.
3470 lease->valid_lft_ = 0;
3471 lease->preferred_lft_ = 0;
3472 // Set the lease state to released to indicate that this lease
3473 // must be preserved in the database. It is particularly useful
3474 // in HA to differentiate between the leases that should be
3475 // updated in the partner's database and deleted from the partner's
3476 // database.
3477 lease->state_ = Lease6::STATE_RELEASED;
3478 LeaseMgrFactory::instance().updateLease6(lease);
3479 expired = true;
3480 success = true;
3481 } else {
3482 success = LeaseMgrFactory::instance().deleteLease(lease);
3483 }
3484 }
3485
3486 // Here the success should be true if we removed lease successfully
3487 // and false if skip flag was set or the removal failed for whatever reason
3488
3489 if (!success) {
3490 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3491 "Server failed to release a lease"));
3492
3494 .arg(query->getLabel())
3495 .arg(lease->addr_.toText())
3496 .arg(lease->iaid_);
3497 general_status = STATUS_UnspecFail;
3498
3499 return (ia_rsp);
3500 } else {
3501 old_lease = lease;
3502
3504 .arg(query->getLabel())
3505 .arg(lease->addr_.toText())
3506 .arg(lease->iaid_);
3507
3508 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
3509 "Lease released. Thank you, please come again."));
3510
3511 if (expired) {
3513 .arg(query->getLabel())
3514 .arg(lease->addr_.toText())
3515 .arg(lease->iaid_);
3516 } else {
3518 .arg(query->getLabel())
3519 .arg(lease->addr_.toText())
3520 .arg(lease->iaid_);
3521
3522 // Check if a lease has flags indicating that the FQDN update has
3523 // been performed. If so, create NameChangeRequest which removes
3524 // the entries.
3525 queueNCR(CHG_REMOVE, lease);
3526 }
3527
3528 // Need to decrease statistic for assigned addresses.
3529 StatsMgr::instance().addValue(
3530 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-nas"),
3531 static_cast<int64_t>(-1));
3532
3533 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3534 if (subnet) {
3535 auto const& pool = subnet->getPool(Lease::TYPE_NA, lease->addr_, false);
3536 if (pool) {
3537 StatsMgr::instance().addValue(
3538 StatsMgr::generateName("subnet", subnet->getID(),
3539 StatsMgr::generateName("pool", pool->getID(), "assigned-nas")),
3540 static_cast<int64_t>(-1));
3541 }
3542 }
3543
3544 return (ia_rsp);
3545 }
3546}
3547
3549Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
3550 int& general_status, boost::shared_ptr<Option6IA> ia,
3551 Lease6Ptr& old_lease) {
3552 // Release can be done in one of two ways:
3553 // Approach 1: extract address from client's IA_NA and see if it belongs
3554 // to this particular client.
3555 // Approach 2: find a subnet for this client, get a lease for
3556 // this subnet/duid/iaid and check if its content matches to what the
3557 // client is asking us to release.
3558 //
3559 // This method implements approach 1.
3560
3561 // That's our response. We will fill it in as we check the lease to be
3562 // released.
3563 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
3564
3565 boost::shared_ptr<Option6IAPrefix> release_prefix =
3566 boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
3567 if (!release_prefix) {
3568 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3569 "You did not include a prefix in your RELEASE"));
3570 general_status = STATUS_NoBinding;
3571 return (ia_rsp);
3572 }
3573
3575 release_prefix->getAddress());
3576
3577 if (!lease) {
3578 // Client releasing a lease that we don't know about.
3579
3580 // Insert status code NoBinding.
3581 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3582 "Sorry, no known leases for this duid/iaid, can't release."));
3583 general_status = STATUS_NoBinding;
3584
3585 return (ia_rsp);
3586 }
3587
3588 if (!lease->duid_) {
3589 // Something is gravely wrong here. We do have a lease, but it does not
3590 // have mandatory DUID information attached. Someone was messing with our
3591 // database.
3593 .arg(query->getLabel())
3594 .arg(release_prefix->getAddress().toText())
3595 .arg(static_cast<int>(release_prefix->getLength()));
3596
3597 general_status = STATUS_UnspecFail;
3598 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3599 "Database consistency check failed when trying to RELEASE"));
3600 return (ia_rsp);
3601 }
3602
3603 if (*duid != *(lease->duid_)) {
3604 // Sorry, it's not your address. You can't release it.
3606 .arg(query->getLabel())
3607 .arg(release_prefix->getAddress().toText())
3608 .arg(static_cast<int>(release_prefix->getLength()))
3609 .arg(lease->duid_->toText());
3610
3611 general_status = STATUS_NoBinding;
3612 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3613 "This address does not belong to you, you can't release it"));
3614 return (ia_rsp);
3615 }
3616
3617 if (ia->getIAID() != lease->iaid_) {
3618 // This address belongs to this client, but to a different IA
3620 .arg(query->getLabel())
3621 .arg(release_prefix->getAddress().toText())
3622 .arg(static_cast<int>(release_prefix->getLength()))
3623 .arg(lease->iaid_)
3624 .arg(ia->getIAID());
3625 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3626 "This is your address, but you used wrong IAID"));
3627 general_status = STATUS_NoBinding;
3628 return (ia_rsp);
3629 }
3630
3631 // It is not necessary to check if the address matches as we used
3632 // getLease6(addr) method that is supposed to return a proper lease.
3633
3634 bool skip = false;
3635 // Execute all callouts registered for packet6_send
3636 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
3637 CalloutHandlePtr callout_handle = getCalloutHandle(query);
3638
3639 // Use the RAII wrapper to make sure that the callout handle state is
3640 // reset when this object goes out of scope. All hook points must do
3641 // it to prevent possible circular dependency between the callout
3642 // handle and its arguments.
3643 ScopedCalloutHandleState callout_handle_state(callout_handle);
3644
3645 // Enable copying options from the packet within hook library.
3646 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
3647
3648 // Pass the original packet
3649 callout_handle->setArgument("query6", query);
3650
3651 // Pass the lease to be updated
3652 callout_handle->setArgument("lease6", lease);
3653
3654 // Call all installed callouts
3655 HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
3656
3657 // Callouts decided to skip the next processing step. The next
3658 // processing step would be to send the packet, so skip at this
3659 // stage means "drop response".
3660 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3661 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3662 skip = true;
3664 .arg(query->getLabel());
3665 }
3666 }
3667
3668 // Ok, we've passed all checks. Let's release this prefix.
3669 bool success = false; // was the removal operation successful?
3670 bool expired = false; // explicitly expired instead of removed?
3671 auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3672
3673 // Callout didn't indicate to skip the release process. Let's release
3674 // the lease.
3675 if (!skip) {
3676 // Delete lease only if affinity is disabled.
3677 if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3678 expiration_cfg->getHoldReclaimedTime() &&
3679 lease->valid_lft_ != Lease::INFINITY_LFT) {
3680 // Expire the lease.
3681 lease->valid_lft_ = 0;
3682 lease->preferred_lft_ = 0;
3683 // Set the lease state to released to indicate that this lease
3684 // must be preserved in the database. It is particularly useful
3685 // in HA to differentiate between the leases that should be
3686 // updated in the partner's database and deleted from the partner's
3687 // database.
3688 lease->state_ = Lease6::STATE_RELEASED;
3689 LeaseMgrFactory::instance().updateLease6(lease);
3690 expired = true;
3691 success = true;
3692 } else {
3693 success = LeaseMgrFactory::instance().deleteLease(lease);
3694 }
3695 }
3696
3697 // Here the success should be true if we removed lease successfully
3698 // and false if skip flag was set or the removal failed for whatever reason
3699
3700 if (!success) {
3701 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3702 "Server failed to release a lease"));
3703
3705 .arg(query->getLabel())
3706 .arg(lease->addr_.toText())
3707 .arg(static_cast<int>(lease->prefixlen_))
3708 .arg(lease->iaid_);
3709 general_status = STATUS_UnspecFail;
3710
3711 } else {
3712 old_lease = lease;
3713
3715 .arg(query->getLabel())
3716 .arg(lease->addr_.toText())
3717 .arg(static_cast<int>(lease->prefixlen_))
3718 .arg(lease->iaid_);
3719
3720 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
3721 "Lease released. Thank you, please come again."));
3722
3723 if (expired) {
3725 .arg(query->getLabel())
3726 .arg(lease->addr_.toText())
3727 .arg(static_cast<int>(lease->prefixlen_))
3728 .arg(lease->iaid_);
3729 } else {
3731 .arg(query->getLabel())
3732 .arg(lease->addr_.toText())
3733 .arg(static_cast<int>(lease->prefixlen_))
3734 .arg(lease->iaid_);
3735 }
3736
3737 // Need to decrease statistic for assigned prefixes.
3738 StatsMgr::instance().addValue(
3739 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"),
3740 static_cast<int64_t>(-1));
3741
3742 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3743 if (subnet) {
3744 auto const& pool = subnet->getPool(Lease::TYPE_PD, lease->addr_, false);
3745 if (pool) {
3746 StatsMgr::instance().addValue(
3747 StatsMgr::generateName("subnet", subnet->getID(),
3748 StatsMgr::generateName("pd-pool", pool->getID(), "assigned-pds")),
3749 static_cast<int64_t>(-1));
3750 }
3751 }
3752 }
3753
3754 return (ia_rsp);
3755}
3756
3757Pkt6Ptr
3759
3760 Pkt6Ptr solicit = ctx.query_;
3761 Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
3762
3763 // Handle Rapid Commit option, if present.
3764 if (ctx.subnet_ && ctx.subnet_->getRapidCommit()) {
3765 OptionPtr opt_rapid_commit = solicit->getOption(D6O_RAPID_COMMIT);
3766 if (opt_rapid_commit) {
3767
3769 .arg(solicit->getLabel());
3770
3771 // If Rapid Commit has been sent by the client, change the
3772 // response type to Reply and include Rapid Commit option.
3773 response->setType(DHCPV6_REPLY);
3774 response->addOption(opt_rapid_commit);
3775 }
3776 }
3777
3778 // "Fake" allocation is the case when the server is processing the Solicit
3779 // message without the Rapid Commit option and advertises a lease to
3780 // the client, but doesn't commit this lease to the lease database. If
3781 // the Solicit contains the Rapid Commit option and the server is
3782 // configured to honor the Rapid Commit option, or the client has sent
3783 // the Request message, the lease will be committed to the lease
3784 // database. The type of the server's response may be used to determine
3785 // if this is the fake allocation case or not. When the server sends
3786 // Reply message it means that it is committing leases. Other message
3787 // type (Advertise) means that server is not committing leases (fake
3788 // allocation).
3789 ctx.fake_allocation_ = (response->getType() != DHCPV6_REPLY);
3790
3791 processClientFqdn(solicit, response, ctx);
3792
3793 if (MultiThreadingMgr::instance().getMode()) {
3794 // The lease reclamation cannot run at the same time.
3795 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3796
3797 assignLeases(solicit, response, ctx);
3798 } else {
3799 assignLeases(solicit, response, ctx);
3800 }
3801
3803 // Evaluate additional classes.
3804 evaluateAdditionalClasses(solicit, ctx);
3805
3807 .arg(solicit->getLabel())
3808 .arg(solicit->getName())
3809 .arg(solicit->getClasses().toText());
3810
3811 copyClientOptions(solicit, response);
3812 CfgOptionList co_list;
3813 buildCfgOptionList(solicit, ctx, co_list);
3814 appendDefaultOptions(solicit, response, co_list);
3815 appendRequestedOptions(solicit, response, co_list);
3816 appendRequestedVendorOptions(solicit, response, ctx, co_list);
3817
3818 updateReservedFqdn(ctx, response);
3819
3820 // Only generate name change requests if sending a Reply as a result
3821 // of receiving Rapid Commit option.
3822 if (response->getType() == DHCPV6_REPLY) {
3823 createNameChangeRequests(response, ctx);
3824 }
3825
3826 return (response);
3827}
3828
3829Pkt6Ptr
3831
3832 Pkt6Ptr request = ctx.query_;
3833 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
3834
3835 processClientFqdn(request, reply, ctx);
3836
3837 if (MultiThreadingMgr::instance().getMode()) {
3838 // The lease reclamation cannot run at the same time.
3839 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3840
3841 assignLeases(request, reply, ctx);
3842 } else {
3843 assignLeases(request, reply, ctx);
3844 }
3845
3847 // Evaluate additional classes.
3848 evaluateAdditionalClasses(request, ctx);
3849
3851 .arg(request->getLabel())
3852 .arg(request->getName())
3853 .arg(request->getClasses().toText());
3854
3855 copyClientOptions(request, reply);
3856 CfgOptionList co_list;
3857 buildCfgOptionList(request, ctx, co_list);
3858 appendDefaultOptions(request, reply, co_list);
3859 appendRequestedOptions(request, reply, co_list);
3860 appendRequestedVendorOptions(request, reply, ctx, co_list);
3861
3862 updateReservedFqdn(ctx, reply);
3863 generateFqdn(reply, ctx);
3864 createNameChangeRequests(reply, ctx);
3865
3866 return (reply);
3867}
3868
3869Pkt6Ptr
3871
3872 Pkt6Ptr renew = ctx.query_;
3873 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
3874
3875 processClientFqdn(renew, reply, ctx);
3876
3877 if (MultiThreadingMgr::instance().getMode()) {
3878 // The lease reclamation cannot run at the same time.
3879 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3880
3881 extendLeases(renew, reply, ctx);
3882 } else {
3883 extendLeases(renew, reply, ctx);
3884 }
3885
3887 // Evaluate additional classes.
3888 evaluateAdditionalClasses(renew, ctx);
3889
3891 .arg(renew->getLabel())
3892 .arg(renew->getName())
3893 .arg(renew->getClasses().toText());
3894
3895 copyClientOptions(renew, reply);
3896 CfgOptionList co_list;
3897 buildCfgOptionList(renew, ctx, co_list);
3898 appendDefaultOptions(renew, reply, co_list);
3899 appendRequestedOptions(renew, reply, co_list);
3900 appendRequestedVendorOptions(renew, reply, ctx, co_list);
3901
3902 updateReservedFqdn(ctx, reply);
3903 generateFqdn(reply, ctx);
3904 createNameChangeRequests(reply, ctx);
3905
3906 return (reply);
3907}
3908
3909Pkt6Ptr
3911
3912 Pkt6Ptr rebind = ctx.query_;
3913 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
3914
3915 processClientFqdn(rebind, reply, ctx);
3916
3917 if (MultiThreadingMgr::instance().getMode()) {
3918 // The lease reclamation cannot run at the same time.
3919 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3920
3921 extendLeases(rebind, reply, ctx);
3922 } else {
3923 extendLeases(rebind, reply, ctx);
3924 }
3925
3927 // Evaluate additional classes.
3928 evaluateAdditionalClasses(rebind, ctx);
3929
3931 .arg(rebind->getLabel())
3932 .arg(rebind->getName())
3933 .arg(rebind->getClasses().toText());
3934
3935 copyClientOptions(rebind, reply);
3936 CfgOptionList co_list;
3937 buildCfgOptionList(rebind, ctx, co_list);
3938 appendDefaultOptions(rebind, reply, co_list);
3939 appendRequestedOptions(rebind, reply, co_list);
3940 appendRequestedVendorOptions(rebind, reply, ctx, co_list);
3941
3942 updateReservedFqdn(ctx, reply);
3943 generateFqdn(reply, ctx);
3944 createNameChangeRequests(reply, ctx);
3945
3946 return (reply);
3947}
3948
3949Pkt6Ptr
3951
3952 Pkt6Ptr confirm = ctx.query_;
3954 // Evaluate additional classes.
3955 evaluateAdditionalClasses(confirm, ctx);
3956
3958 .arg(confirm->getLabel())
3959 .arg(confirm->getName())
3960 .arg(confirm->getClasses().toText());
3961
3962 // Get IA_NAs from the Confirm. If there are none, the message is
3963 // invalid and must be discarded. There is nothing more to do.
3964 OptionCollection ias = confirm->getOptions(D6O_IA_NA);
3965 if (ias.empty()) {
3966 return (Pkt6Ptr());
3967 }
3968
3969 // The server sends Reply message in response to Confirm.
3970 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
3971 // Make sure that the necessary options are included.
3972 copyClientOptions(confirm, reply);
3973 CfgOptionList co_list;
3974 buildCfgOptionList(confirm, ctx, co_list);
3975 appendDefaultOptions(confirm, reply, co_list);
3976 appendRequestedOptions(confirm, reply, co_list);
3977 appendRequestedVendorOptions(confirm, reply, ctx, co_list);
3978 // Indicates if at least one address has been verified. If no addresses
3979 // are verified it means that the client has sent no IA_NA options
3980 // or no IAAddr options and that client's message has to be discarded.
3981 bool verified = false;
3982 // Check if subnet was selected for the message. If no subnet
3983 // has been selected, the client is not on link.
3984 ConstSubnetPtr subnet = ctx.subnet_;
3985
3986 // Regardless if the subnet has been selected or not, we will iterate
3987 // over the IA_NA options to check if they hold any addresses. If there
3988 // are no, the Confirm is discarded.
3989 // Check addresses in IA_NA options and make sure they are appropriate.
3990 for (auto const& ia : ias) {
3991 const OptionCollection& opts = ia.second->getOptions();
3992 for (auto const& opt : opts) {
3993 // Ignore options other than IAAddr.
3994 if (opt.second->getType() == D6O_IAADDR) {
3995 // Check that the address is in range in the subnet selected.
3996 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
3997 Option6IAAddr>(opt.second);
3998 // If there is subnet selected and the address has been included
3999 // in IA_NA, mark it verified and verify that it belongs to the
4000 // subnet.
4001 if (iaaddr) {
4002 // If at least one address is not in range, then return
4003 // the NotOnLink status code.
4004 if (subnet && !subnet->inRange(iaaddr->getAddress())) {
4005 std::ostringstream status_msg;
4006 status_msg << "Address " << iaaddr->getAddress()
4007 << " is not on link.";
4008 reply->addOption(createStatusCode(*confirm,
4010 status_msg.str()));
4011 return (reply);
4012 }
4013 verified = true;
4014 } else {
4015 isc_throw(Unexpected, "failed to cast the IA Address option"
4016 " to the Option6IAAddrPtr. This is programming"
4017 " error and should be reported");
4018 }
4019 }
4020 }
4021 }
4022
4023 // It seems that the client hasn't included any addresses in which case
4024 // the Confirm must be discarded.
4025 if (!verified) {
4026 return (Pkt6Ptr());
4027 }
4028
4029 // If there is a subnet, there were addresses in IA_NA options and the
4030 // addresses where consistent with the subnet then the client is on link.
4031 if (subnet) {
4032 // All addresses in range, so return success.
4033 reply->addOption(createStatusCode(*confirm, STATUS_Success,
4034 "All addresses are on-link"));
4035 } else {
4036 reply->addOption(createStatusCode(*confirm, STATUS_NotOnLink,
4037 "No subnet selected"));
4038 }
4039
4040 return (reply);
4041}
4042
4043Pkt6Ptr
4045
4046 Pkt6Ptr release = ctx.query_;
4048 // Evaluate additional classes.
4049 evaluateAdditionalClasses(release, ctx);
4050
4052 .arg(release->getLabel())
4053 .arg(release->getName())
4054 .arg(release->getClasses().toText());
4055
4056 // Create an empty Reply message.
4057 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
4058
4059 // Copy client options (client-id, also relay information if present)
4060 copyClientOptions(release, reply);
4061
4062 // Get the configured option list
4063 CfgOptionList co_list;
4064 // buildCfgOptionList(release, ctx, co_list);
4065 appendDefaultOptions(release, reply, co_list);
4066
4067 releaseLeases(release, reply, ctx);
4068
4071
4072 return (reply);
4073}
4074
4075Pkt6Ptr
4077
4078 Pkt6Ptr decline = ctx.query_;
4080 // Evaluate additional classes.
4081 evaluateAdditionalClasses(decline, ctx);
4082
4084 .arg(decline->getLabel())
4085 .arg(decline->getName())
4086 .arg(decline->getClasses().toText());
4087
4088 // Create an empty Reply message.
4089 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
4090
4091 // Copy client options (client-id, also relay information if present)
4092 copyClientOptions(decline, reply);
4093
4094 // Get the configured option list
4095 CfgOptionList co_list;
4096 buildCfgOptionList(decline, ctx, co_list);
4097
4098 // Include server-id
4099 appendDefaultOptions(decline, reply, co_list);
4100
4101 if (declineLeases(decline, reply, ctx)) {
4102 return (reply);
4103 } else {
4104
4105 // declineLeases returns false only if the hooks set the next step
4106 // status to DROP. We'll just doing as requested.
4107 return (Pkt6Ptr());
4108 }
4109}
4110
4111bool
4114
4115 // We need to decline addresses for all IA_NA options in the client's
4116 // DECLINE message.
4117
4118 // Let's set the status to be success by default. We can override it with
4119 // error status if needed. The important thing to understand here is that
4120 // the global status code may be set to success only if all IA options were
4121 // handled properly. Therefore the declineIA options
4122 // may turn the status code to some error, but can't turn it back to success.
4123 int general_status = STATUS_Success;
4124
4125 for (auto const& opt : decline->options_) {
4126 switch (opt.second->getType()) {
4127 case D6O_IA_NA: {
4128 OptionPtr answer_opt = declineIA(decline, ctx.duid_, general_status,
4129 boost::dynamic_pointer_cast<Option6IA>(opt.second),
4130 ctx.new_leases_);
4131 if (answer_opt) {
4132
4133 // We have an answer, let's use it.
4134 reply->addOption(answer_opt);
4135 } else {
4136
4137 // The only case when declineIA could return NULL is if one of the
4138 // hook callouts set next step status to DROP. We just need to drop
4139 // this packet.
4140 return (false);
4141 }
4142 break;
4143 }
4144 default:
4145 // We don't care for the remaining options
4146 ;
4147 }
4148 }
4149
4150 return (true);
4151}
4152
4154Dhcpv6Srv::declineIA(const Pkt6Ptr& decline, const DuidPtr& duid,
4155 int& general_status, boost::shared_ptr<Option6IA> ia,
4156 Lease6Collection& new_leases) {
4157
4159 .arg(decline->getLabel())
4160 .arg(ia->getIAID());
4161
4162 // Decline can be done in one of two ways:
4163 // Approach 1: extract address from client's IA_NA and see if it belongs
4164 // to this particular client.
4165 // Approach 2: find a subnet for this client, get a lease for
4166 // this subnet/duid/iaid and check if its content matches to what the
4167 // client is asking us to decline.
4168 //
4169 // This method implements approach 1.
4170
4171 // That's our response
4172 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
4173
4174 const OptionCollection& opts = ia->getOptions();
4175 int total_addrs = 0; // Let's count the total number of addresses.
4176 for (auto const& opt : opts) {
4177
4178 // Let's ignore nested options other than IAADDR (there shouldn't be anything
4179 // else in IA_NA in Decline message, but let's be on the safe side).
4180 if (opt.second->getType() != D6O_IAADDR) {
4181 continue;
4182 }
4183 Option6IAAddrPtr decline_addr = boost::dynamic_pointer_cast<Option6IAAddr>
4184 (opt.second);
4185 if (!decline_addr) {
4186 continue;
4187 }
4188
4189 total_addrs++;
4190
4192 decline_addr->getAddress());
4193
4194 if (!lease || lease->expired() || lease->state_ != Lease::STATE_DEFAULT) {
4195 // Client trying to decline a lease that we don't know about.
4197 .arg(decline->getLabel()).arg(decline_addr->getAddress().toText());
4198
4199 // According to RFC 8415, section 18.3.8:
4200 // "For each IA in the Decline message for which the server has no
4201 // binding information, the server adds an IA option using the IAID
4202 // from the Decline message and includes a Status Code option with
4203 // the value NoBinding in the IA option".
4204 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4205 "Server does not know about such an address."));
4206
4207 // In the same section of RFC 8415:
4208 // "The server ignores addresses not assigned to the IAs (though it may"
4209 // choose to log an error if it finds such addresses)."
4210 continue; // There may be other addresses.
4211 }
4212
4213 if (!lease->duid_) {
4214 // Something is gravely wrong here. We do have a lease, but it does not
4215 // have mandatory DUID information attached. Someone was messing with our
4216 // database.
4217
4219 .arg(decline->getLabel())
4220 .arg(decline_addr->getAddress().toText());
4221
4222 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_UnspecFail,
4223 "Database consistency check failed when attempting Decline."));
4224
4225 continue;
4226 }
4227
4228 // Ok, there's a sane lease with an address. Let's check if DUID matches first.
4229 if (*duid != *(lease->duid_)) {
4230
4231 // Sorry, it's not your address. You can't release it.
4233 .arg(decline->getLabel())
4234 .arg(decline_addr->getAddress().toText())
4235 .arg(lease->duid_->toText());
4236
4237 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4238 "This address does not belong to you, you can't decline it"));
4239
4240 continue;
4241 }
4242
4243 // Let's check if IAID matches.
4244 if (ia->getIAID() != lease->iaid_) {
4245 // This address belongs to this client, but to a different IA
4247 .arg(decline->getLabel())
4248 .arg(lease->addr_.toText())
4249 .arg(ia->getIAID())
4250 .arg(lease->iaid_);
4251 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4252 "This is your address, but you used wrong IAID"));
4253
4254 continue;
4255 }
4256
4257 // Ok, all is good. Decline this lease.
4258 if (!declineLease(decline, lease, ia_rsp)) {
4259 // declineLease returns false only when hook callouts set the next
4260 // step status to drop. We just propagate the bad news here.
4261 return (OptionPtr());
4262
4263 } else {
4264 new_leases.push_back(lease);
4265 }
4266 }
4267
4268 if (total_addrs == 0) {
4269 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4270 "No addresses sent in IA_NA"));
4271 general_status = STATUS_NoBinding;
4272 }
4273
4274 return (ia_rsp);
4275}
4276
4277void
4278Dhcpv6Srv::setStatusCode(boost::shared_ptr<isc::dhcp::Option6IA>& container,
4279 const OptionPtr& status) {
4280 // Let's delete any old status code we may have.
4281 container->delOption(D6O_STATUS_CODE);
4282
4283 container->addOption(status);
4284}
4285
4286bool
4287Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
4288 boost::shared_ptr<Option6IA> ia_rsp) {
4289 // We do not want to decrease the assigned-nas at this time. While
4290 // technically a declined address is no longer allocated, the
4291 // primary usage of the assigned-nas statistic is to monitor pool
4292 // utilization. Most people would forget to include declined-addresses
4293 // in the calculation, and simply do assigned-nas/total-nas. This
4294 // would have a bias towards under-representing pool utilization,
4295 // if we decreased allocated immediately after receiving DHCPDECLINE,
4296 // rather than later when we recover the address.
4297
4298 // Let's call lease6_decline hooks if necessary.
4299 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_decline_)) {
4300 CalloutHandlePtr callout_handle = getCalloutHandle(decline);
4301
4302 // Use the RAII wrapper to make sure that the callout handle state is
4303 // reset when this object goes out of scope. All hook points must do
4304 // it to prevent possible circular dependency between the callout
4305 // handle and its arguments.
4306 ScopedCalloutHandleState callout_handle_state(callout_handle);
4307
4308 // Enable copying options from the packet within hook library.
4309 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(decline);
4310
4311 // Pass the original packet
4312 callout_handle->setArgument("query6", decline);
4313
4314 // Pass the lease to be updated
4315 callout_handle->setArgument("lease6", lease);
4316
4317 // Call callouts
4318 HooksManager::callCallouts(Hooks.hook_index_lease6_decline_,
4319 *callout_handle);
4320
4321 // Callouts decided to SKIP the next processing step. The next
4322 // processing step would be to actually decline the lease, so we'll
4323 // keep the lease as is.
4324 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4326 .arg(decline->getLabel())
4327 .arg(decline->getIface())
4328 .arg(lease->addr_.toText());
4329 return (true);
4330 }
4331
4332 // Callouts decided to DROP the packet. Let's simply log it and
4333 // return false, so callers will act accordingly.
4334 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
4336 .arg(decline->getLabel())
4337 .arg(decline->getIface())
4338 .arg(lease->addr_.toText());
4339 return (false);
4340 }
4341 }
4342
4343 Lease6Ptr old_values = boost::make_shared<Lease6>(*lease);
4344
4345 // @todo: Call hooks.
4346
4347 // We need to disassociate the lease from the client. Once we move a lease
4348 // to declined state, it is no longer associated with the client in any
4349 // way.
4350 lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
4351
4352 try {
4353 LeaseMgrFactory::instance().updateLease6(lease);
4354 } catch (const Exception& ex) {
4355 // Update failed.
4357 .arg(decline->getLabel())
4358 .arg(lease->addr_.toText())
4359 .arg(ex.what());
4360 return (false);
4361 }
4362
4363 // Check if a lease has flags indicating that the FQDN update has
4364 // been performed. If so, create NameChangeRequest which removes
4365 // the entries. This method does all necessary checks.
4366 queueNCR(CHG_REMOVE, old_values);
4367
4368 // Bump up the subnet-specific statistic.
4369 StatsMgr::instance().addValue(
4370 StatsMgr::generateName("subnet", lease->subnet_id_, "declined-addresses"),
4371 static_cast<int64_t>(1));
4372
4373 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
4374 if (subnet) {
4375 auto const& pool = subnet->getPool(Lease::TYPE_NA, lease->addr_, false);
4376 if (pool) {
4377 StatsMgr::instance().addValue(
4378 StatsMgr::generateName("subnet", subnet->getID(),
4379 StatsMgr::generateName("pool", pool->getID(), "declined-addresses")),
4380 static_cast<int64_t>(1));
4381 }
4382 }
4383
4384 // Global declined addresses counter.
4385 StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
4386
4387 LOG_INFO(lease6_logger, DHCP6_DECLINE_LEASE).arg(decline->getLabel())
4388 .arg(lease->addr_.toText()).arg(lease->valid_lft_);
4389
4390 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_Success,
4391 "Lease declined. Hopefully the next one will be better."));
4392
4393 return (true);
4394}
4395
4396Pkt6Ptr
4398
4399 Pkt6Ptr inf_request = ctx.query_;
4400 conditionallySetReservedClientClasses(inf_request, ctx);
4401 // Evaluate additional classes.
4402 evaluateAdditionalClasses(inf_request, ctx);
4403
4405 .arg(inf_request->getLabel())
4406 .arg(inf_request->getName())
4407 .arg(inf_request->getClasses().toText());
4408
4409 // Create a Reply packet, with the same trans-id as the client's.
4410 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, inf_request->getTransid()));
4411
4412 // Copy client options (client-id, also relay information if present)
4413 copyClientOptions(inf_request, reply);
4414
4415 // Build the configured option list for append methods
4416 CfgOptionList co_list;
4417 buildCfgOptionList(inf_request, ctx, co_list);
4418
4419 // Append default options, i.e. options that the server is supposed
4420 // to put in all messages it sends (server-id for now, but possibly other
4421 // options once we start supporting authentication)
4422 appendDefaultOptions(inf_request, reply, co_list);
4423
4424 // Try to assign options that were requested by the client.
4425 appendRequestedOptions(inf_request, reply, co_list);
4426
4427 // Try to assign vendor options that were requested by the client.
4428 appendRequestedVendorOptions(inf_request, reply, ctx, co_list);
4429
4430 return (reply);
4431}
4432
4433void
4435
4436 // flags are in transid
4437 // uint32_t flags = dhcp4_query->getTransid();
4438 // do nothing with DHCPV4_QUERY_FLAGS_UNICAST
4439
4440 // Get the DHCPv4 message option
4441 OptionPtr dhcp4_msg = dhcp4_query->getOption(D6O_DHCPV4_MSG);
4442 if (dhcp4_msg) {
4443 try {
4444 // Forward the whole message to the DHCPv4 server via IPC
4445 Dhcp6to4Ipc::instance().send(dhcp4_query);
4446 } catch (...) {
4447 // Assume the error was already logged
4448 return;
4449 }
4450 }
4451
4452 // This method does not return anything as we always sent back
4453 // the response via Dhcp6To4Ipc.
4454}
4455
4456void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt) {
4457 OptionVendorClassPtr vclass;
4458 for (auto const& opt : pkt->getOptions(D6O_VENDOR_CLASS)) {
4459 vclass = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
4460 if (!vclass || vclass->getTuplesNum() == 0) {
4461 continue;
4462 }
4463
4464 if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) {
4466
4467 } else if (vclass->hasTuple(DOCSIS3_CLASS_EROUTER)) {
4469
4470 } else {
4471 pkt->addClass(VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText());
4472 }
4473 }
4474}
4475
4477 // All packets belong to ALL.
4478 pkt->addClass("ALL");
4479
4480 // First: built-in vendor class processing
4481 classifyByVendor(pkt);
4482
4483 // Run match expressions on classes not depending on KNOWN/UNKNOWN.
4484 evaluateClasses(pkt, false);
4485}
4486
4487void Dhcpv6Srv::evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known) {
4488 // Note getClientClassDictionary() cannot be null
4489 const ClientClassDictionaryPtr& dict =
4490 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4491 const ClientClassDefListPtr& defs_ptr = dict->getClasses();
4492 for (auto const& it : *defs_ptr) {
4493 // Note second cannot be null
4494 const ExpressionPtr& expr_ptr = it->getMatchExpr();
4495 // Nothing to do without an expression to evaluate
4496 if (!expr_ptr) {
4497 continue;
4498 }
4499 // Not the right time if only when required
4500 if (it->getAdditional()) {
4501 continue;
4502 }
4503 // Not the right pass.
4504 if (it->getDependOnKnown() != depend_on_known) {
4505 continue;
4506 }
4507 it->test(pkt, expr_ptr);
4508 }
4509}
4510
4511void
4513 const ClientClassDictionaryPtr& dict =
4514 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4515 const ClientClassDefListPtr& defs_ptr = dict->getClasses();
4516 for (auto const& def : *defs_ptr) {
4517 // Only remove evaluated classes. Other classes can be
4518 // assigned via hooks libraries and we should not remove
4519 // them because there is no way they can be added back.
4520 if (def->getMatchExpr()) {
4521 pkt->classes_.erase(def->getName());
4522 }
4523 }
4524}
4525
4526void
4528 const AllocEngine::ClientContext6& ctx) {
4529 if (ctx.currentHost() && pkt) {
4530 const ClientClasses& classes = ctx.currentHost()->getClientClasses6();
4531 for (auto const& cclass : classes) {
4532 pkt->addClass(cclass);
4533 }
4534 }
4535}
4536
4537void
4539 const AllocEngine::ClientContext6& ctx) {
4540 if (ctx.subnet_) {
4541 SharedNetwork6Ptr shared_network;
4542 ctx.subnet_->getSharedNetwork(shared_network);
4543 if (shared_network) {
4544 ConstHostPtr host = ctx.currentHost();
4545 if (host && (host->getIPv6SubnetID() != SUBNET_ID_GLOBAL)) {
4546 setReservedClientClasses(pkt, ctx);
4547 }
4548 }
4549 }
4550}
4551
4552void
4554 // Get additional classes to evaluate added elsewhere, posssibly by hooks.
4555 ClientClasses classes = pkt->getAdditionalClasses();
4556 ConstSubnet6Ptr subnet = ctx.subnet_;
4557
4558 if (subnet) {
4559 // host reservation???
4560
4561 // Begin by pools
4562 for (auto const& resource : ctx.allocated_resources_) {
4563 PoolPtr pool =
4564 ctx.subnet_->getPool(resource.getPrefixLength() == 128 ?
4566 resource.getAddress(),
4567 false);
4568 if (pool) {
4569 const ClientClasses& pool_to_add = pool->getAdditionalClasses();
4570 for (auto const& cclass : pool_to_add) {
4571 classes.insert(cclass);
4572 }
4573 }
4574 }
4575
4576 // Followed by the subnet
4577 const ClientClasses& to_add = subnet->getAdditionalClasses();
4578 for (auto const& cclass : to_add) {
4579 classes.insert(cclass);
4580 }
4581
4582 // And finish by the shared-network
4583 SharedNetwork6Ptr network;
4584 subnet->getSharedNetwork(network);
4585 if (network) {
4586 const ClientClasses& net_to_add = network->getAdditionalClasses();
4587 for (auto const& cclass : net_to_add) {
4588 classes.insert(cclass);
4589 }
4590 }
4591 }
4592
4593 // Run match expressions
4594 // Note getClientClassDictionary() cannot be null
4595 const ClientClassDictionaryPtr& dict =
4596 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4597 for (auto const& cclass : classes) {
4598 const ClientClassDefPtr class_def = dict->findClass(cclass);
4599 if (!class_def) {
4602 .arg(cclass);
4603 // Ignore it as it can't have an attached action
4604 continue;
4605 }
4606 const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
4607 // Add a class without an expression to evaluate
4608 if (!expr_ptr) {
4611 .arg(cclass);
4612 pkt->addClass(cclass);
4613 continue;
4614 }
4615 // Evaluate the expression which can return false (no match),
4616 // true (match) or raise an exception (error)
4617 try {
4618 bool status = evaluateBool(*expr_ptr, *pkt);
4620 .arg(pkt->getLabel())
4621 .arg(cclass)
4622 .arg(status ? "true" : "false");
4623 if (status) {
4624 // Matching: add the class
4625 pkt->addClass(cclass);
4626 }
4627 } catch (const Exception& ex) {
4629 .arg(pkt->getLabel())
4630 .arg(cclass)
4631 .arg(ex.what());
4632 }
4633 }
4634}
4635
4636void
4637Dhcpv6Srv::updateReservedFqdn(AllocEngine::ClientContext6& ctx,
4638 const Pkt6Ptr& answer) {
4639 if (!answer) {
4640 isc_throw(isc::Unexpected, "an instance of the object encapsulating"
4641 " a message must not be NULL when updating reserved FQDN");
4642 }
4643
4644 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>
4645 (answer->getOption(D6O_CLIENT_FQDN));
4646
4647 // If Client FQDN option is not included, there is nothing to do.
4648 if (!fqdn) {
4649 return;
4650 }
4651
4652 std::string name = fqdn->getDomainName();
4653
4654 // If there is a host reservation for this client we have to check whether
4655 // this reservation has the same hostname as the hostname currently
4656 // present in the FQDN option. If not, it indicates that the allocation
4657 // engine picked a different subnet (from within a shared network) for
4658 // reservations and we have to send this new value to the client.
4659 if (ctx.currentHost() &&
4660 !ctx.currentHost()->getHostname().empty()) {
4661 std::string new_name = CfgMgr::instance().getD2ClientMgr().
4662 qualifyName(ctx.currentHost()->getHostname(), *ctx.getDdnsParams(), true);
4663
4664 if (new_name != name) {
4665 fqdn->setDomainName(new_name, Option6ClientFqdn::FULL);
4666
4667 // Replace previous instance of Client FQDN option.
4668 answer->delOption(D6O_CLIENT_FQDN);
4669 answer->addOption(fqdn);
4670 }
4671 }
4672}
4673
4674void
4675Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer,
4677 if (!answer) {
4678 isc_throw(isc::Unexpected, "an instance of the object encapsulating"
4679 " a message must not be NULL when generating FQDN");
4680 }
4681
4684
4685 // It is likely that client hasn't included the FQDN option. In such case,
4686 // FQDN option will be NULL. Also, there is nothing to do if the option
4687 // is present and conveys the non-empty FQDN.
4688 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
4689 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
4690 if (!fqdn || !fqdn->getDomainName().empty()) {
4691 return;
4692 }
4693
4694 // Get the first IA_NA acquired for the client.
4695 OptionPtr ia = answer->getOption(D6O_IA_NA);
4696 if (!ia) {
4697 return;
4698 }
4699
4700 // If it has any IAAddr with a valid lifetime > 0, use it to
4701 // generate unique FQDN.
4702 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
4703 Option6IAAddr>(ia->getOption(D6O_IAADDR));
4704 if (!iaaddr || iaaddr->getValid() == 0) {
4705 return;
4706 }
4707
4708 // Get the IPv6 address acquired by the client.
4709 IOAddress addr = iaaddr->getAddress();
4710 std::string generated_name =
4711 CfgMgr::instance().getD2ClientMgr().generateFqdn(addr, *ctx.getDdnsParams());
4712
4714 .arg(answer->getLabel())
4715 .arg(generated_name);
4716
4717 try {
4718 // The lease has been acquired but the FQDN for this lease hasn't
4719 // been updated in the lease database. We now have new FQDN
4720 // generated, so the lease database has to be updated here.
4721 // However, never update lease database for Advertise, just send
4722 // our notion of client's FQDN in the Client FQDN option.
4723 if (answer->getType() != DHCPV6_ADVERTISE) {
4724 Lease6Ptr lease;
4725 for (auto const& l : ctx.new_leases_) {
4726 if ((l->type_ == Lease::TYPE_NA) && (l->addr_ == addr)) {
4727 lease = l;
4728 break;
4729 }
4730 }
4731 if (lease) {
4732 lease->hostname_ = generated_name;
4733 lease->reuseable_valid_lft_ = 0;
4734 LeaseMgrFactory::instance().updateLease6(lease);
4735
4736 } else {
4737 isc_throw(isc::Unexpected, "there is no lease in the database "
4738 " for address " << addr << ", so as it is impossible"
4739 " to update FQDN data. This is a programmatic error"
4740 " as the given address is now being handed to the"
4741 " client");
4742 }
4743 }
4744 // Set the generated FQDN in the Client FQDN option.
4745 fqdn->setDomainName(generated_name, Option6ClientFqdn::FULL);
4746
4747 answer->delOption(D6O_CLIENT_FQDN);
4748 answer->addOption(fqdn);
4749 ctx.hostname_ = generated_name;
4750 } catch (const Exception& ex) {
4752 .arg(answer->getLabel())
4753 .arg(addr.toText())
4754 .arg(ex.what());
4755 }
4756}
4757
4758void
4760 D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
4761 if (d2_mgr.ddnsEnabled()) {
4762 // Updates are enabled, so lets start the sender, passing in
4763 // our error handler.
4764 // This may throw so wherever this is called needs to ready.
4765 d2_mgr.startSender(std::bind(&Dhcpv6Srv::d2ClientErrorHandler,
4766 this, ph::_1, ph::_2));
4767 }
4768}
4769
4770void
4772 D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
4773 if (d2_mgr.ddnsEnabled()) {
4774 // Updates are enabled, so lets stop the sender
4775 d2_mgr.stop();
4776 d2_mgr.stopSender();
4777 }
4778}
4779
4780void
4785 arg(result).arg((ncr ? ncr->toText() : " NULL "));
4786 // We cannot communicate with kea-dhcp-ddns, suspend further updates.
4789 CfgMgr::instance().getD2ClientMgr().suspendUpdates();
4790}
4791
4792std::string
4794 std::stringstream tmp;
4795
4796 tmp << VERSION;
4797 if (extended) {
4798 tmp << " (" << EXTENDED_VERSION << ")" << endl;
4799 tmp << "premium: " << PREMIUM_EXTENDED_VERSION << endl;
4800 tmp << "linked with:" << endl;
4801 tmp << "- " << Logger::getVersion() << endl;
4802 tmp << "- " << CryptoLink::getVersion();
4804 if (info.size()) {
4805 tmp << endl << "lease backends:";
4806 for (auto const& version : info) {
4807 tmp << endl << "- " << version;
4808 }
4809 }
4811 if (info.size()) {
4812 tmp << endl << "host backends:";
4813 for (auto const& version : info) {
4814 tmp << endl << "- " << version;
4815 }
4816 }
4817 // @todo: more details about database runtime
4818 }
4819
4820 return (tmp.str());
4821}
4822
4823void Dhcpv6Srv::processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp) {
4824
4825 if (query->relay_info_.empty()) {
4826 // RSOO is inserted by relay agents, nothing to do here if it's
4827 // a direct message.
4828 return;
4829 }
4830
4831 // Get RSOO configuration.
4832 ConstCfgRSOOPtr cfg_rsoo = CfgMgr::instance().getCurrentCfg()->getCfgRSOO();
4833
4834 // Let's get over all relays (encapsulation levels). We need to do
4835 // it in the same order as the client packet traversed the relays.
4836 for (int i = query->relay_info_.size(); i > 0 ; --i) {
4837 OptionPtr rsoo_container = query->getRelayOption(D6O_RSOO, i - 1);
4838 if (rsoo_container) {
4839 // There are RSOO options. Let's get through them one by one
4840 // and if it's RSOO-enabled and there's no such option provided yet,
4841 // copy it to the server's response
4842 const OptionCollection& rsoo = rsoo_container->getOptions();
4843 for (auto const& opt : rsoo) {
4844
4845 // Echo option if it is RSOO enabled option and there is no such
4846 // option added yet.
4847 if (cfg_rsoo->enabled(opt.second->getType()) &&
4848 !rsp->getOption(opt.second->getType())) {
4849 rsp->addOption(opt.second);
4850 }
4851 }
4852 }
4853 }
4854}
4855
4857
4858 if (query->relay_info_.empty()) {
4859 // No relay agent
4860 return (0);
4861 }
4862
4863 // Did the last relay agent add a relay-source-port?
4864 if (query->getRelayOption(D6O_RELAY_SOURCE_PORT, 0)) {
4865 // RFC 8357 section 5.2
4866 return (query->getRemotePort());
4867 }
4868
4869 return (0);
4870}
4871
4872void Dhcpv6Srv::processStatsReceived(const Pkt6Ptr& query) {
4873 // Note that we're not bumping pkt6-received statistic as it was
4874 // increased early in the packet reception code.
4875
4876 string stat_name = "pkt6-unknown-received";
4877 switch (query->getType()) {
4878 case DHCPV6_SOLICIT:
4879 stat_name = "pkt6-solicit-received";
4880 break;
4881 case DHCPV6_ADVERTISE:
4882 // Should not happen, but let's keep a counter for it
4883 stat_name = "pkt6-advertise-received";
4884 break;
4885 case DHCPV6_REQUEST:
4886 stat_name = "pkt6-request-received";
4887 break;
4888 case DHCPV6_CONFIRM:
4889 stat_name = "pkt6-confirm-received";
4890 break;
4891 case DHCPV6_RENEW:
4892 stat_name = "pkt6-renew-received";
4893 break;
4894 case DHCPV6_REBIND:
4895 stat_name = "pkt6-rebind-received";
4896 break;
4897 case DHCPV6_REPLY:
4898 // Should not happen, but let's keep a counter for it
4899 stat_name = "pkt6-reply-received";
4900 break;
4901 case DHCPV6_RELEASE:
4902 stat_name = "pkt6-release-received";
4903 break;
4904 case DHCPV6_DECLINE:
4905 stat_name = "pkt6-decline-received";
4906 break;
4907 case DHCPV6_RECONFIGURE:
4908 stat_name = "pkt6-reconfigure-received";
4909 break;
4911 stat_name = "pkt6-infrequest-received";
4912 break;
4914 stat_name = "pkt6-dhcpv4-query-received";
4915 break;
4917 // Should not happen, but let's keep a counter for it
4918 stat_name = "pkt6-dhcpv4-response-received";
4919 break;
4920 default:
4921 ; // do nothing
4922 }
4923
4924 StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
4925}
4926
4928 // Increase generic counter for sent packets.
4929 StatsMgr::instance().addValue("pkt6-sent", static_cast<int64_t>(1));
4930
4931 // Increase packet type specific counter for packets sent.
4932 string stat_name;
4933 switch (response->getType()) {
4934 case DHCPV6_ADVERTISE:
4935 stat_name = "pkt6-advertise-sent";
4936 break;
4937 case DHCPV6_REPLY:
4938 stat_name = "pkt6-reply-sent";
4939 break;
4941 stat_name = "pkt6-dhcpv4-response-sent";
4942 break;
4943 default:
4944 // That should never happen
4945 return;
4946 }
4947
4948 StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
4949}
4950
4952 return (Hooks.hook_index_buffer6_send_);
4953}
4954
4955bool
4956Dhcpv6Srv::requestedInORO(const Pkt6Ptr& query, const uint16_t code) const {
4958 boost::dynamic_pointer_cast<OptionUint16Array>(query->getOption(D6O_ORO));
4959
4960 if (oro) {
4961 const std::vector<uint16_t>& codes = oro->getValues();
4962 return (std::find(codes.begin(), codes.end(), code) != codes.end());
4963 }
4964
4965 return (false);
4966}
4967
4968tuple<bool, uint32_t>
4969Dhcpv6Srv::parkingLimitExceeded(string const& hook_label) {
4970 // Get the parking limit. Parsing should ensure the value is present.
4971 uint32_t parked_packet_limit(0);
4972 ConstElementPtr const& ppl(
4973 CfgMgr::instance().getCurrentCfg()->getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT));
4974 if (ppl) {
4975 parked_packet_limit = ppl->intValue();
4976 }
4977
4978 if (parked_packet_limit) {
4979 ParkingLotPtr const& parking_lot(
4980 ServerHooks::getServerHooks().getParkingLotPtr(hook_label));
4981
4982 if (parking_lot && parked_packet_limit <= parking_lot->size()) {
4983 return make_tuple(true, parked_packet_limit);
4984 }
4985 }
4986 return make_tuple(false, parked_packet_limit);
4987}
4988
4989
4991 // Dump all of our current packets, anything that is mid-stream
4993}
4994
4996#ifdef FUZZING
4997 char const* const rotate(getenv("KEA_DHCP6_FUZZING_ROTATE_PORT"));
4998 if (rotate) {
4999 InterprocessSyncFile file("kea-dhcp6-fuzzing-rotate-port");
5000 InterprocessSyncLocker locker(file);
5001 while (!locker.lock()) {
5002 this_thread::sleep_for(1s);
5003 }
5004 fstream port_file;
5005 port_file.open("/tmp/port6.txt", ios::in);
5006 string line;
5007 int port;
5008 getline(port_file, line);
5009 port_file.close();
5010 if (line.empty()) {
5011 port = 2000;
5012 } else {
5013 port = stoi(line);
5014 if (port < 3000) {
5015 ++port;
5016 } else {
5017 port = 2000;
5018 }
5019 }
5020 port_file.open("/tmp/port6.txt", ios::out | ios::trunc);
5021 port_file << to_string(port) << endl;
5022 port_file.close();
5023 locker.unlock();
5024 return port;
5025 }
5026#endif // FUZZING
5027 return server_port_;
5028}
5029
5031void
5032Dhcpv6Srv::setTeeTimes(uint32_t preferred_lft,
5033 const ConstSubnet6Ptr& subnet,
5034 Option6IAPtr& resp) {
5035 // Default T2 time to zero.
5036 uint32_t t2_time = 0;
5037
5038 // If T2 is explicitly configured we'll use that value.
5039 if (!subnet->getT2().unspecified()) {
5040 t2_time = subnet->getT2();
5041 } else if (subnet->getCalculateTeeTimes()) {
5042 // Calculating tee times is enabled, so calculate it.
5043 t2_time = static_cast<uint32_t>(round(subnet->getT2Percent() * preferred_lft));
5044 }
5045
5046 // We allow T2 to be any value.
5047 resp->setT2(t2_time);
5048
5049 // Default T1 time to zero.
5050 uint32_t t1_time = 0;
5051
5052 // If T1 is explicitly configured we'll use try value.
5053 if (!subnet->getT1().unspecified()) {
5054 t1_time = subnet->getT1();
5055 } else if (subnet->getCalculateTeeTimes()) {
5056 // Calculating tee times is enabled, so calculate it.
5057 t1_time = static_cast<uint32_t>(round(subnet->getT1Percent() * preferred_lft));
5058 }
5059
5060 // T1 is sane if it is less than or equal to T2.
5061 if (t1_time < t2_time) {
5062 resp->setT1(t1_time);
5063 } else {
5064 // It's either explicitly 0 or insane, leave it to the client
5065 resp->setT1(0);
5066 }
5067}
5068
5069void
5072 const ConstSubnet6Ptr orig_subnet) {
5073 // If the subnet's are the same there's nothing to do.
5074 if ((!ctx.subnet_) || (!orig_subnet) || (orig_subnet->getID() == ctx.subnet_->getID())) {
5075 return;
5076 }
5077
5078 // We get the network for logging only. It should always be set as this a dynamic
5079 // change should only happen within shared-networks. Not having one might not be
5080 // an error if a hook changed the subnet?
5081 SharedNetwork6Ptr network;
5082 orig_subnet->getSharedNetwork(network);
5084 .arg(question->getLabel())
5085 .arg(orig_subnet->toText())
5086 .arg(ctx.subnet_->toText())
5087 .arg(network ? network->getName() : "<no network?>");
5088
5089 // The DDNS parameters may have changed with the subnet, so we need to
5090 // recalculate the client name.
5091
5092 // Save the current DNS values on the context.
5093 std::string prev_hostname = ctx.hostname_;
5094 bool prev_fwd_dns_update = ctx.fwd_dns_update_;
5095 bool prev_rev_dns_update = ctx.rev_dns_update_;
5096
5097 // Remove the current FQDN option from the answer.
5098 answer->delOption(D6O_CLIENT_FQDN);
5099
5100 // Recalculate the client's FQDN. This will replace the FQDN option and
5101 // update the context values for hostname_ and DNS directions.
5102 processClientFqdn(question, answer, ctx);
5103
5104 // If this is a real allocation and the DNS values changed we need to
5105 // update the leases.
5106 if (!ctx.fake_allocation_ &&
5107 ((prev_hostname != ctx.hostname_) ||
5108 (prev_fwd_dns_update != ctx.fwd_dns_update_) ||
5109 (prev_rev_dns_update != ctx.rev_dns_update_))) {
5110 for (auto const& l : ctx.new_leases_) {
5111 l->hostname_ = ctx.hostname_;
5112 l->fqdn_fwd_ = ctx.fwd_dns_update_;
5113 l->fqdn_rev_ = ctx.rev_dns_update_;
5114 l->reuseable_valid_lft_ = 0;
5115 LeaseMgrFactory::instance().updateLease6(l);
5116 }
5117 }
5118}
5119
5120std::list<std::list<std::string>> Dhcpv6Srv::jsonPathsToRedact() const{
5121 static std::list<std::list<std::string>> const list({
5122 {"config-control", "config-databases", "[]"},
5123 {"hooks-libraries", "[]", "parameters", "*"},
5124 {"hosts-database"},
5125 {"hosts-databases", "[]"},
5126 {"lease-database"},
5127 });
5128 return list;
5129}
5130
5131} // namespace dhcp
5132} // namespace isc
int version()
returns Kea hooks version.
CtrlAgentHooks Hooks
Defines elements for storing the names of client classes.
This is a base class for exceptions thrown from the DNS library module.
A generic exception that is thrown when an unexpected error condition occurs.
static HttpCommandMgr & instance()
HttpCommandMgr is a singleton class.
Defines a single hint.
DHCPv4 and DHCPv6 allocation engine.
std::vector< Resource > HintContainer
Container for client's hints.
Implementation of the mechanisms to control the use of the Configuration Backends by the DHCPv6 serve...
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:25
static SubnetSelector initSelector(const Pkt6Ptr &query)
Build selector from a client's message.
Container for storing client class names.
Definition classify.h:108
void insert(const ClientClass &class_name)
Insert an element.
Definition classify.h:155
Client race avoidance RAII handler.
D2ClientMgr isolates Kea from the details of being a D2 client.
This exception is thrown when DHCP server hits the error which should result in discarding the messag...
Definition dhcp6_srv.h:48
Factory for generating DUIDs (DHCP Unique Identifiers).
DuidPtr get()
Returns current DUID.
Holds DUID (DHCPv6 Unique Identifier)
Definition duid.h:142
static constexpr size_t MIN_DUID_LEN
minimum duid size
Definition duid.h:149
static constexpr size_t MAX_DUID_LEN
maximum duid size
Definition duid.h:155
static Dhcp6to4Ipc & instance()
Returns pointer to the sole instance of Dhcp6to4Ipc.
void shutdown() override
Instructs the server to shut down.
Definition dhcp6_srv.cc:355
RequirementLevel
defines if certain option may, must or must not appear
Definition dhcp6_srv.h:74
OptionPtr getServerID()
Returns server-identifier option.
Definition dhcp6_srv.h:135
Pkt6Ptr processPacket(Pkt6Ptr query)
Process a single incoming DHCPv6 packet.
Definition dhcp6_srv.cc:804
Pkt6Ptr processLocalizedQuery6(AllocEngine::ClientContext6 &ctx)
Process a localized incoming DHCPv6 query.
void processPacketAndSendResponseNoThrow(Pkt6Ptr query)
Process a single incoming DHCPv6 packet and sends the response.
Definition dhcp6_srv.cc:779
OptionPtr extendIA_PD(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Extends lifetime of the prefix.
void setReservedClientClasses(const Pkt6Ptr &pkt, const AllocEngine::ClientContext6 &ctx)
Assigns classes retrieved from host reservation database.
Pkt6Ptr processDecline(AllocEngine::ClientContext6 &ctx)
Process incoming Decline message.
void evaluateClasses(const Pkt6Ptr &pkt, bool depend_on_known)
Evaluate classes.
Pkt6Ptr processRenew(AllocEngine::ClientContext6 &ctx)
Processes incoming Renew message.
static void processStatsSent(const Pkt6Ptr &response)
Updates statistics for transmitted packets.
void evaluateAdditionalClasses(const Pkt6Ptr &pkt, AllocEngine::ClientContext6 &ctx)
Evaluates classes in the additional classes lists.
void processLocalizedQuery6AndSendResponse(Pkt6Ptr query, AllocEngine::ClientContext6 &ctx)
Process a localized incoming DHCPv6 query.
int run()
Main server processing loop.
Definition dhcp6_srv.cc:643
void setPacketStatisticsDefaults()
This function sets statistics related to DHCPv6 packets processing to their initial values.
Definition dhcp6_srv.cc:301
bool sanityCheck(const Pkt6Ptr &pkt)
Verifies if specified packet meets RFC requirements.
static uint16_t checkRelaySourcePort(const Pkt6Ptr &query)
Used for DHCPv4-over-DHCPv6 too.
void assignLeases(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Assigns leases.
void stopD2()
Stops DHCP_DDNS client IO if DDNS updates are enabled.
void copyClientOptions(const Pkt6Ptr &question, Pkt6Ptr &answer)
Copies required options from client message to server answer.
boost::shared_ptr< AllocEngine > alloc_engine_
Allocation Engine.
Definition dhcp6_srv.h:1238
virtual void sendPacket(const Pkt6Ptr &pkt)
dummy wrapper around IfaceMgr::send()
Definition dhcp6_srv.cc:364
bool testServerID(const Pkt6Ptr &pkt)
Compare received server id with our server id.
Definition dhcp6_srv.cc:369
virtual void d2ClientErrorHandler(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Implements the error handler for DHCP_DDNS IO errors.
OptionPtr declineIA(const Pkt6Ptr &decline, const DuidPtr &duid, int &general_status, boost::shared_ptr< Option6IA > ia, Lease6Collection &new_leases)
Declines leases in a single IA_NA option.
uint16_t getServerPort() const
Get UDP port on which server should listen.
void runOne()
Main server processing step.
Definition dhcp6_srv.cc:701
virtual Pkt6Ptr receivePacket(int timeout)
dummy wrapper around IfaceMgr::receive6
Definition dhcp6_srv.cc:360
void processPacketBufferSend(hooks::CalloutHandlePtr &callout_handle, Pkt6Ptr &rsp)
Executes buffer6_send callout and sends the response.
OptionPtr releaseIA_NA(const DuidPtr &duid, const Pkt6Ptr &query, int &general_status, boost::shared_ptr< Option6IA > ia, Lease6Ptr &old_lease)
Releases specific IA_NA option.
void buildCfgOptionList(const Pkt6Ptr &question, AllocEngine::ClientContext6 &ctx, CfgOptionList &co_list)
Build the configured option list.
void appendDefaultOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, const CfgOptionList &co_list)
Appends default options to server's answer.
OptionPtr assignIA_NA(const isc::dhcp::Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Processes IA_NA option (and assigns addresses if necessary).
static const std::string VENDOR_CLASS_PREFIX
this is a prefix added to the content of vendor-class option
Definition dhcp6_srv.h:972
OptionPtr serverid_
Server DUID (to be sent in server-identifier option)
Definition dhcp6_srv.h:1218
void setTeeTimes(uint32_t preferred_lft, const ConstSubnet6Ptr &subnet, Option6IAPtr &resp)
Sets the T1 and T2 timers in the outbound IA.
void conditionallySetReservedClientClasses(const Pkt6Ptr &pkt, const AllocEngine::ClientContext6 &ctx)
Assigns classes retrieved from host reservation database if they haven't been yet set.
void checkDynamicSubnetChange(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, const ConstSubnet6Ptr orig_subnet)
Iterates over new leases, update stale DNS entries.
void processPacketAndSendResponse(Pkt6Ptr query)
Process a single incoming DHCPv6 packet and sends the response.
Definition dhcp6_srv.cc:793
OptionPtr releaseIA_PD(const DuidPtr &duid, const Pkt6Ptr &query, int &general_status, boost::shared_ptr< Option6IA > ia, Lease6Ptr &old_lease)
Releases specific IA_PD option.
void processDhcp4Query(const Pkt6Ptr &dhcp4_query)
Processes incoming DHCPv4-query message.
Pkt6Ptr processRebind(AllocEngine::ClientContext6 &ctx)
Processes incoming Rebind message.
bool earlyGHRLookup(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx)
Initialize client context and perform early global reservations lookup.
Definition dhcp6_srv.cc:487
void initContext0(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx)
Initialize client context (first part).
Definition dhcp6_srv.cc:474
virtual ~Dhcpv6Srv()
Destructor. Used during DHCPv6 service shutdown.
Definition dhcp6_srv.cc:311
void initContext(AllocEngine::ClientContext6 &ctx, bool &drop)
Initializes client context for specified packet.
Definition dhcp6_srv.cc:551
Pkt6Ptr processRequest(AllocEngine::ClientContext6 &ctx)
Processes incoming Request and returns Reply response.
void sendResponseNoThrow(hooks::CalloutHandlePtr &callout_handle, Pkt6Ptr query, Pkt6Ptr &rsp, ConstSubnet6Ptr &subnet)
Process an unparked DHCPv6 packet and sends the response.
NetworkStatePtr network_state_
Holds information about disabled DHCP service and/or disabled subnet/network scopes.
Definition dhcp6_srv.h:1246
std::list< std::list< std::string > > jsonPathsToRedact() const final override
Return a list of all paths that contain passwords or secrets for kea-dhcp6.
OptionPtr assignIA_PD(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, boost::shared_ptr< Option6IA > ia)
Processes IA_PD option (and assigns prefixes if necessary).
bool testUnicast(const Pkt6Ptr &pkt) const
Check if the message can be sent to unicast.
Definition dhcp6_srv.cc:391
Pkt6Ptr processRelease(AllocEngine::ClientContext6 &ctx)
Process incoming Release message.
void processClientFqdn(const Pkt6Ptr &question, const Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Processes Client FQDN Option.
void setStatusCode(boost::shared_ptr< Option6IA > &container, const OptionPtr &status)
A simple utility method that sets the status code.
static int getHookIndexBuffer6Send()
Returns the index of the buffer6_send hook.
void classifyPacket(const Pkt6Ptr &pkt)
Assigns incoming packet to zero or more classes.
static HWAddrPtr getMAC(const Pkt6Ptr &pkt)
Attempts to get a MAC/hardware address using configured sources.
Dhcpv6Srv(uint16_t server_port=DHCP6_SERVER_PORT, uint16_t client_port=0)
Default constructor.
Definition dhcp6_srv.cc:256
bool declineLeases(const Pkt6Ptr &decline, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to decline all leases in specified Decline message.
void releaseLeases(const Pkt6Ptr &release, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to release received addresses.
void extendLeases(const Pkt6Ptr &query, Pkt6Ptr &reply, AllocEngine::ClientContext6 &ctx)
Attempts to extend the lifetime of IAs.
void processRSOO(const Pkt6Ptr &query, const Pkt6Ptr &rsp)
Processes Relay-supplied options, if present.
static std::string getVersion(bool extended)
returns Kea version on stdout and exit.
OptionPtr extendIA_NA(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx, Option6IAPtr ia)
Extends lifetime of the specific IA_NA option.
Pkt6Ptr processConfirm(AllocEngine::ClientContext6 &ctx)
Processes incoming Confirm message and returns Reply.
void sanityCheckDUID(const OptionPtr &opt, const std::string &opt_name)
verifies if received DUID option (client-id or server-id) is sane
static void setHostIdentifiers(AllocEngine::ClientContext6 &ctx)
Set host identifiers within a context.
Definition dhcp6_srv.cc:412
void processPacketPktSend(hooks::CalloutHandlePtr &callout_handle, Pkt6Ptr &query, Pkt6Ptr &rsp, ConstSubnet6Ptr &subnet)
Executes pkt6_send callout.
Pkt6Ptr processDhcp6Query(Pkt6Ptr query)
Process a single incoming DHCPv6 query.
void processDhcp6QueryAndSendResponse(Pkt6Ptr query)
Process a single incoming DHCPv6 query.
asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service used by the server.
Definition dhcp6_srv.h:110
void appendRequestedOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, const CfgOptionList &co_list)
Appends requested options to server's answer.
uint16_t client_port_
UDP port number to which server sends all responses.
Definition dhcp6_srv.h:1187
isc::dhcp::ConstSubnet6Ptr selectSubnet(const Pkt6Ptr &question, bool &drop)
Selects a subnet for a given client's packet.
volatile bool shutdown_
Indicates if shutdown is in progress.
Definition dhcp6_srv.h:1222
Pkt6Ptr processSolicit(AllocEngine::ClientContext6 &ctx)
Processes incoming Solicit and returns response.
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
static std::string duidToString(const OptionPtr &opt)
converts DUID to text Converts content of DUID option to a text representation, e....
OptionPtr getPDExclude(const AllocEngine::ClientContext6 &ctx, const Lease6Ptr &lease)
Return the PD exclude option to include.
static void removeDependentEvaluatedClasses(const Pkt6Ptr &pkt)
Removed evaluated client classes.
void createNameChangeRequests(const Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Creates a number of isc::dhcp_ddns::NameChangeRequest objects based on the DHCPv6 Client FQDN Option.
Pkt6Ptr processInfRequest(AllocEngine::ClientContext6 &ctx)
Processes incoming Information-request message.
uint16_t server_port_
UDP port number on which server listens.
Definition dhcp6_srv.h:1184
void appendRequestedVendorOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, const CfgOptionList &co_list)
Appends requested vendor options to server's answer.
bool declineLease(const Pkt6Ptr &decline, const Lease6Ptr lease, boost::shared_ptr< Option6IA > ia_rsp)
Declines specific IPv6 lease.
void discardPackets()
Discards parked packets Clears the packet parking lots of all packets.
static std::list< std::string > getDBVersions()
Return extended version info for registered backends.
static void create()
Creates new instance of the HostMgr.
Definition host_mgr.cc:52
IdentifierType
Type of the host identifier.
Definition host.h:337
@ IDENT_FLEX
Flexible host identifier.
Definition host.h:342
std::string getIdentifierAsText() const
Returns host identifier in a textual form.
Definition host.cc:312
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
static TrackingLeaseMgr & instance()
Return current lease manager.
static std::list< std::string > getDBVersions()
Return extended version info for registered backends.
static void destroy()
Destroy lease manager.
static void splitNtpServerOptions6(isc::dhcp::OptionCollection &options)
Split NTP server option to one suboption per instance.
Controls the DHCP service enabling status.
Represents DHCPv6 Client FQDN Option (code 39).
static const uint8_t FLAG_S
S bit.
static const uint8_t FLAG_N
N bit.
isc::asiolink::IOAddress getAddress() const
Returns address contained within this option.
Class that represents IAPREFIX option in DHCPv6.
This class represents Status Code option (13) from RFC 8415.
Option descriptor.
Definition cfg_option.h:48
This class represents vendor-specific information option.
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition option.cc:199
Represents a DHCPv6 packet.
Definition pkt6.h:44
An exception that is thrown if a DHCPv6 protocol violation occurs while processing a message (e....
Definition utils.h:17
Exception thrown when a call to select is interrupted by a signal.
Definition iface_mgr.h:55
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition option.h:52
Container class for handling the DHCID value within a NameChangeRequest.
Definition ncr_msg.h:113
Represents a DHCP-DDNS client request.
Definition ncr_msg.h:254
Result
Defines the outcome of an asynchronous NCR send.
Definition ncr_io.h:478
@ NEXT_STEP_PARK
park the packet
@ NEXT_STEP_CONTINUE
continue normally
@ NEXT_STEP_DROP
drop the packet
@ NEXT_STEP_SKIP
skip the next processing step
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static std::vector< std::string > getLibraryNames()
Return list of loaded libraries.
static bool unloadLibraries()
Unload libraries.
static void park(const std::string &hook_name, T parked_object, std::function< void()> unpark_callback)
Park an object (packet).
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
static void prepareUnloadLibraries()
Prepare the unloading of libraries.
static bool drop(const std::string &hook_name, T parked_object)
Removes parked object without calling a callback.
static void clearParkingLots()
Clears any parking packets.
Wrapper class around callout handle which automatically resets handle's state.
static ServerHooks & getServerHooks()
Return ServerHooks object.
static std::string getVersion()
Version.
Definition log/logger.cc:60
int getExitValue()
Fetches the exit value.
Definition daemon.h:220
Statistics Manager class.
static StatsMgr & instance()
Statistics Manager accessor method.
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
RAII class creating a critical section.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:343
Read mutex RAII handler.
Defines classes for storing client class definitions.
Defines the D2ClientConfig class.
Defines the D2ClientMgr class.
@ STATUS_NoAddrsAvail
Definition dhcp6.h:168
@ STATUS_NoPrefixAvail
Definition dhcp6.h:172
@ STATUS_NotOnLink
Definition dhcp6.h:170
@ STATUS_Success
Definition dhcp6.h:166
@ STATUS_NoBinding
Definition dhcp6.h:169
@ STATUS_UnspecFail
Definition dhcp6.h:167
@ D6O_CLIENT_FQDN
Definition dhcp6.h:59
@ D6O_RSOO
Definition dhcp6.h:86
@ D6O_SERVERID
Definition dhcp6.h:22
@ D6O_CLIENTID
Definition dhcp6.h:21
@ D6O_VENDOR_OPTS
Definition dhcp6.h:37
@ D6O_RELAY_SOURCE_PORT
Definition dhcp6.h:155
@ D6O_RAPID_COMMIT
Definition dhcp6.h:34
@ D6O_IA_NA
Definition dhcp6.h:23
@ D6O_ORO
Definition dhcp6.h:26
@ D6O_PD_EXCLUDE
Definition dhcp6.h:87
@ D6O_IA_PD
Definition dhcp6.h:45
@ D6O_DHCPV4_MSG
Definition dhcp6.h:107
@ D6O_IAADDR
Definition dhcp6.h:25
@ D6O_VENDOR_CLASS
Definition dhcp6.h:36
@ D6O_STATUS_CODE
Definition dhcp6.h:33
@ D6O_IAPREFIX
Definition dhcp6.h:46
@ DHCPV6_ADVERTISE
Definition dhcp6.h:199
@ DHCPV6_REQUEST
Definition dhcp6.h:200
@ DHCPV6_RENEW
Definition dhcp6.h:202
@ DHCPV6_DHCPV4_QUERY
Definition dhcp6.h:221
@ DHCPV6_DHCPV4_RESPONSE
Definition dhcp6.h:222
@ DHCPV6_RECONFIGURE
Definition dhcp6.h:207
@ DHCPV6_REBIND
Definition dhcp6.h:203
@ DHCPV6_REPLY
Definition dhcp6.h:204
@ DHCPV6_SOLICIT
Definition dhcp6.h:198
@ DHCPV6_RELEASE
Definition dhcp6.h:205
@ DHCPV6_INFORMATION_REQUEST
Definition dhcp6.h:208
@ DHCPV6_CONFIRM
Definition dhcp6.h:201
@ DHCPV6_DECLINE
Definition dhcp6.h:206
Defines the Dhcp6to4Ipc class.
#define DOCSIS3_V6_ORO
#define VENDOR_ID_CABLE_LABS
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< OptionUint16Array > OptionUint16ArrayPtr
OptionIntArray< uint16_t > OptionUint16Array
When a message is logged with DEBUG severity, the debug level associated with the message is also spe...
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
@ info
Definition db_log.h:120
ConflictResolutionMode StringToConflictResolutionMode(const std::string &mode_str)
Function which converts string to ConflictResolutionMode enum values.
Definition ncr_msg.cc:45
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition ncr_msg.h:241
const isc::log::MessageID DHCP6_DDNS_REQUEST_SEND_FAILED
boost::shared_ptr< OptionVendor > OptionVendorPtr
Pointer to a vendor option.
const isc::log::MessageID DHCP6_PD_LEASE_ADVERT
const isc::log::MessageID DHCP6_BUFFER_RECEIVED
const isc::log::MessageID DHCP6_RELEASE_NA_DELETED
isc::log::Logger bad_packet6_logger(DHCP6_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition dhcp6_log.h:94
const isc::log::MessageID DHCP6_PACKET_DROP_PARSE_FAIL
const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_NO_TEST
const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_RESULT
const isc::log::MessageID DHCP6_RELEASE_PD_DELETED
const isc::log::MessageID DHCP6_LEASE_ALLOC
const isc::log::MessageID DHCP6_FLEX_ID
const isc::log::MessageID DHCP6_PACKET_PROCESS_EXCEPTION_MAIN
const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_SKIP
uint32_t calculateDdnsTtl(uint32_t lease_lft, const util::Optional< double > &ddns_ttl_percent)
Calculates TTL for a DNS resource record based on lease life time.
const isc::log::MessageID DHCP6_SUBNET_SELECTION_FAILED
const isc::log::MessageID DHCP6_PACKET_DROP_SERVERID_MISMATCH
const isc::log::MessageID DHCP6_HOOK_DECLINE_SKIP
const isc::log::MessageID DHCP6_PD_LEASE_REUSE
const isc::log::MessageID DHCP6_PACKET_DROP_UNICAST
const isc::log::MessageID DHCP6_LEASE_PD_WITHOUT_DUID
const isc::log::MessageID DHCP6_LEASE_ALLOC_FAIL
const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_UNDEFINED
const isc::log::MessageID DHCP6_PACKET_SEND_FAIL
const isc::log::MessageID DHCP6_PACKET_PROCESS_EXCEPTION
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
const isc::log::MessageID DHCP6_QUERY_LABEL
const isc::log::MessageID DHCP6_BUFFER_UNPACK
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:623
std::vector< uint32_t > CfgMACSources
Container for defined MAC/hardware address sources.
const isc::log::MessageID DHCP6_SRV_D2STOP_ERROR
const isc::log::MessageID DHCP6_PACKET_SEND
const isc::log::MessageID DHCP6_DECLINE_FAIL_LEASE_WITHOUT_DUID
const int DBG_DHCP6_BASIC_DATA
Debug level used to log the traces with some basic data.
Definition dhcp6_log.h:43
const isc::log::MessageID DHCP6_HOOK_PACKET_RCVD_SKIP
const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_ERROR
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
const isc::log::MessageID DHCP6_OPEN_SOCKET
const isc::log::MessageID DHCP6_PACK_FAIL
const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID
const isc::log::MessageID DHCP6_HOOK_DDNS_UPDATE
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition lease.h:508
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition lease.h:673
const int DBG_DHCP6_HOOKS
Debug level used to trace hook related operations.
Definition dhcp6_log.h:34
const isc::log::MessageID DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP
ContinuationPtr makeContinuation(Continuation &&cont)
Continuation factory.
const int DBG_DHCP6_START
Debug level used to log information during server startup.
Definition dhcp6_log.h:22
const isc::log::MessageID DHCP6_PD_LEASE_ALLOC
const isc::log::MessageID DHCP6_DDNS_GENERATE_FQDN
const isc::log::MessageID DHCP6_RELEASE_PD_EXPIRED
boost::shared_ptr< Option6IA > Option6IAPtr
A pointer to the Option6IA object.
Definition option6_ia.h:20
const isc::log::MessageID DHCP6_DDNS_REMOVE_OLD_LEASE_FQDN
boost::shared_ptr< const CfgRSOO > ConstCfgRSOOPtr
Pointer to the const object.
Definition cfg_rsoo.h:74
boost::shared_ptr< const CfgHostOperations > ConstCfgHostOperationsPtr
Pointer to the const object.
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition option.h:40
const isc::log::MessageID DHCP6_SUBNET_DATA
const isc::log::MessageID DHCP6_UNKNOWN_MSG_RECEIVED
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
boost::shared_ptr< DdnsParams > DdnsParamsPtr
Defines a pointer for DdnsParams instances.
Definition srv_config.h:180
const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_DROP
const isc::log::MessageID DHCP6_ADD_GLOBAL_STATUS_CODE
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
const isc::log::MessageID DHCP6_DDNS_RESPONSE_FQDN_DATA
const isc::log::MessageID DHCP6_RELEASE_NA
const isc::log::MessageID DHCP6_CLASSES_ASSIGNED
const isc::log::MessageID DHCP6_REQUIRED_OPTIONS_CHECK_FAIL
const isc::log::MessageID DHCP6_PROCESS_IA_NA_EXTEND
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition host.h:273
const isc::log::MessageID DHCP6_RELEASE_PD_FAIL
const isc::log::MessageID DHCP6_SRV_CONSTRUCT_ERROR
const isc::log::MessageID DHCP6_LEASE_RENEW
const char * DOCSIS3_CLASS_EROUTER
The class as specified in vendor-class option by the devices.
Definition libdhcp++.cc:94
const isc::log::MessageID DHCP6_RELEASE_NA_FAIL
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
const isc::log::MessageID DHCP6_PACKET_PROCESS_STD_EXCEPTION
const isc::log::MessageID DHCP6_PACKET_RECEIVE_FAIL
const isc::log::MessageID DHCP6_PROCESS_IA_NA_SOLICIT
const isc::log::MessageID DHCP6_DECLINE_FAIL_IAID_MISMATCH
const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_PARK
OptionContainer::nth_index< 5 >::type OptionContainerCancelIndex
Type of the index #5 - option cancellation flag.
Definition cfg_option.h:339
boost::shared_ptr< Option6StatusCode > Option6StatusCodePtr
Pointer to the isc::dhcp::Option6StatusCode.
std::pair< OptionContainerPersistIndex::const_iterator, OptionContainerPersistIndex::const_iterator > OptionContainerPersistRange
Pair of iterators to represent the range of options having the same persistency flag.
Definition cfg_option.h:337
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
const isc::log::MessageID DHCP4_HOOK_SUBNET6_SELECT_PARKING_LOT_FULL
isc::log::Logger packet6_logger(DHCP6_PACKET_LOGGER_NAME)
Logger for processed packets.
Definition dhcp6_log.h:100
const isc::log::MessageID DHCP6_DECLINE_LEASE
boost::shared_ptr< Expression > ExpressionPtr
Definition token.h:31
const isc::log::MessageID DHCP6_LEASE_ADVERT_FAIL
const isc::log::MessageID DHCP6_HOOK_LEASES6_PARKING_LOT_FULL
const isc::log::MessageID DHCP6_PD_LEASE_ADVERT_FAIL
boost::shared_ptr< Pool > PoolPtr
a pointer to either IPv4 or IPv6 Pool
Definition pool.h:488
isc::hooks::CalloutHandlePtr getCalloutHandle(const T &pktptr)
CalloutHandle Store.
const isc::log::MessageID DHCP6_PACKET_PROCESS_FAIL
const isc::log::MessageID DHCP6_RELEASE_NA_EXPIRED
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
const isc::log::MessageID DHCP6_PACKET_RECEIVED
const isc::log::MessageID DHCP6_RESPONSE_DATA
const isc::log::MessageID DHCP6_DDNS_RECEIVE_FQDN
const isc::log::MessageID DHCP6_PACKET_OPTIONS_SKIPPED
const isc::log::MessageID DHCP6_NO_INTERFACES
const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_DUID
const isc::log::MessageID DHCP6_LEASE_REUSE
isc::log::Logger ddns6_logger(DHCP6_DDNS_LOGGER_NAME)
Logger for Hostname or FQDN processing.
Definition dhcp6_log.h:112
boost::shared_ptr< Continuation > ContinuationPtr
Define the type of shared pointers to continuations.
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition cfg_option.h:323
boost::shared_ptr< ClientClassDefList > ClientClassDefListPtr
Defines a pointer to a ClientClassDefList.
const isc::log::MessageID DHCP6_HOOK_LEASES6_COMMITTED_DROP
const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_DROP
const isc::log::MessageID DHCP6_DDNS_GENERATED_FQDN_UPDATE_FAIL
const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS2
boost::shared_ptr< Option6IAAddr > Option6IAAddrPtr
A pointer to the isc::dhcp::Option6IAAddr object.
const char * DOCSIS3_CLASS_MODEM
DOCSIS3.0 compatible cable modem.
Definition libdhcp++.cc:91
const isc::log::MessageID DHCP6_SHUTDOWN_REQUEST
const isc::log::MessageID DHCP6_HOOK_BUFFER_RCVD_SKIP
const isc::log::MessageID DHCP6_DECLINE_FAIL
bool evaluateBool(const Expression &expr, Pkt &pkt)
Evaluate a RPN expression for a v4 or v6 packet and return a true or false decision.
Definition evaluate.cc:34
const isc::log::MessageID DHCP6_QUERY_DATA
const int DBG_DHCP6_DETAIL_DATA
This level is used to log the contents of packets received and sent.
Definition dhcp6_log.h:54
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition host.h:840
const isc::log::MessageID DHCP6_PROCESS_IA_PD_SOLICIT
const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS
boost::shared_ptr< Lease6Collection > Lease6CollectionPtr
A shared pointer to the collection of IPv6 leases.
Definition lease.h:676
const isc::log::MessageID DHCP6_DECLINE_PROCESS_IA
const isc::log::MessageID DHCP6_PROCESS_IA_NA_REQUEST
OptionContainer::nth_index< 2 >::type OptionContainerPersistIndex
Type of the index #2 - option persistency flag.
Definition cfg_option.h:332
isc::log::Logger lease6_logger(DHCP6_LEASE_LOGGER_NAME)
Logger for lease allocation logic.
Definition dhcp6_log.h:117
const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED
boost::shared_ptr< OptionVendorClass > OptionVendorClassPtr
Defines a pointer to the OptionVendorClass.
const isc::log::MessageID DHCP6_LEASE_NA_WITHOUT_DUID
const isc::log::MessageID DHCP6_HOOK_DECLINE_DROP
const isc::log::MessageID DHCP6_PROCESS_IA_PD_REQUEST
const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_SKIP
const isc::log::MessageID DHCP6_PD_LEASE_RENEW
const isc::log::MessageID DHCP6_CLASS_ASSIGNED
boost::shared_ptr< const Subnet > ConstSubnetPtr
A generic pointer to either const Subnet4 or const Subnet6 object.
Definition subnet.h:452
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
const isc::log::MessageID DHCP6_PROCESS_IA_NA_RELEASE
const isc::log::MessageID DHCP6_PACKET_DROP_DROP_CLASS_EARLY
const isc::log::MessageID DHCP6_LEASE_ADVERT
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition option.h:24
const isc::log::MessageID DHCP6_DECLINE_FAIL_DUID_MISMATCH
const isc::log::MessageID DHCP6_SRV_UNLOAD_LIBRARIES_ERROR
const isc::log::MessageID DHCP6_HOOK_LEASES6_COMMITTED_PARK
const isc::log::MessageID DHCP6_DECLINE_FAIL_NO_LEASE
const isc::log::MessageID DHCP6_RAPID_COMMIT
const isc::log::MessageID DHCP6_BUFFER_WAIT_SIGNAL
bool isClientClassBuiltIn(const ClientClass &client_class)
Check if a client class name is builtin.
boost::shared_ptr< Option6ClientFqdn > Option6ClientFqdnPtr
A pointer to the Option6ClientFqdn object.
const isc::log::MessageID DHCP6_PROCESS_IA_PD_EXTEND
std::pair< OptionContainerCancelIndex::const_iterator, OptionContainerCancelIndex::const_iterator > OptionContainerCancelRange
Pair of iterators to represent the range of options having the same cancellation flag.
Definition cfg_option.h:344
const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION
const isc::log::MessageID DHCP6_PACKET_PROCESS_STD_EXCEPTION_MAIN
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_SUBNET_SELECTED
const isc::log::MessageID DHCP6_RELEASE_NA_FAIL_WRONG_IAID
const isc::log::MessageID DHCP6_HOOK_BUFFER_RCVD_DROP
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
const isc::log::MessageID DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST
const isc::log::MessageID DHCP6_RELEASE_NA_FAIL_WRONG_DUID
const isc::log::MessageID DHCP6_ADD_STATUS_CODE_FOR_IA
isc::log::Logger options6_logger(DHCP6_OPTIONS_LOGGER_NAME)
Logger for options parser.
Definition dhcp6_log.h:106
const isc::log::MessageID DHCP6_LEASE_DATA
const isc::log::MessageID DHCP6_HOOK_BUFFER_SEND_SKIP
const int DBG_DHCP6_DETAIL
Debug level used to trace detailed errors.
Definition dhcp6_log.h:51
const isc::log::MessageID DHCP6_SUBNET_DYNAMICALLY_CHANGED
const isc::log::MessageID DHCP6_PACKET_QUEUE_FULL
const isc::log::MessageID DHCP6_PD_LEASE_ALLOC_FAIL
const isc::log::MessageID DHCP6_HOOK_LEASE6_RELEASE_PD_SKIP
const isc::log::MessageID DHCP6_RELEASE_PD
std::list< ConstCfgOptionPtr > CfgOptionList
Const pointer list.
Definition cfg_option.h:838
const isc::log::MessageID DHCP6_DDNS_FQDN_GENERATED
const isc::log::MessageID DHCP6_PACKET_DROP_DHCP_DISABLED
boost::shared_ptr< Pool6 > Pool6Ptr
a pointer an IPv6 Pool
Definition pool.h:298
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition hooks_log.h:37
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
boost::shared_ptr< ParkingLot > ParkingLotPtr
Type of the pointer to the parking lot.
const int DBGLVL_TRACE_BASIC
Trace basic operations.
const int DBGLVL_PKT_HANDLING
This debug level is reserved for logging the details of packet handling, such as dropping the packet ...
bool equalValues(const T &ptr1, const T &ptr2)
This function checks if two pointers are non-null and values are equal.
Defines the logger used by the top-level component of kea-lfc.
This file defines abstract classes for exchanging NameChangeRequests.
This file provides the classes needed to embody, compose, and decompose DNS update requests that are ...
Standard implementation of read-write mutexes with writer preference using C++11 mutex and condition ...
#define DHCP6_OPTION_SPACE
Lease6Collection old_leases_
A pointer to any old leases that the client had before update but are no longer valid after the updat...
Option6IAPtr ia_rsp_
A pointer to the IA_NA/IA_PD option to be sent in response.
void addHint(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128, const uint32_t preferred=0, const uint32_t valid=0)
Convenience method adding new hint.
Lease6Collection reused_leases_
Set of leases marked for reuse by lease caching.
uint32_t iaid_
The IAID field from IA_NA or IA_PD that is being processed.
Context information for the DHCPv6 leases allocation.
IAContext & currentIA()
Returns IA specific context for the currently processed IA.
std::vector< IAContext > ias_
Container holding IA specific contexts.
void addHostIdentifier(const Host::IdentifierType &id_type, const std::vector< uint8_t > &identifier)
Convenience function adding host identifier into host_identifiers_ list.
bool fake_allocation_
Indicates if this is a real or fake allocation.
ConstHostPtr currentHost() const
Returns host from the most preferred subnet.
DuidPtr duid_
Client identifier.
Lease6Collection new_leases_
A collection of newly allocated leases.
std::vector< IAContext > & getIAContexts()
HWAddrPtr hwaddr_
Hardware/MAC address (if available, may be NULL)
hooks::CalloutHandlePtr callout_handle_
Callout handle associated with the client's message.
ResourceContainer allocated_resources_
Holds addresses and prefixes allocated for all IAs.
bool rev_dns_update_
A boolean value which indicates that server takes responsibility for the reverse DNS Update for this ...
DdnsParamsPtr getDdnsParams()
Returns the set of DDNS behavioral parameters based on the selected subnet.
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
Pkt6Ptr query_
A pointer to the client's message.
bool early_global_reservations_lookup_
Indicates if early global reservation is enabled.
void createIAContext()
Creates new IA context.
ConstSubnet6Ptr subnet_
Subnet selected for the client by the server.
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
bool fwd_dns_update_
A boolean value which indicates that server takes responsibility for the forward DNS Update for this ...
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire)
Definition lease.h:34
static std::string lifetimeToText(uint32_t lifetime)
Print lifetime.
Definition lease.cc:33
static const uint32_t STATE_DEFAULT
A lease in the default state.
Definition lease.h:69
static const uint32_t STATE_RELEASED
Released lease held in the database for lease affinity.
Definition lease.h:78
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition lease.h:49
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition lease.h:47
Subnet selector used to specify parameters used to select a subnet.