Kea 2.7.8
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),
265 alloc_engine_(), name_change_reqs_(),
266 network_state_(new NetworkState()),
267 cb_control_(new CBControlDHCPv6()) {
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 d2_mgr.adjustDomainName<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
2336 }
2337
2338 // Once we have the FQDN setup to use it for the lease hostname. This
2339 // only gets replaced later if the FQDN is to be generated from the address.
2340 ctx.hostname_ = fqdn_resp->getDomainName();
2341
2342 // The FQDN has been processed successfully. Let's append it to the
2343 // response to be sent to a client. Note that the Client FQDN option is
2344 // always sent back to the client if Client FQDN was included in the
2345 // client's message.
2347 .arg(question->getLabel())
2348 .arg(fqdn_resp->toText());
2349 answer->addOption(fqdn_resp);
2350
2351 // Optionally, call a hook that may override the decisions made
2352 // earlier.
2353 if (HooksManager::calloutsPresent(Hooks.hook_index_ddns6_update_)) {
2354 CalloutHandlePtr callout_handle = getCalloutHandle(question);
2355
2356 // Use the RAII wrapper to make sure that the callout handle state is
2357 // reset when this object goes out of scope. All hook points must do
2358 // it to prevent possible circular dependency between the callout
2359 // handle and its arguments.
2360 ScopedCalloutHandleState callout_handle_state(callout_handle);
2361
2362 // Setup the callout arguments.
2363 ConstSubnet6Ptr subnet = ctx.subnet_;
2364 callout_handle->setArgument("query6", question);
2365 callout_handle->setArgument("response6", answer);
2366 callout_handle->setArgument("subnet6", subnet);
2367 callout_handle->setArgument("hostname", ctx.hostname_);
2368 callout_handle->setArgument("fwd-update", ctx.fwd_dns_update_);
2369 callout_handle->setArgument("rev-update", ctx.rev_dns_update_);
2370 callout_handle->setArgument("ddns-params", ddns_params);
2371
2372 // Call callouts
2373 HooksManager::callCallouts(Hooks.hook_index_ddns6_update_, *callout_handle);
2374
2375 // Let's get the parameters returned by hook.
2376 string hook_hostname;
2377 bool hook_fwd_dns_update;
2378 bool hook_rev_dns_update;
2379 callout_handle->getArgument("hostname", hook_hostname);
2380 callout_handle->getArgument("fwd-update", hook_fwd_dns_update);
2381 callout_handle->getArgument("rev-update", hook_rev_dns_update);
2382
2383 // If there's anything changed by the hook, log it and then update the parameters
2384 if ((ctx.hostname_ != hook_hostname) || (ctx.fwd_dns_update_!= hook_fwd_dns_update) ||
2385 (ctx.rev_dns_update_ != hook_rev_dns_update)) {
2387 .arg(ctx.hostname_).arg(hook_hostname)
2388 .arg(ctx.fwd_dns_update_).arg(hook_fwd_dns_update)
2389 .arg(ctx.rev_dns_update_).arg(hook_rev_dns_update);
2390
2391 // Update the FQDN option in the response.
2392 fqdn_resp = boost::dynamic_pointer_cast<Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
2393 if (fqdn) {
2394 fqdn_resp->setDomainName(hook_hostname, Option6ClientFqdn::FULL);
2395 if (!(hook_fwd_dns_update || hook_rev_dns_update)) {
2396 // Hook disabled updates, Set flags back to client accordingly.
2397 fqdn_resp->setFlag(Option6ClientFqdn::FLAG_S, 0);
2398 fqdn_resp->setFlag(Option6ClientFqdn::FLAG_N, 1);
2399 }
2400 }
2401
2402 ctx.hostname_ = hook_hostname;
2403 ctx.fwd_dns_update_ = hook_fwd_dns_update;
2404 ctx.rev_dns_update_ = hook_rev_dns_update;
2405 }
2406 }
2407}
2408
2409void
2412 // Don't create NameChangeRequests if DNS updates are disabled.
2413 if (!(ctx.getDdnsParams()->getEnableUpdates())) {
2414 return;
2415 }
2416
2417 // The response message instance is always required. For instance it
2418 // holds the Client Identifier. It is a programming error if supplied
2419 // message is NULL.
2420 if (!answer) {
2421 isc_throw(isc::Unexpected, "an instance of the object"
2422 << " encapsulating server's message must not be"
2423 << " NULL when creating DNS NameChangeRequest");
2424 }
2425
2426 // It is likely that client haven't included the FQDN option. In such case,
2427 // FQDN option will be NULL. This is valid state, so we simply return.
2428 Option6ClientFqdnPtr opt_fqdn = boost::dynamic_pointer_cast<
2429 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
2430 if (!opt_fqdn) {
2431 return;
2432 }
2433
2434 // Get the update directions that should be performed based on our
2435 // response FQDN flags.
2436 bool do_fwd = false;
2437 bool do_rev = false;
2439 do_fwd, do_rev);
2440
2441 // Get the Client Id. It is mandatory and a function creating a response
2442 // would have thrown an exception if it was missing. Thus throwing
2443 // Unexpected if it is missing as it is a programming error.
2444 OptionPtr opt_duid = answer->getOption(D6O_CLIENTID);
2445 if (!opt_duid) {
2447 "client identifier is required when creating a new"
2448 " DNS NameChangeRequest");
2449 }
2450 DuidPtr duid = DuidPtr(new DUID(opt_duid->getData()));
2451
2452 // Get the FQDN in the on-wire format. It will be needed to compute
2453 // DHCID.
2454 OutputBuffer name_buf(1);
2455 opt_fqdn->packDomainName(name_buf);
2456 const std::vector<uint8_t>& buf_vec = name_buf.getVector();
2457 // Compute DHCID from Client Identifier and FQDN.
2458 isc::dhcp_ddns::D2Dhcid dhcid(*duid, buf_vec);
2459
2460 // Iterate over the IAContexts (there should be one per client IA processed).
2461 // For the first address in each IA_NA, create the appropriate NCR(s).
2464 for (auto& ia_ctx : ctx.getIAContexts()) {
2465 if ((ia_ctx.type_ != Lease::TYPE_NA) || !ia_ctx.ia_rsp_) {
2466 continue;
2467 }
2468
2471 Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
2472 Option6IAAddr>(ia_ctx.ia_rsp_->getOption(D6O_IAADDR));
2473
2474 // We need an address to create a name-to-address mapping.
2475 // If address is missing for any reason, go to the next IA.
2476 if (!iaaddr) {
2477 continue;
2478 }
2479
2480 bool extended_only = false;
2481 // If the lease is being reused (i.e. lease caching in effect) skip it.
2482 IOAddress ia_address = iaaddr->getAddress();
2483 for (auto const& l : ia_ctx.reused_leases_) {
2484 if (l->addr_ == ia_address) {
2485 extended_only = true;
2486 break;
2487 }
2488 }
2489
2490 if (extended_only) {
2491 continue;
2492 }
2493
2494 // If the lease for iaaddr is in the list of changed leases, we need
2495 // to determine if the changes included changes to the FQDN. If so
2496 // then we may need to do a CHG_REMOVE.
2497 for (auto const& l : ia_ctx.changed_leases_) {
2498
2499 if (l->addr_ == ia_address) {
2500 // The address is the same so this must be renewal. If we're not
2501 // always updating on renew, then we only renew if DNS info has
2502 // changed.
2503 if ((l->reuseable_valid_lft_ > 0) ||
2504 (!ctx.getDdnsParams()->getUpdateOnRenew() &&
2505 (l->hostname_ == opt_fqdn->getDomainName() &&
2506 l->fqdn_fwd_ == do_fwd && l->fqdn_rev_ == do_rev))) {
2507 extended_only = true;
2508 } else {
2509 // Queue a CHG_REMOVE of the old data.
2510 // NCR will only be created if the lease hostname is not
2511 // empty and at least one of the direction flags is true
2512 queueNCR(CHG_REMOVE, l);
2513 }
2514
2515 break;
2516 }
2517 }
2518
2519 if (!(do_fwd || do_rev) || (extended_only)) {
2520 // Flags indicate no updates needed or it was an extension of
2521 // an existing lease with no FQDN changes. In the case of the
2522 // former, the most likely scenario is that we are honoring the
2523 // client's request that no updates be done.
2524 continue;
2525 }
2526
2527 // Create new NameChangeRequest. Use the domain name from the FQDN.
2528 // This is an FQDN included in the response to the client, so it
2529 // holds a fully qualified domain-name already (not partial).
2530 // Get the IP address from the lease.
2532 auto cr_mode = StringToConflictResolutionMode(ctx.getDdnsParams()->getConflictResolutionMode());
2534 do_fwd, do_rev,
2535 opt_fqdn->getDomainName(),
2536 iaaddr->getAddress().toText(),
2537 dhcid, 0,
2538 calculateDdnsTtl(iaaddr->getValid(),
2539 ctx.getDdnsParams()->getTtlPercent(),
2540 ctx.getDdnsParams()->getTtl(),
2541 ctx.getDdnsParams()->getTtlMin(),
2542 ctx.getDdnsParams()->getTtlMax()),
2543 cr_mode));
2545 .arg(answer->getLabel())
2546 .arg(ncr->toText());
2547
2548 // Post the NCR to the D2ClientMgr.
2550
2555 return;
2556 }
2557}
2558
2561 CfgMACSources mac_sources = CfgMgr::instance().getCurrentCfg()->
2562 getMACSources().get();
2563 HWAddrPtr hwaddr;
2564 for (auto const& it : mac_sources) {
2565 hwaddr = pkt->getMAC(it);
2566 if (hwaddr) {
2567 return (hwaddr);
2568 }
2569 }
2570 return (hwaddr);
2571}
2572
2575 const Lease6Ptr& lease) {
2576
2577 // Search the reservation the prefix is from.
2578 ConstHostPtr host = ctx.currentHost();
2579 if (host) {
2580 IPv6ResrvRange resvs = host->getIPv6Reservations(IPv6Resrv::TYPE_PD);
2581 BOOST_FOREACH(auto const& resv, resvs) {
2582 if ((resv.second.getPrefix() == lease->addr_) &&
2583 (resv.second.getPrefixLen() == lease->prefixlen_)) {
2584 return (resv.second.getPDExclude());
2585 }
2586 }
2587 }
2588
2589 // Search the pool the address is from.
2590 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2591 Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
2592 subnet->getPool(Lease::TYPE_PD, lease->addr_));
2593 if (pool) {
2594 return (pool->getPrefixExcludeOption());
2595 }
2596 return (OptionPtr());
2597}
2598
2602 boost::shared_ptr<Option6IA> ia) {
2603
2604 // Check if the client sent us a hint in his IA_NA. Clients may send an
2605 // address in their IA_NA options as a suggestion (e.g. the last address
2606 // they used before).
2607 Option6IAAddrPtr hint_opt =
2608 boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
2610 if (hint_opt) {
2611 hint = hint_opt->getAddress();
2612 }
2613
2614 if (ctx.fake_allocation_) {
2616 .arg(query->getLabel())
2617 .arg(ia->getIAID())
2618 .arg(hint_opt ? hint.toText() : "(no hint)");
2619 } else {
2621 .arg(query->getLabel())
2622 .arg(ia->getIAID())
2623 .arg(hint_opt ? hint.toText() : "(no hint)");
2624 }
2625
2626 // convenience values
2627 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2628
2629 // If there is no subnet selected for handling this IA_NA, the only thing left to do is
2630 // to say that we are sorry, but the user won't get an address. As a convenience, we
2631 // use a different status text to indicate that (compare to the same status code,
2632 // but different wording below)
2633 if (!subnet) {
2634 // Create an empty IA_NA option with IAID matching the request.
2635 // Note that we don't use OptionDefinition class to create this option.
2636 // This is because we prefer using a constructor of Option6IA that
2637 // initializes IAID. Otherwise we would have to use setIAID() after
2638 // creation of the option which has some performance implications.
2639 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2640
2641 // Insert status code NoAddrsAvail.
2642 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoAddrsAvail,
2643 "Server could not select subnet for"
2644 " this client"));
2645 return (ia_rsp);
2646 }
2647
2648 // Set per-IA context values.
2649 ctx.createIAContext();
2650 ctx.currentIA().iaid_ = ia->getIAID();
2651 if (hint_opt) {
2652 ctx.currentIA().addHint(hint_opt);
2653 } else {
2654 ctx.currentIA().addHint(hint);
2655 }
2657
2658 // Use allocation engine to pick a lease for this client. Allocation engine
2659 // will try to honor the hint, but it is just a hint - some other address
2660 // may be used instead. If fake_allocation is set to false, the lease will
2661 // be inserted into the LeaseMgr as well.
2662 Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
2663
2665 Lease6Ptr lease;
2666 if (!leases.empty()) {
2667 lease = *leases.begin();
2668 }
2669
2670 // Create IA_NA that we will put in the response.
2671 // Do not use OptionDefinition to create option's instance so
2672 // as we can initialize IAID using a constructor.
2673 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2674 ctx.currentIA().ia_rsp_ = ia_rsp;
2675
2676 if (lease) {
2677 // We have a lease! Let's wrap its content into IA_NA option
2678 // with IAADDR suboption.
2679 if (ctx.fake_allocation_) {
2681 .arg(query->getLabel())
2682 .arg(lease->addr_.toText())
2683 .arg(ia->getIAID());
2684 } else if (lease->reuseable_valid_lft_ == 0) {
2686 .arg(query->getLabel())
2687 .arg(lease->addr_.toText())
2688 .arg(ia->getIAID())
2689 .arg(Lease::lifetimeToText(lease->valid_lft_));
2690 } else {
2691 lease->valid_lft_ = lease->reuseable_valid_lft_;
2692 lease->preferred_lft_ = lease->reuseable_preferred_lft_;
2693 ctx.currentIA().reused_leases_.push_back(lease);
2695 .arg(query->getLabel())
2696 .arg(lease->addr_.toText())
2697 .arg(ia->getIAID())
2698 .arg(Lease::lifetimeToText(lease->valid_lft_));
2699
2700 // Increment the reuse statistics.
2701 StatsMgr::instance().addValue("v6-ia-na-lease-reuses", int64_t(1));
2702 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
2703 "v6-ia-na-lease-reuses"),
2704 int64_t(1));
2705 }
2707 .arg(query->getLabel())
2708 .arg(ia->getIAID())
2709 .arg(lease->toText());
2710
2711 // Set the values for T1 and T2.
2712 setTeeTimes(lease->preferred_lft_, subnet, ia_rsp);
2713
2714 Option6IAAddrPtr addr(new Option6IAAddr(D6O_IAADDR, lease->addr_,
2715 lease->preferred_lft_,
2716 lease->valid_lft_));
2717 ia_rsp->addOption(addr);
2718
2719 // It would be possible to insert status code=0(success) as well,
2720 // but this is considered waste of bandwidth as absence of status
2721 // code is considered a success.
2722
2723 } else {
2724 // Allocation engine did not allocate a lease. The engine logged
2725 // cause of that failure. The only thing left is to insert
2726 // status code to pass the sad news to the client.
2727
2730 .arg(query->getLabel())
2731 .arg(ia->getIAID());
2732
2733 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2735 "Sorry, no address could be"
2736 " allocated."));
2737 }
2738 return (ia_rsp);
2739}
2740
2744 boost::shared_ptr<Option6IA> ia) {
2745
2746 // Check if the client sent us a hint in his IA_PD. Clients may send an
2747 // address in their IA_PD options as a suggestion (e.g. the last address
2748 // they used before). While the hint consists of a full prefix (prefix +
2749 // length), getting just the prefix is sufficient to identify a lease.
2750 Option6IAPrefixPtr hint_opt =
2751 boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
2753 if (hint_opt) {
2754 hint = hint_opt->getAddress();
2755 }
2756
2757 if (ctx.fake_allocation_) {
2759 .arg(query->getLabel())
2760 .arg(ia->getIAID())
2761 .arg(hint_opt ? hint.toText() : "(no hint)");
2762 } else {
2764 .arg(query->getLabel())
2765 .arg(ia->getIAID())
2766 .arg(hint_opt ? hint.toText() : "(no hint)");
2767 }
2768
2769 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2770
2771 // Create IA_PD that we will put in the response.
2772 // Do not use OptionDefinition to create option's instance so
2773 // as we can initialize IAID using a constructor.
2774 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
2775
2776 // If there is no subnet selected for handling this IA_PD, the only thing
2777 // left to do is to say that we are sorry, but the user won't get an address.
2778 // As a convenience, we use a different status text to indicate that
2779 // (compare to the same status code, but different wording below)
2780 if (!subnet) {
2781
2782 // Insert status code NoAddrsAvail.
2783 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoPrefixAvail,
2784 "Sorry, no subnet available."));
2785 return (ia_rsp);
2786 }
2787
2788 // Set per-IA context values.
2789 ctx.createIAContext();
2790 ctx.currentIA().iaid_ = ia->getIAID();
2791 if (hint_opt) {
2792 ctx.currentIA().addHint(hint_opt);
2793 } else {
2794 ctx.currentIA().addHint(hint, 0);
2795 }
2797 ctx.currentIA().ia_rsp_ = ia_rsp;
2798
2799 // Use allocation engine to pick a lease for this client. Allocation engine
2800 // will try to honor the hint, but it is just a hint - some other address
2801 // may be used instead. If fake_allocation is set to false, the lease will
2802 // be inserted into the LeaseMgr as well.
2803 Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
2804
2805 if (!leases.empty()) {
2806
2807 // Need to retain the shortest preferred lease time to use
2808 // for calculating T1 and T2.
2809 uint32_t min_preferred_lft = (*leases.begin())->preferred_lft_;
2810
2811 const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
2812 for (auto const& l : leases) {
2813
2814 // We have a lease! Let's wrap its content into IA_PD option
2815 // with IAADDR suboption.
2816 if (ctx.fake_allocation_) {
2818 .arg(query->getLabel())
2819 .arg(l->addr_.toText())
2820 .arg(static_cast<int>(l->prefixlen_))
2821 .arg(ia->getIAID());
2822 } else if (l->reuseable_valid_lft_ == 0) {
2824 .arg(query->getLabel())
2825 .arg(l->addr_.toText())
2826 .arg(static_cast<int>(l->prefixlen_))
2827 .arg(ia->getIAID())
2828 .arg(Lease::lifetimeToText(l->valid_lft_));
2829 } else {
2830 l->valid_lft_ = l->reuseable_valid_lft_;
2831 l->preferred_lft_ = l->reuseable_preferred_lft_;
2833 .arg(query->getLabel())
2834 .arg(l->addr_.toText())
2835 .arg(static_cast<int>(l->prefixlen_))
2836 .arg(ia->getIAID())
2837 .arg(Lease::lifetimeToText(l->valid_lft_));
2838
2839 // Increment the reuse statistics.
2840 StatsMgr::instance().addValue("v6-ia-pd-lease-reuses", int64_t(1));
2841 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
2842 "v6-ia-pd-lease-reuses"),
2843 int64_t(1));
2844 }
2845
2846 // Check for new minimum lease time
2847 if ((l->preferred_lft_ > 0) && (min_preferred_lft > l->preferred_lft_)) {
2848 min_preferred_lft = l->preferred_lft_;
2849 }
2850
2851 boost::shared_ptr<Option6IAPrefix>
2852 addr(new Option6IAPrefix(D6O_IAPREFIX, l->addr_,
2853 l->prefixlen_, l->preferred_lft_,
2854 l->valid_lft_));
2855 ia_rsp->addOption(addr);
2856
2857 if (pd_exclude_requested) {
2858 OptionPtr pd_exclude_option = getPDExclude(ctx, l);
2859 if (pd_exclude_option) {
2860 addr->addOption(pd_exclude_option);
2861 }
2862 }
2863 }
2864
2865 // Set T1 and T2, using the shortest preferred lifetime among the leases.
2866 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
2867
2868 // It would be possible to insert status code=0(success) as well,
2869 // but this is considered waste of bandwidth as absence of status
2870 // code is considered a success.
2871
2872 } else {
2873 // Allocation engine did not allocate a lease. The engine logged
2874 // cause of that failure. The only thing left is to insert
2875 // status code to pass the sad news to the client.
2876
2879 .arg(query->getLabel())
2880 .arg(ia->getIAID());
2881
2882 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
2884 "Sorry, no prefixes could"
2885 " be allocated."));
2886 }
2887 return (ia_rsp);
2888}
2889
2893 boost::shared_ptr<Option6IA> ia) {
2894
2896 .arg(query->getLabel())
2897 .arg(ia->getIAID());
2898
2899 // convenience values
2900 const ConstSubnet6Ptr& subnet = ctx.subnet_;
2901
2902 // Create empty IA_NA option with IAID matching the request.
2903 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
2904
2905 if (!subnet) {
2915 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
2916 "Sorry, no known leases for this duid/iaid."));
2917 return (ia_rsp);
2918 }
2919
2920 // Set per-IA context values.
2921 ctx.createIAContext();
2922 ctx.currentIA().iaid_ = ia->getIAID();
2924 ctx.currentIA().ia_rsp_ = ia_rsp;
2925
2926 // Extract the addresses that the client is trying to obtain.
2927 OptionCollection addrs = ia->getOptions();
2928 for (auto const& it : addrs) {
2929 if (it.second->getType() != D6O_IAADDR) {
2930 continue;
2931 }
2932 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(it.second);
2933 if (!iaaddr) {
2934 // That's weird. Option code was ok, but the object type was not.
2935 // This should never happen. The only case would be with badly
2936 // mis-implemented hook libraries that insert invalid option objects.
2937 // There's no way to protect against this.
2938 continue;
2939 }
2940 ctx.currentIA().addHint(iaaddr);
2941 }
2942
2943 Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
2944
2945 // Ok, now we have the leases extended. We have:
2946 // - what the client tried to renew in ctx.hints_
2947 // - what we actually assigned in leases
2948 // - old leases that are no longer valid in ctx.old_leases_
2949
2950 // For each IA inserted by the client we have to determine what to do
2951 // about included addresses and notify the client. We will iterate over
2952 // those prefixes and remove those that we have already processed. We
2953 // don't want to remove them from the context, so we need to copy them
2954 // into temporary container.
2956
2957 // Retains the shortest valid lease time to use
2958 // for calculating T1 and T2.
2959 uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
2960
2961 // For all leases we have now, add the IAADDR with non-zero lifetimes.
2962 for (auto const& l : leases) {
2963 if (l->reuseable_valid_lft_ == 0) {
2965 .arg(query->getLabel())
2966 .arg(l->addr_.toText())
2967 .arg(ia->getIAID());
2968 } else {
2969 l->valid_lft_ = l->reuseable_valid_lft_;
2970 l->preferred_lft_ = l->reuseable_preferred_lft_;
2972 .arg(query->getLabel())
2973 .arg(l->addr_.toText())
2974 .arg(ia->getIAID())
2975 .arg(Lease::lifetimeToText(l->valid_lft_));
2976
2977 // Increment the reuse statistics.
2978 StatsMgr::instance().addValue("v6-ia-na-lease-reuses", int64_t(1));
2979 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
2980 "v6-ia-na-lease-reuses"),
2981 int64_t(1));
2982 }
2983
2985 l->addr_, l->preferred_lft_, l->valid_lft_));
2986 ia_rsp->addOption(iaaddr);
2987
2988 // Check for new minimum lease time
2989 if ((l->preferred_lft_ > 0) && (min_preferred_lft > l->preferred_lft_)) {
2990 min_preferred_lft = l->preferred_lft_;
2991 }
2992
2993 // Now remove this address from the hints list.
2994 AllocEngine::Resource hint_type(l->addr_);
2995 hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
2996 hints.end());
2997 }
2998
2999 // For the leases that we just retired, send the addresses with 0 lifetimes.
3000 for (auto const& l : ctx.currentIA().old_leases_) {
3001
3002 // Send an address with zero lifetimes only when this lease belonged to
3003 // this client. Do not send it when we're reusing an old lease that belonged
3004 // to someone else.
3005 if (equalValues(query->getClientId(), l->duid_)) {
3007 l->addr_, 0, 0));
3008 ia_rsp->addOption(iaaddr);
3009 }
3010
3011 // Now remove this address from the hints list.
3012 AllocEngine::Resource hint_type(l->addr_);
3013 hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
3014
3015 // If the new FQDN settings have changed for the lease, we need to
3016 // delete any existing FQDN records for this lease.
3017 if ((l->hostname_ != ctx.hostname_) || (l->fqdn_fwd_ != ctx.fwd_dns_update_) ||
3018 (l->fqdn_rev_ != ctx.rev_dns_update_)) {
3021 .arg(query->getLabel())
3022 .arg(l->toText())
3023 .arg(ctx.hostname_)
3024 .arg(ctx.rev_dns_update_ ? "true" : "false")
3025 .arg(ctx.fwd_dns_update_ ? "true" : "false");
3026
3027 queueNCR(CHG_REMOVE, l);
3028 }
3029 }
3030
3031 // Finally, if there are any addresses requested that we haven't dealt with
3032 // already, inform the client that he can't have them.
3033 for (auto const& hint : hints) {
3035 hint.getAddress(), 0, 0));
3036 ia_rsp->addOption(iaaddr);
3037 }
3038
3039 if (!leases.empty()) {
3040 // We allocated leases so we need to update T1 and T2.
3041 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
3042 } else {
3043 // The server wasn't able allocate new lease and renew an existing
3044 // lease. In that case, the server sends NoAddrsAvail per RFC 8415.
3045 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
3047 "Sorry, no addresses could be"
3048 " assigned at this time."));
3049 }
3050
3051 return (ia_rsp);
3052}
3053
3057 boost::shared_ptr<Option6IA> ia) {
3058
3060 .arg(query->getLabel())
3061 .arg(ia->getIAID());
3062
3063 const ConstSubnet6Ptr& subnet = ctx.subnet_;
3064 const DuidPtr& duid = ctx.duid_;
3065
3066 // Let's create a IA_PD response and fill it in later
3067 Option6IAPtr ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
3068
3069 // If there is no subnet for the particular client, we can't retrieve
3070 // information about client's leases from lease database. We treat this
3071 // as no binding for the client.
3072 if (!subnet) {
3073 // Per RFC 8415, section 18.3.4, if there is no binding and we are
3074 // processing a Renew, the NoBinding status code should be returned.
3075 if (query->getType() == DHCPV6_RENEW) {
3076 // Insert status code NoBinding
3077 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3078 "Sorry, no known PD leases"
3079 " for this duid/iaid."));
3080 return (ia_rsp);
3081
3082 // Per RFC 8415, section 18.3.5, if there is no binding and we are
3083 // processing Rebind, the message has to be discarded (assuming that
3084 // the server doesn't know if the prefix in the IA_PD option is
3085 // appropriate for the client's link). The exception being thrown
3086 // here should propagate to the main loop and cause the message to
3087 // be discarded.
3088 } else {
3089
3098 isc_throw(DHCPv6DiscardMessageError, "no subnet found for the"
3099 " client sending Rebind to extend lifetime of the"
3100 " prefix (DUID=" << duid->toText() << ", IAID="
3101 << ia->getIAID() << ")");
3102 }
3103 }
3104
3105 // Set per-IA context values.
3106 ctx.createIAContext();
3107 ctx.currentIA().iaid_ = ia->getIAID();
3109 ctx.currentIA().ia_rsp_ = ia_rsp;
3110
3111 // Extract prefixes that the client is trying to renew.
3112 OptionCollection addrs = ia->getOptions();
3113 for (auto const& it : addrs) {
3114 if (it.second->getType() != D6O_IAPREFIX) {
3115 continue;
3116 }
3117 Option6IAPrefixPtr prf = boost::dynamic_pointer_cast<Option6IAPrefix>(it.second);
3118 if (!prf) {
3119 // That's weird. Option code was ok, but the object type was not.
3120 // This should never happen. The only case would be with badly
3121 // mis-implemented hook libraries that insert invalid option objects.
3122 // There's no way to protect against this.
3123 continue;
3124 }
3125
3126 // Put the client's prefix into the hints list.
3127 ctx.currentIA().addHint(prf);
3128 }
3129
3130 // Call Allocation Engine and attempt to renew leases. Number of things
3131 // may happen. Leases may be extended, revoked (if the lease is no longer
3132 // valid or reserved for someone else), or new leases may be added.
3133 // Important parameters are:
3134 // - returned container - current valid leases
3135 // - old_leases - leases that used to be, but are no longer valid
3136 // - changed_leases - leases that have FQDN changed (not really important
3137 // in PD context)
3138 Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
3139
3140 // For each IA inserted by the client we have to determine what to do
3141 // about included prefixes and notify the client. We will iterate over
3142 // those prefixes and remove those that we have already processed. We
3143 // don't want to remove them from the context, so we need to copy them
3144 // into temporary container.
3146
3147 const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
3148
3149 // Retains the shortest valid lease time to use
3150 // for calculating T1 and T2.
3151 uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
3152
3153 for (auto const& l : leases) {
3154 if (l->reuseable_valid_lft_ == 0) {
3156 .arg(query->getLabel())
3157 .arg(l->addr_.toText())
3158 .arg(static_cast<int>(l->prefixlen_))
3159 .arg(ia->getIAID());
3160 } else {
3161 l->valid_lft_ = l->reuseable_valid_lft_;
3162 l->preferred_lft_ = l->reuseable_preferred_lft_;
3164 .arg(query->getLabel())
3165 .arg(l->addr_.toText())
3166 .arg(static_cast<int>(l->prefixlen_))
3167 .arg(ia->getIAID())
3168 .arg(Lease::lifetimeToText(l->valid_lft_));
3169
3170 // Increment the reuse statistics.
3171 StatsMgr::instance().addValue("v6-ia-pd-lease-reuses", int64_t(1));
3172 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", l->subnet_id_,
3173 "v6-ia-pd-lease-reuses"),
3174 int64_t(1));
3175 }
3176
3178 l->addr_, l->prefixlen_,
3179 l->preferred_lft_, l->valid_lft_));
3180 ia_rsp->addOption(prf);
3181
3182 if (pd_exclude_requested) {
3183 OptionPtr pd_exclude_option = getPDExclude(ctx, l);
3184 if (pd_exclude_option) {
3185 prf->addOption(pd_exclude_option);
3186 }
3187 }
3188
3189 // Check for new minimum lease time
3190 if ((l->preferred_lft_ > 0) && (l->preferred_lft_ < min_preferred_lft)) {
3191 min_preferred_lft = l->preferred_lft_;
3192 }
3193
3194 // Now remove this prefix from the hints list.
3195 AllocEngine::Resource hint_type(l->addr_, l->prefixlen_);
3196 hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
3197 hints.end());
3198 }
3199
3201 for (auto const& l : ctx.currentIA().old_leases_) {
3202
3203 // Send a prefix with zero lifetimes only when this lease belonged to
3204 // this client. Do not send it when we're reusing an old lease that belonged
3205 // to someone else.
3206 if (equalValues(query->getClientId(), l->duid_)) {
3207 Option6IAPrefixPtr prefix(new Option6IAPrefix(D6O_IAPREFIX, l->addr_,
3208 l->prefixlen_, 0, 0));
3209 ia_rsp->addOption(prefix);
3210 }
3211
3212 // Now remove this prefix from the hints list.
3213 AllocEngine::Resource hint_type(l->addr_, l->prefixlen_);
3214 hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
3215 }
3216
3217 // Finally, if there are any prefixes requested that we haven't dealt with
3218 // already, inform the client that he can't have them.
3219 for (auto const& prefix : hints) {
3220
3221 // Send the prefix with the zero lifetimes only if the prefix
3222 // contains non-zero value. A zero value indicates that the hint was
3223 // for the prefix length.
3224 if (!prefix.getAddress().isV6Zero()) {
3225 OptionPtr prefix_opt(new Option6IAPrefix(D6O_IAPREFIX,
3226 prefix.getAddress(),
3227 prefix.getPrefixLength(),
3228 0, 0));
3229 ia_rsp->addOption(prefix_opt);
3230 }
3231 }
3232
3233 if (!leases.empty()) {
3234 // We allocated leases so we need to update T1 and T2.
3235 setTeeTimes(min_preferred_lft, subnet, ia_rsp);
3236 } else {
3237 // All is left is to insert the status code.
3238 // The server wasn't able allocate new lease and renew an existing
3239 // lease. In that case, the server sends NoPrefixAvail per RFC 8415.
3240 ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
3242 "Sorry, no prefixes could be"
3243 " assigned at this time."));
3244 }
3245
3246 return (ia_rsp);
3247}
3248
3249void
3252
3253 // We will try to extend lease lifetime for all IA options in the client's
3254 // Renew or Rebind message.
3256
3257 // For the lease extension it is critical that the client has sent
3258 // DUID. There is no need to check for the presence of the DUID here
3259 // because we have already checked it in the sanityCheck().
3260
3261 // Save the originally selected subnet.
3262 ConstSubnet6Ptr orig_subnet = ctx.subnet_;
3263
3264 for (auto const& opt : query->options_) {
3265 switch (opt.second->getType()) {
3266 case D6O_IA_NA: {
3267 OptionPtr answer_opt = extendIA_NA(query, ctx,
3268 boost::dynamic_pointer_cast<
3269 Option6IA>(opt.second));
3270 if (answer_opt) {
3271 reply->addOption(answer_opt);
3272 }
3273 break;
3274 }
3275
3276 case D6O_IA_PD: {
3277 OptionPtr answer_opt = extendIA_PD(query, ctx,
3278 boost::dynamic_pointer_cast<
3279 Option6IA>(opt.second));
3280 if (answer_opt) {
3281 reply->addOption(answer_opt);
3282 }
3283 break;
3284 }
3285
3286 default:
3287 break;
3288 }
3289 }
3290
3291 // Need to check for pool-level DDNS parameters and if the
3292 // subnet was modified by the allocation engine, there are things
3293 // we need to do either case.
3294 checkPostAssignmentChanges(query, reply, ctx, orig_subnet);
3295}
3296
3297void
3300
3301 // We need to release addresses for all IA options in the client's
3302 // RELEASE message.
3303
3310
3311 // Let's set the status to be success by default. We can override it with
3312 // error status if needed. The important thing to understand here is that
3313 // the global status code may be set to success only if all IA options were
3314 // handled properly. Therefore the releaseIA_NA and releaseIA_PD options
3315 // may turn the status code to some error, but can't turn it back to success.
3316 int general_status = STATUS_Success;
3317 for (auto const& opt : release->options_) {
3318 Lease6Ptr old_lease;
3319 switch (opt.second->getType()) {
3320 case D6O_IA_NA: {
3321 OptionPtr answer_opt = releaseIA_NA(ctx.duid_, release, general_status,
3322 boost::dynamic_pointer_cast<Option6IA>(opt.second),
3323 old_lease);
3324 if (answer_opt) {
3325 reply->addOption(answer_opt);
3326 }
3327 break;
3328 }
3329 case D6O_IA_PD: {
3330 OptionPtr answer_opt = releaseIA_PD(ctx.duid_, release, general_status,
3331 boost::dynamic_pointer_cast<Option6IA>(opt.second),
3332 old_lease);
3333 if (answer_opt) {
3334 reply->addOption(answer_opt);
3335 }
3336 break;
3337 }
3338 // @todo: add support for IA_TA
3339 default:
3340 // remaining options are stateless and thus ignored in this context
3341 ;
3342 }
3343
3344 // Store the old lease.
3345 if (old_lease) {
3346 ctx.currentIA().old_leases_.push_back(old_lease);
3347 }
3348 }
3349
3350 // Include top-level status code as well.
3351 reply->addOption(createStatusCode(*release, general_status,
3352 "Summary status for all processed IA_NAs"));
3353}
3354
3356Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
3357 int& general_status, boost::shared_ptr<Option6IA> ia,
3358 Lease6Ptr& old_lease) {
3359
3361 .arg(query->getLabel())
3362 .arg(ia->getIAID());
3363
3364 // Release can be done in one of two ways:
3365 // Approach 1: extract address from client's IA_NA and see if it belongs
3366 // to this particular client.
3367 // Approach 2: find a subnet for this client, get a lease for
3368 // this subnet/duid/iaid and check if its content matches to what the
3369 // client is asking us to release.
3370 //
3371 // This method implements approach 1.
3372
3373 // That's our response
3374 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
3375
3376 Option6IAAddrPtr release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
3377 (ia->getOption(D6O_IAADDR));
3378 if (!release_addr) {
3379 ia_rsp->addOption(createStatusCode(*query, STATUS_NoBinding,
3380 "You did not include an address in your RELEASE"));
3381 general_status = STATUS_NoBinding;
3382 return (ia_rsp);
3383 }
3384
3386 release_addr->getAddress());
3387
3388 if (!lease || (lease->state_ == Lease::STATE_REGISTERED)) {
3389 // client releasing a lease that we don't know about.
3390
3391 // Insert status code NoBinding.
3392 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3393 "Sorry, no known leases for this duid/iaid, can't release."));
3394 general_status = STATUS_NoBinding;
3395
3396 return (ia_rsp);
3397 }
3398
3399 if (!lease->duid_) {
3400 // Something is gravely wrong here. We do have a lease, but it does not
3401 // have mandatory DUID information attached. Someone was messing with our
3402 // database.
3403
3405 .arg(query->getLabel())
3406 .arg(release_addr->getAddress().toText());
3407
3408 general_status = STATUS_UnspecFail;
3409 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3410 "Database consistency check failed when trying to RELEASE"));
3411 return (ia_rsp);
3412 }
3413
3414 if (*duid != *(lease->duid_)) {
3415
3416 // Sorry, it's not your address. You can't release it.
3418 .arg(query->getLabel())
3419 .arg(release_addr->getAddress().toText())
3420 .arg(lease->duid_->toText());
3421
3422 general_status = STATUS_NoBinding;
3423 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3424 "This address does not belong to you, you can't release it"));
3425 return (ia_rsp);
3426 }
3427
3428 if (ia->getIAID() != lease->iaid_) {
3429 // This address belongs to this client, but to a different IA
3431 .arg(query->getLabel())
3432 .arg(release_addr->getAddress().toText())
3433 .arg(lease->iaid_)
3434 .arg(ia->getIAID());
3435 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3436 "This is your address, but you used wrong IAID"));
3437 general_status = STATUS_NoBinding;
3438 return (ia_rsp);
3439 }
3440
3441 // It is not necessary to check if the address matches as we used
3442 // getLease6(addr) method that is supposed to return a proper lease.
3443
3444 bool skip = false;
3445 // Execute all callouts registered for packet6_send
3446 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
3447 CalloutHandlePtr callout_handle = getCalloutHandle(query);
3448
3449 // Use the RAII wrapper to make sure that the callout handle state is
3450 // reset when this object goes out of scope. All hook points must do
3451 // it to prevent possible circular dependency between the callout
3452 // handle and its arguments.
3453 ScopedCalloutHandleState callout_handle_state(callout_handle);
3454
3455 // Enable copying options from the packet within hook library.
3456 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
3457
3458 // Delete all previous arguments
3459 callout_handle->deleteAllArguments();
3460
3461 // Pass the original packet
3462 callout_handle->setArgument("query6", query);
3463
3464 // Pass the lease to be updated
3465 callout_handle->setArgument("lease6", lease);
3466
3467 // Call all installed callouts
3468 HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
3469
3470 // Callouts decided to skip the next processing step. The next
3471 // processing step would be to send the packet, so skip at this
3472 // stage means "drop response".
3473 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3474 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3475 skip = true;
3477 .arg(query->getLabel());
3478 }
3479 }
3480
3481 // Ok, we've passed all checks. Let's release this address.
3482 bool success = false; // was the removal operation successful?
3483 bool expired = false; // explicitly expired instead of removed?
3484 auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3485
3486 // Callout didn't indicate to skip the release process. Let's release
3487 // the lease.
3488 if (!skip) {
3489 // Delete lease only if affinity is disabled.
3490 if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3491 expiration_cfg->getHoldReclaimedTime() &&
3492 lease->valid_lft_ != Lease::INFINITY_LFT) {
3493 // Expire the lease.
3494 lease->valid_lft_ = 0;
3495 lease->preferred_lft_ = 0;
3496 // Set the lease state to released to indicate that this lease
3497 // must be preserved in the database. It is particularly useful
3498 // in HA to differentiate between the leases that should be
3499 // updated in the partner's database and deleted from the partner's
3500 // database.
3501 lease->state_ = Lease6::STATE_RELEASED;
3503 expired = true;
3504 success = true;
3505 } else {
3506 success = LeaseMgrFactory::instance().deleteLease(lease);
3507 }
3508 }
3509
3510 // Here the success should be true if we removed lease successfully
3511 // and false if skip flag was set or the removal failed for whatever reason
3512
3513 if (!success) {
3514 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3515 "Server failed to release a lease"));
3516
3518 .arg(query->getLabel())
3519 .arg(lease->addr_.toText())
3520 .arg(lease->iaid_);
3521 general_status = STATUS_UnspecFail;
3522
3523 return (ia_rsp);
3524 } else {
3525 old_lease = lease;
3526
3528 .arg(query->getLabel())
3529 .arg(lease->addr_.toText())
3530 .arg(lease->iaid_);
3531
3532 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
3533 "Lease released. Thank you, please come again."));
3534
3535 if (expired) {
3537 .arg(query->getLabel())
3538 .arg(lease->addr_.toText())
3539 .arg(lease->iaid_);
3540 } else {
3542 .arg(query->getLabel())
3543 .arg(lease->addr_.toText())
3544 .arg(lease->iaid_);
3545
3546 // Check if a lease has flags indicating that the FQDN update has
3547 // been performed. If so, create NameChangeRequest which removes
3548 // the entries.
3549 queueNCR(CHG_REMOVE, lease);
3550 }
3551
3552 // Need to decrease statistic for assigned addresses.
3554 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-nas"),
3555 static_cast<int64_t>(-1));
3556
3557 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3558 if (subnet) {
3559 auto const& pool = subnet->getPool(Lease::TYPE_NA, lease->addr_, false);
3560 if (pool) {
3562 StatsMgr::generateName("subnet", subnet->getID(),
3563 StatsMgr::generateName("pool", pool->getID(), "assigned-nas")),
3564 static_cast<int64_t>(-1));
3565 }
3566 }
3567
3568 return (ia_rsp);
3569 }
3570}
3571
3573Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
3574 int& general_status, boost::shared_ptr<Option6IA> ia,
3575 Lease6Ptr& old_lease) {
3576 // Release can be done in one of two ways:
3577 // Approach 1: extract address from client's IA_NA and see if it belongs
3578 // to this particular client.
3579 // Approach 2: find a subnet for this client, get a lease for
3580 // this subnet/duid/iaid and check if its content matches to what the
3581 // client is asking us to release.
3582 //
3583 // This method implements approach 1.
3584
3585 // That's our response. We will fill it in as we check the lease to be
3586 // released.
3587 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
3588
3589 boost::shared_ptr<Option6IAPrefix> release_prefix =
3590 boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
3591 if (!release_prefix) {
3592 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3593 "You did not include a prefix in your RELEASE"));
3594 general_status = STATUS_NoBinding;
3595 return (ia_rsp);
3596 }
3597
3599 release_prefix->getAddress());
3600
3601 if (!lease) {
3602 // Client releasing a lease that we don't know about.
3603
3604 // Insert status code NoBinding.
3605 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3606 "Sorry, no known leases for this duid/iaid, can't release."));
3607 general_status = STATUS_NoBinding;
3608
3609 return (ia_rsp);
3610 }
3611
3612 if (!lease->duid_) {
3613 // Something is gravely wrong here. We do have a lease, but it does not
3614 // have mandatory DUID information attached. Someone was messing with our
3615 // database.
3617 .arg(query->getLabel())
3618 .arg(release_prefix->getAddress().toText())
3619 .arg(static_cast<int>(release_prefix->getLength()));
3620
3621 general_status = STATUS_UnspecFail;
3622 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3623 "Database consistency check failed when trying to RELEASE"));
3624 return (ia_rsp);
3625 }
3626
3627 if (*duid != *(lease->duid_)) {
3628 // Sorry, it's not your address. You can't release it.
3630 .arg(query->getLabel())
3631 .arg(release_prefix->getAddress().toText())
3632 .arg(static_cast<int>(release_prefix->getLength()))
3633 .arg(lease->duid_->toText());
3634
3635 general_status = STATUS_NoBinding;
3636 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3637 "This address does not belong to you, you can't release it"));
3638 return (ia_rsp);
3639 }
3640
3641 if (ia->getIAID() != lease->iaid_) {
3642 // This address belongs to this client, but to a different IA
3644 .arg(query->getLabel())
3645 .arg(release_prefix->getAddress().toText())
3646 .arg(static_cast<int>(release_prefix->getLength()))
3647 .arg(lease->iaid_)
3648 .arg(ia->getIAID());
3649 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_NoBinding,
3650 "This is your address, but you used wrong IAID"));
3651 general_status = STATUS_NoBinding;
3652 return (ia_rsp);
3653 }
3654
3655 // It is not necessary to check if the address matches as we used
3656 // getLease6(addr) method that is supposed to return a proper lease.
3657
3658 bool skip = false;
3659 // Execute all callouts registered for packet6_send
3660 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_release_)) {
3661 CalloutHandlePtr callout_handle = getCalloutHandle(query);
3662
3663 // Use the RAII wrapper to make sure that the callout handle state is
3664 // reset when this object goes out of scope. All hook points must do
3665 // it to prevent possible circular dependency between the callout
3666 // handle and its arguments.
3667 ScopedCalloutHandleState callout_handle_state(callout_handle);
3668
3669 // Enable copying options from the packet within hook library.
3670 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(query);
3671
3672 // Pass the original packet
3673 callout_handle->setArgument("query6", query);
3674
3675 // Pass the lease to be updated
3676 callout_handle->setArgument("lease6", lease);
3677
3678 // Call all installed callouts
3679 HooksManager::callCallouts(Hooks.hook_index_lease6_release_, *callout_handle);
3680
3681 // Callouts decided to skip the next processing step. The next
3682 // processing step would be to send the packet, so skip at this
3683 // stage means "drop response".
3684 if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
3685 (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
3686 skip = true;
3688 .arg(query->getLabel());
3689 }
3690 }
3691
3692 // Ok, we've passed all checks. Let's release this prefix.
3693 bool success = false; // was the removal operation successful?
3694 bool expired = false; // explicitly expired instead of removed?
3695 auto expiration_cfg = CfgMgr::instance().getCurrentCfg()->getCfgExpiration();
3696
3697 // Callout didn't indicate to skip the release process. Let's release
3698 // the lease.
3699 if (!skip) {
3700 // Delete lease only if affinity is disabled.
3701 if (expiration_cfg->getFlushReclaimedTimerWaitTime() &&
3702 expiration_cfg->getHoldReclaimedTime() &&
3703 lease->valid_lft_ != Lease::INFINITY_LFT) {
3704 // Expire the lease.
3705 lease->valid_lft_ = 0;
3706 lease->preferred_lft_ = 0;
3707 // Set the lease state to released to indicate that this lease
3708 // must be preserved in the database. It is particularly useful
3709 // in HA to differentiate between the leases that should be
3710 // updated in the partner's database and deleted from the partner's
3711 // database.
3712 lease->state_ = Lease6::STATE_RELEASED;
3714 expired = true;
3715 success = true;
3716 } else {
3717 success = LeaseMgrFactory::instance().deleteLease(lease);
3718 }
3719 }
3720
3721 // Here the success should be true if we removed lease successfully
3722 // and false if skip flag was set or the removal failed for whatever reason
3723
3724 if (!success) {
3725 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_UnspecFail,
3726 "Server failed to release a lease"));
3727
3729 .arg(query->getLabel())
3730 .arg(lease->addr_.toText())
3731 .arg(static_cast<int>(lease->prefixlen_))
3732 .arg(lease->iaid_);
3733 general_status = STATUS_UnspecFail;
3734
3735 } else {
3736 old_lease = lease;
3737
3739 .arg(query->getLabel())
3740 .arg(lease->addr_.toText())
3741 .arg(static_cast<int>(lease->prefixlen_))
3742 .arg(lease->iaid_);
3743
3744 ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
3745 "Lease released. Thank you, please come again."));
3746
3747 if (expired) {
3749 .arg(query->getLabel())
3750 .arg(lease->addr_.toText())
3751 .arg(static_cast<int>(lease->prefixlen_))
3752 .arg(lease->iaid_);
3753 } else {
3755 .arg(query->getLabel())
3756 .arg(lease->addr_.toText())
3757 .arg(static_cast<int>(lease->prefixlen_))
3758 .arg(lease->iaid_);
3759 }
3760
3761 // Need to decrease statistic for assigned prefixes.
3763 StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"),
3764 static_cast<int64_t>(-1));
3765
3766 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3767 if (subnet) {
3768 auto const& pool = subnet->getPool(Lease::TYPE_PD, lease->addr_, false);
3769 if (pool) {
3771 StatsMgr::generateName("subnet", subnet->getID(),
3772 StatsMgr::generateName("pd-pool", pool->getID(), "assigned-pds")),
3773 static_cast<int64_t>(-1));
3774 }
3775 }
3776 }
3777
3778 return (ia_rsp);
3779}
3780
3781Pkt6Ptr
3783
3784 Pkt6Ptr solicit = ctx.query_;
3785 Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
3786
3787 // Handle Rapid Commit option, if present.
3788 if (ctx.subnet_ && ctx.subnet_->getRapidCommit()) {
3789 OptionPtr opt_rapid_commit = solicit->getOption(D6O_RAPID_COMMIT);
3790 if (opt_rapid_commit) {
3791
3793 .arg(solicit->getLabel());
3794
3795 // If Rapid Commit has been sent by the client, change the
3796 // response type to Reply and include Rapid Commit option.
3797 response->setType(DHCPV6_REPLY);
3798 response->addOption(opt_rapid_commit);
3799 }
3800 }
3801
3802 // "Fake" allocation is the case when the server is processing the Solicit
3803 // message without the Rapid Commit option and advertises a lease to
3804 // the client, but doesn't commit this lease to the lease database. If
3805 // the Solicit contains the Rapid Commit option and the server is
3806 // configured to honor the Rapid Commit option, or the client has sent
3807 // the Request message, the lease will be committed to the lease
3808 // database. The type of the server's response may be used to determine
3809 // if this is the fake allocation case or not. When the server sends
3810 // Reply message it means that it is committing leases. Other message
3811 // type (Advertise) means that server is not committing leases (fake
3812 // allocation).
3813 ctx.fake_allocation_ = (response->getType() != DHCPV6_REPLY);
3814
3815 processClientFqdn(solicit, response, ctx);
3816
3817 if (MultiThreadingMgr::instance().getMode()) {
3818 // The lease reclamation cannot run at the same time.
3819 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3820
3821 assignLeases(solicit, response, ctx);
3822 } else {
3823 assignLeases(solicit, response, ctx);
3824 }
3825
3827 // Evaluate additional classes.
3828 evaluateAdditionalClasses(solicit, ctx);
3829
3831 .arg(solicit->getLabel())
3832 .arg(solicit->getName())
3833 .arg(solicit->getClasses().toText());
3834
3835 copyClientOptions(solicit, response);
3836 CfgOptionList co_list;
3837 buildCfgOptionList(solicit, ctx, co_list);
3838 appendDefaultOptions(solicit, response, co_list);
3839 appendRequestedOptions(solicit, response, co_list);
3840 appendRequestedVendorOptions(solicit, response, ctx, co_list);
3841
3842 updateReservedFqdn(ctx, response);
3843
3844 // Only generate name change requests if sending a Reply as a result
3845 // of receiving Rapid Commit option.
3846 if (response->getType() == DHCPV6_REPLY) {
3847 createNameChangeRequests(response, ctx);
3848 }
3849
3850 return (response);
3851}
3852
3853Pkt6Ptr
3855
3856 Pkt6Ptr request = ctx.query_;
3857 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
3858
3859 processClientFqdn(request, reply, ctx);
3860
3861 if (MultiThreadingMgr::instance().getMode()) {
3862 // The lease reclamation cannot run at the same time.
3863 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3864
3865 assignLeases(request, reply, ctx);
3866 } else {
3867 assignLeases(request, reply, ctx);
3868 }
3869
3871 // Evaluate additional classes.
3872 evaluateAdditionalClasses(request, ctx);
3873
3875 .arg(request->getLabel())
3876 .arg(request->getName())
3877 .arg(request->getClasses().toText());
3878
3879 copyClientOptions(request, reply);
3880 CfgOptionList co_list;
3881 buildCfgOptionList(request, ctx, co_list);
3882 appendDefaultOptions(request, reply, co_list);
3883 appendRequestedOptions(request, reply, co_list);
3884 appendRequestedVendorOptions(request, reply, ctx, co_list);
3885
3886 updateReservedFqdn(ctx, reply);
3887 generateFqdn(reply, ctx);
3888 createNameChangeRequests(reply, ctx);
3889
3890 return (reply);
3891}
3892
3893Pkt6Ptr
3895
3896 Pkt6Ptr renew = ctx.query_;
3897 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
3898
3899 processClientFqdn(renew, reply, ctx);
3900
3901 if (MultiThreadingMgr::instance().getMode()) {
3902 // The lease reclamation cannot run at the same time.
3903 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3904
3905 extendLeases(renew, reply, ctx);
3906 } else {
3907 extendLeases(renew, reply, ctx);
3908 }
3909
3911 // Evaluate additional classes.
3912 evaluateAdditionalClasses(renew, ctx);
3913
3915 .arg(renew->getLabel())
3916 .arg(renew->getName())
3917 .arg(renew->getClasses().toText());
3918
3919 copyClientOptions(renew, reply);
3920 CfgOptionList co_list;
3921 buildCfgOptionList(renew, ctx, co_list);
3922 appendDefaultOptions(renew, reply, co_list);
3923 appendRequestedOptions(renew, reply, co_list);
3924 appendRequestedVendorOptions(renew, reply, ctx, co_list);
3925
3926 updateReservedFqdn(ctx, reply);
3927 generateFqdn(reply, ctx);
3928 createNameChangeRequests(reply, ctx);
3929
3930 return (reply);
3931}
3932
3933Pkt6Ptr
3935
3936 Pkt6Ptr rebind = ctx.query_;
3937 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
3938
3939 processClientFqdn(rebind, reply, ctx);
3940
3941 if (MultiThreadingMgr::instance().getMode()) {
3942 // The lease reclamation cannot run at the same time.
3943 ReadLockGuard share(alloc_engine_->getReadWriteMutex());
3944
3945 extendLeases(rebind, reply, ctx);
3946 } else {
3947 extendLeases(rebind, reply, ctx);
3948 }
3949
3951 // Evaluate additional classes.
3952 evaluateAdditionalClasses(rebind, ctx);
3953
3955 .arg(rebind->getLabel())
3956 .arg(rebind->getName())
3957 .arg(rebind->getClasses().toText());
3958
3959 copyClientOptions(rebind, reply);
3960 CfgOptionList co_list;
3961 buildCfgOptionList(rebind, ctx, co_list);
3962 appendDefaultOptions(rebind, reply, co_list);
3963 appendRequestedOptions(rebind, reply, co_list);
3964 appendRequestedVendorOptions(rebind, reply, ctx, co_list);
3965
3966 updateReservedFqdn(ctx, reply);
3967 generateFqdn(reply, ctx);
3968 createNameChangeRequests(reply, ctx);
3969
3970 return (reply);
3971}
3972
3973Pkt6Ptr
3975
3976 Pkt6Ptr confirm = ctx.query_;
3978 // Evaluate additional classes.
3979 evaluateAdditionalClasses(confirm, ctx);
3980
3982 .arg(confirm->getLabel())
3983 .arg(confirm->getName())
3984 .arg(confirm->getClasses().toText());
3985
3986 // Get IA_NAs from the Confirm. If there are none, the message is
3987 // invalid and must be discarded. There is nothing more to do.
3988 OptionCollection ias = confirm->getOptions(D6O_IA_NA);
3989 if (ias.empty()) {
3990 return (Pkt6Ptr());
3991 }
3992
3993 // The server sends Reply message in response to Confirm.
3994 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
3995 // Make sure that the necessary options are included.
3996 copyClientOptions(confirm, reply);
3997 CfgOptionList co_list;
3998 buildCfgOptionList(confirm, ctx, co_list);
3999 appendDefaultOptions(confirm, reply, co_list);
4000 appendRequestedOptions(confirm, reply, co_list);
4001 appendRequestedVendorOptions(confirm, reply, ctx, co_list);
4002 // Indicates if at least one address has been verified. If no addresses
4003 // are verified it means that the client has sent no IA_NA options
4004 // or no IAAddr options and that client's message has to be discarded.
4005 bool verified = false;
4006 // Check if subnet was selected for the message. If no subnet
4007 // has been selected, the client is not on link.
4008 ConstSubnetPtr subnet = ctx.subnet_;
4009
4010 // Regardless if the subnet has been selected or not, we will iterate
4011 // over the IA_NA options to check if they hold any addresses. If there
4012 // are no, the Confirm is discarded.
4013 // Check addresses in IA_NA options and make sure they are appropriate.
4014 for (auto const& ia : ias) {
4015 const OptionCollection& opts = ia.second->getOptions();
4016 for (auto const& opt : opts) {
4017 // Ignore options other than IAAddr.
4018 if (opt.second->getType() == D6O_IAADDR) {
4019 // Check that the address is in range in the subnet selected.
4020 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
4021 Option6IAAddr>(opt.second);
4022 // If there is subnet selected and the address has been included
4023 // in IA_NA, mark it verified and verify that it belongs to the
4024 // subnet.
4025 if (iaaddr) {
4026 // If at least one address is not in range, then return
4027 // the NotOnLink status code.
4028 if (subnet && !subnet->inRange(iaaddr->getAddress())) {
4029 std::ostringstream status_msg;
4030 status_msg << "Address " << iaaddr->getAddress()
4031 << " is not on link.";
4032 reply->addOption(createStatusCode(*confirm,
4034 status_msg.str()));
4035 return (reply);
4036 }
4037 verified = true;
4038 } else {
4039 isc_throw(Unexpected, "failed to cast the IA Address option"
4040 " to the Option6IAAddrPtr. This is programming"
4041 " error and should be reported");
4042 }
4043 }
4044 }
4045 }
4046
4047 // It seems that the client hasn't included any addresses in which case
4048 // the Confirm must be discarded.
4049 if (!verified) {
4050 return (Pkt6Ptr());
4051 }
4052
4053 // If there is a subnet, there were addresses in IA_NA options and the
4054 // addresses where consistent with the subnet then the client is on link.
4055 if (subnet) {
4056 // All addresses in range, so return success.
4057 reply->addOption(createStatusCode(*confirm, STATUS_Success,
4058 "All addresses are on-link"));
4059 } else {
4060 reply->addOption(createStatusCode(*confirm, STATUS_NotOnLink,
4061 "No subnet selected"));
4062 }
4063
4064 return (reply);
4065}
4066
4067Pkt6Ptr
4069
4070 Pkt6Ptr release = ctx.query_;
4072 // Evaluate additional classes.
4073 evaluateAdditionalClasses(release, ctx);
4074
4076 .arg(release->getLabel())
4077 .arg(release->getName())
4078 .arg(release->getClasses().toText());
4079
4080 // Create an empty Reply message.
4081 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
4082
4083 // Copy client options (client-id, also relay information if present)
4084 copyClientOptions(release, reply);
4085
4086 // Get the configured option list
4087 CfgOptionList co_list;
4088 // buildCfgOptionList(release, ctx, co_list);
4089 appendDefaultOptions(release, reply, co_list);
4090
4091 releaseLeases(release, reply, ctx);
4092
4095
4096 return (reply);
4097}
4098
4099Pkt6Ptr
4101
4102 Pkt6Ptr decline = ctx.query_;
4104 // Evaluate additional classes.
4105 evaluateAdditionalClasses(decline, ctx);
4106
4108 .arg(decline->getLabel())
4109 .arg(decline->getName())
4110 .arg(decline->getClasses().toText());
4111
4112 // Create an empty Reply message.
4113 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
4114
4115 // Copy client options (client-id, also relay information if present)
4116 copyClientOptions(decline, reply);
4117
4118 // Get the configured option list
4119 CfgOptionList co_list;
4120 buildCfgOptionList(decline, ctx, co_list);
4121
4122 // Include server-id
4123 appendDefaultOptions(decline, reply, co_list);
4124
4125 if (declineLeases(decline, reply, ctx)) {
4126 return (reply);
4127 } else {
4128
4129 // declineLeases returns false only if the hooks set the next step
4130 // status to DROP. We'll just doing as requested.
4131 return (Pkt6Ptr());
4132 }
4133}
4134
4135bool
4138
4139 // We need to decline addresses for all IA_NA options in the client's
4140 // DECLINE message.
4141
4142 // Let's set the status to be success by default. We can override it with
4143 // error status if needed. The important thing to understand here is that
4144 // the global status code may be set to success only if all IA options were
4145 // handled properly. Therefore the declineIA options
4146 // may turn the status code to some error, but can't turn it back to success.
4147 int general_status = STATUS_Success;
4148
4149 for (auto const& opt : decline->options_) {
4150 switch (opt.second->getType()) {
4151 case D6O_IA_NA: {
4152 OptionPtr answer_opt = declineIA(decline, ctx.duid_, general_status,
4153 boost::dynamic_pointer_cast<Option6IA>(opt.second),
4154 ctx.new_leases_);
4155 if (answer_opt) {
4156
4157 // We have an answer, let's use it.
4158 reply->addOption(answer_opt);
4159 } else {
4160
4161 // The only case when declineIA could return NULL is if one of the
4162 // hook callouts set next step status to DROP. We just need to drop
4163 // this packet.
4164 return (false);
4165 }
4166 break;
4167 }
4168 default:
4169 // We don't care for the remaining options
4170 ;
4171 }
4172 }
4173
4174 return (true);
4175}
4176
4178Dhcpv6Srv::declineIA(const Pkt6Ptr& decline, const DuidPtr& duid,
4179 int& general_status, boost::shared_ptr<Option6IA> ia,
4180 Lease6Collection& new_leases) {
4181
4183 .arg(decline->getLabel())
4184 .arg(ia->getIAID());
4185
4186 // Decline can be done in one of two ways:
4187 // Approach 1: extract address from client's IA_NA and see if it belongs
4188 // to this particular client.
4189 // Approach 2: find a subnet for this client, get a lease for
4190 // this subnet/duid/iaid and check if its content matches to what the
4191 // client is asking us to decline.
4192 //
4193 // This method implements approach 1.
4194
4195 // That's our response
4196 boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
4197
4198 const OptionCollection& opts = ia->getOptions();
4199 int total_addrs = 0; // Let's count the total number of addresses.
4200 for (auto const& opt : opts) {
4201
4202 // Let's ignore nested options other than IAADDR (there shouldn't be anything
4203 // else in IA_NA in Decline message, but let's be on the safe side).
4204 if (opt.second->getType() != D6O_IAADDR) {
4205 continue;
4206 }
4207 Option6IAAddrPtr decline_addr = boost::dynamic_pointer_cast<Option6IAAddr>
4208 (opt.second);
4209 if (!decline_addr) {
4210 continue;
4211 }
4212
4213 total_addrs++;
4214
4216 decline_addr->getAddress());
4217
4218 if (!lease || lease->expired() || lease->state_ != Lease::STATE_DEFAULT) {
4219 // Client trying to decline a lease that we don't know about.
4221 .arg(decline->getLabel()).arg(decline_addr->getAddress().toText());
4222
4223 // According to RFC 8415, section 18.3.8:
4224 // "For each IA in the Decline message for which the server has no
4225 // binding information, the server adds an IA option using the IAID
4226 // from the Decline message and includes a Status Code option with
4227 // the value NoBinding in the IA option".
4228 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4229 "Server does not know about such an address."));
4230
4231 // In the same section of RFC 8415:
4232 // "The server ignores addresses not assigned to the IAs (though it may"
4233 // choose to log an error if it finds such addresses)."
4234 continue; // There may be other addresses.
4235 }
4236
4237 if (!lease->duid_) {
4238 // Something is gravely wrong here. We do have a lease, but it does not
4239 // have mandatory DUID information attached. Someone was messing with our
4240 // database.
4241
4243 .arg(decline->getLabel())
4244 .arg(decline_addr->getAddress().toText());
4245
4246 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_UnspecFail,
4247 "Database consistency check failed when attempting Decline."));
4248
4249 continue;
4250 }
4251
4252 // Ok, there's a sane lease with an address. Let's check if DUID matches first.
4253 if (*duid != *(lease->duid_)) {
4254
4255 // Sorry, it's not your address. You can't release it.
4257 .arg(decline->getLabel())
4258 .arg(decline_addr->getAddress().toText())
4259 .arg(lease->duid_->toText());
4260
4261 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4262 "This address does not belong to you, you can't decline it"));
4263
4264 continue;
4265 }
4266
4267 // Let's check if IAID matches.
4268 if (ia->getIAID() != lease->iaid_) {
4269 // This address belongs to this client, but to a different IA
4271 .arg(decline->getLabel())
4272 .arg(lease->addr_.toText())
4273 .arg(ia->getIAID())
4274 .arg(lease->iaid_);
4275 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4276 "This is your address, but you used wrong IAID"));
4277
4278 continue;
4279 }
4280
4281 // Ok, all is good. Decline this lease.
4282 if (!declineLease(decline, lease, ia_rsp)) {
4283 // declineLease returns false only when hook callouts set the next
4284 // step status to drop. We just propagate the bad news here.
4285 return (OptionPtr());
4286
4287 } else {
4288 new_leases.push_back(lease);
4289 }
4290 }
4291
4292 if (total_addrs == 0) {
4293 setStatusCode(ia_rsp, createStatusCode(*decline, *ia_rsp, STATUS_NoBinding,
4294 "No addresses sent in IA_NA"));
4295 general_status = STATUS_NoBinding;
4296 }
4297
4298 return (ia_rsp);
4299}
4300
4301void
4302Dhcpv6Srv::setStatusCode(boost::shared_ptr<isc::dhcp::Option6IA>& container,
4303 const OptionPtr& status) {
4304 // Let's delete any old status code we may have.
4305 container->delOption(D6O_STATUS_CODE);
4306
4307 container->addOption(status);
4308}
4309
4310bool
4311Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
4312 boost::shared_ptr<Option6IA> ia_rsp) {
4313 // We do not want to decrease the assigned-nas at this time. While
4314 // technically a declined address is no longer allocated, the
4315 // primary usage of the assigned-nas statistic is to monitor pool
4316 // utilization. Most people would forget to include declined-addresses
4317 // in the calculation, and simply do assigned-nas/total-nas. This
4318 // would have a bias towards under-representing pool utilization,
4319 // if we decreased allocated immediately after receiving DHCPDECLINE,
4320 // rather than later when we recover the address.
4321
4322 // Let's call lease6_decline hooks if necessary.
4323 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_decline_)) {
4324 CalloutHandlePtr callout_handle = getCalloutHandle(decline);
4325
4326 // Use the RAII wrapper to make sure that the callout handle state is
4327 // reset when this object goes out of scope. All hook points must do
4328 // it to prevent possible circular dependency between the callout
4329 // handle and its arguments.
4330 ScopedCalloutHandleState callout_handle_state(callout_handle);
4331
4332 // Enable copying options from the packet within hook library.
4333 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(decline);
4334
4335 // Pass the original packet
4336 callout_handle->setArgument("query6", decline);
4337
4338 // Pass the lease to be updated
4339 callout_handle->setArgument("lease6", lease);
4340
4341 // Call callouts
4342 HooksManager::callCallouts(Hooks.hook_index_lease6_decline_,
4343 *callout_handle);
4344
4345 // Callouts decided to SKIP the next processing step. The next
4346 // processing step would be to actually decline the lease, so we'll
4347 // keep the lease as is.
4348 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4350 .arg(decline->getLabel())
4351 .arg(decline->getIface())
4352 .arg(lease->addr_.toText());
4353 return (true);
4354 }
4355
4356 // Callouts decided to DROP the packet. Let's simply log it and
4357 // return false, so callers will act accordingly.
4358 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
4360 .arg(decline->getLabel())
4361 .arg(decline->getIface())
4362 .arg(lease->addr_.toText());
4363 return (false);
4364 }
4365 }
4366
4367 Lease6Ptr old_values = boost::make_shared<Lease6>(*lease);
4368
4369 // @todo: Call hooks.
4370
4371 // We need to disassociate the lease from the client. Once we move a lease
4372 // to declined state, it is no longer associated with the client in any
4373 // way.
4374 lease->decline(CfgMgr::instance().getCurrentCfg()->getDeclinePeriod());
4375
4376 try {
4378 } catch (const Exception& ex) {
4379 // Update failed.
4381 .arg(decline->getLabel())
4382 .arg(lease->addr_.toText())
4383 .arg(ex.what());
4384 return (false);
4385 }
4386
4387 // Check if a lease has flags indicating that the FQDN update has
4388 // been performed. If so, create NameChangeRequest which removes
4389 // the entries. This method does all necessary checks.
4390 queueNCR(CHG_REMOVE, old_values);
4391
4392 // Bump up the subnet-specific statistic.
4394 StatsMgr::generateName("subnet", lease->subnet_id_, "declined-addresses"),
4395 static_cast<int64_t>(1));
4396
4397 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
4398 if (subnet) {
4399 auto const& pool = subnet->getPool(Lease::TYPE_NA, lease->addr_, false);
4400 if (pool) {
4402 StatsMgr::generateName("subnet", subnet->getID(),
4403 StatsMgr::generateName("pool", pool->getID(), "declined-addresses")),
4404 static_cast<int64_t>(1));
4405 }
4406 }
4407
4408 // Global declined addresses counter.
4409 StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
4410
4411 LOG_INFO(lease6_logger, DHCP6_DECLINE_LEASE).arg(decline->getLabel())
4412 .arg(lease->addr_.toText()).arg(lease->valid_lft_);
4413
4414 ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_Success,
4415 "Lease declined. Hopefully the next one will be better."));
4416
4417 return (true);
4418}
4419
4420Pkt6Ptr
4422
4423 Pkt6Ptr inf_request = ctx.query_;
4424 conditionallySetReservedClientClasses(inf_request, ctx);
4425 // Evaluate additional classes.
4426 evaluateAdditionalClasses(inf_request, ctx);
4427
4429 .arg(inf_request->getLabel())
4430 .arg(inf_request->getName())
4431 .arg(inf_request->getClasses().toText());
4432
4433 // Create a Reply packet, with the same trans-id as the client's.
4434 Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, inf_request->getTransid()));
4435
4436 // Copy client options (client-id, also relay information if present)
4437 copyClientOptions(inf_request, reply);
4438
4439 // Build the configured option list for append methods
4440 CfgOptionList co_list;
4441 buildCfgOptionList(inf_request, ctx, co_list);
4442
4443 // Append default options, i.e. options that the server is supposed
4444 // to put in all messages it sends (server-id for now, but possibly other
4445 // options once we start supporting authentication)
4446 appendDefaultOptions(inf_request, reply, co_list);
4447
4448 // Try to assign options that were requested by the client.
4449 appendRequestedOptions(inf_request, reply, co_list);
4450
4451 // Try to assign vendor options that were requested by the client.
4452 appendRequestedVendorOptions(inf_request, reply, ctx, co_list);
4453
4454 return (reply);
4455}
4456
4457void
4459
4460 // flags are in transid
4461 // uint32_t flags = dhcp4_query->getTransid();
4462 // do nothing with DHCPV4_QUERY_FLAGS_UNICAST
4463
4464 // Get the DHCPv4 message option
4465 OptionPtr dhcp4_msg = dhcp4_query->getOption(D6O_DHCPV4_MSG);
4466 if (dhcp4_msg) {
4467 try {
4468 // Forward the whole message to the DHCPv4 server via IPC
4469 Dhcp6to4Ipc::instance().send(dhcp4_query);
4470 } catch (...) {
4471 // Assume the error was already logged
4472 return;
4473 }
4474 }
4475
4476 // This method does not return anything as we always sent back
4477 // the response via Dhcp6To4Ipc.
4478}
4479
4480Pkt6Ptr
4482
4483 ConstSubnetPtr subnet = ctx.subnet_;
4484 // Silently ignore message which can't be localized
4485 if (!subnet) {
4486 return (Pkt6Ptr());
4487 }
4488
4489 Pkt6Ptr addr_reg_inf = ctx.query_;
4490
4491 // Get the client source address.
4492 IOAddress addr = addr_reg_inf->getRemoteAddr();
4493 // If there are some relays get the peer address of the closest relay
4494 // to the client.
4495 size_t relay_level = addr_reg_inf->relay_info_.size();
4496 if (relay_level > 0) {
4497 addr = addr_reg_inf->getRelay6LinkAddress(relay_level - 1);
4498 }
4499
4500 Option6IAAddrPtr iaaddr;
4501 Lease6Ptr old_lease;
4502 const uint32_t no_iaid = 0; /* there's no IAID in the ADDR-REG-INFORM */
4503
4504 // Check if the message is bad and shout be dropped.
4505 try {
4506 // Check there is no IA_NA option.
4507 if (addr_reg_inf->getOption(D6O_IA_NA)) {
4508 isc_throw(RFCViolation, "unexpected IA_NA option");
4509 }
4510
4511 // Check there is no IA_TA option.
4512 if (addr_reg_inf->getOption(D6O_IA_TA)) {
4513 isc_throw(RFCViolation, "unexpected IA_TA option");
4514 }
4515
4516 // Check there is no IA_PD option.
4517 if (addr_reg_inf->getOption(D6O_IA_PD)) {
4518 isc_throw(RFCViolation, "unexpected IA_PD option");
4519 }
4520
4521 // Get IAADDR from the Address registration inform.
4522 // There must be one.
4523 OptionCollection addrs = addr_reg_inf->getOptions(D6O_IAADDR);
4524 if (addrs.size() != 1) {
4525 isc_throw(RFCViolation, "Exactly 1 IAADDR option expected, but "
4526 << addrs.size() << " received");
4527 }
4528 iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(addrs.begin()->second);
4529 if (!iaaddr) {
4530 isc_throw(Unexpected, "can't convert the IAADDR option");
4531 }
4532
4533 // Client and IADDR addresses must match.
4534 if (addr != iaaddr->getAddress()) {
4535 isc_throw(RFCViolation, "Address mismatch: client at " << addr
4536 << " wants to register " << iaaddr->getAddress());
4537 }
4538
4539 // Should be in the subnet.
4540 if (!subnet->inRange(addr)) {
4541 isc_throw(RFCViolation, "Address " << addr << " is not in subnet "
4542 << subnet->toText() << " (id " << subnet->getID() << ")");
4543 }
4544
4545 // Check if there is a lease for the address.
4547 addr);
4548
4549 // Address registration can't be mixed with standard allocation.
4550 if (old_lease && (old_lease->state_ != Lease6::STATE_REGISTERED)) {
4551 isc_throw(RFCViolation, "Address " << addr << " already in use "
4552 << *old_lease);
4553 }
4554
4555 // Address must not be reserved.
4556 auto hosts = HostMgr::instance().getAll6(addr);
4557 if (!hosts.empty()) {
4558 isc_throw(RFCViolation, "Address " << addr << " is reserved");
4559 }
4560 } catch (const std::exception &ex) {
4561 // Incoming processing failed.
4563 .arg(addr)
4564 .arg(ex.what());
4565 return (Pkt6Ptr());
4566 }
4567
4568 // Check if the client is the same.
4569 if (old_lease) {
4570 if (old_lease->duid_ && (*ctx.duid_ != *(old_lease->duid_))) {
4572 .arg(addr)
4573 .arg(ctx.duid_->toText())
4574 .arg(old_lease->duid_->toText());
4575 }
4576 }
4577
4578 // Build response.
4579 Pkt6Ptr addr_reg_rep(new Pkt6(DHCPV6_ADDR_REG_REPLY,
4580 addr_reg_inf->getTransid()));
4581 addr_reg_rep->addOption(iaaddr);
4582
4583 // Set per-IA context values for DDNS.
4584 // Note the address is considered as not-temporary address.
4585 ctx.createIAContext();
4587 ctx.currentIA().iaid_ = no_iaid;
4588 Option6IAPtr ia(new Option6IA(D6O_IA_NA, no_iaid));
4589 ia->addOption(iaaddr);
4590 ctx.currentIA().ia_rsp_ = ia;
4591
4592 // Process FQDN.
4593 processClientFqdn(addr_reg_inf, addr_reg_rep, ctx);
4594
4595 Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, ctx.duid_,
4596 no_iaid, iaaddr->getPreferred(),
4597 iaaddr->getValid(), subnet->getID(),
4598 ctx.hwaddr_));
4599 lease->state_ = Lease6::STATE_REGISTERED;
4600 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4601 lease->fqdn_rev_ = ctx.rev_dns_update_;
4602 lease->hostname_ = ctx.hostname_;
4603
4604 conditionallySetReservedClientClasses(addr_reg_inf, ctx);
4605 // Evaluate additional classes.
4606 evaluateAdditionalClasses(addr_reg_inf, ctx);
4607
4609 .arg(addr_reg_inf->getLabel())
4610 .arg(addr_reg_inf->getName())
4611 .arg(addr_reg_inf->getClasses().toText());
4612
4613 copyClientOptions(addr_reg_inf, addr_reg_rep);
4614 CfgOptionList co_list;
4615 buildCfgOptionList(addr_reg_inf, ctx, co_list);
4616 // The RFC says to not do that...
4617 appendDefaultOptions(addr_reg_inf, addr_reg_rep, co_list);
4618 appendRequestedOptions(addr_reg_inf, addr_reg_rep, co_list);
4619 appendRequestedVendorOptions(addr_reg_inf, addr_reg_rep, ctx, co_list);
4620
4621 // Handle the "addr6_register" callout point.
4622 bool skip = false;
4623 if (HooksManager::calloutsPresent(Hooks.hook_index_addr6_register_)) {
4624 CalloutHandlePtr callout_handle = getCalloutHandle(addr_reg_inf);
4625 ScopedCalloutHandleState callout_handle_state(callout_handle);
4626
4627 // Pass the query6 argument.
4628 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(addr_reg_inf);
4629 callout_handle->setArgument("query6", addr_reg_inf);
4630
4631 // Pass the response6 argument.
4632 ScopedEnableOptionsCopy<Pkt6> rsp6_options_copy(addr_reg_rep);
4633 callout_handle->setArgument("response6", addr_reg_rep);
4634
4635 // Pass the address6 argument.
4636 callout_handle->setArgument("address6", addr);
4637
4638 // Pass the old_lease argument.
4639 callout_handle->setArgument("old_lease6", old_lease);
4640
4641 // Pass the new_lease argument.
4642 callout_handle->setArgument("new_lease6", lease);
4643
4644 // Call callouts
4645 HooksManager::callCallouts(Hooks.hook_index_addr6_register_, *callout_handle);
4646
4647 // Callouts decided to skip the next processing step. This means
4648 // to not perform the lease operation.
4649 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4652 .arg(addr_reg_inf->getLabel())
4653 .arg(old_lease ? "update" : "add")
4654 .arg(addr);
4655 skip = true;
4656 } else
4657 // Callouts decided to drop the next processing step. This means
4658 // cancel processing so drop the query.
4659 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
4662 .arg(addr_reg_inf->getLabel())
4663 .arg(addr);
4664 return (Pkt6Ptr());
4665 }
4666 }
4667
4668 if (!skip) {
4669 // Statefull registration.
4670 if (old_lease) {
4671 try {
4673 } catch (const std::exception& ex) {
4674 // Assume that stats and DNS were handled by someone else.
4676 .arg(addr)
4677 .arg(ex.what());
4678 return (Pkt6Ptr());
4679 }
4680 // Save the old lease for the DNS update.
4681 ctx.currentIA().changed_leases_.push_back(old_lease);
4682 // Update stats when the subnet changed.
4683 if (old_lease->subnet_id_ != lease->subnet_id_) {
4685 StatsMgr::generateName("subnet", old_lease->subnet_id_,
4686 "registered-nas"),
4687 static_cast<int64_t>(-1));
4689 StatsMgr::generateName("subnet", lease->subnet_id_,
4690 "registered-nas"),
4691 static_cast<int64_t>(1));
4693 StatsMgr::generateName("subnet", lease->subnet_id_,
4694 "cumulative-registered-nas"),
4695 static_cast<int64_t>(1));
4696 }
4697 } else {
4698 if (!LeaseMgrFactory::instance().addLease(lease)) {
4699 // Assume that stats and DNS were handled by someone else.
4701 .arg(addr);
4702 return (Pkt6Ptr());
4703 }
4704 // Update stats.
4706 StatsMgr::generateName("subnet", lease->subnet_id_,
4707 "registered-nas"),
4708 static_cast<int64_t>(1));
4710 StatsMgr::generateName("subnet", lease->subnet_id_,
4711 "cumulative-registered-nas"),
4712 static_cast<int64_t>(1));
4713 StatsMgr::instance().addValue("cumulative-registered-nas",
4714 static_cast<int64_t>(1));
4715 }
4716 // Save the new lease for the leases6_committed callout.
4717 ctx.new_leases_.push_back(lease);
4718 }
4719
4720 // Deal with FQDN.
4721 updateReservedFqdn(ctx, addr_reg_rep);
4722 generateFqdn(addr_reg_rep, ctx);
4723 createNameChangeRequests(addr_reg_rep, ctx);
4724
4725 return (addr_reg_rep);
4726}
4727
4728void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt) {
4729 OptionVendorClassPtr vclass;
4730 for (auto const& opt : pkt->getOptions(D6O_VENDOR_CLASS)) {
4731 vclass = boost::dynamic_pointer_cast<OptionVendorClass>(opt.second);
4732 if (!vclass || vclass->getTuplesNum() == 0) {
4733 continue;
4734 }
4735
4736 if (vclass->hasTuple(DOCSIS3_CLASS_MODEM)) {
4738
4739 } else if (vclass->hasTuple(DOCSIS3_CLASS_EROUTER)) {
4741
4742 } else {
4743 pkt->addClass(VENDOR_CLASS_PREFIX + vclass->getTuple(0).getText());
4744 }
4745 }
4746}
4747
4749 // All packets belong to ALL.
4750 pkt->addClass("ALL");
4751
4752 // First: built-in vendor class processing
4753 classifyByVendor(pkt);
4754
4755 // Run match expressions on classes not depending on KNOWN/UNKNOWN.
4756 evaluateClasses(pkt, false);
4757}
4758
4759void Dhcpv6Srv::evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known) {
4760 // Note getClientClassDictionary() cannot be null
4761 const ClientClassDictionaryPtr& dict =
4762 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4763 const ClientClassDefListPtr& defs_ptr = dict->getClasses();
4764 for (auto const& it : *defs_ptr) {
4765 // Note second cannot be null
4766 const ExpressionPtr& expr_ptr = it->getMatchExpr();
4767 // Nothing to do without an expression to evaluate
4768 if (!expr_ptr) {
4769 continue;
4770 }
4771 // Not the right time if only when required
4772 if (it->getAdditional()) {
4773 continue;
4774 }
4775 // Not the right pass.
4776 if (it->getDependOnKnown() != depend_on_known) {
4777 continue;
4778 }
4779 it->test(pkt, expr_ptr);
4780 }
4781}
4782
4783void
4785 const ClientClassDictionaryPtr& dict =
4786 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4787 const ClientClassDefListPtr& defs_ptr = dict->getClasses();
4788 for (auto const& def : *defs_ptr) {
4789 // Only remove evaluated classes. Other classes can be
4790 // assigned via hooks libraries and we should not remove
4791 // them because there is no way they can be added back.
4792 if (def->getMatchExpr()) {
4793 pkt->classes_.erase(def->getName());
4794 }
4795 }
4796}
4797
4798void
4800 const AllocEngine::ClientContext6& ctx) {
4801 if (ctx.currentHost() && pkt) {
4802 const ClientClasses& classes = ctx.currentHost()->getClientClasses6();
4803 for (auto const& cclass : classes) {
4804 pkt->addClass(cclass);
4805 }
4806 }
4807}
4808
4809void
4811 const AllocEngine::ClientContext6& ctx) {
4812 if (ctx.subnet_) {
4813 SharedNetwork6Ptr shared_network;
4814 ctx.subnet_->getSharedNetwork(shared_network);
4815 if (shared_network) {
4816 ConstHostPtr host = ctx.currentHost();
4817 if (host && (host->getIPv6SubnetID() != SUBNET_ID_GLOBAL)) {
4818 setReservedClientClasses(pkt, ctx);
4819 }
4820 }
4821 }
4822}
4823
4824void
4826 // Get additional classes to evaluate added elsewhere, posssibly by hooks.
4827 ClientClasses classes = pkt->getAdditionalClasses();
4828 ConstSubnet6Ptr subnet = ctx.subnet_;
4829
4830 if (subnet) {
4831 // host reservation???
4832
4833 // Begin by pools
4834 for (auto const& resource : ctx.allocated_resources_) {
4835 PoolPtr pool =
4836 ctx.subnet_->getPool(resource.getPrefixLength() == 128 ?
4838 resource.getAddress(),
4839 false);
4840 if (pool) {
4841 const ClientClasses& pool_to_add = pool->getAdditionalClasses();
4842 for (auto const& cclass : pool_to_add) {
4843 classes.insert(cclass);
4844 }
4845 }
4846 }
4847
4848 // Followed by the subnet
4849 const ClientClasses& to_add = subnet->getAdditionalClasses();
4850 for (auto const& cclass : to_add) {
4851 classes.insert(cclass);
4852 }
4853
4854 // And finish by the shared-network
4855 SharedNetwork6Ptr network;
4856 subnet->getSharedNetwork(network);
4857 if (network) {
4858 const ClientClasses& net_to_add = network->getAdditionalClasses();
4859 for (auto const& cclass : net_to_add) {
4860 classes.insert(cclass);
4861 }
4862 }
4863 }
4864
4865 // Run match expressions
4866 // Note getClientClassDictionary() cannot be null
4867 const ClientClassDictionaryPtr& dict =
4868 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4869 for (auto const& cclass : classes) {
4870 const ClientClassDefPtr class_def = dict->findClass(cclass);
4871 if (!class_def) {
4874 .arg(cclass);
4875 // Ignore it as it can't have an attached action
4876 continue;
4877 }
4878 const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
4879 // Add a class without an expression to evaluate
4880 if (!expr_ptr) {
4883 .arg(cclass);
4884 pkt->addClass(cclass);
4885 continue;
4886 }
4887 // Evaluate the expression which can return false (no match),
4888 // true (match) or raise an exception (error)
4889 try {
4890 bool status = evaluateBool(*expr_ptr, *pkt);
4892 .arg(pkt->getLabel())
4893 .arg(cclass)
4894 .arg(status ? "true" : "false");
4895 if (status) {
4896 // Matching: add the class
4897 pkt->addClass(cclass);
4898 }
4899 } catch (const Exception& ex) {
4901 .arg(pkt->getLabel())
4902 .arg(cclass)
4903 .arg(ex.what());
4904 }
4905 }
4906}
4907
4908void
4909Dhcpv6Srv::updateReservedFqdn(AllocEngine::ClientContext6& ctx,
4910 const Pkt6Ptr& answer) {
4911 if (!answer) {
4912 isc_throw(isc::Unexpected, "an instance of the object encapsulating"
4913 " a message must not be NULL when updating reserved FQDN");
4914 }
4915
4916 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>
4917 (answer->getOption(D6O_CLIENT_FQDN));
4918
4919 // If Client FQDN option is not included, there is nothing to do.
4920 if (!fqdn) {
4921 return;
4922 }
4923
4924 std::string name = fqdn->getDomainName();
4925
4926 // If there is a host reservation for this client we have to check whether
4927 // this reservation has the same hostname as the hostname currently
4928 // present in the FQDN option. If not, it indicates that the allocation
4929 // engine picked a different subnet (from within a shared network) for
4930 // reservations and we have to send this new value to the client.
4931 if (ctx.currentHost() &&
4932 !ctx.currentHost()->getHostname().empty()) {
4933 std::string new_name = CfgMgr::instance().getD2ClientMgr().
4934 qualifyName(ctx.currentHost()->getHostname(), *ctx.getDdnsParams(), true);
4935
4936 if (new_name != name) {
4937 fqdn->setDomainName(new_name, Option6ClientFqdn::FULL);
4938
4939 // Replace previous instance of Client FQDN option.
4940 answer->delOption(D6O_CLIENT_FQDN);
4941 answer->addOption(fqdn);
4942 }
4943 }
4944}
4945
4946void
4947Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer,
4949 if (!answer) {
4950 isc_throw(isc::Unexpected, "an instance of the object encapsulating"
4951 " a message must not be NULL when generating FQDN");
4952 }
4953
4956
4957 // It is likely that client hasn't included the FQDN option. In such case,
4958 // FQDN option will be NULL. Also, there is nothing to do if the option
4959 // is present and conveys the non-empty FQDN.
4960 Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
4961 Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
4962 if (!fqdn || !fqdn->getDomainName().empty()) {
4963 return;
4964 }
4965
4966 // Get the first IA_NA acquired for the client.
4967 OptionPtr ia = answer->getOption(D6O_IA_NA);
4968 if (!ia) {
4969 return;
4970 }
4971
4972 // If it has any IAAddr with a valid lifetime > 0, use it to
4973 // generate unique FQDN.
4974 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
4975 Option6IAAddr>(ia->getOption(D6O_IAADDR));
4976 if (!iaaddr || iaaddr->getValid() == 0) {
4977 return;
4978 }
4979
4980 // Get the IPv6 address acquired by the client.
4981 IOAddress addr = iaaddr->getAddress();
4982 std::string generated_name =
4984
4986 .arg(answer->getLabel())
4987 .arg(generated_name);
4988
4989 try {
4990 // The lease has been acquired but the FQDN for this lease hasn't
4991 // been updated in the lease database. We now have new FQDN
4992 // generated, so the lease database has to be updated here.
4993 // However, never update lease database for Advertise, just send
4994 // our notion of client's FQDN in the Client FQDN option.
4995 if (answer->getType() != DHCPV6_ADVERTISE) {
4996 Lease6Ptr lease;
4997 for (auto const& l : ctx.new_leases_) {
4998 if ((l->type_ == Lease::TYPE_NA) && (l->addr_ == addr)) {
4999 lease = l;
5000 break;
5001 }
5002 }
5003 if (lease) {
5004 lease->hostname_ = generated_name;
5005 lease->reuseable_valid_lft_ = 0;
5007
5008 } else {
5009 isc_throw(isc::Unexpected, "there is no lease in the database "
5010 " for address " << addr << ", so as it is impossible"
5011 " to update FQDN data. This is a programmatic error"
5012 " as the given address is now being handed to the"
5013 " client");
5014 }
5015 }
5016 // Set the generated FQDN in the Client FQDN option.
5017 fqdn->setDomainName(generated_name, Option6ClientFqdn::FULL);
5018
5019 answer->delOption(D6O_CLIENT_FQDN);
5020 answer->addOption(fqdn);
5021 ctx.hostname_ = generated_name;
5022 } catch (const Exception& ex) {
5024 .arg(answer->getLabel())
5025 .arg(addr.toText())
5026 .arg(ex.what());
5027 }
5028}
5029
5030void
5033 if (d2_mgr.ddnsEnabled()) {
5034 // Updates are enabled, so lets start the sender, passing in
5035 // our error handler.
5036 // This may throw so wherever this is called needs to ready.
5038 this, ph::_1, ph::_2));
5039 }
5040}
5041
5042void
5045 if (d2_mgr.ddnsEnabled()) {
5046 // Updates are enabled, so lets stop the sender
5047 d2_mgr.stop();
5048 d2_mgr.stopSender();
5049 }
5050}
5051
5052void
5057 arg(result).arg((ncr ? ncr->toText() : " NULL "));
5058 // We cannot communicate with kea-dhcp-ddns, suspend further updates.
5062}
5063
5064std::string
5066 std::stringstream tmp;
5067
5068 tmp << VERSION;
5069 if (extended) {
5070 tmp << " (" << EXTENDED_VERSION << ")" << endl;
5071 tmp << "premium: " << PREMIUM_EXTENDED_VERSION << endl;
5072 tmp << "linked with:" << endl;
5073 tmp << "- " << Logger::getVersion() << endl;
5074 tmp << "- " << CryptoLink::getVersion();
5076 if (info.size()) {
5077 tmp << endl << "lease backends:";
5078 for (auto const& version : info) {
5079 tmp << endl << "- " << version;
5080 }
5081 }
5083 if (info.size()) {
5084 tmp << endl << "host backends:";
5085 for (auto const& version : info) {
5086 tmp << endl << "- " << version;
5087 }
5088 }
5090 if (info.size()) {
5091 tmp << endl << "forensic backends:";
5092 for (auto const& version : info) {
5093 tmp << endl << "- " << version;
5094 }
5095 }
5096 // @todo: more details about database runtime
5097 }
5098
5099 return (tmp.str());
5100}
5101
5102void Dhcpv6Srv::processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp) {
5103
5104 if (query->relay_info_.empty()) {
5105 // RSOO is inserted by relay agents, nothing to do here if it's
5106 // a direct message.
5107 return;
5108 }
5109
5110 // Get RSOO configuration.
5111 ConstCfgRSOOPtr cfg_rsoo = CfgMgr::instance().getCurrentCfg()->getCfgRSOO();
5112
5113 // Let's get over all relays (encapsulation levels). We need to do
5114 // it in the same order as the client packet traversed the relays.
5115 for (int i = query->relay_info_.size(); i > 0 ; --i) {
5116 OptionPtr rsoo_container = query->getRelayOption(D6O_RSOO, i - 1);
5117 if (rsoo_container) {
5118 // There are RSOO options. Let's get through them one by one
5119 // and if it's RSOO-enabled and there's no such option provided yet,
5120 // copy it to the server's response
5121 const OptionCollection& rsoo = rsoo_container->getOptions();
5122 for (auto const& opt : rsoo) {
5123
5124 // Echo option if it is RSOO enabled option and there is no such
5125 // option added yet.
5126 if (cfg_rsoo->enabled(opt.second->getType()) &&
5127 !rsp->getOption(opt.second->getType())) {
5128 rsp->addOption(opt.second);
5129 }
5130 }
5131 }
5132 }
5133}
5134
5136
5137 if (query->relay_info_.empty()) {
5138 // No relay agent
5139 return (0);
5140 }
5141
5142 // Did the last relay agent add a relay-source-port?
5143 if (query->getRelayOption(D6O_RELAY_SOURCE_PORT, 0)) {
5144 // RFC 8357 section 5.2
5145 return (query->getRemotePort());
5146 }
5147
5148 return (0);
5149}
5150
5151void Dhcpv6Srv::processStatsReceived(const Pkt6Ptr& query) {
5152 // Note that we're not bumping pkt6-received statistic as it was
5153 // increased early in the packet reception code.
5154
5155 string stat_name = "pkt6-unknown-received";
5156 switch (query->getType()) {
5157 case DHCPV6_SOLICIT:
5158 stat_name = "pkt6-solicit-received";
5159 break;
5160 case DHCPV6_ADVERTISE:
5161 // Should not happen, but let's keep a counter for it
5162 stat_name = "pkt6-advertise-received";
5163 break;
5164 case DHCPV6_REQUEST:
5165 stat_name = "pkt6-request-received";
5166 break;
5167 case DHCPV6_CONFIRM:
5168 stat_name = "pkt6-confirm-received";
5169 break;
5170 case DHCPV6_RENEW:
5171 stat_name = "pkt6-renew-received";
5172 break;
5173 case DHCPV6_REBIND:
5174 stat_name = "pkt6-rebind-received";
5175 break;
5176 case DHCPV6_REPLY:
5177 // Should not happen, but let's keep a counter for it
5178 stat_name = "pkt6-reply-received";
5179 break;
5180 case DHCPV6_RELEASE:
5181 stat_name = "pkt6-release-received";
5182 break;
5183 case DHCPV6_DECLINE:
5184 stat_name = "pkt6-decline-received";
5185 break;
5186 case DHCPV6_RECONFIGURE:
5187 stat_name = "pkt6-reconfigure-received";
5188 break;
5190 stat_name = "pkt6-infrequest-received";
5191 break;
5193 stat_name = "pkt6-dhcpv4-query-received";
5194 break;
5196 // Should not happen, but let's keep a counter for it
5197 stat_name = "pkt6-dhcpv4-response-received";
5198 break;
5200 stat_name = "pkt6-addr-reg-inform-received";
5201 break;
5203 // Should not happen, but let's keep a counter for it
5204 stat_name = "pkt6-addr-reg-reply-received";
5205 break;
5206 default:
5207 ; // do nothing
5208 }
5209
5210 StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
5211}
5212
5214 // Increase generic counter for sent packets.
5215 StatsMgr::instance().addValue("pkt6-sent", static_cast<int64_t>(1));
5216
5217 // Increase packet type specific counter for packets sent.
5218 string stat_name;
5219 switch (response->getType()) {
5220 case DHCPV6_ADVERTISE:
5221 stat_name = "pkt6-advertise-sent";
5222 break;
5223 case DHCPV6_REPLY:
5224 stat_name = "pkt6-reply-sent";
5225 break;
5227 stat_name = "pkt6-dhcpv4-response-sent";
5228 break;
5230 stat_name = "pkt6-addr-reg-reply-sent";
5231 break;
5232 default:
5233 // That should never happen
5234 return;
5235 }
5236
5237 StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
5238}
5239
5241 return (Hooks.hook_index_buffer6_send_);
5242}
5243
5244bool
5245Dhcpv6Srv::requestedInORO(const Pkt6Ptr& query, const uint16_t code) const {
5247 boost::dynamic_pointer_cast<OptionUint16Array>(query->getOption(D6O_ORO));
5248
5249 if (oro) {
5250 const std::vector<uint16_t>& codes = oro->getValues();
5251 return (std::find(codes.begin(), codes.end(), code) != codes.end());
5252 }
5253
5254 return (false);
5255}
5256
5257tuple<bool, uint32_t>
5258Dhcpv6Srv::parkingLimitExceeded(string const& hook_label) {
5259 // Get the parking limit. Parsing should ensure the value is present.
5260 uint32_t parked_packet_limit(0);
5261 ConstElementPtr const& ppl(
5262 CfgMgr::instance().getCurrentCfg()->getConfiguredGlobal(CfgGlobals::PARKED_PACKET_LIMIT));
5263 if (ppl) {
5264 parked_packet_limit = ppl->intValue();
5265 }
5266
5267 if (parked_packet_limit) {
5268 ParkingLotPtr const& parking_lot(
5269 ServerHooks::getServerHooks().getParkingLotPtr(hook_label));
5270
5271 if (parking_lot && parked_packet_limit <= parking_lot->size()) {
5272 return make_tuple(true, parked_packet_limit);
5273 }
5274 }
5275 return make_tuple(false, parked_packet_limit);
5276}
5277
5278
5280 // Dump all of our current packets, anything that is mid-stream
5282}
5283
5285#ifdef FUZZING
5286 char const* const rotate(getenv("KEA_DHCP6_FUZZING_ROTATE_PORT"));
5287 if (rotate) {
5288 InterprocessSyncFile file("kea-dhcp6-fuzzing-rotate-port");
5289 InterprocessSyncLocker locker(file);
5290 while (!locker.lock()) {
5291 this_thread::sleep_for(1s);
5292 }
5293 fstream port_file;
5294 port_file.open("/tmp/port6.txt", ios::in);
5295 string line;
5296 int port;
5297 getline(port_file, line);
5298 port_file.close();
5299 if (line.empty()) {
5300 port = 2000;
5301 } else {
5302 port = stoi(line);
5303 if (port < 3000) {
5304 ++port;
5305 } else {
5306 port = 2000;
5307 }
5308 }
5309 port_file.open("/tmp/port6.txt", ios::out | ios::trunc);
5310 port_file << to_string(port) << endl;
5311 port_file.close();
5312 locker.unlock();
5313 return port;
5314 }
5315#endif // FUZZING
5316 return server_port_;
5317}
5318
5320void
5321Dhcpv6Srv::setTeeTimes(uint32_t preferred_lft,
5322 const ConstSubnet6Ptr& subnet,
5323 Option6IAPtr& resp) {
5324 // Default T2 time to zero.
5325 uint32_t t2_time = 0;
5326
5327 // If T2 is explicitly configured we'll use that value.
5328 if (!subnet->getT2().unspecified()) {
5329 t2_time = subnet->getT2();
5330 } else if (subnet->getCalculateTeeTimes()) {
5331 // Calculating tee times is enabled, so calculate it.
5332 t2_time = static_cast<uint32_t>(round(subnet->getT2Percent() * preferred_lft));
5333 }
5334
5335 // We allow T2 to be any value.
5336 resp->setT2(t2_time);
5337
5338 // Default T1 time to zero.
5339 uint32_t t1_time = 0;
5340
5341 // If T1 is explicitly configured we'll use try value.
5342 if (!subnet->getT1().unspecified()) {
5343 t1_time = subnet->getT1();
5344 } else if (subnet->getCalculateTeeTimes()) {
5345 // Calculating tee times is enabled, so calculate it.
5346 t1_time = static_cast<uint32_t>(round(subnet->getT1Percent() * preferred_lft));
5347 }
5348
5349 // T1 is sane if it is less than or equal to T2.
5350 if (t1_time < t2_time) {
5351 resp->setT1(t1_time);
5352 } else {
5353 // It's either explicitly 0 or insane, leave it to the client
5354 resp->setT1(0);
5355 }
5356}
5357
5358void
5361 const ConstSubnet6Ptr orig_subnet) {
5362 bool reprocess_client_name = false;
5363
5364 // Find the pool to which the first active lease address belongs and use it
5365 // to update DDNS parameters to include those from the pool.
5366 OptionPtr ia = answer->getOption(D6O_IA_NA);
5367 if (ia) {
5368 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
5369 if (iaaddr && (iaaddr->getValid() > 0)) {
5370 auto ddns_params = ctx.getDdnsParams();
5371 auto pool = ddns_params->setPoolFromAddress(iaaddr->getAddress());
5372 if (pool) {
5373 // If the pool has any DDNS parameters we need to recalculate the FQDN.
5374 reprocess_client_name = pool->hasDdnsParameters();
5375 }
5376 }
5377 }
5378
5379 // Check if the subnet was dynamically changed by the allocation engine.
5380 if (ctx.subnet_ && orig_subnet && (orig_subnet->getID() != ctx.subnet_->getID())) {
5381 // We get the network for logging only. It should always be set as
5382 // this a dynamic change should only happen within shared-networks.
5383 // Not having one might not be an error if a hook changed the subnet?
5384 SharedNetwork6Ptr network;
5385 orig_subnet->getSharedNetwork(network);
5387 .arg(question->getLabel())
5388 .arg(orig_subnet->toText())
5389 .arg(ctx.subnet_->toText())
5390 .arg(network ? network->getName() : "<no network?>");
5391
5392 // The subnet changed so we need to recalculate the FQDN.
5393 reprocess_client_name = true;
5394 }
5395
5396 // Recalulate the FQDN if either the pool has DDNS parameters or the
5397 // selected subnet was changed.
5398 if (reprocess_client_name) {
5399 // Save the current DNS values on the context.
5400 std::string prev_hostname = ctx.hostname_;
5401 bool prev_fwd_dns_update = ctx.fwd_dns_update_;
5402 bool prev_rev_dns_update = ctx.rev_dns_update_;
5403
5404 // Remove the current FQDN option from the answer.
5405 answer->delOption(D6O_CLIENT_FQDN);
5406
5407 // Recalculate the client's FQDN. This will replace the FQDN option and
5408 // update the context values for hostname_ and DNS directions.
5409 processClientFqdn(question, answer, ctx);
5410
5411 // If this is a real allocation and the DNS values changed we need to
5412 // update the leases.
5413 if (!ctx.fake_allocation_ &&
5414 ((prev_hostname != ctx.hostname_) ||
5415 (prev_fwd_dns_update != ctx.fwd_dns_update_) ||
5416 (prev_rev_dns_update != ctx.rev_dns_update_))) {
5417 for (auto const& l : ctx.new_leases_) {
5418 l->hostname_ = ctx.hostname_;
5419 l->fqdn_fwd_ = ctx.fwd_dns_update_;
5420 l->fqdn_rev_ = ctx.rev_dns_update_;
5421 l->reuseable_valid_lft_ = 0;
5423 }
5424 }
5425 }
5426}
5427
5428std::list<std::list<std::string>> Dhcpv6Srv::jsonPathsToRedact() const{
5429 static std::list<std::list<std::string>> const list({
5430 {"config-control", "config-databases", "[]"},
5431 {"hooks-libraries", "[]", "parameters", "*"},
5432 {"hosts-database"},
5433 {"hosts-databases", "[]"},
5434 {"lease-database"},
5435 });
5436 return list;
5437}
5438
5439} // namespace dhcp
5440} // namespace isc
int version()
returns Kea hooks version.
CtrlAgentHooks Hooks
Defines elements for storing the names of client classes.
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
Defines a single hint.
DHCPv4 and DHCPv6 allocation engine.
std::vector< Resource > HintContainer
Container for client's hints.
Implementation of the mechanisms to control the use of the Configuration Backends by the DHCPv6 serve...
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition cfgmgr.cc:68
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:28
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:115
static SubnetSelector initSelector(const Pkt6Ptr &query)
Build selector from a client's message.
Container for storing client class names.
Definition classify.h:109
void insert(const ClientClass &class_name)
Insert an element.
Definition classify.h:159
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition classify.cc:80
Client race avoidance RAII handler.
bool tryLock(Pkt4Ptr query, ContinuationPtr cont=ContinuationPtr())
Tries to acquires a client.
D2ClientMgr isolates Kea from the details of being a D2 client.
std::string generateFqdn(const asiolink::IOAddress &address, const DdnsParams &ddns_params, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
void getUpdateDirections(const T &fqdn_resp, bool &forward, bool &reverse)
Get directional update flags based on server FQDN flags.
void stop()
Stop the sender.
void suspendUpdates()
Suspends sending requests.
void adjustDomainName(const T &fqdn, T &fqdn_resp, const DdnsParams &ddns_params)
Set server FQDN name based on configuration and a given FQDN.
void sendRequest(dhcp_ddns::NameChangeRequestPtr &ncr)
Send the given NameChangeRequests to kea-dhcp-ddns.
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
void adjustFqdnFlags(const T &fqdn, T &fqdn_resp, const DdnsParams &ddns_params)
Set server FQDN flags based on configuration and a given FQDN.
std::string qualifyName(const std::string &partial_name, const DdnsParams &ddns_params, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
void startSender(D2ClientErrorHandler error_handler, const isc::asiolink::IOServicePtr &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
This exception is thrown when DHCP server hits the error which should result in discarding the messag...
Definition dhcp6_srv.h:48
Factory for generating DUIDs (DHCP Unique Identifiers).
DuidPtr get()
Returns current DUID.
Holds DUID (DHCPv6 Unique Identifier)
Definition duid.h:142
static constexpr size_t MIN_DUID_LEN
minimum duid size
Definition duid.h:149
static constexpr size_t MAX_DUID_LEN
maximum duid size
Definition duid.h:155
void send(const Pkt6Ptr &pkt)
Send message over IPC.
void close()
Close communication socket.
static Dhcp6to4Ipc & instance()
Returns pointer to the sole instance of Dhcp6to4Ipc.
static uint16_t client_port
void shutdown() override
Instructs the server to shut down.
Definition dhcp6_srv.cc: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
isc::dhcp::ConstSubnet6Ptr selectSubnet(const Pkt6Ptr &question, bool &drop)
Selects a subnet for a given client's packet.
volatile bool shutdown_
Indicates if shutdown is in progress.
Definition dhcp6_srv.h:1229
void checkPostAssignmentChanges(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, const ConstSubnet6Ptr orig_subnet)
Iterates over new leases, update stale DNS entries.
Pkt6Ptr processSolicit(AllocEngine::ClientContext6 &ctx)
Processes incoming Solicit and returns response.
void startD2()
Starts DHCP_DDNS client IO if DDNS updates are enabled.
static std::string duidToString(const OptionPtr &opt)
converts DUID to text Converts content of DUID option to a text representation, e....
OptionPtr getPDExclude(const AllocEngine::ClientContext6 &ctx, const Lease6Ptr &lease)
Return the PD exclude option to include.
static void removeDependentEvaluatedClasses(const Pkt6Ptr &pkt)
Removed evaluated client classes.
void createNameChangeRequests(const Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx)
Creates a number of isc::dhcp_ddns::NameChangeRequest objects based on the DHCPv6 Client FQDN Option.
Pkt6Ptr processInfRequest(AllocEngine::ClientContext6 &ctx)
Processes incoming Information-request message.
uint16_t server_port_
UDP port number on which server listens.
Definition dhcp6_srv.h:1191
void appendRequestedVendorOptions(const Pkt6Ptr &question, Pkt6Ptr &answer, AllocEngine::ClientContext6 &ctx, const CfgOptionList &co_list)
Appends requested vendor options to server's answer.
bool declineLease(const Pkt6Ptr &decline, const Lease6Ptr lease, boost::shared_ptr< Option6IA > ia_rsp)
Declines specific IPv6 lease.
void discardPackets()
Discards parked packets Clears the packet parking lots of all packets.
static std::list< std::string > getDBVersions()
Return extended version info for registered backends.
ConstHostCollection getAll6(const SubnetID &subnet_id, const HostMgrOperationTarget target) const
Return all hosts in a DHCPv6 subnet.
Definition host_mgr.cc:171
static void create()
Creates new instance of the HostMgr.
Definition host_mgr.cc:52
static HostMgr & instance()
Returns a sole instance of the HostMgr.
Definition host_mgr.cc:114
IdentifierType
Type of the host identifier.
Definition host.h:337
@ IDENT_FLEX
Flexible host identifier.
Definition host.h:342
std::string getIdentifierAsText() const
Returns host identifier in a textual form.
Definition host.cc:312
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
bool send(const Pkt6Ptr &pkt)
Sends an IPv6 packet.
void closeSockets()
Closes all open sockets.
Definition iface_mgr.cc:286
static TrackingLeaseMgr & instance()
Return current lease manager.
static std::list< std::string > getDBVersions()
Return extended version info for registered backends.
static void destroy()
Destroy lease manager.
virtual bool deleteLease(const Lease4Ptr &lease)=0
Deletes an IPv4 lease.
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const =0
Returns existing IPv6 lease for a given IPv6 address.
virtual void updateLease6(const Lease6Ptr &lease6)=0
Updates IPv6 lease.
static std::list< std::string > getDBVersions()
Return extended version info for registered backends.
static void splitNtpServerOptions6(isc::dhcp::OptionCollection &options)
Split NTP server option to one suboption per instance.
Controls the DHCP service enabling status.
Represents DHCPv6 Client FQDN Option (code 39).
static const uint8_t FLAG_S
S bit.
static const uint8_t FLAG_N
N bit.
Class that represents IAPREFIX option in DHCPv6.
uint32_t getIAID() const
Returns IA identifier.
Definition option6_ia.h:87
This class represents Status Code option (13) from RFC 8415.
Option descriptor.
Definition cfg_option.h:48
OptionPtr option_
Option instance.
Definition cfg_option.h:51
bool allowedForClientClasses(const ClientClasses &cclasses) const
Validates an OptionDescriptor's client-classes against a list of classes.
Definition cfg_option.cc:63
Forward declaration to OptionIntArray.
This class represents vendor-specific information option.
const OptionCollection & getOptions() const
Returns all encapsulated options.
Definition option.h:354
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition option.cc:199
Represents a DHCPv6 packet.
Definition pkt6.h:44
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition pkt6.cc: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:220
Statistics Manager class.
static StatsMgr & instance()
Statistics Manager accessor method.
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
RAII class creating a critical section.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
ThreadPool< std::function< void()> > & getThreadPool()
Get the dhcp thread pool.
void apply(bool enabled, uint32_t thread_count, uint32_t queue_size)
Apply the multi-threading related settings.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h: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.
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
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
When a message is logged with DEBUG severity, the debug level associated with the message is also spe...
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
@ info
Definition db_log.h:120
ConflictResolutionMode StringToConflictResolutionMode(const std::string &mode_str)
Function which converts string to ConflictResolutionMode enum values.
Definition ncr_msg.cc:45
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition ncr_msg.h:241
const isc::log::MessageID DHCP6_DDNS_REQUEST_SEND_FAILED
boost::shared_ptr< OptionVendor > OptionVendorPtr
Pointer to a vendor option.
const isc::log::MessageID DHCP6_PD_LEASE_ADVERT
const isc::log::MessageID DHCP6_BUFFER_RECEIVED
const isc::log::MessageID DHCP6_RELEASE_NA_DELETED
isc::log::Logger bad_packet6_logger(DHCP6_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition dhcp6_log.h:94
const isc::log::MessageID DHCP6_PACKET_DROP_PARSE_FAIL
const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_NO_TEST
const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_RESULT
const isc::log::MessageID DHCP6_RELEASE_PD_DELETED
const isc::log::MessageID DHCP6_LEASE_ALLOC
const isc::log::MessageID DHCP6_FLEX_ID
const isc::log::MessageID DHCP6_REGISTERED_LEASE_ADD_FAIL
uint32_t calculateDdnsTtl(uint32_t lease_lft, const util::Optional< double > &ddns_ttl_percent, const util::Optional< uint32_t > &ddns_ttl, const util::Optional< uint32_t > &ddns_ttl_min, const util::Optional< uint32_t > &ddns_ttl_max)
Calculates TTL for a DNS resource record based on lease life time.
const isc::log::MessageID DHCP6_PACKET_PROCESS_EXCEPTION_MAIN
const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_SKIP
const isc::log::MessageID DHCP6_ADDR_REG_INFORM_CLIENT_CHANGE
const isc::log::MessageID DHCP6_SUBNET_SELECTION_FAILED
const isc::log::MessageID DHCP6_PACKET_DROP_SERVERID_MISMATCH
const isc::log::MessageID DHCP6_HOOK_DECLINE_SKIP
const isc::log::MessageID DHCP6_PD_LEASE_REUSE
const isc::log::MessageID DHCP6_PACKET_DROP_UNICAST
const isc::log::MessageID DHCP6_LEASE_PD_WITHOUT_DUID
const isc::log::MessageID DHCP6_LEASE_ALLOC_FAIL
const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_UNDEFINED
const isc::log::MessageID DHCP6_PACKET_SEND_FAIL
const isc::log::MessageID DHCP6_PACKET_PROCESS_EXCEPTION
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
const isc::log::MessageID DHCP6_QUERY_LABEL
const isc::log::MessageID DHCP6_BUFFER_UNPACK
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:623
std::vector< uint32_t > CfgMACSources
Container for defined MAC/hardware address sources.
const isc::log::MessageID DHCP6_SRV_D2STOP_ERROR
const isc::log::MessageID DHCP6_REGISTERED_LEASE_UPDATE_FAIL
const isc::log::MessageID DHCP6_PACKET_SEND
const isc::log::MessageID DHCP6_DECLINE_FAIL_LEASE_WITHOUT_DUID
const int DBG_DHCP6_BASIC_DATA
Debug level used to log the traces with some basic data.
Definition dhcp6_log.h:43
const isc::log::MessageID DHCP6_HOOK_PACKET_RCVD_SKIP
const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_ERROR
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
const isc::log::MessageID DHCP6_OPEN_SOCKET
const isc::log::MessageID DHCP6_PACK_FAIL
const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID
const isc::log::MessageID DHCP6_HOOK_DDNS_UPDATE
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition lease.h:528
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition lease.h:693
const int DBG_DHCP6_HOOKS
Debug level used to trace hook related operations.
Definition dhcp6_log.h:34
const isc::log::MessageID DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP
ContinuationPtr makeContinuation(Continuation &&cont)
Continuation factory.
const int DBG_DHCP6_START
Debug level used to log information during server startup.
Definition dhcp6_log.h:22
const isc::log::MessageID DHCP6_PD_LEASE_ALLOC
const isc::log::MessageID DHCP6_DDNS_GENERATE_FQDN
const isc::log::MessageID DHCP6_RELEASE_PD_EXPIRED
boost::shared_ptr< Option6IA > Option6IAPtr
A pointer to the Option6IA object.
Definition option6_ia.h:20
const isc::log::MessageID DHCP6_DDNS_REMOVE_OLD_LEASE_FQDN
boost::shared_ptr< const CfgRSOO > ConstCfgRSOOPtr
Pointer to the const object.
Definition cfg_rsoo.h:74
boost::shared_ptr< const CfgHostOperations > ConstCfgHostOperationsPtr
Pointer to the const object.
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition option.h:40
const isc::log::MessageID DHCP6_SUBNET_DATA
const isc::log::MessageID DHCP6_UNKNOWN_MSG_RECEIVED
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
boost::shared_ptr< DdnsParams > DdnsParamsPtr
Defines a pointer for DdnsParams instances.
const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_DROP
const isc::log::MessageID DHCP6_ADD_GLOBAL_STATUS_CODE
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
const isc::log::MessageID DHCP6_DDNS_RESPONSE_FQDN_DATA
const isc::log::MessageID DHCP6_RELEASE_NA
const isc::log::MessageID DHCP6_CLASSES_ASSIGNED
const isc::log::MessageID DHCP6_REQUIRED_OPTIONS_CHECK_FAIL
const isc::log::MessageID DHCP6_PROCESS_IA_NA_EXTEND
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition host.h:273
const isc::log::MessageID DHCP6_RELEASE_PD_FAIL
const isc::log::MessageID DHCP6_SRV_CONSTRUCT_ERROR
const isc::log::MessageID DHCP6_LEASE_RENEW
const char * DOCSIS3_CLASS_EROUTER
The class as specified in vendor-class option by the devices.
Definition libdhcp++.cc:94
const isc::log::MessageID DHCP6_RELEASE_NA_FAIL
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
const isc::log::MessageID DHCP6_PACKET_PROCESS_STD_EXCEPTION
const isc::log::MessageID DHCP6_PACKET_RECEIVE_FAIL
const isc::log::MessageID DHCP6_PROCESS_IA_NA_SOLICIT
const isc::log::MessageID DHCP6_ADDR_REG_INFORM_FAIL
const isc::log::MessageID DHCP6_DECLINE_FAIL_IAID_MISMATCH
const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_PARK
OptionContainer::nth_index< 5 >::type OptionContainerCancelIndex
Type of the index #5 - option cancellation flag.
Definition cfg_option.h: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.
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