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