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