Kea 2.0.2
alloc_engine.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2021 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), subnet_(), host_subnet_(), duid_(),
460 hwaddr_(), host_identifiers_(), hosts_(), fwd_dns_update_(false),
461 rev_dns_update_(false), hostname_(), callout_handle_(), ias_(),
462 ddns_params_() {
463}
464
466 const DuidPtr& duid,
467 const bool fwd_dns,
468 const bool rev_dns,
469 const std::string& hostname,
470 const bool fake_allocation,
471 const Pkt6Ptr& query,
472 const CalloutHandlePtr& callout_handle)
473 : query_(query), fake_allocation_(fake_allocation), subnet_(subnet),
474 duid_(duid), hwaddr_(), host_identifiers_(), hosts_(),
475 fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname),
476 callout_handle_(callout_handle), allocated_resources_(), new_leases_(),
477 ias_(), ddns_params_() {
478
479 // Initialize host identifiers.
480 if (duid) {
481 addHostIdentifier(Host::IDENT_DUID, duid->getDuid());
482 }
483}
484
486 : iaid_(0), type_(Lease::TYPE_NA), hints_(), old_leases_(),
487 changed_leases_(), new_resources_(), ia_rsp_() {
488}
489
490void
493 const uint8_t prefix_len,
494 const uint32_t preferred,
495 const uint32_t valid) {
496 hints_.push_back(Resource(prefix, prefix_len, preferred, valid));
497}
498
499void
502 if (!iaaddr) {
503 isc_throw(BadValue, "IAADDR option pointer is null.");
504 }
505 addHint(iaaddr->getAddress(), 128,
506 iaaddr->getPreferred(), iaaddr->getValid());
507}
508
509void
511IAContext::addHint(const Option6IAPrefixPtr& iaprefix) {
512 if (!iaprefix) {
513 isc_throw(BadValue, "IAPREFIX option pointer is null.");
514 }
515 addHint(iaprefix->getAddress(), iaprefix->getLength(),
516 iaprefix->getPreferred(), iaprefix->getValid());
517}
518
519void
522 const uint8_t prefix_len) {
523 static_cast<void>(new_resources_.insert(Resource(prefix, prefix_len)));
524}
525
526bool
529 const uint8_t prefix_len) const {
530 return (static_cast<bool>(new_resources_.count(Resource(prefix,
531 prefix_len))));
532}
533
534void
537 const uint8_t prefix_len) {
538 static_cast<void>(allocated_resources_.insert(Resource(prefix,
539 prefix_len)));
540}
541
542bool
544isAllocated(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const {
545 return (static_cast<bool>
546 (allocated_resources_.count(Resource(prefix, prefix_len))));
547}
548
552 if (subnet && subnet->getReservationsInSubnet()) {
553 auto host = hosts_.find(subnet->getID());
554 if (host != hosts_.cend()) {
555 return (host->second);
556 }
557 }
558
559 return (globalHost());
560}
561
565 if (subnet && subnet_->getReservationsGlobal()) {
566 auto host = hosts_.find(SUBNET_ID_GLOBAL);
567 if (host != hosts_.cend()) {
568 return (host->second);
569 }
570 }
571
572 return (ConstHostPtr());
573}
574
575bool
577 ConstHostPtr ghost = globalHost();
578 return (ghost && ghost->hasReservation(resv));
579}
580
583 // We already have it return it unless the context subnet has changed.
584 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
585 return (ddns_params_);
586 }
587
588 // Doesn't exist yet or is stale, (re)create it.
589 if (subnet_) {
590 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
591 return (ddns_params_);
592 }
593
594 // Asked for it without a subnet? This case really shouldn't occur but
595 // for now let's return an instance with default values.
596 return (DdnsParamsPtr(new DdnsParams()));
597}
598
599void
601 ctx.hosts_.clear();
602
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 std::map<SubnetID, ConstHostPtr> host_map;
611 SharedNetwork6Ptr network;
612 subnet->getSharedNetwork(network);
613
614 // @todo: This code can be trivially optimized.
615 if (subnet->getReservationsGlobal()) {
617 if (ghost) {
618 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
619
620 // If we had only to fetch global reservations it is done.
621 if (!subnet->getReservationsInSubnet()) {
622 return;
623 }
624 }
625 }
626
627 // If the subnet belongs to a shared network it is usually going to be
628 // more efficient to make a query for all reservations for a particular
629 // client rather than a query for each subnet within this shared network.
630 // The only case when it is going to be less efficient is when there are
631 // more host identifier types in use than subnets within a shared network.
632 // As it breaks RADIUS use of host caching this can be disabled by the
633 // host manager.
634 const bool use_single_query = network &&
636 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
637
638 if (use_single_query) {
639 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
640 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
641 &id_pair.second[0],
642 id_pair.second.size());
643 // Store the hosts in the temporary map, because some hosts may
644 // belong to subnets outside of the shared network. We'll need
645 // to eliminate them.
646 for (auto host = hosts.begin(); host != hosts.end(); ++host) {
647 if ((*host)->getIPv6SubnetID() != SUBNET_ID_GLOBAL) {
648 host_map[(*host)->getIPv6SubnetID()] = *host;
649 }
650 }
651 }
652 }
653
654 // We can only search for the reservation if a subnet has been selected.
655 while (subnet) {
656
657 // Only makes sense to get reservations if the client has access
658 // to the class and host reservations are enabled for this subnet.
659 if (subnet->clientSupported(ctx.query_->getClasses()) &&
660 subnet->getReservationsInSubnet()) {
661 // Iterate over configured identifiers in the order of preference
662 // and try to use each of them to search for the reservations.
663 if (use_single_query) {
664 if (host_map.count(subnet->getID()) > 0) {
665 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
666 }
667 } else {
668 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
669 // Attempt to find a host using a specified identifier.
670 ConstHostPtr host = HostMgr::instance().get6(subnet->getID(),
671 id_pair.first,
672 &id_pair.second[0],
673 id_pair.second.size());
674 // If we found matching host for this subnet.
675 if (host) {
676 ctx.hosts_[subnet->getID()] = host;
677 break;
678 }
679 }
680 }
681 }
682
683 // We need to get to the next subnet if this is a shared network. If it
684 // is not (a plain subnet), getNextSubnet will return NULL and we're
685 // done here.
686 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
687 }
688}
689
692 ConstHostPtr host;
693 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
694 // Attempt to find a host using a specified identifier.
695 host = HostMgr::instance().get6(SUBNET_ID_GLOBAL, id_pair.first,
696 &id_pair.second[0], id_pair.second.size());
697
698 // If we found matching global host we're done.
699 if (host) {
700 break;
701 }
702 }
703
704 return (host);
705}
706
709
710 try {
711 if (!ctx.subnet_) {
712 isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation");
713 } else
714 if (!ctx.duid_) {
715 isc_throw(InvalidOperation, "DUID is mandatory for IPv6 lease allocation");
716 }
717
718 // Check if there are existing leases for that shared network and
719 // DUID/IAID.
720 Subnet6Ptr subnet = ctx.subnet_;
721 Lease6Collection all_leases =
723 *ctx.duid_,
724 ctx.currentIA().iaid_);
725
726 // Iterate over the leases and eliminate those that are outside of
727 // our shared network.
728 Lease6Collection leases;
729 while (subnet) {
730 for (auto l : all_leases) {
731 if ((l)->subnet_id_ == subnet->getID()) {
732 leases.push_back(l);
733 }
734 }
735
736 subnet = subnet->getNextSubnet(ctx.subnet_);
737 }
738
739 // Now do the checks:
740 // Case 1. if there are no leases, and there are reservations...
741 // 1.1. are the reserved addresses are used by someone else?
742 // yes: we have a problem
743 // no: assign them => done
744 // Case 2. if there are leases and there are no reservations...
745 // 2.1 are the leases reserved for someone else?
746 // yes: release them, assign something else
747 // no: renew them => done
748 // Case 3. if there are leases and there are reservations...
749 // 3.1 are the leases matching reservations?
750 // yes: renew them => done
751 // no: release existing leases, assign new ones based on reservations
752 // Case 4/catch-all. if there are no leases and no reservations...
753 // assign new leases
754
755 // Case 1: There are no leases and there's a reservation for this host.
756 if (leases.empty() && !ctx.hosts_.empty()) {
757
760 .arg(ctx.query_->getLabel());
761
762 // Try to allocate leases that match reservations. Typically this will
763 // succeed, except cases where the reserved addresses are used by
764 // someone else.
765 allocateReservedLeases6(ctx, leases);
766
767 leases = updateLeaseData(ctx, leases);
768
769 // If not, we'll need to continue and will eventually fall into case 4:
770 // getting a regular lease. That could happen when we're processing
771 // request from client X, there's a reserved address A for X, but
772 // A is currently used by client Y. We can't immediately reassign A
773 // from X to Y, because Y keeps using it, so X would send Decline right
774 // away. Need to wait till Y renews, then we can release A, so it
775 // will become available for X.
776
777 // Case 2: There are existing leases and there are no reservations.
778 //
779 // There is at least one lease for this client and there are no reservations.
780 // We will return these leases for the client, but we may need to update
781 // FQDN information.
782 } else if (!leases.empty() && ctx.hosts_.empty()) {
783
786 .arg(ctx.query_->getLabel());
787
788 // Check if the existing leases are reserved for someone else.
789 // If they're not, we're ok to keep using them.
790 removeNonmatchingReservedLeases6(ctx, leases);
791
792 leases = updateLeaseData(ctx, leases);
793
794 // If leases are empty at this stage, it means that we used to have
795 // leases for this client, but we checked and those leases are reserved
796 // for someone else, so we lost them. We will need to continue and
797 // will finally end up in case 4 (no leases, no reservations), so we'll
798 // assign something new.
799
800 // Case 3: There are leases and there are reservations.
801 } else if (!leases.empty() && !ctx.hosts_.empty()) {
802
805 .arg(ctx.query_->getLabel());
806
807 // First, check if have leases matching reservations, and add new
808 // leases if we don't have them.
809 allocateReservedLeases6(ctx, leases);
810
811 // leases now contain both existing and new leases that were created
812 // from reservations.
813
814 // Second, let's remove leases that are reserved for someone else.
815 // This applies to any existing leases. This will not happen frequently,
816 // but it may happen with the following chain of events:
817 // 1. client A gets address X;
818 // 2. reservation for client B for address X is made by a administrator;
819 // 3. client A reboots
820 // 4. client A requests the address (X) he got previously
821 removeNonmatchingReservedLeases6(ctx, leases);
822
823 // leases now contain existing and new leases, but we removed those
824 // leases that are reserved for someone else (non-matching reserved).
825
826 // There's one more check to do. Let's remove leases that are not
827 // matching reservations, i.e. if client X has address A, but there's
828 // a reservation for address B, we should release A and reassign B.
829 // Caveat: do this only if we have at least one reserved address.
830 removeNonreservedLeases6(ctx, leases);
831
832 // All checks are done. Let's hope we have some leases left.
833
834 // Update any leases we have left.
835 leases = updateLeaseData(ctx, leases);
836
837 // If we don't have any leases at this stage, it means that we hit
838 // one of the following cases:
839 // - we have a reservation, but it's not for this IAID/ia-type and
840 // we had to return the address we were using
841 // - we have a reservation for this iaid/ia-type, but the reserved
842 // address is currently used by someone else. We can't assign it
843 // yet.
844 // - we had an address, but we just discovered that it's reserved for
845 // someone else, so we released it.
846 }
847
848 if (leases.empty()) {
849 // Case 4/catch-all: One of the following is true:
850 // - we don't have leases and there are no reservations
851 // - we used to have leases, but we lost them, because they are now
852 // reserved for someone else
853 // - we have a reservation, but it is not usable yet, because the address
854 // is still used by someone else
855 //
856 // In any case, we need to go through normal lease assignment process
857 // for now. This is also a catch-all or last resort approach, when we
858 // couldn't find any reservations (or couldn't use them).
859
862 .arg(ctx.query_->getLabel());
863
864 leases = allocateUnreservedLeases6(ctx);
865 }
866
867 if (!leases.empty()) {
868 // If there are any leases allocated, let's store in them in the
869 // IA context so as they are available when we process subsequent
870 // IAs.
871 BOOST_FOREACH(Lease6Ptr lease, leases) {
872 ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
873 ctx.new_leases_.push_back(lease);
874 }
875 return (leases);
876 }
877
878 } catch (const isc::Exception& e) {
879
880 // Some other error, return an empty lease.
882 .arg(ctx.query_->getLabel())
883 .arg(e.what());
884 }
885
886 return (Lease6Collection());
887}
888
890AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
891
892 AllocatorPtr allocator = getAllocator(ctx.currentIA().type_);
893
894 if (!allocator) {
895 isc_throw(InvalidOperation, "No allocator specified for "
896 << Lease6::typeToText(ctx.currentIA().type_));
897 }
898
899 Lease6Collection leases;
900
902 if (!ctx.currentIA().hints_.empty()) {
904 hint = ctx.currentIA().hints_[0].getAddress();
905 }
906
907 Subnet6Ptr original_subnet = ctx.subnet_;
908 Subnet6Ptr subnet = ctx.subnet_;
909
910 Pool6Ptr pool;
911
912 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
913
914 for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
915
916 if (!subnet->clientSupported(ctx.query_->getClasses())) {
917 continue;
918 }
919
920 ctx.subnet_ = subnet;
921
922 // check if the hint is in pool and is available
923 // This is equivalent of subnet->inPool(hint), but returns the pool
924 pool = boost::dynamic_pointer_cast<Pool6>
925 (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(),
926 hint));
927
928 // check if the pool is allowed
929 if (!pool || !pool->clientSupported(ctx.query_->getClasses())) {
930 continue;
931 }
932
933 bool in_subnet = subnet->getReservationsInSubnet();
934
936 Lease6Ptr lease =
937 LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, hint);
938 if (!lease) {
939
940 // In-pool reservations: Check if this address is reserved for someone
941 // else. There is no need to check for whom it is reserved, because if
942 // it has been reserved for us we would have already allocated a lease.
943
945 // When out-of-pool flag is true the server may assume that all host
946 // reservations are for addresses that do not belong to the dynamic
947 // pool. Therefore, it can skip the reservation checks when dealing
948 // with in-pool addresses.
949 if (in_subnet &&
950 (!subnet->getReservationsOutOfPool() ||
951 !subnet->inPool(ctx.currentIA().type_, hint))) {
952 hosts = getIPv6Resrv(subnet->getID(), hint);
953 }
954
955 if (hosts.empty()) {
956 // If the in-pool reservations are disabled, or there is no
957 // reservation for a given hint, we're good to go.
958
959 // The hint is valid and not currently used, let's create a
960 // lease for it
961 lease = createLease6(ctx, hint, pool->getLength(), callout_status);
962
963 // It can happen that the lease allocation failed (we could
964 // have lost the race condition. That means that the hint is
965 // no longer usable and we need to continue the regular
966 // allocation path.
967 if (lease) {
968
970 Lease6Collection collection;
971 collection.push_back(lease);
972 return (collection);
973 }
974 } else {
977 .arg(ctx.query_->getLabel())
978 .arg(hint.toText());
979 }
980
981 } else if (lease->expired()) {
982
983 // If the lease is expired, we may likely reuse it, but...
985 // When out-of-pool flag is true the server may assume that all host
986 // reservations are for addresses that do not belong to the dynamic
987 // pool. Therefore, it can skip the reservation checks when dealing
988 // with in-pool addresses.
989 if (in_subnet &&
990 (!subnet->getReservationsOutOfPool() ||
991 !subnet->inPool(ctx.currentIA().type_, hint))) {
992 hosts = getIPv6Resrv(subnet->getID(), hint);
993 }
994
995 // Let's check if there is a reservation for this address.
996 if (hosts.empty()) {
997
998 // Copy an existing, expired lease so as it can be returned
999 // to the caller.
1000 Lease6Ptr old_lease(new Lease6(*lease));
1001 ctx.currentIA().old_leases_.push_back(old_lease);
1002
1004 lease = reuseExpiredLease(lease, ctx, pool->getLength(),
1005 callout_status);
1006
1008 leases.push_back(lease);
1009 return (leases);
1010
1011 } else {
1014 .arg(ctx.query_->getLabel())
1015 .arg(hint.toText());
1016 }
1017 }
1018 }
1019
1020 // We have the choice in the order checking the lease and
1021 // the reservation. The default is to begin by the lease
1022 // if the multi-threading is disabled.
1023 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
1024
1025 uint64_t total_attempts = 0;
1026
1027 // Need to check if the subnet belongs to a shared network. If so,
1028 // we might be able to find a better subnet for lease allocation,
1029 // for which it is more likely that there are some leases available.
1030 // If we stick to the selected subnet, we may end up walking over
1031 // the entire subnet (or more subnets) to discover that the pools
1032 // have been exhausted. Using a subnet from which a lease was
1033 // assigned most recently is an optimization which increases
1034 // the likelihood of starting from the subnet which pools are not
1035 // exhausted.
1036 SharedNetwork6Ptr network;
1037 original_subnet->getSharedNetwork(network);
1038 if (network) {
1039 // This would try to find a subnet with the same set of classes
1040 // as the current subnet, but with the more recent "usage timestamp".
1041 // This timestamp is only updated for the allocations made with an
1042 // allocator (unreserved lease allocations), not the static
1043 // allocations or requested addresses.
1044 original_subnet = network->getPreferredSubnet(original_subnet, ctx.currentIA().type_);
1045 }
1046
1047 ctx.subnet_ = subnet = original_subnet;
1048
1049 // The following counter tracks the number of subnets with matching client
1050 // classes from which the allocation engine attempted to assign leases.
1051 uint64_t subnets_with_unavail_leases = 0;
1052 // The following counter tracks the number of subnets in which there were
1053 // no matching pools for the client.
1054 uint64_t subnets_with_unavail_pools = 0;
1055
1056 for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
1057
1058 if (!subnet->clientSupported(ctx.query_->getClasses())) {
1059 continue;
1060 }
1061
1062 // The hint was useless (it was not provided at all, was used by someone else,
1063 // was out of pool or reserved for someone else). Search the pool until first
1064 // of the following occurs:
1065 // - we find a free address
1066 // - we find an address for which the lease has expired
1067 // - we exhaust number of tries
1068 uint64_t possible_attempts =
1069 subnet->getPoolCapacity(ctx.currentIA().type_,
1070 ctx.query_->getClasses());
1071
1072 // If the number of tries specified in the allocation engine constructor
1073 // is set to 0 (unlimited) or the pools capacity is lower than that number,
1074 // let's use the pools capacity as the maximum number of tries. Trying
1075 // more than the actual pools capacity is a waste of time. If the specified
1076 // number of tries is lower than the pools capacity, use that number.
1077 uint64_t max_attempts = ((attempts_ == 0) || (possible_attempts < attempts_)) ? possible_attempts : attempts_;
1078
1079 if (max_attempts > 0) {
1080 // If max_attempts is greater than 0, there are some pools in this subnet
1081 // from which we can potentially get a lease.
1082 ++subnets_with_unavail_leases;
1083 } else {
1084 // If max_attempts is 0, it is an indication that there are no pools
1085 // in the subnet from which we can get a lease.
1086 ++subnets_with_unavail_pools;
1087 continue;
1088 }
1089
1090 bool in_subnet = subnet->getReservationsInSubnet();
1091 bool out_of_pool = subnet->getReservationsOutOfPool();
1092
1093 // Set the default status code in case the lease6_select callouts
1094 // do not exist and the callout handle has a status returned by
1095 // any of the callouts already invoked for this packet.
1096 if (ctx.callout_handle_) {
1097 ctx.callout_handle_->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
1098 }
1099
1100 for (uint64_t i = 0; i < max_attempts; ++i) {
1101
1102 ++total_attempts;
1103
1104 IOAddress candidate = allocator->pickAddress(subnet,
1105 ctx.query_->getClasses(),
1106 ctx.duid_,
1107 hint);
1108 // The first step is to find out prefix length. It is 128 for
1109 // non-PD leases.
1110 uint8_t prefix_len = 128;
1111 if (ctx.currentIA().type_ == Lease::TYPE_PD) {
1112 pool = boost::dynamic_pointer_cast<Pool6>(
1113 subnet->getPool(ctx.currentIA().type_,
1114 ctx.query_->getClasses(),
1115 candidate));
1116 if (pool) {
1117 prefix_len = pool->getLength();
1118 }
1119 }
1120
1121 // First check for reservation when it is the choice.
1122 if (check_reservation_first && in_subnet && !out_of_pool) {
1123 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1124 if (!hosts.empty()) {
1125 // Don't allocate.
1126 continue;
1127 }
1128 }
1129
1130 // Check if the resource is busy i.e. can be being allocated
1131 // by another thread to another client.
1132 ResourceHandler resource_handler;
1133 if (MultiThreadingMgr::instance().getMode() &&
1134 !resource_handler.tryLock(ctx.currentIA().type_, candidate)) {
1135 // Don't allocate.
1136 continue;
1137 }
1138
1139 // Look for an existing lease for the candidate.
1140 Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1141 candidate);
1142
1143 if (!existing) {
1147 if (!check_reservation_first && in_subnet && !out_of_pool) {
1148 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1149 if (!hosts.empty()) {
1150 // Don't allocate.
1151 continue;
1152 }
1153 }
1154
1155 // there's no existing lease for selected candidate, so it is
1156 // free. Let's allocate it.
1157
1158 ctx.subnet_ = subnet;
1159 Lease6Ptr lease = createLease6(ctx, candidate, prefix_len, callout_status);
1160 if (lease) {
1161 // We are allocating a new lease (not renewing). So, the
1162 // old lease should be NULL.
1163 ctx.currentIA().old_leases_.clear();
1164
1165 leases.push_back(lease);
1166 return (leases);
1167
1168 } else if (ctx.callout_handle_ &&
1169 (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
1170 // Don't retry when the callout status is not continue.
1171 break;
1172 }
1173
1174 // Although the address was free just microseconds ago, it may have
1175 // been taken just now. If the lease insertion fails, we continue
1176 // allocation attempts.
1177 } else if (existing->expired()) {
1178 // Make sure it's not reserved.
1179 if (!check_reservation_first && in_subnet && !out_of_pool) {
1180 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1181 if (!hosts.empty()) {
1182 // Don't allocate.
1183 continue;
1184 }
1185 }
1186
1187 // Copy an existing, expired lease so as it can be returned
1188 // to the caller.
1189 Lease6Ptr old_lease(new Lease6(*existing));
1190 ctx.currentIA().old_leases_.push_back(old_lease);
1191
1192 ctx.subnet_ = subnet;
1193 existing = reuseExpiredLease(existing, ctx, prefix_len,
1194 callout_status);
1195
1196 leases.push_back(existing);
1197 return (leases);
1198 }
1199 }
1200 }
1201
1202 if (network) {
1203 // The client is in the shared network. Let's log the high level message
1204 // indicating which shared network the client belongs to.
1206 .arg(ctx.query_->getLabel())
1207 .arg(network->getName())
1208 .arg(subnets_with_unavail_leases)
1209 .arg(subnets_with_unavail_pools);
1210
1211 } else {
1212 // The client is not connected to a shared network. It is connected
1213 // to a subnet. Let's log the ID of that subnet.
1215 .arg(ctx.query_->getLabel())
1216 .arg(ctx.subnet_->getID());
1217 }
1218 if (total_attempts == 0) {
1219 // In this case, it seems that none of the pools in the subnets could
1220 // be used for that client, both in case the client is connected to
1221 // a shared network or to a single subnet. Apparently, the client was
1222 // rejected to use the pools because of the client classes' mismatch.
1224 .arg(ctx.query_->getLabel());
1225 } else {
1226 // This is an old log message which provides a number of attempts
1227 // made by the allocation engine to allocate a lease. The only case
1228 // when we don't want to log this message is when the number of
1229 // attempts is zero (condition above), because it would look silly.
1231 .arg(ctx.query_->getLabel())
1232 .arg(total_attempts);
1233 }
1234
1235 // We failed to allocate anything. Let's return empty collection.
1236 return (Lease6Collection());
1237}
1238
1239void
1240AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
1241 Lease6Collection& existing_leases) {
1242
1243 // If there are no reservations or the reservation is v4, there's nothing to do.
1244 if (ctx.hosts_.empty()) {
1247 .arg(ctx.query_->getLabel());
1248 return;
1249 }
1250
1251 // Let's convert this from Lease::Type to IPv6Reserv::Type
1252 IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1254
1255 // We want to avoid allocating new lease for an IA if there is already
1256 // a valid lease for which client has reservation. So, we first check if
1257 // we already have a lease for a reserved address or prefix.
1258 BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
1259 if ((lease->valid_lft_ != 0)) {
1260 if ((ctx.hosts_.count(lease->subnet_id_) > 0) &&
1261 ctx.hosts_[lease->subnet_id_]->hasReservation(makeIPv6Resrv(*lease))) {
1262 // We found existing lease for a reserved address or prefix.
1263 // We'll simply extend the lifetime of the lease.
1266 .arg(ctx.query_->getLabel())
1267 .arg(lease->typeToText(lease->type_))
1268 .arg(lease->addr_.toText());
1269
1270 // Besides IP reservations we're also going to return other reserved
1271 // parameters, such as hostname. We want to hand out the hostname value
1272 // from the same reservation entry as IP addresses. Thus, let's see if
1273 // there is any hostname reservation.
1274 if (!ctx.host_subnet_) {
1275 SharedNetwork6Ptr network;
1276 ctx.subnet_->getSharedNetwork(network);
1277 if (network) {
1278 // Remember the subnet that holds this preferred host
1279 // reservation. The server will use it to return appropriate
1280 // FQDN, classes etc.
1281 ctx.host_subnet_ = network->getSubnet(lease->subnet_id_);
1282 ConstHostPtr host = ctx.hosts_[lease->subnet_id_];
1283 // If there is a hostname reservation here we should stick
1284 // to this reservation. By updating the hostname in the
1285 // context we make sure that the database is updated with
1286 // this new value and the server doesn't need to do it and
1287 // its processing performance is not impacted by the hostname
1288 // updates.
1289 if (host && !host->getHostname().empty()) {
1290 // We have to determine whether the hostname is generated
1291 // in response to client's FQDN or not. If yes, we will
1292 // need to qualify the hostname. Otherwise, we just use
1293 // the hostname as it is specified for the reservation.
1294 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1295 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1296 qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1297 static_cast<bool>(fqdn));
1298 }
1299 }
1300 }
1301
1302 // Got a lease for a reservation in this IA.
1303 return;
1304 }
1305 }
1306 }
1307
1308 // There is no lease for a reservation in this IA. So, let's now iterate
1309 // over reservations specified and try to allocate one of them for the IA.
1310
1311 for (Subnet6Ptr subnet = ctx.subnet_; subnet;
1312 subnet = subnet->getNextSubnet(ctx.subnet_)) {
1313
1314 SubnetID subnet_id = subnet->getID();
1315
1316 // No hosts for this subnet or the subnet not supported.
1317 if (!subnet->clientSupported(ctx.query_->getClasses()) ||
1318 ctx.hosts_.count(subnet_id) == 0) {
1319 continue;
1320 }
1321
1322 ConstHostPtr host = ctx.hosts_[subnet_id];
1323
1324 bool in_subnet = subnet->getReservationsInSubnet();
1325
1326 // Get the IPv6 reservations of specified type.
1327 const IPv6ResrvRange& reservs = host->getIPv6Reservations(type);
1328 BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
1329 // We do have a reservation for address or prefix.
1330 const IOAddress& addr = type_lease_tuple.second.getPrefix();
1331 uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1332
1333 // We have allocated this address/prefix while processing one of the
1334 // previous IAs, so let's try another reservation.
1335 if (ctx.isAllocated(addr, prefix_len)) {
1336 continue;
1337 }
1338
1339 // The out-of-pool flag indicates that no client should be assigned
1340 // reserved addresses from within the dynamic pool, and for that
1341 // reason look only for reservations that are outside the pools,
1342 // hence the inPool check.
1343 if (!in_subnet ||
1344 (subnet->getReservationsOutOfPool() &&
1345 subnet->inPool(ctx.currentIA().type_, addr))) {
1346 continue;
1347 }
1348
1349 // If there's a lease for this address, let's not create it.
1350 // It doesn't matter whether it is for this client or for someone else.
1351 if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1352 addr)) {
1353
1354 // Let's remember the subnet from which the reserved address has been
1355 // allocated. We'll use this subnet for allocating other reserved
1356 // resources.
1357 ctx.subnet_ = subnet;
1358
1359 if (!ctx.host_subnet_) {
1360 ctx.host_subnet_ = subnet;
1361 if (!host->getHostname().empty()) {
1362 // If there is a hostname reservation here we should stick
1363 // to this reservation. By updating the hostname in the
1364 // context we make sure that the database is updated with
1365 // this new value and the server doesn't need to do it and
1366 // its processing performance is not impacted by the hostname
1367 // updates.
1368
1369 // We have to determine whether the hostname is generated
1370 // in response to client's FQDN or not. If yes, we will
1371 // need to qualify the hostname. Otherwise, we just use
1372 // the hostname as it is specified for the reservation.
1373 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1374 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1375 qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1376 static_cast<bool>(fqdn));
1377 }
1378 }
1379
1380 // Ok, let's create a new lease...
1381 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
1382 Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1383
1384 // ... and add it to the existing leases list.
1385 existing_leases.push_back(lease);
1386
1387 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1389 .arg(addr.toText())
1390 .arg(ctx.query_->getLabel());
1391 } else {
1393 .arg(addr.toText())
1394 .arg(static_cast<int>(prefix_len))
1395 .arg(ctx.query_->getLabel());
1396 }
1397
1398 // We found a lease for this client and this IA. Let's return.
1399 // Returning after the first lease was assigned is useful if we
1400 // have multiple reservations for the same client. If the client
1401 // sends 2 IAs, the first time we call allocateReservedLeases6 will
1402 // use the first reservation and return. The second time, we'll
1403 // go over the first reservation, but will discover that there's
1404 // a lease corresponding to it and will skip it and then pick
1405 // the second reservation and turn it into the lease. This approach
1406 // would work for any number of reservations.
1407 return;
1408 }
1409 }
1410 }
1411
1412 // Found no subnet reservations so now try the global reservation.
1413 allocateGlobalReservedLeases6(ctx, existing_leases);
1414}
1415
1416void
1417AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
1418 Lease6Collection& existing_leases) {
1419 // Get the global host
1420 ConstHostPtr ghost = ctx.globalHost();
1421 if (!ghost) {
1422 return;
1423 }
1424
1425 // We want to avoid allocating a new lease for an IA if there is already
1426 // a valid lease for which client has reservation. So, we first check if
1427 // we already have a lease for a reserved address or prefix.
1428 BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
1429 if ((lease->valid_lft_ != 0) &&
1430 (ghost->hasReservation(makeIPv6Resrv(*lease)))) {
1431 // We found existing lease for a reserved address or prefix.
1432 // We'll simply extend the lifetime of the lease.
1435 .arg(ctx.query_->getLabel())
1436 .arg(lease->typeToText(lease->type_))
1437 .arg(lease->addr_.toText());
1438
1439 // Besides IP reservations we're also going to return other reserved
1440 // parameters, such as hostname. We want to hand out the hostname value
1441 // from the same reservation entry as IP addresses. Thus, let's see if
1442 // there is any hostname reservation.
1443 if (!ghost->getHostname().empty()) {
1444 // We have to determine whether the hostname is generated
1445 // in response to client's FQDN or not. If yes, we will
1446 // need to qualify the hostname. Otherwise, we just use
1447 // the hostname as it is specified for the reservation.
1448 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1449 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1450 qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1451 static_cast<bool>(fqdn));
1452 }
1453
1454 // Got a lease for a reservation in this IA.
1455 return;
1456 }
1457 }
1458
1459 // There is no lease for a reservation in this IA. So, let's now iterate
1460 // over reservations specified and try to allocate one of them for the IA.
1461
1462 // Let's convert this from Lease::Type to IPv6Reserv::Type
1463 IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1465
1466 const IPv6ResrvRange& reservs = ghost->getIPv6Reservations(type);
1467 BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
1468 // We do have a reservation for address or prefix.
1469 const IOAddress& addr = type_lease_tuple.second.getPrefix();
1470 uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1471
1472 // We have allocated this address/prefix while processing one of the
1473 // previous IAs, so let's try another reservation.
1474 if (ctx.isAllocated(addr, prefix_len)) {
1475 continue;
1476 }
1477
1478 // If there's a lease for this address, let's not create it.
1479 // It doesn't matter whether it is for this client or for someone else.
1480 if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
1481
1482 if (!ghost->getHostname().empty()) {
1483 // If there is a hostname reservation here we should stick
1484 // to this reservation. By updating the hostname in the
1485 // context we make sure that the database is updated with
1486 // this new value and the server doesn't need to do it and
1487 // its processing performance is not impacted by the hostname
1488 // updates.
1489
1490 // We have to determine whether the hostname is generated
1491 // in response to client's FQDN or not. If yes, we will
1492 // need to qualify the hostname. Otherwise, we just use
1493 // the hostname as it is specified for the reservation.
1494 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1495 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1496 qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1497 static_cast<bool>(fqdn));
1498 }
1499
1500 // Ok, let's create a new lease...
1501 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
1502 Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1503
1504 // ... and add it to the existing leases list.
1505 existing_leases.push_back(lease);
1506
1507 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1509 .arg(addr.toText())
1510 .arg(ctx.query_->getLabel());
1511 } else {
1513 .arg(addr.toText())
1514 .arg(static_cast<int>(prefix_len))
1515 .arg(ctx.query_->getLabel());
1516 }
1517
1518 // We found a lease for this client and this IA. Let's return.
1519 // Returning after the first lease was assigned is useful if we
1520 // have multiple reservations for the same client. If the client
1521 // sends 2 IAs, the first time we call allocateReservedLeases6 will
1522 // use the first reservation and return. The second time, we'll
1523 // go over the first reservation, but will discover that there's
1524 // a lease corresponding to it and will skip it and then pick
1525 // the second reservation and turn it into the lease. This approach
1526 // would work for any number of reservations.
1527 return;
1528 }
1529 }
1530}
1531
1532void
1533AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
1534 Lease6Collection& existing_leases) {
1535 // If there are no leases (so nothing to remove) just return.
1536 if (existing_leases.empty() || !ctx.subnet_) {
1537 return;
1538 }
1539 // If host reservation is disabled (so there are no reserved leases)
1540 // use the simplified version.
1541 if (!ctx.subnet_->getReservationsInSubnet() &&
1542 !ctx.subnet_->getReservationsGlobal()) {
1543 removeNonmatchingReservedNoHostLeases6(ctx, existing_leases);
1544 return;
1545 }
1546
1547 // We need a copy, so we won't be iterating over a container and
1548 // removing from it at the same time. It's only a copy of pointers,
1549 // so the operation shouldn't be that expensive.
1550 Lease6Collection copy = existing_leases;
1551
1552 BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
1553 // If we have reservation we should check if the reservation is for
1554 // the candidate lease. If so, we simply accept the lease.
1555 IPv6Resrv resv = makeIPv6Resrv(*candidate);
1556 if ((ctx.hasGlobalReservation(resv)) ||
1557 ((ctx.hosts_.count(candidate->subnet_id_) > 0) &&
1558 (ctx.hosts_[candidate->subnet_id_]->hasReservation(resv)))) {
1559 // We have a subnet reservation
1560 continue;
1561 }
1562
1563 // The candidate address doesn't appear to be reserved for us.
1564 // We have to make a bit more expensive operation here to retrieve
1565 // the reservation for the candidate lease and see if it is
1566 // reserved for someone else.
1567 auto hosts = getIPv6Resrv(ctx.subnet_->getID(), candidate->addr_);
1568 // If lease is not reserved to someone else, it means that it can
1569 // be allocated to us from a dynamic pool, but we must check if
1570 // this lease belongs to any pool. If it does, we can proceed to
1571 // checking the next lease.
1572 if (hosts.empty() && inAllowedPool(ctx, candidate->type_,
1573 candidate->addr_, false)) {
1574 continue;
1575 }
1576
1577 if (!hosts.empty()) {
1578 // Ok, we have a problem. This host has a lease that is reserved
1579 // for someone else. We need to recover from this.
1580 if (hosts.size() == 1) {
1581 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1583 .arg(candidate->addr_.toText())
1584 .arg(ctx.duid_->toText())
1585 .arg(hosts.front()->getIdentifierAsText());
1586 } else {
1588 .arg(candidate->addr_.toText())
1589 .arg(static_cast<int>(candidate->prefixlen_))
1590 .arg(ctx.duid_->toText())
1591 .arg(hosts.front()->getIdentifierAsText());
1592 }
1593 } else {
1594 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1596 .arg(candidate->addr_.toText())
1597 .arg(ctx.duid_->toText())
1598 .arg(hosts.size());
1599 } else {
1601 .arg(candidate->addr_.toText())
1602 .arg(static_cast<int>(candidate->prefixlen_))
1603 .arg(ctx.duid_->toText())
1604 .arg(hosts.size());
1605 }
1606 }
1607 }
1608
1609 // Remove this lease from LeaseMgr as it is reserved to someone
1610 // else or doesn't belong to a pool.
1611 if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1612 // Concurrent delete performed by other instance which should
1613 // properly handle dns and stats updates.
1614 continue;
1615 }
1616
1617 // Update DNS if needed.
1618 queueNCR(CHG_REMOVE, candidate);
1619
1620 // Need to decrease statistic for assigned addresses.
1621 StatsMgr::instance().addValue(
1622 StatsMgr::generateName("subnet", candidate->subnet_id_,
1623 ctx.currentIA().type_ == Lease::TYPE_NA ?
1624 "assigned-nas" : "assigned-pds"),
1625 static_cast<int64_t>(-1));
1626
1627 // In principle, we could trigger a hook here, but we will do this
1628 // only if we get serious complaints from actual users. We want the
1629 // conflict resolution procedure to really work and user libraries
1630 // should not interfere with it.
1631
1632 // Add this to the list of removed leases.
1633 ctx.currentIA().old_leases_.push_back(candidate);
1634
1635 // Let's remove this candidate from existing leases
1636 removeLeases(existing_leases, candidate->addr_);
1637 }
1638}
1639
1640void
1641AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
1642 Lease6Collection& existing_leases) {
1643 // We need a copy, so we won't be iterating over a container and
1644 // removing from it at the same time. It's only a copy of pointers,
1645 // so the operation shouldn't be that expensive.
1646 Lease6Collection copy = existing_leases;
1647
1648 BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
1649 // Lease can be allocated to us from a dynamic pool, but we must
1650 // check if this lease belongs to any allowed pool. If it does,
1651 // we can proceed to checking the next lease.
1652 if (inAllowedPool(ctx, candidate->type_,
1653 candidate->addr_, false)) {
1654 continue;
1655 }
1656
1657 // Remove this lease from LeaseMgr as it doesn't belong to a pool.
1658 if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1659 // Concurrent delete performed by other instance which should
1660 // properly handle dns and stats updates.
1661 continue;
1662 }
1663
1664 // Update DNS if needed.
1665 queueNCR(CHG_REMOVE, candidate);
1666
1667 // Need to decrease statistic for assigned addresses.
1668 StatsMgr::instance().addValue(
1669 StatsMgr::generateName("subnet", candidate->subnet_id_,
1670 ctx.currentIA().type_ == Lease::TYPE_NA ?
1671 "assigned-nas" : "assigned-pds"),
1672 static_cast<int64_t>(-1));
1673
1674 // Add this to the list of removed leases.
1675 ctx.currentIA().old_leases_.push_back(candidate);
1676
1677 // Let's remove this candidate from existing leases
1678 removeLeases(existing_leases, candidate->addr_);
1679 }
1680}
1681
1682bool
1683AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
1684
1685 bool removed = false;
1686 for (Lease6Collection::iterator lease = container.begin();
1687 lease != container.end(); ++lease) {
1688 if ((*lease)->addr_ == addr) {
1689 lease->reset();
1690 removed = true;
1691 }
1692 }
1693
1694 // Remove all elements that have NULL value
1695 container.erase(std::remove(container.begin(), container.end(), Lease6Ptr()),
1696 container.end());
1697
1698 return (removed);
1699}
1700
1701void
1702AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
1703 Lease6Collection& existing_leases) {
1704 // This method removes leases that are not reserved for this host.
1705 // It will keep at least one lease, though, as a fallback.
1706 int total = existing_leases.size();
1707 if (total <= 1) {
1708 return;
1709 }
1710
1711 // This is officially not scary code anymore. iterates and marks specified
1712 // leases for deletion, by setting appropriate pointers to NULL.
1713 for (Lease6Collection::iterator lease = existing_leases.begin();
1714 lease != existing_leases.end(); ++lease) {
1715
1716 // If there is reservation for this keep it.
1717 IPv6Resrv resv = makeIPv6Resrv(*(*lease));
1718 if (ctx.hasGlobalReservation(resv) ||
1719 ((ctx.hosts_.count((*lease)->subnet_id_) > 0) &&
1720 (ctx.hosts_[(*lease)->subnet_id_]->hasReservation(resv)))) {
1721 continue;
1722 }
1723
1724 // @todo - If this is for a fake_allocation, we should probably
1725 // not be deleting the lease or removing DNS entries. We should
1726 // simply remove it from the list.
1727 // We have reservations, but not for this lease. Release it.
1728 // Remove this lease from LeaseMgr
1729 if (!LeaseMgrFactory::instance().deleteLease(*lease)) {
1730 // Concurrent delete performed by other instance which should
1731 // properly handle dns and stats updates.
1732 continue;
1733 }
1734
1735 // Update DNS if required.
1736 queueNCR(CHG_REMOVE, *lease);
1737
1738 // Need to decrease statistic for assigned addresses.
1739 StatsMgr::instance().addValue(
1740 StatsMgr::generateName("subnet", (*lease)->subnet_id_,
1741 ctx.currentIA().type_ == Lease::TYPE_NA ?
1742 "assigned-nas" : "assigned-pds"),
1743 static_cast<int64_t>(-1));
1744
1746
1747 // Add this to the list of removed leases.
1748 ctx.currentIA().old_leases_.push_back(*lease);
1749
1750 // Set this pointer to NULL. The pointer is still valid. We're just
1751 // setting the Lease6Ptr to NULL value. We'll remove all NULL
1752 // pointers once the loop is finished.
1753 lease->reset();
1754
1755 if (--total == 1) {
1756 // If there's only one lease left, break the loop.
1757 break;
1758 }
1759
1760 }
1761
1762 // Remove all elements that we previously marked for deletion (those that
1763 // have NULL value).
1764 existing_leases.erase(std::remove(existing_leases.begin(),
1765 existing_leases.end(), Lease6Ptr()), existing_leases.end());
1766}
1767
1769AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
1770 uint8_t prefix_len,
1771 CalloutHandle::CalloutNextStep& callout_status) {
1772
1773 if (!expired->expired()) {
1774 isc_throw(BadValue, "Attempt to recycle lease that is still valid");
1775 }
1776
1777 if (expired->type_ != Lease::TYPE_PD) {
1778 prefix_len = 128; // non-PD lease types must be always /128
1779 }
1780
1781 if (!ctx.fake_allocation_) {
1782 // The expired lease needs to be reclaimed before it can be reused.
1783 // This includes declined leases for which probation period has
1784 // elapsed.
1785 reclaimExpiredLease(expired, ctx.callout_handle_);
1786 }
1787
1788 // address, lease type and prefixlen (0) stay the same
1789 expired->iaid_ = ctx.currentIA().iaid_;
1790 expired->duid_ = ctx.duid_;
1791 // Use subnet's preferred triplet to conditionally determine
1792 // preferred lifetime based on hint
1793 if (!ctx.currentIA().hints_.empty() &&
1794 ctx.currentIA().hints_[0].getPreferred()) {
1795 uint32_t preferred = ctx.currentIA().hints_[0].getPreferred();
1796 expired->preferred_lft_ = ctx.subnet_->getPreferred().get(preferred);
1797 } else {
1798 expired->preferred_lft_ = ctx.subnet_->getPreferred();
1799 }
1800 // Use subnet's valid triplet to conditionally determine
1801 // valid lifetime based on hint
1802 expired->reuseable_valid_lft_ = 0;
1803 if (!ctx.currentIA().hints_.empty() &&
1804 ctx.currentIA().hints_[0].getValid()) {
1805 uint32_t valid = ctx.currentIA().hints_[0].getValid();
1806 expired->valid_lft_ = ctx.subnet_->getValid().get(valid);
1807 } else {
1808 expired->valid_lft_ = ctx.subnet_->getValid();
1809 }
1810 expired->cltt_ = time(NULL);
1811 expired->subnet_id_ = ctx.subnet_->getID();
1812 expired->hostname_ = ctx.hostname_;
1813 expired->fqdn_fwd_ = ctx.fwd_dns_update_;
1814 expired->fqdn_rev_ = ctx.rev_dns_update_;
1815 expired->prefixlen_ = prefix_len;
1816 expired->state_ = Lease::STATE_DEFAULT;
1817
1820 .arg(ctx.query_->getLabel())
1821 .arg(expired->toText());
1822
1823 // Let's execute all callouts registered for lease6_select
1824 if (ctx.callout_handle_ &&
1825 HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1826
1827 // Use the RAII wrapper to make sure that the callout handle state is
1828 // reset when this object goes out of scope. All hook points must do
1829 // it to prevent possible circular dependency between the callout
1830 // handle and its arguments.
1831 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1832
1833 // Enable copying options from the packet within hook library.
1834 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
1835
1836 // Pass necessary arguments
1837
1838 // Pass the original packet
1839 ctx.callout_handle_->setArgument("query6", ctx.query_);
1840
1841 // Subnet from which we do the allocation
1842 ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
1843
1844 // Is this solicit (fake = true) or request (fake = false)
1845 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
1846
1847 // The lease that will be assigned to a client
1848 ctx.callout_handle_->setArgument("lease6", expired);
1849
1850 // Call the callouts
1851 HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
1852
1853 callout_status = ctx.callout_handle_->getStatus();
1854
1855 // Callouts decided to skip the action. This means that the lease is not
1856 // assigned, so the client will get NoAddrAvail as a result. The lease
1857 // won't be inserted into the database.
1858 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
1860 return (Lease6Ptr());
1861 }
1862
1867
1868 // Let's use whatever callout returned. Hopefully it is the same lease
1869 // we handed to it.
1870 ctx.callout_handle_->getArgument("lease6", expired);
1871 }
1872
1873 if (!ctx.fake_allocation_) {
1874 // Add(update) the extended information on the lease.
1875 static_cast<void>(updateLease6ExtendedInfo(expired, ctx));
1876
1877 // for REQUEST we do update the lease
1879
1880 // If the lease is in the current subnet we need to account
1881 // for the re-assignment of The lease.
1882 if (ctx.subnet_->inPool(ctx.currentIA().type_, expired->addr_)) {
1883 StatsMgr::instance().addValue(
1884 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1885 ctx.currentIA().type_ == Lease::TYPE_NA ?
1886 "assigned-nas" : "assigned-pds"),
1887 static_cast<int64_t>(1));
1888 StatsMgr::instance().addValue(
1889 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1890 ctx.currentIA().type_ == Lease::TYPE_NA ?
1891 "cumulative-assigned-nas" :
1892 "cumulative-assigned-pds"),
1893 static_cast<int64_t>(1));
1894 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1895 "cumulative-assigned-nas" :
1896 "cumulative-assigned-pds",
1897 static_cast<int64_t>(1));
1898 }
1899 }
1900
1901 // We do nothing for SOLICIT. We'll just update database when
1902 // the client gets back to us with REQUEST message.
1903
1904 // it's not really expired at this stage anymore - let's return it as
1905 // an updated lease
1906 return (expired);
1907}
1908
1909void
1910AllocEngine::getLifetimes6(ClientContext6& ctx, uint32_t& preferred, uint32_t& valid) {
1911 // If the triplets are specified in one of our classes use it.
1912 // We use the first one we find for each lifetime.
1913 Triplet<uint32_t> candidate_preferred;
1914 Triplet<uint32_t> candidate_valid;
1915 const ClientClasses classes = ctx.query_->getClasses();
1916 if (!classes.empty()) {
1917 // Let's get class definitions
1918 const ClientClassDictionaryPtr& dict =
1919 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
1920
1921 // Iterate over the assigned class defintions.
1922 int have_both = 0;
1923 for (auto name = classes.cbegin();
1924 name != classes.cend() && have_both < 2; ++name) {
1925 ClientClassDefPtr cl = dict->findClass(*name);
1926 if (candidate_preferred.unspecified() &&
1927 (cl && (!cl->getPreferred().unspecified()))) {
1928 candidate_preferred = cl->getPreferred();
1929 ++have_both;
1930 }
1931
1932 if (candidate_valid.unspecified() &&
1933 (cl && (!cl->getValid().unspecified()))) {
1934 candidate_valid = cl->getValid();
1935 ++have_both;
1936 }
1937 }
1938 }
1939
1940 // If no classes specified preferred lifetime, get it from the subnet.
1941 if (!candidate_preferred) {
1942 candidate_preferred = ctx.subnet_->getPreferred();
1943 }
1944
1945 // If no classes specified valid lifetime, get it from the subnet.
1946 if (!candidate_valid) {
1947 candidate_valid = ctx.subnet_->getValid();
1948 }
1949
1950 // Set the outbound parameters to the values we have so far.
1951 preferred = candidate_preferred;
1952 valid = candidate_valid;
1953
1954 // If client requested either value, use the requested value(s) bounded by
1955 // the candidate triplet(s).
1956 if (!ctx.currentIA().hints_.empty()) {
1957 if (ctx.currentIA().hints_[0].getPreferred()) {
1958 preferred = candidate_preferred.get(ctx.currentIA().hints_[0].getPreferred());
1959 }
1960
1961 if (ctx.currentIA().hints_[0].getValid()) {
1962 valid = candidate_valid.get(ctx.currentIA().hints_[0].getValid());
1963 }
1964 }
1965}
1966
1967Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
1968 const IOAddress& addr,
1969 uint8_t prefix_len,
1970 CalloutHandle::CalloutNextStep& callout_status) {
1971
1972 if (ctx.currentIA().type_ != Lease::TYPE_PD) {
1973 prefix_len = 128; // non-PD lease types must be always /128
1974 }
1975
1976 uint32_t preferred = 0;
1977 uint32_t valid = 0;
1978 getLifetimes6(ctx, preferred, valid);
1979
1980 Lease6Ptr lease(new Lease6(ctx.currentIA().type_, addr, ctx.duid_,
1981 ctx.currentIA().iaid_, preferred,
1982 valid, ctx.subnet_->getID(),
1983 ctx.hwaddr_, prefix_len));
1984
1985 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
1986 lease->fqdn_rev_ = ctx.rev_dns_update_;
1987 lease->hostname_ = ctx.hostname_;
1988
1989 // Let's execute all callouts registered for lease6_select
1990 if (ctx.callout_handle_ &&
1991 HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1992
1993 // Use the RAII wrapper to make sure that the callout handle state is
1994 // reset when this object goes out of scope. All hook points must do
1995 // it to prevent possible circular dependency between the callout
1996 // handle and its arguments.
1997 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1998
1999 // Enable copying options from the packet within hook library.
2000 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2001
2002 // Pass necessary arguments
2003
2004 // Pass the original packet
2005 ctx.callout_handle_->setArgument("query6", ctx.query_);
2006
2007 // Subnet from which we do the allocation
2008 ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
2009
2010 // Is this solicit (fake = true) or request (fake = false)
2011 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
2012
2013 // The lease that will be assigned to a client
2014 ctx.callout_handle_->setArgument("lease6", lease);
2015
2016 // This is the first callout, so no need to clear any arguments
2017 HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
2018
2019 callout_status = ctx.callout_handle_->getStatus();
2020
2021 // Callouts decided to skip the action. This means that the lease is not
2022 // assigned, so the client will get NoAddrAvail as a result. The lease
2023 // won't be inserted into the database.
2024 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
2026 return (Lease6Ptr());
2027 }
2028
2029 // Let's use whatever callout returned. Hopefully it is the same lease
2030 // we handed to it.
2031 ctx.callout_handle_->getArgument("lease6", lease);
2032 }
2033
2034 if (!ctx.fake_allocation_) {
2035 // Add(update) the extended information on the lease.
2036 static_cast<void>(updateLease6ExtendedInfo(lease, ctx));
2037
2038 // That is a real (REQUEST) allocation
2039 bool status = LeaseMgrFactory::instance().addLease(lease);
2040
2041 if (status) {
2042 // The lease insertion succeeded - if the lease is in the
2043 // current subnet lets bump up the statistic.
2044 if (ctx.subnet_->inPool(ctx.currentIA().type_, addr)) {
2045 StatsMgr::instance().addValue(
2046 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2047 ctx.currentIA().type_ == Lease::TYPE_NA ?
2048 "assigned-nas" : "assigned-pds"),
2049 static_cast<int64_t>(1));
2050 StatsMgr::instance().addValue(
2051 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2052 ctx.currentIA().type_ == Lease::TYPE_NA ?
2053 "cumulative-assigned-nas" :
2054 "cumulative-assigned-pds"),
2055 static_cast<int64_t>(1));
2056 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2057 "cumulative-assigned-nas" :
2058 "cumulative-assigned-pds",
2059 static_cast<int64_t>(1));
2060 }
2061
2062 // Record it so it won't be updated twice.
2063 ctx.currentIA().addNewResource(addr, prefix_len);
2064
2065 return (lease);
2066 } else {
2067 // One of many failures with LeaseMgr (e.g. lost connection to the
2068 // database, database failed etc.). One notable case for that
2069 // is that we are working in multi-process mode and we lost a race
2070 // (some other process got that address first)
2071 return (Lease6Ptr());
2072 }
2073 } else {
2074 // That is only fake (SOLICIT without rapid-commit) allocation
2075
2076 // It is for advertise only. We should not insert the lease and callers
2077 // have already verified the lease does not exist in the database.
2078 return (lease);
2079 }
2080}
2081
2084 try {
2085 if (!ctx.subnet_) {
2086 isc_throw(InvalidOperation, "Subnet is required for allocation");
2087 }
2088
2089 if (!ctx.duid_) {
2090 isc_throw(InvalidOperation, "DUID is mandatory for allocation");
2091 }
2092
2093 // Check if there are any leases for this client.
2094 Subnet6Ptr subnet = ctx.subnet_;
2095 Lease6Collection leases;
2096 while (subnet) {
2097 Lease6Collection leases_subnet =
2099 *ctx.duid_,
2100 ctx.currentIA().iaid_,
2101 subnet->getID());
2102 leases.insert(leases.end(), leases_subnet.begin(), leases_subnet.end());
2103
2104 subnet = subnet->getNextSubnet(ctx.subnet_);
2105 }
2106
2107 if (!leases.empty()) {
2110 .arg(ctx.query_->getLabel());
2111
2112 // Check if the existing leases are reserved for someone else.
2113 // If they're not, we're ok to keep using them.
2114 removeNonmatchingReservedLeases6(ctx, leases);
2115 }
2116
2117 if (!ctx.hosts_.empty()) {
2118
2121 .arg(ctx.query_->getLabel());
2122
2123 // If we have host reservation, allocate those leases.
2124 allocateReservedLeases6(ctx, leases);
2125
2126 // There's one more check to do. Let's remove leases that are not
2127 // matching reservations, i.e. if client X has address A, but there's
2128 // a reservation for address B, we should release A and reassign B.
2129 // Caveat: do this only if we have at least one reserved address.
2130 removeNonreservedLeases6(ctx, leases);
2131 }
2132
2133 // If we happen to removed all leases, get something new for this guy.
2134 // Depending on the configuration, we may enable or disable granting
2135 // new leases during renewals. This is controlled with the
2136 // allow_new_leases_in_renewals_ field.
2137 if (leases.empty()) {
2138
2141 .arg(ctx.query_->getLabel());
2142
2143 leases = allocateUnreservedLeases6(ctx);
2144 }
2145
2146 // Extend all existing leases that passed all checks.
2147 for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
2148 if (ctx.currentIA().isNewResource((*l)->addr_,
2149 (*l)->prefixlen_)) {
2150 // This lease was just created so is already extended.
2151 continue;
2152 }
2155 .arg(ctx.query_->getLabel())
2156 .arg((*l)->typeToText((*l)->type_))
2157 .arg((*l)->addr_);
2158 extendLease6(ctx, *l);
2159 }
2160
2161 if (!leases.empty()) {
2162 // If there are any leases allocated, let's store in them in the
2163 // IA context so as they are available when we process subsequent
2164 // IAs.
2165 BOOST_FOREACH(Lease6Ptr lease, leases) {
2166 ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
2167 ctx.new_leases_.push_back(lease);
2168 }
2169 }
2170
2171 return (leases);
2172
2173 } catch (const isc::Exception& e) {
2174
2175 // Some other error, return an empty lease.
2177 .arg(ctx.query_->getLabel())
2178 .arg(e.what());
2179 }
2180
2181 return (Lease6Collection());
2182}
2183
2184void
2185AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
2186
2187 if (!lease || !ctx.subnet_) {
2188 return;
2189 }
2190
2191 // It is likely that the lease for which we're extending the lifetime doesn't
2192 // belong to the current but a sibling subnet.
2193 if (ctx.subnet_->getID() != lease->subnet_id_) {
2194 SharedNetwork6Ptr network;
2195 ctx.subnet_->getSharedNetwork(network);
2196 if (network) {
2197 Subnet6Ptr subnet = network->getSubnet(SubnetID(lease->subnet_id_));
2198 // Found the actual subnet this lease belongs to. Stick to this
2199 // subnet.
2200 if (subnet) {
2201 ctx.subnet_ = subnet;
2202 }
2203 }
2204 }
2205
2206 // If the lease is not global and it is either out of range (NAs only) or it
2207 // is not permitted by subnet client classification, delete it.
2208 if (!(ctx.hasGlobalReservation(makeIPv6Resrv(*lease))) &&
2209 (((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
2210 !ctx.subnet_->clientSupported(ctx.query_->getClasses()))) {
2211 // Oh dear, the lease is no longer valid. We need to get rid of it.
2212
2213 // Remove this lease from LeaseMgr
2214 if (!LeaseMgrFactory::instance().deleteLease(lease)) {
2215 // Concurrent delete performed by other instance which should
2216 // properly handle dns and stats updates.
2217 return;
2218 }
2219
2220 // Updated DNS if required.
2221 queueNCR(CHG_REMOVE, lease);
2222
2223 // Need to decrease statistic for assigned addresses.
2224 StatsMgr::instance().addValue(
2225 StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-nas"),
2226 static_cast<int64_t>(-1));
2227
2228 // Add it to the removed leases list.
2229 ctx.currentIA().old_leases_.push_back(lease);
2230
2231 return;
2232 }
2233
2236 .arg(ctx.query_->getLabel())
2237 .arg(lease->toText());
2238
2239 // Keep the old data in case the callout tells us to skip update.
2240 Lease6Ptr old_data(new Lease6(*lease));
2241
2242 bool changed = false;
2243 uint32_t current_preferred_lft = lease->preferred_lft_;
2244 if (!ctx.currentIA().hints_.empty() &&
2245 ctx.currentIA().hints_[0].getPreferred()) {
2246 uint32_t preferred = ctx.currentIA().hints_[0].getPreferred();
2247 lease->preferred_lft_ = ctx.subnet_->getPreferred().get(preferred);
2248 } else {
2249 lease->preferred_lft_ = ctx.subnet_->getPreferred();
2250 }
2251 if (lease->preferred_lft_ < current_preferred_lft) {
2252 changed = true;
2253 }
2254 lease->reuseable_valid_lft_ = 0;
2255 if (!ctx.currentIA().hints_.empty() &&
2256 ctx.currentIA().hints_[0].getValid()) {
2257 uint32_t valid = ctx.currentIA().hints_[0].getValid();
2258 lease->valid_lft_ = ctx.subnet_->getValid().get(valid);
2259 } else {
2260 lease->valid_lft_ = ctx.subnet_->getValid();
2261 }
2262 if (lease->valid_lft_ < lease->current_valid_lft_) {
2263 changed = true;
2264 }
2265
2266 lease->cltt_ = time(NULL);
2267 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2268 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
2269 (lease->hostname_ != ctx.hostname_)) {
2270 changed = true;
2271 lease->hostname_ = ctx.hostname_;
2272 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2273 lease->fqdn_rev_ = ctx.rev_dns_update_;
2274 }
2275 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
2276 (ctx.hwaddr_ &&
2277 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
2278 changed = true;
2279 lease->hwaddr_ = ctx.hwaddr_;
2280 }
2281 if (lease->state_ != Lease::STATE_DEFAULT) {
2282 changed = true;
2283 lease->state_ = Lease::STATE_DEFAULT;
2284 }
2287 .arg(ctx.query_->getLabel())
2288 .arg(lease->toText());
2289
2290 bool skip = false;
2291 // Get the callouts specific for the processed message and execute them.
2292 int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
2293 Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
2294 if (HooksManager::calloutsPresent(hook_point)) {
2295 CalloutHandlePtr callout_handle = ctx.callout_handle_;
2296
2297 // Use the RAII wrapper to make sure that the callout handle state is
2298 // reset when this object goes out of scope. All hook points must do
2299 // it to prevent possible circular dependency between the callout
2300 // handle and its arguments.
2301 ScopedCalloutHandleState callout_handle_state(callout_handle);
2302
2303 // Enable copying options from the packet within hook library.
2304 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2305
2306 // Pass the original packet
2307 callout_handle->setArgument("query6", ctx.query_);
2308
2309 // Pass the lease to be updated
2310 callout_handle->setArgument("lease6", lease);
2311
2312 // Pass the IA option to be sent in response
2313 if (lease->type_ == Lease::TYPE_NA) {
2314 callout_handle->setArgument("ia_na", ctx.currentIA().ia_rsp_);
2315 } else {
2316 callout_handle->setArgument("ia_pd", ctx.currentIA().ia_rsp_);
2317 }
2318
2319 // Call all installed callouts
2320 HooksManager::callCallouts(hook_point, *callout_handle);
2321
2322 // Callouts decided to skip the next processing step. The next
2323 // processing step would actually renew the lease, so skip at this
2324 // stage means "keep the old lease as it is".
2325 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2326 skip = true;
2329 .arg(ctx.query_->getName());
2330 }
2331
2333 }
2334
2335 if (!skip) {
2336 bool update_stats = false;
2337
2338 // If the lease we're renewing has expired, we need to reclaim this
2339 // lease before we can renew it.
2340 if (old_data->expired()) {
2341 reclaimExpiredLease(old_data, ctx.callout_handle_);
2342
2343 // If the lease is in the current subnet we need to account
2344 // for the re-assignment of the lease.
2345 if (ctx.subnet_->inPool(ctx.currentIA().type_, old_data->addr_)) {
2346 update_stats = true;
2347 }
2348 changed = true;
2349 }
2350
2351 // @todo should we call storeLease6ExtendedInfo() here ?
2352 if (updateLease6ExtendedInfo(lease, ctx)) {
2353 changed = true;
2354 }
2355
2356 // Try to reuse the lease.
2357 if (!changed) {
2358 setLeaseReusable(lease, current_preferred_lft, ctx);
2359 }
2360
2361
2362 // Now that the lease has been reclaimed, we can go ahead and update it
2363 // in the lease database.
2364 if (lease->reuseable_valid_lft_ == 0) {
2366 }
2367
2368 if (update_stats) {
2369 StatsMgr::instance().addValue(
2370 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2371 ctx.currentIA().type_ == Lease::TYPE_NA ?
2372 "assigned-nas" : "assigned-pds"),
2373 static_cast<int64_t>(1));
2374 StatsMgr::instance().addValue(
2375 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2376 ctx.currentIA().type_ == Lease::TYPE_NA ?
2377 "cumulative-assigned-nas" :
2378 "cumulative-assigned-pds"),
2379 static_cast<int64_t>(1));
2380 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2381 "cumulative-assigned-nas" :
2382 "cumulative-assigned-pds",
2383 static_cast<int64_t>(1));
2384 }
2385
2386 } else {
2387 // Copy back the original date to the lease. For MySQL it doesn't make
2388 // much sense, but for memfile, the Lease6Ptr points to the actual lease
2389 // in memfile, so the actual update is performed when we manipulate
2390 // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
2391 *lease = *old_data;
2392 }
2393
2394 // Add the old lease to the changed lease list. This allows the server
2395 // to make decisions regarding DNS updates.
2396 ctx.currentIA().changed_leases_.push_back(old_data);
2397}
2398
2400AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
2401 Lease6Collection updated_leases;
2402 for (Lease6Collection::const_iterator lease_it = leases.begin();
2403 lease_it != leases.end(); ++lease_it) {
2404 Lease6Ptr lease(new Lease6(**lease_it));
2405 if (ctx.currentIA().isNewResource(lease->addr_, lease->prefixlen_)) {
2406 // This lease was just created so is already up to date.
2407 updated_leases.push_back(lease);
2408 continue;
2409 }
2410
2411 lease->reuseable_valid_lft_ = 0;
2412 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2413 lease->fqdn_rev_ = ctx.rev_dns_update_;
2414 lease->hostname_ = ctx.hostname_;
2415 if (!ctx.fake_allocation_) {
2416 bool update_stats = false;
2417
2418 if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
2419 // Transition lease state to default (aka assigned)
2420 lease->state_ = Lease::STATE_DEFAULT;
2421
2422 // If the lease is in the current subnet we need to account
2423 // for the re-assignment of the lease.
2424 if (inAllowedPool(ctx, ctx.currentIA().type_,
2425 lease->addr_, true)) {
2426 update_stats = true;
2427 }
2428 }
2429
2430 bool fqdn_changed = ((lease->type_ != Lease::TYPE_PD) &&
2431 !(lease->hasIdenticalFqdn(**lease_it)));
2432
2433 lease->cltt_ = time(NULL);
2434 if (!fqdn_changed) {
2435 uint32_t current_preferred_lft = lease->preferred_lft_;
2436 setLeaseReusable(lease, current_preferred_lft, ctx);
2437 }
2438 if (lease->reuseable_valid_lft_ == 0) {
2439 ctx.currentIA().changed_leases_.push_back(*lease_it);
2441 }
2442
2443 if (update_stats) {
2444 StatsMgr::instance().addValue(
2445 StatsMgr::generateName("subnet", lease->subnet_id_,
2446 ctx.currentIA().type_ == Lease::TYPE_NA ?
2447 "assigned-nas" : "assigned-pds"),
2448 static_cast<int64_t>(1));
2449 StatsMgr::instance().addValue(
2450 StatsMgr::generateName("subnet", lease->subnet_id_,
2451 ctx.currentIA().type_ == Lease::TYPE_NA ?
2452 "cumulative-assigned-nas" :
2453 "cumulative-assigned-pds"),
2454 static_cast<int64_t>(1));
2455 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2456 "cumulative-assigned-nas" :
2457 "cumulative-assigned-pds",
2458 static_cast<int64_t>(1));
2459 }
2460 }
2461
2462 updated_leases.push_back(lease);
2463 }
2464
2465 return (updated_leases);
2466}
2467
2468void
2469AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout,
2470 const bool remove_lease,
2471 const uint16_t max_unwarned_cycles) {
2472
2475 .arg(max_leases)
2476 .arg(timeout);
2477
2478 // Create stopwatch and automatically start it to measure the time
2479 // taken by the routine.
2480 util::Stopwatch stopwatch;
2481
2482 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2483
2484 // This value indicates if we have been able to deal with all expired
2485 // leases in this pass.
2486 bool incomplete_reclamation = false;
2487 Lease6Collection leases;
2488 // The value of 0 has a special meaning - reclaim all.
2489 if (max_leases > 0) {
2490 // If the value is non-zero, the caller has limited the number of
2491 // leases to reclaim. We obtain one lease more to see if there will
2492 // be still leases left after this pass.
2493 lease_mgr.getExpiredLeases6(leases, max_leases + 1);
2494 // There are more leases expired leases than we will process in this
2495 // pass, so we should mark it as an incomplete reclamation. We also
2496 // remove this extra lease (which we don't want to process anyway)
2497 // from the collection.
2498 if (leases.size() > max_leases) {
2499 leases.pop_back();
2500 incomplete_reclamation = true;
2501 }
2502
2503 } else {
2504 // If there is no limitation on the number of leases to reclaim,
2505 // we will try to process all. Hence, we don't mark it as incomplete
2506 // reclamation just yet.
2507 lease_mgr.getExpiredLeases6(leases, max_leases);
2508 }
2509
2510 // Do not initialize the callout handle until we know if there are any
2511 // lease6_expire callouts installed.
2512 CalloutHandlePtr callout_handle;
2513 if (!leases.empty() &&
2514 HooksManager::calloutsPresent(Hooks.hook_index_lease6_expire_)) {
2515 callout_handle = HooksManager::createCalloutHandle();
2516 }
2517
2518 size_t leases_processed = 0;
2519 BOOST_FOREACH(Lease6Ptr lease, leases) {
2520
2521 try {
2522 // Reclaim the lease.
2523 if (MultiThreadingMgr::instance().getMode()) {
2524 // The reclamation is exclusive of packet processing.
2525 WriteLockGuard exclusive(rw_mutex_);
2526
2527 reclaimExpiredLease(lease, remove_lease, callout_handle);
2528 ++leases_processed;
2529 } else {
2530 reclaimExpiredLease(lease, remove_lease, callout_handle);
2531 ++leases_processed;
2532 }
2533
2534 } catch (const std::exception& ex) {
2536 .arg(lease->addr_.toText())
2537 .arg(ex.what());
2538 }
2539
2540 // Check if we have hit the timeout for running reclamation routine and
2541 // return if we have. We're checking it here, because we always want to
2542 // allow reclaiming at least one lease.
2543 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2544 // Timeout. This will likely mean that we haven't been able to process
2545 // all leases we wanted to process. The reclamation pass will be
2546 // probably marked as incomplete.
2547 if (!incomplete_reclamation) {
2548 if (leases_processed < leases.size()) {
2549 incomplete_reclamation = true;
2550 }
2551 }
2552
2555 .arg(timeout);
2556 break;
2557 }
2558 }
2559
2560 // Stop measuring the time.
2561 stopwatch.stop();
2562
2563 // Mark completion of the lease reclamation routine and present some stats.
2566 .arg(leases_processed)
2567 .arg(stopwatch.logFormatTotalDuration());
2568
2569 // Check if this was an incomplete reclamation and increase the number of
2570 // consecutive incomplete reclamations.
2571 if (incomplete_reclamation) {
2572 ++incomplete_v6_reclamations_;
2573 // If the number of incomplete reclamations is beyond the threshold, we
2574 // need to issue a warning.
2575 if ((max_unwarned_cycles > 0) &&
2576 (incomplete_v6_reclamations_ > max_unwarned_cycles)) {
2578 .arg(max_unwarned_cycles);
2579 // We issued a warning, so let's now reset the counter.
2580 incomplete_v6_reclamations_ = 0;
2581 }
2582
2583 } else {
2584 // This was a complete reclamation, so let's reset the counter.
2585 incomplete_v6_reclamations_ = 0;
2586
2589 }
2590}
2591
2592void
2596 .arg(secs);
2597
2598 uint64_t deleted_leases = 0;
2599 try {
2600 // Try to delete leases from the lease database.
2601 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2602 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
2603
2604 } catch (const std::exception& ex) {
2606 .arg(ex.what());
2607 }
2608
2611 .arg(deleted_leases);
2612}
2613
2614void
2615AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout,
2616 const bool remove_lease,
2617 const uint16_t max_unwarned_cycles) {
2618
2621 .arg(max_leases)
2622 .arg(timeout);
2623
2624 // Create stopwatch and automatically start it to measure the time
2625 // taken by the routine.
2626 util::Stopwatch stopwatch;
2627
2628 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2629
2630 // This value indicates if we have been able to deal with all expired
2631 // leases in this pass.
2632 bool incomplete_reclamation = false;
2633 Lease4Collection leases;
2634 // The value of 0 has a special meaning - reclaim all.
2635 if (max_leases > 0) {
2636 // If the value is non-zero, the caller has limited the number of
2637 // leases to reclaim. We obtain one lease more to see if there will
2638 // be still leases left after this pass.
2639 lease_mgr.getExpiredLeases4(leases, max_leases + 1);
2640 // There are more leases expired leases than we will process in this
2641 // pass, so we should mark it as an incomplete reclamation. We also
2642 // remove this extra lease (which we don't want to process anyway)
2643 // from the collection.
2644 if (leases.size() > max_leases) {
2645 leases.pop_back();
2646 incomplete_reclamation = true;
2647 }
2648
2649 } else {
2650 // If there is no limitation on the number of leases to reclaim,
2651 // we will try to process all. Hence, we don't mark it as incomplete
2652 // reclamation just yet.
2653 lease_mgr.getExpiredLeases4(leases, max_leases);
2654 }
2655
2656 // Do not initialize the callout handle until we know if there are any
2657 // lease4_expire callouts installed.
2658 CalloutHandlePtr callout_handle;
2659 if (!leases.empty() &&
2660 HooksManager::calloutsPresent(Hooks.hook_index_lease4_expire_)) {
2661 callout_handle = HooksManager::createCalloutHandle();
2662 }
2663
2664 size_t leases_processed = 0;
2665 BOOST_FOREACH(Lease4Ptr lease, leases) {
2666
2667 try {
2668 // Reclaim the lease.
2669 if (MultiThreadingMgr::instance().getMode()) {
2670 // The reclamation is exclusive of packet processing.
2671 WriteLockGuard exclusive(rw_mutex_);
2672
2673 reclaimExpiredLease(lease, remove_lease, callout_handle);
2674 ++leases_processed;
2675 } else {
2676 reclaimExpiredLease(lease, remove_lease, callout_handle);
2677 ++leases_processed;
2678 }
2679
2680 } catch (const std::exception& ex) {
2682 .arg(lease->addr_.toText())
2683 .arg(ex.what());
2684 }
2685
2686 // Check if we have hit the timeout for running reclamation routine and
2687 // return if we have. We're checking it here, because we always want to
2688 // allow reclaiming at least one lease.
2689 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2690 // Timeout. This will likely mean that we haven't been able to process
2691 // all leases we wanted to process. The reclamation pass will be
2692 // probably marked as incomplete.
2693 if (!incomplete_reclamation) {
2694 if (leases_processed < leases.size()) {
2695 incomplete_reclamation = true;
2696 }
2697 }
2698
2701 .arg(timeout);
2702 break;
2703 }
2704 }
2705
2706 // Stop measuring the time.
2707 stopwatch.stop();
2708
2709 // Mark completion of the lease reclamation routine and present some stats.
2712 .arg(leases_processed)
2713 .arg(stopwatch.logFormatTotalDuration());
2714
2715 // Check if this was an incomplete reclamation and increase the number of
2716 // consecutive incomplete reclamations.
2717 if (incomplete_reclamation) {
2718 ++incomplete_v4_reclamations_;
2719 // If the number of incomplete reclamations is beyond the threshold, we
2720 // need to issue a warning.
2721 if ((max_unwarned_cycles > 0) &&
2722 (incomplete_v4_reclamations_ > max_unwarned_cycles)) {
2724 .arg(max_unwarned_cycles);
2725 // We issued a warning, so let's now reset the counter.
2726 incomplete_v4_reclamations_ = 0;
2727 }
2728
2729 } else {
2730 // This was a complete reclamation, so let's reset the counter.
2731 incomplete_v4_reclamations_ = 0;
2732
2735 }
2736}
2737
2738template<typename LeasePtrType>
2739void
2740AllocEngine::reclaimExpiredLease(const LeasePtrType& lease, const bool remove_lease,
2741 const CalloutHandlePtr& callout_handle) {
2742 reclaimExpiredLease(lease, remove_lease ? DB_RECLAIM_REMOVE : DB_RECLAIM_UPDATE,
2743 callout_handle);
2744}
2745
2746template<typename LeasePtrType>
2747void
2748AllocEngine::reclaimExpiredLease(const LeasePtrType& lease,
2749 const CalloutHandlePtr& callout_handle) {
2750 // This variant of the method is used by the code which allocates or
2751 // renews leases. It may be the case that the lease has already been
2752 // reclaimed, so there is nothing to do.
2753 if (!lease->stateExpiredReclaimed()) {
2754 reclaimExpiredLease(lease, DB_RECLAIM_LEAVE_UNCHANGED, callout_handle);
2755 }
2756}
2757
2758void
2759AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
2760 const DbReclaimMode& reclaim_mode,
2761 const CalloutHandlePtr& callout_handle) {
2762
2765 .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
2766 .arg(lease->addr_.toText())
2767 .arg(static_cast<int>(lease->prefixlen_));
2768
2769 // The skip flag indicates if the callouts have taken responsibility
2770 // for reclaiming the lease. The callout will set this to true if
2771 // it reclaims the lease itself. In this case the reclamation routine
2772 // will not update DNS nor update the database.
2773 bool skipped = false;
2774 if (callout_handle) {
2775
2776 // Use the RAII wrapper to make sure that the callout handle state is
2777 // reset when this object goes out of scope. All hook points must do
2778 // it to prevent possible circular dependency between the callout
2779 // handle and its arguments.
2780 ScopedCalloutHandleState callout_handle_state(callout_handle);
2781
2782 callout_handle->deleteAllArguments();
2783 callout_handle->setArgument("lease6", lease);
2784 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2785
2786 HooksManager::callCallouts(Hooks.hook_index_lease6_expire_,
2787 *callout_handle);
2788
2789 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2790 }
2791
2794
2795 if (!skipped) {
2796
2797 // Generate removal name change request for D2, if required.
2798 // This will return immediately if the DNS wasn't updated
2799 // when the lease was created.
2800 queueNCR(CHG_REMOVE, lease);
2801
2802 // Let's check if the lease that just expired is in DECLINED state.
2803 // If it is, we need to perform a couple extra steps.
2804 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2805 if (lease->state_ == Lease::STATE_DECLINED) {
2806 // Do extra steps required for declined lease reclamation:
2807 // - call the recover hook
2808 // - bump decline-related stats
2809 // - log separate message
2810 // There's no point in keeping a declined lease after its
2811 // reclamation. A declined lease doesn't have any client
2812 // identifying information anymore. So we'll flag it for
2813 // removal unless the hook has set the skip flag.
2814 remove_lease = reclaimDeclined(lease);
2815 }
2816
2817 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2818 // Reclaim the lease - depending on the configuration, set the
2819 // expired-reclaimed state or simply remove it.
2820 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2821 reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
2822 std::bind(&LeaseMgr::updateLease6,
2823 &lease_mgr, ph::_1));
2824 }
2825 }
2826
2827 // Update statistics.
2828
2829 // Decrease number of assigned leases.
2830 if (lease->type_ == Lease::TYPE_NA) {
2831 // IA_NA
2832 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2833 lease->subnet_id_,
2834 "assigned-nas"),
2835 int64_t(-1));
2836
2837 } else if (lease->type_ == Lease::TYPE_PD) {
2838 // IA_PD
2839 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2840 lease->subnet_id_,
2841 "assigned-pds"),
2842 int64_t(-1));
2843
2844 }
2845
2846 // Increase total number of reclaimed leases.
2847 StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
2848
2849 // Increase number of reclaimed leases for a subnet.
2850 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2851 lease->subnet_id_,
2852 "reclaimed-leases"),
2853 int64_t(1));
2854}
2855
2856void
2857AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
2858 const DbReclaimMode& reclaim_mode,
2859 const CalloutHandlePtr& callout_handle) {
2860
2863 .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
2864 .arg(lease->addr_.toText());
2865
2866 // The skip flag indicates if the callouts have taken responsibility
2867 // for reclaiming the lease. The callout will set this to true if
2868 // it reclaims the lease itself. In this case the reclamation routine
2869 // will not update DNS nor update the database.
2870 bool skipped = false;
2871 if (callout_handle) {
2872
2873 // Use the RAII wrapper to make sure that the callout handle state is
2874 // reset when this object goes out of scope. All hook points must do
2875 // it to prevent possible circular dependency between the callout
2876 // handle and its arguments.
2877 ScopedCalloutHandleState callout_handle_state(callout_handle);
2878
2879 callout_handle->setArgument("lease4", lease);
2880 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2881
2882 HooksManager::callCallouts(Hooks.hook_index_lease4_expire_,
2883 *callout_handle);
2884
2885 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2886 }
2887
2890
2891 if (!skipped) {
2892
2893 // Generate removal name change request for D2, if required.
2894 // This will return immediately if the DNS wasn't updated
2895 // when the lease was created.
2896 queueNCR(CHG_REMOVE, lease);
2897 // Clear DNS fields so we avoid redundant removes.
2898 lease->hostname_.clear();
2899 lease->fqdn_fwd_ = false;
2900 lease->fqdn_rev_ = false;
2901
2902 // Let's check if the lease that just expired is in DECLINED state.
2903 // If it is, we need to perform a couple extra steps.
2904 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2905 if (lease->state_ == Lease::STATE_DECLINED) {
2906 // Do extra steps required for declined lease reclamation:
2907 // - call the recover hook
2908 // - bump decline-related stats
2909 // - log separate message
2910 // There's no point in keeping a declined lease after its
2911 // reclamation. A declined lease doesn't have any client
2912 // identifying information anymore. So we'll flag it for
2913 // removal unless the hook has set the skip flag.
2914 remove_lease = reclaimDeclined(lease);
2915 }
2916
2917 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2918 // Reclaim the lease - depending on the configuration, set the
2919 // expired-reclaimed state or simply remove it.
2920 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2921 reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
2922 std::bind(&LeaseMgr::updateLease4,
2923 &lease_mgr, ph::_1));
2924 }
2925 }
2926
2927 // Update statistics.
2928
2929 // Decrease number of assigned addresses.
2930 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2931 lease->subnet_id_,
2932 "assigned-addresses"),
2933 int64_t(-1));
2934
2935 // Increase total number of reclaimed leases.
2936 StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
2937
2938 // Increase number of reclaimed leases for a subnet.
2939 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2940 lease->subnet_id_,
2941 "reclaimed-leases"),
2942 int64_t(1));
2943}
2944
2945void
2949 .arg(secs);
2950
2951 uint64_t deleted_leases = 0;
2952 try {
2953 // Try to delete leases from the lease database.
2954 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2955 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
2956
2957 } catch (const std::exception& ex) {
2959 .arg(ex.what());
2960 }
2961
2964 .arg(deleted_leases);
2965}
2966
2967bool
2968AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
2969 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
2970 return (true);
2971 }
2972
2973 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_recover_)) {
2974 CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
2975
2976 // Use the RAII wrapper to make sure that the callout handle state is
2977 // reset when this object goes out of scope. All hook points must do
2978 // it to prevent possible circular dependency between the callout
2979 // handle and its arguments.
2980 ScopedCalloutHandleState callout_handle_state(callout_handle);
2981
2982 // Pass necessary arguments
2983 callout_handle->setArgument("lease4", lease);
2984
2985 // Call the callouts
2986 HooksManager::callCallouts(Hooks.hook_index_lease4_recover_, *callout_handle);
2987
2988 // Callouts decided to skip the action. This means that the lease is not
2989 // assigned, so the client will get NoAddrAvail as a result. The lease
2990 // won't be inserted into the database.
2991 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2993 .arg(lease->addr_.toText());
2994 return (false);
2995 }
2996 }
2997
2999 .arg(lease->addr_.toText())
3000 .arg(lease->valid_lft_);
3001
3002 StatsMgr& stats_mgr = StatsMgr::instance();
3003
3004 // Decrease subnet specific counter for currently declined addresses
3005 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3006 "declined-addresses"), static_cast<int64_t>(-1));
3007
3008 // Decrease global counter for declined addresses
3009 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3010
3011 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3012
3013 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3014 "reclaimed-declined-addresses"), static_cast<int64_t>(1));
3015
3016 // Note that we do not touch assigned-addresses counters. Those are
3017 // modified in whatever code calls this method.
3018 return (true);
3019}
3020
3021bool
3022AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
3023 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3024 return (true);
3025 }
3026
3027 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_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("lease6", lease);
3038
3039 // Call the callouts
3040 HooksManager::callCallouts(Hooks.hook_index_lease6_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-nas counters. Those are
3071 // modified in whatever code calls this method.
3072
3073 return (true);
3074}
3075
3076template<typename LeasePtrType>
3077void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
3078 const bool remove_lease,
3079 const std::function<void (const LeasePtrType&)>&
3080 lease_update_fun) const {
3081
3082 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3083
3084 // Reclaim the lease - depending on the configuration, set the
3085 // expired-reclaimed state or simply remove it.
3086 if (remove_lease) {
3087 lease_mgr.deleteLease(lease);
3088 } else if (lease_update_fun) {
3089 // Clear FQDN information as we have already sent the
3090 // name change request to remove the DNS record.
3091 lease->reuseable_valid_lft_ = 0;
3092 lease->hostname_.clear();
3093 lease->fqdn_fwd_ = false;
3094 lease->fqdn_rev_ = false;
3095 lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
3096 lease_update_fun(lease);
3097
3098 } else {
3099 return;
3100 }
3101
3102 // Lease has been reclaimed.
3105 .arg(lease->addr_.toText());
3106}
3107
3108} // namespace dhcp
3109} // namespace isc
3110
3111// ##########################################################################
3112// # DHCPv4 lease allocation code starts here.
3113// ##########################################################################
3114
3115namespace {
3116
3134bool
3135addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
3136 // When out-of-pool flag is true the server may assume that all host
3137 // reservations are for addresses that do not belong to the dynamic pool.
3138 // Therefore, it can skip the reservation checks when dealing with in-pool
3139 // addresses.
3140 if (ctx.subnet_ && ctx.subnet_->getReservationsInSubnet() &&
3141 (!ctx.subnet_->getReservationsOutOfPool() ||
3142 !ctx.subnet_->inPool(Lease::TYPE_V4, address))) {
3143 // The global parameter ip-reservations-unique controls whether it is allowed
3144 // to specify multiple reservations for the same IP address or delegated prefix
3145 // or IP reservations must be unique. Some host backends do not support the
3146 // former, thus we can't always use getAll4 calls to get the reservations
3147 // for the given IP. When we're in the default mode, when IP reservations
3148 // are unique, we should call get4 (supported by all backends). If we're in
3149 // the mode in which non-unique reservations are allowed the backends which
3150 // don't support it are not used and we can safely call getAll4.
3151 ConstHostCollection hosts;
3152 if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
3153 // Reservations are unique. It is safe to call get4 to get the unique host.
3154 ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
3155 if (host) {
3156 hosts.push_back(host);
3157 }
3158 } else {
3159 // Reservations can be non-unique. Need to get all reservations for that address.
3160 hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
3161 }
3162
3163 for (auto host : hosts) {
3164 for (const AllocEngine::IdentifierPair& id_pair : ctx.host_identifiers_) {
3165 // If we find the matching host we know that this address is reserved
3166 // for us and we can return immediately.
3167 if (id_pair.first == host->getIdentifierType() &&
3168 id_pair.second == host->getIdentifier()) {
3169 return (false);
3170 }
3171 }
3172 }
3173 // We didn't find a matching host. If there are any reservations it means that
3174 // address is reserved for another client or multiple clients. If there are
3175 // no reservations address is not reserved for another client.
3176 return (!hosts.empty());
3177 }
3178 return (false);
3179}
3180
3196bool
3197hasAddressReservation(AllocEngine::ClientContext4& ctx) {
3198 if (ctx.hosts_.empty()) {
3199 return (false);
3200 }
3201
3202 // Flag used to perform search for global reservations only once.
3203 bool search_global_done = false;
3204
3205 Subnet4Ptr subnet = ctx.subnet_;
3206 while (subnet) {
3207 // Skip search if the global reservations have already been examined.
3208 if (!search_global_done && subnet->getReservationsGlobal()) {
3209 auto host = ctx.hosts_.find(SUBNET_ID_GLOBAL);
3210 // if we want global + other modes we would need to
3211 // return only if true, else continue
3212 if (host != ctx.hosts_.end() && host->second &&
3213 !host->second->getIPv4Reservation().isV4Zero()) {
3214 return (true);
3215 }
3216 // No need to perform this search again as there are no global
3217 // reservations.
3218 search_global_done = true;
3219 }
3220
3221 if (subnet->getReservationsInSubnet()) {
3222 auto host = ctx.hosts_.find(subnet->getID());
3223 // The out-of-pool flag indicates that no client should be assigned
3224 // reserved addresses from within the dynamic pool, and for that
3225 // reason look only for reservations that are outside the pools,
3226 // hence the inPool check.
3227 if (host != ctx.hosts_.end() && host->second) {
3228 auto reservation = host->second->getIPv4Reservation();
3229 if (!reservation.isV4Zero() &&
3230 (!subnet->getReservationsOutOfPool() ||
3231 !subnet->inPool(Lease::TYPE_V4, reservation))) {
3232 ctx.subnet_ = subnet;
3233 return (true);
3234 }
3235 }
3236 }
3237
3238 // No address reservation found here, so let's try another subnet
3239 // within the same shared network.
3240 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3241 }
3242
3243 return (false);
3244}
3245
3261void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
3262 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3263
3264 Subnet4Ptr original_subnet = ctx.subnet_;
3265
3266 // Client identifier is optional. First check if we can try to lookup
3267 // by client-id.
3268 bool try_clientid_lookup = (ctx.clientid_ &&
3270 ctx.query_->getClasses()));
3271
3272 // If it is possible to use client identifier to try to find client's lease.
3273 if (try_clientid_lookup) {
3274 // Get all leases for this client identifier. When shared networks are
3275 // in use it is more efficient to make a single query rather than
3276 // multiple queries, one for each subnet.
3277 Lease4Collection leases_client_id = lease_mgr.getLease4(*ctx.clientid_);
3278
3279 // Iterate over the subnets within the shared network to see if any client's
3280 // lease belongs to them.
3281 for (Subnet4Ptr subnet = original_subnet; subnet;
3282 subnet = subnet->getNextSubnet(original_subnet,
3283 ctx.query_->getClasses())) {
3284
3285 // If client identifier has been supplied and the server wasn't
3286 // explicitly configured to ignore client identifiers for this subnet
3287 // check if there is a lease within this subnet.
3288 if (subnet->getMatchClientId()) {
3289 for (auto l = leases_client_id.begin(); l != leases_client_id.end(); ++l) {
3290 if ((*l)->subnet_id_ == subnet->getID()) {
3291 // Lease found, so stick to this lease.
3292 client_lease = (*l);
3293 ctx.subnet_ = subnet;
3294 return;
3295 }
3296 }
3297 }
3298 }
3299 }
3300
3301 // If no lease found using the client identifier, try the lookup using
3302 // the HW address.
3303 if (!client_lease && ctx.hwaddr_) {
3304
3305 // Get all leases for this HW address.
3306 Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
3307
3308 for (Subnet4Ptr subnet = original_subnet; subnet;
3309 subnet = subnet->getNextSubnet(original_subnet,
3310 ctx.query_->getClasses())) {
3311 ClientIdPtr client_id;
3312 if (subnet->getMatchClientId()) {
3313 client_id = ctx.clientid_;
3314 }
3315
3316 // Try to find the lease that matches current subnet and belongs to
3317 // this client, so both HW address and client identifier match.
3318 for (Lease4Collection::const_iterator client_lease_it = leases_hw_address.begin();
3319 client_lease_it != leases_hw_address.end(); ++client_lease_it) {
3320 Lease4Ptr existing_lease = *client_lease_it;
3321 if ((existing_lease->subnet_id_ == subnet->getID()) &&
3322 existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
3323 // Found the lease of this client, so return it.
3324 client_lease = existing_lease;
3325 // We got a lease but the subnet it belongs to may differ from
3326 // the original subnet. Let's now stick to this subnet.
3327 ctx.subnet_ = subnet;
3328 return;
3329 }
3330 }
3331 }
3332 }
3333}
3334
3347bool
3348inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
3349 // If the subnet belongs to a shared network we will be iterating
3350 // over the subnets that belong to this shared network.
3351 Subnet4Ptr current_subnet = ctx.subnet_;
3352 while (current_subnet) {
3353
3354 if (current_subnet->inPool(Lease::TYPE_V4, address,
3355 ctx.query_->getClasses())) {
3356 // We found a subnet that this address belongs to, so it
3357 // seems that this subnet is the good candidate for allocation.
3358 // Let's update the selected subnet.
3359 ctx.subnet_ = current_subnet;
3360 return (true);
3361 }
3362
3363 current_subnet = current_subnet->getNextSubnet(ctx.subnet_,
3364 ctx.query_->getClasses());
3365 }
3366
3367 return (false);
3368}
3369
3370} // namespace
3371
3372namespace isc {
3373namespace dhcp {
3374
3376 : subnet_(), clientid_(), hwaddr_(),
3377 requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
3378 fwd_dns_update_(false), rev_dns_update_(false),
3379 hostname_(""), callout_handle_(), fake_allocation_(false),
3380 old_lease_(), new_lease_(), hosts_(), conflicting_lease_(),
3381 query_(), host_identifiers_(),
3382 ddns_params_() {
3383}
3384
3386 const ClientIdPtr& clientid,
3387 const HWAddrPtr& hwaddr,
3388 const asiolink::IOAddress& requested_addr,
3389 const bool fwd_dns_update,
3390 const bool rev_dns_update,
3391 const std::string& hostname,
3392 const bool fake_allocation)
3393 : subnet_(subnet), clientid_(clientid), hwaddr_(hwaddr),
3394 requested_address_(requested_addr),
3395 fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
3396 hostname_(hostname), callout_handle_(),
3397 fake_allocation_(fake_allocation), old_lease_(), new_lease_(),
3398 hosts_(), host_identifiers_(),
3399 ddns_params_(new DdnsParams()) {
3400
3401 // Initialize host identifiers.
3402 if (hwaddr) {
3403 addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
3404 }
3405}
3406
3409 if (subnet_ && subnet_->getReservationsInSubnet()) {
3410 auto host = hosts_.find(subnet_->getID());
3411 if (host != hosts_.cend()) {
3412 return (host->second);
3413 }
3414 }
3415
3416 return (globalHost());
3417}
3418
3421 if (subnet_ && subnet_->getReservationsGlobal()) {
3422 auto host = hosts_.find(SUBNET_ID_GLOBAL);
3423 if (host != hosts_.cend()) {
3424 return (host->second);
3425 }
3426 }
3427
3428 return (ConstHostPtr());
3429}
3430
3433 // We already have it return it unless the context subnet has changed.
3434 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
3435 return (ddns_params_);
3436 }
3437
3438 // Doesn't exist yet or is stale, (re)create it.
3439 if (subnet_) {
3440 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
3441 return (ddns_params_);
3442 }
3443
3444 // Asked for it without a subnet? This case really shouldn't occur but
3445 // for now let's return an instance with default values.
3446 return (DdnsParamsPtr(new DdnsParams()));
3447}
3448
3451 // The NULL pointer indicates that the old lease didn't exist. It may
3452 // be later set to non NULL value if existing lease is found in the
3453 // database.
3454 ctx.old_lease_.reset();
3455 ctx.new_lease_.reset();
3456
3457 // Before we start allocation process, we need to make sure that the
3458 // selected subnet is allowed for this client. If not, we'll try to
3459 // use some other subnet within the shared network. If there are no
3460 // subnets allowed for this client within the shared network, we
3461 // can't allocate a lease.
3462 Subnet4Ptr subnet = ctx.subnet_;
3463 if (subnet && !subnet->clientSupported(ctx.query_->getClasses())) {
3464 ctx.subnet_ = subnet->getNextSubnet(subnet, ctx.query_->getClasses());
3465 }
3466
3467 try {
3468 if (!ctx.subnet_) {
3469 isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
3470 }
3471
3472 if (!ctx.hwaddr_) {
3473 isc_throw(BadValue, "HWAddr must be defined");
3474 }
3475
3476 if (ctx.fake_allocation_) {
3477 return (discoverLease4(ctx));
3478
3479 } else {
3480 ctx.new_lease_ = requestLease4(ctx);
3481 }
3482
3483 } catch (const isc::Exception& e) {
3484 // Some other error, return an empty lease.
3486 .arg(ctx.query_->getLabel())
3487 .arg(e.what());
3488 }
3489
3490 return (ctx.new_lease_);
3491}
3492
3493void
3495 ctx.hosts_.clear();
3496
3497 // If there is no subnet, there is nothing to do.
3498 if (!ctx.subnet_) {
3499 return;
3500 }
3501
3502 auto subnet = ctx.subnet_;
3503
3504 std::map<SubnetID, ConstHostPtr> host_map;
3505 SharedNetwork4Ptr network;
3506 subnet->getSharedNetwork(network);
3507
3508 // @todo: This code can be trivially optimized.
3509 if (subnet->getReservationsGlobal()) {
3511 if (ghost) {
3512 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
3513
3514 // If we had only to fetch global reservations it is done.
3515 if (!subnet->getReservationsInSubnet()) {
3516 return;
3517 }
3518 }
3519 }
3520
3521 // If the subnet belongs to a shared network it is usually going to be
3522 // more efficient to make a query for all reservations for a particular
3523 // client rather than a query for each subnet within this shared network.
3524 // The only case when it is going to be less efficient is when there are
3525 // more host identifier types in use than subnets within a shared network.
3526 // As it breaks RADIUS use of host caching this can be disabled by the
3527 // host manager.
3528 const bool use_single_query = network &&
3530 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
3531
3532 if (use_single_query) {
3533 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3534 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
3535 &id_pair.second[0],
3536 id_pair.second.size());
3537 // Store the hosts in the temporary map, because some hosts may
3538 // belong to subnets outside of the shared network. We'll need
3539 // to eliminate them.
3540 for (auto host = hosts.begin(); host != hosts.end(); ++host) {
3541 if ((*host)->getIPv4SubnetID() != SUBNET_ID_GLOBAL) {
3542 host_map[(*host)->getIPv4SubnetID()] = *host;
3543 }
3544 }
3545 }
3546 }
3547
3548 // We can only search for the reservation if a subnet has been selected.
3549 while (subnet) {
3550
3551 // Only makes sense to get reservations if the client has access
3552 // to the class and host reservations are enabled for this subnet.
3553 if (subnet->clientSupported(ctx.query_->getClasses()) &&
3554 subnet->getReservationsInSubnet()) {
3555 // Iterate over configured identifiers in the order of preference
3556 // and try to use each of them to search for the reservations.
3557 if (use_single_query) {
3558 if (host_map.count(subnet->getID()) > 0) {
3559 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
3560 }
3561 } else {
3562 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3563 // Attempt to find a host using a specified identifier.
3564 ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
3565 id_pair.first,
3566 &id_pair.second[0],
3567 id_pair.second.size());
3568 // If we found matching host for this subnet.
3569 if (host) {
3570 ctx.hosts_[subnet->getID()] = host;
3571 break;
3572 }
3573 }
3574 }
3575 }
3576
3577 // We need to get to the next subnet if this is a shared network. If it
3578 // is not (a plain subnet), getNextSubnet will return NULL and we're
3579 // done here.
3580 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3581 }
3582}
3583
3586 ConstHostPtr host;
3587 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3588 // Attempt to find a host using a specified identifier.
3589 host = HostMgr::instance().get4(SUBNET_ID_GLOBAL, id_pair.first,
3590 &id_pair.second[0], id_pair.second.size());
3591
3592 // If we found matching global host we're done.
3593 if (host) {
3594 break;
3595 }
3596 }
3597
3598 return (host);
3599}
3600
3602AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
3603 // Find an existing lease for this client. This function will return true
3604 // if there is a conflict with existing lease and the allocation should
3605 // not be continued.
3606 Lease4Ptr client_lease;
3607 findClientLease(ctx, client_lease);
3608
3609 // new_lease will hold the pointer to the lease that we will offer to the
3610 // caller.
3611 Lease4Ptr new_lease;
3612
3613 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
3614
3615 // Check if there is a reservation for the client. If there is, we want to
3616 // assign the reserved address, rather than any other one.
3617 if (hasAddressReservation(ctx)) {
3618
3621 .arg(ctx.query_->getLabel())
3622 .arg(ctx.currentHost()->getIPv4Reservation().toText());
3623
3624 // If the client doesn't have a lease or the leased address is different
3625 // than the reserved one then let's try to allocate the reserved address.
3626 // Otherwise the address that the client has is the one for which it
3627 // has a reservation, so just renew it.
3628 if (!client_lease || (client_lease->addr_ != ctx.currentHost()->getIPv4Reservation())) {
3629 // The call below will return a pointer to the lease for the address
3630 // reserved to this client, if the lease is available, i.e. is not
3631 // currently assigned to any other client.
3632 // Note that we don't remove the existing client's lease at this point
3633 // because this is not a real allocation, we just offer what we can
3634 // allocate in the DHCPREQUEST time.
3635 new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
3636 callout_status);
3637 if (!new_lease) {
3639 .arg(ctx.query_->getLabel())
3640 .arg(ctx.currentHost()->getIPv4Reservation().toText())
3641 .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
3642 "(no lease info)");
3643 }
3644
3645 } else {
3646 new_lease = renewLease4(client_lease, ctx);
3647 }
3648 }
3649
3650 // Client does not have a reservation or the allocation of the reserved
3651 // address has failed, probably because the reserved address is in use
3652 // by another client. If the client has a lease, we will check if we can
3653 // offer this lease to the client. The lease can't be offered in the
3654 // situation when it is reserved for another client or when the address
3655 // is not in the dynamic pool. The former may be the result of adding the
3656 // new reservation for the address used by this client. The latter may
3657 // be due to the client using the reserved out-of-the pool address, for
3658 // which the reservation has just been removed.
3659 if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
3660 !addressReserved(client_lease->addr_, ctx)) {
3661
3664 .arg(ctx.query_->getLabel());
3665
3666 new_lease = renewLease4(client_lease, ctx);
3667 }
3668
3669 // The client doesn't have any lease or the lease can't be offered
3670 // because it is either reserved for some other client or the
3671 // address is not in the dynamic pool.
3672 // Let's use the client's hint (requested IP address), if the client
3673 // has provided it, and try to offer it. This address must not be
3674 // reserved for another client, and must be in the range of the
3675 // dynamic pool.
3676 if (!new_lease && !ctx.requested_address_.isV4Zero() &&
3677 inAllowedPool(ctx, ctx.requested_address_) &&
3678 !addressReserved(ctx.requested_address_, ctx)) {
3679
3682 .arg(ctx.requested_address_.toText())
3683 .arg(ctx.query_->getLabel());
3684
3685 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3686 callout_status);
3687 }
3688
3689 // The allocation engine failed to allocate all of the candidate
3690 // addresses. We will now use the allocator to pick the address
3691 // from the dynamic pool.
3692 if (!new_lease) {
3693
3696 .arg(ctx.query_->getLabel());
3697
3698 new_lease = allocateUnreservedLease4(ctx);
3699 }
3700
3701 // Some of the methods like reuseExpiredLease4 may set the old lease to point
3702 // to the lease which they remove/override. If it is not set, but we have
3703 // found that the client has the lease the client's lease is the one
3704 // to return as an old lease.
3705 if (!ctx.old_lease_ && client_lease) {
3706 ctx.old_lease_ = client_lease;
3707 }
3708
3709 return (new_lease);
3710}
3711
3713AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
3714 // Find an existing lease for this client. This function will return true
3715 // if there is a conflict with existing lease and the allocation should
3716 // not be continued.
3717 Lease4Ptr client_lease;
3718 findClientLease(ctx, client_lease);
3719
3720 // When the client sends the DHCPREQUEST, it should always specify the
3721 // address which it is requesting or renewing. That is, the client should
3722 // either use the requested IP address option or set the ciaddr. However,
3723 // we try to be liberal and allow the clients to not specify an address
3724 // in which case the allocation engine will pick a suitable address
3725 // for the client.
3726 if (!ctx.requested_address_.isV4Zero()) {
3727 // If the client has specified an address, make sure this address
3728 // is not reserved for another client. If it is, stop here because
3729 // we can't allocate this address.
3730 if (addressReserved(ctx.requested_address_, ctx)) {
3731
3734 .arg(ctx.query_->getLabel())
3735 .arg(ctx.requested_address_.toText());
3736
3737 return (Lease4Ptr());
3738 }
3739
3740 } else if (hasAddressReservation(ctx)) {
3741 // The client hasn't specified an address to allocate, so the
3742 // allocation engine needs to find an appropriate address.
3743 // If there is a reservation for the client, let's try to
3744 // allocate the reserved address.
3745 ctx.requested_address_ = ctx.currentHost()->getIPv4Reservation();
3746
3749 .arg(ctx.query_->getLabel())
3750 .arg(ctx.requested_address_.toText());
3751 }
3752
3753 if (!ctx.requested_address_.isV4Zero()) {
3754 // There is a specific address to be allocated. Let's find out if
3755 // the address is in use.
3757 // If the address is in use (allocated and not expired), we check
3758 // if the address is in use by our client or another client.
3759 // If it is in use by another client, the address can't be
3760 // allocated.
3761 if (existing && !existing->expired() &&
3762 !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
3763 ctx.clientid_ : ClientIdPtr())) {
3764
3767 .arg(ctx.query_->getLabel())
3768 .arg(ctx.requested_address_.toText());
3769
3770 return (Lease4Ptr());
3771 }
3772
3773 // If the client has a reservation but it is requesting a different
3774 // address it is possible that the client was offered this different
3775 // address because the reserved address is in use. We will have to
3776 // check if the address is in use.
3777 if (hasAddressReservation(ctx) &&
3778 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) {
3779 existing =
3780 LeaseMgrFactory::instance().getLease4(ctx.currentHost()->getIPv4Reservation());
3781 // If the reserved address is not in use, i.e. the lease doesn't
3782 // exist or is expired, and the client is requesting a different
3783 // address, return NULL. The client should go back to the
3784 // DHCPDISCOVER and the reserved address will be offered.
3785 if (!existing || existing->expired()) {
3786
3789 .arg(ctx.query_->getLabel())
3790 .arg(ctx.currentHost()->getIPv4Reservation().toText())
3791 .arg(ctx.requested_address_.toText());
3792
3793 return (Lease4Ptr());
3794 }
3795 }
3796
3797 // The use of the out-of-pool addresses is only allowed when the requested
3798 // address is reserved for the client. If the address is not reserved one
3799 // and it doesn't belong to the dynamic pool, do not allocate it.
3800 if ((!hasAddressReservation(ctx) ||
3801 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
3802 !inAllowedPool(ctx, ctx.requested_address_)) {
3803
3806 .arg(ctx.query_->getLabel())
3807 .arg(ctx.requested_address_);
3808
3809 return (Lease4Ptr());
3810 }
3811 }
3812
3813 // We have gone through all the checks, so we can now allocate the address
3814 // for the client.
3815
3816 // If the client is requesting an address which is assigned to the client
3817 // let's just renew this address. Also, renew this address if the client
3818 // doesn't request any specific address.
3819 // Added extra checks: the address is reserved or belongs to the dynamic
3820 // pool for the case the pool class has changed before the request.
3821 if (client_lease) {
3822 if (((client_lease->addr_ == ctx.requested_address_) ||
3824 (hasAddressReservation(ctx) ||
3825 inAllowedPool(ctx, client_lease->addr_))) {
3826
3829 .arg(ctx.query_->getLabel())
3830 .arg(ctx.requested_address_);
3831
3832 return (renewLease4(client_lease, ctx));
3833 }
3834 }
3835
3836 // new_lease will hold the pointer to the allocated lease if we allocate
3837 // successfully.
3838 Lease4Ptr new_lease;
3839
3840 // The client doesn't have the lease or it is requesting an address
3841 // which it doesn't have. Let's try to allocate the requested address.
3842 if (!ctx.requested_address_.isV4Zero()) {
3843
3846 .arg(ctx.query_->getLabel())
3847 .arg(ctx.requested_address_.toText());
3848
3849 // The call below will return a pointer to the lease allocated
3850 // for the client if there is no lease for the requested address,
3851 // or the existing lease has expired. If the allocation fails,
3852 // e.g. because the lease is in use, we will return NULL to
3853 // indicate that we were unable to allocate the lease.
3854 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
3855 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3856 callout_status);
3857
3858 } else {
3859
3862 .arg(ctx.query_->getLabel());
3863
3864 // We will only get here if the client didn't specify which
3865 // address it wanted to be allocated. The allocation engine will
3866 // to pick the address from the dynamic pool.
3867 new_lease = allocateUnreservedLease4(ctx);
3868 }
3869
3870 // If we allocated the lease for the client, but the client already had a
3871 // lease, we will need to return the pointer to the previous lease and
3872 // the previous lease needs to be removed from the lease database.
3873 if (new_lease && client_lease) {
3874 ctx.old_lease_ = Lease4Ptr(new Lease4(*client_lease));
3875
3878 .arg(ctx.query_->getLabel())
3879 .arg(client_lease->addr_.toText());
3880
3881 if (LeaseMgrFactory::instance().deleteLease(client_lease)) {
3882 // Need to decrease statistic for assigned addresses.
3883 StatsMgr::instance().addValue(
3884 StatsMgr::generateName("subnet", client_lease->subnet_id_,
3885 "assigned-addresses"),
3886 static_cast<int64_t>(-1));
3887 }
3888 }
3889
3890 // Return the allocated lease or NULL pointer if allocation was
3891 // unsuccessful.
3892 return (new_lease);
3893}
3894
3895uint32_t
3897
3898 // If it's BOOTP, use infinite valid lifetime.
3899 if (ctx.query_->inClass("BOOTP")) {
3900 return (Lease::INFINITY_LFT);
3901 }
3902
3903 // Use the dhcp-lease-time content from the client if it's there.
3904 uint32_t requested_lft = 0;
3905 OptionPtr opt = ctx.query_->getOption(DHO_DHCP_LEASE_TIME);
3906 if (opt) {
3907 OptionUint32Ptr opt_lft = boost::dynamic_pointer_cast<OptionInt<uint32_t> >(opt);
3908 if (opt_lft) {
3909 requested_lft = opt_lft->getValue();
3910 }
3911 }
3912
3913 // If the triplet is specified in one of our classes use it.
3914 // We use the first one we find.
3915 Triplet<uint32_t> candidate_lft;
3916 const ClientClasses classes = ctx.query_->getClasses();
3917 if (!classes.empty()) {
3918 // Let's get class definitions
3919 const ClientClassDictionaryPtr& dict =
3920 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3921
3922 // Iterate over the assigned class defintions.
3923 for (ClientClasses::const_iterator name = classes.cbegin();
3924 name != classes.cend(); ++name) {
3925 ClientClassDefPtr cl = dict->findClass(*name);
3926 if (cl && (!cl->getValid().unspecified())) {
3927 candidate_lft = cl->getValid();
3928 break;
3929 }
3930 }
3931 }
3932
3933 // If no classes specified it, get it from the subnet.
3934 if (!candidate_lft) {
3935 candidate_lft = ctx.subnet_->getValid();
3936 }
3937
3938 // If client requested a value, use the value bounded by
3939 // the candidate triplet.
3940 if (requested_lft > 0) {
3941 return (candidate_lft.get(requested_lft));
3942 }
3943
3944 // Use the candidate's default value.
3945 return (candidate_lft.get());
3946}
3947
3949AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
3950 CalloutHandle::CalloutNextStep& callout_status) {
3951 if (!ctx.hwaddr_) {
3952 isc_throw(BadValue, "Can't create a lease with NULL HW address");
3953 }
3954 if (!ctx.subnet_) {
3955 isc_throw(BadValue, "Can't create a lease without a subnet");
3956 }
3957
3958 // Get the context appropriate valid lifetime.
3959 uint32_t valid_lft = getValidLft(ctx);
3960
3961 time_t now = time(NULL);
3962
3963 ClientIdPtr client_id;
3964 if (ctx.subnet_->getMatchClientId()) {
3965 client_id = ctx.clientid_;
3966 }
3967
3968 Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
3969 valid_lft, now, ctx.subnet_->getID()));
3970
3971 // Set FQDN specific lease parameters.
3972 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
3973 lease->fqdn_rev_ = ctx.rev_dns_update_;
3974 lease->hostname_ = ctx.hostname_;
3975
3976 // Add(update) the extended information on the lease.
3977 static_cast<void>(updateLease4ExtendedInfo(lease, ctx));
3978
3979 // Let's execute all callouts registered for lease4_select
3980 if (ctx.callout_handle_ &&
3981 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
3982
3983 // Use the RAII wrapper to make sure that the callout handle state is
3984 // reset when this object goes out of scope. All hook points must do
3985 // it to prevent possible circular dependency between the callout
3986 // handle and its arguments.
3987 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
3988
3989 // Enable copying options from the packet within hook library.
3990 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
3991
3992 // Pass necessary arguments
3993 // Pass the original client query
3994 ctx.callout_handle_->setArgument("query4", ctx.query_);
3995
3996 // Subnet from which we do the allocation (That's as far as we can go
3997 // with using SubnetPtr to point to Subnet4 object. Users should not
3998 // be confused with dynamic_pointer_casts. They should get a concrete
3999 // pointer (Subnet4Ptr) pointing to a Subnet4 object.
4000 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4001 ctx.callout_handle_->setArgument("subnet4", subnet4);
4002
4003 // Is this solicit (fake = true) or request (fake = false)
4004 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4005
4006 // Pass the intended lease as well
4007 ctx.callout_handle_->setArgument("lease4", lease);
4008
4009 // This is the first callout, so no need to clear any arguments
4010 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4011
4012 callout_status = ctx.callout_handle_->getStatus();
4013
4014 // Callouts decided to skip the action. This means that the lease is not
4015 // assigned, so the client will get NoAddrAvail as a result. The lease
4016 // won't be inserted into the database.
4017 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4019 return (Lease4Ptr());
4020 }
4021
4022 // Let's use whatever callout returned. Hopefully it is the same lease
4023 // we handled to it.
4024 ctx.callout_handle_->getArgument("lease4", lease);
4025 }
4026
4027 if (!ctx.fake_allocation_) {
4028 // That is a real (REQUEST) allocation
4029 bool status = LeaseMgrFactory::instance().addLease(lease);
4030 if (status) {
4031
4032 // The lease insertion succeeded, let's bump up the statistic.
4033 StatsMgr::instance().addValue(
4034 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4035 "assigned-addresses"),
4036 static_cast<int64_t>(1));
4037 StatsMgr::instance().addValue(
4038 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4039 "cumulative-assigned-addresses"),
4040 static_cast<int64_t>(1));
4041 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4042 static_cast<int64_t>(1));
4043
4044 return (lease);
4045 } else {
4046 // One of many failures with LeaseMgr (e.g. lost connection to the
4047 // database, database failed etc.). One notable case for that
4048 // is that we are working in multi-process mode and we lost a race
4049 // (some other process got that address first)
4050 return (Lease4Ptr());
4051 }
4052 } else {
4053 // That is only fake (DISCOVER) allocation
4054
4055 // It is for OFFER only. We should not insert the lease and callers
4056 // have already verified the lease does not exist in the database.
4057 return (lease);
4058 }
4059}
4060
4062AllocEngine::renewLease4(const Lease4Ptr& lease,
4064 if (!lease) {
4065 isc_throw(BadValue, "null lease specified for renewLease4");
4066 }
4067
4068 // Let's keep the old data. This is essential if we are using memfile
4069 // (the lease returned points directly to the lease4 object in the database)
4070 // We'll need it if we want to skip update (i.e. roll back renewal)
4072 Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
4073 ctx.old_lease_.reset(new Lease4(*old_values));
4074
4075 // Update the lease with the information from the context.
4076 // If there was no significant changes, try reuse.
4077 lease->reuseable_valid_lft_ = 0;
4078 if (!updateLease4Information(lease, ctx)) {
4079 setLeaseReusable(lease, ctx);
4080 }
4081
4082 if (!ctx.fake_allocation_) {
4083 // If the lease is expired we have to reclaim it before
4084 // re-assigning it to the client. The lease reclamation
4085 // involves execution of hooks and DNS update.
4086 if (ctx.old_lease_->expired()) {
4087 reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
4088 }
4089
4090 lease->state_ = Lease::STATE_DEFAULT;
4091 }
4092
4093 bool skip = false;
4094 // Execute all callouts registered for lease4_renew.
4095 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_renew_)) {
4096
4097 // Use the RAII wrapper to make sure that the callout handle state is
4098 // reset when this object goes out of scope. All hook points must do
4099 // it to prevent possible circular dependency between the callout
4100 // handle and its arguments.
4101 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4102
4103 // Enable copying options from the packet within hook library.
4104 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4105
4106 // Subnet from which we do the allocation. Convert the general subnet
4107 // pointer to a pointer to a Subnet4. Note that because we are using
4108 // boost smart pointers here, we need to do the cast using the boost
4109 // version of dynamic_pointer_cast.
4110 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4111
4112 // Pass the parameters. Note the clientid is passed only if match-client-id
4113 // is set. This is done that way, because the lease4-renew hook point is
4114 // about renewing a lease and the configuration parameter says the
4115 // client-id should be ignored. Hence no clientid value if match-client-id
4116 // is false.
4117 ctx.callout_handle_->setArgument("query4", ctx.query_);
4118 ctx.callout_handle_->setArgument("subnet4", subnet4);
4119 ctx.callout_handle_->setArgument("clientid", subnet4->getMatchClientId() ?
4120 ctx.clientid_ : ClientIdPtr());
4121 ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
4122
4123 // Pass the lease to be updated
4124 ctx.callout_handle_->setArgument("lease4", lease);
4125
4126 // Call all installed callouts
4127 HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
4128 *ctx.callout_handle_);
4129
4130 // Callouts decided to skip the next processing step. The next
4131 // processing step would actually renew the lease, so skip at this
4132 // stage means "keep the old lease as it is".
4133 if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4134 skip = true;
4137 }
4138
4140 }
4141
4142 if (!ctx.fake_allocation_ && !skip && (lease->reuseable_valid_lft_ == 0)) {
4143 // for REQUEST we do update the lease
4145
4146 // We need to account for the re-assignment of The lease.
4147 if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
4148 StatsMgr::instance().addValue(
4149 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4150 "assigned-addresses"),
4151 static_cast<int64_t>(1));
4152 StatsMgr::instance().addValue(
4153 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4154 "cumulative-assigned-addresses"),
4155 static_cast<int64_t>(1));
4156 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4157 static_cast<int64_t>(1));
4158 }
4159 }
4160 if (skip) {
4161 // Rollback changes (really useful only for memfile)
4163 *lease = *old_values;
4164 }
4165
4166 return (lease);
4167}
4168
4170AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
4172 CalloutHandle::CalloutNextStep& callout_status) {
4173 if (!expired) {
4174 isc_throw(BadValue, "null lease specified for reuseExpiredLease");
4175 }
4176
4177 if (!ctx.subnet_) {
4178 isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
4179 }
4180
4181 if (!ctx.fake_allocation_) {
4182 // The expired lease needs to be reclaimed before it can be reused.
4183 // This includes declined leases for which probation period has
4184 // elapsed.
4185 reclaimExpiredLease(expired, ctx.callout_handle_);
4186 expired->state_ = Lease::STATE_DEFAULT;
4187 }
4188
4189 expired->reuseable_valid_lft_ = 0;
4190 static_cast<void>(updateLease4Information(expired, ctx));
4191
4194 .arg(ctx.query_->getLabel())
4195 .arg(expired->toText());
4196
4197 // Let's execute all callouts registered for lease4_select
4198 if (ctx.callout_handle_ &&
4199 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4200
4201 // Enable copying options from the packet within hook library.
4202 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4203
4204 // Use the RAII wrapper to make sure that the callout handle state is
4205 // reset when this object goes out of scope. All hook points must do
4206 // it to prevent possible circular dependency between the callout
4207 // handle and its arguments.
4208 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4209
4210 // Pass necessary arguments
4211 // Pass the original client query
4212 ctx.callout_handle_->setArgument("query4", ctx.query_);
4213
4214 // Subnet from which we do the allocation. Convert the general subnet
4215 // pointer to a pointer to a Subnet4. Note that because we are using
4216 // boost smart pointers here, we need to do the cast using the boost
4217 // version of dynamic_pointer_cast.
4218 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4219 ctx.callout_handle_->setArgument("subnet4", subnet4);
4220
4221 // Is this solicit (fake = true) or request (fake = false)
4222 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4223
4224 // The lease that will be assigned to a client
4225 ctx.callout_handle_->setArgument("lease4", expired);
4226
4227 // Call the callouts
4228 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4229
4230 callout_status = ctx.callout_handle_->getStatus();
4231
4232 // Callouts decided to skip the action. This means that the lease is not
4233 // assigned, so the client will get NoAddrAvail as a result. The lease
4234 // won't be inserted into the database.
4235 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4238 return (Lease4Ptr());
4239 }
4240
4242
4243 // Let's use whatever callout returned. Hopefully it is the same lease
4244 // we handed to it.
4245 ctx.callout_handle_->getArgument("lease4", expired);
4246 }
4247
4248 if (!ctx.fake_allocation_) {
4249 // for REQUEST we do update the lease
4251
4252 // We need to account for the re-assignment of The lease.
4253 StatsMgr::instance().addValue(
4254 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4255 "assigned-addresses"),
4256 static_cast<int64_t>(1));
4257 StatsMgr::instance().addValue(
4258 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4259 "cumulative-assigned-addresses"),
4260 static_cast<int64_t>(1));
4261 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4262 static_cast<int64_t>(1));
4263 }
4264
4265 // We do nothing for SOLICIT. We'll just update database when
4266 // the client gets back to us with REQUEST message.
4267
4268 // it's not really expired at this stage anymore - let's return it as
4269 // an updated lease
4270 return (expired);
4271}
4272
4274AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
4275 CalloutHandle::CalloutNextStep& callout_status) {
4276 ctx.conflicting_lease_.reset();
4277
4278 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4279 if (exist_lease) {
4280 if (exist_lease->expired()) {
4281 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4282 // reuseExpiredLease4() will reclaim the use which will
4283 // queue an NCR remove it needed. Clear the DNS fields in
4284 // the old lease to avoid a redundant remove in server logic.
4285 ctx.old_lease_->hostname_.clear();
4286 ctx.old_lease_->fqdn_fwd_ = false;
4287 ctx.old_lease_->fqdn_rev_ = false;
4288 return (reuseExpiredLease4(exist_lease, ctx, callout_status));
4289
4290 } else {
4291 // If there is a lease and it is not expired, pass this lease back
4292 // to the caller in the context. The caller may need to know
4293 // which lease we're conflicting with.
4294 ctx.conflicting_lease_ = exist_lease;
4295 }
4296
4297 } else {
4298 return (createLease4(ctx, candidate, callout_status));
4299 }
4300 return (Lease4Ptr());
4301}
4302
4304AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
4305 Lease4Ptr new_lease;
4307 Subnet4Ptr subnet = ctx.subnet_;
4308
4309 // Need to check if the subnet belongs to a shared network. If so,
4310 // we might be able to find a better subnet for lease allocation,
4311 // for which it is more likely that there are some leases available.
4312 // If we stick to the selected subnet, we may end up walking over
4313 // the entire subnet (or more subnets) to discover that the address
4314 // pools have been exhausted. Using a subnet from which an address
4315 // was assigned most recently is an optimization which increases
4316 // the likelihood of starting from the subnet which address pools
4317 // are not exhausted.
4318 SharedNetwork4Ptr network;
4319 ctx.subnet_->getSharedNetwork(network);
4320 if (network) {
4321 // This would try to find a subnet with the same set of classes
4322 // as the current subnet, but with the more recent "usage timestamp".
4323 // This timestamp is only updated for the allocations made with an
4324 // allocator (unreserved lease allocations), not the static
4325 // allocations or requested addresses.
4326 ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
4327 }
4328
4329 // We have the choice in the order checking the lease and
4330 // the reservation. The default is to begin by the lease
4331 // if the multi-threading is disabled.
4332 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
4333
4334 Subnet4Ptr original_subnet = subnet;
4335
4336 uint64_t total_attempts = 0;
4337
4338 // The following counter tracks the number of subnets with matching client
4339 // classes from which the allocation engine attempted to assign leases.
4340 uint64_t subnets_with_unavail_leases = 0;
4341 // The following counter tracks the number of subnets in which there were
4342 // no matching pools for the client.
4343 uint64_t subnets_with_unavail_pools = 0;
4344
4345 while (subnet) {
4346 ClientIdPtr client_id;
4347 if (subnet->getMatchClientId()) {
4348 client_id = ctx.clientid_;
4349 }
4350
4351 uint64_t possible_attempts =
4352 subnet->getPoolCapacity(Lease::TYPE_V4,
4353 ctx.query_->getClasses());
4354
4355 // If the number of tries specified in the allocation engine constructor
4356 // is set to 0 (unlimited) or the pools capacity is lower than that number,
4357 // let's use the pools capacity as the maximum number of tries. Trying
4358 // more than the actual pools capacity is a waste of time. If the specified
4359 // number of tries is lower than the pools capacity, use that number.
4360 uint64_t max_attempts = ((attempts_ == 0 || possible_attempts < attempts_) ? possible_attempts : attempts_);
4361
4362 if (max_attempts > 0) {
4363 // If max_attempts is greater than 0, there are some pools in this subnet
4364 // from which we can potentially get a lease.
4365 ++subnets_with_unavail_leases;
4366 } else {
4367 // If max_attempts is 0, it is an indication that there are no pools
4368 // in the subnet from which we can get a lease.
4369 ++subnets_with_unavail_pools;
4370 }
4371
4372 CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
4373
4374 for (uint64_t i = 0; i < max_attempts; ++i) {
4375
4376 ++total_attempts;
4377
4378 IOAddress candidate = allocator->pickAddress(subnet,
4379 ctx.query_->getClasses(),
4380 client_id,
4381 ctx.requested_address_);
4382 // First check for reservation when it is the choice.
4383 if (check_reservation_first && addressReserved(candidate, ctx)) {
4384 // Don't allocate.
4385 continue;
4386 }
4387
4388 // Check if the resource is busy i.e. can be being allocated
4389 // by another thread to another client.
4390 ResourceHandler4 resource_handler;
4391 if (MultiThreadingMgr::instance().getMode() &&
4392 !resource_handler.tryLock4(candidate)) {
4393 // Don't allocate.
4394 continue;
4395 }
4396
4397 // Check for an existing lease for the candidate address.
4398 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4399 if (!exist_lease) {
4400 // No existing lease, is it reserved?
4401 if (check_reservation_first || !addressReserved(candidate, ctx)) {
4402 // Not reserved use it.
4403 new_lease = createLease4(ctx, candidate, callout_status);
4404 }
4405 } else {
4406 // An lease exists, is expired, and not reserved use it.
4407 if (exist_lease->expired() &&
4408 (check_reservation_first || !addressReserved(candidate, ctx))) {
4409 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4410 new_lease = reuseExpiredLease4(exist_lease, ctx, callout_status);
4411 }
4412 }
4413
4414 // We found a lease we can use, return it.
4415 if (new_lease) {
4416 return (new_lease);
4417 }
4418
4419 if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
4420 // Don't retry when the callout status is not continue.
4421 subnet.reset();
4422 break;
4423 }
4424 }
4425
4426 // This pointer may be set to NULL if hooks set SKIP status.
4427 if (subnet) {
4428 subnet = subnet->getNextSubnet(original_subnet, ctx.query_->getClasses());
4429
4430 if (subnet) {
4431 ctx.subnet_ = subnet;
4432 }
4433 }
4434 }
4435
4436 if (network) {
4437 // The client is in the shared network. Let's log the high level message
4438 // indicating which shared network the client belongs to.
4440 .arg(ctx.query_->getLabel())
4441 .arg(network->getName())
4442 .arg(subnets_with_unavail_leases)
4443 .arg(subnets_with_unavail_pools);
4444
4445 } else {
4446 // The client is not connected to a shared network. It is connected
4447 // to a subnet. Let's log some details about the subnet.
4449 .arg(ctx.query_->getLabel())
4450 .arg(ctx.subnet_->toText())
4451 .arg(ctx.subnet_->getID())
4452 .arg(ctx.subnet_->getSharedNetworkName());
4453 }
4454 if (total_attempts == 0) {
4455 // In this case, it seems that none of the pools in the subnets could
4456 // be used for that client, both in case the client is connected to
4457 // a shared network or to a single subnet. Apparently, the client was
4458 // rejected to use the pools because of the client classes' mismatch.
4460 .arg(ctx.query_->getLabel());
4461 } else {
4462 // This is an old log message which provides a number of attempts
4463 // made by the allocation engine to allocate a lease. The only case
4464 // when we don't want to log this message is when the number of
4465 // attempts is zero (condition above), because it would look silly.
4467 .arg(ctx.query_->getLabel())
4468 .arg(total_attempts);
4469 }
4470
4471 const ClientClasses& classes = ctx.query_->getClasses();
4472 if (!classes.empty()) {
4474 .arg(ctx.query_->getLabel())
4475 .arg(classes.toText());
4476 }
4477
4478 return (new_lease);
4479}
4480
4481bool
4482AllocEngine::updateLease4Information(const Lease4Ptr& lease,
4483 AllocEngine::ClientContext4& ctx) const {
4484 bool changed = false;
4485 if (lease->subnet_id_ != ctx.subnet_->getID()) {
4486 changed = true;
4487 lease->subnet_id_ = ctx.subnet_->getID();
4488 }
4489 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
4490 (ctx.hwaddr_ &&
4491 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
4492 changed = true;
4493 lease->hwaddr_ = ctx.hwaddr_;
4494 }
4495 if (ctx.subnet_->getMatchClientId() && ctx.clientid_) {
4496 if (!lease->client_id_ || (*ctx.clientid_ != *lease->client_id_)) {
4497 changed = true;
4498 lease->client_id_ = ctx.clientid_;
4499 }
4500 } else if (lease->client_id_) {
4501 changed = true;
4502 lease->client_id_ = ClientIdPtr();
4503 }
4504 lease->cltt_ = time(NULL);
4505
4506 // Get the context appropriate valid lifetime.
4507 lease->valid_lft_ = getValidLft(ctx);
4508
4509 // Reduced valid lifetime is a significant change.
4510 if (lease->valid_lft_ < lease->current_valid_lft_) {
4511 changed = true;
4512 }
4513
4514 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
4515 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
4516 (lease->hostname_ != ctx.hostname_)) {
4517 changed = true;
4518 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4519 lease->fqdn_rev_ = ctx.rev_dns_update_;
4520 lease->hostname_ = ctx.hostname_;
4521 }
4522
4523 // Add(update) the extended information on the lease.
4524 if (updateLease4ExtendedInfo(lease, ctx)) {
4525 changed = true;
4526 }
4527
4528 return (changed);
4529}
4530
4531bool
4533 const AllocEngine::ClientContext4& ctx) const {
4534 bool changed = false;
4535
4536 // If storage is not enabled then punt.
4537 if (!ctx.subnet_->getStoreExtendedInfo()) {
4538 return (changed);
4539 }
4540
4541 // Look for relay agent information option (option 82)
4542 OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS);
4543 if (!rai) {
4544 // Pkt4 doesn't have it, so nothing to store (or update).
4545 return (changed);
4546 }
4547
4548 // Create a StringElement with the hex string for relay-agent-info.
4549 ElementPtr relay_agent(new StringElement(rai->toHexString()));
4550
4551 // Now we wrap the agent info in a map. This allows for future expansion.
4552 ElementPtr extended_info = Element::createMap();
4553 extended_info->set("relay-agent-info", relay_agent);
4554
4555 // Get a writable copy of the lease's current user context.
4556 ElementPtr user_context;
4557 if (lease->getContext()) {
4558 user_context = UserContext::toElement(lease->getContext());
4559 } else {
4560 user_context = Element::createMap();
4561 }
4562
4563 // Add/replace the extended info entry.
4564 ConstElementPtr old_extended_info = user_context->get("ISC");
4565 if (!old_extended_info || (*old_extended_info != *extended_info)) {
4566 changed = true;
4567 user_context->set("ISC", extended_info);
4568 }
4569
4570 // Update the lease's user_context.
4571 lease->setContext(user_context);
4572
4573 return (changed);
4574}
4575
4576bool
4578 const AllocEngine::ClientContext6& ctx) const {
4579 bool changed = false;
4580
4581 // If storage is not enabled then punt.
4582 if (!ctx.subnet_->getStoreExtendedInfo()) {
4583 return (changed);
4584 }
4585
4586 // If we do not have relay information, then punt.
4587 if (ctx.query_->relay_info_.empty()) {
4588 return (changed);
4589 }
4590
4591 // We need to convert the vector of RelayInfo instances in
4592 // into an Element hierarchy like this:
4593 // "relay-info": [
4594 // {
4595 // "hop": 123,
4596 // "link": "2001:db8::1",
4597 // "peer": "2001:db8::2",
4598 // "options": "0x..."
4599 // },..]
4600 //
4601 ElementPtr relay_list = Element::createList();
4602 for (auto relay : ctx.query_->relay_info_) {
4603 ElementPtr relay_elem = Element::createMap();
4604 relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
4605 relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
4606 relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
4607
4608 // If there are relay options, we'll pack them into a buffer and then
4609 // convert that into a hex string. If there are no options, we omit
4610 // then entry.
4611 if (!relay.options_.empty()) {
4612 OutputBuffer buf(128);
4613 LibDHCP::packOptions6(buf, relay.options_);
4614
4615 if (buf.getLength() > 0) {
4616 const uint8_t* cp = static_cast<const uint8_t*>(buf.getData());
4617 std::vector<uint8_t>bytes;
4618 std::stringstream ss;
4619
4620 bytes.assign(cp, cp + buf.getLength());
4621 ss << "0x" << encode::encodeHex(bytes);
4622 relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
4623 }
4624 }
4625
4626 relay_list->add(relay_elem);
4627 }
4628
4629 // Now we wrap the list of relays in a map. This allows for future expansion.
4630 ElementPtr extended_info = Element::createMap();
4631 extended_info->set("relays", relay_list);
4632
4633 // Get a writable copy of the lease's current user context.
4634 ElementPtr user_context;
4635 if (lease->getContext()) {
4636 user_context = UserContext::toElement(lease->getContext());
4637 } else {
4638 user_context = Element::createMap();
4639 }
4640
4641 // Add/replace the extended info entry.
4642 ConstElementPtr old_extended_info = user_context->get("ISC");
4643 if (!old_extended_info || (*old_extended_info != *extended_info)) {
4644 changed = true;
4645 user_context->set("ISC", extended_info);
4646 }
4647
4648 // Update the lease's user_context.
4649 lease->setContext(user_context);
4650
4651 return (changed);
4652}
4653
4654void
4655AllocEngine::setLeaseReusable(const Lease4Ptr& lease,
4656 const ClientContext4& ctx) const {
4657 // Sanity.
4658 lease->reuseable_valid_lft_ = 0;
4659 const Subnet4Ptr& subnet = ctx.subnet_;
4660 if (!subnet) {
4661 return;
4662 }
4663 if (lease->state_ != Lease::STATE_DEFAULT) {
4664 return;
4665 }
4666
4667 // Always reuse infinite lifetime leases.
4668 if (lease->valid_lft_ == Lease::INFINITY_LFT) {
4669 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
4670 return;
4671 }
4672
4673 // Refuse time not going forward.
4674 if (lease->cltt_ < lease->current_cltt_) {
4675 return;
4676 }
4677
4678 uint32_t age = lease->cltt_ - lease->current_cltt_;
4679 // Already expired.
4680 if (age >= lease->current_valid_lft_) {
4681 return;
4682 }
4683
4684 // Try cache max age.
4685 uint32_t max_age = 0;
4686 if (!subnet->getCacheMaxAge().unspecified()) {
4687 max_age = subnet->getCacheMaxAge().get();
4688 if ((max_age == 0) || (age > max_age)) {
4689 return;
4690 }
4691 }
4692
4693 // Try cache threshold.
4694 if (!subnet->getCacheThreshold().unspecified()) {
4695 double threshold = subnet->getCacheThreshold().get();
4696 if ((threshold <= 0.) || (threshold > 1.)) {
4697 return;
4698 }
4699 max_age = lease->valid_lft_ * threshold;
4700 if (age > max_age) {
4701 return;
4702 }
4703 }
4704
4705 // No cache.
4706 if (max_age == 0) {
4707 return;
4708 }
4709
4710 // Seems to be reusable.
4711 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
4712}
4713
4714void
4715AllocEngine::setLeaseReusable(const Lease6Ptr& lease,
4716 uint32_t current_preferred_lft,
4717 const ClientContext6& ctx) const {
4718 // Sanity.
4719 lease->reuseable_valid_lft_ = 0;
4720 lease->reuseable_preferred_lft_ = 0;
4721 const Subnet6Ptr& subnet = ctx.subnet_;
4722 if (!subnet) {
4723 return;
4724 }
4725 if (lease->state_ != Lease::STATE_DEFAULT) {
4726 return;
4727 }
4728
4729 // Refuse time not going forward.
4730 if (lease->cltt_ < lease->current_cltt_) {
4731 return;
4732 }
4733
4734 uint32_t age = lease->cltt_ - lease->current_cltt_;
4735 // Already expired.
4736 if (age >= lease->current_valid_lft_) {
4737 return;
4738 }
4739
4740 // Try cache max age.
4741 uint32_t max_age = 0;
4742 if (!subnet->getCacheMaxAge().unspecified()) {
4743 max_age = subnet->getCacheMaxAge().get();
4744 if ((max_age == 0) || (age > max_age)) {
4745 return;
4746 }
4747 }
4748
4749 // Try cache threshold.
4750 if (!subnet->getCacheThreshold().unspecified()) {
4751 double threshold = subnet->getCacheThreshold().get();
4752 if ((threshold <= 0.) || (threshold > 1.)) {
4753 return;
4754 }
4755 max_age = lease->valid_lft_ * threshold;
4756 if (age > max_age) {
4757 return;
4758 }
4759 }
4760
4761 // No cache.
4762 if (max_age == 0) {
4763 return;
4764 }
4765
4766 // Seems to be reusable.
4767 if ((current_preferred_lft == Lease::INFINITY_LFT) ||
4768 (current_preferred_lft == 0)) {
4769 // Keep these values.
4770 lease->reuseable_preferred_lft_ = current_preferred_lft;
4771 } else if (current_preferred_lft > age) {
4772 lease->reuseable_preferred_lft_ = current_preferred_lft - age;
4773 } else {
4774 // Can be a misconfiguration so stay safe...
4775 return;
4776 }
4777 if (lease->current_valid_lft_ == Lease::INFINITY_LFT) {
4778 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
4779 } else {
4780 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
4781 }
4782}
4783
4784} // namespace dhcp
4785} // 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.
Notes: IntElement type is changed to int64_t.
Definition: data.h:588
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:990
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:43
std::list< ClientClass >::const_iterator const_iterator
Type of iterators.
Definition: classify.h:47
bool empty() const
Check if classes is empty.
Definition: classify.h:73
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition: classify.cc:40
const_iterator cbegin() const
Iterator to the first element.
Definition: classify.h:86
const_iterator cend() const
Iterator to the past the end element.
Definition: classify.h:91
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition: srv_config.h:46
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:222
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:862
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:394
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:585
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.
T get(T hint) const
Returns value with a hint.
Definition: triplet.h:99
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:121
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
Write mutex RAII handler.
@ D6O_CLIENT_FQDN
Definition: dhcp6.h:59
@ DHCPV6_RENEW
Definition: dhcp6.h:208
#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:1152
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:513
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:522
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:492
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition: lease.h:640
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:670
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:162
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_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
Unique identifier for a subnet (both v4 and v6)
Definition: lease.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:487
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:283
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 ...
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.
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.
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:536
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:533
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:656
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:646
ConstHostPtr currentHost() const
Returns host from the most preferred subnet.
DuidPtr duid_
Client identifier.
Definition: alloc_engine.h:485
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:524
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:477
Subnet6Ptr host_subnet_
Subnet from which host reservations should be retrieved.
Definition: alloc_engine.h:482
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:521
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
IdentifierList host_identifiers_
A list holding host identifiers extracted from a message received by the server.
Definition: alloc_engine.h:492
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
Definition: alloc_engine.h:499
Structure that holds a lease for IPv4 address.
Definition: lease.h:294
Structure that holds a lease for IPv6 address and/or prefix.
Definition: lease.h:503
a common structure for IPv4 and IPv6 leases
Definition: lease.h:35
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire)
Definition: lease.h:38
static const uint32_t STATE_DEFAULT
A lease in the default state.
Definition: lease.h:73
static const uint32_t STATE_DECLINED
Declined lease.
Definition: lease.h:76
static const uint32_t STATE_EXPIRED_RECLAIMED
Expired and reclaimed lease.
Definition: lease.h:79
Type
Type of lease or pool.
Definition: lease.h:50
@ TYPE_TA
the lease contains temporary IPv6 address
Definition: lease.h:52
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition: lease.h:53
@ TYPE_V4
IPv4 lease.
Definition: lease.h:54
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition: lease.h:51
static std::string typeToText(Type type)
returns text representation of a lease type
Definition: lease.cc:52