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