Kea 3.2.1-git
dhcp6_srv.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2026 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>
48#include <dhcpsrv/cfg_globals.h>
51#include <dhcpsrv/cfg_option.h>
53#include <dhcpsrv/cfg_rsoo.h>
55#include <dhcpsrv/cfgmgr.h>
59#include <dhcpsrv/host.h>
61#include <dhcpsrv/host_mgr.h>
62#include <dhcpsrv/lease.h>
67#include <dhcpsrv/pool.h>
70#include <dhcpsrv/srv_config.h>
71#include <dhcpsrv/subnet.h>
72#include <dhcpsrv/subnet_id.h>
74#include <dhcpsrv/utils.h>
75#include <eval/evaluate.h>
76#include <eval/token.h>
79#include <hooks/hooks_log.h>
80#include <hooks/hooks_manager.h>
81#include <hooks/parking_lots.h>
82#include <hooks/server_hooks.h>
84#include <log/log_dbglevels.h>
85#include <log/log_formatter.h>
86#include <log/logger.h>
87#include <log/macros.h>
88#include <stats/stats_mgr.h>
89#include <util/buffer.h>
91#include <util/optional.h>
92#include <util/pointer_util.h>
94#include <util/thread_pool.h>
95#include <util/triplet.h>
96
97#include <algorithm>
98#include <cmath>
99#include <cstdint>
100#include <cstdlib>
101#include <exception>
102#include <functional>
103#include <iomanip>
104#include <limits>
105#include <list>
106#include <map>
107#include <memory>
108#include <set>
109#include <sstream>
110#include <string>
111#include <tuple>
112#include <utility>
113#include <vector>
114
115#include <boost/algorithm/string/erase.hpp>
116#include <boost/algorithm/string/join.hpp>
117#include <boost/algorithm/string/split.hpp>
118#include <boost/foreach.hpp>
119#include <boost/tokenizer.hpp>
120
121using namespace isc;
122using namespace isc::asiolink;
123using namespace isc::cryptolink;
124using namespace isc::data;
125using namespace isc::dhcp;
126using namespace isc::dhcp_ddns;
127using namespace isc::hooks;
128using namespace isc::log;
129using namespace isc::log::interprocess;
130using namespace isc::stats;
131using namespace isc::util;
132using namespace std;
133namespace ph = std::placeholders;
134
135namespace {
136
138struct Dhcp6Hooks {
139 int hook_index_buffer6_receive_;
140 int hook_index_pkt6_receive_;
141 int hook_index_subnet6_select_;
142 int hook_index_leases6_committed_;
143 int hook_index_lease6_release_;
144 int hook_index_pkt6_send_;
145 int hook_index_buffer6_send_;
146 int hook_index_lease6_decline_;
147 int hook_index_host6_identifier_;
148 int hook_index_ddns6_update_;
149 int hook_index_addr6_register_;
150
152 Dhcp6Hooks() {
153 hook_index_buffer6_receive_ = HooksManager::registerHook("buffer6_receive");
154 hook_index_pkt6_receive_ = HooksManager::registerHook("pkt6_receive");
155 hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
156 hook_index_leases6_committed_ = HooksManager::registerHook("leases6_committed");
157 hook_index_lease6_release_ = HooksManager::registerHook("lease6_release");
158 hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send");
159 hook_index_buffer6_send_ = HooksManager::registerHook("buffer6_send");
160 hook_index_lease6_decline_ = HooksManager::registerHook("lease6_decline");
161 hook_index_host6_identifier_ = HooksManager::registerHook("host6_identifier");
162 hook_index_ddns6_update_ = HooksManager::registerHook("ddns6_update");
163 hook_index_addr6_register_ = HooksManager::registerHook("addr6_register");
164 }
165};
166
167// Declare a Hooks object. As this is outside any function or method, it
168// will be instantiated (and the constructor run) when the module is loaded.
169// As a result, the hook indexes will be defined before any method in this
170// module is called.
171Dhcp6Hooks Hooks;
172
185createStatusCode(const Pkt6& pkt, const uint16_t status_code,
186 const std::string& status_message) {
187 Option6StatusCodePtr option_status(new Option6StatusCode(status_code,
188 status_message));
190 .arg(pkt.getLabel())
191 .arg(option_status->dataToText());
192 return (option_status);
193}
194
210createStatusCode(const Pkt6& pkt, const Option6IA& ia, const uint16_t status_code,
211 const std::string& status_message) {
212 Option6StatusCodePtr option_status(new Option6StatusCode(status_code,
213 status_message));
215 .arg(pkt.getLabel())
216 .arg(ia.getIAID())
217 .arg(option_status->dataToText());
218 return (option_status);
219}
220
223std::set<std::string> dhcp6_statistics = {
224 "pkt6-received",
225 "pkt6-solicit-received",
226 "pkt6-advertise-received",
227 "pkt6-request-received",
228 "pkt6-reply-received",
229 "pkt6-renew-received",
230 "pkt6-rebind-received",
231 "pkt6-decline-received",
232 "pkt6-release-received",
233 "pkt6-infrequest-received",
234 "pkt6-lease-query-received",
235 "pkt6-dhcpv4-query-received",
236 "pkt6-dhcpv4-response-received",
237 "pkt6-addr-reg-inform-received",
238 "pkt6-addr-reg-reply-received",
239 "pkt6-unknown-received",
240 "pkt6-sent",
241 "pkt6-advertise-sent",
242 "pkt6-reply-sent",
243 "pkt6-lease-query-reply-sent",
244 "pkt6-dhcpv4-response-sent",
245 "pkt6-addr-reg-reply-sent",
246 "pkt6-service-disabled",
247 "pkt6-parse-failed",
248 "pkt6-queue-full",
249 "pkt6-duplicate",
250 "pkt6-rfc-violation",
251 "pkt6-admin-filtered",
252 "pkt6-not-for-us",
253 "pkt6-processing-failed",
254 "pkt6-limit-exceeded",
255 "pkt6-receive-drop",
256 "v6-allocation-fail",
257 "v6-allocation-fail-shared-network",
258 "v6-allocation-fail-subnet",
259 "v6-allocation-fail-no-pools",
260 "v6-allocation-fail-classes",
261 "v6-ia-na-lease-reuses",
262 "v6-ia-pd-lease-reuses",
263};
264
265} // namespace
266
267namespace isc {
268namespace dhcp {
269
270const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
271
272Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port)
273 : io_service_(new IOService()), server_port_(server_port),
274 client_port_(client_port), serverid_(), shutdown_(true),
279 .arg(server_port);
280
281 Dhcp6to4Ipc::instance().client_port = client_port;
282
283 // Initialize objects required for DHCP server operation.
284 try {
285 // Port 0 is used for testing purposes where in most cases we don't
286 // rely on the physical interfaces. Therefore, it should be possible
287 // to create an object even when there are no usable interfaces.
288 if ((server_port > 0) && (IfaceMgr::instance().countIfaces() == 0)) {
290 return;
291 }
292
293 // Create a DUID instance but do not store it into a file.
294 DUIDFactory duid_factory;
295 DuidPtr duid = duid_factory.get();
296 serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
297
298 // Instantiate allocation engine. The number of allocation attempts equal
299 // to zero indicates that the allocation engine will use the number of
300 // attempts depending on the pool size.
301 alloc_engine_.reset(new AllocEngine(0));
302
304
305 } catch (const std::exception &e) {
307 return;
308 }
309
310 // Initializing all observations with default value
312
313 // All done, so can proceed
314 shutdown_ = false;
315}
316
318 StatsMgr& stats_mgr = StatsMgr::instance();
319
320 // Iterate over set of observed statistics
321 for (auto const& it : dhcp6_statistics) {
322 // Initialize them with default value 0
323 stats_mgr.setValue(it, static_cast<int64_t>(0));
324 }
325}
326
328 // Discard any parked packets
330
331 try {
332 stopD2();
333 } catch (const std::exception& ex) {
334 // Highly unlikely, but lets Report it but go on
336 }
337
338 try {
340 } catch (const std::exception& ex) {
341 // Highly unlikely, but lets Report it but go on
342 // LOG_ERROR(dhcp6_logger, DHCP6_SRV_DHCP4O6_ERROR).arg(ex.what());
343 }
344
346
347 // The lease manager was instantiated during DHCPv6Srv configuration,
348 // so we should clean up after ourselves.
350
351 // Destroy the host manager before hooks unload.
353
354 // Explicitly unload hooks
357 auto names = HooksManager::getLibraryNames();
358 std::string msg;
359 if (!names.empty()) {
360 msg = names[0];
361 for (size_t i = 1; i < names.size(); ++i) {
362 msg += std::string(", ") + names[i];
363 }
364 }
366 }
368 io_service_->stopAndPoll();
369}
370
375
377 return (IfaceMgr::instance().receive6(timeout));
378}
379
380void Dhcpv6Srv::sendPacket(const Pkt6Ptr& packet) {
381 IfaceMgr::instance().send(packet);
382}
383
384bool
390 OptionPtr server_id = pkt->getOption(D6O_SERVERID);
391 if (server_id){
392 // Let us test received ServerID if it is same as ServerID
393 // which is being used by server
394 if (getServerID()->getData() != server_id->getData()){
396 .arg(pkt->getLabel())
397 .arg(duidToString(server_id))
398 .arg(duidToString(getServerID()));
399 StatsMgr::instance().addValue("pkt6-not-for-us",
400 static_cast<int64_t>(1));
401 return (false);
402 }
403 }
404 // return True if: no serverid received or ServerIDs matching
405 return (true);
406}
407
408bool
410 if (pkt->relay_info_.empty() && !pkt->getLocalAddr().isV6Multicast()) {
412 .arg(pkt->getLabel())
413 .arg(pkt->getName());
414 StatsMgr::instance().addValue("pkt6-rfc-violation",
415 static_cast<int64_t>(1));
416 return (false);
417 }
418 return (true);
419}
420
421void
423 const ConstCfgHostOperationsPtr cfg =
424 CfgMgr::instance().getCurrentCfg()->getCfgHostOperations6();
425 for (auto const& id_type : cfg->getIdentifierTypes()) {
426 switch (id_type) {
427 case Host::IDENT_DUID:
428 if (ctx.duid_) {
429 ctx.addHostIdentifier(id_type, ctx.duid_->getDuid());
430 }
431 break;
432
434 if (ctx.hwaddr_ && !ctx.hwaddr_->hwaddr_.empty()) {
435 ctx.addHostIdentifier(id_type, ctx.hwaddr_->hwaddr_);
436 }
437 break;
438 case Host::IDENT_FLEX:
439 // At this point the information in the packet has been unpacked into
440 // the various packet fields and option objects has been created.
441 // Execute callouts registered for host6_identifier.
442 if (HooksManager::calloutsPresent(Hooks.hook_index_host6_identifier_)) {
443 CalloutHandlePtr callout_handle = getCalloutHandle(ctx.query_);
444
446 std::vector<uint8_t> id;
447
448 // Use the RAII wrapper to make sure that the callout handle state is
449 // reset when this object goes out of scope. All hook points must do
450 // it to prevent possible circular dependency between the callout
451 // handle and its arguments.
452 ScopedCalloutHandleState callout_handle_state(callout_handle);
453
454 // Pass incoming packet as argument
455 callout_handle->setArgument("query6", ctx.query_);
456 callout_handle->setArgument("id_type", type);
457 callout_handle->setArgument("id_value", id);
458
459 // Call callouts
460 HooksManager::callCallouts(Hooks.hook_index_host6_identifier_,
461 *callout_handle);
462
463 callout_handle->getArgument("id_type", type);
464 callout_handle->getArgument("id_value", id);
465
466 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_CONTINUE) &&
467 !id.empty()) {
468
470 .arg(ctx.query_->getLabel())
471 .arg(Host::getIdentifierAsText(type, &id[0], id.size()));
472
473 ctx.addHostIdentifier(type, id);
474 }
475 }
476 break;
477 default:
478 ;
479 }
480 }
481}
482
483void
486 // Pointer to client's query.
487 ctx.query_ = query;
488
489 // DUID.
490 ctx.duid_ = query->getClientId();
491
492 // Hardware address.
493 ctx.hwaddr_ = getMAC(query);
494}
495
496bool
499 // First part of context initialization.
500 initContext0(query, ctx);
501
502 // Get the early-global-reservations-lookup flag value.
505 if (egrl) {
506 ctx.early_global_reservations_lookup_ = egrl->boolValue();
507 }
508
509 // Perform early global reservations lookup when wanted.
511 // Get the host identifiers.
513
514 // Check for global host reservations.
515 ConstHostPtr global_host = alloc_engine_->findGlobalReservation(ctx);
516
517 if (global_host && !global_host->getClientClasses6().empty()) {
518 // Remove dependent evaluated classes.
520
521 // Add classes from the global reservations.
522 const ClientClasses& classes = global_host->getClientClasses6();
523 for (auto const& cclass : classes) {
524 query->addClass(cclass);
525 }
526
527 // Evaluate classes before KNOWN.
528 evaluateClasses(query, false);
529 }
530
531 if (global_host) {
532 // Add the KNOWN class;
533 query->addClass("KNOWN");
535 .arg(query->getLabel())
536 .arg("KNOWN");
537
538 // Evaluate classes after KNOWN.
539 evaluateClasses(query, true);
540
541 // Check the DROP special class.
542 if (query->inClass("DROP")) {
545 .arg(query->makeLabel(query->getClientId(), nullptr))
546 .arg(query->toText());
547 StatsMgr::instance().addValue("pkt6-admin-filtered",
548 static_cast<int64_t>(1));
549 StatsMgr::instance().addValue("pkt6-receive-drop",
550 static_cast<int64_t>(1));
551 return (false);
552 }
553
554 // Store the reservation.
555 ctx.hosts_[SUBNET_ID_GLOBAL] = global_host;
556 }
557 }
558
559 return (true);
560}
561
562void
564 // Sanity check.
565 if (!ctx.query_) {
566 drop = true;
567 return;
568 }
569 ctx.fwd_dns_update_ = false;
570 ctx.rev_dns_update_ = false;
571 ctx.hostname_ = "";
573
574 // Collect host identifiers if host reservations enabled. The identifiers
575 // are stored in order of preference. The server will use them in that
576 // order to search for host reservations.
578 if (ctx.subnet_) {
579 // Before we can check for static reservations, we need to prepare
580 // a set of identifiers to be used for this.
583 }
584
585 // Find host reservations using specified identifiers.
586 alloc_engine_->findReservation(ctx);
587
588 // Get shared network to see if it is set for a subnet.
589 ctx.subnet_->getSharedNetwork(sn);
590 }
591
592 // Global host reservations are independent of a selected subnet. If the
593 // global reservations contain client classes we should use them in case
594 // they are meant to affect pool selection. Also, if the subnet does not
595 // belong to a shared network we can use the reserved client classes
596 // because there is no way our subnet could change. Such classes may
597 // affect selection of a pool within the selected subnet.
598 auto global_host = ctx.globalHost();
599 auto current_host = ctx.currentHost();
601 global_host && !global_host->getClientClasses6().empty()) ||
602 (!sn && current_host && !current_host->getClientClasses6().empty())) {
603 // We have already evaluated client classes and some of them may
604 // be in conflict with the reserved classes. Suppose there are
605 // two classes defined in the server configuration: first_class
606 // and second_class and the test for the second_class it looks
607 // like this: "not member('first_class')". If the first_class
608 // initially evaluates to false, the second_class evaluates to
609 // true. If the first_class is now set within the hosts reservations
610 // and we don't remove the previously evaluated second_class we'd
611 // end up with both first_class and second_class evaluated to
612 // true. In order to avoid that, we have to remove the classes
613 // evaluated in the first pass and evaluate them again. As
614 // a result, the first_class set via the host reservation will
615 // replace the second_class because the second_class will this
616 // time evaluate to false as desired.
619 evaluateClasses(ctx.query_, false);
620 }
621
622 // Set KNOWN builtin class if something was found, UNKNOWN if not.
623 if (!ctx.hosts_.empty()) {
624 ctx.query_->addClass("KNOWN");
626 .arg(ctx.query_->getLabel())
627 .arg("KNOWN");
628 } else {
629 ctx.query_->addClass("UNKNOWN");
631 .arg(ctx.query_->getLabel())
632 .arg("UNKNOWN");
633 }
634
635 // Perform second pass of classification.
636 evaluateClasses(ctx.query_, true);
637
638 const ClientClasses& classes = ctx.query_->getClasses();
640 .arg(ctx.query_->getLabel())
641 .arg(classes.toText());
642
643 // Check the DROP special class.
644 if (ctx.query_->inClass("DROP")) {
646 .arg(ctx.query_->makeLabel(ctx.query_->getClientId(), 0))
647 .arg(ctx.query_->toText());
648 StatsMgr::instance().addValue("pkt6-admin-filtered",
649 static_cast<int64_t>(1));
650 StatsMgr::instance().addValue("pkt6-receive-drop",
651 static_cast<int64_t>(1));
652 drop = true;
653 }
654}
655
656int
658#ifdef HAVE_AFL
659 // Get the values of the environment variables used to control the
660 // fuzzing.
661
662 // Specfies the interface to be used to pass packets from AFL to Kea.
663 const char* interface = getenv("KEA_AFL_INTERFACE");
664 if (!interface) {
665 isc_throw(FuzzInitFail, "no fuzzing interface has been set");
666 }
667
668 // The address on the interface to be used.
669 const char* address = getenv("KEA_AFL_ADDRESS");
670 if (!address) {
671 isc_throw(FuzzInitFail, "no fuzzing address has been set");
672 }
673
674 // Set up structures needed for fuzzing.
675 PacketFuzzer fuzzer(server_port_, interface, address);
676
677 // The next line is needed as a signature for AFL to recognize that we are
678 // running persistent fuzzing. This has to be in the main image file.
679 while (__AFL_LOOP(fuzzer.maxLoopCount())) {
680 // Read from stdin and put the data read into an address/port on which
681 // Kea is listening, read for Kea to read it via asynchronous I/O.
682 fuzzer.transfer();
683#else
684 while (!shutdown_) {
685#endif // HAVE_AFL
686 try {
687 runOne();
688 // Handle events registered by hooks using external IOService objects.
690 getIOService()->poll();
691 } catch (const std::exception& e) {
692 // General catch-all standard exceptions that are not caught by more
693 // specific catches.
695 .arg(e.what());
696
697 } catch (...) {
698 // General catch-all non-standard exception that are not caught
699 // by more specific catches.
701 }
702 }
703
704 // Stop everything before we change into single-threaded mode.
706
707 // destroying the thread pool
708 MultiThreadingMgr::instance().apply(false, 0, 0);
709
710 return (getExitValue());
711}
712
713void
715 // client's message and server's response
716 Pkt6Ptr query;
717
718 try {
719 // Set select() timeout to 1s. This value should not be modified
720 // because it is important that the select() returns control
721 // frequently so as the IOService can be polled for ready handlers.
722 uint32_t timeout = 1;
723 query = receivePacket(timeout);
724
725 // Log if packet has arrived. We can't log the detailed information
726 // about the DHCP message because it hasn't been unpacked/parsed
727 // yet, and it can't be parsed at this point because hooks will
728 // have to process it first. The only information available at this
729 // point are: the interface, source address and destination addresses
730 // and ports.
731 if (query) {
733 .arg(query->getRemoteAddr().toText())
734 .arg(query->getRemotePort())
735 .arg(query->getLocalAddr().toText())
736 .arg(query->getLocalPort())
737 .arg(query->getIface());
738
739 // Log reception of the packet. We need to increase it early, as
740 // any failures in unpacking will cause the packet to be dropped.
741 // we will increase type specific packets further down the road.
742 // See processStatsReceived().
743 StatsMgr::instance().addValue("pkt6-received", static_cast<int64_t>(1));
744 }
745
746 // We used to log that the wait was interrupted, but this is no longer
747 // the case. Our wait time is 1s now, so the lack of query packet more
748 // likely means that nothing new appeared within a second, rather than
749 // we were interrupted. And we don't want to print a message every
750 // second.
751
752 } catch (const SignalInterruptOnSelect&) {
753 // Packet reception interrupted because a signal has been received.
754 // This is not an error because we might have received a SIGTERM,
755 // SIGINT, SIGHUP or SIGCHLD which are handled by the server. For
756 // signals that are not handled by the server we rely on the default
757 // behavior of the system.
759 } catch (const std::exception& e) {
761 .arg(e.what());
762 }
763
764 // Timeout may be reached or signal received, which breaks select()
765 // with no packet received
766 if (!query) {
767 return;
768 }
769
770 // If the DHCP service has been globally disabled, drop the packet.
771 if (!network_state_->isServiceEnabled()) {
773 .arg(query->getLabel());
774 // Increase the statistics of service disabled and dropped packets.
775 StatsMgr::instance().addValue("pkt6-service-disabled",
776 static_cast<int64_t>(1));
777 StatsMgr::instance().addValue("pkt6-receive-drop",
778 static_cast<int64_t>(1));
779 return;
780 } else {
781 if (MultiThreadingMgr::instance().getMode()) {
782 query->addPktEvent("mt_queued");
783 typedef function<void()> CallBack;
784 boost::shared_ptr<CallBack> call_back =
785 boost::make_shared<CallBack>(std::bind(&Dhcpv6Srv::processPacketAndSendResponseNoThrow,
786 this, query));
787 if (!MultiThreadingMgr::instance().getThreadPool().add(call_back)) {
789 StatsMgr::instance().addValue("pkt6-queue-full",
790 static_cast<int64_t>(1));
791 StatsMgr::instance().addValue("pkt6-receive-drop",
792 static_cast<int64_t>(1));
793 }
794 } else {
796 }
797 }
798}
799
800void
802 try {
804 } catch (const std::exception& e) {
806 .arg(query->getLabel())
807 .arg(e.what());
808 StatsMgr::instance().addValue("pkt6-processing-failed",
809 static_cast<int64_t>(1));
810 StatsMgr::instance().addValue("pkt6-receive-drop",
811 static_cast<int64_t>(1));
812 } catch (...) {
814 .arg(query->getLabel());
815 StatsMgr::instance().addValue("pkt6-processing-failed",
816 static_cast<int64_t>(1));
817 StatsMgr::instance().addValue("pkt6-receive-drop",
818 static_cast<int64_t>(1));
819 }
820}
821
822void
824 Pkt6Ptr rsp = processPacket(query);
825 if (!rsp) {
826 return;
827 }
828
829 CalloutHandlePtr callout_handle = getCalloutHandle(query);
830 processPacketBufferSend(callout_handle, rsp);
831}
832
835 query->addPktEvent("process_started");
836
837 // All packets belong to ALL.
838 query->addClass("ALL");
839
840 bool skip_unpack = false;
841
842 // The packet has just been received so contains the uninterpreted wire
843 // data; execute callouts registered for buffer6_receive.
844 if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
845 CalloutHandlePtr callout_handle = getCalloutHandle(query);
846
847 // Use the RAII wrapper to make sure that the callout handle state is
848 // reset when this object goes out of scope. All hook points must do
849 // it to prevent possible circular dependency between the callout
850 // handle and its arguments.
851 ScopedCalloutHandleState callout_handle_state(callout_handle);
852
853 // Enable copying options from the packet within hook library.
854 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
855
856 // Pass incoming packet as argument
857 callout_handle->setArgument("query6", query);
858
859 // Call callouts
860 HooksManager::callCallouts(Hooks.hook_index_buffer6_receive_, *callout_handle);
861
862 // Callouts decided to skip the next processing step. The next
863 // processing step would be to parse the packet, so skip at this
864 // stage means that callouts did the parsing already, so server
865 // should skip parsing.
866 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
868 .arg(query->getRemoteAddr().toText())
869 .arg(query->getLocalAddr().toText())
870 .arg(query->getIface());
871 skip_unpack = true;
872 }
873
874 // Callouts decided to drop the received packet
875 // The response (rsp) is null so the caller (runOne) will
876 // immediately return too.
877 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
879 .arg(query->getRemoteAddr().toText())
880 .arg(query->getLocalAddr().toText())
881 .arg(query->getIface());
882
883 // Not increasing the statistics of the dropped packets because it
884 // is the callouts' responsibility to increase it.
885 return (Pkt6Ptr());
886 }
887
888 callout_handle->getArgument("query6", query);
889 if (!query) {
890 // Please use the status instead of resetting query!
891 return (Pkt6Ptr());
892 }
893 }
894
895 // Unpack the packet information unless the buffer6_receive callouts
896 // indicated they did it
897 if (!skip_unpack) {
898 try {
900 .arg(query->getRemoteAddr().toText())
901 .arg(query->getLocalAddr().toText())
902 .arg(query->getIface());
903 query->unpack();
904 } catch (const SkipRemainingOptionsError& e) {
905 // An option failed to unpack but we are to attempt to process it
906 // anyway. Log it and let's hope for the best.
909 .arg(query->getLabel())
910 .arg(e.what());
911 } catch (const std::exception &e) {
912 // Failed to parse the packet.
914 .arg(query->getLabel())
915 .arg(query->getRemoteAddr().toText())
916 .arg(query->getLocalAddr().toText())
917 .arg(query->getIface())
918 .arg(e.what())
919 .arg(query->makeLabel(query->getClientId(), nullptr));
920
921 // Increase the statistics of parse failures and dropped packets.
922 StatsMgr::instance().addValue("pkt6-parse-failed",
923 static_cast<int64_t>(1));
924 StatsMgr::instance().addValue("pkt6-receive-drop",
925 static_cast<int64_t>(1));
926 return (Pkt6Ptr());
927 }
928 }
929
930 // Classify can emit INFO logs so help to track the query.
932 .arg(query->getLabel());
933
934 // Update statistics accordingly for received packet.
935 processStatsReceived(query);
936
937 // Check if received query carries server identifier matching
938 // server identifier being used by the server.
939 if (!testServerID(query)) {
940
941 // Increase the statistic of dropped packets.
942 // The drop cause statistic was increased by testServerID.
943 StatsMgr::instance().addValue("pkt6-receive-drop",
944 static_cast<int64_t>(1));
945 return (Pkt6Ptr());
946 }
947
948 // Check if the received query has been sent to unicast or multicast.
949 // All queries per RFC 9915 will be discarded if sent to unicast address.
950 if (!testUnicast(query)) {
951
952 // Increase the statistic of dropped packets.
953 StatsMgr::instance().addValue("pkt6-receive-drop",
954 static_cast<int64_t>(1));
955 return (Pkt6Ptr());
956 }
957
958 // Assign this packet to a class, if possible
959 classifyPacket(query);
960
962 .arg(query->getLabel())
963 .arg(query->getName())
964 .arg(static_cast<int>(query->getType()))
965 .arg(query->getRemoteAddr())
966 .arg(query->getLocalAddr())
967 .arg(query->getIface());
969 .arg(query->getLabel())
970 .arg(query->toText());
971
972 // At this point the information in the packet has been unpacked into
973 // the various packet fields and option objects has been created.
974 // Execute callouts registered for packet6_receive.
975 if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
976 CalloutHandlePtr callout_handle = getCalloutHandle(query);
977
978 // Use the RAII wrapper to make sure that the callout handle state is
979 // reset when this object goes out of scope. All hook points must do
980 // it to prevent possible circular dependency between the callout
981 // handle and its arguments.
982 ScopedCalloutHandleState callout_handle_state(callout_handle);
983
984 // Enable copying options from the packet within hook library.
985 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
986
987 // Pass incoming packet as argument
988 callout_handle->setArgument("query6", query);
989
990 // Call callouts
991 HooksManager::callCallouts(Hooks.hook_index_pkt6_receive_, *callout_handle);
992
993 // Callouts decided to skip the next processing step. The next
994 // processing step would be to process the packet, so skip at this
995 // stage means drop.
996 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
997 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
999 .arg(query->getLabel());
1000 // Not increasing the statistics of the dropped packets because it
1001 // is the callouts' responsibility to increase it.
1002 return (Pkt6Ptr());
1003 }
1004
1005 callout_handle->getArgument("query6", query);
1006 if (!query) {
1007 // Please use the status instead of resetting query!
1008 return (Pkt6Ptr());
1009 }
1010 }
1011
1012 // Reject the message if it doesn't pass the sanity check.
1013 // Statistics were updated by sanityCheck.
1014 if (!sanityCheck(query)) {
1015 return (Pkt6Ptr());
1016 }
1017
1018 // Check the DROP special class.
1019 if (query->inClass("DROP")) {
1021 .arg(query->makeLabel(query->getClientId(), nullptr))
1022 .arg(query->toText());
1023 StatsMgr::instance().addValue("pkt6-admin-filtered",
1024 static_cast<int64_t>(1));
1025 StatsMgr::instance().addValue("pkt6-receive-drop",
1026 static_cast<int64_t>(1));
1027 return (Pkt6Ptr());
1028 }
1029
1030 return (processDhcp6Query(query));
1031}
1032
1033void
1035 try {
1036 Pkt6Ptr rsp = processDhcp6Query(query);
1037 if (!rsp) {
1038 return;
1039 }
1040
1041 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1042 processPacketBufferSend(callout_handle, rsp);
1043 } catch (const std::exception& e) {
1045 .arg(query->getLabel())
1046 .arg(e.what());
1047 StatsMgr::instance().addValue("pkt6-processing-failed",
1048 static_cast<int64_t>(1));
1049 StatsMgr::instance().addValue("pkt6-receive-drop",
1050 static_cast<int64_t>(1));
1051 } catch (...) {
1053 .arg(query->getLabel());
1054 StatsMgr::instance().addValue("pkt6-processing-failed",
1055 static_cast<int64_t>(1));
1056 StatsMgr::instance().addValue("pkt6-receive-drop",
1057 static_cast<int64_t>(1));
1058 }
1059}
1060
1061Pkt6Ptr
1063 // Create a client race avoidance RAII handler.
1064 ClientHandler client_handler;
1065
1066 // Check for lease modifier queries from the same client being processed.
1067 if (MultiThreadingMgr::instance().getMode() &&
1068 ((query->getType() == DHCPV6_SOLICIT) ||
1069 (query->getType() == DHCPV6_REQUEST) ||
1070 (query->getType() == DHCPV6_RENEW) ||
1071 (query->getType() == DHCPV6_REBIND) ||
1072 (query->getType() == DHCPV6_RELEASE) ||
1073 (query->getType() == DHCPV6_DECLINE) ||
1074 (query->getType() == DHCPV6_ADDR_REG_INFORM))) {
1075 ContinuationPtr cont =
1077 this, query));
1078 if (!client_handler.tryLock(query, cont)) {
1079 return (Pkt6Ptr());
1080 }
1081 }
1082
1083 // Let's create a simplified client context here.
1085 if (!earlyGHRLookup(query, ctx)) {
1086 return (Pkt6Ptr());
1087 }
1088
1089 if (query->getType() == DHCPV6_DHCPV4_QUERY) {
1090 // This call never throws. Should this change, this section must be
1091 // enclosed in try-catch.
1092 processDhcp4Query(query);
1093 return (Pkt6Ptr());
1094 }
1095
1096 // Complete the client context initialization.
1097 bool drop = false;
1098 ctx.subnet_ = selectSubnet(query, drop);
1099 if (drop) {
1100 // Caller will immediately drop the packet so simply return now.
1101 return (Pkt6Ptr());
1102 }
1103
1104 return (processLocalizedQuery6(ctx));
1105}
1106
1107void
1110 try {
1112 if (!rsp) {
1113 return;
1114 }
1115
1116 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1117 processPacketBufferSend(callout_handle, rsp);
1118 } catch (const std::exception& e) {
1120 .arg(query->getLabel())
1121 .arg(e.what());
1122 StatsMgr::instance().addValue("pkt6-processing-failed",
1123 static_cast<int64_t>(1));
1124 StatsMgr::instance().addValue("pkt6-receive-drop",
1125 static_cast<int64_t>(1));
1126 } catch (...) {
1128 .arg(query->getLabel());
1129 StatsMgr::instance().addValue("pkt6-processing-failed",
1130 static_cast<int64_t>(1));
1131 StatsMgr::instance().addValue("pkt6-receive-drop",
1132 static_cast<int64_t>(1));
1133 }
1134}
1135
1136void
1138 // Initialize context.
1140 initContext0(query, ctx);
1141
1142 // Subnet is cached in the callout context associated to the query.
1143 try {
1144 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1145 callout_handle->getContext("subnet6", ctx.subnet_);
1146 } catch (const Exception&) {
1147 // No subnet, leave it to null...
1148 }
1149
1151}
1152
1153Pkt6Ptr
1155 Pkt6Ptr query = ctx.query_;
1156 bool drop = false;
1157 initContext(ctx, drop);
1158 // Stop here if initContext decided to drop the packet.
1159 if (drop) {
1160 return (Pkt6Ptr());
1161 }
1162
1163 Pkt6Ptr rsp;
1164 // The only expected exception is RFCViolation.
1165 bool rfc_violation = false;
1166 try {
1167 try {
1168 switch (query->getType()) {
1169 case DHCPV6_SOLICIT:
1170 rsp = processSolicit(ctx);
1171 break;
1172
1173 case DHCPV6_REQUEST:
1174 rsp = processRequest(ctx);
1175 break;
1176
1177 case DHCPV6_RENEW:
1178 rsp = processRenew(ctx);
1179 break;
1180
1181 case DHCPV6_REBIND:
1182 rsp = processRebind(ctx);
1183 break;
1184
1185 case DHCPV6_CONFIRM:
1186 rsp = processConfirm(ctx);
1187 break;
1188
1189 case DHCPV6_RELEASE:
1190 rsp = processRelease(ctx);
1191 break;
1192
1193 case DHCPV6_DECLINE:
1194 rsp = processDecline(ctx);
1195 break;
1196
1198 rsp = processInfRequest(ctx);
1199 break;
1200
1202 rsp = processAddrRegInform(ctx);
1203 break;
1204
1205 default:
1206 return (rsp);
1207 }
1208 } catch (const RFCViolation&) {
1209 rfc_violation = true;
1210 throw;
1211 }
1212 } catch (const std::exception& e) {
1213
1214 // Catch-all exception (at least for ones based on the isc Exception
1215 // class, which covers more or less all that are explicitly raised
1216 // in the Kea code), but also the standard one, which may possibly be
1217 // thrown from boost code. Just log the problem and ignore the packet.
1218 // (The problem is logged as a debug message because debug is
1219 // disabled by default - it prevents a DDOS attack based on the
1220 // sending of problem packets.)
1222 .arg(query->getLabel())
1223 .arg(query->getName())
1224 .arg(query->getRemoteAddr().toText())
1225 .arg(e.what());
1226
1227 // Increase the statistic of dropped packets.
1228 // The RFCViolation thrower updated the drop cause statistic.
1229 if (!rfc_violation) {
1230 StatsMgr::instance().addValue("pkt6-processing-failed",
1231 static_cast<int64_t>(1));
1232 }
1233 StatsMgr::instance().addValue("pkt6-receive-drop",
1234 static_cast<int64_t>(1));
1235 return (Pkt6Ptr());
1236 }
1237
1238 if (!rsp) {
1239 return (rsp);
1240 }
1241
1242 // Process relay-supplied options. It is important to call this very
1243 // late in the process, because we now have all the options the
1244 // server wanted to send already set. This is important, because
1245 // RFC6422, section 6 states:
1246 //
1247 // The server SHOULD discard any options that appear in the RSOO
1248 // for which it already has one or more candidates.
1249 //
1250 // So we ignore any RSOO options if there's an option with the same
1251 // code already present.
1252 processRSOO(query, rsp);
1253
1254 rsp->setRemoteAddr(query->getRemoteAddr());
1255 rsp->setLocalAddr(query->getLocalAddr());
1256
1257 if (client_port_) {
1258 // A command line option enforces a specific client port
1259 rsp->setRemotePort(client_port_);
1260 } else if (rsp->relay_info_.empty()) {
1261 // Direct traffic, send back to the client directly
1262 rsp->setRemotePort(DHCP6_CLIENT_PORT);
1263 } else {
1264 // Relayed traffic, send back to the relay agent
1265 uint16_t relay_port = checkRelaySourcePort(query);
1266 rsp->setRemotePort(relay_port ? relay_port : DHCP6_SERVER_PORT);
1267 }
1268
1269 if (server_port_) {
1270 rsp->setLocalPort(server_port_);
1271 } else {
1272 rsp->setLocalPort(DHCP6_SERVER_PORT);
1273 }
1274 rsp->setIndex(query->getIndex());
1275 rsp->setIface(query->getIface());
1276
1277 CalloutHandlePtr callout_handle = getCalloutHandle(query);
1278 if (!ctx.fake_allocation_ && (ctx.query_->getType() != DHCPV6_CONFIRM) &&
1279 (ctx.query_->getType() != DHCPV6_INFORMATION_REQUEST) &&
1280 HooksManager::calloutsPresent(Hooks.hook_index_leases6_committed_)) {
1281 // The ScopedCalloutHandleState class which guarantees that the task
1282 // is added to the thread pool after the response is reset (if needed)
1283 // and CalloutHandle state is reset. In ST it does nothing.
1284 // A smart pointer is used to store the ScopedCalloutHandleState so that
1285 // a copy of the pointer is created by the lambda and only on the
1286 // destruction of the last reference the task is added.
1287 // In MT there are 2 cases:
1288 // 1. packet is unparked before current thread smart pointer to
1289 // ScopedCalloutHandleState is destroyed:
1290 // - the lambda uses the smart pointer to set the callout which adds the
1291 // task, but the task is added after ScopedCalloutHandleState is
1292 // destroyed, on the destruction of the last reference which is held
1293 // by the current thread.
1294 // 2. packet is unparked after the current thread smart pointer to
1295 // ScopedCalloutHandleState is destroyed:
1296 // - the current thread reference to ScopedCalloutHandleState is
1297 // destroyed, but the reference in the lambda keeps it alive until
1298 // the lambda is called and the last reference is released, at which
1299 // time the task is actually added.
1300 // Use the RAII wrapper to make sure that the callout handle state is
1301 // reset when this object goes out of scope. All hook points must do
1302 // it to prevent possible circular dependency between the callout
1303 // handle and its arguments.
1304 std::shared_ptr<ScopedCalloutHandleState> callout_handle_state =
1305 std::make_shared<ScopedCalloutHandleState>(callout_handle);
1306
1307 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
1308
1309 // Also pass the corresponding query packet as argument
1310 callout_handle->setArgument("query6", query);
1311
1312 // Pass the response packet as an argument.
1313 ScopedEnableOptionsCopy<Pkt6> rsp6_options_copy(rsp);
1314 callout_handle->setArgument("response6", rsp);
1315
1316 Lease6CollectionPtr new_leases(new Lease6Collection());
1317 if (!ctx.new_leases_.empty()) {
1318 // Filter out reused leases as they were not committed.
1319 for (auto const& new_lease : ctx.new_leases_) {
1320 if (new_lease->reuseable_valid_lft_ == 0) {
1321 new_leases->push_back(new_lease);
1322 }
1323 }
1324 }
1325 callout_handle->setArgument("leases6", new_leases);
1326
1327 Lease6CollectionPtr deleted_leases(new Lease6Collection());
1328
1329 // Do per IA lists
1330 for (auto const& iac : ctx.ias_) {
1331 if (!iac.old_leases_.empty()) {
1332 for (auto const& old_lease : iac.old_leases_) {
1333 if (ctx.new_leases_.empty()) {
1334 deleted_leases->push_back(old_lease);
1335 continue;
1336 }
1337 bool in_new = false;
1338 for (auto const& new_lease : ctx.new_leases_) {
1339 if ((new_lease->addr_ == old_lease->addr_) &&
1340 ((new_lease->type_ != Lease::TYPE_PD) ||
1341 (new_lease->prefixlen_ == old_lease->prefixlen_))) {
1342 in_new = true;
1343 break;
1344 }
1345 }
1346 if (!in_new) {
1347 deleted_leases->push_back(old_lease);
1348 }
1349 }
1350 }
1351 }
1352 callout_handle->setArgument("deleted_leases6", deleted_leases);
1353
1354 // Get the parking limit. Parsing should ensure the value is present.
1355 uint32_t parked_packet_limit = 0;
1357 getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT);
1358 if (ppl) {
1359 parked_packet_limit = ppl->intValue();
1360 }
1361
1362 if (parked_packet_limit) {
1363 auto const& parking_lot = ServerHooks::getServerHooks().
1364 getParkingLotPtr("leases6_committed");
1365 if (parking_lot && (parking_lot->size() >= parked_packet_limit)) {
1366 // We can't park it so we're going to throw it on the floor.
1369 .arg(parked_packet_limit)
1370 .arg(query->getLabel());
1371 StatsMgr::instance().addValue("pkt6-queue-full",
1372 static_cast<int64_t>(1));
1373 StatsMgr::instance().addValue("pkt6-receive-drop",
1374 static_cast<int64_t>(1));
1375 rsp.reset();
1376 return (rsp);
1377 }
1378 }
1379
1380 // We proactively park the packet. We'll unpark it without invoking
1381 // the callback (i.e. drop) unless the callout status is set to
1382 // NEXT_STEP_PARK. Otherwise the callback we bind here will be
1383 // executed when the hook library unparks the packet.
1384 ConstSubnet6Ptr subnet = ctx.subnet_;
1385 HooksManager::park("leases6_committed", query,
1386 [this, callout_handle, query, rsp, callout_handle_state, subnet]() mutable {
1387 if (MultiThreadingMgr::instance().getMode()) {
1388 typedef function<void()> CallBack;
1389 boost::shared_ptr<CallBack> call_back =
1390 boost::make_shared<CallBack>(std::bind(&Dhcpv6Srv::sendResponseNoThrow,
1391 this, callout_handle, query, rsp, subnet));
1392 callout_handle_state->on_completion_ = [call_back]() {
1394 };
1395 } else {
1396 processPacketPktSend(callout_handle, query, rsp, subnet);
1397 processPacketBufferSend(callout_handle, rsp);
1398 }
1399 });
1400
1401 try {
1402 // Call all installed callouts
1403 HooksManager::callCallouts(Hooks.hook_index_leases6_committed_,
1404 *callout_handle);
1405 } catch (...) {
1406 // Make sure we don't orphan a parked packet.
1407 HooksManager::drop("leases6_committed", query);
1408 throw;
1409 }
1410
1411 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
1413 .arg(query->getLabel());
1414 // Since the hook library(ies) are going to do the unparking, then
1415 // reset the pointer to the response to indicate to the caller that
1416 // it should return, as the packet processing will continue via
1417 // the callback.
1418 rsp.reset();
1419 } else {
1420 // Drop the park job on the packet, it isn't needed.
1421 HooksManager::drop("leases6_committed", query);
1422 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1424 .arg(query->getLabel());
1425 rsp.reset();
1426 }
1427 }
1428 }
1429
1430 // If we have a response prep it for shipment.
1431 if (rsp) {
1432 processPacketPktSend(callout_handle, query, rsp, ctx.subnet_);
1433 }
1434
1435 return (rsp);
1436}
1437
1438void
1440 Pkt6Ptr query, Pkt6Ptr& rsp,
1441 ConstSubnet6Ptr& subnet) {
1442 try {
1443 processPacketPktSend(callout_handle, query, rsp, subnet);
1444 processPacketBufferSend(callout_handle, rsp);
1445 } catch (const std::exception& e) {
1447 .arg(query->getLabel())
1448 .arg(e.what());
1449 } catch (...) {
1451 .arg(query->getLabel());
1452 }
1453}
1454
1455void
1457 Pkt6Ptr& query, Pkt6Ptr& rsp,
1458 ConstSubnet6Ptr& subnet) {
1459 query->addPktEvent("process_completed");
1460 if (!rsp) {
1461 return;
1462 }
1463
1464 // Specifies if server should do the packing
1465 bool skip_pack = false;
1466
1467 // Server's reply packet now has all options and fields set.
1468 // Options are represented by individual objects, but the
1469 // output wire data has not been prepared yet.
1470 // Execute all callouts registered for packet6_send
1471 if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
1472
1473 // Use the RAII wrapper to make sure that the callout handle state is
1474 // reset when this object goes out of scope. All hook points must do
1475 // it to prevent possible circular dependency between the callout
1476 // handle and its arguments.
1477 ScopedCalloutHandleState callout_handle_state(callout_handle);
1478
1479 // Enable copying options from the packets within hook library.
1480 ScopedEnableOptionsCopy<Pkt6> query_resp_options_copy(query, rsp);
1481
1482 // Pass incoming packet as argument
1483 callout_handle->setArgument("query6", query);
1484
1485 // Set our response
1486 callout_handle->setArgument("response6", rsp);
1487
1488 // Pass the selected subnet as an argument.
1489 callout_handle->setArgument("subnet6", subnet);
1490
1491 // Call all installed callouts
1492 HooksManager::callCallouts(Hooks.hook_index_pkt6_send_, *callout_handle);
1493
1494 // Callouts decided to skip the next processing step. The next
1495 // processing step would be to pack the packet (create wire data).
1496 // That step will be skipped if any callout sets skip flag.
1497 // It essentially means that the callout already did packing,
1498 // so the server does not have to do it again.
1499 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
1501 .arg(rsp->getLabel());
1502 skip_pack = true;
1503 }
1504
1506 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
1508 .arg(rsp->getLabel());
1509 rsp.reset();
1510 return;
1511 }
1512 }
1513
1514 if (!skip_pack) {
1515 try {
1516 LibDHCP::splitNtpServerOptions6(rsp->options_);
1517 rsp->pack();
1518 } catch (const std::exception& e) {
1520 .arg(query->getLabel())
1521 .arg(e.what());
1522 return;
1523 }
1524
1525 }
1526}
1527
1528void
1530 Pkt6Ptr& rsp) {
1531 if (!rsp) {
1532 return;
1533 }
1534
1535 try {
1536 // Now all fields and options are constructed into output wire buffer.
1537 // Option objects modification does not make sense anymore. Hooks
1538 // can only manipulate wire buffer at this stage.
1539 // Let's execute all callouts registered for buffer6_send
1540 if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
1541
1542 // Use the RAII wrapper to make sure that the callout handle state is
1543 // reset when this object goes out of scope. All hook points must do
1544 // it to prevent possible circular dependency between the callout
1545 // handle and its arguments.
1546 ScopedCalloutHandleState callout_handle_state(callout_handle);
1547
1548 // Enable copying options from the packet within hook library.
1549 ScopedEnableOptionsCopy<Pkt6> response6_options_copy(rsp);
1550
1551 // Pass incoming packet as argument
1552 callout_handle->setArgument("response6", rsp);
1553
1554 // Call callouts
1555 HooksManager::callCallouts(Hooks.hook_index_buffer6_send_,
1556 *callout_handle);
1557
1558 // Callouts decided to skip the next processing step. The next
1559 // processing step would be to parse the packet, so skip at this
1560 // stage means drop.
1561 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
1562 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
1565 .arg(rsp->getLabel());
1566 return;
1567 }
1568
1569 callout_handle->getArgument("response6", rsp);
1570 }
1571
1573 .arg(rsp->getLabel())
1574 .arg(rsp->getName())
1575 .arg(static_cast<int>(rsp->getType()))
1576 .arg(rsp->getLocalAddr().isV6Zero() ? "*" : rsp->getLocalAddr().toText())
1577 .arg(rsp->getLocalPort())
1578 .arg(rsp->getRemoteAddr())
1579 .arg(rsp->getRemotePort())
1580 .arg(rsp->getIface());
1581
1583 .arg(rsp->getLabel())
1584 .arg(rsp->getName())
1585 .arg(static_cast<int>(rsp->getType()))
1586 .arg(rsp->toText());
1587 sendPacket(rsp);
1588
1589 // Update statistics accordingly for sent packet.
1590 processStatsSent(rsp);
1591
1592 } catch (const std::exception& e) {
1594 .arg(rsp->getLabel())
1595 .arg(e.what());
1596 }
1597}
1598
1599std::string
1601 stringstream tmp;
1602
1603 OptionBuffer data = opt->getData();
1604
1605 bool colon = false;
1606 for (auto const& it : data) {
1607 if (colon) {
1608 tmp << ":";
1609 }
1610 tmp << hex << setw(2) << setfill('0') << static_cast<uint16_t>(it);
1611 if (!colon) {
1612 colon = true;
1613 }
1614 }
1615
1616 return tmp.str();
1617}
1618
1619void
1621 // Add client-id.
1622 OptionPtr clientid = question->getOption(D6O_CLIENTID);
1623 if (clientid) {
1624 answer->addOption(clientid);
1625 }
1627
1628 // If this is a relayed message, we need to copy relay information
1629 if (!question->relay_info_.empty()) {
1630 answer->copyRelayInfo(question);
1631 }
1632
1633}
1634
1635void
1637 const CfgOptionList&) {
1638 // add server-id
1639 answer->addOption(getServerID());
1640}
1641
1642void
1645 CfgOptionList& co_list) {
1646 // Firstly, host specific options.
1647 if (ctx.currentHost() && !ctx.currentHost()->getCfgOption6()->empty()) {
1648 co_list.push_back(ctx.currentHost()->getCfgOption6());
1649 }
1650
1651 // Secondly, pool specific options. Pools are defined within a subnet, so
1652 // if there is no subnet, there is nothing to do.
1653 if (ctx.subnet_) {
1654 for (auto const& resource : ctx.allocated_resources_) {
1655 PoolPtr pool =
1656 ctx.subnet_->getPool(resource.getPrefixLength() == 128 ?
1658 resource.getAddress(),
1659 false);
1660 if (pool && !pool->getCfgOption()->empty()) {
1661 co_list.push_back(pool->getCfgOption());
1662 }
1663 }
1664
1665 // Thirdly, subnet configured options.
1666 if (!ctx.subnet_->getCfgOption()->empty()) {
1667 co_list.push_back(ctx.subnet_->getCfgOption());
1668 }
1669
1670 // Fourthly, shared network specific options.
1671 SharedNetwork6Ptr network;
1672 ctx.subnet_->getSharedNetwork(network);
1673 if (network && !network->getCfgOption()->empty()) {
1674 co_list.push_back(network->getCfgOption());
1675 }
1676 }
1677
1678 // Each class in the incoming packet
1679 const ClientClasses& classes = question->getClasses();
1680 for (auto const& cclass : classes) {
1681 // Find the client class definition for this class
1683 getClientClassDictionary()->findClass(cclass);
1684 if (!ccdef) {
1685 // Not found: the class is built-in or not configured
1686 if (!isClientClassBuiltIn(cclass)) {
1688 .arg(question->getLabel())
1689 .arg(cclass);
1690 }
1691 // Skip it
1692 continue;
1693 }
1694
1695 if (ccdef->getCfgOption()->empty()) {
1696 // Skip classes which don't configure options
1697 continue;
1698 }
1699
1700 co_list.push_back(ccdef->getCfgOption());
1701 }
1702
1703 // Last global options
1704 if (!CfgMgr::instance().getCurrentCfg()->getCfgOption()->empty()) {
1705 co_list.push_back(CfgMgr::instance().getCurrentCfg()->getCfgOption());
1706 }
1707}
1708
1709void
1711 const CfgOptionList& co_list) {
1712 // Unlikely short cut
1713 if (co_list.empty()) {
1714 return;
1715 }
1716
1717 set<uint16_t> requested_opts;
1718
1719 // Client requests some options using ORO option. Try to
1720 // get this option from client's message.
1721 OptionUint16ArrayPtr option_oro = boost::dynamic_pointer_cast<
1722 OptionUint16Array>(question->getOption(D6O_ORO));
1723
1724 // Get the list of options that client requested.
1725 if (option_oro) {
1726 for (uint16_t code : option_oro->getValues()) {
1727 static_cast<void>(requested_opts.insert(code));
1728 }
1729 }
1730
1731 set<uint16_t> cancelled_opts;
1732
1733 // Iterate on the configured option list to add persistent and
1734 // cancelled options.
1735 for (auto const& copts : co_list) {
1736 const OptionContainerPtr& opts = copts->getAll(DHCP6_OPTION_SPACE);
1737 if (!opts) {
1738 continue;
1739 }
1740 // Get persistent options.
1741 const OptionContainerPersistIndex& pidx = opts->get<2>();
1742 const OptionContainerPersistRange& prange = pidx.equal_range(true);
1743 BOOST_FOREACH(auto const& desc, prange) {
1744 // Add the persistent option code to requested options.
1745 if (desc.option_) {
1746 uint16_t code = desc.option_->getType();
1747 static_cast<void>(requested_opts.insert(code));
1748 }
1749 }
1750 // Get cancelled options.
1751 const OptionContainerCancelIndex& cidx = opts->get<5>();
1752 const OptionContainerCancelRange& crange = cidx.equal_range(true);
1753 BOOST_FOREACH(auto const& desc, crange) {
1754 // Add the cancelled option code to the cancelled options.
1755 if (desc.option_) {
1756 uint16_t code = desc.option_->getType();
1757 static_cast<void>(cancelled_opts.insert(code));
1758 }
1759 }
1760 }
1761
1762 const auto& cclasses = question->getClasses();
1763 // For each requested option code get the first instance of the option
1764 // to be returned to the client.
1765 for (uint16_t opt : requested_opts) {
1766 // Skip if cancelled.
1767 if (cancelled_opts.count(opt) > 0) {
1768 continue;
1769 }
1770 // Add nothing when it is already there.
1771 // Skip special cases: D6O_VENDOR_OPTS
1772 if (opt == D6O_VENDOR_OPTS) {
1773 continue;
1774 }
1775 if (!answer->getOption(opt)) {
1776 // Iterate on the configured option list
1777 for (auto const& copts : co_list) {
1779 opt, cclasses);
1780 // Got it: add it and jump to the outer loop.
1781 if (desc.option_) {
1782 if (opt == D6O_UNICAST) {
1784 .arg(answer->getLabel());
1785 }
1786 answer->addOption(desc.option_);
1787 break;
1788 }
1789 }
1790 }
1791 }
1792
1793 // Special cases for vendor class and options which are identified
1794 // by the code/type and the vendor/enterprise id vs. the code/type only.
1795 if ((requested_opts.count(D6O_VENDOR_CLASS) > 0) &&
1796 (cancelled_opts.count(D6O_VENDOR_CLASS) == 0)) {
1797 // Keep vendor ids which are already in the response to insert
1798 // D6O_VENDOR_CLASS options at most once per vendor.
1799 set<uint32_t> vendor_ids;
1800 // Get what already exists in the response.
1801 for (auto const& opt : answer->getOptions(D6O_VENDOR_CLASS)) {
1802 OptionVendorClassPtr vendor_class;
1803 vendor_class = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
1804 if (vendor_class) {
1805 uint32_t vendor_id = vendor_class->getVendorId();
1806 static_cast<void>(vendor_ids.insert(vendor_id));
1807 }
1808 }
1809 // Iterate on the configured option list.
1810 for (auto const& copts : co_list) {
1811 for (auto const& desc : copts->getList(DHCP6_OPTION_SPACE, D6O_VENDOR_CLASS)) {
1812 // Empty or not allowed, skip i.
1813 if (!desc.option_ || !desc.allowedForClientClasses(cclasses)) {
1814 continue;
1815 }
1816 OptionVendorClassPtr vendor_class =
1817 boost::dynamic_pointer_cast<OptionVendorClass>(desc.option_);
1818 if (!vendor_class) {
1819 continue;
1820 }
1821 // Is the vendor id already in the response?
1822 uint32_t vendor_id = vendor_class->getVendorId();
1823 if (vendor_ids.count(vendor_id) > 0) {
1824 continue;
1825 }
1826 // Got it: add it.
1827 answer->addOption(desc.option_);
1828 static_cast<void>(vendor_ids.insert(vendor_id));
1829 }
1830 }
1831 }
1832
1833 if ((requested_opts.count(D6O_VENDOR_OPTS) > 0) &&
1834 (cancelled_opts.count(D6O_VENDOR_OPTS) == 0)) {
1835 // Keep vendor ids which are already in the response to insert
1836 // D6O_VENDOR_OPTS options at most once per vendor.
1837 set<uint32_t> vendor_ids;
1838 // Get what already exists in the response.
1839 for (auto const& opt : answer->getOptions(D6O_VENDOR_OPTS)) {
1840 OptionVendorPtr vendor_opts;
1841 vendor_opts = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1842 if (vendor_opts) {
1843 uint32_t vendor_id = vendor_opts->getVendorId();
1844 static_cast<void>(vendor_ids.insert(vendor_id));
1845 }
1846 }
1847 // Iterate on the configured option list
1848 for (auto const& copts : co_list) {
1849 for (auto const& desc : copts->getList(DHCP6_OPTION_SPACE, D6O_VENDOR_OPTS)) {
1850 // Empty or not allowed, skip it.
1851 if (!desc.option_ || !desc.allowedForClientClasses(cclasses)) {
1852 continue;
1853 }
1854 OptionVendorPtr vendor_opts =
1855 boost::dynamic_pointer_cast<OptionVendor>(desc.option_);
1856 if (!vendor_opts) {
1857 continue;
1858 }
1859 // Is the vendor id already in the response?
1860 uint32_t vendor_id = vendor_opts->getVendorId();
1861 if (vendor_ids.count(vendor_id) > 0) {
1862 continue;
1863 }
1864 // Append a fresh vendor option as the next method should
1865 // add suboptions to it.
1866 vendor_opts.reset(new OptionVendor(Option::V6, vendor_id));
1867 answer->addOption(vendor_opts);
1868 static_cast<void>(vendor_ids.insert(vendor_id));
1869 }
1870 }
1871 }
1872}
1873
1874void
1876 Pkt6Ptr& answer,
1878 const CfgOptionList& co_list) {
1879
1880 // Leave if there is no subnet matching the incoming packet.
1881 // There is no need to log the error message here because
1882 // it will be logged in the assignLease() when it fails to
1883 // pick the suitable subnet. We don't want to duplicate
1884 // error messages in such case.
1885 //
1886 // Also, if there's no options to possibly assign, give up.
1887 if (!ctx.subnet_ || co_list.empty()) {
1888 return;
1889 }
1890
1891 set<uint32_t> vendor_ids;
1892
1893 // The server could have provided the option using client classification or
1894 // hooks. If there're vendor info options in the response already, use them.
1895 map<uint32_t, OptionVendorPtr> vendor_rsps;
1896 for (auto const& opt : answer->getOptions(D6O_VENDOR_OPTS)) {
1897 OptionVendorPtr vendor_rsp;
1898 vendor_rsp = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1899 if (vendor_rsp) {
1900 uint32_t vendor_id = vendor_rsp->getVendorId();
1901 vendor_rsps[vendor_id] = vendor_rsp;
1902 static_cast<void>(vendor_ids.insert(vendor_id));
1903 }
1904 }
1905
1906 // Next, try to get the vendor-id from the client packet's
1907 // vendor-specific information option (17).
1908 map<uint32_t, OptionVendorPtr> vendor_reqs;
1909 for (auto const& opt : question->getOptions(D6O_VENDOR_OPTS)) {
1910 OptionVendorPtr vendor_req;
1911 vendor_req = boost::dynamic_pointer_cast<OptionVendor>(opt.second);
1912 if (vendor_req) {
1913 uint32_t vendor_id = vendor_req->getVendorId();
1914 vendor_reqs[vendor_id] = vendor_req;
1915 static_cast<void>(vendor_ids.insert(vendor_id));
1916 }
1917 }
1918
1919 // Finally, try to get the vendor-id from the client packet's vendor-class
1920 // option (16).
1921 for (auto const& opt : question->getOptions(D6O_VENDOR_CLASS)) {
1922 OptionVendorClassPtr vendor_class;
1923 vendor_class = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
1924 if (vendor_class) {
1925 uint32_t vendor_id = vendor_class->getVendorId();
1926 static_cast<void>(vendor_ids.insert(vendor_id));
1927 }
1928 }
1929
1930 // If there's no vendor option in either request or response, then there's no way
1931 // to figure out what the vendor-id values are and we give up.
1932 if (vendor_ids.empty()) {
1933 return;
1934 }
1935
1936 map<uint32_t, set<uint16_t> > requested_opts;
1937
1938 // Let's try to get ORO within that vendor-option.
1939 // This is specific to vendor-id=4491 (Cable Labs). Other vendors may have
1940 // different policies.
1942 if (vendor_reqs.count(VENDOR_ID_CABLE_LABS) > 0) {
1943 OptionVendorPtr vendor_req = vendor_reqs[VENDOR_ID_CABLE_LABS];
1944 OptionPtr oro_generic = vendor_req->getOption(DOCSIS3_V6_ORO);
1945 if (oro_generic) {
1946 // Vendor ID 4491 makes Kea look at DOCSIS3_V6_OPTION_DEFINITIONS
1947 // when parsing options. Based on that, oro_generic will have been
1948 // created as an OptionUint16Array, but might not be for other
1949 // vendor IDs.
1950 oro = boost::dynamic_pointer_cast<OptionUint16Array>(oro_generic);
1951 }
1952 if (oro) {
1953 set<uint16_t> oro_req_opts;
1954 for (uint16_t code : oro->getValues()) {
1955 static_cast<void>(oro_req_opts.insert(code));
1956 }
1957 requested_opts[VENDOR_ID_CABLE_LABS] = oro_req_opts;
1958 }
1959 }
1960
1961 map<uint32_t, set<uint16_t> > cancelled_opts;
1962 const auto& cclasses = question->getClasses();
1963
1964 // Iterate on the configured option list to add persistent and
1965 // cancelled options.
1966 for (uint32_t vendor_id : vendor_ids) {
1967 for (auto const& copts : co_list) {
1968 const OptionContainerPtr& opts = copts->getAll(vendor_id);
1969 if (!opts) {
1970 continue;
1971 }
1972 // Get persistent options.
1973 const OptionContainerPersistIndex& pidx = opts->get<2>();
1974 const OptionContainerPersistRange& prange = pidx.equal_range(true);
1975 BOOST_FOREACH(auto const& desc, prange) {
1976 if (!desc.option_) {
1977 continue;
1978 }
1979 // Add the persistent option code to requested options
1980 uint16_t code = desc.option_->getType();
1981 static_cast<void>(requested_opts[vendor_id].insert(code));
1982 }
1983 // Get cancelled options.
1984 const OptionContainerCancelIndex& cidx = opts->get<5>();
1985 const OptionContainerCancelRange& crange = cidx.equal_range(true);
1986 BOOST_FOREACH(auto const& desc, crange) {
1987 if (!desc.option_) {
1988 continue;
1989 }
1990 // Add the cancelled option code to cancelled options
1991 uint16_t code = desc.option_->getType();
1992 static_cast<void>(cancelled_opts[vendor_id].insert(code));
1993 }
1994 }
1995
1996 // If there is nothing to add don't do anything with this vendor.
1997 // This will explicitly not echo back vendor options from the request
1998 // that either correspond to a vendor not known to Kea even if the
1999 // option encapsulates data or there are no persistent options
2000 // configured for this vendor so Kea does not send any option back.
2001 if (requested_opts[vendor_id].empty()) {
2002 continue;
2003 }
2004
2005 // It's possible that the vendor opts option was inserted already
2006 // by client class or a hook. If that is so, let's use it.
2007 OptionVendorPtr vendor_rsp;
2008 if (vendor_rsps.count(vendor_id) > 0) {
2009 vendor_rsp = vendor_rsps[vendor_id];
2010 } else {
2011 vendor_rsp.reset(new OptionVendor(Option::V6, vendor_id));
2012 }
2013
2014 // Get the list of options that client requested.
2015 bool added = false;
2016
2017 for (uint16_t opt : requested_opts[vendor_id]) {
2018 if (cancelled_opts[vendor_id].count(opt) > 0) {
2019 continue;
2020 }
2021 if (!vendor_rsp->getOption(opt)) {
2022 for (auto const& copts : co_list) {
2023 OptionDescriptor desc = copts->allowedForClientClasses(vendor_id,
2024 opt, cclasses);
2025 // Got it: add it and jump to outer loop.
2026 if (desc.option_) {
2027 vendor_rsp->addOption(desc.option_);
2028 added = true;
2029 break;
2030 }
2031 }
2032 }
2033 }
2034
2035 // If we added some sub-options and the vendor opts option is not in
2036 // the response already, then add it.
2037 if (added && (vendor_rsps.count(vendor_id) == 0)) {
2038 answer->addOption(vendor_rsp);
2039 }
2040 }
2041}
2042
2043bool
2045 try {
2046 switch (pkt->getType()) {
2047 case DHCPV6_SOLICIT:
2048 case DHCPV6_REBIND:
2049 case DHCPV6_CONFIRM:
2052 return (true);
2053
2054 case DHCPV6_REQUEST:
2055 case DHCPV6_RENEW:
2056 case DHCPV6_RELEASE:
2057 case DHCPV6_DECLINE:
2059 return (true);
2060
2064 return (true);
2065
2066 default:
2069 .arg(pkt->getLabel())
2070 .arg(static_cast<int>(pkt->getType()))
2071 .arg(pkt->getIface());
2072 }
2073
2074 } catch (const RFCViolation& e) {
2076 .arg(pkt->getLabel())
2077 .arg(pkt->getName())
2078 .arg(pkt->getRemoteAddr().toText())
2079 .arg(e.what());
2080 StatsMgr::instance().addValue("pkt6-rfc-violation",
2081 static_cast<int64_t>(1));
2082 }
2083
2084 // Increase the statistic of dropped packets.
2085 StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
2086 return (false);
2087}
2088
2089void
2091 RequirementLevel serverid) {
2092 OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
2093 switch (clientid) {
2094 case MANDATORY: {
2095 if (client_ids.size() != 1) {
2096 isc_throw(RFCViolation, "Exactly 1 client-id option expected in "
2097 << pkt->getName() << ", but " << client_ids.size()
2098 << " received");
2099 }
2100 sanityCheckDUID(client_ids.begin()->second, "client-id");
2101 break;
2102 }
2103 case OPTIONAL:
2104 if (client_ids.size() > 1) {
2105 isc_throw(RFCViolation, "Too many (" << client_ids.size()
2106 << ") client-id options received in " << pkt->getName());
2107 }
2108 if (!client_ids.empty()) {
2109 sanityCheckDUID(client_ids.begin()->second, "client-id");
2110 }
2111 break;
2112
2113 case FORBIDDEN:
2114 // doesn't make sense - client-id is always allowed
2115 break;
2116 }
2117
2118 OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
2119 switch (serverid) {
2120 case FORBIDDEN:
2121 if (!server_ids.empty()) {
2122 isc_throw(RFCViolation, "Server-id option was not expected, but "
2123 << server_ids.size() << " received in " << pkt->getName());
2124 }
2125 break;
2126
2127 case MANDATORY:
2128 if (server_ids.size() != 1) {
2129 isc_throw(RFCViolation, "Invalid number of server-id options received ("
2130 << server_ids.size() << "), exactly 1 expected in message "
2131 << pkt->getName());
2132 }
2133 sanityCheckDUID(server_ids.begin()->second, "server-id");
2134 break;
2135
2136 case OPTIONAL:
2137 if (server_ids.size() > 1) {
2138 isc_throw(RFCViolation, "Too many (" << server_ids.size()
2139 << ") server-id options received in " << pkt->getName());
2140 }
2141 if (!server_ids.empty()) {
2142 sanityCheckDUID(server_ids.begin()->second, "server-id");
2143 }
2144 }
2145}
2146
2147void Dhcpv6Srv::sanityCheckDUID(const OptionPtr& opt, const std::string& opt_name) {
2148 if (!opt) {
2149 isc_throw(RFCViolation, "Unable to find expected option " << opt_name);
2150 }
2151
2152 // The client-id or server-id has to have at least 3 bytes of useful data:
2153 // two for duid type and one more for actual duid value.
2154 uint16_t len = opt->len() - opt->getHeaderLen();
2155 if (len < DUID::MIN_DUID_LEN || len > DUID::MAX_DUID_LEN) {
2156 isc_throw(RFCViolation, "Received invalid DUID for " << opt_name << ", received "
2157 << len << " byte(s). It must be at least " << DUID::MIN_DUID_LEN
2158 << " and no more than " << DUID::MAX_DUID_LEN);
2159 }
2160}
2161
2163Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question, bool& drop) {
2164 const SubnetSelector& selector = CfgSubnets6::initSelector(question);
2165
2167 getCfgSubnets6()->selectSubnet(selector);
2168
2169 // Let's execute all callouts registered for subnet6_receive
2170 if (HooksManager::calloutsPresent(Hooks.hook_index_subnet6_select_)) {
2171 CalloutHandlePtr callout_handle = getCalloutHandle(question);
2172
2173 // Use the RAII wrapper to make sure that the callout handle state is
2174 // reset when this object goes out of scope. All hook points must do
2175 // it to prevent possible circular dependency between the callout
2176 // handle and its arguments.
2177 shared_ptr<ScopedCalloutHandleState> callout_handle_state(
2178 std::make_shared<ScopedCalloutHandleState>(callout_handle));
2179
2180 // Enable copying options from the packet within hook library.
2181 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(question);
2182
2183 // Set new arguments
2184 callout_handle->setArgument("query6", question);
2185 callout_handle->setArgument("subnet6", subnet);
2186
2187 // We pass pointer to const collection for performance reasons.
2188 // Otherwise we would get a non-trivial performance penalty each
2189 // time subnet6_select is called.
2190 callout_handle->setArgument("subnet6collection",
2191 CfgMgr::instance().getCurrentCfg()->
2192 getCfgSubnets6()->getAll());
2193
2194 auto const tpl(parkingLimitExceeded("subnet6_select"));
2195 bool const exceeded(get<0>(tpl));
2196 if (exceeded) {
2197 uint32_t const limit(get<1>(tpl));
2198 // We can't park it so we're going to throw it on the floor.
2201 .arg(limit)
2202 .arg(question->getLabel());
2203 StatsMgr::instance().addValue("pkt6-queue-full",
2204 static_cast<int64_t>(1));
2205 StatsMgr::instance().addValue("pkt6-receive-drop",
2206 static_cast<int64_t>(1));
2207 return (ConstSubnet6Ptr());
2208 }
2209
2210 // We proactively park the packet.
2211 // Not MT compatible because the unparking callback can be called
2212 // before the current thread exists from this block.
2213 HooksManager::park("subnet6_select", question, [this, question, callout_handle_state]() {
2214 if (MultiThreadingMgr::instance().getMode()) {
2215 boost::shared_ptr<function<void()>> callback(
2216 boost::make_shared<function<void()>>([this, question]() mutable {
2218 }));
2219 callout_handle_state->on_completion_ = [callback]() {
2221 };
2222 } else {
2224 }
2225 });
2226
2227 // Call user (and server-side) callouts
2228 try {
2229 HooksManager::callCallouts(Hooks.hook_index_subnet6_select_,
2230 *callout_handle);
2231 } catch (...) {
2232 // Make sure we don't orphan a parked packet.
2233 HooksManager::drop("subnet6_select", question);
2234 throw;
2235 }
2236
2237 // Callouts parked the packet. Same as drop but callouts will resume
2238 // processing or drop the packet later.
2239 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
2241 .arg(question->getLabel());
2242 drop = true;
2243 return (ConstSubnet6Ptr());
2244 } else {
2245 HooksManager::drop("subnet6_select", question);
2246 }
2247
2248 // Callouts decided to skip this step. This means that no
2249 // subnet will be selected. Packet processing will continue,
2250 // but it will be severely limited (i.e. only global options
2251 // will be assigned)
2252 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2254 .arg(question->getLabel());
2255 return (ConstSubnet6Ptr());
2256 }
2257
2258 // Callouts decided to drop the packet. It is a superset of the
2259 // skip case so no subnet will be selected.
2260 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
2262 .arg(question->getLabel());
2263 drop = true;
2264 return (ConstSubnet6Ptr());
2265 }
2266
2267 // Use whatever subnet was specified by the callout
2268 callout_handle->getArgument("subnet6", subnet);
2269 }
2270
2271 if (subnet) {
2272 // Log at higher debug level that subnet has been found.
2274 .arg(question->getLabel())
2275 .arg(subnet->getID());
2276 // Log detailed information about the selected subnet at the
2277 // lower debug level.
2279 .arg(question->getLabel())
2280 .arg(subnet->toText());
2281
2282 } else {
2284 .arg(question->getLabel());
2285 }
2286
2287 return (subnet);
2288}
2289
2290void
2291Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
2293 // Save the originally selected subnet.
2294 ConstSubnet6Ptr orig_subnet = ctx.subnet_;
2295
2296 // We need to allocate addresses for all IA_NA options in the client's
2297 // question (i.e. SOLICIT or REQUEST) message. IA_TA options are ignored.
2298
2299 // For the lease allocation it is critical that the client has sent
2300 // DUID. There is no need to check for the presence of the DUID here
2301 // because we have already checked it in the sanityCheck().
2302
2303 // Now that we have all information about the client, let's iterate over all
2304 // received options and handle IA_NA options one by one and store our
2305 // responses in answer message (ADVERTISE or REPLY).
2306 for (auto const& opt : question->options_) {
2307 switch (opt.second->getType()) {
2308 case D6O_IA_NA: {
2309 OptionPtr answer_opt = assignIA_NA(question, ctx,
2310 boost::dynamic_pointer_cast<
2311 Option6IA>(opt.second));
2312 if (answer_opt) {
2313 answer->addOption(answer_opt);
2314 }
2315 break;
2316 }
2317 case D6O_IA_PD: {
2318 OptionPtr answer_opt = assignIA_PD(question, ctx,
2319 boost::dynamic_pointer_cast<
2320 Option6IA>(opt.second));
2321 if (answer_opt) {
2322 answer->addOption(answer_opt);
2323 }
2324 break;
2325 }
2326 default:
2327 break;
2328 }
2329 }
2330
2331 // Need to check for pool-level DDNS parameters and if the
2332 // subnet was modified by the allocation engine, there are things
2333 // we need to do either case.
2334 checkPostAssignmentChanges(question, answer, ctx, orig_subnet);
2335}
2336
2337void
2338Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
2341 DdnsParamsPtr ddns_params = ctx.getDdnsParams();
2342
2343 // Get Client FQDN Option from the client's message. If this option hasn't
2344 // been included, do nothing.
2345 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
2346 Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
2347 if (!fqdn) {
2348 if (ddns_params->getEnableUpdates() &&
2349 (ddns_params->getReplaceClientNameMode() == D2ClientConfig::RCM_ALWAYS ||
2350 ddns_params->getReplaceClientNameMode() == D2ClientConfig::RCM_WHEN_NOT_PRESENT)) {
2351 // Fabricate an empty "client" FQDN with flags requesting
2352 // the server do all the updates. The flags will get modified
2353 // below according the configuration options, the name will
2354 // be supplied later on.
2358 .arg(question->getLabel());
2359 } else {
2360 // No FQDN so get the lease hostname from the host reservation if
2361 // there is one.
2362 if (ctx.currentHost()) {
2363 ctx.hostname_ = ctx.currentHost()->getHostname();
2364 }
2365
2366 return;
2367 }
2368 }
2369
2371 .arg(question->getLabel())
2372 .arg(fqdn->toText());
2373
2374 // Create the DHCPv6 Client FQDN Option to be included in the server's
2375 // response to a client.
2376 Option6ClientFqdnPtr fqdn_resp(new Option6ClientFqdn(*fqdn));
2377
2378 // Set the server S, N, and O flags based on client's flags and
2379 // current configuration.
2380 d2_mgr.adjustFqdnFlags<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
2381
2382 // Get DDNS update direction flags
2384 ctx.rev_dns_update_);
2385
2386 // If there's a reservation and it has a hostname specified, use it!
2387 if (ctx.currentHost() && !ctx.currentHost()->getHostname().empty()) {
2388 // Add the qualifying suffix.
2389 // After #3765, this will only occur if the suffix is not empty.
2390 fqdn_resp->setDomainName(d2_mgr.qualifyName(ctx.currentHost()->getHostname(),
2391 *ddns_params, true),
2393 } else {
2394 // Adjust the domain name based on domain name value and type sent by
2395 // the client and current configuration.
2396 try {
2397 d2_mgr.adjustDomainName<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
2398 } catch(const FQDNScrubbedEmpty& scrubbed) {
2400 .arg(question->getLabel())
2401 .arg(scrubbed.what());
2402 return;
2403 }
2404 }
2405
2406 // Once we have the FQDN setup to use it for the lease hostname. This
2407 // only gets replaced later if the FQDN is to be generated from the address.
2408 ctx.hostname_ = fqdn_resp->getDomainName();
2409
2410 // The FQDN has been processed successfully. Let's append it to the
2411 // response to be sent to a client. Note that the Client FQDN option is
2412 // always sent back to the client if Client FQDN was included in the
2413 // client's message.
2415 .arg(question->getLabel())
2416 .arg(fqdn_resp->toText());
2417 answer->addOption(fqdn_resp);
2418
2419 // Optionally, call a hook that may override the decisions made
2420 // earlier.
2421 if (HooksManager::calloutsPresent(Hooks.hook_index_ddns6_update_)) {
2422 CalloutHandlePtr callout_handle = getCalloutHandle(question);
2423
2424 // Use the RAII wrapper to make sure that the callout handle state is
2425 // reset when this object goes out of scope. All hook points must do
2426 // it to prevent possible circular dependency between the callout
2427 // handle and its arguments.
2428 ScopedCalloutHandleState callout_handle_state(callout_handle);
2429
2430 // Setup the callout arguments.
2431 ConstSubnet6Ptr subnet = ctx.subnet_;
2432 callout_handle->setArgument("query6", question);
2433 callout_handle->setArgument("response6", answer);
2434 callout_handle->setArgument("subnet6", subnet);
2435 callout_handle->setArgument("hostname", ctx.hostname_);
2436 callout_handle->setArgument("fwd-update", ctx.fwd_dns_update_);
2437 callout_handle->setArgument("rev-update", ctx.rev_dns_update_);
2438 callout_handle->setArgument("ddns-params", ddns_params);
2439
2440 // Call callouts
2441 HooksManager::callCallouts(Hooks.hook_index_ddns6_update_, *callout_handle);
2442
2443 // Let's get the parameters returned by hook.
2444 string hook_hostname;
2445 bool hook_fwd_dns_update;
2446 bool hook_rev_dns_update;
2447 callout_handle->getArgument("hostname", hook_hostname);
2448 callout_handle->getArgument("fwd-update", hook_fwd_dns_update);
2449 callout_handle->getArgument("rev-update", hook_rev_dns_update);
2450
2451 // If there's anything changed by the hook, log it and then update the parameters
2452 if ((ctx.hostname_ != hook_hostname) || (ctx.fwd_dns_update_!= hook_fwd_dns_update) ||
2453 (ctx.rev_dns_update_ != hook_rev_dns_update)) {
2455 .arg(ctx.hostname_).arg(hook_hostname)
2456 .arg(ctx.fwd_dns_update_).arg(hook_fwd_dns_update)
2457 .arg(ctx.rev_dns_update_).arg(hook_rev_dns_update);
2458
2459 // Update the FQDN option in the response.
2460 fqdn_resp = boost::dynamic_pointer_cast<Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
2461 if (fqdn) {
2462 fqdn_resp->setDomainName(hook_hostname, Option6ClientFqdn::FULL);
2463 if (!(hook_fwd_dns_update || hook_rev_dns_update)) {
2464 // Hook disabled updates, Set flags back to client accordingly.
2465 fqdn_resp->setFlag(Option6ClientFqdn::FLAG_S, 0);
2466 fqdn_resp->setFlag(Option6ClientFqdn::FLAG_N, 1);
2467 }
2468 }
2469
2470 ctx.hostname_ = hook_hostname;
2471 ctx.fwd_dns_update_ = hook_fwd_dns_update;
2472 ctx.rev_dns_update_ = hook_rev_dns_update;
2473 }
2474 }
2475}
2476
2477void
2480 // Don't create NameChangeRequests if DNS updates are disabled.
2481 if (!(ctx.getDdnsParams()->getEnableUpdates())) {
2482 return;
2483 }
2484
2485 // The response message instance is always required. For instance it
2486 // holds the Client Identifier. It is a programming error if supplied
2487 // message is NULL.
2488 if (!answer) {
2489 isc_throw(Unexpected, "an instance of the object"
2490 << " encapsulating server's message must not be"
2491 << " NULL when creating DNS NameChangeRequest");
2492 }
2493
2494 // It is likely that client haven't included the FQDN option. In such case,
2495 // FQDN option will be NULL. This is valid state, so we simply return.
2496 Option6ClientFqdnPtr opt_fqdn = boost::dynamic_pointer_cast<
2497 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
2498 if (!opt_fqdn) {
2499 return;
2500 }
2501
2502 // Get the update directions that should be performed based on our
2503 // response FQDN flags.
2504 bool do_fwd = false;
2505 bool do_rev = false;
2507 do_fwd, do_rev);
2508
2509 // Get the Client Id. It is mandatory and a function creating a response
2510 // would have thrown an exception if it was missing. Thus throwing
2511 // Unexpected if it is missing as it is a programming error.
2512 OptionPtr opt_duid = answer->getOption(D6O_CLIENTID);
2513 if (!opt_duid) {
2515 "client identifier is required when creating a new"
2516 " DNS NameChangeRequest");
2517 }
2518 DuidPtr duid = DuidPtr(new DUID(opt_duid->getData()));
2519
2520 // Get the FQDN in the on-wire format. It will be needed to compute
2521 // DHCID.
2522 OutputBuffer name_buf(1);
2523 opt_fqdn->packDomainName(name_buf);
2524 const std::vector<uint8_t>& buf_vec = name_buf.getVector();
2525 // Compute DHCID from Client Identifier and FQDN.
2526 isc::dhcp_ddns::D2Dhcid dhcid(*duid, buf_vec);
2527
2528 // Iterate over the IAContexts (there should be one per client IA processed).
2529 // For the first address in each IA_NA, create the appropriate NCR(s).
2532 NameChangeRequestPtr remove_ncr;
2533 NameChangeRequestPtr add_ncr;
2534 for (auto& ia_ctx : ctx.getIAContexts()) {
2535 if ((ia_ctx.type_ != Lease::TYPE_NA) || !ia_ctx.ia_rsp_) {
2536 continue;
2537 }
2538
2541 Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
2542 Option6IAAddr>(ia_ctx.ia_rsp_->getOption(D6O_IAADDR));
2543
2544 // We need an address to create a name-to-address mapping.
2545 // If address is missing for any reason, go to the next IA.
2546 if (!iaaddr) {
2547 continue;
2548 }
2549
2550 bool extended_only = false;
2551 // If the lease is being reused (i.e. lease caching in effect) skip it.
2552 IOAddress ia_address = iaaddr->getAddress();
2553 for (auto const& l : ia_ctx.reused_leases_) {
2554 if (l->addr_ == ia_address) {
2555 extended_only = true;
2556 break;
2557 }
2558 }
2559
2560 if (extended_only) {
2561 continue;
2562 }
2563
2564 // If the lease for iaaddr is in the list of changed leases, we need
2565 // to determine if the changes included changes to the FQDN. If so
2566 // then we may need to do a CHG_REMOVE.
2567 for (auto const& l : ia_ctx.changed_leases_) {
2568 if (l->addr_ == ia_address) {
2569 // The address is the same so this must be renewal. If we're not
2570 // always updating on renew, then we only renew if DNS info has
2571 // changed.
2572 bool ddns_changed = ((*(ctx.duid_) != *(l->duid_)) ||
2573 (l->hostname_ != opt_fqdn->getDomainName()) ||
2574 (l->fqdn_fwd_ != do_fwd) || (l->fqdn_rev_ != do_rev));
2575
2576 if ((l->reuseable_valid_lft_ > 0) ||
2577 (!ctx.getDdnsParams()->getUpdateOnRenew() && !ddns_changed)) {
2578 extended_only = true;
2579 } else {
2580 // Queue a CHG_REMOVE of the old data if it's different.
2581 // NCR will only be created if the lease hostname is not
2582 // empty and at least one of the direction flags is true
2583 if (ddns_changed) {
2584 remove_ncr = generateNCR(CHG_REMOVE, l);
2585 }
2586 }
2587
2588 break;
2589 }
2590 }
2591
2592 if (!(do_fwd || do_rev) || (extended_only)) {
2593 // Flags indicate no updates needed or it was an extension of
2594 // an existing lease with no FQDN changes. In the case of the
2595 // former, the most likely scenario is that we are honoring the
2596 // client's request that no updates be done.
2597 continue;
2598 }
2599
2600 // Create new NameChangeRequest. Use the domain name from the FQDN.
2601 // This is an FQDN included in the response to the client, so it
2602 // holds a fully qualified domain-name already (not partial).
2603 // Get the IP address from the lease.
2604 auto cr_mode = StringToConflictResolutionMode(ctx.getDdnsParams()->getConflictResolutionMode());
2605 add_ncr.reset(
2607 do_rev, opt_fqdn->getDomainName(),
2608 iaaddr->getAddress().toText(),
2609 dhcid, calculateDdnsTtl(iaaddr->getValid(),
2610 ctx.getDdnsParams()->getTtlPercent(),
2611 ctx.getDdnsParams()->getTtl(),
2612 ctx.getDdnsParams()->getTtlMin(),
2613 ctx.getDdnsParams()->getTtlMax()),
2614 cr_mode));
2615
2617 .arg(answer->getLabel())
2618 .arg(add_ncr->toText());
2619
2624 break;
2625 }
2626
2627 if (remove_ncr) {
2628 // Chain the remove and add together so they arrive in-order.
2629 remove_ncr->setNextNcr(add_ncr);
2631 } else if (add_ncr) {
2633 }
2634}
2635
2638 CfgMACSources mac_sources = CfgMgr::instance().getCurrentCfg()->
2639 getMACSources().get();
2640 HWAddrPtr hwaddr;
2641 for (auto const& it : mac_sources) {
2642 hwaddr = pkt->getMAC(it);
2643 if (hwaddr) {
2644 return (hwaddr);
2645 }
2646 }
2647 return (hwaddr);
2648}
2649
2652 const Lease6Ptr& lease) {
2653
2654 // Search the reservation the prefix is from.
2655 ConstHostPtr host = ctx.currentHost();
2656 if (host) {
2657 IPv6ResrvRange resvs = host->getIPv6Reservations(IPv6Resrv::TYPE_PD);
2658 BOOST_FOREACH(auto const& resv, resvs) {
2659 if ((resv.second.getPrefix() == lease->addr_) &&
2660 (resv.second.getPrefixLen() == lease->prefixlen_)) {
2661 return (resv.second.getPDExclude());
2662 }
2663 }
2664 }
2665
2666 // Search the pool the address is from.
2667 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2668 Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
2669 subnet->getPool(Lease::TYPE_PD, lease->addr_));
2670 if (pool) {
2671 return (pool->getPrefixExcludeOption());
2672 }
2673 return (OptionPtr());
2674}
2675
2679 boost::shared_ptr<Option6IA> ia) {
2680
2681 // Check if the client sent us a hint in his IA_NA. Clients may send an
2682 // address in their IA_NA options as a suggestion (e.g. the last address
2683 // they used before).
2684 Option6IAAddrPtr hint_opt =
2685 boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
2687 if (hint_opt) {
2688 hint = hint_opt->getAddress();
2689 }
2690
2691 if (ctx.fake_allocation_) {
2693 .arg(query->getLabel())
2694 .arg(ia->getIAID())
2695 .arg(hint_opt ? hint.toText() : "(no hint)");
2696 } else {
2698 .arg(query->getLabel())
2699 .arg(ia->getIAID())
2700 .arg(hint_opt ? hint.toText() : "(no hint)");
2701 }
2702
2703 // convenience values
2704 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2705
2706 // If there is no subnet selected for handling this IA_NA, the only thing left to do is
2707 // to say that we are sorry, but the user won't get an address. As a convenience, we
2708 // use a different status text to indicate that (compare to the same status code,
2709 // but different wording below)
2710 if (!subnet) {
2711 // Create an empty IA_NA option with IAID matching the request.
2712 // Note that we don't use OptionDefinition class to create this option.
2713 // This is because we prefer using a constructor of Option6IA that
2714 // initializes IAID. Otherwise we would have to use setIAID() after
2715 // creation of the option which has some performance implications.
2716 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2717
2718 // Insert status code NoAddrsAvail.
2719 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoAddrsAvail,
2720 "Server could not select subnet for"
2721 " this client"));
2722 return (ia_rsp);
2723 }
2724
2725 // Set per-IA context values.
2726 ctx.createIAContext();
2727 ctx.currentIA().iaid_ = ia->getIAID();
2728 if (hint_opt) {
2729 ctx.currentIA().addHint(hint_opt);
2730 } else {
2731 ctx.currentIA().addHint(hint);
2732 }
2734
2735 // Use allocation engine to pick a lease for this client. Allocation engine
2736 // will try to honor the hint, but it is just a hint - some other address
2737 // may be used instead. If fake_allocation is set to false, the lease will
2738 // be inserted into the LeaseMgr as well.
2739 Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
2740
2742 Lease6Ptr lease;
2743 if (!leases.empty()) {
2744 lease = *leases.begin();
2745 }
2746
2747 // Create IA_NA that we will put in the response.
2748 // Do not use OptionDefinition to create option's instance so
2749 // as we can initialize IAID using a constructor.
2750 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2751 ctx.currentIA().ia_rsp_ = ia_rsp;
2752
2753 if (lease) {
2754 // We have a lease! Let's wrap its content into IA_NA option
2755 // with IAADDR suboption.
2756 if (ctx.fake_allocation_) {
2758 .arg(query->getLabel())
2759 .arg(lease->addr_.toText())
2760 .arg(ia->getIAID());
2761 } else if (lease->reuseable_valid_lft_ == 0) {
2763 .arg(query->getLabel())
2764 .arg(lease->addr_.toText())
2765 .arg(ia->getIAID())
2766 .arg(Lease::lifetimeToText(lease->valid_lft_));
2767 } else {
2768 lease->valid_lft_ = lease->reuseable_valid_lft_;
2769 lease->preferred_lft_ = lease->reuseable_preferred_lft_;
2770 ctx.currentIA().reused_leases_.push_back(lease);
2772 .arg(query->getLabel())
2773 .arg(lease->addr_.toText())
2774 .arg(ia->getIAID())
2775 .arg(Lease::lifetimeToText(lease->valid_lft_));
2776
2777 // Increment the reuse statistics.
2778 StatsMgr::instance().addValue("v6-ia-na-lease-reuses", int64_t(1));
2779 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
2780 "v6-ia-na-lease-reuses"),
2781 int64_t(1));
2782 }
2784 .arg(query->getLabel())
2785 .arg(ia->getIAID())
2786 .arg(lease->toText());
2787
2788 // Set the values for T1 and T2.
2789 setTeeTimes(lease->preferred_lft_, subnet, ia_rsp);
2790
2791 Option6IAAddrPtr addr(new Option6IAAddr(D6O_IAADDR, lease->addr_,
2792 lease->preferred_lft_,
2793 lease->valid_lft_));
2794 ia_rsp->addOption(addr);
2795
2796 // It would be possible to insert status code=0(success) as well,
2797 // but this is considered waste of bandwidth as absence of status
2798 // code is considered a success.
2799
2800 } else {
2801 // Allocation engine did not allocate a lease. The engine logged
2802 // cause of that failure. The only thing left is to insert
2803 // status code to pass the sad news to the client.
2804
2807 .arg(query->getLabel())
2808 .arg(ia->getIAID());
2809
2810 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2812 "Sorry, no address could be"
2813 " allocated."));
2814 }
2815 return (ia_rsp);
2816}
2817
2821 boost::shared_ptr<Option6IA> ia) {
2822
2823 // Check if the client sent us a hint in his IA_PD. Clients may send an
2824 // address in their IA_PD options as a suggestion (e.g. the last address
2825 // they used before). While the hint consists of a full prefix (prefix +
2826 // length), getting just the prefix is sufficient to identify a lease.
2827 Option6IAPrefixPtr hint_opt =
2828 boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
2830 if (hint_opt) {
2831 hint = hint_opt->getAddress();
2832 }
2833
2834 if (ctx.fake_allocation_) {
2836 .arg(query->getLabel())
2837 .arg(ia->getIAID())
2838 .arg(hint_opt ? hint.toText() : "(no hint)");
2839 } else {
2841 .arg(query->getLabel())
2842 .arg(ia->getIAID())
2843 .arg(hint_opt ? hint.toText() : "(no hint)");
2844 }
2845
2846 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2847
2848 // Create IA_PD that we will put in the response.
2849 // Do not use OptionDefinition to create option's instance so
2850 // as we can initialize IAID using a constructor.
2851 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
2852
2853 // If there is no subnet selected for handling this IA_PD, the only thing
2854 // left to do is to say that we are sorry, but the user won't get an address.
2855 // As a convenience, we use a different status text to indicate that
2856 // (compare to the same status code, but different wording below)
2857 if (!subnet) {
2858
2859 // Insert status code NoPrefixAvail.
2860 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoPrefixAvail,
2861 "Sorry, no subnet available."));
2862 return (ia_rsp);
2863 }
2864
2865 // Set per-IA context values.
2866 ctx.createIAContext();
2867 ctx.currentIA().iaid_ = ia->getIAID();
2868 if (hint_opt) {
2869 ctx.currentIA().addHint(hint_opt);
2870 } else {
2871 ctx.currentIA().addHint(hint, 0);
2872 }
2874 ctx.currentIA().ia_rsp_ = ia_rsp;
2875
2876 // Use allocation engine to pick a lease for this client. Allocation engine
2877 // will try to honor the hint, but it is just a hint - some other address
2878 // may be used instead. If fake_allocation is set to false, the lease will
2879 // be inserted into the LeaseMgr as well.
2880 Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
2881
2882 if (!leases.empty()) {
2883
2884 // Need to retain the shortest preferred lease time to use
2885 // for calculating T1 and T2.
2886 uint32_t min_preferred_lft = (*leases.begin())->preferred_lft_;
2887
2888 const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
2889 for (auto const& l : leases) {
2890
2891 // We have a lease! Let's wrap its content into IA_PD option
2892 // with IAADDR suboption.
2893 if (ctx.fake_allocation_) {
2895 .arg(query->getLabel())
2896 .arg(l->addr_.toText())
2897 .arg(static_cast<int>(l->prefixlen_))
2898 .arg(ia->getIAID());
2899 } else if (l->reuseable_valid_lft_ == 0) {
2901 .arg(query->getLabel())
2902 .arg(l->addr_.toText())
2903 .arg(static_cast<int>(l->prefixlen_))
2904 .arg(ia->getIAID())
2905 .arg(Lease::lifetimeToText(l->valid_lft_));
2906 } else {
2907 l->valid_lft_ = l->reuseable_valid_lft_;
2908 l->preferred_lft_ = l->reuseable_preferred_lft_;
2910 .arg(query->getLabel())
2911 .arg(l->addr_.toText())
2912 .arg(static_cast<int>(l->prefixlen_))
2913 .arg(ia->getIAID())
2914 .arg(Lease::lifetimeToText(l->valid_lft_));
2915
2916 // Increment the reuse statistics.
2917 StatsMgr::instance().addValue("v6-ia-pd-lease-reuses", int64_t(1));
2918 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
2919 "v6-ia-pd-lease-reuses"),
2920 int64_t(1));
2921 }
2922
2923 // Check for new minimum lease time
2924 if ((l->preferred_lft_ > 0) && (min_preferred_lft > l->preferred_lft_)) {
2925 min_preferred_lft = l->preferred_lft_;
2926 }
2927
2928 boost::shared_ptr<Option6IAPrefix>
2929 addr(new Option6IAPrefix(D6O_IAPREFIX, l->addr_,
2930 l->prefixlen_, l->preferred_lft_,
2931 l->valid_lft_));
2932 ia_rsp->addOption(addr);
2933
2934 if (pd_exclude_requested) {
2935 OptionPtr pd_exclude_option = getPDExclude(ctx, l);
2936 if (pd_exclude_option) {
2937 addr->addOption(pd_exclude_option);
2938 }
2939 }
2940 }
2941
2942 // Set T1 and T2, using the shortest preferred lifetime among the leases.
2943 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
2944
2945 // It would be possible to insert status code=0(success) as well,
2946 // but this is considered waste of bandwidth as absence of status
2947 // code is considered a success.
2948
2949 } else {
2950 // Allocation engine did not allocate a lease. The engine logged
2951 // cause of that failure. The only thing left is to insert
2952 // status code to pass the sad news to the client.
2953
2956 .arg(query->getLabel())
2957 .arg(ia->getIAID());
2958
2959 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2961 "Sorry, no prefixes could"
2962 " be allocated."));
2963 }
2964 return (ia_rsp);
2965}
2966
2970 boost::shared_ptr<Option6IA> ia) {
2971
2973 .arg(query->getLabel())
2974 .arg(ia->getIAID());
2975
2976 // convenience values
2977 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2978
2979 // Create empty IA_NA option with IAID matching the request.
2980 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2981
2982 if (!subnet) {
2992 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2993 "Sorry, no known leases for this duid/iaid."));
2994 return (ia_rsp);
2995 }
2996
2997 // Set per-IA context values.
2998 ctx.createIAContext();
2999 ctx.currentIA().iaid_ = ia->getIAID();
3001 ctx.currentIA().ia_rsp_ = ia_rsp;
3002
3003 // Extract the addresses that the client is trying to obtain.
3004 OptionCollection addrs = ia->getOptions();
3005 for (auto const& it : addrs) {
3006 if (it.second->getType() != D6O_IAADDR) {
3007 continue;
3008 }
3009 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(it.second);
3010 if (!iaaddr) {
3011 // That's weird. Option code was ok, but the object type was not.
3012 // This should never happen. The only case would be with badly
3013 // mis-implemented hook libraries that insert invalid option objects.
3014 // There's no way to protect against this.
3015 continue;
3016 }
3017 ctx.currentIA().addHint(iaaddr);
3018 }
3019
3020 Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
3021
3022 // Ok, now we have the leases extended. We have:
3023 // - what the client tried to renew in ctx.hints_
3024 // - what we actually assigned in leases
3025 // - old leases that are no longer valid in ctx.old_leases_
3026
3027 // For each IA inserted by the client we have to determine what to do
3028 // about included addresses and notify the client. We will iterate over
3029 // those prefixes and remove those that we have already processed. We
3030 // don't want to remove them from the context, so we need to copy them
3031 // into temporary container.
3033
3034 // Retains the shortest valid lease time to use
3035 // for calculating T1 and T2.
3036 uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
3037
3038 // For all leases we have now, add the IAADDR with non-zero lifetimes.
3039 for (auto const& l : leases) {
3040 if (l->reuseable_valid_lft_ == 0) {
3042 .arg(query->getLabel())
3043 .arg(l->addr_.toText())
3044 .arg(ia->getIAID());
3045 } else {
3046 l->valid_lft_ = l->reuseable_valid_lft_;
3047 l->preferred_lft_ = l->reuseable_preferred_lft_;
3049 .arg(query->getLabel())
3050 .arg(l->addr_.toText())
3051 .arg(ia->getIAID())
3052 .arg(Lease::lifetimeToText(l->valid_lft_));
3053
3054 // Increment the reuse statistics.
3055 StatsMgr::instance().addValue("v6-ia-na-lease-reuses", int64_t(1));
3056 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
3057 "v6-ia-na-lease-reuses"),
3058 int64_t(1));
3059 }
3060
3062 l->addr_, l->preferred_lft_, l->valid_lft_));
3063 ia_rsp->addOption(iaaddr);
3064
3065 // Check for new minimum lease time
3066 if ((l->preferred_lft_ > 0) && (min_preferred_lft > l->preferred_lft_)) {
3067 min_preferred_lft = l->preferred_lft_;
3068 }
3069
3070 // Now remove this address from the hints list.
3071 AllocEngine::Resource hint_type(l->addr_);
3072 hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
3073 hints.end());
3074 }
3075
3076 // For the leases that we just retired, send the addresses with 0 lifetimes.
3077 for (auto const& l : ctx.currentIA().old_leases_) {
3078
3079 // Send an address with zero lifetimes only when this lease belonged to
3080 // this client. Do not send it when we're reusing an old lease that belonged
3081 // to someone else.
3082 if (equalValues(query->getClientId(), l->duid_)) {
3084 l->addr_, 0, 0));
3085 ia_rsp->addOption(iaaddr);
3086 }
3087
3088 // Now remove this address from the hints list.
3089 AllocEngine::Resource hint_type(l->addr_);
3090 hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
3091
3092 // If the new FQDN settings have changed for the lease, we need to
3093 // delete any existing FQDN records for this lease.
3094 if ((l->hostname_ != ctx.hostname_) || (l->fqdn_fwd_ != ctx.fwd_dns_update_) ||
3095 (l->fqdn_rev_ != ctx.rev_dns_update_)) {
3098 .arg(query->getLabel())
3099 .arg(l->toText())
3100 .arg(ctx.hostname_)
3101 .arg(ctx.rev_dns_update_ ? "true" : "false")
3102 .arg(ctx.fwd_dns_update_ ? "true" : "false");
3103
3104 queueNCR(CHG_REMOVE, l);
3105 }
3106 }
3107
3108 // Finally, if there are any addresses requested that we haven't dealt with
3109 // already, inform the client that he can't have them.
3110 for (auto const& hint : hints) {
3112 hint.getAddress(), 0, 0));
3113 ia_rsp->addOption(iaaddr);
3114 }
3115
3116 if (!leases.empty()) {
3117 // We allocated leases so we need to update T1 and T2.
3118 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
3119 } else {
3120 // The server wasn't able allocate new lease and renew an existing
3121 // lease. In that case, the server sends NoAddrsAvail per RFC 9915.
3122 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
3124 "Sorry, no addresses could be"
3125 " assigned at this time."));
3126 }
3127
3128 return (ia_rsp);
3129}
3130
3134 boost::shared_ptr<Option6IA> ia) {
3135
3137 .arg(query->getLabel())
3138 .arg(ia->getIAID());
3139
3140 const ConstSubnet6Ptr& subnet = ctx.subnet_;
3141 const DuidPtr& duid = ctx.duid_;
3142
3143 // Let's create a IA_PD response and fill it in later
3144 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
3145
3146 // If there is no subnet for the particular client, we can't retrieve
3147 // information about client's leases from lease database. We treat this
3148 // as no binding for the client.
3149 if (!subnet) {
3150 // Per RFC 9915, section 18.3.4, if there is no binding and we are
3151 // processing a Renew, the NoBinding status code should be returned.
3152 if (query->getType() == DHCPV6_RENEW) {
3153 // Insert status code NoBinding
3154 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3155 "Sorry, no known PD leases"
3156 " for this duid/iaid."));
3157 return (ia_rsp);
3158
3159 // Per RFC 9915, section 18.3.5, if there is no binding and we are
3160 // processing Rebind, the message has to be discarded (assuming that
3161 // the server doesn't know if the prefix in the IA_PD option is
3162 // appropriate for the client's link). The exception being thrown
3163 // here should propagate to the main loop and cause the message to
3164 // be discarded.
3165 } else {
3166
3175 isc_throw(DHCPv6DiscardMessageError, "no subnet found for the"
3176 " client sending Rebind to extend lifetime of the"
3177 " prefix (DUID=" << duid->toText() << ", IAID="
3178 << ia->getIAID() << ")");
3179 }
3180 }
3181
3182 // Set per-IA context values.
3183 ctx.createIAContext();
3184 ctx.currentIA().iaid_ = ia->getIAID();
3186 ctx.currentIA().ia_rsp_ = ia_rsp;
3187
3188 // Extract prefixes that the client is trying to renew.
3189 OptionCollection addrs = ia->getOptions();
3190 for (auto const& it : addrs) {
3191 if (it.second->getType() != D6O_IAPREFIX) {
3192 continue;
3193 }
3194 Option6IAPrefixPtr prf = boost::dynamic_pointer_cast<Option6IAPrefix>(it.second);
3195 if (!prf) {
3196 // That's weird. Option code was ok, but the object type was not.
3197 // This should never happen. The only case would be with badly
3198 // mis-implemented hook libraries that insert invalid option objects.
3199 // There's no way to protect against this.
3200 continue;
3201 }
3202
3203 // Put the client's prefix into the hints list.
3204 ctx.currentIA().addHint(prf);
3205 }
3206
3207 // Call Allocation Engine and attempt to renew leases. Number of things
3208 // may happen. Leases may be extended, revoked (if the lease is no longer
3209 // valid or reserved for someone else), or new leases may be added.
3210 // Important parameters are:
3211 // - returned container - current valid leases
3212 // - old_leases - leases that used to be, but are no longer valid
3213 // - changed_leases - leases that have FQDN changed (not really important
3214 // in PD context)
3215 Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
3216
3217 // For each IA inserted by the client we have to determine what to do
3218 // about included prefixes and notify the client. We will iterate over
3219 // those prefixes and remove those that we have already processed. We
3220 // don't want to remove them from the context, so we need to copy them
3221 // into temporary container.
3223
3224 const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
3225
3226 // Retains the shortest valid lease time to use
3227 // for calculating T1 and T2.
3228 uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
3229
3230 for (auto const& l : leases) {
3231 if (l->reuseable_valid_lft_ == 0) {
3233 .arg(query->getLabel())
3234 .arg(l->addr_.toText())
3235 .arg(static_cast<int>(l->prefixlen_))
3236 .arg(ia->getIAID());
3237 } else {
3238 l->valid_lft_ = l->reuseable_valid_lft_;
3239 l->preferred_lft_ = l->reuseable_preferred_lft_;
3241 .arg(query->getLabel())
3242 .arg(l->addr_.toText())
3243 .arg(static_cast<int>(l->prefixlen_))
3244 .arg(ia->getIAID())
3245 .arg(Lease::lifetimeToText(l->valid_lft_));
3246
3247 // Increment the reuse statistics.
3248 StatsMgr::instance().addValue("v6-ia-pd-lease-reuses", int64_t(1));
3249 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
3250 "v6-ia-pd-lease-reuses"),
3251 int64_t(1));
3252 }
3253
3255 l->addr_, l->prefixlen_,
3256 l->preferred_lft_, l->valid_lft_));
3257 ia_rsp->addOption(prf);
3258
3259 if (pd_exclude_requested) {
3260 OptionPtr pd_exclude_option = getPDExclude(ctx, l);
3261 if (pd_exclude_option) {
3262 prf->addOption(pd_exclude_option);
3263 }
3264 }
3265
3266 // Check for new minimum lease time
3267 if ((l->preferred_lft_ > 0) && (l->preferred_lft_ < min_preferred_lft)) {
3268 min_preferred_lft = l->preferred_lft_;
3269 }
3270
3271 // Now remove this prefix from the hints list.
3272 AllocEngine::Resource hint_type(l->addr_, l->prefixlen_);
3273 hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
3274 hints.end());
3275 }
3276
3278 for (auto const& l : ctx.currentIA().old_leases_) {
3279
3280 // Send a prefix with zero lifetimes only when this lease belonged to
3281 // this client. Do not send it when we're reusing an old lease that belonged
3282 // to someone else.
3283 if (equalValues(query->getClientId(), l->duid_)) {
3284 Option6IAPrefixPtr prefix(new Option6IAPrefix(D6O_IAPREFIX, l->addr_,
3285 l->prefixlen_, 0, 0));
3286 ia_rsp->addOption(prefix);
3287 }
3288
3289 // Now remove this prefix from the hints list.
3290 AllocEngine::Resource hint_type(l->addr_, l->prefixlen_);
3291 hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
3292 }
3293
3294 // Finally, if there are any prefixes requested that we haven't dealt with
3295 // already, inform the client that he can't have them.
3296 for (auto const& prefix : hints) {
3297
3298 // Send the prefix with the zero lifetimes only if the prefix
3299 // contains non-zero value. A zero value indicates that the hint was
3300 // for the prefix length.
3301 if (!prefix.getAddress().isV6Zero()) {
3302 OptionPtr prefix_opt(new Option6IAPrefix(D6O_IAPREFIX,
3303 prefix.getAddress(),
3304 prefix.getPrefixLength(),
3305 0, 0));
3306 ia_rsp->addOption(prefix_opt);
3307 }
3308 }
3309
3310 if (!leases.empty()) {
3311 // We allocated leases so we need to update T1 and T2.
3312 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
3313 } else {
3314 // All is left is to insert the status code.
3315 // The server wasn't able allocate new lease and renew an existing
3316 // lease. In that case, the server sends NoPrefixAvail per RFC 9915.
3317 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
3319 "Sorry, no prefixes could be"
3320 " assigned at this time."));
3321 }
3322
3323 return (ia_rsp);
3324}
3325
3326void
3329
3330 // We will try to extend lease lifetime for all IA options in the client's
3331 // Renew or Rebind message. IA_TA options are ignored.
3332
3333 // For the lease extension it is critical that the client has sent
3334 // DUID. There is no need to check for the presence of the DUID here
3335 // because we have already checked it in the sanityCheck().
3336
3337 // Save the originally selected subnet.
3338 ConstSubnet6Ptr orig_subnet = ctx.subnet_;
3339
3340 for (auto const& opt : query->options_) {
3341 switch (opt.second->getType()) {
3342 case D6O_IA_NA: {
3343 OptionPtr answer_opt = extendIA_NA(query, ctx,
3344 boost::dynamic_pointer_cast<
3345 Option6IA>(opt.second));
3346 if (answer_opt) {
3347 reply->addOption(answer_opt);
3348 }
3349 break;
3350 }
3351
3352 case D6O_IA_PD: {
3353 OptionPtr answer_opt = extendIA_PD(query, ctx,
3354 boost::dynamic_pointer_cast<
3355 Option6IA>(opt.second));
3356 if (answer_opt) {
3357 reply->addOption(answer_opt);
3358 }
3359 break;
3360 }
3361
3362 default:
3363 break;
3364 }
3365 }
3366
3367 // Need to check for pool-level DDNS parameters and if the
3368 // subnet was modified by the allocation engine, there are things
3369 // we need to do either case.
3370 checkPostAssignmentChanges(query, reply, ctx, orig_subnet);
3371}
3372
3373void
3376
3377 // We need to release addresses for all IA options in the client's
3378 // RELEASE message. IA_TA options are ignored.
3379
3385
3386 // Let's set the status to be success by default. We can override it with
3387 // error status if needed. The important thing to understand here is that
3388 // the global status code may be set to success only if all IA options were
3389 // handled properly. Therefore the releaseIA_NA and releaseIA_PD options
3390 // may turn the status code to some error, but can't turn it back to success.
3391 int general_status = STATUS_Success;
3392 for (auto const& opt : release->options_) {
3393 Lease6Ptr old_lease;
3394 switch (opt.second->getType()) {
3395 case D6O_IA_NA: {
3396 OptionPtr answer_opt = releaseIA_NA(ctx.duid_, release, general_status,
3397 boost::dynamic_pointer_cast<Option6IA>(opt.second),
3398 old_lease);
3399 if (answer_opt) {
3400 reply->addOption(answer_opt);
3401 }
3402 break;
3403 }
3404 case D6O_IA_PD: {
3405 OptionPtr answer_opt = releaseIA_PD(ctx.duid_, release, general_status,
3406 boost::dynamic_pointer_cast<Option6IA>(opt.second),
3407 old_lease);
3408 if (answer_opt) {
3409 reply->addOption(answer_opt);
3410 }
3411 break;
3412 }
3413 case D6O_IA_TA:
3414 default:
3415 // remaining options are stateless and thus ignored in this context
3416 ;
3417 }
3418
3419 // Store the old lease.
3420 if (old_lease) {
3421 ctx.currentIA().old_leases_.push_back(old_lease);
3422 }
3423 }
3424
3425 // Include top-level status code as well.
3426 reply->addOption(createStatusCode(*release, general_status,
3427 "Summary status for all processed IA_NAs"));
3428}
3429
3431Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
3432 int& general_status, boost::shared_ptr<Option6IA> ia,
3433 Lease6Ptr& old_lease) {
3434
3436 .arg(query->getLabel())
3437 .arg(ia->getIAID());
3438
3439 // Release can be done in one of two ways:
3440 // Approach 1: extract address from client's IA_NA and see if it belongs
3441 // to this particular client.
3442 // Approach 2: find a subnet for this client, get a lease for
3443 // this subnet/duid/iaid and check if its content matches to what the
3444 // client is asking us to release.
3445 //
3446 // This method implements approach 1.
3447
3448 // That's our response
3449 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
3450
3451 Option6IAAddrPtr release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
3452 (ia->getOption(D6O_IAADDR));
3453 if (!release_addr) {
3454 ia_rsp->addOption(createStatusCode(*query, STATUS_NoBinding,
3455 "You did not include an address in your RELEASE"));
3456 general_status = STATUS_NoBinding;
3457 return (ia_rsp);
3458 }
3459
3461 release_addr->getAddress());
3462
3463 if (!lease || (lease->state_ == Lease::STATE_REGISTERED)) {
3464 // client releasing a lease that we don't know about.
3465
3466 // Insert status code NoBinding.
3467 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3468 "Sorry, no known leases for this duid/iaid, can't release."));
3469 general_status = STATUS_NoBinding;
3470
3471 return (ia_rsp);
3472 }
3473
3474 if (!lease->duid_) {
3475 // Something is gravely wrong here. We do have a lease, but it does not
3476 // have mandatory DUID information attached. Someone was messing with our
3477 // database.
3478
3480 .arg(query->getLabel())
3481 .arg(release_addr->getAddress().toText());
3482
3483 general_status = STATUS_UnspecFail;
3484 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3485 "Database consistency check failed when trying to RELEASE"));
3486 return (ia_rsp);
3487 }
3488
3489 if (*duid != *(lease->duid_)) {
3490
3491 // Sorry, it's not your address. You can't release it.
3493 .arg(query->getLabel())
3494 .arg(release_addr->getAddress().toText())
3495 .arg(lease->duid_->toText());
3496
3497 general_status = STATUS_NoBinding;
3498 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3499 "This address does not belong to you, you can't release it"));
3500 return (ia_rsp);
3501 }
3502
3503 if (ia->getIAID() != lease->iaid_) {
3504 // This address belongs to this client, but to a different IA
3506 .arg(query->getLabel())
3507 .arg(release_addr->getAddress().toText())
3508 .arg(lease->iaid_)
3509 .arg(ia->getIAID());
3510 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3511 "This is your address, but you used wrong IAID"));
3512 general_status = STATUS_NoBinding;
3513 return (ia_rsp);
3514 }
3515
3516 // It is not necessary to check if the address matches as we used
3517 // getLease6(addr) method that is supposed to return a proper lease.
3518
3519 bool skip = false;
3520 // Execute all callouts registered for packet6_send
3521 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
3522 CalloutHandlePtr callout_handle = getCalloutHandle(query);
3523
3524 // Use the RAII wrapper to make sure that the callout handle state is
3525 // reset when this object goes out of scope. All hook points must do
3526 // it to prevent possible circular dependency between the callout
3527 // handle and its arguments.
3528 ScopedCalloutHandleState callout_handle_state(callout_handle);
3529
3530 // Enable copying options from the packet within hook library.
3531 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
3532
3533 // Delete all previous arguments
3534 callout_handle->deleteAllArguments();
3535
3536 // Pass the original packet
3537 callout_handle->setArgument("query6", query);
3538
3539 // Pass the lease to be updated
3540 callout_handle->setArgument("lease6", lease);
3541
3542 // Call all installed callouts
3543 HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
3544
3545 // Callouts decided to skip the next processing step. The next
3546 // processing step would be to send the packet, so skip at this
3547 // stage means "drop response".
3548 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3549 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3550 skip = true;
3552 .arg(query->getLabel());
3553 }
3554 }
3555
3556 // Ok, we've passed all checks. Let's release this address.
3557 bool success = false; // was the removal operation successful?
3558 bool expired = false; // explicitly expired instead of removed?
3559 auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3560
3561 // Callout didn't indicate to skip the release process. Let's release
3562 // the lease.
3563 if (!skip) {
3564 // Delete lease only if affinity is disabled.
3565 if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3566 expiration_cfg->getHoldReclaimedTime() &&
3567 lease->valid_lft_ != Lease::INFINITY_LFT) {
3568 // Expire the lease.
3569 lease->valid_lft_ = 0;
3570 lease->preferred_lft_ = 0;
3571 // Set the lease state to released to indicate that this lease
3572 // must be preserved in the database. It is particularly useful
3573 // in HA to differentiate between the leases that should be
3574 // updated in the partner's database and deleted from the partner's
3575 // database.
3576 lease->state_ = Lease6::STATE_RELEASED;
3578 expired = true;
3579 success = true;
3580 } else {
3581 success = LeaseMgrFactory::instance().deleteLease(lease);
3582 }
3583 }
3584
3585 // Here the success should be true if we removed lease successfully
3586 // and false if skip flag was set or the removal failed for whatever reason
3587
3588 if (!success) {
3589 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3590 "Server failed to release a lease"));
3591
3593 .arg(query->getLabel())
3594 .arg(lease->addr_.toText())
3595 .arg(lease->iaid_);
3596 general_status = STATUS_UnspecFail;
3597
3598 return (ia_rsp);
3599 } else {
3600 old_lease = lease;
3601
3603 .arg(query->getLabel())
3604 .arg(lease->addr_.toText())
3605 .arg(lease->iaid_);
3606
3607 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
3608 "Lease released. Thank you, please come again."));
3609
3610 if (expired) {
3612 .arg(query->getLabel())
3613 .arg(lease->addr_.toText())
3614 .arg(lease->iaid_);
3615 } else {
3617 .arg(query->getLabel())
3618 .arg(lease->addr_.toText())
3619 .arg(lease->iaid_);
3620
3621 // Check if a lease has flags indicating that the FQDN update has
3622 // been performed. If so, create NameChangeRequest which removes
3623 // the entries.
3624 queueNCR(CHG_REMOVE, lease);
3625 }
3626
3627 // Need to decrease statistic for assigned addresses.
3628 StatsMgr::instance().addValue("assigned-nas", static_cast<int64_t>(-1));
3629
3631 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-nas"),
3632 static_cast<int64_t>(-1));
3633
3634 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3635 if (subnet) {
3636 auto const& pool = subnet->getPool(Lease::TYPE_NA, lease->addr_, false);
3637 if (pool) {
3639 StatsMgr::generateName("subnet", subnet->getID(),
3640 StatsMgr::generateName("pool", pool->getID(), "assigned-nas")),
3641 static_cast<int64_t>(-1));
3642 }
3643 }
3644
3645 return (ia_rsp);
3646 }
3647}
3648
3650Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
3651 int& general_status, boost::shared_ptr<Option6IA> ia,
3652 Lease6Ptr& old_lease) {
3653 // Release can be done in one of two ways:
3654 // Approach 1: extract address from client's IA_NA and see if it belongs
3655 // to this particular client.
3656 // Approach 2: find a subnet for this client, get a lease for
3657 // this subnet/duid/iaid and check if its content matches to what the
3658 // client is asking us to release.
3659 //
3660 // This method implements approach 1.
3661
3662 // That's our response. We will fill it in as we check the lease to be
3663 // released.
3664 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
3665
3666 boost::shared_ptr<Option6IAPrefix> release_prefix =
3667 boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
3668 if (!release_prefix) {
3669 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3670 "You did not include a prefix in your RELEASE"));
3671 general_status = STATUS_NoBinding;
3672 return (ia_rsp);
3673 }
3674
3676 release_prefix->getAddress());
3677
3678 if (!lease) {
3679 // Client releasing a lease that we don't know about.
3680
3681 // Insert status code NoBinding.
3682 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3683 "Sorry, no known leases for this duid/iaid, can't release."));
3684 general_status = STATUS_NoBinding;
3685
3686 return (ia_rsp);
3687 }
3688
3689 if (!lease->duid_) {
3690 // Something is gravely wrong here. We do have a lease, but it does not
3691 // have mandatory DUID information attached. Someone was messing with our
3692 // database.
3694 .arg(query->getLabel())
3695 .arg(release_prefix->getAddress().toText())
3696 .arg(static_cast<int>(release_prefix->getLength()));
3697
3698 general_status = STATUS_UnspecFail;
3699 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3700 "Database consistency check failed when trying to RELEASE"));
3701 return (ia_rsp);
3702 }
3703
3704 if (*duid != *(lease->duid_)) {
3705 // Sorry, it's not your address. You can't release it.
3707 .arg(query->getLabel())
3708 .arg(release_prefix->getAddress().toText())
3709 .arg(static_cast<int>(release_prefix->getLength()))
3710 .arg(lease->duid_->toText());
3711
3712 general_status = STATUS_NoBinding;
3713 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3714 "This address does not belong to you, you can't release it"));
3715 return (ia_rsp);
3716 }
3717
3718 if (ia->getIAID() != lease->iaid_) {
3719 // This address belongs to this client, but to a different IA
3721 .arg(query->getLabel())
3722 .arg(release_prefix->getAddress().toText())
3723 .arg(static_cast<int>(release_prefix->getLength()))
3724 .arg(lease->iaid_)
3725 .arg(ia->getIAID());
3726 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3727 "This is your address, but you used wrong IAID"));
3728 general_status = STATUS_NoBinding;
3729 return (ia_rsp);
3730 }
3731
3732 // It is not necessary to check if the address matches as we used
3733 // getLease6(addr) method that is supposed to return a proper lease.
3734
3735 bool skip = false;
3736 // Execute all callouts registered for packet6_send
3737 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
3738 CalloutHandlePtr callout_handle = getCalloutHandle(query);
3739
3740 // Use the RAII wrapper to make sure that the callout handle state is
3741 // reset when this object goes out of scope. All hook points must do
3742 // it to prevent possible circular dependency between the callout
3743 // handle and its arguments.
3744 ScopedCalloutHandleState callout_handle_state(callout_handle);
3745
3746 // Enable copying options from the packet within hook library.
3747 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
3748
3749 // Pass the original packet
3750 callout_handle->setArgument("query6", query);
3751
3752 // Pass the lease to be updated
3753 callout_handle->setArgument("lease6", lease);
3754
3755 // Call all installed callouts
3756 HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
3757
3758 // Callouts decided to skip the next processing step. The next
3759 // processing step would be to send the packet, so skip at this
3760 // stage means "drop response".
3761 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3762 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3763 skip = true;
3765 .arg(query->getLabel());
3766 }
3767 }
3768
3769 // Ok, we've passed all checks. Let's release this prefix.
3770 bool success = false; // was the removal operation successful?
3771 bool expired = false; // explicitly expired instead of removed?
3772 auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3773
3774 // Callout didn't indicate to skip the release process. Let's release
3775 // the lease.
3776 if (!skip) {
3777 // Delete lease only if affinity is disabled.
3778 if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3779 expiration_cfg->getHoldReclaimedTime() &&
3780 lease->valid_lft_ != Lease::INFINITY_LFT) {
3781 // Expire the lease.
3782 lease->valid_lft_ = 0;
3783 lease->preferred_lft_ = 0;
3784 // Set the lease state to released to indicate that this lease
3785 // must be preserved in the database. It is particularly useful
3786 // in HA to differentiate between the leases that should be
3787 // updated in the partner's database and deleted from the partner's
3788 // database.
3789 lease->state_ = Lease6::STATE_RELEASED;
3791 expired = true;
3792 success = true;
3793 } else {
3794 success = LeaseMgrFactory::instance().deleteLease(lease);
3795 }
3796 }
3797
3798 // Here the success should be true if we removed lease successfully
3799 // and false if skip flag was set or the removal failed for whatever reason
3800
3801 if (!success) {
3802 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3803 "Server failed to release a lease"));
3804
3806 .arg(query->getLabel())
3807 .arg(lease->addr_.toText())
3808 .arg(static_cast<int>(lease->prefixlen_))
3809 .arg(lease->iaid_);
3810 general_status = STATUS_UnspecFail;
3811
3812 } else {
3813 old_lease = lease;
3814
3816 .arg(query->getLabel())
3817 .arg(lease->addr_.toText())
3818 .arg(static_cast<int>(lease->prefixlen_))
3819 .arg(lease->iaid_);
3820
3821 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
3822 "Lease released. Thank you, please come again."));
3823
3824 if (expired) {
3826 .arg(query->getLabel())
3827 .arg(lease->addr_.toText())
3828 .arg(static_cast<int>(lease->prefixlen_))
3829 .arg(lease->iaid_);
3830 } else {
3832 .arg(query->getLabel())
3833 .arg(lease->addr_.toText())
3834 .arg(static_cast<int>(lease->prefixlen_))
3835 .arg(lease->iaid_);
3836 }
3837
3838 // Need to decrease statistic for assigned prefixes.
3839 StatsMgr::instance().addValue("assigned-pds", static_cast<int64_t>(-1));
3840
3842 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"),
3843 static_cast<int64_t>(-1));
3844
3845 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3846 if (subnet) {
3847 auto const& pool = subnet->getPool(Lease::TYPE_PD, lease->addr_, false);
3848 if (pool) {
3850 StatsMgr::generateName("subnet", subnet->getID(),
3851 StatsMgr::generateName("pd-pool", pool->getID(), "assigned-pds")),
3852 static_cast<int64_t>(-1));
3853 }
3854 }
3855 }
3856
3857 return (ia_rsp);
3858}
3859
3860void
3861Dhcpv6Srv::reject(const Pkt6Ptr& query, Pkt6Ptr& answer) {
3863 .arg(query->makeLabel(query->getClientId(), 0))
3864 .arg(query->toText());
3865 // Handle IAs.
3866 for (auto const& opt : query->options_) {
3867 switch (opt.second->getType()) {
3868 case D6O_IA_NA: {
3869 OptionPtr answer_opt = rejectIA_NA(query,
3870 boost::dynamic_pointer_cast<
3871 Option6IA>(opt.second));
3872 if (answer_opt) {
3873 answer->addOption(answer_opt);
3874 }
3875 break;
3876 }
3877 case D6O_IA_PD: {
3878 OptionPtr answer_opt = rejectIA_PD(query,
3879 boost::dynamic_pointer_cast<
3880 Option6IA>(opt.second));
3881 if (answer_opt) {
3882 answer->addOption(answer_opt);
3883 }
3884 break;
3885 }
3886 default:
3887 break;
3888 }
3889 }
3890}
3891
3894 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
3895 // Insert status code NoAddrsAvail.
3896 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoAddrsAvail,
3897 "Server rejected this request"));
3898 return (ia_rsp);
3899}
3900
3903 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
3904 // Insert status code NoPrefixAvail.
3905 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoPrefixAvail,
3906 "Server rejected this request"));
3907 return (ia_rsp);
3908}
3909
3910Pkt6Ptr
3912
3913 Pkt6Ptr solicit = ctx.query_;
3914 Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
3915
3916 // Handle Rapid Commit option, if present.
3917 if (ctx.subnet_ && ctx.subnet_->getRapidCommit()) {
3918 OptionPtr opt_rapid_commit = solicit->getOption(D6O_RAPID_COMMIT);
3919 if (opt_rapid_commit) {
3920
3922 .arg(solicit->getLabel());
3923
3924 // If Rapid Commit has been sent by the client, change the
3925 // response type to Reply and include Rapid Commit option.
3926 response->setType(DHCPV6_REPLY);
3927 response->addOption(opt_rapid_commit);
3928 }
3929 }
3930
3931 // "Fake" allocation is the case when the server is processing the Solicit
3932 // message without the Rapid Commit option and advertises a lease to
3933 // the client, but doesn't commit this lease to the lease database. If
3934 // the Solicit contains the Rapid Commit option and the server is
3935 // configured to honor the Rapid Commit option, or the client has sent
3936 // the Request message, the lease will be committed to the lease
3937 // database. The type of the server's response may be used to determine
3938 // if this is the fake allocation case or not. When the server sends
3939 // Reply message it means that it is committing leases. Other message
3940 // type (Advertise) means that server is not committing leases (fake
3941 // allocation).
3942 ctx.fake_allocation_ = (response->getType() != DHCPV6_REPLY);
3943
3944 processClientFqdn(solicit, response, ctx);
3945
3946 if (solicit->inClass("REJECT")) {
3947 reject(solicit, response);
3948 } else if (MultiThreadingMgr::instance().getMode()) {
3949 // The lease reclamation cannot run at the same time.
3950 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3951 assignLeases(solicit, response, ctx);
3952 } else {
3953 assignLeases(solicit, response, ctx);
3954 }
3955
3957 // Evaluate additional classes.
3958 evaluateAdditionalClasses(solicit, ctx);
3959
3961 .arg(solicit->getLabel())
3962 .arg(solicit->getName())
3963 .arg(solicit->getClasses().toText());
3964
3965 copyClientOptions(solicit, response);
3966 CfgOptionList co_list;
3967 buildCfgOptionList(solicit, ctx, co_list);
3968 appendDefaultOptions(solicit, response, co_list);
3969 appendRequestedOptions(solicit, response, co_list);
3970 appendRequestedVendorOptions(solicit, response, ctx, co_list);
3971
3972 updateReservedFqdn(ctx, response);
3973
3974 // Only generate name change requests if sending a Reply as a result
3975 // of receiving Rapid Commit option.
3976 if (response->getType() == DHCPV6_REPLY) {
3977 createNameChangeRequests(response, ctx);
3978 }
3979
3980 return (response);
3981}
3982
3983Pkt6Ptr
3985
3986 Pkt6Ptr request = ctx.query_;
3987 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
3988
3989 processClientFqdn(request, reply, ctx);
3990
3991 if (request->inClass("REJECT")) {
3992 reject(request, reply);
3993 } else if (MultiThreadingMgr::instance().getMode()) {
3994 // The lease reclamation cannot run at the same time.
3995 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3996 assignLeases(request, reply, ctx);
3997 } else {
3998 assignLeases(request, reply, ctx);
3999 }
4000
4002 // Evaluate additional classes.
4003 evaluateAdditionalClasses(request, ctx);
4004
4006 .arg(request->getLabel())
4007 .arg(request->getName())
4008 .arg(request->getClasses().toText());
4009
4010 copyClientOptions(request, reply);
4011 CfgOptionList co_list;
4012 buildCfgOptionList(request, ctx, co_list);
4013 appendDefaultOptions(request, reply, co_list);
4014 appendRequestedOptions(request, reply, co_list);
4015 appendRequestedVendorOptions(request, reply, ctx, co_list);
4016
4017 updateReservedFqdn(ctx, reply);
4018 generateFqdn(reply, ctx);
4019 createNameChangeRequests(reply, ctx);
4020
4021 return (reply);
4022}
4023
4024Pkt6Ptr
4026
4027 Pkt6Ptr renew = ctx.query_;
4028 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
4029
4030 processClientFqdn(renew, reply, ctx);
4031
4032 if (renew->inClass("REJECT")) {
4033 reject(renew, reply);
4034 } else if (MultiThreadingMgr::instance().getMode()) {
4035 // The lease reclamation cannot run at the same time.
4036 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
4037
4038 extendLeases(renew, reply, ctx);
4039 } else {
4040 extendLeases(renew, reply, ctx);
4041 }
4042
4044 // Evaluate additional classes.
4045 evaluateAdditionalClasses(renew, ctx);
4046
4048 .arg(renew->getLabel())
4049 .arg(renew->getName())
4050 .arg(renew->getClasses().toText());
4051
4052 copyClientOptions(renew, reply);
4053 CfgOptionList co_list;
4054 buildCfgOptionList(renew, ctx, co_list);
4055 appendDefaultOptions(renew, reply, co_list);
4056 appendRequestedOptions(renew, reply, co_list);
4057 appendRequestedVendorOptions(renew, reply, ctx, co_list);
4058
4059 updateReservedFqdn(ctx, reply);
4060 generateFqdn(reply, ctx);
4061 createNameChangeRequests(reply, ctx);
4062
4063 return (reply);
4064}
4065
4066Pkt6Ptr
4068
4069 Pkt6Ptr rebind = ctx.query_;
4070 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
4071
4072 processClientFqdn(rebind, reply, ctx);
4073
4074 if (rebind->inClass("REJECT")) {
4075 reject(rebind, reply);
4076 } else if (MultiThreadingMgr::instance().getMode()) {
4077 // The lease reclamation cannot run at the same time.
4078 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
4079
4080 extendLeases(rebind, reply, ctx);
4081 } else {
4082 extendLeases(rebind, reply, ctx);
4083 }
4084
4086 // Evaluate additional classes.
4087 evaluateAdditionalClasses(rebind, ctx);
4088
4090 .arg(rebind->getLabel())
4091 .arg(rebind->getName())
4092 .arg(rebind->getClasses().toText());
4093
4094 copyClientOptions(rebind, reply);
4095 CfgOptionList co_list;
4096 buildCfgOptionList(rebind, ctx, co_list);
4097 appendDefaultOptions(rebind, reply, co_list);
4098 appendRequestedOptions(rebind, reply, co_list);
4099 appendRequestedVendorOptions(rebind, reply, ctx, co_list);
4100
4101 updateReservedFqdn(ctx, reply);
4102 generateFqdn(reply, ctx);
4103 createNameChangeRequests(reply, ctx);
4104
4105 return (reply);
4106}
4107
4108Pkt6Ptr
4110
4111 Pkt6Ptr confirm = ctx.query_;
4113 // Evaluate additional classes.
4114 evaluateAdditionalClasses(confirm, ctx);
4115
4117 .arg(confirm->getLabel())
4118 .arg(confirm->getName())
4119 .arg(confirm->getClasses().toText());
4120
4121 // Get IA_NAs from the Confirm. If there are none, the message is
4122 // invalid and must be discarded. There is nothing more to do.
4123 OptionCollection ias = confirm->getOptions(D6O_IA_NA);
4124 if (ias.empty()) {
4125 return (Pkt6Ptr());
4126 }
4127
4128 // The server sends Reply message in response to Confirm.
4129 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
4130 // Make sure that the necessary options are included.
4131 copyClientOptions(confirm, reply);
4132 CfgOptionList co_list;
4133 buildCfgOptionList(confirm, ctx, co_list);
4134 appendDefaultOptions(confirm, reply, co_list);
4135 appendRequestedOptions(confirm, reply, co_list);
4136 appendRequestedVendorOptions(confirm, reply, ctx, co_list);
4137 // Indicates if at least one address has been verified. If no addresses
4138 // are verified it means that the client has sent no IA_NA options
4139 // or no IAAddr options and that client's message has to be discarded.
4140 bool verified = false;
4141 // Check if subnet was selected for the message. If no subnet
4142 // has been selected, the client is not on link.
4143 ConstSubnetPtr subnet = ctx.subnet_;
4144
4145 // Regardless if the subnet has been selected or not, we will iterate
4146 // over the IA_NA options to check if they hold any addresses. If there
4147 // are no, the Confirm is discarded.
4148 // Check addresses in IA_NA options and make sure they are appropriate.
4149 for (auto const& ia : ias) {
4150 const OptionCollection& opts = ia.second->getOptions();
4151 for (auto const& opt : opts) {
4152 // Ignore options other than IAAddr.
4153 if (opt.second->getType() == D6O_IAADDR) {
4154 // Check that the address is in range in the subnet selected.
4155 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
4156 Option6IAAddr>(opt.second);
4157 // If there is subnet selected and the address has been included
4158 // in IA_NA, mark it verified and verify that it belongs to the
4159 // subnet.
4160 if (iaaddr) {
4161 // If at least one address is not in range, then return
4162 // the NotOnLink status code.
4163 if (subnet && !subnet->inRange(iaaddr->getAddress())) {
4164 std::ostringstream status_msg;
4165 status_msg << "Address " << iaaddr->getAddress()
4166 << " is not on link.";
4167 reply->addOption(createStatusCode(*confirm,
4169 status_msg.str()));
4170 return (reply);
4171 }
4172 verified = true;
4173 } else {
4174 isc_throw(Unexpected, "failed to cast the IA Address option"
4175 " to the Option6IAAddrPtr. This is programming"
4176 " error and should be reported");
4177 }
4178 }
4179 }
4180 }
4181
4182 // It seems that the client hasn't included any addresses in which case
4183 // the Confirm must be discarded.
4184 if (!verified) {
4185 return (Pkt6Ptr());
4186 }
4187
4188 // If there is a subnet, there were addresses in IA_NA options and the
4189 // addresses where consistent with the subnet then the client is on link.
4190 if (subnet) {
4191 // All addresses in range, so return success.
4192 reply->addOption(createStatusCode(*confirm, STATUS_Success,
4193 "All addresses are on-link"));
4194 } else {
4195 reply->addOption(createStatusCode(*confirm, STATUS_NotOnLink,
4196 "No subnet selected"));
4197 }
4198
4199 return (reply);
4200}
4201
4202Pkt6Ptr
4204
4205 Pkt6Ptr release = ctx.query_;
4207 // Evaluate additional classes.
4208 evaluateAdditionalClasses(release, ctx);
4209
4211 .arg(release->getLabel())
4212 .arg(release->getName())
4213 .arg(release->getClasses().toText());
4214
4215 // Create an empty Reply message.
4216 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
4217
4218 // Copy client options (client-id, also relay information if present)
4219 copyClientOptions(release, reply);
4220
4221 // Get the configured option list
4222 CfgOptionList co_list;
4223 // buildCfgOptionList(release, ctx, co_list);
4224 appendDefaultOptions(release, reply, co_list);
4225
4226 releaseLeases(release, reply, ctx);
4227
4230
4231 return (reply);
4232}
4233
4234Pkt6Ptr
4236
4237 Pkt6Ptr decline = ctx.query_;
4239 // Evaluate additional classes.
4240 evaluateAdditionalClasses(decline, ctx);
4241
4243 .arg(decline->getLabel())
4244 .arg(decline->getName())
4245 .arg(decline->getClasses().toText());
4246
4247 // Create an empty Reply message.
4248 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
4249
4250 // Copy client options (client-id, also relay information if present)
4251 copyClientOptions(decline, reply);
4252
4253 // Get the configured option list
4254 CfgOptionList co_list;
4255 buildCfgOptionList(decline, ctx, co_list);
4256
4257 // Include server-id
4258 appendDefaultOptions(decline, reply, co_list);
4259
4260 if (declineLeases(decline, reply, ctx)) {
4261 return (reply);
4262 } else {
4263
4264 // declineLeases returns false only if the hooks set the next step
4265 // status to DROP. We'll just doing as requested.
4266 return (Pkt6Ptr());
4267 }
4268}
4269
4270bool
4273
4274 // We need to decline addresses for all IA_NA options in the client's
4275 // DECLINE message.
4276
4277 // Let's set the status to be success by default. We can override it with
4278 // error status if needed. The important thing to understand here is that
4279 // the global status code may be set to success only if all IA options were
4280 // handled properly. Therefore the declineIA options
4281 // may turn the status code to some error, but can't turn it back to success.
4282 int general_status = STATUS_Success;
4283
4284 for (auto const& opt : decline->options_) {
4285 switch (opt.second->getType()) {
4286 case D6O_IA_NA: {
4287 OptionPtr answer_opt = declineIA(decline, ctx.duid_, general_status,
4288 boost::dynamic_pointer_cast<Option6IA>(opt.second),
4289 ctx.new_leases_);
4290 if (answer_opt) {
4291
4292 // We have an answer, let's use it.
4293 reply->addOption(answer_opt);
4294 } else {
4295
4296 // The only case when declineIA could return NULL is if one of the
4297 // hook callouts set next step status to DROP. We just need to drop
4298 // this packet.
4299 return (false);
4300 }
4301 break;
4302 }
4303 default:
4304 // We don't care for the remaining options
4305 ;
4306 }
4307 }
4308
4309 return (true);
4310}
4311
4313Dhcpv6Srv::declineIA(const Pkt6Ptr& decline, const DuidPtr& duid,
4314 int& general_status, boost::shared_ptr<Option6IA> ia,
4315 Lease6Collection& new_leases) {
4316
4318 .arg(decline->getLabel())
4319 .arg(ia->getIAID());
4320
4321 // Decline can be done in one of two ways:
4322 // Approach 1: extract address from client's IA_NA and see if it belongs
4323 // to this particular client.
4324 // Approach 2: find a subnet for this client, get a lease for
4325 // this subnet/duid/iaid and check if its content matches to what the
4326 // client is asking us to decline.
4327 //
4328 // This method implements approach 1.
4329
4330 // That's our response
4331 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
4332
4333 const OptionCollection& opts = ia->getOptions();
4334 int total_addrs = 0; // Let's count the total number of addresses.
4335 for (auto const& opt : opts) {
4336
4337 // Let's ignore nested options other than IAADDR (there shouldn't be anything
4338 // else in IA_NA in Decline message, but let's be on the safe side).
4339 if (opt.second->getType() != D6O_IAADDR) {
4340 continue;
4341 }
4342 Option6IAAddrPtr decline_addr = boost::dynamic_pointer_cast<Option6IAAddr>
4343 (opt.second);
4344 if (!decline_addr) {
4345 continue;
4346 }
4347
4348 total_addrs++;
4349
4351 decline_addr->getAddress());
4352
4353 if (!lease || lease->expired() || lease->state_ != Lease::STATE_DEFAULT) {
4354 // Client trying to decline a lease that we don't know about.
4356 .arg(decline->getLabel()).arg(decline_addr->getAddress().toText());
4357
4358 // According to RFC 9915, section 18.3.8:
4359 // "For each IA in the Decline message for which the server has no
4360 // binding information, the server adds an IA option using the IAID
4361 // from the Decline message and includes a Status Code option with
4362 // the value NoBinding in the IA option".
4363 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4364 "Server does not know about such an address."));
4365
4366 // In the same section of RFC 9915:
4367 // "The server ignores addresses not assigned to the IAs (though it may"
4368 // choose to log an error if it finds such addresses)."
4369 continue; // There may be other addresses.
4370 }
4371
4372 if (!lease->duid_) {
4373 // Something is gravely wrong here. We do have a lease, but it does not
4374 // have mandatory DUID information attached. Someone was messing with our
4375 // database.
4376
4378 .arg(decline->getLabel())
4379 .arg(decline_addr->getAddress().toText());
4380
4381 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_UnspecFail,
4382 "Database consistency check failed when attempting Decline."));
4383
4384 continue;
4385 }
4386
4387 // Ok, there's a sane lease with an address. Let's check if DUID matches first.
4388 if (*duid != *(lease->duid_)) {
4389
4390 // Sorry, it's not your address. You can't release it.
4392 .arg(decline->getLabel())
4393 .arg(decline_addr->getAddress().toText())
4394 .arg(lease->duid_->toText());
4395
4396 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4397 "This address does not belong to you, you can't decline it"));
4398
4399 continue;
4400 }
4401
4402 // Let's check if IAID matches.
4403 if (ia->getIAID() != lease->iaid_) {
4404 // This address belongs to this client, but to a different IA
4406 .arg(decline->getLabel())
4407 .arg(lease->addr_.toText())
4408 .arg(ia->getIAID())
4409 .arg(lease->iaid_);
4410 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4411 "This is your address, but you used wrong IAID"));
4412
4413 continue;
4414 }
4415
4416 // Ok, all is good. Decline this lease.
4417 if (!declineLease(decline, lease, ia_rsp)) {
4418 // declineLease returns false only when hook callouts set the next
4419 // step status to drop. We just propagate the bad news here.
4420 return (OptionPtr());
4421
4422 } else {
4423 new_leases.push_back(lease);
4424 }
4425 }
4426
4427 if (total_addrs == 0) {
4428 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4429 "No addresses sent in IA_NA"));
4430 general_status = STATUS_NoBinding;
4431 }
4432
4433 return (ia_rsp);
4434}
4435
4436void
4437Dhcpv6Srv::setStatusCode(boost::shared_ptr<isc::dhcp::Option6IA>& container,
4438 const OptionPtr& status) {
4439 // Let's delete any old status code we may have.
4440 container->delOption(D6O_STATUS_CODE);
4441
4442 container->addOption(status);
4443}
4444
4445bool
4446Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
4447 boost::shared_ptr<Option6IA> ia_rsp) {
4448 // We do not want to decrease the assigned-nas at this time. While
4449 // technically a declined address is no longer allocated, the
4450 // primary usage of the assigned-nas statistic is to monitor pool
4451 // utilization. Most people would forget to include declined-addresses
4452 // in the calculation, and simply do assigned-nas/total-nas. This
4453 // would have a bias towards under-representing pool utilization,
4454 // if we decreased allocated immediately after receiving DHCPDECLINE,
4455 // rather than later when we recover the address.
4456
4457 // Let's call lease6_decline hooks if necessary.
4458 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_decline_)) {
4459 CalloutHandlePtr callout_handle = getCalloutHandle(decline);
4460
4461 // Use the RAII wrapper to make sure that the callout handle state is
4462 // reset when this object goes out of scope. All hook points must do
4463 // it to prevent possible circular dependency between the callout
4464 // handle and its arguments.
4465 ScopedCalloutHandleState callout_handle_state(callout_handle);
4466
4467 // Enable copying options from the packet within hook library.
4468 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(decline);
4469
4470 // Pass the original packet
4471 callout_handle->setArgument("query6", decline);
4472
4473 // Pass the lease to be updated
4474 callout_handle->setArgument("lease6", lease);
4475
4476 // Call callouts
4477 HooksManager::callCallouts(Hooks.hook_index_lease6_decline_,
4478 *callout_handle);
4479
4480 // Callouts decided to SKIP the next processing step. The next
4481 // processing step would be to actually decline the lease, so we'll
4482 // keep the lease as is.
4483 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4485 .arg(decline->getLabel())
4486 .arg(decline->getIface())
4487 .arg(lease->addr_.toText());
4488 return (true);
4489 }
4490
4491 // Callouts decided to DROP the packet. Let's simply log it and
4492 // return false, so callers will act accordingly.
4493 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
4495 .arg(decline->getLabel())
4496 .arg(decline->getIface())
4497 .arg(lease->addr_.toText());
4498 return (false);
4499 }
4500 }
4501
4502 Lease6Ptr old_values = boost::make_shared<Lease6>(*lease);
4503
4504 // @todo: Call hooks.
4505
4506 // We need to disassociate the lease from the client. Once we move a lease
4507 // to declined state, it is no longer associated with the client in any
4508 // way.
4509 lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
4510
4511 try {
4513 } catch (const Exception& ex) {
4514 // Update failed.
4516 .arg(decline->getLabel())
4517 .arg(lease->addr_.toText())
4518 .arg(ex.what());
4519 return (false);
4520 }
4521
4522 // Check if a lease has flags indicating that the FQDN update has
4523 // been performed. If so, create NameChangeRequest which removes
4524 // the entries. This method does all necessary checks.
4525 queueNCR(CHG_REMOVE, old_values);
4526
4527 // Bump up the subnet-specific statistic.
4529 StatsMgr::generateName("subnet", lease->subnet_id_, "declined-addresses"),
4530 static_cast<int64_t>(1));
4531
4532 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
4533 if (subnet) {
4534 auto const& pool = subnet->getPool(Lease::TYPE_NA, lease->addr_, false);
4535 if (pool) {
4537 StatsMgr::generateName("subnet", subnet->getID(),
4538 StatsMgr::generateName("pool", pool->getID(), "declined-addresses")),
4539 static_cast<int64_t>(1));
4540 }
4541 }
4542
4543 // Global declined addresses counter.
4544 StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
4545
4546 LOG_INFO(lease6_logger, DHCP6_DECLINE_LEASE).arg(decline->getLabel())
4547 .arg(lease->addr_.toText()).arg(lease->valid_lft_);
4548
4549 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_Success,
4550 "Lease declined. Hopefully the next one will be better."));
4551
4552 return (true);
4553}
4554
4555Pkt6Ptr
4557
4558 Pkt6Ptr inf_request = ctx.query_;
4559 conditionallySetReservedClientClasses(inf_request, ctx);
4560 // Evaluate additional classes.
4561 evaluateAdditionalClasses(inf_request, ctx);
4562
4564 .arg(inf_request->getLabel())
4565 .arg(inf_request->getName())
4566 .arg(inf_request->getClasses().toText());
4567
4568 // Create a Reply packet, with the same trans-id as the client's.
4569 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, inf_request->getTransid()));
4570
4571 // Copy client options (client-id, also relay information if present)
4572 copyClientOptions(inf_request, reply);
4573
4574 // Build the configured option list for append methods
4575 CfgOptionList co_list;
4576 buildCfgOptionList(inf_request, ctx, co_list);
4577
4578 // Append default options, i.e. options that the server is supposed
4579 // to put in all messages it sends (server-id for now, but possibly other
4580 // options once we start supporting authentication)
4581 appendDefaultOptions(inf_request, reply, co_list);
4582
4583 // Try to assign options that were requested by the client.
4584 appendRequestedOptions(inf_request, reply, co_list);
4585
4586 // Try to assign vendor options that were requested by the client.
4587 appendRequestedVendorOptions(inf_request, reply, ctx, co_list);
4588
4589 return (reply);
4590}
4591
4592void
4594
4595 // flags are in transid
4596 // uint32_t flags = dhcp4_query->getTransid();
4597 // do nothing with DHCPV4_QUERY_FLAGS_UNICAST
4598
4599 // Get the DHCPv4 message option
4600 OptionPtr dhcp4_msg = dhcp4_query->getOption(D6O_DHCPV4_MSG);
4601 if (dhcp4_msg) {
4602 try {
4603 // Forward the whole message to the DHCPv4 server via IPC
4604 Dhcp6to4Ipc::instance().send(dhcp4_query);
4605 } catch (...) {
4606 // Assume the error was already logged
4607 return;
4608 }
4609 }
4610
4611 // This method does not return anything as we always sent back
4612 // the response via Dhcp6To4Ipc.
4613}
4614
4615Pkt6Ptr
4617 // Get the allow-address-registration flag value.
4618 // If it's false, punt.
4619 auto allow_address_registration = CfgMgr::instance().getCurrentCfg()->
4620 getConfiguredGlobal(CfgGlobals::ALLOW_ADDRESS_REGISTRATION);
4621
4622 if (allow_address_registration && !allow_address_registration->boolValue()) {
4624 .arg(ctx.query_->getLabel());
4625 StatsMgr::instance().addValue("pkt6-admin-filtered",
4626 static_cast<int64_t>(1));
4627 StatsMgr::instance().addValue("pkt6-receive-drop",
4628 static_cast<int64_t>(1));
4629 return(Pkt6Ptr());
4630 }
4631
4632 ConstSubnetPtr subnet = ctx.subnet_;
4633 // Silently ignore message which can't be localized
4634 if (!subnet) {
4635 return (Pkt6Ptr());
4636 }
4637
4638 Pkt6Ptr addr_reg_inf = ctx.query_;
4639
4640 // Get the client source address.
4641 IOAddress addr = addr_reg_inf->getRemoteAddr();
4642 // If there are some relays get the peer address of the closest relay
4643 // to the client.
4644 size_t relay_level = addr_reg_inf->relay_info_.size();
4645 if (relay_level > 0) {
4646 addr = addr_reg_inf->getRelay6PeerAddress(relay_level - 1);
4647 }
4648
4649 Option6IAAddrPtr iaaddr;
4650 Lease6Ptr old_lease;
4651 const uint32_t no_iaid = 0; /* there's no IAID in the ADDR-REG-INFORM */
4652
4653 // Check if the message is bad and shout be dropped.
4654 bool invalid = false;
4655 try {
4656 // Check there is no IA_NA option.
4657 if (addr_reg_inf->getOption(D6O_IA_NA)) {
4658 invalid = true;
4659 StatsMgr::instance().addValue("pkt6-rfc-violation",
4660 static_cast<int64_t>(1));
4661 isc_throw(RFCViolation, "unexpected IA_NA option");
4662 }
4663
4664 // Check there is no IA_TA option.
4665 if (addr_reg_inf->getOption(D6O_IA_TA)) {
4666 invalid = true;
4667 StatsMgr::instance().addValue("pkt6-rfc-violation",
4668 static_cast<int64_t>(1));
4669 isc_throw(RFCViolation, "unexpected IA_TA option");
4670 }
4671
4672 // Check there is no IA_PD option.
4673 if (addr_reg_inf->getOption(D6O_IA_PD)) {
4674 invalid = true;
4675 StatsMgr::instance().addValue("pkt6-rfc-violation",
4676 static_cast<int64_t>(1));
4677 isc_throw(RFCViolation, "unexpected IA_PD option");
4678 }
4679
4680 // Get IAADDR from the Address registration inform.
4681 // There must be one.
4682 OptionCollection addrs = addr_reg_inf->getOptions(D6O_IAADDR);
4683 if (addrs.size() != 1) {
4684 invalid = true;
4685 StatsMgr::instance().addValue("pkt6-rfc-violation",
4686 static_cast<int64_t>(1));
4687 isc_throw(RFCViolation, "Exactly 1 IAADDR option expected, but "
4688 << addrs.size() << " received");
4689 }
4690 iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(addrs.begin()->second);
4691 if (!iaaddr) {
4692 invalid = true;
4693 StatsMgr::instance().addValue("pkt6-rfc-violation",
4694 static_cast<int64_t>(1));
4695 isc_throw(Unexpected, "can't convert the IAADDR option");
4696 }
4697
4698 // Client and IADDR addresses must match.
4699 if (addr != iaaddr->getAddress()) {
4700 invalid = true;
4701 StatsMgr::instance().addValue("pkt6-rfc-violation",
4702 static_cast<int64_t>(1));
4703 isc_throw(RFCViolation, "Address mismatch: client at " << addr
4704 << " wants to register " << iaaddr->getAddress());
4705 }
4706
4707 // Should be in the subnet.
4708 if (!subnet->inRange(addr)) {
4709 StatsMgr::instance().addValue("pkt6-rfc-violation",
4710 static_cast<int64_t>(1));
4711 isc_throw(RFCViolation, "Address " << addr << " is not in subnet "
4712 << subnet->toText() << " (id " << subnet->getID() << ")");
4713 }
4714
4715 // Check if there is a lease for the address.
4717 addr);
4718
4719 // Address registration can't be mixed with standard allocation.
4720 if (old_lease && (old_lease->state_ != Lease6::STATE_REGISTERED)) {
4721 invalid = true;
4722 StatsMgr::instance().addValue("pkt6-rfc-violation",
4723 static_cast<int64_t>(1));
4724 isc_throw(RFCViolation, "Address " << addr << " already in use "
4725 << *old_lease);
4726 }
4727
4728 // Address must not be reserved.
4729 auto hosts = HostMgr::instance().getAll6(addr);
4730 if (!hosts.empty()) {
4731 invalid = true;
4732 StatsMgr::instance().addValue("pkt6-rfc-violation",
4733 static_cast<int64_t>(1));
4734 isc_throw(RFCViolation, "Address " << addr << " is reserved");
4735 }
4736 } catch (const std::exception &ex) {
4737 // Incoming processing failed.
4739 .arg(addr)
4740 .arg(ex.what());
4741 if (!invalid) {
4742 StatsMgr::instance().addValue("pkt6-processing-failed",
4743 static_cast<int64_t>(1));
4744 }
4745 StatsMgr::instance().addValue("pkt6-receive-drop",
4746 static_cast<int64_t>(1));
4747 return (Pkt6Ptr());
4748 }
4749
4750 // Check if the client is the same.
4751 if (old_lease) {
4752 if (old_lease->duid_ && (*ctx.duid_ != *(old_lease->duid_))) {
4754 .arg(addr)
4755 .arg(ctx.duid_->toText())
4756 .arg(old_lease->duid_->toText());
4757 }
4758 }
4759
4760 // Build response.
4761 Pkt6Ptr addr_reg_rep(new Pkt6(DHCPV6_ADDR_REG_REPLY,
4762 addr_reg_inf->getTransid()));
4763 addr_reg_rep->addOption(iaaddr);
4764
4765 // Set per-IA context values for DDNS.
4766 // Note the address is considered as not-temporary address.
4767 ctx.createIAContext();
4769 ctx.currentIA().iaid_ = no_iaid;
4770 Option6IAPtr ia(new Option6IA(D6O_IA_NA, no_iaid));
4771 ia->addOption(iaaddr);
4772 ctx.currentIA().ia_rsp_ = ia;
4773
4774 // Process FQDN.
4775 processClientFqdn(addr_reg_inf, addr_reg_rep, ctx);
4776
4777 Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, ctx.duid_,
4778 no_iaid, iaaddr->getPreferred(),
4779 iaaddr->getValid(), subnet->getID(),
4780 ctx.hwaddr_));
4781 lease->state_ = Lease6::STATE_REGISTERED;
4782 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4783 lease->fqdn_rev_ = ctx.rev_dns_update_;
4784 lease->hostname_ = ctx.hostname_;
4785
4786 conditionallySetReservedClientClasses(addr_reg_inf, ctx);
4787 // Evaluate additional classes.
4788 evaluateAdditionalClasses(addr_reg_inf, ctx);
4789
4791 .arg(addr_reg_inf->getLabel())
4792 .arg(addr_reg_inf->getName())
4793 .arg(addr_reg_inf->getClasses().toText());
4794
4795 copyClientOptions(addr_reg_inf, addr_reg_rep);
4796 CfgOptionList co_list;
4797 buildCfgOptionList(addr_reg_inf, ctx, co_list);
4798 // The RFC says to not do that...
4799 appendDefaultOptions(addr_reg_inf, addr_reg_rep, co_list);
4800 appendRequestedOptions(addr_reg_inf, addr_reg_rep, co_list);
4801 appendRequestedVendorOptions(addr_reg_inf, addr_reg_rep, ctx, co_list);
4802
4803 // Handle the "addr6_register" callout point.
4804 bool skip = false;
4805 if (HooksManager::calloutsPresent(Hooks.hook_index_addr6_register_)) {
4806 CalloutHandlePtr callout_handle = getCalloutHandle(addr_reg_inf);
4807 ScopedCalloutHandleState callout_handle_state(callout_handle);
4808
4809 // Pass the query6 argument.
4810 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(addr_reg_inf);
4811 callout_handle->setArgument("query6", addr_reg_inf);
4812
4813 // Pass the response6 argument.
4814 ScopedEnableOptionsCopy<Pkt6> rsp6_options_copy(addr_reg_rep);
4815 callout_handle->setArgument("response6", addr_reg_rep);
4816
4817 // Pass the address6 argument.
4818 callout_handle->setArgument("address6", addr);
4819
4820 // Pass the old_lease argument.
4821 callout_handle->setArgument("old_lease6", old_lease);
4822
4823 // Pass the new_lease argument.
4824 callout_handle->setArgument("new_lease6", lease);
4825
4826 // Call callouts
4827 HooksManager::callCallouts(Hooks.hook_index_addr6_register_, *callout_handle);
4828
4829 // Callouts decided to skip the next processing step. This means
4830 // to not perform the lease operation.
4831 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4834 .arg(addr_reg_inf->getLabel())
4835 .arg(old_lease ? "update" : "add")
4836 .arg(addr);
4837 skip = true;
4838 } else
4839 // Callouts decided to drop the next processing step. This means
4840 // cancel processing so drop the query.
4841 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
4844 .arg(addr_reg_inf->getLabel())
4845 .arg(addr);
4846 return (Pkt6Ptr());
4847 }
4848 }
4849
4850 if (!skip) {
4851 // Statefull registration.
4852 if (old_lease) {
4853 try {
4855 } catch (const std::exception& ex) {
4856 // Assume that stats and DNS were handled by someone else.
4858 .arg(addr)
4859 .arg(ex.what());
4860 return (Pkt6Ptr());
4861 }
4862 // Save the old lease for the DNS update.
4863 ctx.currentIA().changed_leases_.push_back(old_lease);
4864 // Update stats when the subnet changed.
4865 if (old_lease->subnet_id_ != lease->subnet_id_) {
4867 StatsMgr::generateName("subnet", old_lease->subnet_id_,
4868 "registered-nas"),
4869 static_cast<int64_t>(-1));
4871 StatsMgr::generateName("subnet", lease->subnet_id_,
4872 "registered-nas"),
4873 static_cast<int64_t>(1));
4875 StatsMgr::generateName("subnet", lease->subnet_id_,
4876 "cumulative-registered-nas"),
4877 static_cast<int64_t>(1));
4878 }
4879 } else {
4880 if (!LeaseMgrFactory::instance().addLease(lease)) {
4881 // Assume that stats and DNS were handled by someone else.
4883 .arg(addr);
4884 return (Pkt6Ptr());
4885 }
4886 // Update stats.
4888 StatsMgr::generateName("subnet", lease->subnet_id_,
4889 "registered-nas"),
4890 static_cast<int64_t>(1));
4892 StatsMgr::generateName("subnet", lease->subnet_id_,
4893 "cumulative-registered-nas"),
4894 static_cast<int64_t>(1));
4895 StatsMgr::instance().addValue("cumulative-registered-nas",
4896 static_cast<int64_t>(1));
4897 }
4898 // Save the new lease for the leases6_committed callout.
4899 ctx.new_leases_.push_back(lease);
4900 }
4901
4902 // Deal with FQDN.
4903 updateReservedFqdn(ctx, addr_reg_rep);
4904 generateFqdn(addr_reg_rep, ctx);
4905 createNameChangeRequests(addr_reg_rep, ctx);
4906
4907 return (addr_reg_rep);
4908}
4909
4910void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt) {
4911 OptionVendorClassPtr vclass;
4912 for (auto const& opt : pkt->getOptions(D6O_VENDOR_CLASS)) {
4913 vclass = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
4914 if (!vclass || vclass->getTuplesNum() == 0) {
4915 continue;
4916 }
4917
4918 if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) {
4920
4921 } else if (vclass->hasTuple(DOCSIS3_CLASS_EROUTER)) {
4923
4924 } else {
4925 pkt->addClass(VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText());
4926 }
4927 }
4928}
4929
4931 // All packets belong to ALL.
4932 pkt->addClass("ALL");
4933
4934 // First: built-in vendor class processing
4935 classifyByVendor(pkt);
4936
4937 // Run match expressions on classes not depending on KNOWN/UNKNOWN.
4938 evaluateClasses(pkt, false);
4939}
4940
4941void Dhcpv6Srv::evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known) {
4942 // Note getClientClassDictionary() cannot be null
4943 const ClientClassDictionaryPtr& dict =
4944 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4945 const ClientClassDefListPtr& defs_ptr = dict->getClasses();
4946 for (auto const& it : *defs_ptr) {
4947 // Note second cannot be null
4948 const ExpressionPtr& expr_ptr = it->getMatchExpr();
4949 // Nothing to do without an expression to evaluate
4950 if (!expr_ptr) {
4951 continue;
4952 }
4953 // Not the right time if only when required
4954 if (it->getAdditional()) {
4955 continue;
4956 }
4957 // Not the right pass.
4958 if (it->getDependOnKnown() != depend_on_known) {
4959 continue;
4960 }
4961 it->test(pkt, expr_ptr);
4962 }
4963}
4964
4965void
4967 const ClientClassDictionaryPtr& dict =
4968 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4969 const ClientClassDefListPtr& defs_ptr = dict->getClasses();
4970 for (auto const& def : *defs_ptr) {
4971 // Only remove evaluated classes. Other classes can be
4972 // assigned via hooks libraries and we should not remove
4973 // them because there is no way they can be added back.
4974 if (def->getMatchExpr()) {
4975 pkt->classes_.erase(def->getName());
4976 }
4977 }
4978}
4979
4980void
4982 const AllocEngine::ClientContext6& ctx) {
4983 if (ctx.currentHost() && pkt) {
4984 const ClientClasses& classes = ctx.currentHost()->getClientClasses6();
4985 for (auto const& cclass : classes) {
4986 pkt->addClass(cclass);
4987 }
4988 }
4989}
4990
4991void
4993 const AllocEngine::ClientContext6& ctx) {
4994 if (ctx.subnet_) {
4995 SharedNetwork6Ptr shared_network;
4996 ctx.subnet_->getSharedNetwork(shared_network);
4997 if (shared_network) {
4998 ConstHostPtr host = ctx.currentHost();
4999 if (host && (host->getIPv6SubnetID() != SUBNET_ID_GLOBAL)) {
5000 setReservedClientClasses(pkt, ctx);
5001 }
5002 }
5003 }
5004}
5005
5006void
5008 // Get additional classes to evaluate added elsewhere, possibly by hooks.
5009 ClientClasses classes = pkt->getAdditionalClasses();
5010 ConstSubnet6Ptr subnet = ctx.subnet_;
5011
5012 if (subnet) {
5013 // host reservation???
5014
5015 // Begin by pools
5016 for (auto const& resource : ctx.allocated_resources_) {
5017 PoolPtr pool =
5018 ctx.subnet_->getPool(resource.getPrefixLength() == 128 ?
5020 resource.getAddress(),
5021 false);
5022 if (pool) {
5023 const ClientClasses& pool_to_add = pool->getAdditionalClasses();
5024 for (auto const& cclass : pool_to_add) {
5025 classes.insert(cclass);
5026 }
5027 }
5028 }
5029
5030 // Followed by the subnet
5031 const ClientClasses& to_add = subnet->getAdditionalClasses();
5032 for (auto const& cclass : to_add) {
5033 classes.insert(cclass);
5034 }
5035
5036 // And finish by the shared-network
5037 SharedNetwork6Ptr network;
5038 subnet->getSharedNetwork(network);
5039 if (network) {
5040 const ClientClasses& net_to_add = network->getAdditionalClasses();
5041 for (auto const& cclass : net_to_add) {
5042 classes.insert(cclass);
5043 }
5044 }
5045 }
5046
5047 // Run match expressions
5048 // Note getClientClassDictionary() cannot be null
5049 const ClientClassDictionaryPtr& dict =
5050 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
5051 for (auto const& cclass : classes) {
5052 const ClientClassDefPtr class_def = dict->findClass(cclass);
5053 if (!class_def) {
5056 .arg(cclass);
5057 // Ignore it as it can't have an attached action
5058 continue;
5059 }
5060 const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
5061 // Add a class without an expression to evaluate
5062 if (!expr_ptr) {
5065 .arg(cclass);
5066 pkt->addClass(cclass);
5067 continue;
5068 }
5069 // Evaluate the expression which can return false (no match),
5070 // true (match) or raise an exception (error)
5071 try {
5072 bool status = evaluateBool(*expr_ptr, *pkt);
5074 .arg(pkt->getLabel())
5075 .arg(cclass)
5076 .arg(status ? "true" : "false");
5077 if (status) {
5078 // Matching: add the class
5079 pkt->addClass(cclass);
5080 }
5081 } catch (const Exception& ex) {
5083 .arg(pkt->getLabel())
5084 .arg(cclass)
5085 .arg(ex.what());
5086 }
5087 }
5088}
5089
5090void
5091Dhcpv6Srv::updateReservedFqdn(AllocEngine::ClientContext6& ctx,
5092 const Pkt6Ptr& answer) {
5093 if (!answer) {
5094 isc_throw(isc::Unexpected, "an instance of the object encapsulating"
5095 " a message must not be NULL when updating reserved FQDN");
5096 }
5097
5098 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>
5099 (answer->getOption(D6O_CLIENT_FQDN));
5100
5101 // If Client FQDN option is not included, there is nothing to do.
5102 if (!fqdn) {
5103 return;
5104 }
5105
5106 std::string name = fqdn->getDomainName();
5107
5108 // If there is a host reservation for this client we have to check whether
5109 // this reservation has the same hostname as the hostname currently
5110 // present in the FQDN option. If not, it indicates that the allocation
5111 // engine picked a different subnet (from within a shared network) for
5112 // reservations and we have to send this new value to the client.
5113 if (ctx.currentHost() &&
5114 !ctx.currentHost()->getHostname().empty()) {
5115 std::string new_name = CfgMgr::instance().getD2ClientMgr().
5116 qualifyName(ctx.currentHost()->getHostname(), *ctx.getDdnsParams(), true);
5117
5118 if (new_name != name) {
5119 fqdn->setDomainName(new_name, Option6ClientFqdn::FULL);
5120
5121 // Replace previous instance of Client FQDN option.
5122 answer->delOption(D6O_CLIENT_FQDN);
5123 answer->addOption(fqdn);
5124 }
5125 }
5126}
5127
5128void
5129Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer,
5130 AllocEngine::ClientContext6& ctx) {
5131 if (!answer) {
5132 isc_throw(isc::Unexpected, "an instance of the object encapsulating"
5133 " a message must not be NULL when generating FQDN");
5134 }
5135
5138
5139 // It is likely that client hasn't included the FQDN option. In such case,
5140 // FQDN option will be NULL. Also, there is nothing to do if the option
5141 // is present and conveys the non-empty FQDN.
5142 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
5143 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
5144 if (!fqdn || !fqdn->getDomainName().empty()) {
5145 return;
5146 }
5147
5148 // Get the first IA_NA acquired for the client.
5149 OptionPtr ia = answer->getOption(D6O_IA_NA);
5150 if (!ia) {
5151 return;
5152 }
5153
5154 // If it has any IAAddr with a valid lifetime > 0, use it to
5155 // generate unique FQDN.
5156 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
5157 Option6IAAddr>(ia->getOption(D6O_IAADDR));
5158 if (!iaaddr || iaaddr->getValid() == 0) {
5159 return;
5160 }
5161
5162 // Get the IPv6 address acquired by the client.
5163 IOAddress addr = iaaddr->getAddress();
5164 std::string generated_name =
5166
5168 .arg(answer->getLabel())
5169 .arg(generated_name);
5170
5171 try {
5172 // The lease has been acquired but the FQDN for this lease hasn't
5173 // been updated in the lease database. We now have new FQDN
5174 // generated, so the lease database has to be updated here.
5175 // However, never update lease database for Advertise, just send
5176 // our notion of client's FQDN in the Client FQDN option.
5177 if (answer->getType() != DHCPV6_ADVERTISE) {
5178 Lease6Ptr lease;
5179 for (auto const& l : ctx.new_leases_) {
5180 if ((l->type_ == Lease::TYPE_NA) && (l->addr_ == addr)) {
5181 lease = l;
5182 break;
5183 }
5184 }
5185 if (lease) {
5186 lease->hostname_ = generated_name;
5187 lease->reuseable_valid_lft_ = 0;
5189
5190 } else {
5191 isc_throw(isc::Unexpected, "there is no lease in the database "
5192 " for address " << addr << ", so as it is impossible"
5193 " to update FQDN data. This is a programmatic error"
5194 " as the given address is now being handed to the"
5195 " client");
5196 }
5197 }
5198 // Set the generated FQDN in the Client FQDN option.
5199 fqdn->setDomainName(generated_name, Option6ClientFqdn::FULL);
5200
5201 answer->delOption(D6O_CLIENT_FQDN);
5202 answer->addOption(fqdn);
5203 ctx.hostname_ = generated_name;
5204 } catch (const Exception& ex) {
5206 .arg(answer->getLabel())
5207 .arg(addr.toText())
5208 .arg(ex.what());
5209 }
5210}
5211
5212void
5215 if (d2_mgr.ddnsEnabled()) {
5216 // Updates are enabled, so lets start the sender, passing in
5217 // our error handler.
5218 // This may throw so wherever this is called needs to ready.
5220 this, ph::_1, ph::_2));
5221 }
5222}
5223
5224void
5227 if (d2_mgr.ddnsEnabled()) {
5228 // Updates are enabled, so lets stop the sender
5229 d2_mgr.stop();
5230 d2_mgr.stopSender();
5231 }
5232}
5233
5234void
5238 .arg(NameChangeSender::resultToText(result))
5239 .arg((ncr ? ncr->toText() : " NULL "));
5240 // We cannot communicate with kea-dhcp-ddns, suspend further updates.
5244}
5245
5246std::string
5248 std::stringstream tmp;
5249
5250 tmp << VERSION;
5251 if (extended) {
5252 tmp << " (" << SOURCE_OF_INSTALLATION << ")" << endl;
5253 tmp << "premium: " << PREMIUM_EXTENDED_VERSION << endl;
5254 tmp << "linked with:" << endl;
5255 tmp << "- " << Logger::getVersion() << endl;
5256 tmp << "- " << CryptoLink::getVersion();
5258 if (info.size()) {
5259 tmp << endl << "lease backends:";
5260 for (auto const& version : info) {
5261 tmp << endl << "- " << version;
5262 }
5263 }
5265 if (info.size()) {
5266 tmp << endl << "host backends:";
5267 for (auto const& version : info) {
5268 tmp << endl << "- " << version;
5269 }
5270 }
5272 if (info.size()) {
5273 tmp << endl << "forensic backends:";
5274 for (auto const& version : info) {
5275 tmp << endl << "- " << version;
5276 }
5277 }
5278 // @todo: more details about database runtime
5279 }
5280
5281 return (tmp.str());
5282}
5283
5284void Dhcpv6Srv::processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp) {
5285
5286 if (query->relay_info_.empty()) {
5287 // RSOO is inserted by relay agents, nothing to do here if it's
5288 // a direct message.
5289 return;
5290 }
5291
5292 // Get RSOO configuration.
5293 ConstCfgRSOOPtr cfg_rsoo = CfgMgr::instance().getCurrentCfg()->getCfgRSOO();
5294
5295 // Let's get over all relays (encapsulation levels). We need to do
5296 // it in the same order as the client packet traversed the relays.
5297 for (int i = query->relay_info_.size(); i > 0 ; --i) {
5298 OptionPtr rsoo_container = query->getRelayOption(D6O_RSOO, i - 1);
5299 if (rsoo_container) {
5300 // There are RSOO options. Let's get through them one by one
5301 // and if it's RSOO-enabled and there's no such option provided yet,
5302 // copy it to the server's response
5303 const OptionCollection& rsoo = rsoo_container->getOptions();
5304 for (auto const& opt : rsoo) {
5305
5306 // Echo option if it is RSOO enabled option and there is no such
5307 // option added yet.
5308 if (cfg_rsoo->enabled(opt.second->getType()) &&
5309 !rsp->getOption(opt.second->getType())) {
5310 rsp->addOption(opt.second);
5311 }
5312 }
5313 }
5314 }
5315}
5316
5318
5319 if (query->relay_info_.empty()) {
5320 // No relay agent
5321 return (0);
5322 }
5323
5324 // Did the last relay agent add a relay-source-port?
5325 if (query->getRelayOption(D6O_RELAY_SOURCE_PORT, 0)) {
5326 // RFC 8357 section 5.2
5327 return (query->getRemotePort());
5328 }
5329
5330 return (0);
5331}
5332
5333void Dhcpv6Srv::processStatsReceived(const Pkt6Ptr& query) {
5334 // Note that we're not bumping pkt6-received statistic as it was
5335 // increased early in the packet reception code.
5336
5337 string stat_name = "pkt6-unknown-received";
5338 switch (query->getType()) {
5339 case DHCPV6_SOLICIT:
5340 stat_name = "pkt6-solicit-received";
5341 break;
5342 case DHCPV6_ADVERTISE:
5343 // Should not happen, but let's keep a counter for it
5344 stat_name = "pkt6-advertise-received";
5345 break;
5346 case DHCPV6_REQUEST:
5347 stat_name = "pkt6-request-received";
5348 break;
5349 case DHCPV6_CONFIRM:
5350 stat_name = "pkt6-confirm-received";
5351 break;
5352 case DHCPV6_RENEW:
5353 stat_name = "pkt6-renew-received";
5354 break;
5355 case DHCPV6_REBIND:
5356 stat_name = "pkt6-rebind-received";
5357 break;
5358 case DHCPV6_REPLY:
5359 // Should not happen, but let's keep a counter for it
5360 stat_name = "pkt6-reply-received";
5361 break;
5362 case DHCPV6_RELEASE:
5363 stat_name = "pkt6-release-received";
5364 break;
5365 case DHCPV6_DECLINE:
5366 stat_name = "pkt6-decline-received";
5367 break;
5368 case DHCPV6_RECONFIGURE:
5369 stat_name = "pkt6-reconfigure-received";
5370 break;
5372 stat_name = "pkt6-infrequest-received";
5373 break;
5375 stat_name = "pkt6-dhcpv4-query-received";
5376 break;
5378 // Should not happen, but let's keep a counter for it
5379 stat_name = "pkt6-dhcpv4-response-received";
5380 break;
5382 stat_name = "pkt6-addr-reg-inform-received";
5383 break;
5385 // Should not happen, but let's keep a counter for it
5386 stat_name = "pkt6-addr-reg-reply-received";
5387 break;
5388 default:
5389 ; // do nothing
5390 }
5391
5392 StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
5393}
5394
5396 // Increase generic counter for sent packets.
5397 StatsMgr::instance().addValue("pkt6-sent", static_cast<int64_t>(1));
5398
5399 // Increase packet type specific counter for packets sent.
5400 string stat_name;
5401 switch (response->getType()) {
5402 case DHCPV6_ADVERTISE:
5403 stat_name = "pkt6-advertise-sent";
5404 break;
5405 case DHCPV6_REPLY:
5406 stat_name = "pkt6-reply-sent";
5407 break;
5409 stat_name = "pkt6-dhcpv4-response-sent";
5410 break;
5412 stat_name = "pkt6-addr-reg-reply-sent";
5413 break;
5414 default:
5415 // That should never happen
5416 return;
5417 }
5418
5419 StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
5420}
5421
5423 return (Hooks.hook_index_buffer6_send_);
5424}
5425
5426bool
5427Dhcpv6Srv::requestedInORO(const Pkt6Ptr& query, const uint16_t code) const {
5429 boost::dynamic_pointer_cast<OptionUint16Array>(query->getOption(D6O_ORO));
5430
5431 if (oro) {
5432 const std::vector<uint16_t>& codes = oro->getValues();
5433 return (std::find(codes.begin(), codes.end(), code) != codes.end());
5434 }
5435
5436 return (false);
5437}
5438
5439tuple<bool, uint32_t>
5440Dhcpv6Srv::parkingLimitExceeded(string const& hook_label) {
5441 // Get the parking limit. Parsing should ensure the value is present.
5442 uint32_t parked_packet_limit(0);
5443 ConstElementPtr const& ppl(
5444 CfgMgr::instance().getCurrentCfg()->getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT));
5445 if (ppl) {
5446 parked_packet_limit = ppl->intValue();
5447 }
5448
5449 if (parked_packet_limit) {
5450 ParkingLotPtr const& parking_lot(
5451 ServerHooks::getServerHooks().getParkingLotPtr(hook_label));
5452
5453 if (parking_lot && parked_packet_limit <= parking_lot->size()) {
5454 return make_tuple(true, parked_packet_limit);
5455 }
5456 }
5457 return make_tuple(false, parked_packet_limit);
5458}
5459
5460
5462 // Dump all of our current packets, anything that is mid-stream
5464}
5465
5467#ifdef FUZZING
5468 char const* const rotate(getenv("KEA_DHCP6_FUZZING_ROTATE_PORT"));
5469 if (rotate) {
5470 InterprocessSyncFile file("kea-dhcp6-fuzzing-rotate-port");
5472 while (!locker.lock()) {
5473 this_thread::sleep_for(1s);
5474 }
5475 fstream port_file;
5476 port_file.open("/tmp/port6.txt", ios::in);
5477 string line;
5478 int port;
5479 getline(port_file, line);
5480 port_file.close();
5481 if (line.empty()) {
5482 port = 2000;
5483 } else {
5484 port = stoi(line);
5485 if (port < 3000) {
5486 ++port;
5487 } else {
5488 port = 2000;
5489 }
5490 }
5491 port_file.open("/tmp/port6.txt", ios::out | ios::trunc);
5492 port_file << to_string(port) << endl;
5493 port_file.close();
5494 locker.unlock();
5495 return port;
5496 }
5497#endif // FUZZING
5498 return server_port_;
5499}
5500
5502void
5503Dhcpv6Srv::setTeeTimes(uint32_t preferred_lft,
5504 const ConstSubnet6Ptr& subnet,
5505 Option6IAPtr& resp) {
5506 // Default T2 time to zero.
5507 uint32_t t2_time = 0;
5508
5509 // If T2 is explicitly configured we'll use that value.
5510 if (!subnet->getT2().unspecified()) {
5511 t2_time = subnet->getT2();
5512 } else if (subnet->getCalculateTeeTimes()) {
5513 // Calculating tee times is enabled, so calculate it.
5514 t2_time = static_cast<uint32_t>(round(subnet->getT2Percent() * preferred_lft));
5515 }
5516
5517 // We allow T2 to be any value.
5518 resp->setT2(t2_time);
5519
5520 // Default T1 time to zero.
5521 uint32_t t1_time = 0;
5522
5523 // If T1 is explicitly configured we'll use try value.
5524 if (!subnet->getT1().unspecified()) {
5525 t1_time = subnet->getT1();
5526 } else if (subnet->getCalculateTeeTimes()) {
5527 // Calculating tee times is enabled, so calculate it.
5528 t1_time = static_cast<uint32_t>(round(subnet->getT1Percent() * preferred_lft));
5529 }
5530
5531 // T1 is sane if it is less than or equal to T2.
5532 if (t1_time < t2_time) {
5533 resp->setT1(t1_time);
5534 } else {
5535 // It's either explicitly 0 or insane, leave it to the client
5536 resp->setT1(0);
5537 }
5538}
5539
5540void
5543 const ConstSubnet6Ptr orig_subnet) {
5544 bool reprocess_client_name = false;
5545
5546 // Find the pool to which the first active lease address belongs and use it
5547 // to update DDNS parameters to include those from the pool.
5548 OptionPtr ia = answer->getOption(D6O_IA_NA);
5549 if (ia) {
5550 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
5551 if (iaaddr && (iaaddr->getValid() > 0)) {
5552 auto ddns_params = ctx.getDdnsParams();
5553 auto pool = ddns_params->setPoolFromAddress(iaaddr->getAddress());
5554 if (pool) {
5555 // If the pool has any DDNS parameters we need to recalculate the FQDN.
5556 reprocess_client_name = pool->hasDdnsParameters();
5557 }
5558 }
5559 }
5560
5561 // Check if the subnet was dynamically changed by the allocation engine.
5562 if (ctx.subnet_ && orig_subnet && (orig_subnet->getID() != ctx.subnet_->getID())) {
5563 // We get the network for logging only. It should always be set as
5564 // this a dynamic change should only happen within shared-networks.
5565 // Not having one might not be an error if a hook changed the subnet?
5566 SharedNetwork6Ptr network;
5567 orig_subnet->getSharedNetwork(network);
5569 .arg(question->getLabel())
5570 .arg(orig_subnet->toText())
5571 .arg(ctx.subnet_->toText())
5572 .arg(network ? network->getName() : "<no network?>");
5573
5574 // The subnet changed so we need to recalculate the FQDN.
5575 reprocess_client_name = true;
5576 }
5577
5578 // Recalulate the FQDN if either the pool has DDNS parameters or the
5579 // selected subnet was changed.
5580 if (reprocess_client_name) {
5581 // Save the current DNS values on the context.
5582 std::string prev_hostname = ctx.hostname_;
5583 bool prev_fwd_dns_update = ctx.fwd_dns_update_;
5584 bool prev_rev_dns_update = ctx.rev_dns_update_;
5585
5586 // Remove the current FQDN option from the answer.
5587 answer->delOption(D6O_CLIENT_FQDN);
5588
5589 // Recalculate the client's FQDN. This will replace the FQDN option and
5590 // update the context values for hostname_ and DNS directions.
5591 processClientFqdn(question, answer, ctx);
5592
5593 // If this is a real allocation and the DNS values changed we need to
5594 // update the leases.
5595 if (!ctx.fake_allocation_ &&
5596 ((prev_hostname != ctx.hostname_) ||
5597 (prev_fwd_dns_update != ctx.fwd_dns_update_) ||
5598 (prev_rev_dns_update != ctx.rev_dns_update_))) {
5599 for (auto const& l : ctx.new_leases_) {
5600 l->hostname_ = ctx.hostname_;
5601 l->fqdn_fwd_ = ctx.fwd_dns_update_;
5602 l->fqdn_rev_ = ctx.rev_dns_update_;
5603 l->reuseable_valid_lft_ = 0;
5605 }
5606 }
5607 }
5608}
5609
5610std::list<std::list<std::string>> Dhcpv6Srv::jsonPathsToRedact() const{
5611 static std::list<std::list<std::string>> const list({
5612 {"config-control", "config-databases", "[]"},
5613 {"hooks-libraries", "[]", "parameters", "*"},
5614 {"hosts-database"},
5615 {"hosts-databases", "[]"},
5616 {"lease-database"},
5617 });
5618 return list;
5619}
5620
5621} // namespace dhcp
5622} // namespace isc
Defines elements for storing the names of client classes.
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
Defines a single hint.
AllocEngine(isc::util::uint128_t const &attempts)
Constructor.
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...
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition cfgmgr.cc:69
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:116
static SubnetSelector initSelector(const Pkt6Ptr &query)
Build selector from a client's message.
Container for storing client class names.
Definition classify.h:110
void insert(const ClientClass &class_name)
Insert an element.
Definition classify.h:160
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition classify.cc:80
Client race avoidance RAII handler.
bool tryLock(Pkt4Ptr query, ContinuationPtr cont=ContinuationPtr())
Tries to acquires a client.
D2ClientMgr isolates Kea from the details of being a D2 client.
std::string generateFqdn(const asiolink::IOAddress &address, const DdnsParams &ddns_params, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
void getUpdateDirections(const T &fqdn_resp, bool &forward, bool &reverse)
Get directional update flags based on server FQDN flags.
void stop()
Stop the sender.
void suspendUpdates()
Suspends sending requests.
void adjustDomainName(const T &fqdn, T &fqdn_resp, const DdnsParams &ddns_params)
Set server FQDN name based on configuration and a given FQDN.
void sendRequest(dhcp_ddns::NameChangeRequestPtr &ncr)
Send the given NameChangeRequests to kea-dhcp-ddns.
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
void adjustFqdnFlags(const T &fqdn, T &fqdn_resp, const DdnsParams &ddns_params)
Set server FQDN flags based on configuration and a given FQDN.
std::string qualifyName(const std::string &partial_name, const DdnsParams &ddns_params, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
void startSender(D2ClientErrorHandler error_handler, const isc::asiolink::IOServicePtr &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
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.
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
DUID(const std::vector< uint8_t > &data)
Constructor from vector.
Definition duid.cc:27
void send(const Pkt6Ptr &pkt)
Send message over IPC.
void close()
Close communication socket.
static Dhcp6to4Ipc & instance()
Returns pointer to the sole instance of Dhcp6to4Ipc.
static uint16_t client_port
std::queue< isc::dhcp_ddns::NameChangeRequest > name_change_reqs_
Holds a list of isc::dhcp_ddns::NameChangeRequest objects, which are waiting for sending to kea-dhcp-...
Definition dhcp6_srv.h:1268
void shutdown() override
Instructs the server to shut down.
Definition dhcp6_srv.cc:371
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
OptionPtr rejectIA_PD(const isc::dhcp::Pkt6Ptr &query, Option6IAPtr ia)
Reject IA_PD.
Pkt6Ptr processPacket(Pkt6Ptr query)
Process a single incoming DHCPv6 packet.
Definition dhcp6_srv.cc:834
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:801
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 reject(const Pkt6Ptr &query, Pkt6Ptr &answer)
Reject query.
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:657
void setPacketStatisticsDefaults()
This function sets statistics related to DHCPv6 packets processing to their initial values.
Definition dhcp6_srv.cc:317
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:1264
virtual void sendPacket(const Pkt6Ptr &pkt)
dummy wrapper around IfaceMgr::send()
Definition dhcp6_srv.cc:380
bool testServerID(const Pkt6Ptr &pkt)
Compare received server id with our server id.
Definition dhcp6_srv.cc:385
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.
OptionPtr rejectIA_NA(const isc::dhcp::Pkt6Ptr &query, Option6IAPtr ia)
Reject IA_NA.
uint16_t getServerPort() const
Get UDP port on which server should listen.
void runOne()
Main server processing step.
Definition dhcp6_srv.cc:714
virtual Pkt6Ptr receivePacket(int timeout)
dummy wrapper around IfaceMgr::receive6
Definition dhcp6_srv.cc:376
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:998
OptionPtr serverid_
Server DUID (to be sent in server-identifier option).
Definition dhcp6_srv.h:1244
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 processPacketAndSendResponse(Pkt6Ptr query)
Process a single incoming DHCPv6 packet and sends the response.
Definition dhcp6_srv.cc:823
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:497
void initContext0(const Pkt6Ptr &query, AllocEngine::ClientContext6 &ctx)
Initialize client context (first part).
Definition dhcp6_srv.cc:484
virtual ~Dhcpv6Srv()
Destructor. Used during DHCPv6 service shutdown.
Definition dhcp6_srv.cc:327
void initContext(AllocEngine::ClientContext6 &ctx, bool &drop)
Initializes client context for specified packet.
Definition dhcp6_srv.cc:563
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:1272
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:409
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:272
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.
Pkt6Ptr processAddrRegInform(AllocEngine::ClientContext6 &ctx)
Processes incoming Addr-reg-inform message.
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:422
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:1213
CBControlDHCPv6Ptr cb_control_
Controls access to the configuration backends.
Definition dhcp6_srv.h:1275
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:1248
void checkPostAssignmentChanges(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, const ConstSubnet6Ptr orig_subnet)
Iterates over new leases, update stale DNS entries.
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:1210
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.
Exception thrown when host name sanitizing reduces the domain name to an empty string.
static std::list< std::string > getDBVersions()
Return extended version info for registered backends.
ConstHostCollection getAll6(const SubnetID &subnet_id, const HostMgrOperationTarget target) const
Return all hosts in a DHCPv6 subnet.
Definition host_mgr.cc:171
static void create()
Creates new instance of the HostMgr.
Definition host_mgr.cc:52
static HostMgr & instance()
Returns a sole instance of the HostMgr.
Definition host_mgr.cc:114
std::string getIdentifierAsText() const
Returns host identifier in a textual form.
Definition host.cc:319
IdentifierType
Type of the host identifier.
Definition host.h:337
@ IDENT_FLEX
Flexible host identifier.
Definition host.h:342
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:52
bool send(const Pkt6Ptr &pkt)
Sends an IPv6 packet.
void closeSockets()
Closes all open sockets.
Definition iface_mgr.cc:313
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.
virtual bool deleteLease(const Lease4Ptr &lease)=0
Deletes an IPv4 lease.
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const =0
Returns existing IPv6 lease for a given IPv6 address.
virtual void updateLease6(const Lease6Ptr &lease6)=0
Updates IPv6 lease.
static std::list< std::string > getDBVersions()
Return extended version info for registered backends.
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.
Option6ClientFqdn(const uint8_t flags, const std::string &domain_name, const DomainNameType domain_name_type=FULL)
Constructor, creates option instance using flags and domain name.
Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress &addr, uint32_t preferred, uint32_t valid)
Constructor, used for options constructed (during transmission).
Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress &addr, uint8_t prefix_length, uint32_t preferred, uint32_t valid)
Constructor, used for options constructed (during transmission).
uint32_t getIAID() const
Returns IA identifier.
Definition option6_ia.h:96
Option6IA(uint16_t type, uint32_t iaid)
Ctor, used for constructed options, usually during transmission.
Definition option6_ia.cc:25
Option6StatusCode(const uint16_t status_code, const std::string &status_message)
Constructor, used for options constructed (during transmission).
Option descriptor.
Definition cfg_option.h:49
OptionPtr option_
Option instance.
Definition cfg_option.h:52
bool allowedForClientClasses(const ClientClasses &cclasses) const
Validates an OptionDescriptor's client-classes against a list of classes.
Definition cfg_option.cc:72
OptionVendor(Option::Universe u, const uint32_t vendor_id)
Constructor.
const OptionCollection & getOptions() const
Returns all encapsulated options.
Definition option.h:354
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition option.cc:200
Option(Universe u, uint16_t type)
ctor, used for options constructed, usually during transmission
Definition option.cc:39
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition pkt6.cc:730
Pkt6(uint8_t msg_type, uint32_t transid, DHCPv6Proto proto=UDP)
Constructor, used in replying to a message.
Definition pkt6.cc:58
An exception that is thrown if a DHCPv6 protocol violation occurs while processing a message (e....
Definition utils.h:17
RAII object enabling copying options retrieved from the packet.
Definition pkt.h:46
Exception thrown when a call to select is interrupted by a signal.
Definition iface_mgr.h:58
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:114
NameChangeRequest()
Default Constructor.
Definition ncr_msg.cc:257
static std::string const & resultToText(Result const &result)
Convert enum to string.
Definition ncr_io.h:490
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
bool lock()
Acquire the lock (blocks if something else has acquired a lock on the same task name).
int getExitValue()
Fetches the exit value.
Definition daemon.h:235
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.
ThreadPool< std::function< void()> > & getThreadPool()
Get the dhcp thread pool.
void apply(bool enabled, uint32_t thread_count, uint32_t queue_size)
Apply the multi-threading related settings.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:346
const std::vector< uint8_t > & getVector() const
Return the buffer.
Definition buffer.h:436
Read mutex RAII handler.
Defines classes for storing client class definitions.
int version()
returns Kea hooks version.
Defines the D2ClientConfig class.
Defines the D2ClientMgr class.
Dhcp4Hooks Hooks
Definition dhcp4_srv.cc:213
@ STATUS_NoAddrsAvail
Definition dhcp6.h:178
@ STATUS_NoPrefixAvail
Definition dhcp6.h:182
@ STATUS_NotOnLink
Definition dhcp6.h:180
@ STATUS_Success
Definition dhcp6.h:176
@ STATUS_NoBinding
Definition dhcp6.h:179
@ STATUS_UnspecFail
Definition dhcp6.h:177
@ D6O_CLIENT_FQDN
Definition dhcp6.h:59
@ D6O_UNICAST
Definition dhcp6.h:32
@ 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_IA_TA
Definition dhcp6.h:24
@ 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:209
@ DHCPV6_REQUEST
Definition dhcp6.h:210
@ DHCPV6_RENEW
Definition dhcp6.h:212
@ DHCPV6_DHCPV4_QUERY
Definition dhcp6.h:231
@ DHCPV6_DHCPV4_RESPONSE
Definition dhcp6.h:232
@ DHCPV6_RECONFIGURE
Definition dhcp6.h:217
@ DHCPV6_REBIND
Definition dhcp6.h:213
@ DHCPV6_REPLY
Definition dhcp6.h:214
@ DHCPV6_ADDR_REG_REPLY
Definition dhcp6.h:250
@ DHCPV6_ADDR_REG_INFORM
Definition dhcp6.h:249
@ DHCPV6_SOLICIT
Definition dhcp6.h:208
@ DHCPV6_RELEASE
Definition dhcp6.h:215
@ DHCPV6_INFORMATION_REQUEST
Definition dhcp6.h:218
@ DHCPV6_CONFIRM
Definition dhcp6.h:211
@ DHCPV6_DECLINE
Definition dhcp6.h:216
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
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
int get(CalloutHandle &handle)
The gss-tsig-get command.
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:30
@ info
Definition db_log.h:126
ConflictResolutionMode StringToConflictResolutionMode(const std::string &mode_str)
Function which converts string to ConflictResolutionMode enum values.
Definition ncr_msg.cc:42
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition ncr_msg.h:242
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_PACKET_REJECT_CLASS
const isc::log::MessageID DHCP6_LEASE_ALLOC
const isc::log::MessageID DHCP6_FLEX_ID
const isc::log::MessageID DHCP6_REGISTERED_LEASE_ADD_FAIL
uint32_t calculateDdnsTtl(uint32_t lease_lft, const util::Optional< double > &ddns_ttl_percent, const util::Optional< uint32_t > &ddns_ttl, const util::Optional< uint32_t > &ddns_ttl_min, const util::Optional< uint32_t > &ddns_ttl_max)
Calculates TTL for a DNS resource record based on lease life time.
const isc::log::MessageID DHCP6_PACKET_PROCESS_EXCEPTION_MAIN
const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_SKIP
const isc::log::MessageID DHCP6_ADDR_REG_INFORM_CLIENT_CHANGE
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 NameChangeRequest from the DHCPv4 lease and queues it to be sent to D2.
const isc::log::MessageID DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY
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:620
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_REGISTERED_LEASE_UPDATE_FAIL
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_ADDR6_REGISTER_DISABLED_DROP
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:528
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition lease.h:693
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.
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_ADDR_REG_INFORM_FAIL
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:366
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:364
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:726
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
const isc::log::MessageID DHCP6_HOOK_ADDR6_REGISTER_SKIP
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:350
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:696
const isc::log::MessageID DHCP6_DECLINE_PROCESS_IA
NameChangeRequestPtr generateNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates NameChangeRequest from the DHCPv4 lease.
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:359
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:449
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:371
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
const isc::log::MessageID DHCP6_HOOK_ADDR6_REGISTER_DROP
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
const isc::log::MessageID DHCP6_ADD_DEPRECATED_UNICAST
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:979
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:536
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.
Lease::Type type_
Lease type (IA or PD).
Lease6Collection changed_leases_
A pointer to any leases that have changed FQDN information.
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 ...
Lease6()
Constructor.
Definition lease.cc:517
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire).
Definition lease.h:34
static constexpr uint32_t STATE_DEFAULT
A lease in the default state.
Definition lease.h:69
static std::string lifetimeToText(uint32_t lifetime)
Print lifetime.
Definition lease.cc:34
static constexpr uint32_t STATE_REGISTERED
Registered self-generated lease.
Definition lease.h:81
static constexpr 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.
bool add(const WorkItemPtr &item)
add a work item to the thread pool