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