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