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