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