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