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