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