Kea 2.2.0
alloc_engine.cc
Go to the documentation of this file.
1// Copyright (C) 2012-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
9#include <dhcp/dhcp6.h>
10#include <dhcp/pkt4.h>
11#include <dhcp/pkt6.h>
12#include <dhcp/option_int.h>
13#include <dhcp_ddns/ncr_msg.h>
16#include <dhcpsrv/cfgmgr.h>
17#include <dhcpsrv/dhcpsrv_log.h>
18#include <dhcpsrv/host_mgr.h>
19#include <dhcpsrv/host.h>
22#include <dhcpsrv/network.h>
26#include <hooks/hooks_manager.h>
28#include <stats/stats_mgr.h>
29#include <util/encode/hex.h>
30#include <util/stopwatch.h>
31#include <hooks/server_hooks.h>
32
33#include <boost/foreach.hpp>
34#include <boost/make_shared.hpp>
35
36#include <algorithm>
37#include <cstring>
38#include <limits>
39#include <sstream>
40#include <stdint.h>
41#include <string.h>
42#include <utility>
43#include <vector>
44
45using namespace isc::asiolink;
46using namespace isc::dhcp;
47using namespace isc::dhcp_ddns;
48using namespace isc::hooks;
49using namespace isc::stats;
50using namespace isc::util;
51using namespace isc::data;
52namespace ph = std::placeholders;
53
54namespace {
55
57struct AllocEngineHooks {
58 int hook_index_lease4_select_;
59 int hook_index_lease4_renew_;
60 int hook_index_lease4_expire_;
61 int hook_index_lease4_recover_;
62 int hook_index_lease6_select_;
63 int hook_index_lease6_renew_;
64 int hook_index_lease6_rebind_;
65 int hook_index_lease6_expire_;
66 int hook_index_lease6_recover_;
67
69 AllocEngineHooks() {
70 hook_index_lease4_select_ = HooksManager::registerHook("lease4_select");
71 hook_index_lease4_renew_ = HooksManager::registerHook("lease4_renew");
72 hook_index_lease4_expire_ = HooksManager::registerHook("lease4_expire");
73 hook_index_lease4_recover_= HooksManager::registerHook("lease4_recover");
74 hook_index_lease6_select_ = HooksManager::registerHook("lease6_select");
75 hook_index_lease6_renew_ = HooksManager::registerHook("lease6_renew");
76 hook_index_lease6_rebind_ = HooksManager::registerHook("lease6_rebind");
77 hook_index_lease6_expire_ = HooksManager::registerHook("lease6_expire");
78 hook_index_lease6_recover_= HooksManager::registerHook("lease6_recover");
79 }
80};
81
82// Declare a Hooks object. As this is outside any function or method, it
83// will be instantiated (and the constructor run) when the module is loaded.
84// As a result, the hook indexes will be defined before any method in this
85// module is called.
86AllocEngineHooks Hooks;
87
88} // namespace
89
90namespace isc {
91namespace dhcp {
92
94 : Allocator(lease_type) {
95}
96
99 const uint8_t prefix_len) {
100 if (!prefix.isV6()) {
101 isc_throw(BadValue, "Prefix operations are for IPv6 only (attempted to "
102 "increase prefix " << prefix << ")");
103 }
104
105 // Get a buffer holding an address.
106 const std::vector<uint8_t>& vec = prefix.toBytes();
107
108 if (prefix_len < 1 || prefix_len > 128) {
109 isc_throw(BadValue, "Cannot increase prefix: invalid prefix length: "
110 << prefix_len);
111 }
112
113 uint8_t n_bytes = (prefix_len - 1)/8;
114 uint8_t n_bits = 8 - (prefix_len - n_bytes*8);
115 uint8_t mask = 1 << n_bits;
116
117 // Explanation: n_bytes specifies number of full bytes that are in-prefix.
118 // They can also be used as an offset for the first byte that is not in
119 // prefix. n_bits specifies number of bits on the last byte that is
120 // (often partially) in prefix. For example for a /125 prefix, the values
121 // are 15 and 3, respectively. Mask is a bitmask that has the least
122 // significant bit from the prefix set.
123
124 uint8_t packed[V6ADDRESS_LEN];
125
126 // Copy the address. It must be V6, but we already checked that.
127 std::memcpy(packed, &vec[0], V6ADDRESS_LEN);
128
129 // Can we safely increase only the last byte in prefix without overflow?
130 if (packed[n_bytes] + uint16_t(mask) < 256u) {
131 packed[n_bytes] += mask;
132 return (IOAddress::fromBytes(AF_INET6, packed));
133 }
134
135 // Overflow (done on uint8_t, but the sum is greater than 255)
136 packed[n_bytes] += mask;
137
138 // Deal with the overflow. Start increasing the least significant byte
139 for (int i = n_bytes - 1; i >= 0; --i) {
140 ++packed[i];
141 // If we haven't overflowed (0xff->0x0) the next byte, then we are done
142 if (packed[i] != 0) {
143 break;
144 }
145 }
146
147 return (IOAddress::fromBytes(AF_INET6, packed));
148}
149
152 bool prefix,
153 const uint8_t prefix_len) {
154 if (!prefix) {
155 return (IOAddress::increase(address));
156 } else {
157 return (increasePrefix(address, prefix_len));
158 }
159}
160
162AllocEngine::IterativeAllocator::pickAddressInternal(const SubnetPtr& subnet,
163 const ClientClasses& client_classes,
164 const DuidPtr&,
165 const IOAddress&) {
166 // Is this prefix allocation?
167 bool prefix = pool_type_ == Lease::TYPE_PD;
168 uint8_t prefix_len = 0;
169
170 // Let's get the last allocated address. It is usually set correctly,
171 // but there are times when it won't be (like after removing a pool or
172 // perhaps restarting the server).
173 IOAddress last = subnet->getLastAllocated(pool_type_);
174 bool valid = true;
175 bool retrying = false;
176
177 const PoolCollection& pools = subnet->getPools(pool_type_);
178
179 if (pools.empty()) {
180 isc_throw(AllocFailed, "No pools defined in selected subnet");
181 }
182
183 // first we need to find a pool the last address belongs to.
184 PoolCollection::const_iterator it;
185 PoolCollection::const_iterator first = pools.end();
186 PoolPtr first_pool;
187 for (it = pools.begin(); it != pools.end(); ++it) {
188 if (!(*it)->clientSupported(client_classes)) {
189 continue;
190 }
191 if (first == pools.end()) {
192 first = it;
193 }
194 if ((*it)->inRange(last)) {
195 break;
196 }
197 }
198
199 // Caller checked this cannot happen
200 if (first == pools.end()) {
201 isc_throw(AllocFailed, "No allowed pools defined in selected subnet");
202 }
203
204 // last one was bogus for one of several reasons:
205 // - we just booted up and that's the first address we're allocating
206 // - a subnet was removed or other reconfiguration just completed
207 // - perhaps allocation algorithm was changed
208 // - last pool does not allow this client
209 if (it == pools.end()) {
210 it = first;
211 }
212
213 for (;;) {
214 // Trying next pool
215 if (retrying) {
216 for (; it != pools.end(); ++it) {
217 if ((*it)->clientSupported(client_classes)) {
218 break;
219 }
220 }
221 if (it == pools.end()) {
222 // Really out of luck today. That was the last pool.
223 break;
224 }
225 }
226
227 last = (*it)->getLastAllocated();
228 valid = (*it)->isLastAllocatedValid();
229 if (!valid && (last == (*it)->getFirstAddress())) {
230 // Pool was (re)initialized
231 (*it)->setLastAllocated(last);
232 subnet->setLastAllocated(pool_type_, last);
233 return (last);
234 }
235 // still can be bogus
236 if (valid && !(*it)->inRange(last)) {
237 valid = false;
238 (*it)->resetLastAllocated();
239 (*it)->setLastAllocated((*it)->getFirstAddress());
240 }
241
242 if (valid) {
243 // Ok, we have a pool that the last address belonged to, let's use it.
244 if (prefix) {
245 Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
246
247 if (!pool6) {
248 // Something is gravely wrong here
249 isc_throw(Unexpected, "Wrong type of pool: "
250 << (*it)->toText()
251 << " is not Pool6");
252 }
253 // Get the prefix length
254 prefix_len = pool6->getLength();
255 }
256
257 IOAddress next = increaseAddress(last, prefix, prefix_len);
258 if ((*it)->inRange(next)) {
259 // the next one is in the pool as well, so we haven't hit
260 // pool boundary yet
261 (*it)->setLastAllocated(next);
262 subnet->setLastAllocated(pool_type_, next);
263 return (next);
264 }
265
266 valid = false;
267 (*it)->resetLastAllocated();
268 }
269 // We hit pool boundary, let's try to jump to the next pool and try again
270 ++it;
271 retrying = true;
272 }
273
274 // Let's rewind to the beginning.
275 for (it = first; it != pools.end(); ++it) {
276 if ((*it)->clientSupported(client_classes)) {
277 (*it)->setLastAllocated((*it)->getFirstAddress());
278 (*it)->resetLastAllocated();
279 }
280 }
281
282 // ok to access first element directly. We checked that pools is non-empty
283 last = (*first)->getLastAllocated();
284 (*first)->setLastAllocated(last);
285 subnet->setLastAllocated(pool_type_, last);
286 return (last);
287}
288
290 : Allocator(lease_type) {
291 isc_throw(NotImplemented, "Hashed allocator is not implemented");
292}
293
295AllocEngine::HashedAllocator::pickAddressInternal(const SubnetPtr&,
296 const ClientClasses&,
297 const DuidPtr&,
298 const IOAddress&) {
299 isc_throw(NotImplemented, "Hashed allocator is not implemented");
300}
301
303 : Allocator(lease_type) {
304 isc_throw(NotImplemented, "Random allocator is not implemented");
305}
306
308AllocEngine::RandomAllocator::pickAddressInternal(const SubnetPtr&,
309 const ClientClasses&,
310 const DuidPtr&,
311 const IOAddress&) {
312 isc_throw(NotImplemented, "Random allocator is not implemented");
313}
314
315AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts,
316 bool ipv6)
317 : attempts_(attempts), incomplete_v4_reclamations_(0),
318 incomplete_v6_reclamations_(0) {
319
320 // Choose the basic (normal address) lease type
321 Lease::Type basic_type = ipv6 ? Lease::TYPE_NA : Lease::TYPE_V4;
322
323 // Initialize normal address allocators
324 switch (engine_type) {
325 case ALLOC_ITERATIVE:
326 allocators_[basic_type] = AllocatorPtr(new IterativeAllocator(basic_type));
327 break;
328 case ALLOC_HASHED:
329 allocators_[basic_type] = AllocatorPtr(new HashedAllocator(basic_type));
330 break;
331 case ALLOC_RANDOM:
332 allocators_[basic_type] = AllocatorPtr(new RandomAllocator(basic_type));
333 break;
334 default:
335 isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
336 }
337
338 // If this is IPv6 allocation engine, initialize also temporary addrs
339 // and prefixes
340 if (ipv6) {
341 switch (engine_type) {
342 case ALLOC_ITERATIVE:
345 break;
346 case ALLOC_HASHED:
349 break;
350 case ALLOC_RANDOM:
353 break;
354 default:
355 isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
356 }
357 }
358
359 // Register hook points
360 hook_index_lease4_select_ = Hooks.hook_index_lease4_select_;
361 hook_index_lease6_select_ = Hooks.hook_index_lease6_select_;
362}
363
365 std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
366
367 if (alloc == allocators_.end()) {
368 isc_throw(BadValue, "No allocator initialized for pool type "
369 << Lease::typeToText(type));
370 }
371 return (alloc->second);
372}
373
374} // end of namespace isc::dhcp
375} // end of namespace isc
376
377namespace {
378
388getIPv6Resrv(const SubnetID& subnet_id, const IOAddress& address) {
389 ConstHostCollection reserved;
390 // The global parameter ip-reservations-unique controls whether it is allowed
391 // to specify multiple reservations for the same IP address or delegated prefix
392 // or IP reservations must be unique. Some host backends do not support the
393 // former, thus we can't always use getAll6 calls to get the reservations
394 // for the given IP. When we're in the default mode, when IP reservations
395 // are unique, we should call get6 (supported by all backends). If we're in
396 // the mode in which non-unique reservations are allowed the backends which
397 // don't support it are not used and we can safely call getAll6.
398 if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
399 auto host = HostMgr::instance().get6(subnet_id, address);
400 if (host) {
401 reserved.push_back(host);
402 }
403 } else {
404 auto hosts = HostMgr::instance().getAll6(subnet_id, address);
405 reserved.insert(reserved.end(), hosts.begin(), hosts.end());
406 }
407 return (reserved);
408}
409
422bool
423inAllowedPool(AllocEngine::ClientContext6& ctx, const Lease::Type& lease_type,
424 const IOAddress& address, bool check_subnet) {
425 // If the subnet belongs to a shared network we will be iterating
426 // over the subnets that belong to this shared network.
427 Subnet6Ptr current_subnet = ctx.subnet_;
428 while (current_subnet) {
429
430 if (current_subnet->clientSupported(ctx.query_->getClasses())) {
431 if (check_subnet) {
432 if (current_subnet->inPool(lease_type, address)) {
433 return (true);
434 }
435 } else {
436 if (current_subnet->inPool(lease_type, address,
437 ctx.query_->getClasses())) {
438 return (true);
439 }
440 }
441 }
442
443 current_subnet = current_subnet->getNextSubnet(ctx.subnet_);
444 }
445
446 return (false);
447}
448
449}
450
451// ##########################################################################
452// # DHCPv6 lease allocation code starts here.
453// ##########################################################################
454
455namespace isc {
456namespace dhcp {
457
459 : query_(), fake_allocation_(false),
460 early_global_reservations_lookup_(false), subnet_(), host_subnet_(),
461 duid_(), hwaddr_(), host_identifiers_(), hosts_(),
462 fwd_dns_update_(false), rev_dns_update_(false), hostname_(),
463 callout_handle_(), ias_(), ddns_params_() {
464}
465
467 const DuidPtr& duid,
468 const bool fwd_dns,
469 const bool rev_dns,
470 const std::string& hostname,
471 const bool fake_allocation,
472 const Pkt6Ptr& query,
473 const CalloutHandlePtr& callout_handle)
474 : query_(query), fake_allocation_(fake_allocation),
475 early_global_reservations_lookup_(false), subnet_(subnet),
476 duid_(duid), hwaddr_(), host_identifiers_(), hosts_(),
477 fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname),
478 callout_handle_(callout_handle), allocated_resources_(), new_leases_(),
479 ias_(), ddns_params_() {
480
481 // Initialize host identifiers.
482 if (duid) {
483 addHostIdentifier(Host::IDENT_DUID, duid->getDuid());
484 }
485}
486
488 : iaid_(0), type_(Lease::TYPE_NA), hints_(), old_leases_(),
489 changed_leases_(), new_resources_(), ia_rsp_() {
490}
491
492void
495 const uint8_t prefix_len,
496 const uint32_t preferred,
497 const uint32_t valid) {
498 hints_.push_back(Resource(prefix, prefix_len, preferred, valid));
499}
500
501void
504 if (!iaaddr) {
505 isc_throw(BadValue, "IAADDR option pointer is null.");
506 }
507 addHint(iaaddr->getAddress(), 128,
508 iaaddr->getPreferred(), iaaddr->getValid());
509}
510
511void
513IAContext::addHint(const Option6IAPrefixPtr& iaprefix) {
514 if (!iaprefix) {
515 isc_throw(BadValue, "IAPREFIX option pointer is null.");
516 }
517 addHint(iaprefix->getAddress(), iaprefix->getLength(),
518 iaprefix->getPreferred(), iaprefix->getValid());
519}
520
521void
524 const uint8_t prefix_len) {
525 static_cast<void>(new_resources_.insert(Resource(prefix, prefix_len)));
526}
527
528bool
531 const uint8_t prefix_len) const {
532 return (static_cast<bool>(new_resources_.count(Resource(prefix,
533 prefix_len))));
534}
535
536void
539 const uint8_t prefix_len) {
540 static_cast<void>(allocated_resources_.insert(Resource(prefix,
541 prefix_len)));
542}
543
544bool
546isAllocated(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const {
547 return (static_cast<bool>
548 (allocated_resources_.count(Resource(prefix, prefix_len))));
549}
550
554 if (subnet && subnet->getReservationsInSubnet()) {
555 auto host = hosts_.find(subnet->getID());
556 if (host != hosts_.cend()) {
557 return (host->second);
558 }
559 }
560
561 return (globalHost());
562}
563
567 if (subnet && subnet_->getReservationsGlobal()) {
568 auto host = hosts_.find(SUBNET_ID_GLOBAL);
569 if (host != hosts_.cend()) {
570 return (host->second);
571 }
572 }
573
574 return (ConstHostPtr());
575}
576
577bool
579 ConstHostPtr ghost = globalHost();
580 return (ghost && ghost->hasReservation(resv));
581}
582
585 // We already have it return it unless the context subnet has changed.
586 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
587 return (ddns_params_);
588 }
589
590 // Doesn't exist yet or is stale, (re)create it.
591 if (subnet_) {
592 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
593 return (ddns_params_);
594 }
595
596 // Asked for it without a subnet? This case really shouldn't occur but
597 // for now let's return an instance with default values.
598 return (DdnsParamsPtr(new DdnsParams()));
599}
600
601void
603 // If there is no subnet, there is nothing to do.
604 if (!ctx.subnet_) {
605 return;
606 }
607
608 auto subnet = ctx.subnet_;
609
610 // If already done just return.
612 !subnet->getReservationsInSubnet()) {
613 return;
614 }
615
616 // @todo: This code can be trivially optimized.
618 subnet->getReservationsGlobal()) {
620 if (ghost) {
621 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
622
623 // If we had only to fetch global reservations it is done.
624 if (!subnet->getReservationsInSubnet()) {
625 return;
626 }
627 }
628 }
629
630 std::map<SubnetID, ConstHostPtr> host_map;
631 SharedNetwork6Ptr network;
632 subnet->getSharedNetwork(network);
633
634 // If the subnet belongs to a shared network it is usually going to be
635 // more efficient to make a query for all reservations for a particular
636 // client rather than a query for each subnet within this shared network.
637 // The only case when it is going to be less efficient is when there are
638 // more host identifier types in use than subnets within a shared network.
639 // As it breaks RADIUS use of host caching this can be disabled by the
640 // host manager.
641 const bool use_single_query = network &&
643 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
644
645 if (use_single_query) {
646 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
647 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
648 &id_pair.second[0],
649 id_pair.second.size());
650 // Store the hosts in the temporary map, because some hosts may
651 // belong to subnets outside of the shared network. We'll need
652 // to eliminate them.
653 for (auto host = hosts.begin(); host != hosts.end(); ++host) {
654 if ((*host)->getIPv6SubnetID() != SUBNET_ID_GLOBAL) {
655 host_map[(*host)->getIPv6SubnetID()] = *host;
656 }
657 }
658 }
659 }
660
661 // We can only search for the reservation if a subnet has been selected.
662 while (subnet) {
663
664 // Only makes sense to get reservations if the client has access
665 // to the class and host reservations are enabled for this subnet.
666 if (subnet->clientSupported(ctx.query_->getClasses()) &&
667 subnet->getReservationsInSubnet()) {
668 // Iterate over configured identifiers in the order of preference
669 // and try to use each of them to search for the reservations.
670 if (use_single_query) {
671 if (host_map.count(subnet->getID()) > 0) {
672 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
673 }
674 } else {
675 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
676 // Attempt to find a host using a specified identifier.
677 ConstHostPtr host = HostMgr::instance().get6(subnet->getID(),
678 id_pair.first,
679 &id_pair.second[0],
680 id_pair.second.size());
681 // If we found matching host for this subnet.
682 if (host) {
683 ctx.hosts_[subnet->getID()] = host;
684 break;
685 }
686 }
687 }
688 }
689
690 // We need to get to the next subnet if this is a shared network. If it
691 // is not (a plain subnet), getNextSubnet will return NULL and we're
692 // done here.
693 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
694 }
695}
696
699 ConstHostPtr host;
700 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
701 // Attempt to find a host using a specified identifier.
702 host = HostMgr::instance().get6(SUBNET_ID_GLOBAL, id_pair.first,
703 &id_pair.second[0], id_pair.second.size());
704
705 // If we found matching global host we're done.
706 if (host) {
707 break;
708 }
709 }
710
711 return (host);
712}
713
716
717 try {
718 if (!ctx.subnet_) {
719 isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation");
720 } else
721 if (!ctx.duid_) {
722 isc_throw(InvalidOperation, "DUID is mandatory for IPv6 lease allocation");
723 }
724
725 // Check if there are existing leases for that shared network and
726 // DUID/IAID.
727 Subnet6Ptr subnet = ctx.subnet_;
728 Lease6Collection all_leases =
730 *ctx.duid_,
731 ctx.currentIA().iaid_);
732
733 // Iterate over the leases and eliminate those that are outside of
734 // our shared network.
735 Lease6Collection leases;
736 while (subnet) {
737 for (auto l : all_leases) {
738 if ((l)->subnet_id_ == subnet->getID()) {
739 leases.push_back(l);
740 }
741 }
742
743 subnet = subnet->getNextSubnet(ctx.subnet_);
744 }
745
746 // Now do the checks:
747 // Case 1. if there are no leases, and there are reservations...
748 // 1.1. are the reserved addresses are used by someone else?
749 // yes: we have a problem
750 // no: assign them => done
751 // Case 2. if there are leases and there are no reservations...
752 // 2.1 are the leases reserved for someone else?
753 // yes: release them, assign something else
754 // no: renew them => done
755 // Case 3. if there are leases and there are reservations...
756 // 3.1 are the leases matching reservations?
757 // yes: renew them => done
758 // no: release existing leases, assign new ones based on reservations
759 // Case 4/catch-all. if there are no leases and no reservations...
760 // assign new leases
761
762 // Case 1: There are no leases and there's a reservation for this host.
763 if (leases.empty() && !ctx.hosts_.empty()) {
764
767 .arg(ctx.query_->getLabel());
768
769 // Try to allocate leases that match reservations. Typically this will
770 // succeed, except cases where the reserved addresses are used by
771 // someone else.
772 allocateReservedLeases6(ctx, leases);
773
774 leases = updateLeaseData(ctx, leases);
775
776 // If not, we'll need to continue and will eventually fall into case 4:
777 // getting a regular lease. That could happen when we're processing
778 // request from client X, there's a reserved address A for X, but
779 // A is currently used by client Y. We can't immediately reassign A
780 // from X to Y, because Y keeps using it, so X would send Decline right
781 // away. Need to wait till Y renews, then we can release A, so it
782 // will become available for X.
783
784 // Case 2: There are existing leases and there are no reservations.
785 //
786 // There is at least one lease for this client and there are no reservations.
787 // We will return these leases for the client, but we may need to update
788 // FQDN information.
789 } else if (!leases.empty() && ctx.hosts_.empty()) {
790
793 .arg(ctx.query_->getLabel());
794
795 // Check if the existing leases are reserved for someone else.
796 // If they're not, we're ok to keep using them.
797 removeNonmatchingReservedLeases6(ctx, leases);
798
799 leases = updateLeaseData(ctx, leases);
800
801 // If leases are empty at this stage, it means that we used to have
802 // leases for this client, but we checked and those leases are reserved
803 // for someone else, so we lost them. We will need to continue and
804 // will finally end up in case 4 (no leases, no reservations), so we'll
805 // assign something new.
806
807 // Case 3: There are leases and there are reservations.
808 } else if (!leases.empty() && !ctx.hosts_.empty()) {
809
812 .arg(ctx.query_->getLabel());
813
814 // First, check if have leases matching reservations, and add new
815 // leases if we don't have them.
816 allocateReservedLeases6(ctx, leases);
817
818 // leases now contain both existing and new leases that were created
819 // from reservations.
820
821 // Second, let's remove leases that are reserved for someone else.
822 // This applies to any existing leases. This will not happen frequently,
823 // but it may happen with the following chain of events:
824 // 1. client A gets address X;
825 // 2. reservation for client B for address X is made by a administrator;
826 // 3. client A reboots
827 // 4. client A requests the address (X) he got previously
828 removeNonmatchingReservedLeases6(ctx, leases);
829
830 // leases now contain existing and new leases, but we removed those
831 // leases that are reserved for someone else (non-matching reserved).
832
833 // There's one more check to do. Let's remove leases that are not
834 // matching reservations, i.e. if client X has address A, but there's
835 // a reservation for address B, we should release A and reassign B.
836 // Caveat: do this only if we have at least one reserved address.
837 removeNonreservedLeases6(ctx, leases);
838
839 // All checks are done. Let's hope we have some leases left.
840
841 // Update any leases we have left.
842 leases = updateLeaseData(ctx, leases);
843
844 // If we don't have any leases at this stage, it means that we hit
845 // one of the following cases:
846 // - we have a reservation, but it's not for this IAID/ia-type and
847 // we had to return the address we were using
848 // - we have a reservation for this iaid/ia-type, but the reserved
849 // address is currently used by someone else. We can't assign it
850 // yet.
851 // - we had an address, but we just discovered that it's reserved for
852 // someone else, so we released it.
853 }
854
855 if (leases.empty()) {
856 // Case 4/catch-all: One of the following is true:
857 // - we don't have leases and there are no reservations
858 // - we used to have leases, but we lost them, because they are now
859 // reserved for someone else
860 // - we have a reservation, but it is not usable yet, because the address
861 // is still used by someone else
862 //
863 // In any case, we need to go through normal lease assignment process
864 // for now. This is also a catch-all or last resort approach, when we
865 // couldn't find any reservations (or couldn't use them).
866
869 .arg(ctx.query_->getLabel());
870
871 leases = allocateUnreservedLeases6(ctx);
872 }
873
874 if (!leases.empty()) {
875 // If there are any leases allocated, let's store in them in the
876 // IA context so as they are available when we process subsequent
877 // IAs.
878 BOOST_FOREACH(Lease6Ptr lease, leases) {
879 ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
880 ctx.new_leases_.push_back(lease);
881 }
882 return (leases);
883 }
884
885 } catch (const isc::Exception& e) {
886
887 // Some other error, return an empty lease.
889 .arg(ctx.query_->getLabel())
890 .arg(e.what());
891 }
892
893 return (Lease6Collection());
894}
895
897AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
898
899 AllocatorPtr allocator = getAllocator(ctx.currentIA().type_);
900
901 if (!allocator) {
902 isc_throw(InvalidOperation, "No allocator specified for "
903 << Lease6::typeToText(ctx.currentIA().type_));
904 }
905
906 Lease6Collection leases;
907
909 if (!ctx.currentIA().hints_.empty()) {
911 hint = ctx.currentIA().hints_[0].getAddress();
912 }
913
914 Subnet6Ptr original_subnet = ctx.subnet_;
915 Subnet6Ptr subnet = ctx.subnet_;
916
917 Pool6Ptr pool;
918
919 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
920
921 for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
922
923 if (!subnet->clientSupported(ctx.query_->getClasses())) {
924 continue;
925 }
926
927 ctx.subnet_ = subnet;
928
929 // check if the hint is in pool and is available
930 // This is equivalent of subnet->inPool(hint), but returns the pool
931 pool = boost::dynamic_pointer_cast<Pool6>
932 (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(),
933 hint));
934
935 // check if the pool is allowed
936 if (!pool || !pool->clientSupported(ctx.query_->getClasses())) {
937 continue;
938 }
939
940 bool in_subnet = subnet->getReservationsInSubnet();
941
943 Lease6Ptr lease =
944 LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, hint);
945 if (!lease) {
946
947 // In-pool reservations: Check if this address is reserved for someone
948 // else. There is no need to check for whom it is reserved, because if
949 // it has been reserved for us we would have already allocated a lease.
950
952 // When out-of-pool flag is true the server may assume that all host
953 // reservations are for addresses that do not belong to the dynamic
954 // pool. Therefore, it can skip the reservation checks when dealing
955 // with in-pool addresses.
956 if (in_subnet &&
957 (!subnet->getReservationsOutOfPool() ||
958 !subnet->inPool(ctx.currentIA().type_, hint))) {
959 hosts = getIPv6Resrv(subnet->getID(), hint);
960 }
961
962 if (hosts.empty()) {
963 // If the in-pool reservations are disabled, or there is no
964 // reservation for a given hint, we're good to go.
965
966 // The hint is valid and not currently used, let's create a
967 // lease for it
968 lease = createLease6(ctx, hint, pool->getLength(), callout_status);
969
970 // It can happen that the lease allocation failed (we could
971 // have lost the race condition. That means that the hint is
972 // no longer usable and we need to continue the regular
973 // allocation path.
974 if (lease) {
975
977 Lease6Collection collection;
978 collection.push_back(lease);
979 return (collection);
980 }
981 } else {
984 .arg(ctx.query_->getLabel())
985 .arg(hint.toText());
986 }
987
988 } else if (lease->expired()) {
989
990 // If the lease is expired, we may likely reuse it, but...
992 // When out-of-pool flag is true the server may assume that all host
993 // reservations are for addresses that do not belong to the dynamic
994 // pool. Therefore, it can skip the reservation checks when dealing
995 // with in-pool addresses.
996 if (in_subnet &&
997 (!subnet->getReservationsOutOfPool() ||
998 !subnet->inPool(ctx.currentIA().type_, hint))) {
999 hosts = getIPv6Resrv(subnet->getID(), hint);
1000 }
1001
1002 // Let's check if there is a reservation for this address.
1003 if (hosts.empty()) {
1004
1005 // Copy an existing, expired lease so as it can be returned
1006 // to the caller.
1007 Lease6Ptr old_lease(new Lease6(*lease));
1008 ctx.currentIA().old_leases_.push_back(old_lease);
1009
1011 lease = reuseExpiredLease(lease, ctx, pool->getLength(),
1012 callout_status);
1013
1015 leases.push_back(lease);
1016 return (leases);
1017
1018 } else {
1021 .arg(ctx.query_->getLabel())
1022 .arg(hint.toText());
1023 }
1024 }
1025 }
1026
1027 // We have the choice in the order checking the lease and
1028 // the reservation. The default is to begin by the lease
1029 // if the multi-threading is disabled.
1030 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
1031 // If multi-threading is disabled, honor the configured order for host
1032 // reservations lookup.
1033 if (!check_reservation_first) {
1034 check_reservation_first = CfgMgr::instance().getCurrentCfg()->getReservationsLookupFirst();
1035 }
1036
1037 uint64_t total_attempts = 0;
1038
1039 // Need to check if the subnet belongs to a shared network. If so,
1040 // we might be able to find a better subnet for lease allocation,
1041 // for which it is more likely that there are some leases available.
1042 // If we stick to the selected subnet, we may end up walking over
1043 // the entire subnet (or more subnets) to discover that the pools
1044 // have been exhausted. Using a subnet from which a lease was
1045 // assigned most recently is an optimization which increases
1046 // the likelihood of starting from the subnet which pools are not
1047 // exhausted.
1048 SharedNetwork6Ptr network;
1049 original_subnet->getSharedNetwork(network);
1050 if (network) {
1051 // This would try to find a subnet with the same set of classes
1052 // as the current subnet, but with the more recent "usage timestamp".
1053 // This timestamp is only updated for the allocations made with an
1054 // allocator (unreserved lease allocations), not the static
1055 // allocations or requested addresses.
1056 original_subnet = network->getPreferredSubnet(original_subnet, ctx.currentIA().type_);
1057 }
1058
1059 ctx.subnet_ = subnet = original_subnet;
1060
1061 // The following counter tracks the number of subnets with matching client
1062 // classes from which the allocation engine attempted to assign leases.
1063 uint64_t subnets_with_unavail_leases = 0;
1064 // The following counter tracks the number of subnets in which there were
1065 // no matching pools for the client.
1066 uint64_t subnets_with_unavail_pools = 0;
1067
1068 for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
1069
1070 if (!subnet->clientSupported(ctx.query_->getClasses())) {
1071 continue;
1072 }
1073
1074 // The hint was useless (it was not provided at all, was used by someone else,
1075 // was out of pool or reserved for someone else). Search the pool until first
1076 // of the following occurs:
1077 // - we find a free address
1078 // - we find an address for which the lease has expired
1079 // - we exhaust number of tries
1080 uint64_t possible_attempts =
1081 subnet->getPoolCapacity(ctx.currentIA().type_,
1082 ctx.query_->getClasses());
1083
1084 // If the number of tries specified in the allocation engine constructor
1085 // is set to 0 (unlimited) or the pools capacity is lower than that number,
1086 // let's use the pools capacity as the maximum number of tries. Trying
1087 // more than the actual pools capacity is a waste of time. If the specified
1088 // number of tries is lower than the pools capacity, use that number.
1089 uint64_t max_attempts = ((attempts_ == 0) || (possible_attempts < attempts_)) ? possible_attempts : attempts_;
1090
1091 if (max_attempts > 0) {
1092 // If max_attempts is greater than 0, there are some pools in this subnet
1093 // from which we can potentially get a lease.
1094 ++subnets_with_unavail_leases;
1095 } else {
1096 // If max_attempts is 0, it is an indication that there are no pools
1097 // in the subnet from which we can get a lease.
1098 ++subnets_with_unavail_pools;
1099 continue;
1100 }
1101
1102 bool in_subnet = subnet->getReservationsInSubnet();
1103 bool out_of_pool = subnet->getReservationsOutOfPool();
1104
1105 // Set the default status code in case the lease6_select callouts
1106 // do not exist and the callout handle has a status returned by
1107 // any of the callouts already invoked for this packet.
1108 if (ctx.callout_handle_) {
1109 ctx.callout_handle_->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
1110 }
1111
1112 for (uint64_t i = 0; i < max_attempts; ++i) {
1113
1114 ++total_attempts;
1115
1116 IOAddress candidate = allocator->pickAddress(subnet,
1117 ctx.query_->getClasses(),
1118 ctx.duid_,
1119 hint);
1120 // The first step is to find out prefix length. It is 128 for
1121 // non-PD leases.
1122 uint8_t prefix_len = 128;
1123 if (ctx.currentIA().type_ == Lease::TYPE_PD) {
1124 pool = boost::dynamic_pointer_cast<Pool6>(
1125 subnet->getPool(ctx.currentIA().type_,
1126 ctx.query_->getClasses(),
1127 candidate));
1128 if (pool) {
1129 prefix_len = pool->getLength();
1130 }
1131 }
1132
1133 // First check for reservation when it is the choice.
1134 if (check_reservation_first && in_subnet && !out_of_pool) {
1135 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1136 if (!hosts.empty()) {
1137 // Don't allocate.
1138 continue;
1139 }
1140 }
1141
1142 // Check if the resource is busy i.e. can be being allocated
1143 // by another thread to another client.
1144 ResourceHandler resource_handler;
1145 if (MultiThreadingMgr::instance().getMode() &&
1146 !resource_handler.tryLock(ctx.currentIA().type_, candidate)) {
1147 // Don't allocate.
1148 continue;
1149 }
1150
1151 // Look for an existing lease for the candidate.
1152 Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1153 candidate);
1154
1155 if (!existing) {
1159 if (!check_reservation_first && in_subnet && !out_of_pool) {
1160 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1161 if (!hosts.empty()) {
1162 // Don't allocate.
1163 continue;
1164 }
1165 }
1166
1167 // there's no existing lease for selected candidate, so it is
1168 // free. Let's allocate it.
1169
1170 ctx.subnet_ = subnet;
1171 Lease6Ptr lease = createLease6(ctx, candidate, prefix_len, callout_status);
1172 if (lease) {
1173 // We are allocating a new lease (not renewing). So, the
1174 // old lease should be NULL.
1175 ctx.currentIA().old_leases_.clear();
1176
1177 leases.push_back(lease);
1178 return (leases);
1179
1180 } else if (ctx.callout_handle_ &&
1181 (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
1182 // Don't retry when the callout status is not continue.
1183 break;
1184 }
1185
1186 // Although the address was free just microseconds ago, it may have
1187 // been taken just now. If the lease insertion fails, we continue
1188 // allocation attempts.
1189 } else if (existing->expired()) {
1190 // Make sure it's not reserved.
1191 if (!check_reservation_first && in_subnet && !out_of_pool) {
1192 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1193 if (!hosts.empty()) {
1194 // Don't allocate.
1195 continue;
1196 }
1197 }
1198
1199 // Copy an existing, expired lease so as it can be returned
1200 // to the caller.
1201 Lease6Ptr old_lease(new Lease6(*existing));
1202 ctx.currentIA().old_leases_.push_back(old_lease);
1203
1204 ctx.subnet_ = subnet;
1205 existing = reuseExpiredLease(existing, ctx, prefix_len,
1206 callout_status);
1207
1208 leases.push_back(existing);
1209 return (leases);
1210 }
1211 }
1212 }
1213
1214 if (network) {
1215 // The client is in the shared network. Let's log the high level message
1216 // indicating which shared network the client belongs to.
1218 .arg(ctx.query_->getLabel())
1219 .arg(network->getName())
1220 .arg(subnets_with_unavail_leases)
1221 .arg(subnets_with_unavail_pools);
1222 StatsMgr::instance().addValue("v6-allocation-fail-shared-network",
1223 static_cast<int64_t>(1));
1224 StatsMgr::instance().addValue(
1225 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1226 "v6-allocation-fail-shared-network"),
1227 static_cast<int64_t>(1));
1228 } else {
1229 // The client is not connected to a shared network. It is connected
1230 // to a subnet. Let's log the ID of that subnet.
1231 std::string shared_network = ctx.subnet_->getSharedNetworkName();
1232 if (shared_network.empty()) {
1233 shared_network = "(none)";
1234 }
1236 .arg(ctx.query_->getLabel())
1237 .arg(ctx.subnet_->toText())
1238 .arg(ctx.subnet_->getID())
1239 .arg(shared_network);
1240 StatsMgr::instance().addValue("v6-allocation-fail-subnet",
1241 static_cast<int64_t>(1));
1242 StatsMgr::instance().addValue(
1243 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1244 "v6-allocation-fail-subnet"),
1245 static_cast<int64_t>(1));
1246 }
1247 if (total_attempts == 0) {
1248 // In this case, it seems that none of the pools in the subnets could
1249 // be used for that client, both in case the client is connected to
1250 // a shared network or to a single subnet. Apparently, the client was
1251 // rejected to use the pools because of the client classes' mismatch.
1253 .arg(ctx.query_->getLabel());
1254 StatsMgr::instance().addValue("v6-allocation-fail-no-pools",
1255 static_cast<int64_t>(1));
1256 StatsMgr::instance().addValue(
1257 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1258 "v6-allocation-fail-no-pools"),
1259 static_cast<int64_t>(1));
1260 } else {
1261 // This is an old log message which provides a number of attempts
1262 // made by the allocation engine to allocate a lease. The only case
1263 // when we don't want to log this message is when the number of
1264 // attempts is zero (condition above), because it would look silly.
1266 .arg(ctx.query_->getLabel())
1267 .arg(total_attempts);
1268 StatsMgr::instance().addValue("v6-allocation-fail",
1269 static_cast<int64_t>(1));
1270 StatsMgr::instance().addValue(
1271 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1272 "v6-allocation-fail"),
1273 static_cast<int64_t>(1));
1274 }
1275
1276 const ClientClasses& classes = ctx.query_->getClasses();
1277 if (!classes.empty()) {
1279 .arg(ctx.query_->getLabel())
1280 .arg(classes.toText());
1281 StatsMgr::instance().addValue("v6-allocation-fail-classes",
1282 static_cast<int64_t>(1));
1283 StatsMgr::instance().addValue(
1284 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1285 "v6-allocation-fail-classes"),
1286 static_cast<int64_t>(1));
1287 }
1288
1289 // We failed to allocate anything. Let's return empty collection.
1290 return (Lease6Collection());
1291}
1292
1293void
1294AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
1295 Lease6Collection& existing_leases) {
1296
1297 // If there are no reservations or the reservation is v4, there's nothing to do.
1298 if (ctx.hosts_.empty()) {
1301 .arg(ctx.query_->getLabel());
1302 return;
1303 }
1304
1305 // Let's convert this from Lease::Type to IPv6Reserv::Type
1306 IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1308
1309 // We want to avoid allocating new lease for an IA if there is already
1310 // a valid lease for which client has reservation. So, we first check if
1311 // we already have a lease for a reserved address or prefix.
1312 BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
1313 if ((lease->valid_lft_ != 0)) {
1314 if ((ctx.hosts_.count(lease->subnet_id_) > 0) &&
1315 ctx.hosts_[lease->subnet_id_]->hasReservation(makeIPv6Resrv(*lease))) {
1316 // We found existing lease for a reserved address or prefix.
1317 // We'll simply extend the lifetime of the lease.
1320 .arg(ctx.query_->getLabel())
1321 .arg(lease->typeToText(lease->type_))
1322 .arg(lease->addr_.toText());
1323
1324 // Besides IP reservations we're also going to return other reserved
1325 // parameters, such as hostname. We want to hand out the hostname value
1326 // from the same reservation entry as IP addresses. Thus, let's see if
1327 // there is any hostname reservation.
1328 if (!ctx.host_subnet_) {
1329 SharedNetwork6Ptr network;
1330 ctx.subnet_->getSharedNetwork(network);
1331 if (network) {
1332 // Remember the subnet that holds this preferred host
1333 // reservation. The server will use it to return appropriate
1334 // FQDN, classes etc.
1335 ctx.host_subnet_ = network->getSubnet(lease->subnet_id_);
1336 ConstHostPtr host = ctx.hosts_[lease->subnet_id_];
1337 // If there is a hostname reservation here we should stick
1338 // to this reservation. By updating the hostname in the
1339 // context we make sure that the database is updated with
1340 // this new value and the server doesn't need to do it and
1341 // its processing performance is not impacted by the hostname
1342 // updates.
1343 if (host && !host->getHostname().empty()) {
1344 // We have to determine whether the hostname is generated
1345 // in response to client's FQDN or not. If yes, we will
1346 // need to qualify the hostname. Otherwise, we just use
1347 // the hostname as it is specified for the reservation.
1348 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1349 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1350 qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1351 static_cast<bool>(fqdn));
1352 }
1353 }
1354 }
1355
1356 // Got a lease for a reservation in this IA.
1357 return;
1358 }
1359 }
1360 }
1361
1362 // There is no lease for a reservation in this IA. So, let's now iterate
1363 // over reservations specified and try to allocate one of them for the IA.
1364
1365 for (Subnet6Ptr subnet = ctx.subnet_; subnet;
1366 subnet = subnet->getNextSubnet(ctx.subnet_)) {
1367
1368 SubnetID subnet_id = subnet->getID();
1369
1370 // No hosts for this subnet or the subnet not supported.
1371 if (!subnet->clientSupported(ctx.query_->getClasses()) ||
1372 ctx.hosts_.count(subnet_id) == 0) {
1373 continue;
1374 }
1375
1376 ConstHostPtr host = ctx.hosts_[subnet_id];
1377
1378 bool in_subnet = subnet->getReservationsInSubnet();
1379
1380 // Get the IPv6 reservations of specified type.
1381 const IPv6ResrvRange& reservs = host->getIPv6Reservations(type);
1382 BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
1383 // We do have a reservation for address or prefix.
1384 const IOAddress& addr = type_lease_tuple.second.getPrefix();
1385 uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1386
1387 // We have allocated this address/prefix while processing one of the
1388 // previous IAs, so let's try another reservation.
1389 if (ctx.isAllocated(addr, prefix_len)) {
1390 continue;
1391 }
1392
1393 // The out-of-pool flag indicates that no client should be assigned
1394 // reserved addresses from within the dynamic pool, and for that
1395 // reason look only for reservations that are outside the pools,
1396 // hence the inPool check.
1397 if (!in_subnet ||
1398 (subnet->getReservationsOutOfPool() &&
1399 subnet->inPool(ctx.currentIA().type_, addr))) {
1400 continue;
1401 }
1402
1403 // If there's a lease for this address, let's not create it.
1404 // It doesn't matter whether it is for this client or for someone else.
1405 if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1406 addr)) {
1407
1408 // Let's remember the subnet from which the reserved address has been
1409 // allocated. We'll use this subnet for allocating other reserved
1410 // resources.
1411 ctx.subnet_ = subnet;
1412
1413 if (!ctx.host_subnet_) {
1414 ctx.host_subnet_ = subnet;
1415 if (!host->getHostname().empty()) {
1416 // If there is a hostname reservation here we should stick
1417 // to this reservation. By updating the hostname in the
1418 // context we make sure that the database is updated with
1419 // this new value and the server doesn't need to do it and
1420 // its processing performance is not impacted by the hostname
1421 // updates.
1422
1423 // We have to determine whether the hostname is generated
1424 // in response to client's FQDN or not. If yes, we will
1425 // need to qualify the hostname. Otherwise, we just use
1426 // the hostname as it is specified for the reservation.
1427 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1428 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1429 qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1430 static_cast<bool>(fqdn));
1431 }
1432 }
1433
1434 // Ok, let's create a new lease...
1435 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
1436 Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1437
1438 // ... and add it to the existing leases list.
1439 existing_leases.push_back(lease);
1440
1441 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1443 .arg(addr.toText())
1444 .arg(ctx.query_->getLabel());
1445 } else {
1447 .arg(addr.toText())
1448 .arg(static_cast<int>(prefix_len))
1449 .arg(ctx.query_->getLabel());
1450 }
1451
1452 // We found a lease for this client and this IA. Let's return.
1453 // Returning after the first lease was assigned is useful if we
1454 // have multiple reservations for the same client. If the client
1455 // sends 2 IAs, the first time we call allocateReservedLeases6 will
1456 // use the first reservation and return. The second time, we'll
1457 // go over the first reservation, but will discover that there's
1458 // a lease corresponding to it and will skip it and then pick
1459 // the second reservation and turn it into the lease. This approach
1460 // would work for any number of reservations.
1461 return;
1462 }
1463 }
1464 }
1465
1466 // Found no subnet reservations so now try the global reservation.
1467 allocateGlobalReservedLeases6(ctx, existing_leases);
1468}
1469
1470void
1471AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
1472 Lease6Collection& existing_leases) {
1473 // Get the global host
1474 ConstHostPtr ghost = ctx.globalHost();
1475 if (!ghost) {
1476 return;
1477 }
1478
1479 // We want to avoid allocating a new lease for an IA if there is already
1480 // a valid lease for which client has reservation. So, we first check if
1481 // we already have a lease for a reserved address or prefix.
1482 BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
1483 if ((lease->valid_lft_ != 0) &&
1484 (ghost->hasReservation(makeIPv6Resrv(*lease)))) {
1485 // We found existing lease for a reserved address or prefix.
1486 // We'll simply extend the lifetime of the lease.
1489 .arg(ctx.query_->getLabel())
1490 .arg(lease->typeToText(lease->type_))
1491 .arg(lease->addr_.toText());
1492
1493 // Besides IP reservations we're also going to return other reserved
1494 // parameters, such as hostname. We want to hand out the hostname value
1495 // from the same reservation entry as IP addresses. Thus, let's see if
1496 // there is any hostname reservation.
1497 if (!ghost->getHostname().empty()) {
1498 // We have to determine whether the hostname is generated
1499 // in response to client's FQDN or not. If yes, we will
1500 // need to qualify the hostname. Otherwise, we just use
1501 // the hostname as it is specified for the reservation.
1502 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1503 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1504 qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1505 static_cast<bool>(fqdn));
1506 }
1507
1508 // Got a lease for a reservation in this IA.
1509 return;
1510 }
1511 }
1512
1513 // There is no lease for a reservation in this IA. So, let's now iterate
1514 // over reservations specified and try to allocate one of them for the IA.
1515
1516 // Let's convert this from Lease::Type to IPv6Reserv::Type
1517 IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1519
1520 const IPv6ResrvRange& reservs = ghost->getIPv6Reservations(type);
1521 BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
1522 // We do have a reservation for address or prefix.
1523 const IOAddress& addr = type_lease_tuple.second.getPrefix();
1524 uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1525
1526 // We have allocated this address/prefix while processing one of the
1527 // previous IAs, so let's try another reservation.
1528 if (ctx.isAllocated(addr, prefix_len)) {
1529 continue;
1530 }
1531
1532 // If there's a lease for this address, let's not create it.
1533 // It doesn't matter whether it is for this client or for someone else.
1534 if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
1535
1536 if (!ghost->getHostname().empty()) {
1537 // If there is a hostname reservation here we should stick
1538 // to this reservation. By updating the hostname in the
1539 // context we make sure that the database is updated with
1540 // this new value and the server doesn't need to do it and
1541 // its processing performance is not impacted by the hostname
1542 // updates.
1543
1544 // We have to determine whether the hostname is generated
1545 // in response to client's FQDN or not. If yes, we will
1546 // need to qualify the hostname. Otherwise, we just use
1547 // the hostname as it is specified for the reservation.
1548 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1549 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1550 qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1551 static_cast<bool>(fqdn));
1552 }
1553
1554 // Ok, let's create a new lease...
1555 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
1556 Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1557
1558 // ... and add it to the existing leases list.
1559 existing_leases.push_back(lease);
1560
1561 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1563 .arg(addr.toText())
1564 .arg(ctx.query_->getLabel());
1565 } else {
1567 .arg(addr.toText())
1568 .arg(static_cast<int>(prefix_len))
1569 .arg(ctx.query_->getLabel());
1570 }
1571
1572 // We found a lease for this client and this IA. Let's return.
1573 // Returning after the first lease was assigned is useful if we
1574 // have multiple reservations for the same client. If the client
1575 // sends 2 IAs, the first time we call allocateReservedLeases6 will
1576 // use the first reservation and return. The second time, we'll
1577 // go over the first reservation, but will discover that there's
1578 // a lease corresponding to it and will skip it and then pick
1579 // the second reservation and turn it into the lease. This approach
1580 // would work for any number of reservations.
1581 return;
1582 }
1583 }
1584}
1585
1586void
1587AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
1588 Lease6Collection& existing_leases) {
1589 // If there are no leases (so nothing to remove) just return.
1590 if (existing_leases.empty() || !ctx.subnet_) {
1591 return;
1592 }
1593 // If host reservation is disabled (so there are no reserved leases)
1594 // use the simplified version.
1595 if (!ctx.subnet_->getReservationsInSubnet() &&
1596 !ctx.subnet_->getReservationsGlobal()) {
1597 removeNonmatchingReservedNoHostLeases6(ctx, existing_leases);
1598 return;
1599 }
1600
1601 // We need a copy, so we won't be iterating over a container and
1602 // removing from it at the same time. It's only a copy of pointers,
1603 // so the operation shouldn't be that expensive.
1604 Lease6Collection copy = existing_leases;
1605
1606 BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
1607 // If we have reservation we should check if the reservation is for
1608 // the candidate lease. If so, we simply accept the lease.
1609 IPv6Resrv resv = makeIPv6Resrv(*candidate);
1610 if ((ctx.hasGlobalReservation(resv)) ||
1611 ((ctx.hosts_.count(candidate->subnet_id_) > 0) &&
1612 (ctx.hosts_[candidate->subnet_id_]->hasReservation(resv)))) {
1613 // We have a subnet reservation
1614 continue;
1615 }
1616
1617 // The candidate address doesn't appear to be reserved for us.
1618 // We have to make a bit more expensive operation here to retrieve
1619 // the reservation for the candidate lease and see if it is
1620 // reserved for someone else.
1621 auto hosts = getIPv6Resrv(ctx.subnet_->getID(), candidate->addr_);
1622 // If lease is not reserved to someone else, it means that it can
1623 // be allocated to us from a dynamic pool, but we must check if
1624 // this lease belongs to any pool. If it does, we can proceed to
1625 // checking the next lease.
1626 if (hosts.empty() && inAllowedPool(ctx, candidate->type_,
1627 candidate->addr_, false)) {
1628 continue;
1629 }
1630
1631 if (!hosts.empty()) {
1632 // Ok, we have a problem. This host has a lease that is reserved
1633 // for someone else. We need to recover from this.
1634 if (hosts.size() == 1) {
1635 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1637 .arg(candidate->addr_.toText())
1638 .arg(ctx.duid_->toText())
1639 .arg(hosts.front()->getIdentifierAsText());
1640 } else {
1642 .arg(candidate->addr_.toText())
1643 .arg(static_cast<int>(candidate->prefixlen_))
1644 .arg(ctx.duid_->toText())
1645 .arg(hosts.front()->getIdentifierAsText());
1646 }
1647 } else {
1648 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1650 .arg(candidate->addr_.toText())
1651 .arg(ctx.duid_->toText())
1652 .arg(hosts.size());
1653 } else {
1655 .arg(candidate->addr_.toText())
1656 .arg(static_cast<int>(candidate->prefixlen_))
1657 .arg(ctx.duid_->toText())
1658 .arg(hosts.size());
1659 }
1660 }
1661 }
1662
1663 // Remove this lease from LeaseMgr as it is reserved to someone
1664 // else or doesn't belong to a pool.
1665 if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1666 // Concurrent delete performed by other instance which should
1667 // properly handle dns and stats updates.
1668 continue;
1669 }
1670
1671 // Update DNS if needed.
1672 queueNCR(CHG_REMOVE, candidate);
1673
1674 // Need to decrease statistic for assigned addresses.
1675 StatsMgr::instance().addValue(
1676 StatsMgr::generateName("subnet", candidate->subnet_id_,
1677 ctx.currentIA().type_ == Lease::TYPE_NA ?
1678 "assigned-nas" : "assigned-pds"),
1679 static_cast<int64_t>(-1));
1680
1681 // In principle, we could trigger a hook here, but we will do this
1682 // only if we get serious complaints from actual users. We want the
1683 // conflict resolution procedure to really work and user libraries
1684 // should not interfere with it.
1685
1686 // Add this to the list of removed leases.
1687 ctx.currentIA().old_leases_.push_back(candidate);
1688
1689 // Let's remove this candidate from existing leases
1690 removeLeases(existing_leases, candidate->addr_);
1691 }
1692}
1693
1694void
1695AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
1696 Lease6Collection& existing_leases) {
1697 // We need a copy, so we won't be iterating over a container and
1698 // removing from it at the same time. It's only a copy of pointers,
1699 // so the operation shouldn't be that expensive.
1700 Lease6Collection copy = existing_leases;
1701
1702 BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
1703 // Lease can be allocated to us from a dynamic pool, but we must
1704 // check if this lease belongs to any allowed pool. If it does,
1705 // we can proceed to checking the next lease.
1706 if (inAllowedPool(ctx, candidate->type_,
1707 candidate->addr_, false)) {
1708 continue;
1709 }
1710
1711 // Remove this lease from LeaseMgr as it doesn't belong to a pool.
1712 if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1713 // Concurrent delete performed by other instance which should
1714 // properly handle dns and stats updates.
1715 continue;
1716 }
1717
1718 // Update DNS if needed.
1719 queueNCR(CHG_REMOVE, candidate);
1720
1721 // Need to decrease statistic for assigned addresses.
1722 StatsMgr::instance().addValue(
1723 StatsMgr::generateName("subnet", candidate->subnet_id_,
1724 ctx.currentIA().type_ == Lease::TYPE_NA ?
1725 "assigned-nas" : "assigned-pds"),
1726 static_cast<int64_t>(-1));
1727
1728 // Add this to the list of removed leases.
1729 ctx.currentIA().old_leases_.push_back(candidate);
1730
1731 // Let's remove this candidate from existing leases
1732 removeLeases(existing_leases, candidate->addr_);
1733 }
1734}
1735
1736bool
1737AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
1738
1739 bool removed = false;
1740 for (Lease6Collection::iterator lease = container.begin();
1741 lease != container.end(); ++lease) {
1742 if ((*lease)->addr_ == addr) {
1743 lease->reset();
1744 removed = true;
1745 }
1746 }
1747
1748 // Remove all elements that have NULL value
1749 container.erase(std::remove(container.begin(), container.end(), Lease6Ptr()),
1750 container.end());
1751
1752 return (removed);
1753}
1754
1755void
1756AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
1757 Lease6Collection& existing_leases) {
1758 // This method removes leases that are not reserved for this host.
1759 // It will keep at least one lease, though, as a fallback.
1760 int total = existing_leases.size();
1761 if (total <= 1) {
1762 return;
1763 }
1764
1765 // This is officially not scary code anymore. iterates and marks specified
1766 // leases for deletion, by setting appropriate pointers to NULL.
1767 for (Lease6Collection::iterator lease = existing_leases.begin();
1768 lease != existing_leases.end(); ++lease) {
1769
1770 // If there is reservation for this keep it.
1771 IPv6Resrv resv = makeIPv6Resrv(*(*lease));
1772 if (ctx.hasGlobalReservation(resv) ||
1773 ((ctx.hosts_.count((*lease)->subnet_id_) > 0) &&
1774 (ctx.hosts_[(*lease)->subnet_id_]->hasReservation(resv)))) {
1775 continue;
1776 }
1777
1778 // @todo - If this is for a fake_allocation, we should probably
1779 // not be deleting the lease or removing DNS entries. We should
1780 // simply remove it from the list.
1781 // We have reservations, but not for this lease. Release it.
1782 // Remove this lease from LeaseMgr
1783 if (!LeaseMgrFactory::instance().deleteLease(*lease)) {
1784 // Concurrent delete performed by other instance which should
1785 // properly handle dns and stats updates.
1786 continue;
1787 }
1788
1789 // Update DNS if required.
1790 queueNCR(CHG_REMOVE, *lease);
1791
1792 // Need to decrease statistic for assigned addresses.
1793 StatsMgr::instance().addValue(
1794 StatsMgr::generateName("subnet", (*lease)->subnet_id_,
1795 ctx.currentIA().type_ == Lease::TYPE_NA ?
1796 "assigned-nas" : "assigned-pds"),
1797 static_cast<int64_t>(-1));
1798
1800
1801 // Add this to the list of removed leases.
1802 ctx.currentIA().old_leases_.push_back(*lease);
1803
1804 // Set this pointer to NULL. The pointer is still valid. We're just
1805 // setting the Lease6Ptr to NULL value. We'll remove all NULL
1806 // pointers once the loop is finished.
1807 lease->reset();
1808
1809 if (--total == 1) {
1810 // If there's only one lease left, break the loop.
1811 break;
1812 }
1813
1814 }
1815
1816 // Remove all elements that we previously marked for deletion (those that
1817 // have NULL value).
1818 existing_leases.erase(std::remove(existing_leases.begin(),
1819 existing_leases.end(), Lease6Ptr()), existing_leases.end());
1820}
1821
1823AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
1824 uint8_t prefix_len,
1825 CalloutHandle::CalloutNextStep& callout_status) {
1826
1827 if (!expired->expired()) {
1828 isc_throw(BadValue, "Attempt to recycle lease that is still valid");
1829 }
1830
1831 if (expired->type_ != Lease::TYPE_PD) {
1832 prefix_len = 128; // non-PD lease types must be always /128
1833 }
1834
1835 if (!ctx.fake_allocation_) {
1836 // The expired lease needs to be reclaimed before it can be reused.
1837 // This includes declined leases for which probation period has
1838 // elapsed.
1839 reclaimExpiredLease(expired, ctx.callout_handle_);
1840 }
1841
1842 // address, lease type and prefixlen (0) stay the same
1843 expired->iaid_ = ctx.currentIA().iaid_;
1844 expired->duid_ = ctx.duid_;
1845 // Use subnet's preferred triplet to conditionally determine
1846 // preferred lifetime based on hint
1847 if (!ctx.currentIA().hints_.empty() &&
1848 ctx.currentIA().hints_[0].getPreferred()) {
1849 uint32_t preferred = ctx.currentIA().hints_[0].getPreferred();
1850 expired->preferred_lft_ = ctx.subnet_->getPreferred().get(preferred);
1851 } else {
1852 expired->preferred_lft_ = ctx.subnet_->getPreferred();
1853 }
1854 // Use subnet's valid triplet to conditionally determine
1855 // valid lifetime based on hint
1856 expired->reuseable_valid_lft_ = 0;
1857 if (!ctx.currentIA().hints_.empty() &&
1858 ctx.currentIA().hints_[0].getValid()) {
1859 uint32_t valid = ctx.currentIA().hints_[0].getValid();
1860 expired->valid_lft_ = ctx.subnet_->getValid().get(valid);
1861 } else {
1862 expired->valid_lft_ = ctx.subnet_->getValid();
1863 }
1864 expired->cltt_ = time(NULL);
1865 expired->subnet_id_ = ctx.subnet_->getID();
1866 expired->hostname_ = ctx.hostname_;
1867 expired->fqdn_fwd_ = ctx.fwd_dns_update_;
1868 expired->fqdn_rev_ = ctx.rev_dns_update_;
1869 expired->prefixlen_ = prefix_len;
1870 expired->state_ = Lease::STATE_DEFAULT;
1871
1874 .arg(ctx.query_->getLabel())
1875 .arg(expired->toText());
1876
1877 // Let's execute all callouts registered for lease6_select
1878 if (ctx.callout_handle_ &&
1879 HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1880
1881 // Use the RAII wrapper to make sure that the callout handle state is
1882 // reset when this object goes out of scope. All hook points must do
1883 // it to prevent possible circular dependency between the callout
1884 // handle and its arguments.
1885 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1886
1887 // Enable copying options from the packet within hook library.
1888 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
1889
1890 // Pass necessary arguments
1891
1892 // Pass the original packet
1893 ctx.callout_handle_->setArgument("query6", ctx.query_);
1894
1895 // Subnet from which we do the allocation
1896 ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
1897
1898 // Is this solicit (fake = true) or request (fake = false)
1899 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
1900
1901 // The lease that will be assigned to a client
1902 ctx.callout_handle_->setArgument("lease6", expired);
1903
1904 // Call the callouts
1905 HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
1906
1907 callout_status = ctx.callout_handle_->getStatus();
1908
1909 // Callouts decided to skip the action. This means that the lease is not
1910 // assigned, so the client will get NoAddrAvail as a result. The lease
1911 // won't be inserted into the database.
1912 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
1914 return (Lease6Ptr());
1915 }
1916
1921
1922 // Let's use whatever callout returned. Hopefully it is the same lease
1923 // we handed to it.
1924 ctx.callout_handle_->getArgument("lease6", expired);
1925 }
1926
1927 if (!ctx.fake_allocation_) {
1928 // Add(update) the extended information on the lease.
1929 static_cast<void>(updateLease6ExtendedInfo(expired, ctx));
1930
1931 // for REQUEST we do update the lease
1933
1934 // If the lease is in the current subnet we need to account
1935 // for the re-assignment of The lease.
1936 if (ctx.subnet_->inPool(ctx.currentIA().type_, expired->addr_)) {
1937 StatsMgr::instance().addValue(
1938 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1939 ctx.currentIA().type_ == Lease::TYPE_NA ?
1940 "assigned-nas" : "assigned-pds"),
1941 static_cast<int64_t>(1));
1942 StatsMgr::instance().addValue(
1943 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1944 ctx.currentIA().type_ == Lease::TYPE_NA ?
1945 "cumulative-assigned-nas" :
1946 "cumulative-assigned-pds"),
1947 static_cast<int64_t>(1));
1948 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1949 "cumulative-assigned-nas" :
1950 "cumulative-assigned-pds",
1951 static_cast<int64_t>(1));
1952 }
1953 }
1954
1955 // We do nothing for SOLICIT. We'll just update database when
1956 // the client gets back to us with REQUEST message.
1957
1958 // it's not really expired at this stage anymore - let's return it as
1959 // an updated lease
1960 return (expired);
1961}
1962
1963void
1964AllocEngine::getLifetimes6(ClientContext6& ctx, uint32_t& preferred, uint32_t& valid) {
1965 // If the triplets are specified in one of our classes use it.
1966 // We use the first one we find for each lifetime.
1967 Triplet<uint32_t> candidate_preferred;
1968 Triplet<uint32_t> candidate_valid;
1969 const ClientClasses classes = ctx.query_->getClasses();
1970 if (!classes.empty()) {
1971 // Let's get class definitions
1972 const ClientClassDictionaryPtr& dict =
1973 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
1974
1975 // Iterate over the assigned class defintions.
1976 int have_both = 0;
1977 for (auto name = classes.cbegin();
1978 name != classes.cend() && have_both < 2; ++name) {
1979 ClientClassDefPtr cl = dict->findClass(*name);
1980 if (candidate_preferred.unspecified() &&
1981 (cl && (!cl->getPreferred().unspecified()))) {
1982 candidate_preferred = cl->getPreferred();
1983 ++have_both;
1984 }
1985
1986 if (candidate_valid.unspecified() &&
1987 (cl && (!cl->getValid().unspecified()))) {
1988 candidate_valid = cl->getValid();
1989 ++have_both;
1990 }
1991 }
1992 }
1993
1994 // If no classes specified preferred lifetime, get it from the subnet.
1995 if (!candidate_preferred) {
1996 candidate_preferred = ctx.subnet_->getPreferred();
1997 }
1998
1999 // If no classes specified valid lifetime, get it from the subnet.
2000 if (!candidate_valid) {
2001 candidate_valid = ctx.subnet_->getValid();
2002 }
2003
2004 // Set the outbound parameters to the values we have so far.
2005 preferred = candidate_preferred;
2006 valid = candidate_valid;
2007
2008 // If client requested either value, use the requested value(s) bounded by
2009 // the candidate triplet(s).
2010 if (!ctx.currentIA().hints_.empty()) {
2011 if (ctx.currentIA().hints_[0].getPreferred()) {
2012 preferred = candidate_preferred.get(ctx.currentIA().hints_[0].getPreferred());
2013 }
2014
2015 if (ctx.currentIA().hints_[0].getValid()) {
2016 valid = candidate_valid.get(ctx.currentIA().hints_[0].getValid());
2017 }
2018 }
2019}
2020
2021Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
2022 const IOAddress& addr,
2023 uint8_t prefix_len,
2024 CalloutHandle::CalloutNextStep& callout_status) {
2025
2026 if (ctx.currentIA().type_ != Lease::TYPE_PD) {
2027 prefix_len = 128; // non-PD lease types must be always /128
2028 }
2029
2030 uint32_t preferred = 0;
2031 uint32_t valid = 0;
2032 getLifetimes6(ctx, preferred, valid);
2033
2034 Lease6Ptr lease(new Lease6(ctx.currentIA().type_, addr, ctx.duid_,
2035 ctx.currentIA().iaid_, preferred,
2036 valid, ctx.subnet_->getID(),
2037 ctx.hwaddr_, prefix_len));
2038
2039 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2040 lease->fqdn_rev_ = ctx.rev_dns_update_;
2041 lease->hostname_ = ctx.hostname_;
2042
2043 // Let's execute all callouts registered for lease6_select
2044 if (ctx.callout_handle_ &&
2045 HooksManager::calloutsPresent(hook_index_lease6_select_)) {
2046
2047 // Use the RAII wrapper to make sure that the callout handle state is
2048 // reset when this object goes out of scope. All hook points must do
2049 // it to prevent possible circular dependency between the callout
2050 // handle and its arguments.
2051 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
2052
2053 // Enable copying options from the packet within hook library.
2054 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2055
2056 // Pass necessary arguments
2057
2058 // Pass the original packet
2059 ctx.callout_handle_->setArgument("query6", ctx.query_);
2060
2061 // Subnet from which we do the allocation
2062 ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
2063
2064 // Is this solicit (fake = true) or request (fake = false)
2065 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
2066
2067 // The lease that will be assigned to a client
2068 ctx.callout_handle_->setArgument("lease6", lease);
2069
2070 // This is the first callout, so no need to clear any arguments
2071 HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
2072
2073 callout_status = ctx.callout_handle_->getStatus();
2074
2075 // Callouts decided to skip the action. This means that the lease is not
2076 // assigned, so the client will get NoAddrAvail as a result. The lease
2077 // won't be inserted into the database.
2078 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
2080 return (Lease6Ptr());
2081 }
2082
2083 // Let's use whatever callout returned. Hopefully it is the same lease
2084 // we handed to it.
2085 ctx.callout_handle_->getArgument("lease6", lease);
2086 }
2087
2088 if (!ctx.fake_allocation_) {
2089 // Add(update) the extended information on the lease.
2090 static_cast<void>(updateLease6ExtendedInfo(lease, ctx));
2091
2092 // That is a real (REQUEST) allocation
2093 bool status = LeaseMgrFactory::instance().addLease(lease);
2094
2095 if (status) {
2096 // The lease insertion succeeded - if the lease is in the
2097 // current subnet lets bump up the statistic.
2098 if (ctx.subnet_->inPool(ctx.currentIA().type_, addr)) {
2099 StatsMgr::instance().addValue(
2100 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2101 ctx.currentIA().type_ == Lease::TYPE_NA ?
2102 "assigned-nas" : "assigned-pds"),
2103 static_cast<int64_t>(1));
2104 StatsMgr::instance().addValue(
2105 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2106 ctx.currentIA().type_ == Lease::TYPE_NA ?
2107 "cumulative-assigned-nas" :
2108 "cumulative-assigned-pds"),
2109 static_cast<int64_t>(1));
2110 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2111 "cumulative-assigned-nas" :
2112 "cumulative-assigned-pds",
2113 static_cast<int64_t>(1));
2114 }
2115
2116 // Record it so it won't be updated twice.
2117 ctx.currentIA().addNewResource(addr, prefix_len);
2118
2119 return (lease);
2120 } else {
2121 // One of many failures with LeaseMgr (e.g. lost connection to the
2122 // database, database failed etc.). One notable case for that
2123 // is that we are working in multi-process mode and we lost a race
2124 // (some other process got that address first)
2125 return (Lease6Ptr());
2126 }
2127 } else {
2128 // That is only fake (SOLICIT without rapid-commit) allocation
2129
2130 // It is for advertise only. We should not insert the lease and callers
2131 // have already verified the lease does not exist in the database.
2132 return (lease);
2133 }
2134}
2135
2138 try {
2139 if (!ctx.subnet_) {
2140 isc_throw(InvalidOperation, "Subnet is required for allocation");
2141 }
2142
2143 if (!ctx.duid_) {
2144 isc_throw(InvalidOperation, "DUID is mandatory for allocation");
2145 }
2146
2147 // Check if there are any leases for this client.
2148 Subnet6Ptr subnet = ctx.subnet_;
2149 Lease6Collection leases;
2150 while (subnet) {
2151 Lease6Collection leases_subnet =
2153 *ctx.duid_,
2154 ctx.currentIA().iaid_,
2155 subnet->getID());
2156 leases.insert(leases.end(), leases_subnet.begin(), leases_subnet.end());
2157
2158 subnet = subnet->getNextSubnet(ctx.subnet_);
2159 }
2160
2161 if (!leases.empty()) {
2164 .arg(ctx.query_->getLabel());
2165
2166 // Check if the existing leases are reserved for someone else.
2167 // If they're not, we're ok to keep using them.
2168 removeNonmatchingReservedLeases6(ctx, leases);
2169 }
2170
2171 if (!ctx.hosts_.empty()) {
2172
2175 .arg(ctx.query_->getLabel());
2176
2177 // If we have host reservation, allocate those leases.
2178 allocateReservedLeases6(ctx, leases);
2179
2180 // There's one more check to do. Let's remove leases that are not
2181 // matching reservations, i.e. if client X has address A, but there's
2182 // a reservation for address B, we should release A and reassign B.
2183 // Caveat: do this only if we have at least one reserved address.
2184 removeNonreservedLeases6(ctx, leases);
2185 }
2186
2187 // If we happen to removed all leases, get something new for this guy.
2188 // Depending on the configuration, we may enable or disable granting
2189 // new leases during renewals. This is controlled with the
2190 // allow_new_leases_in_renewals_ field.
2191 if (leases.empty()) {
2192
2195 .arg(ctx.query_->getLabel());
2196
2197 leases = allocateUnreservedLeases6(ctx);
2198 }
2199
2200 // Extend all existing leases that passed all checks.
2201 for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
2202 if (ctx.currentIA().isNewResource((*l)->addr_,
2203 (*l)->prefixlen_)) {
2204 // This lease was just created so is already extended.
2205 continue;
2206 }
2209 .arg(ctx.query_->getLabel())
2210 .arg((*l)->typeToText((*l)->type_))
2211 .arg((*l)->addr_);
2212 extendLease6(ctx, *l);
2213 }
2214
2215 if (!leases.empty()) {
2216 // If there are any leases allocated, let's store in them in the
2217 // IA context so as they are available when we process subsequent
2218 // IAs.
2219 BOOST_FOREACH(Lease6Ptr lease, leases) {
2220 ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
2221 ctx.new_leases_.push_back(lease);
2222 }
2223 }
2224
2225 return (leases);
2226
2227 } catch (const isc::Exception& e) {
2228
2229 // Some other error, return an empty lease.
2231 .arg(ctx.query_->getLabel())
2232 .arg(e.what());
2233 }
2234
2235 return (Lease6Collection());
2236}
2237
2238void
2239AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
2240
2241 if (!lease || !ctx.subnet_) {
2242 return;
2243 }
2244
2245 // It is likely that the lease for which we're extending the lifetime doesn't
2246 // belong to the current but a sibling subnet.
2247 if (ctx.subnet_->getID() != lease->subnet_id_) {
2248 SharedNetwork6Ptr network;
2249 ctx.subnet_->getSharedNetwork(network);
2250 if (network) {
2251 Subnet6Ptr subnet = network->getSubnet(SubnetID(lease->subnet_id_));
2252 // Found the actual subnet this lease belongs to. Stick to this
2253 // subnet.
2254 if (subnet) {
2255 ctx.subnet_ = subnet;
2256 }
2257 }
2258 }
2259
2260 // If the lease is not global and it is either out of range (NAs only) or it
2261 // is not permitted by subnet client classification, delete it.
2262 if (!(ctx.hasGlobalReservation(makeIPv6Resrv(*lease))) &&
2263 (((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
2264 !ctx.subnet_->clientSupported(ctx.query_->getClasses()))) {
2265 // Oh dear, the lease is no longer valid. We need to get rid of it.
2266
2267 // Remove this lease from LeaseMgr
2268 if (!LeaseMgrFactory::instance().deleteLease(lease)) {
2269 // Concurrent delete performed by other instance which should
2270 // properly handle dns and stats updates.
2271 return;
2272 }
2273
2274 // Updated DNS if required.
2275 queueNCR(CHG_REMOVE, lease);
2276
2277 // Need to decrease statistic for assigned addresses.
2278 StatsMgr::instance().addValue(
2279 StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-nas"),
2280 static_cast<int64_t>(-1));
2281
2282 // Add it to the removed leases list.
2283 ctx.currentIA().old_leases_.push_back(lease);
2284
2285 return;
2286 }
2287
2290 .arg(ctx.query_->getLabel())
2291 .arg(lease->toText());
2292
2293 // Keep the old data in case the callout tells us to skip update.
2294 Lease6Ptr old_data(new Lease6(*lease));
2295
2296 bool changed = false;
2297 uint32_t current_preferred_lft = lease->preferred_lft_;
2298 if (!ctx.currentIA().hints_.empty() &&
2299 ctx.currentIA().hints_[0].getPreferred()) {
2300 uint32_t preferred = ctx.currentIA().hints_[0].getPreferred();
2301 lease->preferred_lft_ = ctx.subnet_->getPreferred().get(preferred);
2302 } else {
2303 lease->preferred_lft_ = ctx.subnet_->getPreferred();
2304 }
2305 if (lease->preferred_lft_ < current_preferred_lft) {
2306 changed = true;
2307 }
2308 lease->reuseable_valid_lft_ = 0;
2309 if (!ctx.currentIA().hints_.empty() &&
2310 ctx.currentIA().hints_[0].getValid()) {
2311 uint32_t valid = ctx.currentIA().hints_[0].getValid();
2312 lease->valid_lft_ = ctx.subnet_->getValid().get(valid);
2313 } else {
2314 lease->valid_lft_ = ctx.subnet_->getValid();
2315 }
2316 if (lease->valid_lft_ < lease->current_valid_lft_) {
2317 changed = true;
2318 }
2319
2320 lease->cltt_ = time(NULL);
2321 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2322 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
2323 (lease->hostname_ != ctx.hostname_)) {
2324 changed = true;
2325 lease->hostname_ = ctx.hostname_;
2326 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2327 lease->fqdn_rev_ = ctx.rev_dns_update_;
2328 }
2329 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
2330 (ctx.hwaddr_ &&
2331 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
2332 changed = true;
2333 lease->hwaddr_ = ctx.hwaddr_;
2334 }
2335 if (lease->state_ != Lease::STATE_DEFAULT) {
2336 changed = true;
2337 lease->state_ = Lease::STATE_DEFAULT;
2338 }
2341 .arg(ctx.query_->getLabel())
2342 .arg(lease->toText());
2343
2344 bool skip = false;
2345 // Get the callouts specific for the processed message and execute them.
2346 int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
2347 Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
2348 if (HooksManager::calloutsPresent(hook_point)) {
2349 CalloutHandlePtr callout_handle = ctx.callout_handle_;
2350
2351 // Use the RAII wrapper to make sure that the callout handle state is
2352 // reset when this object goes out of scope. All hook points must do
2353 // it to prevent possible circular dependency between the callout
2354 // handle and its arguments.
2355 ScopedCalloutHandleState callout_handle_state(callout_handle);
2356
2357 // Enable copying options from the packet within hook library.
2358 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2359
2360 // Pass the original packet
2361 callout_handle->setArgument("query6", ctx.query_);
2362
2363 // Pass the lease to be updated
2364 callout_handle->setArgument("lease6", lease);
2365
2366 // Pass the IA option to be sent in response
2367 if (lease->type_ == Lease::TYPE_NA) {
2368 callout_handle->setArgument("ia_na", ctx.currentIA().ia_rsp_);
2369 } else {
2370 callout_handle->setArgument("ia_pd", ctx.currentIA().ia_rsp_);
2371 }
2372
2373 // Call all installed callouts
2374 HooksManager::callCallouts(hook_point, *callout_handle);
2375
2376 // Callouts decided to skip the next processing step. The next
2377 // processing step would actually renew the lease, so skip at this
2378 // stage means "keep the old lease as it is".
2379 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2380 skip = true;
2383 .arg(ctx.query_->getName());
2384 }
2385
2387 }
2388
2389 if (!skip) {
2390 bool update_stats = false;
2391
2392 // If the lease we're renewing has expired, we need to reclaim this
2393 // lease before we can renew it.
2394 if (old_data->expired()) {
2395 reclaimExpiredLease(old_data, ctx.callout_handle_);
2396
2397 // If the lease is in the current subnet we need to account
2398 // for the re-assignment of the lease.
2399 if (ctx.subnet_->inPool(ctx.currentIA().type_, old_data->addr_)) {
2400 update_stats = true;
2401 }
2402 changed = true;
2403 }
2404
2405 // @todo should we call storeLease6ExtendedInfo() here ?
2406 if (updateLease6ExtendedInfo(lease, ctx)) {
2407 changed = true;
2408 }
2409
2410 // Try to reuse the lease.
2411 if (!changed) {
2412 setLeaseReusable(lease, current_preferred_lft, ctx);
2413 }
2414
2415
2416 // Now that the lease has been reclaimed, we can go ahead and update it
2417 // in the lease database.
2418 if (lease->reuseable_valid_lft_ == 0) {
2420 }
2421
2422 if (update_stats) {
2423 StatsMgr::instance().addValue(
2424 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2425 ctx.currentIA().type_ == Lease::TYPE_NA ?
2426 "assigned-nas" : "assigned-pds"),
2427 static_cast<int64_t>(1));
2428 StatsMgr::instance().addValue(
2429 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2430 ctx.currentIA().type_ == Lease::TYPE_NA ?
2431 "cumulative-assigned-nas" :
2432 "cumulative-assigned-pds"),
2433 static_cast<int64_t>(1));
2434 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2435 "cumulative-assigned-nas" :
2436 "cumulative-assigned-pds",
2437 static_cast<int64_t>(1));
2438 }
2439
2440 } else {
2441 // Copy back the original date to the lease. For MySQL it doesn't make
2442 // much sense, but for memfile, the Lease6Ptr points to the actual lease
2443 // in memfile, so the actual update is performed when we manipulate
2444 // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
2445 *lease = *old_data;
2446 }
2447
2448 // Add the old lease to the changed lease list. This allows the server
2449 // to make decisions regarding DNS updates.
2450 ctx.currentIA().changed_leases_.push_back(old_data);
2451}
2452
2454AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
2455 Lease6Collection updated_leases;
2456 for (Lease6Collection::const_iterator lease_it = leases.begin();
2457 lease_it != leases.end(); ++lease_it) {
2458 Lease6Ptr lease(new Lease6(**lease_it));
2459 if (ctx.currentIA().isNewResource(lease->addr_, lease->prefixlen_)) {
2460 // This lease was just created so is already up to date.
2461 updated_leases.push_back(lease);
2462 continue;
2463 }
2464
2465 lease->reuseable_valid_lft_ = 0;
2466 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2467 lease->fqdn_rev_ = ctx.rev_dns_update_;
2468 lease->hostname_ = ctx.hostname_;
2469 if (!ctx.fake_allocation_) {
2470 bool update_stats = false;
2471
2472 if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
2473 // Transition lease state to default (aka assigned)
2474 lease->state_ = Lease::STATE_DEFAULT;
2475
2476 // If the lease is in the current subnet we need to account
2477 // for the re-assignment of the lease.
2478 if (inAllowedPool(ctx, ctx.currentIA().type_,
2479 lease->addr_, true)) {
2480 update_stats = true;
2481 }
2482 }
2483
2484 bool fqdn_changed = ((lease->type_ != Lease::TYPE_PD) &&
2485 !(lease->hasIdenticalFqdn(**lease_it)));
2486
2487 lease->cltt_ = time(NULL);
2488 if (!fqdn_changed) {
2489 uint32_t current_preferred_lft = lease->preferred_lft_;
2490 setLeaseReusable(lease, current_preferred_lft, ctx);
2491 }
2492 if (lease->reuseable_valid_lft_ == 0) {
2493 ctx.currentIA().changed_leases_.push_back(*lease_it);
2495 }
2496
2497 if (update_stats) {
2498 StatsMgr::instance().addValue(
2499 StatsMgr::generateName("subnet", lease->subnet_id_,
2500 ctx.currentIA().type_ == Lease::TYPE_NA ?
2501 "assigned-nas" : "assigned-pds"),
2502 static_cast<int64_t>(1));
2503 StatsMgr::instance().addValue(
2504 StatsMgr::generateName("subnet", lease->subnet_id_,
2505 ctx.currentIA().type_ == Lease::TYPE_NA ?
2506 "cumulative-assigned-nas" :
2507 "cumulative-assigned-pds"),
2508 static_cast<int64_t>(1));
2509 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2510 "cumulative-assigned-nas" :
2511 "cumulative-assigned-pds",
2512 static_cast<int64_t>(1));
2513 }
2514 }
2515
2516 updated_leases.push_back(lease);
2517 }
2518
2519 return (updated_leases);
2520}
2521
2522void
2523AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout,
2524 const bool remove_lease,
2525 const uint16_t max_unwarned_cycles) {
2526
2529 .arg(max_leases)
2530 .arg(timeout);
2531
2532 // Create stopwatch and automatically start it to measure the time
2533 // taken by the routine.
2534 util::Stopwatch stopwatch;
2535
2536 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2537
2538 // This value indicates if we have been able to deal with all expired
2539 // leases in this pass.
2540 bool incomplete_reclamation = false;
2541 Lease6Collection leases;
2542 // The value of 0 has a special meaning - reclaim all.
2543 if (max_leases > 0) {
2544 // If the value is non-zero, the caller has limited the number of
2545 // leases to reclaim. We obtain one lease more to see if there will
2546 // be still leases left after this pass.
2547 lease_mgr.getExpiredLeases6(leases, max_leases + 1);
2548 // There are more leases expired leases than we will process in this
2549 // pass, so we should mark it as an incomplete reclamation. We also
2550 // remove this extra lease (which we don't want to process anyway)
2551 // from the collection.
2552 if (leases.size() > max_leases) {
2553 leases.pop_back();
2554 incomplete_reclamation = true;
2555 }
2556
2557 } else {
2558 // If there is no limitation on the number of leases to reclaim,
2559 // we will try to process all. Hence, we don't mark it as incomplete
2560 // reclamation just yet.
2561 lease_mgr.getExpiredLeases6(leases, max_leases);
2562 }
2563
2564 // Do not initialize the callout handle until we know if there are any
2565 // lease6_expire callouts installed.
2566 CalloutHandlePtr callout_handle;
2567 if (!leases.empty() &&
2568 HooksManager::calloutsPresent(Hooks.hook_index_lease6_expire_)) {
2569 callout_handle = HooksManager::createCalloutHandle();
2570 }
2571
2572 size_t leases_processed = 0;
2573 BOOST_FOREACH(Lease6Ptr lease, leases) {
2574
2575 try {
2576 // Reclaim the lease.
2577 if (MultiThreadingMgr::instance().getMode()) {
2578 // The reclamation is exclusive of packet processing.
2579 WriteLockGuard exclusive(rw_mutex_);
2580
2581 reclaimExpiredLease(lease, remove_lease, callout_handle);
2582 ++leases_processed;
2583 } else {
2584 reclaimExpiredLease(lease, remove_lease, callout_handle);
2585 ++leases_processed;
2586 }
2587
2588 } catch (const std::exception& ex) {
2590 .arg(lease->addr_.toText())
2591 .arg(ex.what());
2592 }
2593
2594 // Check if we have hit the timeout for running reclamation routine and
2595 // return if we have. We're checking it here, because we always want to
2596 // allow reclaiming at least one lease.
2597 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2598 // Timeout. This will likely mean that we haven't been able to process
2599 // all leases we wanted to process. The reclamation pass will be
2600 // probably marked as incomplete.
2601 if (!incomplete_reclamation) {
2602 if (leases_processed < leases.size()) {
2603 incomplete_reclamation = true;
2604 }
2605 }
2606
2609 .arg(timeout);
2610 break;
2611 }
2612 }
2613
2614 // Stop measuring the time.
2615 stopwatch.stop();
2616
2617 // Mark completion of the lease reclamation routine and present some stats.
2620 .arg(leases_processed)
2621 .arg(stopwatch.logFormatTotalDuration());
2622
2623 // Check if this was an incomplete reclamation and increase the number of
2624 // consecutive incomplete reclamations.
2625 if (incomplete_reclamation) {
2626 ++incomplete_v6_reclamations_;
2627 // If the number of incomplete reclamations is beyond the threshold, we
2628 // need to issue a warning.
2629 if ((max_unwarned_cycles > 0) &&
2630 (incomplete_v6_reclamations_ > max_unwarned_cycles)) {
2632 .arg(max_unwarned_cycles);
2633 // We issued a warning, so let's now reset the counter.
2634 incomplete_v6_reclamations_ = 0;
2635 }
2636
2637 } else {
2638 // This was a complete reclamation, so let's reset the counter.
2639 incomplete_v6_reclamations_ = 0;
2640
2643 }
2644}
2645
2646void
2650 .arg(secs);
2651
2652 uint64_t deleted_leases = 0;
2653 try {
2654 // Try to delete leases from the lease database.
2655 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2656 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
2657
2658 } catch (const std::exception& ex) {
2660 .arg(ex.what());
2661 }
2662
2665 .arg(deleted_leases);
2666}
2667
2668void
2669AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout,
2670 const bool remove_lease,
2671 const uint16_t max_unwarned_cycles) {
2672
2675 .arg(max_leases)
2676 .arg(timeout);
2677
2678 // Create stopwatch and automatically start it to measure the time
2679 // taken by the routine.
2680 util::Stopwatch stopwatch;
2681
2682 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2683
2684 // This value indicates if we have been able to deal with all expired
2685 // leases in this pass.
2686 bool incomplete_reclamation = false;
2687 Lease4Collection leases;
2688 // The value of 0 has a special meaning - reclaim all.
2689 if (max_leases > 0) {
2690 // If the value is non-zero, the caller has limited the number of
2691 // leases to reclaim. We obtain one lease more to see if there will
2692 // be still leases left after this pass.
2693 lease_mgr.getExpiredLeases4(leases, max_leases + 1);
2694 // There are more leases expired leases than we will process in this
2695 // pass, so we should mark it as an incomplete reclamation. We also
2696 // remove this extra lease (which we don't want to process anyway)
2697 // from the collection.
2698 if (leases.size() > max_leases) {
2699 leases.pop_back();
2700 incomplete_reclamation = true;
2701 }
2702
2703 } else {
2704 // If there is no limitation on the number of leases to reclaim,
2705 // we will try to process all. Hence, we don't mark it as incomplete
2706 // reclamation just yet.
2707 lease_mgr.getExpiredLeases4(leases, max_leases);
2708 }
2709
2710 // Do not initialize the callout handle until we know if there are any
2711 // lease4_expire callouts installed.
2712 CalloutHandlePtr callout_handle;
2713 if (!leases.empty() &&
2714 HooksManager::calloutsPresent(Hooks.hook_index_lease4_expire_)) {
2715 callout_handle = HooksManager::createCalloutHandle();
2716 }
2717
2718 size_t leases_processed = 0;
2719 BOOST_FOREACH(Lease4Ptr lease, leases) {
2720
2721 try {
2722 // Reclaim the lease.
2723 if (MultiThreadingMgr::instance().getMode()) {
2724 // The reclamation is exclusive of packet processing.
2725 WriteLockGuard exclusive(rw_mutex_);
2726
2727 reclaimExpiredLease(lease, remove_lease, callout_handle);
2728 ++leases_processed;
2729 } else {
2730 reclaimExpiredLease(lease, remove_lease, callout_handle);
2731 ++leases_processed;
2732 }
2733
2734 } catch (const std::exception& ex) {
2736 .arg(lease->addr_.toText())
2737 .arg(ex.what());
2738 }
2739
2740 // Check if we have hit the timeout for running reclamation routine and
2741 // return if we have. We're checking it here, because we always want to
2742 // allow reclaiming at least one lease.
2743 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2744 // Timeout. This will likely mean that we haven't been able to process
2745 // all leases we wanted to process. The reclamation pass will be
2746 // probably marked as incomplete.
2747 if (!incomplete_reclamation) {
2748 if (leases_processed < leases.size()) {
2749 incomplete_reclamation = true;
2750 }
2751 }
2752
2755 .arg(timeout);
2756 break;
2757 }
2758 }
2759
2760 // Stop measuring the time.
2761 stopwatch.stop();
2762
2763 // Mark completion of the lease reclamation routine and present some stats.
2766 .arg(leases_processed)
2767 .arg(stopwatch.logFormatTotalDuration());
2768
2769 // Check if this was an incomplete reclamation and increase the number of
2770 // consecutive incomplete reclamations.
2771 if (incomplete_reclamation) {
2772 ++incomplete_v4_reclamations_;
2773 // If the number of incomplete reclamations is beyond the threshold, we
2774 // need to issue a warning.
2775 if ((max_unwarned_cycles > 0) &&
2776 (incomplete_v4_reclamations_ > max_unwarned_cycles)) {
2778 .arg(max_unwarned_cycles);
2779 // We issued a warning, so let's now reset the counter.
2780 incomplete_v4_reclamations_ = 0;
2781 }
2782
2783 } else {
2784 // This was a complete reclamation, so let's reset the counter.
2785 incomplete_v4_reclamations_ = 0;
2786
2789 }
2790}
2791
2792template<typename LeasePtrType>
2793void
2794AllocEngine::reclaimExpiredLease(const LeasePtrType& lease, const bool remove_lease,
2795 const CalloutHandlePtr& callout_handle) {
2796 reclaimExpiredLease(lease, remove_lease ? DB_RECLAIM_REMOVE : DB_RECLAIM_UPDATE,
2797 callout_handle);
2798}
2799
2800template<typename LeasePtrType>
2801void
2802AllocEngine::reclaimExpiredLease(const LeasePtrType& lease,
2803 const CalloutHandlePtr& callout_handle) {
2804 // This variant of the method is used by the code which allocates or
2805 // renews leases. It may be the case that the lease has already been
2806 // reclaimed, so there is nothing to do.
2807 if (!lease->stateExpiredReclaimed()) {
2808 reclaimExpiredLease(lease, DB_RECLAIM_LEAVE_UNCHANGED, callout_handle);
2809 }
2810}
2811
2812void
2813AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
2814 const DbReclaimMode& reclaim_mode,
2815 const CalloutHandlePtr& callout_handle) {
2816
2819 .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
2820 .arg(lease->addr_.toText())
2821 .arg(static_cast<int>(lease->prefixlen_));
2822
2823 // The skip flag indicates if the callouts have taken responsibility
2824 // for reclaiming the lease. The callout will set this to true if
2825 // it reclaims the lease itself. In this case the reclamation routine
2826 // will not update DNS nor update the database.
2827 bool skipped = false;
2828 if (callout_handle) {
2829
2830 // Use the RAII wrapper to make sure that the callout handle state is
2831 // reset when this object goes out of scope. All hook points must do
2832 // it to prevent possible circular dependency between the callout
2833 // handle and its arguments.
2834 ScopedCalloutHandleState callout_handle_state(callout_handle);
2835
2836 callout_handle->deleteAllArguments();
2837 callout_handle->setArgument("lease6", lease);
2838 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2839
2840 HooksManager::callCallouts(Hooks.hook_index_lease6_expire_,
2841 *callout_handle);
2842
2843 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2844 }
2845
2848
2849 if (!skipped) {
2850
2851 // Generate removal name change request for D2, if required.
2852 // This will return immediately if the DNS wasn't updated
2853 // when the lease was created.
2854 queueNCR(CHG_REMOVE, lease);
2855
2856 // Let's check if the lease that just expired is in DECLINED state.
2857 // If it is, we need to perform a couple extra steps.
2858 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2859 if (lease->state_ == Lease::STATE_DECLINED) {
2860 // Do extra steps required for declined lease reclamation:
2861 // - call the recover hook
2862 // - bump decline-related stats
2863 // - log separate message
2864 // There's no point in keeping a declined lease after its
2865 // reclamation. A declined lease doesn't have any client
2866 // identifying information anymore. So we'll flag it for
2867 // removal unless the hook has set the skip flag.
2868 remove_lease = reclaimDeclined(lease);
2869 }
2870
2871 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2872 // Reclaim the lease - depending on the configuration, set the
2873 // expired-reclaimed state or simply remove it.
2874 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2875 reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
2876 std::bind(&LeaseMgr::updateLease6,
2877 &lease_mgr, ph::_1));
2878 }
2879 }
2880
2881 // Update statistics.
2882
2883 // Decrease number of assigned leases.
2884 if (lease->type_ == Lease::TYPE_NA) {
2885 // IA_NA
2886 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2887 lease->subnet_id_,
2888 "assigned-nas"),
2889 int64_t(-1));
2890
2891 } else if (lease->type_ == Lease::TYPE_PD) {
2892 // IA_PD
2893 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2894 lease->subnet_id_,
2895 "assigned-pds"),
2896 int64_t(-1));
2897
2898 }
2899
2900 // Increase total number of reclaimed leases.
2901 StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
2902
2903 // Increase number of reclaimed leases for a subnet.
2904 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2905 lease->subnet_id_,
2906 "reclaimed-leases"),
2907 int64_t(1));
2908}
2909
2910void
2911AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
2912 const DbReclaimMode& reclaim_mode,
2913 const CalloutHandlePtr& callout_handle) {
2914
2917 .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
2918 .arg(lease->addr_.toText());
2919
2920 // The skip flag indicates if the callouts have taken responsibility
2921 // for reclaiming the lease. The callout will set this to true if
2922 // it reclaims the lease itself. In this case the reclamation routine
2923 // will not update DNS nor update the database.
2924 bool skipped = false;
2925 if (callout_handle) {
2926
2927 // Use the RAII wrapper to make sure that the callout handle state is
2928 // reset when this object goes out of scope. All hook points must do
2929 // it to prevent possible circular dependency between the callout
2930 // handle and its arguments.
2931 ScopedCalloutHandleState callout_handle_state(callout_handle);
2932
2933 callout_handle->setArgument("lease4", lease);
2934 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2935
2936 HooksManager::callCallouts(Hooks.hook_index_lease4_expire_,
2937 *callout_handle);
2938
2939 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2940 }
2941
2944
2945 if (!skipped) {
2946
2947 // Generate removal name change request for D2, if required.
2948 // This will return immediately if the DNS wasn't updated
2949 // when the lease was created.
2950 queueNCR(CHG_REMOVE, lease);
2951 // Clear DNS fields so we avoid redundant removes.
2952 lease->hostname_.clear();
2953 lease->fqdn_fwd_ = false;
2954 lease->fqdn_rev_ = false;
2955
2956 // Let's check if the lease that just expired is in DECLINED state.
2957 // If it is, we need to perform a couple extra steps.
2958 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2959 if (lease->state_ == Lease::STATE_DECLINED) {
2960 // Do extra steps required for declined lease reclamation:
2961 // - call the recover hook
2962 // - bump decline-related stats
2963 // - log separate message
2964 // There's no point in keeping a declined lease after its
2965 // reclamation. A declined lease doesn't have any client
2966 // identifying information anymore. So we'll flag it for
2967 // removal unless the hook has set the skip flag.
2968 remove_lease = reclaimDeclined(lease);
2969 }
2970
2971 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2972 // Reclaim the lease - depending on the configuration, set the
2973 // expired-reclaimed state or simply remove it.
2974 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2975 reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
2976 std::bind(&LeaseMgr::updateLease4,
2977 &lease_mgr, ph::_1));
2978 }
2979 }
2980
2981 // Update statistics.
2982
2983 // Decrease number of assigned addresses.
2984 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2985 lease->subnet_id_,
2986 "assigned-addresses"),
2987 int64_t(-1));
2988
2989 // Increase total number of reclaimed leases.
2990 StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
2991
2992 // Increase number of reclaimed leases for a subnet.
2993 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2994 lease->subnet_id_,
2995 "reclaimed-leases"),
2996 int64_t(1));
2997}
2998
2999void
3003 .arg(secs);
3004
3005 uint64_t deleted_leases = 0;
3006 try {
3007 // Try to delete leases from the lease database.
3008 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3009 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
3010
3011 } catch (const std::exception& ex) {
3013 .arg(ex.what());
3014 }
3015
3018 .arg(deleted_leases);
3019}
3020
3021bool
3022AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
3023 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3024 return (true);
3025 }
3026
3027 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_recover_)) {
3028 CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
3029
3030 // Use the RAII wrapper to make sure that the callout handle state is
3031 // reset when this object goes out of scope. All hook points must do
3032 // it to prevent possible circular dependency between the callout
3033 // handle and its arguments.
3034 ScopedCalloutHandleState callout_handle_state(callout_handle);
3035
3036 // Pass necessary arguments
3037 callout_handle->setArgument("lease4", lease);
3038
3039 // Call the callouts
3040 HooksManager::callCallouts(Hooks.hook_index_lease4_recover_, *callout_handle);
3041
3042 // Callouts decided to skip the action. This means that the lease is not
3043 // assigned, so the client will get NoAddrAvail as a result. The lease
3044 // won't be inserted into the database.
3045 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3047 .arg(lease->addr_.toText());
3048 return (false);
3049 }
3050 }
3051
3053 .arg(lease->addr_.toText())
3054 .arg(lease->valid_lft_);
3055
3056 StatsMgr& stats_mgr = StatsMgr::instance();
3057
3058 // Decrease subnet specific counter for currently declined addresses
3059 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3060 "declined-addresses"), static_cast<int64_t>(-1));
3061
3062 // Decrease global counter for declined addresses
3063 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3064
3065 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3066
3067 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3068 "reclaimed-declined-addresses"), static_cast<int64_t>(1));
3069
3070 // Note that we do not touch assigned-addresses counters. Those are
3071 // modified in whatever code calls this method.
3072 return (true);
3073}
3074
3075bool
3076AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
3077 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3078 return (true);
3079 }
3080
3081 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_recover_)) {
3082 CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
3083
3084 // Use the RAII wrapper to make sure that the callout handle state is
3085 // reset when this object goes out of scope. All hook points must do
3086 // it to prevent possible circular dependency between the callout
3087 // handle and its arguments.
3088 ScopedCalloutHandleState callout_handle_state(callout_handle);
3089
3090 // Pass necessary arguments
3091 callout_handle->setArgument("lease6", lease);
3092
3093 // Call the callouts
3094 HooksManager::callCallouts(Hooks.hook_index_lease6_recover_, *callout_handle);
3095
3096 // Callouts decided to skip the action. This means that the lease is not
3097 // assigned, so the client will get NoAddrAvail as a result. The lease
3098 // won't be inserted into the database.
3099 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3101 .arg(lease->addr_.toText());
3102 return (false);
3103 }
3104 }
3105
3107 .arg(lease->addr_.toText())
3108 .arg(lease->valid_lft_);
3109
3110 StatsMgr& stats_mgr = StatsMgr::instance();
3111
3112 // Decrease subnet specific counter for currently declined addresses
3113 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3114 "declined-addresses"), static_cast<int64_t>(-1));
3115
3116 // Decrease global counter for declined addresses
3117 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3118
3119 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3120
3121 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3122 "reclaimed-declined-addresses"), static_cast<int64_t>(1));
3123
3124 // Note that we do not touch assigned-nas counters. Those are
3125 // modified in whatever code calls this method.
3126
3127 return (true);
3128}
3129
3130template<typename LeasePtrType>
3131void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
3132 const bool remove_lease,
3133 const std::function<void (const LeasePtrType&)>&
3134 lease_update_fun) const {
3135
3136 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3137
3138 // Reclaim the lease - depending on the configuration, set the
3139 // expired-reclaimed state or simply remove it.
3140 if (remove_lease) {
3141 lease_mgr.deleteLease(lease);
3142 } else if (lease_update_fun) {
3143 // Clear FQDN information as we have already sent the
3144 // name change request to remove the DNS record.
3145 lease->reuseable_valid_lft_ = 0;
3146 lease->hostname_.clear();
3147 lease->fqdn_fwd_ = false;
3148 lease->fqdn_rev_ = false;
3149 lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
3150 lease_update_fun(lease);
3151
3152 } else {
3153 return;
3154 }
3155
3156 // Lease has been reclaimed.
3159 .arg(lease->addr_.toText());
3160}
3161
3162} // namespace dhcp
3163} // namespace isc
3164
3165// ##########################################################################
3166// # DHCPv4 lease allocation code starts here.
3167// ##########################################################################
3168
3169namespace {
3170
3188bool
3189addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
3190 // When out-of-pool flag is true the server may assume that all host
3191 // reservations are for addresses that do not belong to the dynamic pool.
3192 // Therefore, it can skip the reservation checks when dealing with in-pool
3193 // addresses.
3194 if (ctx.subnet_ && ctx.subnet_->getReservationsInSubnet() &&
3195 (!ctx.subnet_->getReservationsOutOfPool() ||
3196 !ctx.subnet_->inPool(Lease::TYPE_V4, address))) {
3197 // The global parameter ip-reservations-unique controls whether it is allowed
3198 // to specify multiple reservations for the same IP address or delegated prefix
3199 // or IP reservations must be unique. Some host backends do not support the
3200 // former, thus we can't always use getAll4 calls to get the reservations
3201 // for the given IP. When we're in the default mode, when IP reservations
3202 // are unique, we should call get4 (supported by all backends). If we're in
3203 // the mode in which non-unique reservations are allowed the backends which
3204 // don't support it are not used and we can safely call getAll4.
3205 ConstHostCollection hosts;
3206 if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
3207 // Reservations are unique. It is safe to call get4 to get the unique host.
3208 ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
3209 if (host) {
3210 hosts.push_back(host);
3211 }
3212 } else {
3213 // Reservations can be non-unique. Need to get all reservations for that address.
3214 hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
3215 }
3216
3217 for (auto host : hosts) {
3218 for (const AllocEngine::IdentifierPair& id_pair : ctx.host_identifiers_) {
3219 // If we find the matching host we know that this address is reserved
3220 // for us and we can return immediately.
3221 if (id_pair.first == host->getIdentifierType() &&
3222 id_pair.second == host->getIdentifier()) {
3223 return (false);
3224 }
3225 }
3226 }
3227 // We didn't find a matching host. If there are any reservations it means that
3228 // address is reserved for another client or multiple clients. If there are
3229 // no reservations address is not reserved for another client.
3230 return (!hosts.empty());
3231 }
3232 return (false);
3233}
3234
3250bool
3251hasAddressReservation(AllocEngine::ClientContext4& ctx) {
3252 if (ctx.hosts_.empty()) {
3253 return (false);
3254 }
3255
3256 // Flag used to perform search for global reservations only once.
3257 bool search_global_done = false;
3258
3259 Subnet4Ptr subnet = ctx.subnet_;
3260 while (subnet) {
3261 // Skip search if the global reservations have already been examined.
3262 if (!search_global_done && subnet->getReservationsGlobal()) {
3263 auto host = ctx.hosts_.find(SUBNET_ID_GLOBAL);
3264 // if we want global + other modes we would need to
3265 // return only if true, else continue
3266 if (host != ctx.hosts_.end() && host->second &&
3267 !host->second->getIPv4Reservation().isV4Zero()) {
3268 return (true);
3269 }
3270 // No need to perform this search again as there are no global
3271 // reservations.
3272 search_global_done = true;
3273 }
3274
3275 if (subnet->getReservationsInSubnet()) {
3276 auto host = ctx.hosts_.find(subnet->getID());
3277 // The out-of-pool flag indicates that no client should be assigned
3278 // reserved addresses from within the dynamic pool, and for that
3279 // reason look only for reservations that are outside the pools,
3280 // hence the inPool check.
3281 if (host != ctx.hosts_.end() && host->second) {
3282 auto reservation = host->second->getIPv4Reservation();
3283 if (!reservation.isV4Zero() &&
3284 (!subnet->getReservationsOutOfPool() ||
3285 !subnet->inPool(Lease::TYPE_V4, reservation))) {
3286 ctx.subnet_ = subnet;
3287 return (true);
3288 }
3289 }
3290 }
3291
3292 // No address reservation found here, so let's try another subnet
3293 // within the same shared network.
3294 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3295 }
3296
3297 return (false);
3298}
3299
3315void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
3316 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3317
3318 Subnet4Ptr original_subnet = ctx.subnet_;
3319
3320 // Client identifier is optional. First check if we can try to lookup
3321 // by client-id.
3322 bool try_clientid_lookup = (ctx.clientid_ &&
3324 ctx.query_->getClasses()));
3325
3326 // If it is possible to use client identifier to try to find client's lease.
3327 if (try_clientid_lookup) {
3328 // Get all leases for this client identifier. When shared networks are
3329 // in use it is more efficient to make a single query rather than
3330 // multiple queries, one for each subnet.
3331 Lease4Collection leases_client_id = lease_mgr.getLease4(*ctx.clientid_);
3332
3333 // Iterate over the subnets within the shared network to see if any client's
3334 // lease belongs to them.
3335 for (Subnet4Ptr subnet = original_subnet; subnet;
3336 subnet = subnet->getNextSubnet(original_subnet,
3337 ctx.query_->getClasses())) {
3338
3339 // If client identifier has been supplied and the server wasn't
3340 // explicitly configured to ignore client identifiers for this subnet
3341 // check if there is a lease within this subnet.
3342 if (subnet->getMatchClientId()) {
3343 for (auto l = leases_client_id.begin(); l != leases_client_id.end(); ++l) {
3344 if ((*l)->subnet_id_ == subnet->getID()) {
3345 // Lease found, so stick to this lease.
3346 client_lease = (*l);
3347 ctx.subnet_ = subnet;
3348 return;
3349 }
3350 }
3351 }
3352 }
3353 }
3354
3355 // If no lease found using the client identifier, try the lookup using
3356 // the HW address.
3357 if (!client_lease && ctx.hwaddr_) {
3358
3359 // Get all leases for this HW address.
3360 Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
3361
3362 for (Subnet4Ptr subnet = original_subnet; subnet;
3363 subnet = subnet->getNextSubnet(original_subnet,
3364 ctx.query_->getClasses())) {
3365 ClientIdPtr client_id;
3366 if (subnet->getMatchClientId()) {
3367 client_id = ctx.clientid_;
3368 }
3369
3370 // Try to find the lease that matches current subnet and belongs to
3371 // this client, so both HW address and client identifier match.
3372 for (Lease4Collection::const_iterator client_lease_it = leases_hw_address.begin();
3373 client_lease_it != leases_hw_address.end(); ++client_lease_it) {
3374 Lease4Ptr existing_lease = *client_lease_it;
3375 if ((existing_lease->subnet_id_ == subnet->getID()) &&
3376 existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
3377 // Found the lease of this client, so return it.
3378 client_lease = existing_lease;
3379 // We got a lease but the subnet it belongs to may differ from
3380 // the original subnet. Let's now stick to this subnet.
3381 ctx.subnet_ = subnet;
3382 return;
3383 }
3384 }
3385 }
3386 }
3387}
3388
3401bool
3402inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
3403 // If the subnet belongs to a shared network we will be iterating
3404 // over the subnets that belong to this shared network.
3405 Subnet4Ptr current_subnet = ctx.subnet_;
3406 while (current_subnet) {
3407
3408 if (current_subnet->inPool(Lease::TYPE_V4, address,
3409 ctx.query_->getClasses())) {
3410 // We found a subnet that this address belongs to, so it
3411 // seems that this subnet is the good candidate for allocation.
3412 // Let's update the selected subnet.
3413 ctx.subnet_ = current_subnet;
3414 return (true);
3415 }
3416
3417 current_subnet = current_subnet->getNextSubnet(ctx.subnet_,
3418 ctx.query_->getClasses());
3419 }
3420
3421 return (false);
3422}
3423
3424} // namespace
3425
3426namespace isc {
3427namespace dhcp {
3428
3430 : early_global_reservations_lookup_(false),
3431 subnet_(), clientid_(), hwaddr_(),
3432 requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
3433 fwd_dns_update_(false), rev_dns_update_(false),
3434 hostname_(""), callout_handle_(), fake_allocation_(false),
3435 old_lease_(), new_lease_(), hosts_(), conflicting_lease_(),
3436 query_(), host_identifiers_(), unknown_requested_addr_(false),
3437 ddns_params_() {
3438}
3439
3441 const ClientIdPtr& clientid,
3442 const HWAddrPtr& hwaddr,
3443 const asiolink::IOAddress& requested_addr,
3444 const bool fwd_dns_update,
3445 const bool rev_dns_update,
3446 const std::string& hostname,
3447 const bool fake_allocation)
3448 : early_global_reservations_lookup_(false),
3449 subnet_(subnet), clientid_(clientid), hwaddr_(hwaddr),
3450 requested_address_(requested_addr),
3451 fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
3452 hostname_(hostname), callout_handle_(),
3453 fake_allocation_(fake_allocation), old_lease_(), new_lease_(),
3454 hosts_(), host_identifiers_(), unknown_requested_addr_(false),
3455 ddns_params_(new DdnsParams()) {
3456
3457 // Initialize host identifiers.
3458 if (hwaddr) {
3459 addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
3460 }
3461}
3462
3465 if (subnet_ && subnet_->getReservationsInSubnet()) {
3466 auto host = hosts_.find(subnet_->getID());
3467 if (host != hosts_.cend()) {
3468 return (host->second);
3469 }
3470 }
3471
3472 return (globalHost());
3473}
3474
3477 if (subnet_ && subnet_->getReservationsGlobal()) {
3478 auto host = hosts_.find(SUBNET_ID_GLOBAL);
3479 if (host != hosts_.cend()) {
3480 return (host->second);
3481 }
3482 }
3483
3484 return (ConstHostPtr());
3485}
3486
3489 // We already have it return it unless the context subnet has changed.
3490 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
3491 return (ddns_params_);
3492 }
3493
3494 // Doesn't exist yet or is stale, (re)create it.
3495 if (subnet_) {
3496 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
3497 return (ddns_params_);
3498 }
3499
3500 // Asked for it without a subnet? This case really shouldn't occur but
3501 // for now let's return an instance with default values.
3502 return (DdnsParamsPtr(new DdnsParams()));
3503}
3504
3507 // The NULL pointer indicates that the old lease didn't exist. It may
3508 // be later set to non NULL value if existing lease is found in the
3509 // database.
3510 ctx.old_lease_.reset();
3511 ctx.new_lease_.reset();
3512
3513 // Before we start allocation process, we need to make sure that the
3514 // selected subnet is allowed for this client. If not, we'll try to
3515 // use some other subnet within the shared network. If there are no
3516 // subnets allowed for this client within the shared network, we
3517 // can't allocate a lease.
3518 Subnet4Ptr subnet = ctx.subnet_;
3519 if (subnet && !subnet->clientSupported(ctx.query_->getClasses())) {
3520 ctx.subnet_ = subnet->getNextSubnet(subnet, ctx.query_->getClasses());
3521 }
3522
3523 try {
3524 if (!ctx.subnet_) {
3525 isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
3526 }
3527
3528 if (!ctx.hwaddr_) {
3529 isc_throw(BadValue, "HWAddr must be defined");
3530 }
3531
3532 if (ctx.fake_allocation_) {
3533 return (discoverLease4(ctx));
3534
3535 } else {
3536 ctx.new_lease_ = requestLease4(ctx);
3537 }
3538
3539 } catch (const isc::Exception& e) {
3540 // Some other error, return an empty lease.
3542 .arg(ctx.query_->getLabel())
3543 .arg(e.what());
3544 }
3545
3546 return (ctx.new_lease_);
3547}
3548
3549void
3551 // If there is no subnet, there is nothing to do.
3552 if (!ctx.subnet_) {
3553 return;
3554 }
3555
3556 auto subnet = ctx.subnet_;
3557
3558 // If already done just return.
3560 !subnet->getReservationsInSubnet()) {
3561 return;
3562 }
3563
3564 // @todo: This code can be trivially optimized.
3566 subnet->getReservationsGlobal()) {
3568 if (ghost) {
3569 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
3570
3571 // If we had only to fetch global reservations it is done.
3572 if (!subnet->getReservationsInSubnet()) {
3573 return;
3574 }
3575 }
3576 }
3577
3578 std::map<SubnetID, ConstHostPtr> host_map;
3579 SharedNetwork4Ptr network;
3580 subnet->getSharedNetwork(network);
3581
3582 // If the subnet belongs to a shared network it is usually going to be
3583 // more efficient to make a query for all reservations for a particular
3584 // client rather than a query for each subnet within this shared network.
3585 // The only case when it is going to be less efficient is when there are
3586 // more host identifier types in use than subnets within a shared network.
3587 // As it breaks RADIUS use of host caching this can be disabled by the
3588 // host manager.
3589 const bool use_single_query = network &&
3591 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
3592
3593 if (use_single_query) {
3594 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3595 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
3596 &id_pair.second[0],
3597 id_pair.second.size());
3598 // Store the hosts in the temporary map, because some hosts may
3599 // belong to subnets outside of the shared network. We'll need
3600 // to eliminate them.
3601 for (auto host = hosts.begin(); host != hosts.end(); ++host) {
3602 if ((*host)->getIPv4SubnetID() != SUBNET_ID_GLOBAL) {
3603 host_map[(*host)->getIPv4SubnetID()] = *host;
3604 }
3605 }
3606 }
3607 }
3608
3609 // We can only search for the reservation if a subnet has been selected.
3610 while (subnet) {
3611
3612 // Only makes sense to get reservations if the client has access
3613 // to the class and host reservations are enabled for this subnet.
3614 if (subnet->clientSupported(ctx.query_->getClasses()) &&
3615 subnet->getReservationsInSubnet()) {
3616 // Iterate over configured identifiers in the order of preference
3617 // and try to use each of them to search for the reservations.
3618 if (use_single_query) {
3619 if (host_map.count(subnet->getID()) > 0) {
3620 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
3621 }
3622 } else {
3623 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3624 // Attempt to find a host using a specified identifier.
3625 ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
3626 id_pair.first,
3627 &id_pair.second[0],
3628 id_pair.second.size());
3629 // If we found matching host for this subnet.
3630 if (host) {
3631 ctx.hosts_[subnet->getID()] = host;
3632 break;
3633 }
3634 }
3635 }
3636 }
3637
3638 // We need to get to the next subnet if this is a shared network. If it
3639 // is not (a plain subnet), getNextSubnet will return NULL and we're
3640 // done here.
3641 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3642 }
3643}
3644
3647 ConstHostPtr host;
3648 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3649 // Attempt to find a host using a specified identifier.
3650 host = HostMgr::instance().get4(SUBNET_ID_GLOBAL, id_pair.first,
3651 &id_pair.second[0], id_pair.second.size());
3652
3653 // If we found matching global host we're done.
3654 if (host) {
3655 break;
3656 }
3657 }
3658
3659 return (host);
3660}
3661
3663AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
3664 // Find an existing lease for this client. This function will return null
3665 // if there is a conflict with existing lease and the allocation should
3666 // not be continued.
3667 Lease4Ptr client_lease;
3668 findClientLease(ctx, client_lease);
3669
3670 // new_lease will hold the pointer to the lease that we will offer to the
3671 // caller.
3672 Lease4Ptr new_lease;
3673
3674 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
3675
3676 // Check if there is a reservation for the client. If there is, we want to
3677 // assign the reserved address, rather than any other one.
3678 if (hasAddressReservation(ctx)) {
3679
3682 .arg(ctx.query_->getLabel())
3683 .arg(ctx.currentHost()->getIPv4Reservation().toText());
3684
3685 // If the client doesn't have a lease or the leased address is different
3686 // than the reserved one then let's try to allocate the reserved address.
3687 // Otherwise the address that the client has is the one for which it
3688 // has a reservation, so just renew it.
3689 if (!client_lease || (client_lease->addr_ != ctx.currentHost()->getIPv4Reservation())) {
3690 // The call below will return a pointer to the lease for the address
3691 // reserved to this client, if the lease is available, i.e. is not
3692 // currently assigned to any other client.
3693 // Note that we don't remove the existing client's lease at this point
3694 // because this is not a real allocation, we just offer what we can
3695 // allocate in the DHCPREQUEST time.
3696 new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
3697 callout_status);
3698 if (!new_lease) {
3700 .arg(ctx.query_->getLabel())
3701 .arg(ctx.currentHost()->getIPv4Reservation().toText())
3702 .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
3703 "(no lease info)");
3704 StatsMgr::instance().addValue(StatsMgr::generateName(
3705 "subnet",
3706 ctx.conflicting_lease_->subnet_id_,
3707 "v4-reservation-conflicts"),
3708 static_cast<int64_t>(1));
3709 StatsMgr::instance().addValue("v4-reservation-conflicts",
3710 static_cast<int64_t>(1));
3711 }
3712
3713 } else {
3714 new_lease = renewLease4(client_lease, ctx);
3715 }
3716 }
3717
3718 // Client does not have a reservation or the allocation of the reserved
3719 // address has failed, probably because the reserved address is in use
3720 // by another client. If the client has a lease, we will check if we can
3721 // offer this lease to the client. The lease can't be offered in the
3722 // situation when it is reserved for another client or when the address
3723 // is not in the dynamic pool. The former may be the result of adding the
3724 // new reservation for the address used by this client. The latter may
3725 // be due to the client using the reserved out-of-the pool address, for
3726 // which the reservation has just been removed.
3727 if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
3728 !addressReserved(client_lease->addr_, ctx)) {
3729
3732 .arg(ctx.query_->getLabel());
3733
3734 new_lease = renewLease4(client_lease, ctx);
3735 }
3736
3737 // The client doesn't have any lease or the lease can't be offered
3738 // because it is either reserved for some other client or the
3739 // address is not in the dynamic pool.
3740 // Let's use the client's hint (requested IP address), if the client
3741 // has provided it, and try to offer it. This address must not be
3742 // reserved for another client, and must be in the range of the
3743 // dynamic pool.
3744 if (!new_lease && !ctx.requested_address_.isV4Zero() &&
3745 inAllowedPool(ctx, ctx.requested_address_) &&
3746 !addressReserved(ctx.requested_address_, ctx)) {
3747
3750 .arg(ctx.requested_address_.toText())
3751 .arg(ctx.query_->getLabel());
3752
3753 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3754 callout_status);
3755 }
3756
3757 // The allocation engine failed to allocate all of the candidate
3758 // addresses. We will now use the allocator to pick the address
3759 // from the dynamic pool.
3760 if (!new_lease) {
3761
3764 .arg(ctx.query_->getLabel());
3765
3766 new_lease = allocateUnreservedLease4(ctx);
3767 }
3768
3769 // Some of the methods like reuseExpiredLease4 may set the old lease to point
3770 // to the lease which they remove/override. If it is not set, but we have
3771 // found that the client has the lease the client's lease is the one
3772 // to return as an old lease.
3773 if (!ctx.old_lease_ && client_lease) {
3774 ctx.old_lease_ = client_lease;
3775 }
3776
3777 return (new_lease);
3778}
3779
3781AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
3782 // Find an existing lease for this client. This function will return null
3783 // if there is a conflict with existing lease and the allocation should
3784 // not be continued.
3785 Lease4Ptr client_lease;
3786 findClientLease(ctx, client_lease);
3787
3788 // When the client sends the DHCPREQUEST, it should always specify the
3789 // address which it is requesting or renewing. That is, the client should
3790 // either use the requested IP address option or set the ciaddr. However,
3791 // we try to be liberal and allow the clients to not specify an address
3792 // in which case the allocation engine will pick a suitable address
3793 // for the client.
3794 if (!ctx.requested_address_.isV4Zero()) {
3795 // If the client has specified an address, make sure this address
3796 // is not reserved for another client. If it is, stop here because
3797 // we can't allocate this address.
3798 if (addressReserved(ctx.requested_address_, ctx)) {
3799
3802 .arg(ctx.query_->getLabel())
3803 .arg(ctx.requested_address_.toText());
3804
3805 return (Lease4Ptr());
3806 }
3807
3808 } else if (hasAddressReservation(ctx)) {
3809 // The client hasn't specified an address to allocate, so the
3810 // allocation engine needs to find an appropriate address.
3811 // If there is a reservation for the client, let's try to
3812 // allocate the reserved address.
3813 ctx.requested_address_ = ctx.currentHost()->getIPv4Reservation();
3814
3817 .arg(ctx.query_->getLabel())
3818 .arg(ctx.requested_address_.toText());
3819 }
3820
3821 if (!ctx.requested_address_.isV4Zero()) {
3822 // There is a specific address to be allocated. Let's find out if
3823 // the address is in use.
3825 // If the address is in use (allocated and not expired), we check
3826 // if the address is in use by our client or another client.
3827 // If it is in use by another client, the address can't be
3828 // allocated.
3829 if (existing && !existing->expired() &&
3830 !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
3831 ctx.clientid_ : ClientIdPtr())) {
3832
3835 .arg(ctx.query_->getLabel())
3836 .arg(ctx.requested_address_.toText());
3837
3838 return (Lease4Ptr());
3839 }
3840
3841 // If the client has a reservation but it is requesting a different
3842 // address it is possible that the client was offered this different
3843 // address because the reserved address is in use. We will have to
3844 // check if the address is in use.
3845 if (hasAddressReservation(ctx) &&
3846 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) {
3847 existing =
3848 LeaseMgrFactory::instance().getLease4(ctx.currentHost()->getIPv4Reservation());
3849 // If the reserved address is not in use, i.e. the lease doesn't
3850 // exist or is expired, and the client is requesting a different
3851 // address, return NULL. The client should go back to the
3852 // DHCPDISCOVER and the reserved address will be offered.
3853 if (!existing || existing->expired()) {
3854
3857 .arg(ctx.query_->getLabel())
3858 .arg(ctx.currentHost()->getIPv4Reservation().toText())
3859 .arg(ctx.requested_address_.toText());
3860
3861 return (Lease4Ptr());
3862 }
3863 }
3864
3865 // The use of the out-of-pool addresses is only allowed when the requested
3866 // address is reserved for the client. If the address is not reserved one
3867 // and it doesn't belong to the dynamic pool, do not allocate it.
3868 if ((!hasAddressReservation(ctx) ||
3869 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
3870 !inAllowedPool(ctx, ctx.requested_address_)) {
3871
3874 .arg(ctx.query_->getLabel())
3875 .arg(ctx.requested_address_);
3876
3877 ctx.unknown_requested_addr_ = true;
3878 return (Lease4Ptr());
3879 }
3880 }
3881
3882 // We have gone through all the checks, so we can now allocate the address
3883 // for the client.
3884
3885 // If the client is requesting an address which is assigned to the client
3886 // let's just renew this address. Also, renew this address if the client
3887 // doesn't request any specific address.
3888 // Added extra checks: the address is reserved for this client or belongs
3889 // to the dynamic pool for the case the pool class has changed before the
3890 // request.
3891 if (client_lease) {
3892 if (((client_lease->addr_ == ctx.requested_address_) ||
3894 ((hasAddressReservation(ctx) &&
3895 (ctx.currentHost()->getIPv4Reservation() == ctx.requested_address_)) ||
3896 inAllowedPool(ctx, client_lease->addr_))) {
3897
3900 .arg(ctx.query_->getLabel())
3901 .arg(ctx.requested_address_);
3902
3903 return (renewLease4(client_lease, ctx));
3904 }
3905 }
3906
3907 // new_lease will hold the pointer to the allocated lease if we allocate
3908 // successfully.
3909 Lease4Ptr new_lease;
3910
3911 // The client doesn't have the lease or it is requesting an address
3912 // which it doesn't have. Let's try to allocate the requested address.
3913 if (!ctx.requested_address_.isV4Zero()) {
3914
3917 .arg(ctx.query_->getLabel())
3918 .arg(ctx.requested_address_.toText());
3919
3920 // The call below will return a pointer to the lease allocated
3921 // for the client if there is no lease for the requested address,
3922 // or the existing lease has expired. If the allocation fails,
3923 // e.g. because the lease is in use, we will return NULL to
3924 // indicate that we were unable to allocate the lease.
3925 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
3926 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3927 callout_status);
3928
3929 } else {
3930
3933 .arg(ctx.query_->getLabel());
3934
3935 // We will only get here if the client didn't specify which
3936 // address it wanted to be allocated. The allocation engine will
3937 // to pick the address from the dynamic pool.
3938 new_lease = allocateUnreservedLease4(ctx);
3939 }
3940
3941 // If we allocated the lease for the client, but the client already had a
3942 // lease, we will need to return the pointer to the previous lease and
3943 // the previous lease needs to be removed from the lease database.
3944 if (new_lease && client_lease) {
3945 ctx.old_lease_ = Lease4Ptr(new Lease4(*client_lease));
3946
3949 .arg(ctx.query_->getLabel())
3950 .arg(client_lease->addr_.toText());
3951
3952 if (LeaseMgrFactory::instance().deleteLease(client_lease)) {
3953 // Need to decrease statistic for assigned addresses.
3954 StatsMgr::instance().addValue(
3955 StatsMgr::generateName("subnet", client_lease->subnet_id_,
3956 "assigned-addresses"),
3957 static_cast<int64_t>(-1));
3958 }
3959 }
3960
3961 // Return the allocated lease or NULL pointer if allocation was
3962 // unsuccessful.
3963 return (new_lease);
3964}
3965
3966uint32_t
3968
3969 // If it's BOOTP, use infinite valid lifetime.
3970 if (ctx.query_->inClass("BOOTP")) {
3971 return (Lease::INFINITY_LFT);
3972 }
3973
3974 // Use the dhcp-lease-time content from the client if it's there.
3975 uint32_t requested_lft = 0;
3976 OptionPtr opt = ctx.query_->getOption(DHO_DHCP_LEASE_TIME);
3977 if (opt) {
3978 OptionUint32Ptr opt_lft = boost::dynamic_pointer_cast<OptionInt<uint32_t> >(opt);
3979 if (opt_lft) {
3980 requested_lft = opt_lft->getValue();
3981 }
3982 }
3983
3984 // If the triplet is specified in one of our classes use it.
3985 // We use the first one we find.
3986 Triplet<uint32_t> candidate_lft;
3987 const ClientClasses classes = ctx.query_->getClasses();
3988 if (!classes.empty()) {
3989 // Let's get class definitions
3990 const ClientClassDictionaryPtr& dict =
3991 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3992
3993 // Iterate over the assigned class defintions.
3994 for (ClientClasses::const_iterator name = classes.cbegin();
3995 name != classes.cend(); ++name) {
3996 ClientClassDefPtr cl = dict->findClass(*name);
3997 if (cl && (!cl->getValid().unspecified())) {
3998 candidate_lft = cl->getValid();
3999 break;
4000 }
4001 }
4002 }
4003
4004 // If no classes specified it, get it from the subnet.
4005 if (!candidate_lft) {
4006 candidate_lft = ctx.subnet_->getValid();
4007 }
4008
4009 // If client requested a value, use the value bounded by
4010 // the candidate triplet.
4011 if (requested_lft > 0) {
4012 return (candidate_lft.get(requested_lft));
4013 }
4014
4015 // Use the candidate's default value.
4016 return (candidate_lft.get());
4017}
4018
4020AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
4021 CalloutHandle::CalloutNextStep& callout_status) {
4022 if (!ctx.hwaddr_) {
4023 isc_throw(BadValue, "Can't create a lease with NULL HW address");
4024 }
4025 if (!ctx.subnet_) {
4026 isc_throw(BadValue, "Can't create a lease without a subnet");
4027 }
4028
4029 // Get the context appropriate valid lifetime.
4030 uint32_t valid_lft = getValidLft(ctx);
4031
4032 time_t now = time(NULL);
4033
4034 ClientIdPtr client_id;
4035 if (ctx.subnet_->getMatchClientId()) {
4036 client_id = ctx.clientid_;
4037 }
4038
4039 Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
4040 valid_lft, now, ctx.subnet_->getID()));
4041
4042 // Set FQDN specific lease parameters.
4043 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4044 lease->fqdn_rev_ = ctx.rev_dns_update_;
4045 lease->hostname_ = ctx.hostname_;
4046
4047 // Add(update) the extended information on the lease.
4048 static_cast<void>(updateLease4ExtendedInfo(lease, ctx));
4049
4050 // Let's execute all callouts registered for lease4_select
4051 if (ctx.callout_handle_ &&
4052 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4053
4054 // Use the RAII wrapper to make sure that the callout handle state is
4055 // reset when this object goes out of scope. All hook points must do
4056 // it to prevent possible circular dependency between the callout
4057 // handle and its arguments.
4058 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4059
4060 // Enable copying options from the packet within hook library.
4061 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4062
4063 // Pass necessary arguments
4064 // Pass the original client query
4065 ctx.callout_handle_->setArgument("query4", ctx.query_);
4066
4067 // Subnet from which we do the allocation (That's as far as we can go
4068 // with using SubnetPtr to point to Subnet4 object. Users should not
4069 // be confused with dynamic_pointer_casts. They should get a concrete
4070 // pointer (Subnet4Ptr) pointing to a Subnet4 object.
4071 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4072 ctx.callout_handle_->setArgument("subnet4", subnet4);
4073
4074 // Is this solicit (fake = true) or request (fake = false)
4075 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4076
4077 // Pass the intended lease as well
4078 ctx.callout_handle_->setArgument("lease4", lease);
4079
4080 // This is the first callout, so no need to clear any arguments
4081 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4082
4083 callout_status = ctx.callout_handle_->getStatus();
4084
4085 // Callouts decided to skip the action. This means that the lease is not
4086 // assigned, so the client will get NoAddrAvail as a result. The lease
4087 // won't be inserted into the database.
4088 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4090 return (Lease4Ptr());
4091 }
4092
4093 // Let's use whatever callout returned. Hopefully it is the same lease
4094 // we handled to it.
4095 ctx.callout_handle_->getArgument("lease4", lease);
4096 }
4097
4098 if (!ctx.fake_allocation_) {
4099 // That is a real (REQUEST) allocation
4100 bool status = LeaseMgrFactory::instance().addLease(lease);
4101 if (status) {
4102
4103 // The lease insertion succeeded, let's bump up the statistic.
4104 StatsMgr::instance().addValue(
4105 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4106 "assigned-addresses"),
4107 static_cast<int64_t>(1));
4108 StatsMgr::instance().addValue(
4109 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4110 "cumulative-assigned-addresses"),
4111 static_cast<int64_t>(1));
4112 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4113 static_cast<int64_t>(1));
4114
4115 return (lease);
4116 } else {
4117 // One of many failures with LeaseMgr (e.g. lost connection to the
4118 // database, database failed etc.). One notable case for that
4119 // is that we are working in multi-process mode and we lost a race
4120 // (some other process got that address first)
4121 return (Lease4Ptr());
4122 }
4123 } else {
4124 // That is only fake (DISCOVER) allocation
4125
4126 // It is for OFFER only. We should not insert the lease and callers
4127 // have already verified the lease does not exist in the database.
4128 return (lease);
4129 }
4130}
4131
4133AllocEngine::renewLease4(const Lease4Ptr& lease,
4135 if (!lease) {
4136 isc_throw(BadValue, "null lease specified for renewLease4");
4137 }
4138
4139 // Let's keep the old data. This is essential if we are using memfile
4140 // (the lease returned points directly to the lease4 object in the database)
4141 // We'll need it if we want to skip update (i.e. roll back renewal)
4143 Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
4144 ctx.old_lease_.reset(new Lease4(*old_values));
4145
4146 // Update the lease with the information from the context.
4147 // If there was no significant changes, try reuse.
4148 lease->reuseable_valid_lft_ = 0;
4149 if (!updateLease4Information(lease, ctx)) {
4150 setLeaseReusable(lease, ctx);
4151 }
4152
4153 if (!ctx.fake_allocation_) {
4154 // If the lease is expired we have to reclaim it before
4155 // re-assigning it to the client. The lease reclamation
4156 // involves execution of hooks and DNS update.
4157 if (ctx.old_lease_->expired()) {
4158 reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
4159 }
4160
4161 lease->state_ = Lease::STATE_DEFAULT;
4162 }
4163
4164 bool skip = false;
4165 // Execute all callouts registered for lease4_renew.
4166 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_renew_)) {
4167
4168 // Use the RAII wrapper to make sure that the callout handle state is
4169 // reset when this object goes out of scope. All hook points must do
4170 // it to prevent possible circular dependency between the callout
4171 // handle and its arguments.
4172 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4173
4174 // Enable copying options from the packet within hook library.
4175 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4176
4177 // Subnet from which we do the allocation. Convert the general subnet
4178 // pointer to a pointer to a Subnet4. Note that because we are using
4179 // boost smart pointers here, we need to do the cast using the boost
4180 // version of dynamic_pointer_cast.
4181 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4182
4183 // Pass the parameters. Note the clientid is passed only if match-client-id
4184 // is set. This is done that way, because the lease4-renew hook point is
4185 // about renewing a lease and the configuration parameter says the
4186 // client-id should be ignored. Hence no clientid value if match-client-id
4187 // is false.
4188 ctx.callout_handle_->setArgument("query4", ctx.query_);
4189 ctx.callout_handle_->setArgument("subnet4", subnet4);
4190 ctx.callout_handle_->setArgument("clientid", subnet4->getMatchClientId() ?
4191 ctx.clientid_ : ClientIdPtr());
4192 ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
4193
4194 // Pass the lease to be updated
4195 ctx.callout_handle_->setArgument("lease4", lease);
4196
4197 // Call all installed callouts
4198 HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
4199 *ctx.callout_handle_);
4200
4201 // Callouts decided to skip the next processing step. The next
4202 // processing step would actually renew the lease, so skip at this
4203 // stage means "keep the old lease as it is".
4204 if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4205 skip = true;
4208 }
4209
4211 }
4212
4213 if (!ctx.fake_allocation_ && !skip && (lease->reuseable_valid_lft_ == 0)) {
4214 // for REQUEST we do update the lease
4216
4217 // We need to account for the re-assignment of The lease.
4218 if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
4219 StatsMgr::instance().addValue(
4220 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4221 "assigned-addresses"),
4222 static_cast<int64_t>(1));
4223 StatsMgr::instance().addValue(
4224 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4225 "cumulative-assigned-addresses"),
4226 static_cast<int64_t>(1));
4227 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4228 static_cast<int64_t>(1));
4229 }
4230 }
4231 if (skip) {
4232 // Rollback changes (really useful only for memfile)
4234 *lease = *old_values;
4235 }
4236
4237 return (lease);
4238}
4239
4241AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
4243 CalloutHandle::CalloutNextStep& callout_status) {
4244 if (!expired) {
4245 isc_throw(BadValue, "null lease specified for reuseExpiredLease");
4246 }
4247
4248 if (!ctx.subnet_) {
4249 isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
4250 }
4251
4252 if (!ctx.fake_allocation_) {
4253 // The expired lease needs to be reclaimed before it can be reused.
4254 // This includes declined leases for which probation period has
4255 // elapsed.
4256 reclaimExpiredLease(expired, ctx.callout_handle_);
4257 expired->state_ = Lease::STATE_DEFAULT;
4258 }
4259
4260 expired->reuseable_valid_lft_ = 0;
4261 static_cast<void>(updateLease4Information(expired, ctx));
4262
4265 .arg(ctx.query_->getLabel())
4266 .arg(expired->toText());
4267
4268 // Let's execute all callouts registered for lease4_select
4269 if (ctx.callout_handle_ &&
4270 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4271
4272 // Enable copying options from the packet within hook library.
4273 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4274
4275 // Use the RAII wrapper to make sure that the callout handle state is
4276 // reset when this object goes out of scope. All hook points must do
4277 // it to prevent possible circular dependency between the callout
4278 // handle and its arguments.
4279 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4280
4281 // Pass necessary arguments
4282 // Pass the original client query
4283 ctx.callout_handle_->setArgument("query4", ctx.query_);
4284
4285 // Subnet from which we do the allocation. Convert the general subnet
4286 // pointer to a pointer to a Subnet4. Note that because we are using
4287 // boost smart pointers here, we need to do the cast using the boost
4288 // version of dynamic_pointer_cast.
4289 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4290 ctx.callout_handle_->setArgument("subnet4", subnet4);
4291
4292 // Is this solicit (fake = true) or request (fake = false)
4293 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4294
4295 // The lease that will be assigned to a client
4296 ctx.callout_handle_->setArgument("lease4", expired);
4297
4298 // Call the callouts
4299 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4300
4301 callout_status = ctx.callout_handle_->getStatus();
4302
4303 // Callouts decided to skip the action. This means that the lease is not
4304 // assigned, so the client will get NoAddrAvail as a result. The lease
4305 // won't be inserted into the database.
4306 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4309 return (Lease4Ptr());
4310 }
4311
4313
4314 // Let's use whatever callout returned. Hopefully it is the same lease
4315 // we handed to it.
4316 ctx.callout_handle_->getArgument("lease4", expired);
4317 }
4318
4319 if (!ctx.fake_allocation_) {
4320 // for REQUEST we do update the lease
4322
4323 // We need to account for the re-assignment of The lease.
4324 StatsMgr::instance().addValue(
4325 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4326 "assigned-addresses"),
4327 static_cast<int64_t>(1));
4328 StatsMgr::instance().addValue(
4329 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4330 "cumulative-assigned-addresses"),
4331 static_cast<int64_t>(1));
4332 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4333 static_cast<int64_t>(1));
4334 }
4335
4336 // We do nothing for SOLICIT. We'll just update database when
4337 // the client gets back to us with REQUEST message.
4338
4339 // it's not really expired at this stage anymore - let's return it as
4340 // an updated lease
4341 return (expired);
4342}
4343
4345AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
4346 CalloutHandle::CalloutNextStep& callout_status) {
4347 ctx.conflicting_lease_.reset();
4348
4349 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4350 if (exist_lease) {
4351 if (exist_lease->expired()) {
4352 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4353 // reuseExpiredLease4() will reclaim the use which will
4354 // queue an NCR remove it needed. Clear the DNS fields in
4355 // the old lease to avoid a redundant remove in server logic.
4356 ctx.old_lease_->hostname_.clear();
4357 ctx.old_lease_->fqdn_fwd_ = false;
4358 ctx.old_lease_->fqdn_rev_ = false;
4359 return (reuseExpiredLease4(exist_lease, ctx, callout_status));
4360
4361 } else {
4362 // If there is a lease and it is not expired, pass this lease back
4363 // to the caller in the context. The caller may need to know
4364 // which lease we're conflicting with.
4365 ctx.conflicting_lease_ = exist_lease;
4366 }
4367
4368 } else {
4369 return (createLease4(ctx, candidate, callout_status));
4370 }
4371 return (Lease4Ptr());
4372}
4373
4375AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
4376 Lease4Ptr new_lease;
4378 Subnet4Ptr subnet = ctx.subnet_;
4379
4380 // Need to check if the subnet belongs to a shared network. If so,
4381 // we might be able to find a better subnet for lease allocation,
4382 // for which it is more likely that there are some leases available.
4383 // If we stick to the selected subnet, we may end up walking over
4384 // the entire subnet (or more subnets) to discover that the address
4385 // pools have been exhausted. Using a subnet from which an address
4386 // was assigned most recently is an optimization which increases
4387 // the likelihood of starting from the subnet which address pools
4388 // are not exhausted.
4389 SharedNetwork4Ptr network;
4390 ctx.subnet_->getSharedNetwork(network);
4391 if (network) {
4392 // This would try to find a subnet with the same set of classes
4393 // as the current subnet, but with the more recent "usage timestamp".
4394 // This timestamp is only updated for the allocations made with an
4395 // allocator (unreserved lease allocations), not the static
4396 // allocations or requested addresses.
4397 ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
4398 }
4399
4400 // We have the choice in the order checking the lease and
4401 // the reservation. The default is to begin by the lease
4402 // if the multi-threading is disabled.
4403 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
4404
4405 Subnet4Ptr original_subnet = subnet;
4406
4407 uint64_t total_attempts = 0;
4408
4409 // The following counter tracks the number of subnets with matching client
4410 // classes from which the allocation engine attempted to assign leases.
4411 uint64_t subnets_with_unavail_leases = 0;
4412 // The following counter tracks the number of subnets in which there were
4413 // no matching pools for the client.
4414 uint64_t subnets_with_unavail_pools = 0;
4415
4416 while (subnet) {
4417 ClientIdPtr client_id;
4418 if (subnet->getMatchClientId()) {
4419 client_id = ctx.clientid_;
4420 }
4421
4422 uint64_t possible_attempts =
4423 subnet->getPoolCapacity(Lease::TYPE_V4,
4424 ctx.query_->getClasses());
4425
4426 // If the number of tries specified in the allocation engine constructor
4427 // is set to 0 (unlimited) or the pools capacity is lower than that number,
4428 // let's use the pools capacity as the maximum number of tries. Trying
4429 // more than the actual pools capacity is a waste of time. If the specified
4430 // number of tries is lower than the pools capacity, use that number.
4431 uint64_t max_attempts = ((attempts_ == 0 || possible_attempts < attempts_) ? possible_attempts : attempts_);
4432
4433 if (max_attempts > 0) {
4434 // If max_attempts is greater than 0, there are some pools in this subnet
4435 // from which we can potentially get a lease.
4436 ++subnets_with_unavail_leases;
4437 } else {
4438 // If max_attempts is 0, it is an indication that there are no pools
4439 // in the subnet from which we can get a lease.
4440 ++subnets_with_unavail_pools;
4441 }
4442
4443 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
4444
4445 for (uint64_t i = 0; i < max_attempts; ++i) {
4446
4447 ++total_attempts;
4448
4449 IOAddress candidate = allocator->pickAddress(subnet,
4450 ctx.query_->getClasses(),
4451 client_id,
4452 ctx.requested_address_);
4453 // First check for reservation when it is the choice.
4454 if (check_reservation_first && addressReserved(candidate, ctx)) {
4455 // Don't allocate.
4456 continue;
4457 }
4458
4459 // Check if the resource is busy i.e. can be being allocated
4460 // by another thread to another client.
4461 ResourceHandler4 resource_handler;
4462 if (MultiThreadingMgr::instance().getMode() &&
4463 !resource_handler.tryLock4(candidate)) {
4464 // Don't allocate.
4465 continue;
4466 }
4467
4468 // Check for an existing lease for the candidate address.
4469 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4470 if (!exist_lease) {
4471 // No existing lease, is it reserved?
4472 if (check_reservation_first || !addressReserved(candidate, ctx)) {
4473 // Not reserved use it.
4474 new_lease = createLease4(ctx, candidate, callout_status);
4475 }
4476 } else {
4477 // An lease exists, is expired, and not reserved use it.
4478 if (exist_lease->expired() &&
4479 (check_reservation_first || !addressReserved(candidate, ctx))) {
4480 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4481 new_lease = reuseExpiredLease4(exist_lease, ctx, callout_status);
4482 }
4483 }
4484
4485 // We found a lease we can use, return it.
4486 if (new_lease) {
4487 return (new_lease);
4488 }
4489
4490 if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
4491 // Don't retry when the callout status is not continue.
4492 subnet.reset();
4493 break;
4494 }
4495 }
4496
4497 // This pointer may be set to NULL if hooks set SKIP status.
4498 if (subnet) {
4499 subnet = subnet->getNextSubnet(original_subnet, ctx.query_->getClasses());
4500
4501 if (subnet) {
4502 ctx.subnet_ = subnet;
4503 }
4504 }
4505 }
4506
4507 if (network) {
4508 // The client is in the shared network. Let's log the high level message
4509 // indicating which shared network the client belongs to.
4511 .arg(ctx.query_->getLabel())
4512 .arg(network->getName())
4513 .arg(subnets_with_unavail_leases)
4514 .arg(subnets_with_unavail_pools);
4515 StatsMgr::instance().addValue("v4-allocation-fail-shared-network",
4516 static_cast<int64_t>(1));
4517 StatsMgr::instance().addValue(
4518 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4519 "v4-allocation-fail-shared-network"),
4520 static_cast<int64_t>(1));
4521 } else {
4522 // The client is not connected to a shared network. It is connected
4523 // to a subnet. Let's log some details about the subnet.
4524 std::string shared_network = ctx.subnet_->getSharedNetworkName();
4525 if (shared_network.empty()) {
4526 shared_network = "(none)";
4527 }
4529 .arg(ctx.query_->getLabel())
4530 .arg(ctx.subnet_->toText())
4531 .arg(ctx.subnet_->getID())
4532 .arg(shared_network);
4533 StatsMgr::instance().addValue("v4-allocation-fail-subnet",
4534 static_cast<int64_t>(1));
4535 StatsMgr::instance().addValue(
4536 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4537 "v4-allocation-fail-subnet"),
4538 static_cast<int64_t>(1));
4539 }
4540 if (total_attempts == 0) {
4541 // In this case, it seems that none of the pools in the subnets could
4542 // be used for that client, both in case the client is connected to
4543 // a shared network or to a single subnet. Apparently, the client was
4544 // rejected to use the pools because of the client classes' mismatch.
4546 .arg(ctx.query_->getLabel());
4547 StatsMgr::instance().addValue("v4-allocation-fail-no-pools",
4548 static_cast<int64_t>(1));
4549 StatsMgr::instance().addValue(
4550 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4551 "v4-allocation-fail-no-pools"),
4552 static_cast<int64_t>(1));
4553 } else {
4554 // This is an old log message which provides a number of attempts
4555 // made by the allocation engine to allocate a lease. The only case
4556 // when we don't want to log this message is when the number of
4557 // attempts is zero (condition above), because it would look silly.
4559 .arg(ctx.query_->getLabel())
4560 .arg(total_attempts);
4561 StatsMgr::instance().addValue("v4-allocation-fail",
4562 static_cast<int64_t>(1));
4563 StatsMgr::instance().addValue(
4564 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4565 "v4-allocation-fail"),
4566 static_cast<int64_t>(1));
4567 }
4568
4569 const ClientClasses& classes = ctx.query_->getClasses();
4570 if (!classes.empty()) {
4572 .arg(ctx.query_->getLabel())
4573 .arg(classes.toText());
4574 StatsMgr::instance().addValue("v4-allocation-fail-classes",
4575 static_cast<int64_t>(1));
4576 StatsMgr::instance().addValue(
4577 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4578 "v4-allocation-fail-classes"),
4579 static_cast<int64_t>(1));
4580 }
4581
4582 return (new_lease);
4583}
4584
4585bool
4586AllocEngine::updateLease4Information(const Lease4Ptr& lease,
4587 AllocEngine::ClientContext4& ctx) const {
4588 bool changed = false;
4589 if (lease->subnet_id_ != ctx.subnet_->getID()) {
4590 changed = true;
4591 lease->subnet_id_ = ctx.subnet_->getID();
4592 }
4593 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
4594 (ctx.hwaddr_ &&
4595 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
4596 changed = true;
4597 lease->hwaddr_ = ctx.hwaddr_;
4598 }
4599 if (ctx.subnet_->getMatchClientId() && ctx.clientid_) {
4600 if (!lease->client_id_ || (*ctx.clientid_ != *lease->client_id_)) {
4601 changed = true;
4602 lease->client_id_ = ctx.clientid_;
4603 }
4604 } else if (lease->client_id_) {
4605 changed = true;
4606 lease->client_id_ = ClientIdPtr();
4607 }
4608 lease->cltt_ = time(NULL);
4609
4610 // Get the context appropriate valid lifetime.
4611 lease->valid_lft_ = getValidLft(ctx);
4612
4613 // Reduced valid lifetime is a significant change.
4614 if (lease->valid_lft_ < lease->current_valid_lft_) {
4615 changed = true;
4616 }
4617
4618 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
4619 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
4620 (lease->hostname_ != ctx.hostname_)) {
4621 changed = true;
4622 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4623 lease->fqdn_rev_ = ctx.rev_dns_update_;
4624 lease->hostname_ = ctx.hostname_;
4625 }
4626
4627 // Add(update) the extended information on the lease.
4628 if (updateLease4ExtendedInfo(lease, ctx)) {
4629 changed = true;
4630 }
4631
4632 return (changed);
4633}
4634
4635bool
4637 const AllocEngine::ClientContext4& ctx) const {
4638 bool changed = false;
4639
4640 // If storage is not enabled then punt.
4641 if (!ctx.subnet_->getStoreExtendedInfo()) {
4642 return (changed);
4643 }
4644
4645 // Look for relay agent information option (option 82)
4646 OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS);
4647 if (!rai) {
4648 // Pkt4 doesn't have it, so nothing to store (or update).
4649 return (changed);
4650 }
4651
4652 // Create a StringElement with the hex string for relay-agent-info.
4653 ElementPtr relay_agent(new StringElement(rai->toHexString()));
4654
4655 // Now we wrap the agent info in a map. This allows for future expansion.
4656 ElementPtr extended_info = Element::createMap();
4657 extended_info->set("relay-agent-info", relay_agent);
4658
4659 // Get a writable copy of the lease's current user context.
4660 ElementPtr user_context;
4661 if (lease->getContext()) {
4662 user_context = UserContext::toElement(lease->getContext());
4663 } else {
4664 user_context = Element::createMap();
4665 }
4666
4667 // Add/replace the extended info entry.
4668 ConstElementPtr old_extended_info = user_context->get("ISC");
4669 if (!old_extended_info || (*old_extended_info != *extended_info)) {
4670 changed = true;
4671 user_context->set("ISC", extended_info);
4672 }
4673
4674 // Update the lease's user_context.
4675 lease->setContext(user_context);
4676
4677 return (changed);
4678}
4679
4680bool
4682 const AllocEngine::ClientContext6& ctx) const {
4683 bool changed = false;
4684
4685 // If storage is not enabled then punt.
4686 if (!ctx.subnet_->getStoreExtendedInfo()) {
4687 return (changed);
4688 }
4689
4690 // If we do not have relay information, then punt.
4691 if (ctx.query_->relay_info_.empty()) {
4692 return (changed);
4693 }
4694
4695 // We need to convert the vector of RelayInfo instances in
4696 // into an Element hierarchy like this:
4697 // "relay-info": [
4698 // {
4699 // "hop": 123,
4700 // "link": "2001:db8::1",
4701 // "peer": "2001:db8::2",
4702 // "options": "0x..."
4703 // },..]
4704 //
4705 ElementPtr relay_list = Element::createList();
4706 for (auto relay : ctx.query_->relay_info_) {
4707 ElementPtr relay_elem = Element::createMap();
4708 relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
4709 relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
4710 relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
4711
4712 // If there are relay options, we'll pack them into a buffer and then
4713 // convert that into a hex string. If there are no options, we omit
4714 // then entry.
4715 if (!relay.options_.empty()) {
4716 OutputBuffer buf(128);
4717 LibDHCP::packOptions6(buf, relay.options_);
4718
4719 if (buf.getLength() > 0) {
4720 const uint8_t* cp = static_cast<const uint8_t*>(buf.getData());
4721 std::vector<uint8_t>bytes;
4722 std::stringstream ss;
4723
4724 bytes.assign(cp, cp + buf.getLength());
4725 ss << "0x" << encode::encodeHex(bytes);
4726 relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
4727 }
4728 }
4729
4730 relay_list->add(relay_elem);
4731 }
4732
4733 // Now we wrap the list of relays in a map. This allows for future expansion.
4734 ElementPtr extended_info = Element::createMap();
4735 extended_info->set("relays", relay_list);
4736
4737 // Get a writable copy of the lease's current user context.
4738 ElementPtr user_context;
4739 if (lease->getContext()) {
4740 user_context = UserContext::toElement(lease->getContext());
4741 } else {
4742 user_context = Element::createMap();
4743 }
4744
4745 // Add/replace the extended info entry.
4746 ConstElementPtr old_extended_info = user_context->get("ISC");
4747 if (!old_extended_info || (*old_extended_info != *extended_info)) {
4748 changed = true;
4749 user_context->set("ISC", extended_info);
4750 }
4751
4752 // Update the lease's user_context.
4753 lease->setContext(user_context);
4754
4755 return (changed);
4756}
4757
4758void
4759AllocEngine::setLeaseReusable(const Lease4Ptr& lease,
4760 const ClientContext4& ctx) const {
4761 // Sanity.
4762 lease->reuseable_valid_lft_ = 0;
4763 const Subnet4Ptr& subnet = ctx.subnet_;
4764 if (!subnet) {
4765 return;
4766 }
4767 if (lease->state_ != Lease::STATE_DEFAULT) {
4768 return;
4769 }
4770
4771 // Always reuse infinite lifetime leases.
4772 if (lease->valid_lft_ == Lease::INFINITY_LFT) {
4773 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
4774 return;
4775 }
4776
4777 // Refuse time not going forward.
4778 if (lease->cltt_ < lease->current_cltt_) {
4779 return;
4780 }
4781
4782 uint32_t age = lease->cltt_ - lease->current_cltt_;
4783 // Already expired.
4784 if (age >= lease->current_valid_lft_) {
4785 return;
4786 }
4787
4788 // Try cache max age.
4789 uint32_t max_age = 0;
4790 if (!subnet->getCacheMaxAge().unspecified()) {
4791 max_age = subnet->getCacheMaxAge().get();
4792 if ((max_age == 0) || (age > max_age)) {
4793 return;
4794 }
4795 }
4796
4797 // Try cache threshold.
4798 if (!subnet->getCacheThreshold().unspecified()) {
4799 double threshold = subnet->getCacheThreshold().get();
4800 if ((threshold <= 0.) || (threshold > 1.)) {
4801 return;
4802 }
4803 max_age = lease->valid_lft_ * threshold;
4804 if (age > max_age) {
4805 return;
4806 }
4807 }
4808
4809 // No cache.
4810 if (max_age == 0) {
4811 return;
4812 }
4813
4814 // Seems to be reusable.
4815 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
4816}
4817
4818void
4819AllocEngine::setLeaseReusable(const Lease6Ptr& lease,
4820 uint32_t current_preferred_lft,
4821 const ClientContext6& ctx) const {
4822 // Sanity.
4823 lease->reuseable_valid_lft_ = 0;
4824 lease->reuseable_preferred_lft_ = 0;
4825 const Subnet6Ptr& subnet = ctx.subnet_;
4826 if (!subnet) {
4827 return;
4828 }
4829 if (lease->state_ != Lease::STATE_DEFAULT) {
4830 return;
4831 }
4832
4833 // Refuse time not going forward.
4834 if (lease->cltt_ < lease->current_cltt_) {
4835 return;
4836 }
4837
4838 uint32_t age = lease->cltt_ - lease->current_cltt_;
4839 // Already expired.
4840 if (age >= lease->current_valid_lft_) {
4841 return;
4842 }
4843
4844 // Try cache max age.
4845 uint32_t max_age = 0;
4846 if (!subnet->getCacheMaxAge().unspecified()) {
4847 max_age = subnet->getCacheMaxAge().get();
4848 if ((max_age == 0) || (age > max_age)) {
4849 return;
4850 }
4851 }
4852
4853 // Try cache threshold.
4854 if (!subnet->getCacheThreshold().unspecified()) {
4855 double threshold = subnet->getCacheThreshold().get();
4856 if ((threshold <= 0.) || (threshold > 1.)) {
4857 return;
4858 }
4859 max_age = lease->valid_lft_ * threshold;
4860 if (age > max_age) {
4861 return;
4862 }
4863 }
4864
4865 // No cache.
4866 if (max_age == 0) {
4867 return;
4868 }
4869
4870 // Seems to be reusable.
4871 if ((current_preferred_lft == Lease::INFINITY_LFT) ||
4872 (current_preferred_lft == 0)) {
4873 // Keep these values.
4874 lease->reuseable_preferred_lft_ = current_preferred_lft;
4875 } else if (current_preferred_lft > age) {
4876 lease->reuseable_preferred_lft_ = current_preferred_lft - age;
4877 } else {
4878 // Can be a misconfiguration so stay safe...
4879 return;
4880 }
4881 if (lease->current_valid_lft_ == Lease::INFINITY_LFT) {
4882 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
4883 } else {
4884 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
4885 }
4886}
4887
4888} // namespace dhcp
4889} // namespace isc
CtrlAgentHooks Hooks
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
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 if a function is called in a prohibited way.
A generic exception that is thrown when a function is not implemented.
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:291
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:286
Notes: IntElement type is changed to int64_t.
Definition: data.h:590
Base class for all address/prefix allocation algorithms.
Definition: alloc_engine.h:70
Address/prefix allocator that gets an address based on a hash.
Definition: alloc_engine.h:208
HashedAllocator(Lease::Type type)
Default constructor (does nothing)
Address/prefix allocator that iterates over all addresses.
Definition: alloc_engine.h:148
static isc::asiolink::IOAddress increasePrefix(const isc::asiolink::IOAddress &prefix, const uint8_t prefix_len)
Returns the next prefix.
Definition: alloc_engine.cc:98
static isc::asiolink::IOAddress increaseAddress(const isc::asiolink::IOAddress &address, bool prefix, const uint8_t prefix_len)
Returns the next address or prefix.
IterativeAllocator(Lease::Type type)
Default constructor.
Definition: alloc_engine.cc:93
Random allocator that picks address randomly.
Definition: alloc_engine.h:238
RandomAllocator(Lease::Type type)
Default constructor (does nothing)
Defines a single hint.
Definition: alloc_engine.h:324
bool updateLease6ExtendedInfo(const Lease6Ptr &lease, const ClientContext6 &ctx) const
Stores additional client query parameters on a V6 lease.
static IPv6Resrv makeIPv6Resrv(const Lease6 &lease)
Creates an IPv6Resrv instance from a Lease6.
Definition: alloc_engine.h:995
bool updateLease4ExtendedInfo(const Lease4Ptr &lease, const ClientContext4 &ctx) const
Stores additional client query parameters on a V4 lease.
AllocType
Specifies allocation type.
Definition: alloc_engine.h:268
static ConstHostPtr findGlobalReservation(ClientContext6 &ctx)
Attempts to find the host reservation for the client.
std::pair< Host::IdentifierType, std::vector< uint8_t > > IdentifierPair
A tuple holding host identifier type and value.
Definition: alloc_engine.h:432
isc::util::ReadWriteMutex rw_mutex_
The read-write mutex.
static void getLifetimes6(ClientContext6 &ctx, uint32_t &preferred, uint32_t &valid)
Determines the preferred and valid v6 lease lifetimes.
static void findReservation(ClientContext6 &ctx)
boost::shared_ptr< Allocator > AllocatorPtr
defines a pointer to allocator
Definition: alloc_engine.h:140
void deleteExpiredReclaimedLeases4(const uint32_t secs)
Deletes reclaimed leases expired more than specified amount of time ago.
static uint32_t getValidLft(const ClientContext4 &ctx)
Returns the valid lifetime based on the v4 context.
AllocEngine(AllocType engine_type, uint64_t attempts, bool ipv6=true)
Constructor.
void reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout, const bool remove_lease, const uint16_t max_unwarned_cycles=0)
Reclaims expired IPv6 leases.
void reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout, const bool remove_lease, const uint16_t max_unwarned_cycles=0)
Reclaims expired IPv4 leases.
Lease4Ptr allocateLease4(ClientContext4 &ctx)
Returns IPv4 lease.
void deleteExpiredReclaimedLeases6(const uint32_t secs)
Deletes reclaimed leases expired more than specified amount of time ago.
Lease6Collection allocateLeases6(ClientContext6 &ctx)
Allocates IPv6 leases for a given IA container.
Lease6Collection renewLeases6(ClientContext6 &ctx)
Renews existing DHCPv6 leases for a given IA.
AllocatorPtr getAllocator(Lease::Type type)
Returns allocator for a given pool type.
An exception that is thrown when allocation module fails (e.g.
Definition: alloc_engine.h:43
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
Container for storing client class names.
Definition: classify.h:70
ClientClassContainer::const_iterator const_iterator
Type of iterators.
Definition: classify.h:74
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
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition: srv_config.h:47
virtual ConstHostCollection getAll(const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Return all hosts connected to any subnet for which reservations have been made using a specified iden...
Definition: host_mgr.cc:114
bool getDisableSingleQuery() const
Returns the disable single query flag.
Definition: host_mgr.h:596
virtual ConstHostPtr get6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv6 subnet.
Definition: host_mgr.cc:498
virtual ConstHostPtr get4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv4 subnet.
Definition: host_mgr.cc:368
static HostMgr & instance()
Returns a sole instance of the HostMgr.
Definition: host_mgr.cc:105
virtual ConstHostCollection getAll4(const SubnetID &subnet_id) const
Return all hosts in a DHCPv4 subnet.
Definition: host_mgr.cc:129
@ IDENT_HWADDR
Definition: host.h:308
IPv6 reservation for a host.
Definition: host.h:161
Type
Type of the reservation.
Definition: host.h:167
static LeaseMgr & instance()
Return current lease manager.
Abstract Lease Manager.
Definition: lease_mgr.h:223
virtual Lease6Collection getLeases6(Lease::Type type, const DUID &duid, uint32_t iaid) const =0
Returns existing IPv6 leases for a given DUID+IA combination.
virtual void getExpiredLeases6(Lease6Collection &expired_leases, const size_t max_leases) const =0
Returns a collection of expired DHCPv6 leases.
virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs)=0
Deletes all expired and reclaimed DHCPv6 leases.
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const =0
Returns an IPv4 lease for specified IPv4 address.
virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs)=0
Deletes all expired and reclaimed DHCPv4 leases.
virtual bool addLease(const Lease4Ptr &lease)=0
Adds an IPv4 lease.
virtual bool deleteLease(const Lease4Ptr &lease)=0
Deletes an IPv4 lease.
virtual void getExpiredLeases4(Lease4Collection &expired_leases, const size_t max_leases) const =0
Returns a collection of expired DHCPv4 leases.
virtual void updateLease4(const Lease4Ptr &lease4)=0
Updates IPv4 lease.
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const =0
Returns existing IPv6 lease for a given IPv6 address.
virtual void updateLease6(const Lease6Ptr &lease6)=0
Updates IPv6 lease.
static void packOptions6(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv6 options in a buffer.
Definition: libdhcp++.cc:1069
static std::string makeLabel(const HWAddrPtr &hwaddr, const ClientIdPtr &client_id, const uint32_t transid)
Returns text representation of the given packet identifiers.
Definition: pkt4.cc:397
static std::string makeLabel(const DuidPtr duid, const uint32_t transid, const HWAddrPtr &hwaddr)
Returns text representation of the given packet identifiers.
Definition: pkt6.cc:580
Resource race avoidance RAII handler for DHCPv4.
bool tryLock4(const asiolink::IOAddress &addr)
Tries to acquires a resource.
Resource race avoidance RAII handler.
bool tryLock(Lease::Type type, const asiolink::IOAddress &addr)
Tries to acquires a resource.
RAII object enabling copying options retrieved from the packet.
Definition: pkt.h:40
static bool subnetsIncludeMatchClientId(const Subnet4Ptr &first_subnet, const ClientClasses &client_classes)
Checks if the shared network includes a subnet with the match client ID flag set to true.
CalloutNextStep
Specifies allowed next steps.
Wrapper class around callout handle which automatically resets handle's state.
Statistics Manager class.
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:136
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
Utility class to measure code execution times.
Definition: stopwatch.h:35
long getTotalMilliseconds() const
Retrieves the total measured duration in milliseconds.
Definition: stopwatch.cc:60
void stop()
Stops the stopwatch.
Definition: stopwatch.cc:35
std::string logFormatTotalDuration() const
Returns the total measured duration in the format directly usable in the log messages.
Definition: stopwatch.cc:80
T get(T hint) const
Returns value with a hint.
Definition: triplet.h:99
Write mutex RAII handler.
@ D6O_CLIENT_FQDN
Definition: dhcp6.h:59
@ DHCPV6_RENEW
Definition: dhcp6.h:209
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< OptionUint32 > OptionUint32Ptr
Definition: option_int.h:35
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
#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
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1360
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_NO_LEASES_HR
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_INVALID
const isc::log::MessageID ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_NO_POOLS
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_RECOVER_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_EXTEND_LEASE
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_IN_USE
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_HR_LEASE_EXISTS
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED
const isc::log::MessageID ALLOC_ENGINE_V6_RENEW_HR
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_LEASE_DATA
const isc::log::MessageID ALLOC_ENGINE_V4_REUSE_EXPIRED_LEASE_DATA
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_LEASE
boost::shared_ptr< Subnet > SubnetPtr
A generic pointer to either Subnet4 or Subnet6 object.
Definition: subnet.h:515
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:524
const isc::log::MessageID ALLOC_ENGINE_V6_REUSE_EXPIRED_LEASE_DATA
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_NO_V6_HR
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_ERROR
const isc::log::MessageID ALLOC_ENGINE_V6_HINT_RESERVED
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_OUT_OF_POOL
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_SLOW
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_ADDRESS_RESERVED
isc::log::Logger alloc_engine_logger("alloc-engine")
Logger for the AllocEngine.
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_REQUESTED_LEASE
const isc::log::MessageID ALLOC_ENGINE_LEASE_RECLAIMED
@ DHO_DHCP_AGENT_OPTIONS
Definition: dhcp4.h:151
@ DHO_DHCP_LEASE_TIME
Definition: dhcp4.h:120
const int ALLOC_ENGINE_DBG_TRACE
Logging levels for the AllocEngine.
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_COMPLETE
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_COMPLETE
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:791
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_ERROR
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_LEASES_HR
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_ALLOC_UNRESERVED
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 isc::log::MessageID ALLOC_ENGINE_V4_DECLINED_RECOVERED
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_SELECT_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_SLOW
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_USE_HR
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_TIMEOUT
boost::shared_ptr< Subnet6 > Subnet6Ptr
A pointer to a Subnet6 object.
Definition: subnet.h:672
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 ALLOC_ENGINE_V4_LEASES_RECLAMATION_START
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
std::vector< PoolPtr > PoolCollection
a container for either IPv4 or IPv6 Pools
Definition: pool.h:508
const isc::log::MessageID ALLOC_ENGINE_V6_RENEW_REMOVE_RESERVED
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:243
const isc::log::MessageID ALLOC_ENGINE_V6_LEASE_RECLAIM
const isc::log::MessageID ALLOC_ENGINE_V4_LEASE_RECLAIM
const isc::log::MessageID ALLOC_ENGINE_V4_DISCOVER_HR
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_TIMEOUT
const isc::log::MessageID ALLOC_ENGINE_V6_HR_ADDR_GRANTED
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_CLASSES
const isc::log::MessageID ALLOC_ENGINE_V6_NO_MORE_EXPIRED_LEASES
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_NEW_LEASE_DATA
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_ERROR
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_EXISTING_LEASE
boost::shared_ptr< Pool > PoolPtr
a pointer to either IPv4 or IPv6 Pool
Definition: pool.h:505
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_NO_POOLS
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_SHARED_NETWORK
const isc::log::MessageID ALLOC_ENGINE_V6_EXPIRED_HINT_RESERVED
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
const int ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA
Records detailed results of various operations.
const int DHCPSRV_DBG_HOOKS
Definition: dhcpsrv_log.h:46
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_COMPLETE
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition: subnet_id.h:24
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition: duid.h:103
const isc::log::MessageID ALLOC_ENGINE_V4_NO_MORE_EXPIRED_LEASES
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_EXTEND_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_PICK_ADDRESS
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_NEW_LEASE
boost::shared_ptr< Option6IAAddr > Option6IAAddrPtr
A pointer to the isc::dhcp::Option6IAAddr object.
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_SELECT_SKIP
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:788
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_RECOVER_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_COMPLETE
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_UNRESERVED
std::pair< IPv6Resrv::Type, IPv6Resrv > IPv6ResrvTuple
Definition: host.h:242
const isc::log::MessageID ALLOC_ENGINE_V6_DECLINED_RECOVERED
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_START
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
const isc::log::MessageID ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition: lease.h:498
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_ALLOC_REQUESTED
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_LEASES_NO_HR
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_SUBNET
const isc::log::MessageID ALLOC_ENGINE_V4_DISCOVER_ADDRESS_CONFLICT
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_RENEW_SKIP
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:284
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_REMOVE_LEASE
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_SUBNET
const int ALLOC_ENGINE_DBG_TRACE_DETAIL
Record detailed traces.
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_SHARED_NETWORK
const isc::log::MessageID ALLOC_ENGINE_V6_HR_PREFIX_GRANTED
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_CLASSES
boost::shared_ptr< Pool6 > Pool6Ptr
a pointer an IPv6 Pool
Definition: pool.h:312
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
string encodeHex(const vector< uint8_t > &binary)
Encode binary data in the base16 ('hex') format.
Definition: base_n.cc:469
Definition: edns.h:19
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 ...
static data::ElementPtr toElement(data::ConstElementPtr map)
Copy an Element map.
Definition: user_context.cc:24
Context information for the DHCPv4 lease allocation.
ClientIdPtr clientid_
Client identifier from the DHCP message.
DdnsParamsPtr getDdnsParams()
Returns the set of DDNS behavioral parameters based on the selected subnet.
bool early_global_reservations_lookup_
Indicates if early global reservation is enabled.
ConstHostPtr currentHost() const
Returns host for currently selected subnet.
Pkt4Ptr query_
A pointer to the client's message.
Subnet4Ptr subnet_
Subnet selected for the client by the server.
Lease4Ptr new_lease_
A pointer to a newly allocated lease.
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
bool rev_dns_update_
Perform reverse DNS update.
bool fake_allocation_
Indicates if this is a real or fake allocation.
hooks::CalloutHandlePtr callout_handle_
Callout handle associated with the client's message.
bool unknown_requested_addr_
True when the address DHCPREQUEST'ed by client is not within a dynamic pool the server knows about.
Lease4Ptr old_lease_
A pointer to an old lease that the client had before update.
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
bool fwd_dns_update_
Perform forward DNS update.
asiolink::IOAddress requested_address_
An address that the client desires.
Lease4Ptr conflicting_lease_
A pointer to the object representing a lease in conflict.
void addHostIdentifier(const Host::IdentifierType &id_type, const std::vector< uint8_t > &identifier)
Convenience function adding host identifier into host_identifiers_ list.
IdentifierList host_identifiers_
A list holding host identifiers extracted from a message received by the server.
HWAddrPtr hwaddr_
HW address from the DHCP message.
Lease::Type type_
Lease type (IA or PD)
Definition: alloc_engine.h:541
bool isNewResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128) const
Checks if specified address or prefix was new.
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.
void addNewResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128)
Convenience method adding new prefix or address.
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
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
ConstHostPtr currentHost() const
Returns host from the most preferred subnet.
DuidPtr duid_
Client identifier.
Definition: alloc_engine.h:490
void addAllocatedResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128)
Convenience method adding allocated prefix or address.
Lease6Collection new_leases_
A collection of newly allocated leases.
Definition: alloc_engine.h:529
bool isAllocated(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128) const
Checks if specified address or prefix was allocated.
Subnet6Ptr subnet_
Subnet selected for the client by the server.
Definition: alloc_engine.h:482
Subnet6Ptr host_subnet_
Subnet from which host reservations should be retrieved.
Definition: alloc_engine.h:487
bool hasGlobalReservation(const IPv6Resrv &resv) const
Determines if a global reservation exists.
ResourceContainer allocated_resources_
Holds addresses and prefixes allocated for all IAs.
Definition: alloc_engine.h:526
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
IdentifierList host_identifiers_
A list holding host identifiers extracted from a message received by the server.
Definition: alloc_engine.h:497
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
Definition: alloc_engine.h:504
Structure that holds a lease for IPv4 address.
Definition: lease.h:295
Structure that holds a lease for IPv6 address and/or prefix.
Definition: lease.h:514
a common structure for IPv4 and IPv6 leases
Definition: lease.h:31
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire)
Definition: lease.h:34
static const uint32_t STATE_DEFAULT
A lease in the default state.
Definition: lease.h:69
static const uint32_t STATE_DECLINED
Declined lease.
Definition: lease.h:72
static const uint32_t STATE_EXPIRED_RECLAIMED
Expired and reclaimed lease.
Definition: lease.h:75
Type
Type of lease or pool.
Definition: lease.h:46
@ TYPE_TA
the lease contains temporary IPv6 address
Definition: lease.h:48
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition: lease.h:49
@ TYPE_V4
IPv4 lease.
Definition: lease.h:50
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition: lease.h:47
static std::string typeToText(Type type)
returns text representation of a lease type
Definition: lease.cc:52