Kea 2.7.4
alloc_engine.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
10#include <dhcp/dhcp6.h>
11#include <dhcp/pkt4.h>
12#include <dhcp/pkt6.h>
13#include <dhcp/option_int.h>
14#include <dhcp_ddns/ncr_msg.h>
17#include <dhcpsrv/cfgmgr.h>
18#include <dhcpsrv/dhcpsrv_log.h>
19#include <dhcpsrv/host_mgr.h>
20#include <dhcpsrv/host.h>
24#include <dhcpsrv/network.h>
28#include <hooks/hooks_manager.h>
30#include <stats/stats_mgr.h>
31#include <util/encode/encode.h>
32#include <util/stopwatch.h>
33#include <hooks/server_hooks.h>
34
35#include <boost/foreach.hpp>
36#include <boost/make_shared.hpp>
37
38#include <algorithm>
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::db;
47using namespace isc::dhcp;
48using namespace isc::dhcp_ddns;
49using namespace isc::hooks;
50using namespace isc::stats;
51using namespace isc::util;
52using namespace isc::data;
53namespace ph = std::placeholders;
54
55namespace {
56
58struct AllocEngineHooks {
59 int hook_index_lease4_select_;
60 int hook_index_lease4_renew_;
61 int hook_index_lease4_expire_;
62 int hook_index_lease4_recover_;
63 int hook_index_lease6_select_;
64 int hook_index_lease6_renew_;
65 int hook_index_lease6_rebind_;
66 int hook_index_lease6_expire_;
67 int hook_index_lease6_recover_;
68
70 AllocEngineHooks() {
71 hook_index_lease4_select_ = HooksManager::registerHook("lease4_select");
72 hook_index_lease4_renew_ = HooksManager::registerHook("lease4_renew");
73 hook_index_lease4_expire_ = HooksManager::registerHook("lease4_expire");
74 hook_index_lease4_recover_= HooksManager::registerHook("lease4_recover");
75 hook_index_lease6_select_ = HooksManager::registerHook("lease6_select");
76 hook_index_lease6_renew_ = HooksManager::registerHook("lease6_renew");
77 hook_index_lease6_rebind_ = HooksManager::registerHook("lease6_rebind");
78 hook_index_lease6_expire_ = HooksManager::registerHook("lease6_expire");
79 hook_index_lease6_recover_= HooksManager::registerHook("lease6_recover");
80 }
81};
82
83// Declare a Hooks object. As this is outside any function or method, it
84// will be instantiated (and the constructor run) when the module is loaded.
85// As a result, the hook indexes will be defined before any method in this
86// module is called.
87AllocEngineHooks Hooks;
88
89} // namespace
90
91namespace isc {
92namespace dhcp {
93
95 : attempts_(attempts), incomplete_v4_reclamations_(0),
96 incomplete_v6_reclamations_(0) {
97
98 // Register hook points
99 hook_index_lease4_select_ = Hooks.hook_index_lease4_select_;
100 hook_index_lease6_select_ = Hooks.hook_index_lease6_select_;
101}
102
103} // end of namespace isc::dhcp
104} // end of namespace isc
105
106namespace {
107
117getIPv6Resrv(const SubnetID& subnet_id, const IOAddress& address) {
118 ConstHostCollection reserved;
119 // The global parameter ip-reservations-unique controls whether it is allowed
120 // to specify multiple reservations for the same IP address or delegated prefix
121 // or IP reservations must be unique. Some host backends do not support the
122 // former, thus we can't always use getAll6 calls to get the reservations
123 // for the given IP. When we're in the default mode, when IP reservations
124 // are unique, we should call get6 (supported by all backends). If we're in
125 // the mode in which non-unique reservations are allowed the backends which
126 // don't support it are not used and we can safely call getAll6.
127 if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
128 try {
129 // Reservations are unique. It is safe to call get6 to get the unique host.
130 auto host = HostMgr::instance().get6(subnet_id, address);
131 if (host) {
132 reserved.push_back(host);
133 }
134 } catch (const MultipleRecords& ex) {
136 .arg(address)
137 .arg(subnet_id)
138 .arg(ex.what());
139 throw;
140 }
141 } else {
142 auto hosts = HostMgr::instance().getAll6(subnet_id, address);
143 reserved.insert(reserved.end(), hosts.begin(), hosts.end());
144 }
145 return (reserved);
146}
147
160bool
161inAllowedPool(AllocEngine::ClientContext6& ctx, const Lease::Type& lease_type,
162 const IOAddress& address, bool check_subnet) {
163 // If the subnet belongs to a shared network we will be iterating
164 // over the subnets that belong to this shared network.
165 Subnet6Ptr current_subnet = ctx.subnet_;
166 auto const& classes = ctx.query_->getClasses();
167
168 while (current_subnet) {
169 if (current_subnet->clientSupported(classes)) {
170 if (check_subnet) {
171 if (current_subnet->inPool(lease_type, address)) {
172 return (true);
173 }
174 } else {
175 if (current_subnet->inPool(lease_type, address, classes)) {
176 return (true);
177 }
178 }
179 }
180
181 current_subnet = current_subnet->getNextSubnet(ctx.subnet_);
182 }
183
184 return (false);
185}
186
187}
188
189// ##########################################################################
190// # DHCPv6 lease allocation code starts here.
191// ##########################################################################
192
193namespace isc {
194namespace dhcp {
195
197 : query_(), fake_allocation_(false),
198 early_global_reservations_lookup_(false), subnet_(), host_subnet_(),
199 duid_(), hwaddr_(), host_identifiers_(), hosts_(),
200 fwd_dns_update_(false), rev_dns_update_(false), hostname_(),
201 callout_handle_(), ias_(), ddns_params_() {
202}
203
205 const DuidPtr& duid,
206 const bool fwd_dns,
207 const bool rev_dns,
208 const std::string& hostname,
209 const bool fake_allocation,
210 const Pkt6Ptr& query,
211 const CalloutHandlePtr& callout_handle)
212 : query_(query), fake_allocation_(fake_allocation),
213 early_global_reservations_lookup_(false), subnet_(subnet),
214 duid_(duid), hwaddr_(), host_identifiers_(), hosts_(),
215 fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname),
216 callout_handle_(callout_handle), allocated_resources_(), new_leases_(),
217 ias_(), ddns_params_() {
218
219 // Initialize host identifiers.
220 if (duid) {
221 addHostIdentifier(Host::IDENT_DUID, duid->getDuid());
222 }
223}
224
226 : iaid_(0), type_(Lease::TYPE_NA), hints_(), old_leases_(),
227 changed_leases_(), new_resources_(), ia_rsp_() {
228}
229
230void
233 const uint8_t prefix_len,
234 const uint32_t preferred,
235 const uint32_t valid) {
236 hints_.push_back(Resource(prefix, prefix_len, preferred, valid));
237}
238
239void
242 if (!iaaddr) {
243 isc_throw(BadValue, "IAADDR option pointer is null.");
244 }
245 addHint(iaaddr->getAddress(), 128,
246 iaaddr->getPreferred(), iaaddr->getValid());
247}
248
249void
251IAContext::addHint(const Option6IAPrefixPtr& iaprefix) {
252 if (!iaprefix) {
253 isc_throw(BadValue, "IAPREFIX option pointer is null.");
254 }
255 addHint(iaprefix->getAddress(), iaprefix->getLength(),
256 iaprefix->getPreferred(), iaprefix->getValid());
257}
258
259void
262 const uint8_t prefix_len) {
263 static_cast<void>(new_resources_.insert(Resource(prefix, prefix_len)));
264}
265
266bool
269 const uint8_t prefix_len) const {
270 return (static_cast<bool>(new_resources_.count(Resource(prefix,
271 prefix_len))));
272}
273
274void
277 const uint8_t prefix_len) {
278 static_cast<void>(allocated_resources_.insert(Resource(prefix,
279 prefix_len)));
280}
281
282bool
284isAllocated(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const {
285 return (static_cast<bool>
286 (allocated_resources_.count(Resource(prefix, prefix_len))));
287}
288
292 if (subnet && subnet->getReservationsInSubnet()) {
293 auto host = hosts_.find(subnet->getID());
294 if (host != hosts_.cend()) {
295 return (host->second);
296 }
297 }
298
299 return (globalHost());
300}
301
305 if (subnet && subnet_->getReservationsGlobal()) {
306 auto host = hosts_.find(SUBNET_ID_GLOBAL);
307 if (host != hosts_.cend()) {
308 return (host->second);
309 }
310 }
311
312 return (ConstHostPtr());
313}
314
315bool
317 ConstHostPtr ghost = globalHost();
318 return (ghost && ghost->hasReservation(resv));
319}
320
323 // We already have it return it unless the context subnet has changed.
324 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
325 return (ddns_params_);
326 }
327
328 // Doesn't exist yet or is stale, (re)create it.
329 if (subnet_) {
330 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
331 return (ddns_params_);
332 }
333
334 // Asked for it without a subnet? This case really shouldn't occur but
335 // for now let's return an instance with default values.
336 return (DdnsParamsPtr(new DdnsParams()));
337}
338
339void
341 // If there is no subnet, there is nothing to do.
342 if (!ctx.subnet_) {
343 return;
344 }
345
346 auto subnet = ctx.subnet_;
347
348 // If already done just return.
350 !subnet->getReservationsInSubnet()) {
351 return;
352 }
353
354 // @todo: This code can be trivially optimized.
356 subnet->getReservationsGlobal()) {
358 if (ghost) {
359 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
360
361 // If we had only to fetch global reservations it is done.
362 if (!subnet->getReservationsInSubnet()) {
363 return;
364 }
365 }
366 }
367
368 std::map<SubnetID, ConstHostPtr> host_map;
369 SharedNetwork6Ptr network;
370 subnet->getSharedNetwork(network);
371
372 // If the subnet belongs to a shared network it is usually going to be
373 // more efficient to make a query for all reservations for a particular
374 // client rather than a query for each subnet within this shared network.
375 // The only case when it is going to be less efficient is when there are
376 // more host identifier types in use than subnets within a shared network.
377 // As it breaks RADIUS use of host caching this can be disabled by the
378 // host manager.
379 const bool use_single_query = network &&
380 !HostMgr::instance().getDisableSingleQuery() &&
381 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
382
383 if (use_single_query) {
384 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
385 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
386 &id_pair.second[0],
387 id_pair.second.size());
388 // Store the hosts in the temporary map, because some hosts may
389 // belong to subnets outside of the shared network. We'll need
390 // to eliminate them.
391 for (auto const& host : hosts) {
392 if (host->getIPv6SubnetID() != SUBNET_ID_GLOBAL) {
393 host_map[host->getIPv6SubnetID()] = host;
394 }
395 }
396 }
397 }
398
399 auto const& classes = ctx.query_->getClasses();
400
401 // We can only search for the reservation if a subnet has been selected.
402 while (subnet) {
403
404 // Only makes sense to get reservations if the client has access
405 // to the class and host reservations are enabled for this subnet.
406 if (subnet->clientSupported(classes) && subnet->getReservationsInSubnet()) {
407 // Iterate over configured identifiers in the order of preference
408 // and try to use each of them to search for the reservations.
409 if (use_single_query) {
410 if (host_map.count(subnet->getID()) > 0) {
411 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
412 }
413 } else {
414 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
415 // Attempt to find a host using a specified identifier.
416 ConstHostPtr host = HostMgr::instance().get6(subnet->getID(),
417 id_pair.first,
418 &id_pair.second[0],
419 id_pair.second.size());
420 // If we found matching host for this subnet.
421 if (host) {
422 ctx.hosts_[subnet->getID()] = host;
423 break;
424 }
425 }
426 }
427 }
428
429 // We need to get to the next subnet if this is a shared network. If it
430 // is not (a plain subnet), getNextSubnet will return NULL and we're
431 // done here.
432 subnet = subnet->getNextSubnet(ctx.subnet_, classes);
433 }
434
435 // The hosts can be used by the server to return reserved options to
436 // the DHCP client. Such options must be encapsulated (i.e., they must
437 // include suboptions).
438 for (auto const& host : ctx.hosts_) {
439 host.second->encapsulateOptions();
440 }
441}
442
445 ConstHostPtr host;
446 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
447 // Attempt to find a host using a specified identifier.
448 host = HostMgr::instance().get6(SUBNET_ID_GLOBAL, id_pair.first,
449 &id_pair.second[0], id_pair.second.size());
450
451 // If we found matching global host we're done.
452 if (host) {
453 break;
454 }
455 }
456
457 return (host);
458}
459
462
463 try {
464 if (!ctx.subnet_) {
465 isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation");
466 } else
467 if (!ctx.duid_) {
468 isc_throw(InvalidOperation, "DUID is mandatory for IPv6 lease allocation");
469 }
470
471 // Check if there are existing leases for that shared network and
472 // DUID/IAID.
473 Subnet6Ptr subnet = ctx.subnet_;
474 Lease6Collection all_leases =
475 LeaseMgrFactory::instance().getLeases6(ctx.currentIA().type_,
476 *ctx.duid_,
477 ctx.currentIA().iaid_);
478
479 // Iterate over the leases and eliminate those that are outside of
480 // our shared network.
481 Lease6Collection leases;
482 while (subnet) {
483 for (auto const& l : all_leases) {
484 if ((l)->subnet_id_ == subnet->getID()) {
485 leases.push_back(l);
486 }
487 }
488
489 subnet = subnet->getNextSubnet(ctx.subnet_);
490 }
491
492 // Now do the checks:
493 // Case 1. if there are no leases, and there are reservations...
494 // 1.1. are the reserved addresses are used by someone else?
495 // yes: we have a problem
496 // no: assign them => done
497 // Case 2. if there are leases and there are no reservations...
498 // 2.1 are the leases reserved for someone else?
499 // yes: release them, assign something else
500 // no: renew them => done
501 // Case 3. if there are leases and there are reservations...
502 // 3.1 are the leases matching reservations?
503 // yes: renew them => done
504 // no: release existing leases, assign new ones based on reservations
505 // Case 4/catch-all. if there are no leases and no reservations...
506 // assign new leases
507
508 // Case 1: There are no leases and there's a reservation for this host.
509 if (leases.empty() && !ctx.hosts_.empty()) {
510
513 .arg(ctx.query_->getLabel());
514
515 // Try to allocate leases that match reservations. Typically this will
516 // succeed, except cases where the reserved addresses are used by
517 // someone else.
518 allocateReservedLeases6(ctx, leases);
519
520 leases = updateLeaseData(ctx, leases);
521
522 // If not, we'll need to continue and will eventually fall into case 4:
523 // getting a regular lease. That could happen when we're processing
524 // request from client X, there's a reserved address A for X, but
525 // A is currently used by client Y. We can't immediately reassign A
526 // from X to Y, because Y keeps using it, so X would send Decline right
527 // away. Need to wait till Y renews, then we can release A, so it
528 // will become available for X.
529
530 // Case 2: There are existing leases and there are no reservations.
531 //
532 // There is at least one lease for this client and there are no reservations.
533 // We will return these leases for the client, but we may need to update
534 // FQDN information.
535 } else if (!leases.empty() && ctx.hosts_.empty()) {
536
539 .arg(ctx.query_->getLabel());
540
541 // Check if the existing leases are reserved for someone else.
542 // If they're not, we're ok to keep using them.
543 removeNonmatchingReservedLeases6(ctx, leases);
544
545 leases = updateLeaseData(ctx, leases);
546
547 // If leases are empty at this stage, it means that we used to have
548 // leases for this client, but we checked and those leases are reserved
549 // for someone else, so we lost them. We will need to continue and
550 // will finally end up in case 4 (no leases, no reservations), so we'll
551 // assign something new.
552
553 // Case 3: There are leases and there are reservations.
554 } else if (!leases.empty() && !ctx.hosts_.empty()) {
555
558 .arg(ctx.query_->getLabel());
559
560 // First, check if have leases matching reservations, and add new
561 // leases if we don't have them.
562 allocateReservedLeases6(ctx, leases);
563
564 // leases now contain both existing and new leases that were created
565 // from reservations.
566
567 // Second, let's remove leases that are reserved for someone else.
568 // This applies to any existing leases. This will not happen frequently,
569 // but it may happen with the following chain of events:
570 // 1. client A gets address X;
571 // 2. reservation for client B for address X is made by a administrator;
572 // 3. client A reboots
573 // 4. client A requests the address (X) he got previously
574 removeNonmatchingReservedLeases6(ctx, leases);
575
576 // leases now contain existing and new leases, but we removed those
577 // leases that are reserved for someone else (non-matching reserved).
578
579 // There's one more check to do. Let's remove leases that are not
580 // matching reservations, i.e. if client X has address A, but there's
581 // a reservation for address B, we should release A and reassign B.
582 // Caveat: do this only if we have at least one reserved address.
583 removeNonreservedLeases6(ctx, leases);
584
585 // All checks are done. Let's hope we have some leases left.
586
587 // Update any leases we have left.
588 leases = updateLeaseData(ctx, leases);
589
590 // If we don't have any leases at this stage, it means that we hit
591 // one of the following cases:
592 // - we have a reservation, but it's not for this IAID/ia-type and
593 // we had to return the address we were using
594 // - we have a reservation for this iaid/ia-type, but the reserved
595 // address is currently used by someone else. We can't assign it
596 // yet.
597 // - we had an address, but we just discovered that it's reserved for
598 // someone else, so we released it.
599 }
600
601 if (leases.empty()) {
602 // Case 4/catch-all: One of the following is true:
603 // - we don't have leases and there are no reservations
604 // - we used to have leases, but we lost them, because they are now
605 // reserved for someone else
606 // - we have a reservation, but it is not usable yet, because the address
607 // is still used by someone else
608 //
609 // In any case, we need to go through normal lease assignment process
610 // for now. This is also a catch-all or last resort approach, when we
611 // couldn't find any reservations (or couldn't use them).
612
615 .arg(ctx.query_->getLabel());
616
617 leases = allocateUnreservedLeases6(ctx);
618 }
619
620 if (!leases.empty()) {
621 // If there are any leases allocated, let's store in them in the
622 // IA context so as they are available when we process subsequent
623 // IAs.
624 for (auto const& lease : leases) {
625 ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
626 ctx.new_leases_.push_back(lease);
627 }
628 return (leases);
629 }
630
631 } catch (const isc::Exception& e) {
632
633 // Some other error, return an empty lease.
635 .arg(ctx.query_->getLabel())
636 .arg(e.what());
637 }
638
639 return (Lease6Collection());
640}
641
643AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
644
645 Lease6Collection leases;
646
648 uint8_t hint_prefix_length = 128;
649 if (!ctx.currentIA().hints_.empty()) {
651 hint = ctx.currentIA().hints_[0].getAddress();
652 hint_prefix_length = ctx.currentIA().hints_[0].getPrefixLength();
653 }
654
655 Subnet6Ptr original_subnet = ctx.subnet_;
656
657 Subnet6Ptr subnet = original_subnet;
658
659 SharedNetwork6Ptr network;
660
661 uint64_t total_attempts = 0;
662
663 // The following counter tracks the number of subnets with matching client
664 // classes from which the allocation engine attempted to assign leases.
665 uint64_t subnets_with_unavail_leases = 0;
666 // The following counter tracks the number of subnets in which there were
667 // no matching pools for the client.
668 uint64_t subnets_with_unavail_pools = 0;
669
671
672 // In the case of PDs, the allocation engine will try to match pools with
673 // the delegated prefix length matching the one provided in the hint. If the
674 // hint does not provide a preferred delegated prefix length (value is 0),
675 // the allocation engine will match any pool (any greater delegated prefix
676 // length pool). The match type for the pools is ignored for non PDs.
677 Lease6Ptr hint_lease;
678 bool search_hint_lease = true;
680 if (ctx.currentIA().type_ == Lease::TYPE_PD) {
681 // If the hint has a value of 128, the code might be broken as the hint
682 // was added with the default value 128 for prefix_len by the addHint
683 // function instead of 0. However 128 is not a valid value anyway so it
684 // is reset to 0 (use any delegated prefix length available).
685 if (hint_prefix_length == 128) {
686 hint_prefix_length = 0;
687 }
688 if (!hint_prefix_length) {
689 prefix_length_match = Allocator::PREFIX_LEN_HIGHER;
690 }
691 }
692
693 // Try the first allocation using PREFIX_LEN_EQUAL (or in case of PDs,
694 // PREFIX_LEN_HIGHER when there is no valid delegated prefix length in the
695 // provided hint)
696 Lease6Ptr lease = allocateBestMatch(ctx, hint_lease, search_hint_lease,
697 hint, hint_prefix_length, subnet,
698 network, total_attempts,
699 subnets_with_unavail_leases,
700 subnets_with_unavail_pools,
701 callout_status, prefix_length_match);
702
703 // Try the second allocation using PREFIX_LEN_LOWER only for PDs if the
704 // first allocation using PREFIX_LEN_EQUAL failed (there was a specific
705 // delegated prefix length hint requested).
706 if (!lease && ctx.currentIA().type_ == Lease::TYPE_PD &&
707 prefix_length_match == Allocator::PREFIX_LEN_EQUAL) {
708 prefix_length_match = Allocator::PREFIX_LEN_LOWER;
709 lease = allocateBestMatch(ctx, hint_lease, search_hint_lease, hint,
710 hint_prefix_length, subnet, network,
711 total_attempts, subnets_with_unavail_leases,
712 subnets_with_unavail_pools, callout_status,
713 prefix_length_match);
714 }
715
716 // Try the third allocation using PREFIX_LEN_HIGHER only for PDs if the
717 // second allocation using PREFIX_LEN_LOWER failed (there was a specific
718 // delegated prefix length hint requested).
719 if (!lease && ctx.currentIA().type_ == Lease::TYPE_PD &&
720 prefix_length_match == Allocator::PREFIX_LEN_LOWER) {
721 prefix_length_match = Allocator::PREFIX_LEN_HIGHER;
722 lease = allocateBestMatch(ctx, hint_lease, search_hint_lease, hint,
723 hint_prefix_length, subnet, network,
724 total_attempts, subnets_with_unavail_leases,
725 subnets_with_unavail_pools, callout_status,
726 prefix_length_match);
727 }
728
729 if (lease) {
730 leases.push_back(lease);
731 return (leases);
732 }
733
734 auto const& classes = ctx.query_->getClasses();
735
736 if (network) {
737 // The client is in the shared network. Let's log the high level message
738 // indicating which shared network the client belongs to.
740 .arg(ctx.query_->getLabel())
741 .arg(network->getName())
742 .arg(subnets_with_unavail_leases)
743 .arg(subnets_with_unavail_pools);
744 StatsMgr::instance().addValue("v6-allocation-fail-shared-network",
745 static_cast<int64_t>(1));
746 StatsMgr::instance().addValue(
747 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
748 "v6-allocation-fail-shared-network"),
749 static_cast<int64_t>(1));
750 } else {
751 // The client is not connected to a shared network. It is connected
752 // to a subnet. Let's log the ID of that subnet.
753 std::string shared_network = ctx.subnet_->getSharedNetworkName();
754 if (shared_network.empty()) {
755 shared_network = "(none)";
756 }
758 .arg(ctx.query_->getLabel())
759 .arg(ctx.subnet_->toText())
760 .arg(ctx.subnet_->getID())
761 .arg(shared_network);
762 StatsMgr::instance().addValue("v6-allocation-fail-subnet",
763 static_cast<int64_t>(1));
764 StatsMgr::instance().addValue(
765 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
766 "v6-allocation-fail-subnet"),
767 static_cast<int64_t>(1));
768 }
769 if (total_attempts == 0) {
770 // In this case, it seems that none of the pools in the subnets could
771 // be used for that client, both in case the client is connected to
772 // a shared network or to a single subnet. Apparently, the client was
773 // rejected to use the pools because of the client classes' mismatch.
775 .arg(ctx.query_->getLabel());
776 StatsMgr::instance().addValue("v6-allocation-fail-no-pools",
777 static_cast<int64_t>(1));
778 StatsMgr::instance().addValue(
779 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
780 "v6-allocation-fail-no-pools"),
781 static_cast<int64_t>(1));
782 } else {
783 // This is an old log message which provides a number of attempts
784 // made by the allocation engine to allocate a lease. The only case
785 // when we don't want to log this message is when the number of
786 // attempts is zero (condition above), because it would look silly.
788 .arg(ctx.query_->getLabel())
789 .arg(total_attempts);
790 StatsMgr::instance().addValue("v6-allocation-fail",
791 static_cast<int64_t>(1));
792 StatsMgr::instance().addValue(
793 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
794 "v6-allocation-fail"),
795 static_cast<int64_t>(1));
796 }
797
798 if (!classes.empty()) {
800 .arg(ctx.query_->getLabel())
801 .arg(classes.toText());
802 StatsMgr::instance().addValue("v6-allocation-fail-classes",
803 static_cast<int64_t>(1));
804 StatsMgr::instance().addValue(
805 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
806 "v6-allocation-fail-classes"),
807 static_cast<int64_t>(1));
808 }
809
810 // We failed to allocate anything. Let's return empty collection.
811 return (Lease6Collection());
812}
813
815AllocEngine::allocateBestMatch(ClientContext6& ctx,
816 Lease6Ptr& hint_lease,
817 bool& search_hint_lease,
818 const isc::asiolink::IOAddress& hint,
819 uint8_t hint_prefix_length,
820 Subnet6Ptr original_subnet,
821 SharedNetwork6Ptr& network,
822 uint64_t& total_attempts,
823 uint64_t& subnets_with_unavail_leases,
824 uint64_t& subnets_with_unavail_pools,
826 Allocator::PrefixLenMatchType prefix_length_match) {
827 auto const& classes = ctx.query_->getClasses();
828 Pool6Ptr pool;
829 Subnet6Ptr subnet = original_subnet;
830
831 Lease6Ptr usable_hint_lease;
832 if (!search_hint_lease) {
833 usable_hint_lease = hint_lease;
834 }
835 for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
836 if (!subnet->clientSupported(classes)) {
837 continue;
838 }
839
840 ctx.subnet_ = subnet;
841
842 // check if the hint is in pool and is available
843 // This is equivalent of subnet->inPool(hint), but returns the pool
844 pool = boost::dynamic_pointer_cast<Pool6>
845 (subnet->getPool(ctx.currentIA().type_, classes, hint));
846
847 // check if the pool is allowed
848 if (!pool || !pool->clientSupported(classes)) {
849 continue;
850 }
851
852 if (ctx.currentIA().type_ == Lease::TYPE_PD &&
853 !Allocator::isValidPrefixPool(prefix_length_match, pool,
854 hint_prefix_length)) {
855 continue;
856 }
857
858 bool in_subnet = subnet->getReservationsInSubnet();
859
861 if (search_hint_lease) {
862 search_hint_lease = false;
863 hint_lease = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, hint);
864 usable_hint_lease = hint_lease;
865 }
866 if (!usable_hint_lease) {
867
868 // In-pool reservations: Check if this address is reserved for someone
869 // else. There is no need to check for whom it is reserved, because if
870 // it has been reserved for us we would have already allocated a lease.
871
873 // When out-of-pool flag is true the server may assume that all host
874 // reservations are for addresses that do not belong to the dynamic
875 // pool. Therefore, it can skip the reservation checks when dealing
876 // with in-pool addresses.
877 if (in_subnet &&
878 (!subnet->getReservationsOutOfPool() ||
879 !subnet->inPool(ctx.currentIA().type_, hint))) {
880 hosts = getIPv6Resrv(subnet->getID(), hint);
881 }
882
883 if (hosts.empty()) {
884
885 // If the in-pool reservations are disabled, or there is no
886 // reservation for a given hint, we're good to go.
887
888 // The hint is valid and not currently used, let's create a
889 // lease for it
890 Lease6Ptr new_lease = createLease6(ctx, hint, pool->getLength(), callout_status);
891
892 // It can happen that the lease allocation failed (we could
893 // have lost the race condition. That means that the hint is
894 // no longer usable and we need to continue the regular
895 // allocation path.
896 if (new_lease) {
898 return (new_lease);
899 }
900 } else {
903 .arg(ctx.query_->getLabel())
904 .arg(hint.toText());
905 }
906
907 } else if (usable_hint_lease->expired()) {
908
909 // If the lease is expired, we may likely reuse it, but...
911 // When out-of-pool flag is true the server may assume that all host
912 // reservations are for addresses that do not belong to the dynamic
913 // pool. Therefore, it can skip the reservation checks when dealing
914 // with in-pool addresses.
915 if (in_subnet &&
916 (!subnet->getReservationsOutOfPool() ||
917 !subnet->inPool(ctx.currentIA().type_, hint))) {
918 hosts = getIPv6Resrv(subnet->getID(), hint);
919 }
920
921 // Let's check if there is a reservation for this address.
922 if (hosts.empty()) {
923
924 // Copy an existing, expired lease so as it can be returned
925 // to the caller.
926 Lease6Ptr old_lease(new Lease6(*usable_hint_lease));
927 ctx.currentIA().old_leases_.push_back(old_lease);
928
930 Lease6Ptr lease = reuseExpiredLease(usable_hint_lease, ctx,
931 pool->getLength(),
932 callout_status);
933
935 return (lease);
936
937 } else {
940 .arg(ctx.query_->getLabel())
941 .arg(hint.toText());
942 }
943 }
944 }
945
946 // We have the choice in the order checking the lease and
947 // the reservation. The default is to begin by the lease
948 // if the multi-threading is disabled.
949 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
950 // If multi-threading is disabled, honor the configured order for host
951 // reservations lookup.
952 if (!check_reservation_first) {
953 check_reservation_first = CfgMgr::instance().getCurrentCfg()->getReservationsLookupFirst();
954 }
955
956 // Need to check if the subnet belongs to a shared network. If so,
957 // we might be able to find a better subnet for lease allocation,
958 // for which it is more likely that there are some leases available.
959 // If we stick to the selected subnet, we may end up walking over
960 // the entire subnet (or more subnets) to discover that the pools
961 // have been exhausted. Using a subnet from which a lease was
962 // assigned most recently is an optimization which increases
963 // the likelihood of starting from the subnet which pools are not
964 // exhausted.
965
966 original_subnet->getSharedNetwork(network);
967 if (network) {
968 // This would try to find a subnet with the same set of classes
969 // as the current subnet, but with the more recent "usage timestamp".
970 // This timestamp is only updated for the allocations made with an
971 // allocator (unreserved lease allocations), not the static
972 // allocations or requested addresses.
973 original_subnet = network->getPreferredSubnet(original_subnet, ctx.currentIA().type_);
974 }
975
976 ctx.subnet_ = subnet = original_subnet;
977
978 for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
979 if (!subnet->clientSupported(classes)) {
980 continue;
981 }
982
983 // The hint was useless (it was not provided at all, was used by someone else,
984 // was out of pool or reserved for someone else). Search the pool until first
985 // of the following occurs:
986 // - we find a free address
987 // - we find an address for which the lease has expired
988 // - we exhaust number of tries
989 uint128_t const possible_attempts =
990 subnet->getPoolCapacity(ctx.currentIA().type_,
991 classes,
992 prefix_length_match,
993 hint_prefix_length);
994
995 // If the number of tries specified in the allocation engine constructor
996 // is set to 0 (unlimited) or the pools capacity is lower than that number,
997 // let's use the pools capacity as the maximum number of tries. Trying
998 // more than the actual pools capacity is a waste of time. If the specified
999 // number of tries is lower than the pools capacity, use that number.
1000 uint128_t const max_attempts =
1001 (attempts_ == 0 || possible_attempts < attempts_) ?
1002 possible_attempts :
1003 attempts_;
1004
1005 if (max_attempts > 0) {
1006 // If max_attempts is greater than 0, there are some pools in this subnet
1007 // from which we can potentially get a lease.
1008 ++subnets_with_unavail_leases;
1009 } else {
1010 // If max_attempts is 0, it is an indication that there are no pools
1011 // in the subnet from which we can get a lease.
1012 ++subnets_with_unavail_pools;
1013 continue;
1014 }
1015
1016 bool in_subnet = subnet->getReservationsInSubnet();
1017 bool out_of_pool = subnet->getReservationsOutOfPool();
1018
1019 // Set the default status code in case the lease6_select callouts
1020 // do not exist and the callout handle has a status returned by
1021 // any of the callouts already invoked for this packet.
1022 if (ctx.callout_handle_) {
1023 ctx.callout_handle_->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
1024 }
1025
1026 for (uint64_t i = 0; i < max_attempts; ++i) {
1027 ++total_attempts;
1028
1029 auto allocator = subnet->getAllocator(ctx.currentIA().type_);
1031
1032 // The first step is to find out prefix length. It is 128 for
1033 // non-PD leases.
1034 uint8_t prefix_len = 128;
1035 if (ctx.currentIA().type_ == Lease::TYPE_PD) {
1036 candidate = allocator->pickPrefix(classes, pool, ctx.duid_,
1037 prefix_length_match, hint,
1038 hint_prefix_length);
1039 if (pool) {
1040 prefix_len = pool->getLength();
1041 }
1042 } else {
1043 candidate = allocator->pickAddress(classes, ctx.duid_, hint);
1044 }
1045
1046 // An allocator may return zero address when it has pools exhausted.
1047 if (candidate.isV6Zero()) {
1048 break;
1049 }
1050
1051 // First check for reservation when it is the choice.
1052 if (check_reservation_first && in_subnet && !out_of_pool) {
1053 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1054 if (!hosts.empty()) {
1055 // Don't allocate.
1056 continue;
1057 }
1058 }
1059
1060 // Check if the resource is busy i.e. can be being allocated
1061 // by another thread to another client.
1062 ResourceHandler resource_handler;
1063 if (MultiThreadingMgr::instance().getMode() &&
1064 !resource_handler.tryLock(ctx.currentIA().type_, candidate)) {
1065 // Don't allocate.
1066 continue;
1067 }
1068
1069 // Look for an existing lease for the candidate.
1070 Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1071 candidate);
1072
1073 if (!existing) {
1077 if (!check_reservation_first && in_subnet && !out_of_pool) {
1078 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1079 if (!hosts.empty()) {
1080 // Don't allocate.
1081 continue;
1082 }
1083 }
1084
1085 // there's no existing lease for selected candidate, so it is
1086 // free. Let's allocate it.
1087
1088 ctx.subnet_ = subnet;
1089 Lease6Ptr new_lease = createLease6(ctx, candidate, prefix_len, callout_status);
1090 if (new_lease) {
1091 // We are allocating a new lease (not renewing). So, the
1092 // old lease should be NULL.
1093 ctx.currentIA().old_leases_.clear();
1094
1095 return (new_lease);
1096
1097 } else if (ctx.callout_handle_ &&
1098 (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
1099 // Don't retry when the callout status is not continue.
1100 break;
1101 }
1102
1103 // Although the address was free just microseconds ago, it may have
1104 // been taken just now. If the lease insertion fails, we continue
1105 // allocation attempts.
1106 } else if (existing->expired()) {
1107 // Make sure it's not reserved.
1108 if (!check_reservation_first && in_subnet && !out_of_pool) {
1109 auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1110 if (!hosts.empty()) {
1111 // Don't allocate.
1112 continue;
1113 }
1114 }
1115
1116 // Copy an existing, expired lease so as it can be returned
1117 // to the caller.
1118 Lease6Ptr old_lease(new Lease6(*existing));
1119 ctx.currentIA().old_leases_.push_back(old_lease);
1120
1121 ctx.subnet_ = subnet;
1122 existing = reuseExpiredLease(existing, ctx, prefix_len,
1123 callout_status);
1124
1125 return (existing);
1126 }
1127 }
1128 }
1129 return (Lease6Ptr());
1130}
1131
1132void
1133AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
1134 Lease6Collection& existing_leases) {
1135
1136 // If there are no reservations or the reservation is v4, there's nothing to do.
1137 if (ctx.hosts_.empty()) {
1140 .arg(ctx.query_->getLabel());
1141 return;
1142 }
1143
1144 // Let's convert this from Lease::Type to IPv6Reserv::Type
1145 IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1147
1148 // We want to avoid allocating new lease for an IA if there is already
1149 // a valid lease for which client has reservation. So, we first check if
1150 // we already have a lease for a reserved address or prefix.
1151 for (auto const& lease : existing_leases) {
1152 if ((lease->valid_lft_ != 0)) {
1153 if ((ctx.hosts_.count(lease->subnet_id_) > 0) &&
1154 ctx.hosts_[lease->subnet_id_]->hasReservation(makeIPv6Resrv(*lease))) {
1155 // We found existing lease for a reserved address or prefix.
1156 // We'll simply extend the lifetime of the lease.
1159 .arg(ctx.query_->getLabel())
1160 .arg(lease->typeToText(lease->type_))
1161 .arg(lease->addr_.toText());
1162
1163 // Besides IP reservations we're also going to return other reserved
1164 // parameters, such as hostname. We want to hand out the hostname value
1165 // from the same reservation entry as IP addresses. Thus, let's see if
1166 // there is any hostname reservation.
1167 if (!ctx.host_subnet_) {
1168 SharedNetwork6Ptr network;
1169 ctx.subnet_->getSharedNetwork(network);
1170 if (network) {
1171 // Remember the subnet that holds this preferred host
1172 // reservation. The server will use it to return appropriate
1173 // FQDN, classes etc.
1174 ctx.host_subnet_ = network->getSubnet(lease->subnet_id_);
1175 ConstHostPtr host = ctx.hosts_[lease->subnet_id_];
1176 // If there is a hostname reservation here we should stick
1177 // to this reservation. By updating the hostname in the
1178 // context we make sure that the database is updated with
1179 // this new value and the server doesn't need to do it and
1180 // its processing performance is not impacted by the hostname
1181 // updates.
1182 if (host && !host->getHostname().empty()) {
1183 // We have to determine whether the hostname is generated
1184 // in response to client's FQDN or not. If yes, we will
1185 // need to qualify the hostname. Otherwise, we just use
1186 // the hostname as it is specified for the reservation.
1187 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1188 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1189 qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1190 static_cast<bool>(fqdn));
1191 }
1192 }
1193 }
1194
1195 // Got a lease for a reservation in this IA.
1196 return;
1197 }
1198 }
1199 }
1200
1201 // There is no lease for a reservation in this IA. So, let's now iterate
1202 // over reservations specified and try to allocate one of them for the IA.
1203
1204 auto const& classes = ctx.query_->getClasses();
1205 for (Subnet6Ptr subnet = ctx.subnet_; subnet;
1206 subnet = subnet->getNextSubnet(ctx.subnet_)) {
1207
1208 SubnetID subnet_id = subnet->getID();
1209
1210 // No hosts for this subnet or the subnet not supported.
1211 if (!subnet->clientSupported(classes) || ctx.hosts_.count(subnet_id) == 0) {
1212 continue;
1213 }
1214
1215 ConstHostPtr host = ctx.hosts_[subnet_id];
1216
1217 bool in_subnet = subnet->getReservationsInSubnet();
1218
1219 // Get the IPv6 reservations of specified type.
1220 const IPv6ResrvRange& reservs = host->getIPv6Reservations(type);
1221 BOOST_FOREACH(auto const& type_lease_tuple, reservs) {
1222 // We do have a reservation for address or prefix.
1223 const IOAddress& addr = type_lease_tuple.second.getPrefix();
1224 uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1225
1226 // We have allocated this address/prefix while processing one of the
1227 // previous IAs, so let's try another reservation.
1228 if (ctx.isAllocated(addr, prefix_len)) {
1229 continue;
1230 }
1231
1232 // The out-of-pool flag indicates that no client should be assigned
1233 // reserved addresses from within the dynamic pool, and for that
1234 // reason look only for reservations that are outside the pools,
1235 // hence the inPool check.
1236 if (!in_subnet ||
1237 (subnet->getReservationsOutOfPool() &&
1238 subnet->inPool(ctx.currentIA().type_, addr))) {
1239 continue;
1240 }
1241
1242 // If there's a lease for this address, let's not create it.
1243 // It doesn't matter whether it is for this client or for someone else.
1244 if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1245 addr)) {
1246
1247 // Let's remember the subnet from which the reserved address has been
1248 // allocated. We'll use this subnet for allocating other reserved
1249 // resources.
1250 ctx.subnet_ = subnet;
1251
1252 if (!ctx.host_subnet_) {
1253 ctx.host_subnet_ = subnet;
1254 if (!host->getHostname().empty()) {
1255 // If there is a hostname reservation here we should stick
1256 // to this reservation. By updating the hostname in the
1257 // context we make sure that the database is updated with
1258 // this new value and the server doesn't need to do it and
1259 // its processing performance is not impacted by the hostname
1260 // updates.
1261
1262 // We have to determine whether the hostname is generated
1263 // in response to client's FQDN or not. If yes, we will
1264 // need to qualify the hostname. Otherwise, we just use
1265 // the hostname as it is specified for the reservation.
1266 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1267 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1268 qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1269 static_cast<bool>(fqdn));
1270 }
1271 }
1272
1273 // Ok, let's create a new lease...
1275 Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1276
1277 // ... and add it to the existing leases list.
1278 existing_leases.push_back(lease);
1279
1280 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1282 .arg(addr.toText())
1283 .arg(ctx.query_->getLabel());
1284 } else {
1286 .arg(addr.toText())
1287 .arg(static_cast<int>(prefix_len))
1288 .arg(ctx.query_->getLabel());
1289 }
1290
1291 // We found a lease for this client and this IA. Let's return.
1292 // Returning after the first lease was assigned is useful if we
1293 // have multiple reservations for the same client. If the client
1294 // sends 2 IAs, the first time we call allocateReservedLeases6 will
1295 // use the first reservation and return. The second time, we'll
1296 // go over the first reservation, but will discover that there's
1297 // a lease corresponding to it and will skip it and then pick
1298 // the second reservation and turn it into the lease. This approach
1299 // would work for any number of reservations.
1300 return;
1301 }
1302 }
1303 }
1304
1305 // Found no subnet reservations so now try the global reservation.
1306 allocateGlobalReservedLeases6(ctx, existing_leases);
1307}
1308
1309void
1310AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
1311 Lease6Collection& existing_leases) {
1312 // Get the global host
1313 ConstHostPtr ghost = ctx.globalHost();
1314 if (!ghost) {
1315 return;
1316 }
1317
1318 // We want to avoid allocating a new lease for an IA if there is already
1319 // a valid lease for which client has reservation. So, we first check if
1320 // we already have a lease for a reserved address or prefix.
1321 for (auto const& lease : existing_leases) {
1322 if ((lease->valid_lft_ != 0) &&
1323 (ghost->hasReservation(makeIPv6Resrv(*lease)))) {
1324 // We found existing lease for a reserved address or prefix.
1325 // We'll simply extend the lifetime of the lease.
1328 .arg(ctx.query_->getLabel())
1329 .arg(lease->typeToText(lease->type_))
1330 .arg(lease->addr_.toText());
1331
1332 // Besides IP reservations we're also going to return other reserved
1333 // parameters, such as hostname. We want to hand out the hostname value
1334 // from the same reservation entry as IP addresses. Thus, let's see if
1335 // there is any hostname reservation.
1336 if (!ghost->getHostname().empty()) {
1337 // We have to determine whether the hostname is generated
1338 // in response to client's FQDN or not. If yes, we will
1339 // need to qualify the hostname. Otherwise, we just use
1340 // the hostname as it is specified for the reservation.
1341 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1342 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1343 qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1344 static_cast<bool>(fqdn));
1345 }
1346
1347 // Got a lease for a reservation in this IA.
1348 return;
1349 }
1350 }
1351
1352 // There is no lease for a reservation in this IA. So, let's now iterate
1353 // over reservations specified and try to allocate one of them for the IA.
1354
1355 // Let's convert this from Lease::Type to IPv6Reserv::Type
1356 IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1358
1359 const IPv6ResrvRange& reservs = ghost->getIPv6Reservations(type);
1360 BOOST_FOREACH(auto const& type_lease_tuple, reservs) {
1361 // We do have a reservation for address or prefix.
1362 const IOAddress& addr = type_lease_tuple.second.getPrefix();
1363 uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1364
1365 // We have allocated this address/prefix while processing one of the
1366 // previous IAs, so let's try another reservation.
1367 if (ctx.isAllocated(addr, prefix_len)) {
1368 continue;
1369 }
1370
1371 // If there's a lease for this address, let's not create it.
1372 // It doesn't matter whether it is for this client or for someone else.
1373 if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
1374
1375 // Check the feasibility of this address within this shared-network.
1376 // Assign the context's subnet accordingly.
1377 // Only necessary for IA_NA
1378 if (type == IPv6Resrv::TYPE_NA) {
1379 bool valid_subnet = false;
1380 auto subnet = ctx.subnet_;
1381 while (subnet) {
1382 if (subnet->inRange(addr)) {
1383 valid_subnet = true;
1384 break;
1385 }
1386
1387 subnet = subnet->getNextSubnet(ctx.subnet_);
1388 }
1389
1390 if (!valid_subnet) {
1393 .arg(ctx.query_->getLabel())
1394 .arg(addr.toText())
1395 .arg(labelNetworkOrSubnet(ctx.subnet_));
1396 continue;
1397 }
1398
1399 ctx.subnet_ = subnet;
1400 }
1401
1402 if (!ghost->getHostname().empty()) {
1403 // If there is a hostname reservation here we should stick
1404 // to this reservation. By updating the hostname in the
1405 // context we make sure that the database is updated with
1406 // this new value and the server doesn't need to do it and
1407 // its processing performance is not impacted by the hostname
1408 // updates.
1409
1410 // We have to determine whether the hostname is generated
1411 // in response to client's FQDN or not. If yes, we will
1412 // need to qualify the hostname. Otherwise, we just use
1413 // the hostname as it is specified for the reservation.
1414 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1415 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1416 qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1417 static_cast<bool>(fqdn));
1418 }
1419
1420 // Ok, let's create a new lease...
1422 Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1423
1424 // ... and add it to the existing leases list.
1425 existing_leases.push_back(lease);
1426
1427 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1429 .arg(addr.toText())
1430 .arg(ctx.query_->getLabel());
1431 } else {
1433 .arg(addr.toText())
1434 .arg(static_cast<int>(prefix_len))
1435 .arg(ctx.query_->getLabel());
1436 }
1437
1438 // We found a lease for this client and this IA. Let's return.
1439 // Returning after the first lease was assigned is useful if we
1440 // have multiple reservations for the same client. If the client
1441 // sends 2 IAs, the first time we call allocateReservedLeases6 will
1442 // use the first reservation and return. The second time, we'll
1443 // go over the first reservation, but will discover that there's
1444 // a lease corresponding to it and will skip it and then pick
1445 // the second reservation and turn it into the lease. This approach
1446 // would work for any number of reservations.
1447 return;
1448 }
1449 }
1450}
1451
1452void
1453AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
1454 Lease6Collection& existing_leases) {
1455 // If there are no leases (so nothing to remove) just return.
1456 if (existing_leases.empty() || !ctx.subnet_) {
1457 return;
1458 }
1459 // If host reservation is disabled (so there are no reserved leases)
1460 // use the simplified version.
1461 if (!ctx.subnet_->getReservationsInSubnet() &&
1462 !ctx.subnet_->getReservationsGlobal()) {
1463 removeNonmatchingReservedNoHostLeases6(ctx, existing_leases);
1464 return;
1465 }
1466
1467 // We need a copy, so we won't be iterating over a container and
1468 // removing from it at the same time. It's only a copy of pointers,
1469 // so the operation shouldn't be that expensive.
1470 Lease6Collection copy = existing_leases;
1471
1472 for (auto const& candidate : copy) {
1473 // If we have reservation we should check if the reservation is for
1474 // the candidate lease. If so, we simply accept the lease.
1475 IPv6Resrv resv = makeIPv6Resrv(*candidate);
1476 if ((ctx.hasGlobalReservation(resv)) ||
1477 ((ctx.hosts_.count(candidate->subnet_id_) > 0) &&
1478 (ctx.hosts_[candidate->subnet_id_]->hasReservation(resv)))) {
1479 // We have a subnet reservation
1480 continue;
1481 }
1482
1483 // The candidate address doesn't appear to be reserved for us.
1484 // We have to make a bit more expensive operation here to retrieve
1485 // the reservation for the candidate lease and see if it is
1486 // reserved for someone else.
1487 auto hosts = getIPv6Resrv(ctx.subnet_->getID(), candidate->addr_);
1488 // If lease is not reserved to someone else, it means that it can
1489 // be allocated to us from a dynamic pool, but we must check if
1490 // this lease belongs to any pool. If it does, we can proceed to
1491 // checking the next lease.
1492 if (hosts.empty() && inAllowedPool(ctx, candidate->type_,
1493 candidate->addr_, false)) {
1494 continue;
1495 }
1496
1497 if (!hosts.empty()) {
1498 // Ok, we have a problem. This host has a lease that is reserved
1499 // for someone else. We need to recover from this.
1500 if (hosts.size() == 1) {
1501 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1503 .arg(ctx.query_->getLabel())
1504 .arg(candidate->addr_.toText())
1505 .arg(ctx.duid_->toText())
1506 .arg(hosts.front()->getIdentifierAsText());
1507 } else {
1509 .arg(ctx.query_->getLabel())
1510 .arg(candidate->addr_.toText())
1511 .arg(static_cast<int>(candidate->prefixlen_))
1512 .arg(ctx.duid_->toText())
1513 .arg(hosts.front()->getIdentifierAsText());
1514 }
1515 } else {
1516 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1518 .arg(ctx.query_->getLabel())
1519 .arg(candidate->addr_.toText())
1520 .arg(ctx.duid_->toText())
1521 .arg(hosts.size());
1522 } else {
1524 .arg(ctx.query_->getLabel())
1525 .arg(candidate->addr_.toText())
1526 .arg(static_cast<int>(candidate->prefixlen_))
1527 .arg(ctx.duid_->toText())
1528 .arg(hosts.size());
1529 }
1530 }
1531 }
1532
1533 // Remove this lease from LeaseMgr as it is reserved to someone
1534 // else or doesn't belong to a pool.
1535 if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1536 // Concurrent delete performed by other instance which should
1537 // properly handle dns and stats updates.
1538 continue;
1539 }
1540
1541 // Update DNS if needed.
1542 queueNCR(CHG_REMOVE, candidate);
1543
1544 // Need to decrease statistic for assigned addresses.
1545 StatsMgr::instance().addValue(
1546 StatsMgr::generateName("subnet", candidate->subnet_id_,
1547 ctx.currentIA().type_ == Lease::TYPE_NA ?
1548 "assigned-nas" : "assigned-pds"),
1549 static_cast<int64_t>(-1));
1550
1551 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(candidate->subnet_id_);
1552 if (subnet) {
1553 auto const& pool = subnet->getPool(ctx.currentIA().type_, candidate->addr_, false);
1554 if (pool) {
1555 StatsMgr::instance().addValue(
1556 StatsMgr::generateName("subnet", subnet->getID(),
1557 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ? "pool" : "pd-pool", pool->getID(),
1558 ctx.currentIA().type_ == Lease::TYPE_NA ? "assigned-nas" : "assigned-pds")),
1559 static_cast<int64_t>(-1));
1560 }
1561 }
1562
1563 // In principle, we could trigger a hook here, but we will do this
1564 // only if we get serious complaints from actual users. We want the
1565 // conflict resolution procedure to really work and user libraries
1566 // should not interfere with it.
1567
1568 // Add this to the list of removed leases.
1569 ctx.currentIA().old_leases_.push_back(candidate);
1570
1571 // Let's remove this candidate from existing leases
1572 removeLeases(existing_leases, candidate->addr_);
1573 }
1574}
1575
1576void
1577AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
1578 Lease6Collection& existing_leases) {
1579 // We need a copy, so we won't be iterating over a container and
1580 // removing from it at the same time. It's only a copy of pointers,
1581 // so the operation shouldn't be that expensive.
1582 Lease6Collection copy = existing_leases;
1583
1584 for (auto const& candidate : copy) {
1585 // Lease can be allocated to us from a dynamic pool, but we must
1586 // check if this lease belongs to any allowed pool. If it does,
1587 // we can proceed to checking the next lease.
1588 if (inAllowedPool(ctx, candidate->type_,
1589 candidate->addr_, false)) {
1590 continue;
1591 }
1592
1593 // Remove this lease from LeaseMgr as it doesn't belong to a pool.
1594 if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1595 // Concurrent delete performed by other instance which should
1596 // properly handle dns and stats updates.
1597 continue;
1598 }
1599
1600 // Update DNS if needed.
1601 queueNCR(CHG_REMOVE, candidate);
1602
1603 // Need to decrease statistic for assigned addresses.
1604 StatsMgr::instance().addValue(
1605 StatsMgr::generateName("subnet", candidate->subnet_id_,
1606 ctx.currentIA().type_ == Lease::TYPE_NA ?
1607 "assigned-nas" : "assigned-pds"),
1608 static_cast<int64_t>(-1));
1609
1610 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(candidate->subnet_id_);
1611 if (subnet) {
1612 auto const& pool = subnet->getPool(candidate->type_, candidate->addr_, false);
1613 if (pool) {
1614 StatsMgr::instance().addValue(
1615 StatsMgr::generateName("subnet", subnet->getID(),
1616 StatsMgr::generateName(candidate->type_ == Lease::TYPE_NA ? "pool" : "pd-pool", pool->getID(),
1617 candidate->type_ == Lease::TYPE_NA ? "assigned-nas" : "assigned-pds")),
1618 static_cast<int64_t>(-1));
1619 }
1620 }
1621
1622 // Add this to the list of removed leases.
1623 ctx.currentIA().old_leases_.push_back(candidate);
1624
1625 // Let's remove this candidate from existing leases
1626 removeLeases(existing_leases, candidate->addr_);
1627 }
1628}
1629
1630bool
1631AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
1632
1633 bool removed = false;
1634 for (Lease6Collection::iterator lease = container.begin();
1635 lease != container.end(); ++lease) {
1636 if ((*lease)->addr_ == addr) {
1637 lease->reset();
1638 removed = true;
1639 }
1640 }
1641
1642 // Remove all elements that have NULL value
1643 container.erase(std::remove(container.begin(), container.end(), Lease6Ptr()),
1644 container.end());
1645
1646 return (removed);
1647}
1648
1649void
1650AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
1651 Lease6Collection& existing_leases) {
1652 // This method removes leases that are not reserved for this host.
1653 // It will keep at least one lease, though, as a fallback.
1654 int total = existing_leases.size();
1655 if (total <= 1) {
1656 return;
1657 }
1658
1659 // This is officially not scary code anymore. iterates and marks specified
1660 // leases for deletion, by setting appropriate pointers to NULL.
1661 for (Lease6Collection::iterator lease = existing_leases.begin();
1662 lease != existing_leases.end(); ++lease) {
1663
1664 // If there is reservation for this keep it.
1665 IPv6Resrv resv = makeIPv6Resrv(**lease);
1666 if (ctx.hasGlobalReservation(resv) ||
1667 ((ctx.hosts_.count((*lease)->subnet_id_) > 0) &&
1668 (ctx.hosts_[(*lease)->subnet_id_]->hasReservation(resv)))) {
1669 continue;
1670 }
1671
1672 // @todo - If this is for a fake_allocation, we should probably
1673 // not be deleting the lease or removing DNS entries. We should
1674 // simply remove it from the list.
1675 // We have reservations, but not for this lease. Release it.
1676 // Remove this lease from LeaseMgr
1677 if (!LeaseMgrFactory::instance().deleteLease(*lease)) {
1678 // Concurrent delete performed by other instance which should
1679 // properly handle dns and stats updates.
1680 continue;
1681 }
1682
1683 // Update DNS if required.
1684 queueNCR(CHG_REMOVE, *lease);
1685
1686 // Need to decrease statistic for assigned addresses.
1687 StatsMgr::instance().addValue(
1688 StatsMgr::generateName("subnet", (*lease)->subnet_id_,
1689 ctx.currentIA().type_ == Lease::TYPE_NA ?
1690 "assigned-nas" : "assigned-pds"),
1691 static_cast<int64_t>(-1));
1692
1693 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId((*lease)->subnet_id_);
1694 if (subnet) {
1695 auto const& pool = subnet->getPool(ctx.currentIA().type_, (*lease)->addr_, false);
1696 if (pool) {
1697 StatsMgr::instance().addValue(
1698 StatsMgr::generateName("subnet", subnet->getID(),
1699 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ? "pool" : "pd-pool", pool->getID(),
1700 ctx.currentIA().type_ == Lease::TYPE_NA ? "assigned-nas" : "assigned-pds")),
1701 static_cast<int64_t>(-1));
1702 }
1703 }
1704
1706
1707 // Add this to the list of removed leases.
1708 ctx.currentIA().old_leases_.push_back(*lease);
1709
1710 // Set this pointer to NULL. The pointer is still valid. We're just
1711 // setting the Lease6Ptr to NULL value. We'll remove all NULL
1712 // pointers once the loop is finished.
1713 lease->reset();
1714
1715 if (--total == 1) {
1716 // If there's only one lease left, break the loop.
1717 break;
1718 }
1719 }
1720
1721 // Remove all elements that we previously marked for deletion (those that
1722 // have NULL value).
1723 existing_leases.erase(std::remove(existing_leases.begin(),
1724 existing_leases.end(), Lease6Ptr()), existing_leases.end());
1725}
1726
1728AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
1729 uint8_t prefix_len,
1730 CalloutHandle::CalloutNextStep& callout_status) {
1731
1732 if (!expired->expired()) {
1733 isc_throw(BadValue, "Attempt to recycle lease that is still valid");
1734 }
1735
1736 if (expired->type_ != Lease::TYPE_PD) {
1737 prefix_len = 128; // non-PD lease types must be always /128
1738 }
1739
1740 if (!ctx.fake_allocation_) {
1741 // The expired lease needs to be reclaimed before it can be reused.
1742 // This includes declined leases for which probation period has
1743 // elapsed.
1744 reclaimExpiredLease(expired, ctx.callout_handle_);
1745 }
1746
1747 // address, lease type and prefixlen (0) stay the same
1748 expired->iaid_ = ctx.currentIA().iaid_;
1749 expired->duid_ = ctx.duid_;
1750
1751 // Calculate life times.
1752 getLifetimes6(ctx, expired->preferred_lft_, expired->valid_lft_);
1753 expired->reuseable_valid_lft_ = 0;
1754
1755 expired->cltt_ = time(NULL);
1756 expired->subnet_id_ = ctx.subnet_->getID();
1757 expired->hostname_ = ctx.hostname_;
1758 expired->fqdn_fwd_ = ctx.fwd_dns_update_;
1759 expired->fqdn_rev_ = ctx.rev_dns_update_;
1760 expired->prefixlen_ = prefix_len;
1761 expired->state_ = Lease::STATE_DEFAULT;
1762
1765 .arg(ctx.query_->getLabel())
1766 .arg(expired->toText());
1767
1768 // Let's execute all callouts registered for lease6_select
1769 if (ctx.callout_handle_ &&
1770 HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1771
1772 // Use the RAII wrapper to make sure that the callout handle state is
1773 // reset when this object goes out of scope. All hook points must do
1774 // it to prevent possible circular dependency between the callout
1775 // handle and its arguments.
1776 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1777
1778 // Enable copying options from the packet within hook library.
1779 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
1780
1781 // Pass necessary arguments
1782
1783 // Pass the original packet
1784 ctx.callout_handle_->setArgument("query6", ctx.query_);
1785
1786 // Subnet from which we do the allocation
1787 ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
1788
1789 // Is this solicit (fake = true) or request (fake = false)
1790 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
1791
1792 // The lease that will be assigned to a client
1793 ctx.callout_handle_->setArgument("lease6", expired);
1794
1795 // Call the callouts
1796 HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
1797
1798 callout_status = ctx.callout_handle_->getStatus();
1799
1800 // Callouts decided to skip the action. This means that the lease is not
1801 // assigned, so the client will get NoAddrAvail as a result. The lease
1802 // won't be inserted into the database.
1803 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
1805 return (Lease6Ptr());
1806 }
1807
1812
1813 // Let's use whatever callout returned. Hopefully it is the same lease
1814 // we handed to it.
1815 ctx.callout_handle_->getArgument("lease6", expired);
1816 }
1817
1818 if (!ctx.fake_allocation_) {
1819 // Add (update) the extended information on the lease.
1820 updateLease6ExtendedInfo(expired, ctx);
1821
1822 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, expired->addr_, false);
1823 if (pool) {
1824 expired->pool_id_ = pool->getID();
1825 }
1826
1827 // for REQUEST we do update the lease
1828 LeaseMgrFactory::instance().updateLease6(expired);
1829
1830 // If the lease is in the current subnet we need to account
1831 // for the re-assignment of The lease.
1832 if (ctx.subnet_->inPool(ctx.currentIA().type_, expired->addr_)) {
1833 StatsMgr::instance().addValue(
1834 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1835 ctx.currentIA().type_ == Lease::TYPE_NA ?
1836 "assigned-nas" : "assigned-pds"),
1837 static_cast<int64_t>(1));
1838
1839 StatsMgr::instance().addValue(
1840 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1841 ctx.currentIA().type_ == Lease::TYPE_NA ?
1842 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
1843 static_cast<int64_t>(1));
1844
1845 if (pool) {
1846 StatsMgr::instance().addValue(
1847 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1848 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
1849 "pool" : "pd-pool", pool->getID(),
1850 ctx.currentIA().type_ == Lease::TYPE_NA ?
1851 "assigned-nas" : "assigned-pds")),
1852 static_cast<int64_t>(1));
1853
1854 StatsMgr::instance().addValue(
1855 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1856 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
1857 "pool" : "pd-pool", pool->getID(),
1858 ctx.currentIA().type_ == Lease::TYPE_NA ?
1859 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
1860 static_cast<int64_t>(1));
1861 }
1862
1863 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1864 "cumulative-assigned-nas" : "cumulative-assigned-pds",
1865 static_cast<int64_t>(1));
1866 }
1867 }
1868
1869 // We do nothing for SOLICIT. We'll just update database when
1870 // the client gets back to us with REQUEST message.
1871
1872 // it's not really expired at this stage anymore - let's return it as
1873 // an updated lease
1874 return (expired);
1875}
1876
1877void
1878AllocEngine::getLifetimes6(ClientContext6& ctx, uint32_t& preferred, uint32_t& valid) {
1879 // If the triplets are specified in one of our classes use it.
1880 // We use the first one we find for each lifetime.
1881 Triplet<uint32_t> candidate_preferred;
1882 Triplet<uint32_t> candidate_valid;
1883 const ClientClasses classes = ctx.query_->getClasses();
1884 if (!classes.empty()) {
1885 // Let's get class definitions
1886 const ClientClassDictionaryPtr& dict =
1887 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
1888
1889 // Iterate over the assigned class definitions.
1890 int have_both = 0;
1891 for (auto const& name : classes) {
1892 ClientClassDefPtr cl = dict->findClass(name);
1893 if (candidate_preferred.unspecified() &&
1894 (cl && (!cl->getPreferred().unspecified()))) {
1895 candidate_preferred = cl->getPreferred();
1896 ++have_both;
1897 }
1898
1899 if (candidate_valid.unspecified() &&
1900 (cl && (!cl->getValid().unspecified()))) {
1901 candidate_valid = cl->getValid();
1902 ++have_both;
1903 }
1904 if (have_both == 2) {
1905 break;
1906 }
1907 }
1908 }
1909
1910 // If no classes specified preferred lifetime, get it from the subnet.
1911 if (!candidate_preferred) {
1912 candidate_preferred = ctx.subnet_->getPreferred();
1913 }
1914
1915 // If no classes specified valid lifetime, get it from the subnet.
1916 if (!candidate_valid) {
1917 candidate_valid = ctx.subnet_->getValid();
1918 }
1919
1920 // Set the outbound parameters to the values we have so far.
1921 preferred = candidate_preferred;
1922 valid = candidate_valid;
1923
1924 // If client requested either value, use the requested value(s) bounded by
1925 // the candidate triplet(s).
1926 if (!ctx.currentIA().hints_.empty()) {
1927 if (ctx.currentIA().hints_[0].getPreferred()) {
1928 preferred = candidate_preferred.get(ctx.currentIA().hints_[0].getPreferred());
1929 }
1930
1931 if (ctx.currentIA().hints_[0].getValid()) {
1932 valid = candidate_valid.get(ctx.currentIA().hints_[0].getValid());
1933 }
1934 }
1935
1936 // If preferred isn't set or insane, calculate it as valid_lft * 0.625.
1937 if (!preferred || preferred > valid) {
1938 preferred = ((valid * 5)/8);
1941 .arg(ctx.query_->getLabel())
1942 .arg(preferred);
1943 }
1944}
1945
1946Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
1947 const IOAddress& addr,
1948 uint8_t prefix_len,
1949 CalloutHandle::CalloutNextStep& callout_status) {
1950
1951 if (ctx.currentIA().type_ != Lease::TYPE_PD) {
1952 prefix_len = 128; // non-PD lease types must be always /128
1953 }
1954
1955 uint32_t preferred = 0;
1956 uint32_t valid = 0;
1957 getLifetimes6(ctx, preferred, valid);
1958
1959 Lease6Ptr lease(new Lease6(ctx.currentIA().type_, addr, ctx.duid_,
1960 ctx.currentIA().iaid_, preferred,
1961 valid, ctx.subnet_->getID(),
1962 ctx.hwaddr_, prefix_len));
1963
1964 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
1965 lease->fqdn_rev_ = ctx.rev_dns_update_;
1966 lease->hostname_ = ctx.hostname_;
1967
1968 // Let's execute all callouts registered for lease6_select
1969 if (ctx.callout_handle_ &&
1970 HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1971
1972 // Use the RAII wrapper to make sure that the callout handle state is
1973 // reset when this object goes out of scope. All hook points must do
1974 // it to prevent possible circular dependency between the callout
1975 // handle and its arguments.
1976 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1977
1978 // Enable copying options from the packet within hook library.
1979 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
1980
1981 // Pass necessary arguments
1982
1983 // Pass the original packet
1984 ctx.callout_handle_->setArgument("query6", ctx.query_);
1985
1986 // Subnet from which we do the allocation
1987 ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
1988
1989 // Is this solicit (fake = true) or request (fake = false)
1990 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
1991
1992 // The lease that will be assigned to a client
1993 ctx.callout_handle_->setArgument("lease6", lease);
1994
1995 // This is the first callout, so no need to clear any arguments
1996 HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
1997
1998 callout_status = ctx.callout_handle_->getStatus();
1999
2000 // Callouts decided to skip the action. This means that the lease is not
2001 // assigned, so the client will get NoAddrAvail as a result. The lease
2002 // won't be inserted into the database.
2003 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
2005 return (Lease6Ptr());
2006 }
2007
2008 // Let's use whatever callout returned. Hopefully it is the same lease
2009 // we handed to it.
2010 ctx.callout_handle_->getArgument("lease6", lease);
2011 }
2012
2013 if (!ctx.fake_allocation_) {
2014 // Add (update) the extended information on the lease.
2015 updateLease6ExtendedInfo(lease, ctx);
2016
2017 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2018 if (pool) {
2019 lease->pool_id_ = pool->getID();
2020 }
2021
2022 // That is a real (REQUEST) allocation
2023 bool status = LeaseMgrFactory::instance().addLease(lease);
2024
2025 if (status) {
2026 // The lease insertion succeeded - if the lease is in the
2027 // current subnet lets bump up the statistic.
2028 if (ctx.subnet_->inPool(ctx.currentIA().type_, addr)) {
2029 StatsMgr::instance().addValue(
2030 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2031 ctx.currentIA().type_ == Lease::TYPE_NA ?
2032 "assigned-nas" : "assigned-pds"),
2033 static_cast<int64_t>(1));
2034
2035 StatsMgr::instance().addValue(
2036 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2037 ctx.currentIA().type_ == Lease::TYPE_NA ?
2038 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2039 static_cast<int64_t>(1));
2040
2041 if (pool) {
2042 StatsMgr::instance().addValue(
2043 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2044 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2045 "pool" : "pd-pool", pool->getID(),
2046 ctx.currentIA().type_ == Lease::TYPE_NA ?
2047 "assigned-nas" : "assigned-pds")),
2048 static_cast<int64_t>(1));
2049
2050 StatsMgr::instance().addValue(
2051 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2052 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2053 "pool" : "pd-pool", pool->getID(),
2054 ctx.currentIA().type_ == Lease::TYPE_NA ?
2055 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2056 static_cast<int64_t>(1));
2057 }
2058
2059 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2060 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2061 static_cast<int64_t>(1));
2062 }
2063
2064 // Record it so it won't be updated twice.
2065 ctx.currentIA().addNewResource(addr, prefix_len);
2066
2067 return (lease);
2068 } else {
2069 // One of many failures with LeaseMgr (e.g. lost connection to the
2070 // database, database failed etc.). One notable case for that
2071 // is that we are working in multi-process mode and we lost a race
2072 // (some other process got that address first)
2073 return (Lease6Ptr());
2074 }
2075 } else {
2076 // That is only fake (SOLICIT without rapid-commit) allocation
2077
2078 // It is for advertise only. We should not insert the lease and callers
2079 // have already verified the lease does not exist in the database.
2080 return (lease);
2081 }
2082}
2083
2086 try {
2087 if (!ctx.subnet_) {
2088 isc_throw(InvalidOperation, "Subnet is required for allocation");
2089 }
2090
2091 if (!ctx.duid_) {
2092 isc_throw(InvalidOperation, "DUID is mandatory for allocation");
2093 }
2094
2095 // Check if there are any leases for this client.
2096 Subnet6Ptr subnet = ctx.subnet_;
2097 Lease6Collection leases;
2098 while (subnet) {
2099 Lease6Collection leases_subnet =
2100 LeaseMgrFactory::instance().getLeases6(ctx.currentIA().type_,
2101 *ctx.duid_,
2102 ctx.currentIA().iaid_,
2103 subnet->getID());
2104 leases.insert(leases.end(), leases_subnet.begin(), leases_subnet.end());
2105
2106 subnet = subnet->getNextSubnet(ctx.subnet_);
2107 }
2108
2109 if (!leases.empty()) {
2112 .arg(ctx.query_->getLabel());
2113
2114 // Check if the existing leases are reserved for someone else.
2115 // If they're not, we're ok to keep using them.
2116 removeNonmatchingReservedLeases6(ctx, leases);
2117 }
2118
2119 if (!ctx.hosts_.empty()) {
2120
2123 .arg(ctx.query_->getLabel());
2124
2125 // If we have host reservation, allocate those leases.
2126 allocateReservedLeases6(ctx, leases);
2127
2128 // There's one more check to do. Let's remove leases that are not
2129 // matching reservations, i.e. if client X has address A, but there's
2130 // a reservation for address B, we should release A and reassign B.
2131 // Caveat: do this only if we have at least one reserved address.
2132 removeNonreservedLeases6(ctx, leases);
2133 }
2134
2135 // If we happen to removed all leases, get something new for this guy.
2136 // Depending on the configuration, we may enable or disable granting
2137 // new leases during renewals. This is controlled with the
2138 // allow_new_leases_in_renewals_ field.
2139 if (leases.empty()) {
2140
2143 .arg(ctx.query_->getLabel());
2144
2145 leases = allocateUnreservedLeases6(ctx);
2146 }
2147
2148 // Extend all existing leases that passed all checks.
2149 for (auto const& l : leases) {
2150 if (ctx.currentIA().isNewResource(l->addr_,
2151 l->prefixlen_)) {
2152 // This lease was just created so is already extended.
2153 continue;
2154 }
2157 .arg(ctx.query_->getLabel())
2158 .arg(l->typeToText(l->type_))
2159 .arg(l->addr_);
2160 extendLease6(ctx, l);
2161 }
2162
2163 if (!leases.empty()) {
2164 // If there are any leases allocated, let's store in them in the
2165 // IA context so as they are available when we process subsequent
2166 // IAs.
2167 for (auto const& lease : leases) {
2168 ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
2169 ctx.new_leases_.push_back(lease);
2170 }
2171 }
2172
2173 return (leases);
2174
2175 } catch (const isc::Exception& e) {
2176
2177 // Some other error, return an empty lease.
2179 .arg(ctx.query_->getLabel())
2180 .arg(e.what());
2181 }
2182
2183 return (Lease6Collection());
2184}
2185
2186void
2187AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
2188
2189 if (!lease || !ctx.subnet_) {
2190 return;
2191 }
2192
2193 // It is likely that the lease for which we're extending the lifetime doesn't
2194 // belong to the current but a sibling subnet.
2195 if (ctx.subnet_->getID() != lease->subnet_id_) {
2196 SharedNetwork6Ptr network;
2197 ctx.subnet_->getSharedNetwork(network);
2198 if (network) {
2199 Subnet6Ptr subnet = network->getSubnet(SubnetID(lease->subnet_id_));
2200 // Found the actual subnet this lease belongs to. Stick to this
2201 // subnet.
2202 if (subnet) {
2203 ctx.subnet_ = subnet;
2204 }
2205 }
2206 }
2207
2208 // If the lease is not global and it is either out of range (NAs only) or it
2209 // is not permitted by subnet client classification, delete it.
2210 if (!(ctx.hasGlobalReservation(makeIPv6Resrv(*lease))) &&
2211 (((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
2212 !ctx.subnet_->clientSupported(ctx.query_->getClasses()))) {
2213 // Oh dear, the lease is no longer valid. We need to get rid of it.
2214
2215 // Remove this lease from LeaseMgr
2216 if (!LeaseMgrFactory::instance().deleteLease(lease)) {
2217 // Concurrent delete performed by other instance which should
2218 // properly handle dns and stats updates.
2219 return;
2220 }
2221
2222 // Updated DNS if required.
2223 queueNCR(CHG_REMOVE, lease);
2224
2225 // Need to decrease statistic for assigned addresses.
2226 StatsMgr::instance().addValue(
2227 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2228 ctx.currentIA().type_ == Lease::TYPE_NA ?
2229 "assigned-nas" : "assigned-pds"),
2230 static_cast<int64_t>(-1));
2231
2232 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2233 if (pool) {
2234 StatsMgr::instance().addValue(
2235 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2236 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2237 "pool" : "pd-pool", pool->getID(),
2238 ctx.currentIA().type_ == Lease::TYPE_NA ?
2239 "assigned-nas" : "assigned-pds")),
2240 static_cast<int64_t>(-1));
2241 }
2242
2243 // Add it to the removed leases list.
2244 ctx.currentIA().old_leases_.push_back(lease);
2245
2246 return;
2247 }
2248
2251 .arg(ctx.query_->getLabel())
2252 .arg(lease->toText());
2253
2254 // Keep the old data in case the callout tells us to skip update.
2255 Lease6Ptr old_data(new Lease6(*lease));
2256
2257 bool changed = false;
2258
2259 // Calculate life times.
2260 uint32_t current_preferred_lft = lease->preferred_lft_;
2261 getLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2262
2263 // If either has changed set the changed flag.
2264 if ((lease->preferred_lft_ != current_preferred_lft) ||
2265 (lease->valid_lft_ != lease->current_valid_lft_)) {
2266 changed = true;
2267 }
2268
2269 lease->cltt_ = time(NULL);
2270 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2271 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
2272 (lease->hostname_ != ctx.hostname_)) {
2273 changed = true;
2274 lease->hostname_ = ctx.hostname_;
2275 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2276 lease->fqdn_rev_ = ctx.rev_dns_update_;
2277 }
2278 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
2279 (ctx.hwaddr_ &&
2280 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
2281 changed = true;
2282 lease->hwaddr_ = ctx.hwaddr_;
2283 }
2284 if (lease->state_ != Lease::STATE_DEFAULT) {
2285 changed = true;
2286 lease->state_ = Lease::STATE_DEFAULT;
2287 }
2290 .arg(ctx.query_->getLabel())
2291 .arg(lease->toText());
2292
2293 bool skip = false;
2294 // Get the callouts specific for the processed message and execute them.
2295 int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
2296 Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
2297 if (HooksManager::calloutsPresent(hook_point)) {
2298 CalloutHandlePtr callout_handle = ctx.callout_handle_;
2299
2300 // Use the RAII wrapper to make sure that the callout handle state is
2301 // reset when this object goes out of scope. All hook points must do
2302 // it to prevent possible circular dependency between the callout
2303 // handle and its arguments.
2304 ScopedCalloutHandleState callout_handle_state(callout_handle);
2305
2306 // Enable copying options from the packet within hook library.
2307 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2308
2309 // Pass the original packet
2310 callout_handle->setArgument("query6", ctx.query_);
2311
2312 // Pass the lease to be updated
2313 callout_handle->setArgument("lease6", lease);
2314
2315 // Pass the IA option to be sent in response
2316 if (lease->type_ == Lease::TYPE_NA) {
2317 callout_handle->setArgument("ia_na", ctx.currentIA().ia_rsp_);
2318 } else {
2319 callout_handle->setArgument("ia_pd", ctx.currentIA().ia_rsp_);
2320 }
2321
2322 // Call all installed callouts
2323 HooksManager::callCallouts(hook_point, *callout_handle);
2324
2325 // Callouts decided to skip the next processing step. The next
2326 // processing step would actually renew the lease, so skip at this
2327 // stage means "keep the old lease as it is".
2328 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2329 skip = true;
2332 .arg(ctx.query_->getName());
2333 }
2334
2336 }
2337
2338 if (!skip) {
2339 bool update_stats = false;
2340
2341 // If the lease we're renewing has expired, we need to reclaim this
2342 // lease before we can renew it.
2343 if (old_data->expired()) {
2344 reclaimExpiredLease(old_data, ctx.callout_handle_);
2345
2346 // If the lease is in the current subnet we need to account
2347 // for the re-assignment of the lease.
2348 if (ctx.subnet_->inPool(ctx.currentIA().type_, old_data->addr_)) {
2349 update_stats = true;
2350 }
2351 changed = true;
2352 }
2353
2354 // @todo should we call storeLease6ExtendedInfo() here ?
2355 updateLease6ExtendedInfo(lease, ctx);
2356 if (lease->extended_info_action_ == Lease6::ACTION_UPDATE) {
2357 changed = true;
2358 }
2359
2360 // Try to reuse the lease.
2361 if (!changed) {
2362 setLeaseReusable(lease, current_preferred_lft, ctx);
2363 }
2364
2365 // Now that the lease has been reclaimed, we can go ahead and update it
2366 // in the lease database.
2367 if (lease->reuseable_valid_lft_ == 0) {
2368 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2369 if (pool) {
2370 lease->pool_id_ = pool->getID();
2371 }
2372 LeaseMgrFactory::instance().updateLease6(lease);
2373 }
2374
2375 if (update_stats) {
2376 StatsMgr::instance().addValue(
2377 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2378 ctx.currentIA().type_ == Lease::TYPE_NA ?
2379 "assigned-nas" : "assigned-pds"),
2380 static_cast<int64_t>(1));
2381
2382 StatsMgr::instance().addValue(
2383 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2384 ctx.currentIA().type_ == Lease::TYPE_NA ?
2385 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2386 static_cast<int64_t>(1));
2387
2388 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2389 if (pool) {
2390 StatsMgr::instance().addValue(
2391 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2392 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2393 "pool" : "pd-pool", pool->getID(),
2394 ctx.currentIA().type_ == Lease::TYPE_NA ?
2395 "assigned-nas" : "assigned-pds")),
2396 static_cast<int64_t>(1));
2397
2398 StatsMgr::instance().addValue(
2399 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2400 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2401 "pool" : "pd-pool", pool->getID(),
2402 ctx.currentIA().type_ == Lease::TYPE_NA ?
2403 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2404 static_cast<int64_t>(1));
2405 }
2406
2407 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2408 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2409 static_cast<int64_t>(1));
2410 }
2411
2412 } else {
2413 // Copy back the original date to the lease. For MySQL it doesn't make
2414 // much sense, but for memfile, the Lease6Ptr points to the actual lease
2415 // in memfile, so the actual update is performed when we manipulate
2416 // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
2417 *lease = *old_data;
2418 }
2419
2420 // Add the old lease to the changed lease list. This allows the server
2421 // to make decisions regarding DNS updates.
2422 ctx.currentIA().changed_leases_.push_back(old_data);
2423}
2424
2426AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
2427 Lease6Collection updated_leases;
2428 for (auto const& lease_it : leases) {
2429 Lease6Ptr lease(new Lease6(*lease_it));
2430 if (ctx.currentIA().isNewResource(lease->addr_, lease->prefixlen_)) {
2431 // This lease was just created so is already up to date.
2432 updated_leases.push_back(lease);
2433 continue;
2434 }
2435
2436 lease->reuseable_valid_lft_ = 0;
2437 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2438 lease->fqdn_rev_ = ctx.rev_dns_update_;
2439 lease->hostname_ = ctx.hostname_;
2440 uint32_t current_preferred_lft = lease->preferred_lft_;
2441 if (lease->valid_lft_ == 0) {
2442 // The lease was expired by a release: reset zero lifetimes.
2443 getLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2444 }
2445 if (!ctx.fake_allocation_) {
2446 bool update_stats = false;
2447
2448 if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED || lease->state_ == Lease::STATE_RELEASED) {
2449 // Transition lease state to default (aka assigned)
2450 lease->state_ = Lease::STATE_DEFAULT;
2451
2452 // If the lease is in the current subnet we need to account
2453 // for the re-assignment of the lease.
2454 if (inAllowedPool(ctx, ctx.currentIA().type_,
2455 lease->addr_, true)) {
2456 update_stats = true;
2457 }
2458 }
2459
2460 bool fqdn_changed = ((lease->type_ != Lease::TYPE_PD) &&
2461 !(lease->hasIdenticalFqdn(*lease_it)));
2462
2463 lease->cltt_ = time(NULL);
2464 if (!fqdn_changed) {
2465 setLeaseReusable(lease, current_preferred_lft, ctx);
2466 }
2467 if (lease->reuseable_valid_lft_ == 0) {
2468 ctx.currentIA().changed_leases_.push_back(lease_it);
2469 LeaseMgrFactory::instance().updateLease6(lease);
2470 }
2471
2472 if (update_stats) {
2473 StatsMgr::instance().addValue(
2474 StatsMgr::generateName("subnet", lease->subnet_id_,
2475 ctx.currentIA().type_ == Lease::TYPE_NA ?
2476 "assigned-nas" : "assigned-pds"),
2477 static_cast<int64_t>(1));
2478
2479 StatsMgr::instance().addValue(
2480 StatsMgr::generateName("subnet", lease->subnet_id_,
2481 ctx.currentIA().type_ == Lease::TYPE_NA ?
2482 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2483 static_cast<int64_t>(1));
2484
2485 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2486 if (pool) {
2487 StatsMgr::instance().addValue(
2488 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2489 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2490 "pool" : "pd-pool", pool->getID(),
2491 ctx.currentIA().type_ == Lease::TYPE_NA ?
2492 "assigned-nas" : "assigned-pds")),
2493 static_cast<int64_t>(1));
2494
2495 StatsMgr::instance().addValue(
2496 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2497 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2498 "pool" : "pd-pool", pool->getID(),
2499 ctx.currentIA().type_ == Lease::TYPE_NA ?
2500 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2501 static_cast<int64_t>(1));
2502 }
2503
2504 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2505 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2506 static_cast<int64_t>(1));
2507 }
2508 }
2509
2510 updated_leases.push_back(lease);
2511 }
2512
2513 return (updated_leases);
2514}
2515
2516void
2518 const uint16_t timeout,
2519 const bool remove_lease,
2520 const uint16_t max_unwarned_cycles) {
2521
2524 .arg(max_leases)
2525 .arg(timeout);
2526
2527 try {
2528 reclaimExpiredLeases6Internal(max_leases, timeout, remove_lease,
2529 max_unwarned_cycles);
2530 } catch (const std::exception& ex) {
2533 .arg(ex.what());
2534 }
2535}
2536
2537void
2539 const uint16_t timeout,
2540 const bool remove_lease,
2541 const uint16_t max_unwarned_cycles) {
2542
2543 // Create stopwatch and automatically start it to measure the time
2544 // taken by the routine.
2545 util::Stopwatch stopwatch;
2546
2547 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2548
2549 // This value indicates if we have been able to deal with all expired
2550 // leases in this pass.
2551 bool incomplete_reclamation = false;
2552 Lease6Collection leases;
2553 // The value of 0 has a special meaning - reclaim all.
2554 if (max_leases > 0) {
2555 // If the value is non-zero, the caller has limited the number of
2556 // leases to reclaim. We obtain one lease more to see if there will
2557 // be still leases left after this pass.
2558 lease_mgr.getExpiredLeases6(leases, max_leases + 1);
2559 // There are more leases expired leases than we will process in this
2560 // pass, so we should mark it as an incomplete reclamation. We also
2561 // remove this extra lease (which we don't want to process anyway)
2562 // from the collection.
2563 if (leases.size() > max_leases) {
2564 leases.pop_back();
2565 incomplete_reclamation = true;
2566 }
2567
2568 } else {
2569 // If there is no limitation on the number of leases to reclaim,
2570 // we will try to process all. Hence, we don't mark it as incomplete
2571 // reclamation just yet.
2572 lease_mgr.getExpiredLeases6(leases, max_leases);
2573 }
2574
2575 // Do not initialize the callout handle until we know if there are any
2576 // lease6_expire callouts installed.
2577 CalloutHandlePtr callout_handle;
2578 if (!leases.empty() &&
2579 HooksManager::calloutsPresent(Hooks.hook_index_lease6_expire_)) {
2580 callout_handle = HooksManager::createCalloutHandle();
2581 }
2582
2583 size_t leases_processed = 0;
2584 for (auto const& lease : leases) {
2585
2586 try {
2587 // Reclaim the lease.
2588 if (MultiThreadingMgr::instance().getMode()) {
2589 // The reclamation is exclusive of packet processing.
2590 WriteLockGuard exclusive(rw_mutex_);
2591
2592 reclaimExpiredLease(lease, remove_lease, callout_handle);
2593 ++leases_processed;
2594 } else {
2595 reclaimExpiredLease(lease, remove_lease, callout_handle);
2596 ++leases_processed;
2597 }
2598
2599 } catch (const std::exception& ex) {
2601 .arg(lease->addr_.toText())
2602 .arg(ex.what());
2603 }
2604
2605 // Check if we have hit the timeout for running reclamation routine and
2606 // return if we have. We're checking it here, because we always want to
2607 // allow reclaiming at least one lease.
2608 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2609 // Timeout. This will likely mean that we haven't been able to process
2610 // all leases we wanted to process. The reclamation pass will be
2611 // probably marked as incomplete.
2612 if (!incomplete_reclamation) {
2613 if (leases_processed < leases.size()) {
2614 incomplete_reclamation = true;
2615 }
2616 }
2617
2620 .arg(timeout);
2621 break;
2622 }
2623 }
2624
2625 // Stop measuring the time.
2626 stopwatch.stop();
2627
2628 // Mark completion of the lease reclamation routine and present some stats.
2631 .arg(leases_processed)
2632 .arg(stopwatch.logFormatTotalDuration());
2633
2634 // Check if this was an incomplete reclamation and increase the number of
2635 // consecutive incomplete reclamations.
2636 if (incomplete_reclamation) {
2637 ++incomplete_v6_reclamations_;
2638 // If the number of incomplete reclamations is beyond the threshold, we
2639 // need to issue a warning.
2640 if ((max_unwarned_cycles > 0) &&
2641 (incomplete_v6_reclamations_ > max_unwarned_cycles)) {
2643 .arg(max_unwarned_cycles);
2644 // We issued a warning, so let's now reset the counter.
2645 incomplete_v6_reclamations_ = 0;
2646 }
2647
2648 } else {
2649 // This was a complete reclamation, so let's reset the counter.
2650 incomplete_v6_reclamations_ = 0;
2651
2654 }
2655}
2656
2657void
2661 .arg(secs);
2662
2663 uint64_t deleted_leases = 0;
2664 try {
2665 // Try to delete leases from the lease database.
2666 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2667 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
2668
2669 } catch (const std::exception& ex) {
2671 .arg(ex.what());
2672 }
2673
2676 .arg(deleted_leases);
2677}
2678
2679void
2681 const uint16_t timeout,
2682 const bool remove_lease,
2683 const uint16_t max_unwarned_cycles) {
2684
2687 .arg(max_leases)
2688 .arg(timeout);
2689
2690 try {
2691 reclaimExpiredLeases4Internal(max_leases, timeout, remove_lease,
2692 max_unwarned_cycles);
2693 } catch (const std::exception& ex) {
2696 .arg(ex.what());
2697 }
2698}
2699
2700void
2702 const uint16_t timeout,
2703 const bool remove_lease,
2704 const uint16_t max_unwarned_cycles) {
2705
2706 // Create stopwatch and automatically start it to measure the time
2707 // taken by the routine.
2708 util::Stopwatch stopwatch;
2709
2710 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2711
2712 // This value indicates if we have been able to deal with all expired
2713 // leases in this pass.
2714 bool incomplete_reclamation = false;
2715 Lease4Collection leases;
2716 // The value of 0 has a special meaning - reclaim all.
2717 if (max_leases > 0) {
2718 // If the value is non-zero, the caller has limited the number of
2719 // leases to reclaim. We obtain one lease more to see if there will
2720 // be still leases left after this pass.
2721 lease_mgr.getExpiredLeases4(leases, max_leases + 1);
2722 // There are more leases expired leases than we will process in this
2723 // pass, so we should mark it as an incomplete reclamation. We also
2724 // remove this extra lease (which we don't want to process anyway)
2725 // from the collection.
2726 if (leases.size() > max_leases) {
2727 leases.pop_back();
2728 incomplete_reclamation = true;
2729 }
2730
2731 } else {
2732 // If there is no limitation on the number of leases to reclaim,
2733 // we will try to process all. Hence, we don't mark it as incomplete
2734 // reclamation just yet.
2735 lease_mgr.getExpiredLeases4(leases, max_leases);
2736 }
2737
2738 // Do not initialize the callout handle until we know if there are any
2739 // lease4_expire callouts installed.
2740 CalloutHandlePtr callout_handle;
2741 if (!leases.empty() &&
2742 HooksManager::calloutsPresent(Hooks.hook_index_lease4_expire_)) {
2743 callout_handle = HooksManager::createCalloutHandle();
2744 }
2745
2746 size_t leases_processed = 0;
2747 for (auto const& lease : leases) {
2748
2749 try {
2750 // Reclaim the lease.
2751 if (MultiThreadingMgr::instance().getMode()) {
2752 // The reclamation is exclusive of packet processing.
2753 WriteLockGuard exclusive(rw_mutex_);
2754
2755 reclaimExpiredLease(lease, remove_lease, callout_handle);
2756 ++leases_processed;
2757 } else {
2758 reclaimExpiredLease(lease, remove_lease, callout_handle);
2759 ++leases_processed;
2760 }
2761
2762 } catch (const std::exception& ex) {
2764 .arg(lease->addr_.toText())
2765 .arg(ex.what());
2766 }
2767
2768 // Check if we have hit the timeout for running reclamation routine and
2769 // return if we have. We're checking it here, because we always want to
2770 // allow reclaiming at least one lease.
2771 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2772 // Timeout. This will likely mean that we haven't been able to process
2773 // all leases we wanted to process. The reclamation pass will be
2774 // probably marked as incomplete.
2775 if (!incomplete_reclamation) {
2776 if (leases_processed < leases.size()) {
2777 incomplete_reclamation = true;
2778 }
2779 }
2780
2783 .arg(timeout);
2784 break;
2785 }
2786 }
2787
2788 // Stop measuring the time.
2789 stopwatch.stop();
2790
2791 // Mark completion of the lease reclamation routine and present some stats.
2794 .arg(leases_processed)
2795 .arg(stopwatch.logFormatTotalDuration());
2796
2797 // Check if this was an incomplete reclamation and increase the number of
2798 // consecutive incomplete reclamations.
2799 if (incomplete_reclamation) {
2800 ++incomplete_v4_reclamations_;
2801 // If the number of incomplete reclamations is beyond the threshold, we
2802 // need to issue a warning.
2803 if ((max_unwarned_cycles > 0) &&
2804 (incomplete_v4_reclamations_ > max_unwarned_cycles)) {
2806 .arg(max_unwarned_cycles);
2807 // We issued a warning, so let's now reset the counter.
2808 incomplete_v4_reclamations_ = 0;
2809 }
2810
2811 } else {
2812 // This was a complete reclamation, so let's reset the counter.
2813 incomplete_v4_reclamations_ = 0;
2814
2817 }
2818}
2819
2820template<typename LeasePtrType>
2821void
2822AllocEngine::reclaimExpiredLease(const LeasePtrType& lease, const bool remove_lease,
2823 const CalloutHandlePtr& callout_handle) {
2824 reclaimExpiredLease(lease, remove_lease ? DB_RECLAIM_REMOVE : DB_RECLAIM_UPDATE,
2825 callout_handle);
2826}
2827
2828template<typename LeasePtrType>
2829void
2830AllocEngine::reclaimExpiredLease(const LeasePtrType& lease,
2831 const CalloutHandlePtr& callout_handle) {
2832 // This variant of the method is used by the code which allocates or
2833 // renews leases. It may be the case that the lease has already been
2834 // reclaimed, so there is nothing to do.
2835 if (!lease->stateExpiredReclaimed()) {
2836 reclaimExpiredLease(lease, DB_RECLAIM_LEAVE_UNCHANGED, callout_handle);
2837 }
2838}
2839
2840void
2841AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
2842 const DbReclaimMode& reclaim_mode,
2843 const CalloutHandlePtr& callout_handle) {
2844
2847 .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
2848 .arg(lease->addr_.toText())
2849 .arg(static_cast<int>(lease->prefixlen_));
2850
2851 // The skip flag indicates if the callouts have taken responsibility
2852 // for reclaiming the lease. The callout will set this to true if
2853 // it reclaims the lease itself. In this case the reclamation routine
2854 // will not update DNS nor update the database.
2855 bool skipped = false;
2856 if (callout_handle) {
2857
2858 // Use the RAII wrapper to make sure that the callout handle state is
2859 // reset when this object goes out of scope. All hook points must do
2860 // it to prevent possible circular dependency between the callout
2861 // handle and its arguments.
2862 ScopedCalloutHandleState callout_handle_state(callout_handle);
2863
2864 callout_handle->deleteAllArguments();
2865 callout_handle->setArgument("lease6", lease);
2866 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2867
2868 HooksManager::callCallouts(Hooks.hook_index_lease6_expire_,
2869 *callout_handle);
2870
2871 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2872 }
2873
2876
2877 if (!skipped) {
2878
2879 // Generate removal name change request for D2, if required.
2880 // This will return immediately if the DNS wasn't updated
2881 // when the lease was created.
2882 queueNCR(CHG_REMOVE, lease);
2883
2884 // Let's check if the lease that just expired is in DECLINED state.
2885 // If it is, we need to perform a couple extra steps.
2886 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2887 if (lease->state_ == Lease::STATE_DECLINED) {
2888 // Do extra steps required for declined lease reclamation:
2889 // - call the recover hook
2890 // - bump decline-related stats
2891 // - log separate message
2892 // There's no point in keeping a declined lease after its
2893 // reclamation. A declined lease doesn't have any client
2894 // identifying information anymore. So we'll flag it for
2895 // removal unless the hook has set the skip flag.
2896 remove_lease = reclaimDeclined(lease);
2897 }
2898
2899 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2900 // Reclaim the lease - depending on the configuration, set the
2901 // expired-reclaimed state or simply remove it.
2902 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2903 reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
2904 std::bind(&LeaseMgr::updateLease6,
2905 &lease_mgr, ph::_1));
2906 }
2907 }
2908
2909 // Update statistics.
2910
2911 // Increase number of reclaimed leases for a subnet.
2912 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2913 lease->subnet_id_,
2914 "reclaimed-leases"),
2915 static_cast<int64_t>(1));
2916
2917 // Increase total number of reclaimed leases.
2918 StatsMgr::instance().addValue("reclaimed-leases", static_cast<int64_t>(1));
2919
2920 // Statistics must have been updated during the release.
2921 if (lease->state_ == Lease::STATE_RELEASED) {
2922 return;
2923 }
2924
2925 // Decrease number of assigned leases.
2926 if (lease->type_ == Lease::TYPE_NA) {
2927 // IA_NA
2928 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2929 lease->subnet_id_,
2930 "assigned-nas"),
2931 static_cast<int64_t>(-1));
2932
2933 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
2934 if (subnet) {
2935 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
2936 if (pool) {
2937 StatsMgr::instance().addValue(
2938 StatsMgr::generateName("subnet", subnet->getID(),
2939 StatsMgr::generateName("pool" , pool->getID(),
2940 "assigned-nas")),
2941 static_cast<int64_t>(-1));
2942
2943 StatsMgr::instance().addValue(
2944 StatsMgr::generateName("subnet", subnet->getID(),
2945 StatsMgr::generateName("pool" , pool->getID(),
2946 "reclaimed-leases")),
2947 static_cast<int64_t>(1));
2948 }
2949 }
2950
2951 } else if (lease->type_ == Lease::TYPE_PD) {
2952 // IA_PD
2953 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2954 lease->subnet_id_,
2955 "assigned-pds"),
2956 static_cast<int64_t>(-1));
2957
2958 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
2959 if (subnet) {
2960 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
2961 if (pool) {
2962 StatsMgr::instance().addValue(
2963 StatsMgr::generateName("subnet", subnet->getID(),
2964 StatsMgr::generateName("pd-pool" , pool->getID(),
2965 "assigned-pds")),
2966 static_cast<int64_t>(-1));
2967
2968 StatsMgr::instance().addValue(
2969 StatsMgr::generateName("subnet", subnet->getID(),
2970 StatsMgr::generateName("pd-pool" , pool->getID(),
2971 "reclaimed-leases")),
2972 static_cast<int64_t>(1));
2973 }
2974 }
2975 }
2976}
2977
2978void
2979AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
2980 const DbReclaimMode& reclaim_mode,
2981 const CalloutHandlePtr& callout_handle) {
2982
2985 .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
2986 .arg(lease->addr_.toText());
2987
2988 // The skip flag indicates if the callouts have taken responsibility
2989 // for reclaiming the lease. The callout will set this to true if
2990 // it reclaims the lease itself. In this case the reclamation routine
2991 // will not update DNS nor update the database.
2992 bool skipped = false;
2993 if (callout_handle) {
2994
2995 // Use the RAII wrapper to make sure that the callout handle state is
2996 // reset when this object goes out of scope. All hook points must do
2997 // it to prevent possible circular dependency between the callout
2998 // handle and its arguments.
2999 ScopedCalloutHandleState callout_handle_state(callout_handle);
3000
3001 callout_handle->setArgument("lease4", lease);
3002 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
3003
3004 HooksManager::callCallouts(Hooks.hook_index_lease4_expire_,
3005 *callout_handle);
3006
3007 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
3008 }
3009
3012
3013 if (!skipped) {
3014
3015 // Generate removal name change request for D2, if required.
3016 // This will return immediately if the DNS wasn't updated
3017 // when the lease was created.
3018 queueNCR(CHG_REMOVE, lease);
3019 // Clear DNS fields so we avoid redundant removes.
3020 lease->hostname_.clear();
3021 lease->fqdn_fwd_ = false;
3022 lease->fqdn_rev_ = false;
3023
3024 // Let's check if the lease that just expired is in DECLINED state.
3025 // If it is, we need to perform a couple extra steps.
3026 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
3027 if (lease->state_ == Lease::STATE_DECLINED) {
3028 // Do extra steps required for declined lease reclamation:
3029 // - call the recover hook
3030 // - bump decline-related stats
3031 // - log separate message
3032 // There's no point in keeping a declined lease after its
3033 // reclamation. A declined lease doesn't have any client
3034 // identifying information anymore. So we'll flag it for
3035 // removal unless the hook has set the skip flag.
3036 remove_lease = reclaimDeclined(lease);
3037 }
3038
3039 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
3040 // Reclaim the lease - depending on the configuration, set the
3041 // expired-reclaimed state or simply remove it.
3042 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3043 reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
3044 std::bind(&LeaseMgr::updateLease4,
3045 &lease_mgr, ph::_1));
3046 }
3047 }
3048
3049 // Update statistics.
3050
3051 // Increase total number of reclaimed leases.
3052 StatsMgr::instance().addValue("reclaimed-leases", static_cast<int64_t>(1));
3053
3054 // Increase number of reclaimed leases for a subnet.
3055 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
3056 lease->subnet_id_,
3057 "reclaimed-leases"),
3058 static_cast<int64_t>(1));
3059
3060 // Statistics must have been updated during the release.
3061 if (lease->state_ == Lease4::STATE_RELEASED) {
3062 return;
3063 }
3064
3065 // Decrease number of assigned addresses.
3066 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
3067 lease->subnet_id_,
3068 "assigned-addresses"),
3069 static_cast<int64_t>(-1));
3070
3071 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease->subnet_id_);
3072 if (subnet) {
3073 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3074 if (pool) {
3075 StatsMgr::instance().addValue(
3076 StatsMgr::generateName("subnet", subnet->getID(),
3077 StatsMgr::generateName("pool" , pool->getID(),
3078 "assigned-addresses")),
3079 static_cast<int64_t>(-1));
3080
3081 StatsMgr::instance().addValue(
3082 StatsMgr::generateName("subnet", subnet->getID(),
3083 StatsMgr::generateName("pool" , pool->getID(),
3084 "reclaimed-leases")),
3085 static_cast<int64_t>(1));
3086 }
3087 }
3088}
3089
3090void
3094 .arg(secs);
3095
3096 uint64_t deleted_leases = 0;
3097 try {
3098 // Try to delete leases from the lease database.
3099 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3100 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
3101
3102 } catch (const std::exception& ex) {
3104 .arg(ex.what());
3105 }
3106
3109 .arg(deleted_leases);
3110}
3111
3112bool
3113AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
3114 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3115 return (true);
3116 }
3117
3118 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_recover_)) {
3120
3121 // Use the RAII wrapper to make sure that the callout handle state is
3122 // reset when this object goes out of scope. All hook points must do
3123 // it to prevent possible circular dependency between the callout
3124 // handle and its arguments.
3125 ScopedCalloutHandleState callout_handle_state(callout_handle);
3126
3127 // Pass necessary arguments
3128 callout_handle->setArgument("lease4", lease);
3129
3130 // Call the callouts
3131 HooksManager::callCallouts(Hooks.hook_index_lease4_recover_, *callout_handle);
3132
3133 // Callouts decided to skip the action. This means that the lease is not
3134 // assigned, so the client will get NoAddrAvail as a result. The lease
3135 // won't be inserted into the database.
3136 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3138 .arg(lease->addr_.toText());
3139 return (false);
3140 }
3141 }
3142
3144 .arg(lease->addr_.toText())
3145 .arg(lease->valid_lft_);
3146
3147 StatsMgr& stats_mgr = StatsMgr::instance();
3148
3149 // Decrease subnet specific counter for currently declined addresses
3150 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3151 "declined-addresses"),
3152 static_cast<int64_t>(-1));
3153
3154 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3155 "reclaimed-declined-addresses"),
3156 static_cast<int64_t>(1));
3157
3158 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease->subnet_id_);
3159 if (subnet) {
3160 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3161 if (pool) {
3162 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3163 StatsMgr::generateName("pool" , pool->getID(),
3164 "declined-addresses")),
3165 static_cast<int64_t>(-1));
3166
3167 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3168 StatsMgr::generateName("pool" , pool->getID(),
3169 "reclaimed-declined-addresses")),
3170 static_cast<int64_t>(1));
3171 }
3172 }
3173
3174 // Decrease global counter for declined addresses
3175 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3176
3177 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3178
3179 // Note that we do not touch assigned-addresses counters. Those are
3180 // modified in whatever code calls this method.
3181 return (true);
3182}
3183
3184bool
3185AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
3186 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3187 return (true);
3188 }
3189
3190 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_recover_)) {
3192
3193 // Use the RAII wrapper to make sure that the callout handle state is
3194 // reset when this object goes out of scope. All hook points must do
3195 // it to prevent possible circular dependency between the callout
3196 // handle and its arguments.
3197 ScopedCalloutHandleState callout_handle_state(callout_handle);
3198
3199 // Pass necessary arguments
3200 callout_handle->setArgument("lease6", lease);
3201
3202 // Call the callouts
3203 HooksManager::callCallouts(Hooks.hook_index_lease6_recover_, *callout_handle);
3204
3205 // Callouts decided to skip the action. This means that the lease is not
3206 // assigned, so the client will get NoAddrAvail as a result. The lease
3207 // won't be inserted into the database.
3208 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3210 .arg(lease->addr_.toText());
3211 return (false);
3212 }
3213 }
3214
3216 .arg(lease->addr_.toText())
3217 .arg(lease->valid_lft_);
3218
3219 StatsMgr& stats_mgr = StatsMgr::instance();
3220
3221 // Decrease subnet specific counter for currently declined addresses
3222 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3223 "declined-addresses"),
3224 static_cast<int64_t>(-1));
3225
3226 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3227 "reclaimed-declined-addresses"),
3228 static_cast<int64_t>(1));
3229
3230 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3231 if (subnet) {
3232 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
3233 if (pool) {
3234 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3235 StatsMgr::generateName("pool" , pool->getID(),
3236 "declined-addresses")),
3237 static_cast<int64_t>(-1));
3238
3239 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3240 StatsMgr::generateName("pool" , pool->getID(),
3241 "reclaimed-declined-addresses")),
3242 static_cast<int64_t>(1));
3243 }
3244 }
3245
3246 // Decrease global counter for declined addresses
3247 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3248
3249 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3250
3251 // Note that we do not touch assigned-nas counters. Those are
3252 // modified in whatever code calls this method.
3253
3254 return (true);
3255}
3256
3257void
3259 lease->relay_id_.clear();
3260 lease->remote_id_.clear();
3261 if (lease->getContext()) {
3262 lease->setContext(ElementPtr());
3263 }
3264}
3265
3266void
3268 if (lease->getContext()) {
3269 lease->extended_info_action_ = Lease6::ACTION_DELETE;
3270 lease->setContext(ElementPtr());
3271 }
3272}
3273
3274template<typename LeasePtrType>
3275void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
3276 const bool remove_lease,
3277 const std::function<void (const LeasePtrType&)>&
3278 lease_update_fun) const {
3279
3280 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3281
3282 // Reclaim the lease - depending on the configuration, set the
3283 // expired-reclaimed state or simply remove it.
3284 if (remove_lease) {
3285 lease_mgr.deleteLease(lease);
3286 } else if (lease_update_fun) {
3287 // Clear FQDN information as we have already sent the
3288 // name change request to remove the DNS record.
3289 lease->reuseable_valid_lft_ = 0;
3290 lease->hostname_.clear();
3291 lease->fqdn_fwd_ = false;
3292 lease->fqdn_rev_ = false;
3293 lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
3295 lease_update_fun(lease);
3296
3297 } else {
3298 return;
3299 }
3300
3301 // Lease has been reclaimed.
3304 .arg(lease->addr_.toText());
3305}
3306
3307std::string
3309 if (!subnet) {
3310 return("<empty subnet>");
3311 }
3312
3313 SharedNetwork4Ptr network;
3314 subnet->getSharedNetwork(network);
3315 std::ostringstream ss;
3316 if (network) {
3317 ss << "shared-network: " << network->getName();
3318 } else {
3319 ss << "subnet id: " << subnet->getID();
3320 }
3321
3322 return(ss.str());
3323}
3324
3325} // namespace dhcp
3326} // namespace isc
3327
3328// ##########################################################################
3329// # DHCPv4 lease allocation code starts here.
3330// ##########################################################################
3331
3332namespace {
3333
3351bool
3352addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
3353 // When out-of-pool flag is true the server may assume that all host
3354 // reservations are for addresses that do not belong to the dynamic pool.
3355 // Therefore, it can skip the reservation checks when dealing with in-pool
3356 // addresses.
3357 if (ctx.subnet_ && ctx.subnet_->getReservationsInSubnet() &&
3358 (!ctx.subnet_->getReservationsOutOfPool() ||
3359 !ctx.subnet_->inPool(Lease::TYPE_V4, address))) {
3360 // The global parameter ip-reservations-unique controls whether it is allowed
3361 // to specify multiple reservations for the same IP address or delegated prefix
3362 // or IP reservations must be unique. Some host backends do not support the
3363 // former, thus we can't always use getAll4 calls to get the reservations
3364 // for the given IP. When we're in the default mode, when IP reservations
3365 // are unique, we should call get4 (supported by all backends). If we're in
3366 // the mode in which non-unique reservations are allowed the backends which
3367 // don't support it are not used and we can safely call getAll4.
3368 ConstHostCollection hosts;
3369 if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
3370 try {
3371 // Reservations are unique. It is safe to call get4 to get the unique host.
3372 ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
3373 if (host) {
3374 hosts.push_back(host);
3375 }
3376 } catch (const MultipleRecords& ex) {
3378 .arg(address)
3379 .arg(ctx.subnet_->getID())
3380 .arg(ex.what());
3381 throw;
3382 }
3383 } else {
3384 // Reservations can be non-unique. Need to get all reservations for that address.
3385 hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
3386 }
3387
3388 for (auto const& host : hosts) {
3389 for (const AllocEngine::IdentifierPair& id_pair : ctx.host_identifiers_) {
3390 // If we find the matching host we know that this address is reserved
3391 // for us and we can return immediately.
3392 if (id_pair.first == host->getIdentifierType() &&
3393 id_pair.second == host->getIdentifier()) {
3394 return (false);
3395 }
3396 }
3397 }
3398 // We didn't find a matching host. If there are any reservations it means that
3399 // address is reserved for another client or multiple clients. If there are
3400 // no reservations address is not reserved for another client.
3401 return (!hosts.empty());
3402 }
3403 return (false);
3404}
3405
3421bool
3422hasAddressReservation(AllocEngine::ClientContext4& ctx) {
3423 if (ctx.hosts_.empty()) {
3424 return (false);
3425 }
3426
3427 // Fetch the globally reserved address if there is one.
3428 auto global_host = ctx.hosts_.find(SUBNET_ID_GLOBAL);
3429 auto global_host_address = ((global_host != ctx.hosts_.end() && global_host->second) ?
3430 global_host->second->getIPv4Reservation() :
3432
3433 // Start with currently selected subnet.
3434 Subnet4Ptr subnet = ctx.subnet_;
3435 while (subnet) {
3436 // If global reservations are enabled for this subnet and there is
3437 // globally reserved address and that address is feasible for this
3438 // subnet, update the selected subnet and return true.
3439 if (subnet->getReservationsGlobal() &&
3440 (global_host_address != IOAddress::IPV4_ZERO_ADDRESS()) &&
3441 (subnet->inRange(global_host_address))) {
3442 ctx.subnet_ = subnet;
3443 return (true);
3444 }
3445
3446 if (subnet->getReservationsInSubnet()) {
3447 auto host = ctx.hosts_.find(subnet->getID());
3448 // The out-of-pool flag indicates that no client should be assigned
3449 // reserved addresses from within the dynamic pool, and for that
3450 // reason look only for reservations that are outside the pools,
3451 // hence the inPool check.
3452 if (host != ctx.hosts_.end() && host->second) {
3453 auto reservation = host->second->getIPv4Reservation();
3454 if (!reservation.isV4Zero() &&
3455 (!subnet->getReservationsOutOfPool() ||
3456 !subnet->inPool(Lease::TYPE_V4, reservation))) {
3457 ctx.subnet_ = subnet;
3458 return (true);
3459 }
3460 }
3461 }
3462
3463 // No address reservation found here, so let's try another subnet
3464 // within the same shared network.
3465 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3466 }
3467
3468 if (global_host_address != IOAddress::IPV4_ZERO_ADDRESS()) {
3471 .arg(ctx.query_->getLabel())
3472 .arg(ctx.currentHost()->getIPv4Reservation().toText())
3474 }
3475
3476 return (false);
3477}
3478
3494void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
3495 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3496
3497 Subnet4Ptr original_subnet = ctx.subnet_;
3498
3499 auto const& classes = ctx.query_->getClasses();
3500
3501 // Client identifier is optional. First check if we can try to lookup
3502 // by client-id.
3503 bool try_clientid_lookup = (ctx.clientid_ &&
3504 SharedNetwork4::subnetsIncludeMatchClientId(original_subnet, classes));
3505
3506 // If it is possible to use client identifier to try to find client's lease.
3507 if (try_clientid_lookup) {
3508 // Get all leases for this client identifier. When shared networks are
3509 // in use it is more efficient to make a single query rather than
3510 // multiple queries, one for each subnet.
3511 Lease4Collection leases_client_id = lease_mgr.getLease4(*ctx.clientid_);
3512
3513 // Iterate over the subnets within the shared network to see if any client's
3514 // lease belongs to them.
3515 for (Subnet4Ptr subnet = original_subnet; subnet;
3516 subnet = subnet->getNextSubnet(original_subnet, classes)) {
3517
3518 // If client identifier has been supplied and the server wasn't
3519 // explicitly configured to ignore client identifiers for this subnet
3520 // check if there is a lease within this subnet.
3521 if (subnet->getMatchClientId()) {
3522 for (auto const& l : leases_client_id) {
3523 if (l->subnet_id_ == subnet->getID()) {
3524 // Lease found, so stick to this lease.
3525 client_lease = l;
3526 ctx.subnet_ = subnet;
3527 return;
3528 }
3529 }
3530 }
3531 }
3532 }
3533
3534 // If no lease found using the client identifier, try the lookup using
3535 // the HW address.
3536 if (!client_lease && ctx.hwaddr_) {
3537
3538 // Get all leases for this HW address.
3539 Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
3540
3541 for (Subnet4Ptr subnet = original_subnet; subnet;
3542 subnet = subnet->getNextSubnet(original_subnet, classes)) {
3543 ClientIdPtr client_id;
3544 if (subnet->getMatchClientId()) {
3545 client_id = ctx.clientid_;
3546 }
3547
3548 // Try to find the lease that matches current subnet and belongs to
3549 // this client, so both HW address and client identifier match.
3550 for (auto const& client_lease_it : leases_hw_address) {
3551 Lease4Ptr existing_lease = client_lease_it;
3552 if ((existing_lease->subnet_id_ == subnet->getID()) &&
3553 existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
3554 // Found the lease of this client, so return it.
3555 client_lease = existing_lease;
3556 // We got a lease but the subnet it belongs to may differ from
3557 // the original subnet. Let's now stick to this subnet.
3558 ctx.subnet_ = subnet;
3559 return;
3560 }
3561 }
3562 }
3563 }
3564}
3565
3578bool
3579inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
3580 // If the subnet belongs to a shared network we will be iterating
3581 // over the subnets that belong to this shared network.
3582 Subnet4Ptr current_subnet = ctx.subnet_;
3583 auto const& classes = ctx.query_->getClasses();
3584
3585 while (current_subnet) {
3586 if (current_subnet->inPool(Lease::TYPE_V4, address, classes)) {
3587 // We found a subnet that this address belongs to, so it
3588 // seems that this subnet is the good candidate for allocation.
3589 // Let's update the selected subnet.
3590 ctx.subnet_ = current_subnet;
3591 return (true);
3592 }
3593
3594 current_subnet = current_subnet->getNextSubnet(ctx.subnet_, classes);
3595 }
3596
3597 return (false);
3598}
3599
3600} // namespace
3601
3602namespace isc {
3603namespace dhcp {
3604
3606 : early_global_reservations_lookup_(false),
3607 subnet_(), clientid_(), hwaddr_(),
3608 requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
3609 fwd_dns_update_(false), rev_dns_update_(false),
3610 hostname_(""), callout_handle_(), fake_allocation_(false), offer_lft_(0),
3611 old_lease_(), new_lease_(), hosts_(), conflicting_lease_(),
3612 query_(), host_identifiers_(), unknown_requested_addr_(false),
3613 ddns_params_() {
3614
3615}
3616
3618 const ClientIdPtr& clientid,
3619 const HWAddrPtr& hwaddr,
3620 const asiolink::IOAddress& requested_addr,
3621 const bool fwd_dns_update,
3622 const bool rev_dns_update,
3623 const std::string& hostname,
3624 const bool fake_allocation,
3625 const uint32_t offer_lft)
3626 : early_global_reservations_lookup_(false),
3627 subnet_(subnet), clientid_(clientid), hwaddr_(hwaddr),
3628 requested_address_(requested_addr),
3629 fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
3630 hostname_(hostname), callout_handle_(),
3631 fake_allocation_(fake_allocation), offer_lft_(offer_lft), old_lease_(), new_lease_(),
3632 hosts_(), host_identifiers_(), unknown_requested_addr_(false),
3633 ddns_params_(new DdnsParams()) {
3634
3635 // Initialize host identifiers.
3636 if (hwaddr) {
3637 addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
3638 }
3639}
3640
3643 if (subnet_ && subnet_->getReservationsInSubnet()) {
3644 auto host = hosts_.find(subnet_->getID());
3645 if (host != hosts_.cend()) {
3646 return (host->second);
3647 }
3648 }
3649
3650 return (globalHost());
3651}
3652
3655 if (subnet_ && subnet_->getReservationsGlobal()) {
3656 auto host = hosts_.find(SUBNET_ID_GLOBAL);
3657 if (host != hosts_.cend()) {
3658 return (host->second);
3659 }
3660 }
3661
3662 return (ConstHostPtr());
3663}
3664
3667 // We already have it return it unless the context subnet has changed.
3668 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
3669 return (ddns_params_);
3670 }
3671
3672 // Doesn't exist yet or is stale, (re)create it.
3673 if (subnet_) {
3674 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
3675 return (ddns_params_);
3676 }
3677
3678 // Asked for it without a subnet? This case really shouldn't occur but
3679 // for now let's return an instance with default values.
3680 return (DdnsParamsPtr(new DdnsParams()));
3681}
3682
3685 // The NULL pointer indicates that the old lease didn't exist. It may
3686 // be later set to non NULL value if existing lease is found in the
3687 // database.
3688 ctx.old_lease_.reset();
3689 ctx.new_lease_.reset();
3690
3691 // Before we start allocation process, we need to make sure that the
3692 // selected subnet is allowed for this client. If not, we'll try to
3693 // use some other subnet within the shared network. If there are no
3694 // subnets allowed for this client within the shared network, we
3695 // can't allocate a lease.
3696 Subnet4Ptr subnet = ctx.subnet_;
3697 auto const& classes = ctx.query_->getClasses();
3698 if (subnet && !subnet->clientSupported(classes)) {
3699 ctx.subnet_ = subnet->getNextSubnet(subnet, classes);
3700 }
3701
3702 try {
3703 if (!ctx.subnet_) {
3704 isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
3705 }
3706
3707 if (!ctx.hwaddr_) {
3708 isc_throw(BadValue, "HWAddr must be defined");
3709 }
3710
3711 if (ctx.fake_allocation_) {
3712 ctx.new_lease_ = discoverLease4(ctx);
3713 } else {
3714 ctx.new_lease_ = requestLease4(ctx);
3715 }
3716
3717 } catch (const isc::Exception& e) {
3718 // Some other error, return an empty lease.
3720 .arg(ctx.query_->getLabel())
3721 .arg(e.what());
3722 }
3723
3724 return (ctx.new_lease_);
3725}
3726
3727void
3729 // If there is no subnet, there is nothing to do.
3730 if (!ctx.subnet_) {
3731 return;
3732 }
3733
3734 auto subnet = ctx.subnet_;
3735
3736 // If already done just return.
3738 !subnet->getReservationsInSubnet()) {
3739 return;
3740 }
3741
3742 // @todo: This code can be trivially optimized.
3744 subnet->getReservationsGlobal()) {
3746 if (ghost) {
3747 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
3748
3749 // If we had only to fetch global reservations it is done.
3750 if (!subnet->getReservationsInSubnet()) {
3751 return;
3752 }
3753 }
3754 }
3755
3756 std::map<SubnetID, ConstHostPtr> host_map;
3757 SharedNetwork4Ptr network;
3758 subnet->getSharedNetwork(network);
3759
3760 // If the subnet belongs to a shared network it is usually going to be
3761 // more efficient to make a query for all reservations for a particular
3762 // client rather than a query for each subnet within this shared network.
3763 // The only case when it is going to be less efficient is when there are
3764 // more host identifier types in use than subnets within a shared network.
3765 // As it breaks RADIUS use of host caching this can be disabled by the
3766 // host manager.
3767 const bool use_single_query = network &&
3768 !HostMgr::instance().getDisableSingleQuery() &&
3769 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
3770
3771 if (use_single_query) {
3772 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3773 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
3774 &id_pair.second[0],
3775 id_pair.second.size());
3776 // Store the hosts in the temporary map, because some hosts may
3777 // belong to subnets outside of the shared network. We'll need
3778 // to eliminate them.
3779 for (auto const& host : hosts) {
3780 if (host->getIPv4SubnetID() != SUBNET_ID_GLOBAL) {
3781 host_map[host->getIPv4SubnetID()] = host;
3782 }
3783 }
3784 }
3785 }
3786
3787 auto const& classes = ctx.query_->getClasses();
3788 // We can only search for the reservation if a subnet has been selected.
3789 while (subnet) {
3790
3791 // Only makes sense to get reservations if the client has access
3792 // to the class and host reservations are enabled for this subnet.
3793 if (subnet->clientSupported(classes) && subnet->getReservationsInSubnet()) {
3794 // Iterate over configured identifiers in the order of preference
3795 // and try to use each of them to search for the reservations.
3796 if (use_single_query) {
3797 if (host_map.count(subnet->getID()) > 0) {
3798 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
3799 }
3800 } else {
3801 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3802 // Attempt to find a host using a specified identifier.
3803 ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
3804 id_pair.first,
3805 &id_pair.second[0],
3806 id_pair.second.size());
3807 // If we found matching host for this subnet.
3808 if (host) {
3809 ctx.hosts_[subnet->getID()] = host;
3810 break;
3811 }
3812 }
3813 }
3814 }
3815
3816 // We need to get to the next subnet if this is a shared network. If it
3817 // is not (a plain subnet), getNextSubnet will return NULL and we're
3818 // done here.
3819 subnet = subnet->getNextSubnet(ctx.subnet_, classes);
3820 }
3821
3822 // The hosts can be used by the server to return reserved options to
3823 // the DHCP client. Such options must be encapsulated (i.e., they must
3824 // include suboptions).
3825 for (auto const& host : ctx.hosts_) {
3826 host.second->encapsulateOptions();
3827 }
3828}
3829
3832 ConstHostPtr host;
3833 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3834 // Attempt to find a host using a specified identifier.
3835 host = HostMgr::instance().get4(SUBNET_ID_GLOBAL, id_pair.first,
3836 &id_pair.second[0], id_pair.second.size());
3837
3838 // If we found matching global host we're done.
3839 if (host) {
3840 break;
3841 }
3842 }
3843
3844 return (host);
3845}
3846
3848AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
3849 // Find an existing lease for this client. This function will return null
3850 // if there is a conflict with existing lease and the allocation should
3851 // not be continued.
3852 Lease4Ptr client_lease;
3853 findClientLease(ctx, client_lease);
3854
3855 // Fetch offer_lft to see if we're allocating on DISCOVER.
3856 ctx.offer_lft_ = getOfferLft(ctx);
3857
3858 // new_lease will hold the pointer to the lease that we will offer to the
3859 // caller.
3860 Lease4Ptr new_lease;
3861
3863
3864 // Check if there is a reservation for the client. If there is, we want to
3865 // assign the reserved address, rather than any other one.
3866 if (hasAddressReservation(ctx)) {
3867
3870 .arg(ctx.query_->getLabel())
3871 .arg(ctx.currentHost()->getIPv4Reservation().toText());
3872
3873 // If the client doesn't have a lease or the leased address is different
3874 // than the reserved one then let's try to allocate the reserved address.
3875 // Otherwise the address that the client has is the one for which it
3876 // has a reservation, so just renew it.
3877 if (!client_lease || (client_lease->addr_ != ctx.currentHost()->getIPv4Reservation())) {
3878 // The call below will return a pointer to the lease for the address
3879 // reserved to this client, if the lease is available, i.e. is not
3880 // currently assigned to any other client.
3881 // Note that we don't remove the existing client's lease at this point
3882 // because this is not a real allocation, we just offer what we can
3883 // allocate in the DHCPREQUEST time.
3884 new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
3885 callout_status);
3886 if (!new_lease) {
3888 .arg(ctx.query_->getLabel())
3889 .arg(ctx.currentHost()->getIPv4Reservation().toText())
3890 .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
3891 "(no lease info)");
3892 StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
3893 ctx.conflicting_lease_->subnet_id_,
3894 "v4-reservation-conflicts"),
3895 static_cast<int64_t>(1));
3896 StatsMgr::instance().addValue("v4-reservation-conflicts",
3897 static_cast<int64_t>(1));
3898 }
3899
3900 } else {
3901 new_lease = renewLease4(client_lease, ctx);
3902 }
3903 }
3904
3905 // Client does not have a reservation or the allocation of the reserved
3906 // address has failed, probably because the reserved address is in use
3907 // by another client. If the client has a lease, we will check if we can
3908 // offer this lease to the client. The lease can't be offered in the
3909 // situation when it is reserved for another client or when the address
3910 // is not in the dynamic pool. The former may be the result of adding the
3911 // new reservation for the address used by this client. The latter may
3912 // be due to the client using the reserved out-of-the pool address, for
3913 // which the reservation has just been removed.
3914 if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
3915 !addressReserved(client_lease->addr_, ctx)) {
3916
3919 .arg(ctx.query_->getLabel());
3920
3921 // If offer-lifetime is shorter than the existing expiration, reset
3922 // offer-lifetime to zero. This allows us to simply return the
3923 // existing lease without updating it in the lease store.
3924 if ((ctx.offer_lft_) &&
3925 (time(NULL) + ctx.offer_lft_ < client_lease->getExpirationTime())) {
3926 ctx.offer_lft_ = 0;
3927 }
3928
3929 new_lease = renewLease4(client_lease, ctx);
3930 }
3931
3932 // The client doesn't have any lease or the lease can't be offered
3933 // because it is either reserved for some other client or the
3934 // address is not in the dynamic pool.
3935 // Let's use the client's hint (requested IP address), if the client
3936 // has provided it, and try to offer it. This address must not be
3937 // reserved for another client, and must be in the range of the
3938 // dynamic pool.
3939 if (!new_lease && !ctx.requested_address_.isV4Zero() &&
3940 inAllowedPool(ctx, ctx.requested_address_) &&
3941 !addressReserved(ctx.requested_address_, ctx)) {
3942
3945 .arg(ctx.requested_address_.toText())
3946 .arg(ctx.query_->getLabel());
3947
3948 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3949 callout_status);
3950 }
3951
3952 // The allocation engine failed to allocate all of the candidate
3953 // addresses. We will now use the allocator to pick the address
3954 // from the dynamic pool.
3955 if (!new_lease) {
3956
3959 .arg(ctx.query_->getLabel());
3960
3961 new_lease = allocateUnreservedLease4(ctx);
3962 }
3963
3964 // Some of the methods like reuseExpiredLease4 may set the old lease to point
3965 // to the lease which they remove/override. If it is not set, but we have
3966 // found that the client has the lease the client's lease is the one
3967 // to return as an old lease.
3968 if (!ctx.old_lease_ && client_lease) {
3969 ctx.old_lease_ = client_lease;
3970 }
3971
3972 return (new_lease);
3973}
3974
3976AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
3977 // Find an existing lease for this client. This function will return null
3978 // if there is a conflict with existing lease and the allocation should
3979 // not be continued.
3980 Lease4Ptr client_lease;
3981 findClientLease(ctx, client_lease);
3982
3983 // When the client sends the DHCPREQUEST, it should always specify the
3984 // address which it is requesting or renewing. That is, the client should
3985 // either use the requested IP address option or set the ciaddr. However,
3986 // we try to be liberal and allow the clients to not specify an address
3987 // in which case the allocation engine will pick a suitable address
3988 // for the client.
3989 if (!ctx.requested_address_.isV4Zero()) {
3990 // If the client has specified an address, make sure this address
3991 // is not reserved for another client. If it is, stop here because
3992 // we can't allocate this address.
3993 if (addressReserved(ctx.requested_address_, ctx)) {
3994
3997 .arg(ctx.query_->getLabel())
3998 .arg(ctx.requested_address_.toText());
3999
4000 return (Lease4Ptr());
4001 }
4002
4003 } else if (hasAddressReservation(ctx)) {
4004 // The client hasn't specified an address to allocate, so the
4005 // allocation engine needs to find an appropriate address.
4006 // If there is a reservation for the client, let's try to
4007 // allocate the reserved address.
4008 ctx.requested_address_ = ctx.currentHost()->getIPv4Reservation();
4009
4012 .arg(ctx.query_->getLabel())
4013 .arg(ctx.requested_address_.toText());
4014 }
4015
4016 if (!ctx.requested_address_.isV4Zero()) {
4017 // There is a specific address to be allocated. Let's find out if
4018 // the address is in use.
4019 Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(ctx.requested_address_);
4020 // If the address is in use (allocated and not expired), we check
4021 // if the address is in use by our client or another client.
4022 // If it is in use by another client, the address can't be
4023 // allocated.
4024 if (existing && !existing->expired() &&
4025 !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
4026 ctx.clientid_ : ClientIdPtr())) {
4027
4030 .arg(ctx.query_->getLabel())
4031 .arg(ctx.requested_address_.toText());
4032
4033 return (Lease4Ptr());
4034 }
4035
4036 // If the client has a reservation but it is requesting a different
4037 // address it is possible that the client was offered this different
4038 // address because the reserved address is in use. We will have to
4039 // check if the address is in use.
4040 if (hasAddressReservation(ctx) &&
4041 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) {
4042 existing =
4043 LeaseMgrFactory::instance().getLease4(ctx.currentHost()->getIPv4Reservation());
4044 // If the reserved address is not in use, i.e. the lease doesn't
4045 // exist or is expired, and the client is requesting a different
4046 // address, return NULL. The client should go back to the
4047 // DHCPDISCOVER and the reserved address will be offered.
4048 if (!existing || existing->expired()) {
4049
4052 .arg(ctx.query_->getLabel())
4053 .arg(ctx.currentHost()->getIPv4Reservation().toText())
4054 .arg(ctx.requested_address_.toText());
4055
4056 return (Lease4Ptr());
4057 }
4058 }
4059
4060 // The use of the out-of-pool addresses is only allowed when the requested
4061 // address is reserved for the client. If the address is not reserved one
4062 // and it doesn't belong to the dynamic pool, do not allocate it.
4063 if ((!hasAddressReservation(ctx) ||
4064 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
4065 !inAllowedPool(ctx, ctx.requested_address_)) {
4066
4069 .arg(ctx.query_->getLabel())
4070 .arg(ctx.requested_address_);
4071
4072 ctx.unknown_requested_addr_ = true;
4073 return (Lease4Ptr());
4074 }
4075 }
4076
4077 // We have gone through all the checks, so we can now allocate the address
4078 // for the client.
4079
4080 // If the client is requesting an address which is assigned to the client
4081 // let's just renew this address. Also, renew this address if the client
4082 // doesn't request any specific address.
4083 // Added extra checks: the address is reserved for this client or belongs
4084 // to the dynamic pool for the case the pool class has changed before the
4085 // request.
4086 if (client_lease) {
4087 if (((client_lease->addr_ == ctx.requested_address_) ||
4089 ((hasAddressReservation(ctx) &&
4090 (ctx.currentHost()->getIPv4Reservation() == ctx.requested_address_)) ||
4091 inAllowedPool(ctx, client_lease->addr_))) {
4092
4095 .arg(ctx.query_->getLabel())
4096 .arg(ctx.requested_address_);
4097
4098 return (renewLease4(client_lease, ctx));
4099 }
4100 }
4101
4102 // new_lease will hold the pointer to the allocated lease if we allocate
4103 // successfully.
4104 Lease4Ptr new_lease;
4105
4106 // The client doesn't have the lease or it is requesting an address
4107 // which it doesn't have. Let's try to allocate the requested address.
4108 if (!ctx.requested_address_.isV4Zero()) {
4109
4112 .arg(ctx.query_->getLabel())
4113 .arg(ctx.requested_address_.toText());
4114
4115 // The call below will return a pointer to the lease allocated
4116 // for the client if there is no lease for the requested address,
4117 // or the existing lease has expired. If the allocation fails,
4118 // e.g. because the lease is in use, we will return NULL to
4119 // indicate that we were unable to allocate the lease.
4121 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
4122 callout_status);
4123
4124 } else {
4125
4128 .arg(ctx.query_->getLabel());
4129
4130 // We will only get here if the client didn't specify which
4131 // address it wanted to be allocated. The allocation engine will
4132 // to pick the address from the dynamic pool.
4133 new_lease = allocateUnreservedLease4(ctx);
4134 }
4135
4136 // If we allocated the lease for the client, but the client already had a
4137 // lease, we will need to return the pointer to the previous lease and
4138 // the previous lease needs to be removed from the lease database.
4139 if (new_lease && client_lease) {
4140 ctx.old_lease_ = Lease4Ptr(new Lease4(*client_lease));
4141
4144 .arg(ctx.query_->getLabel())
4145 .arg(client_lease->addr_.toText());
4146
4147 if (LeaseMgrFactory::instance().deleteLease(client_lease) && (client_lease->state_ != Lease4::STATE_RELEASED)) {
4148 // Need to decrease statistic for assigned addresses.
4149 StatsMgr::instance().addValue(
4150 StatsMgr::generateName("subnet", client_lease->subnet_id_,
4151 "assigned-addresses"),
4152 static_cast<int64_t>(-1));
4153
4154 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(client_lease->subnet_id_);
4155 if (subnet) {
4156 auto const& pool = subnet->getPool(Lease::TYPE_V4, client_lease->addr_, false);
4157 if (pool) {
4158 StatsMgr::instance().addValue(
4159 StatsMgr::generateName("subnet", subnet->getID(),
4160 StatsMgr::generateName("pool", pool->getID(),
4161 "assigned-addresses")),
4162 static_cast<int64_t>(-1));
4163 }
4164 }
4165 }
4166 }
4167
4168 // Return the allocated lease or NULL pointer if allocation was
4169 // unsuccessful.
4170 return (new_lease);
4171}
4172
4173uint32_t
4175 // Not a DISCOVER or it's BOOTP, punt.
4176 if ((!ctx.fake_allocation_) || (ctx.query_->inClass("BOOTP"))) {
4177 return (0);
4178 }
4179
4180 util::Optional<uint32_t> offer_lft;
4181
4182 // If specified in one of our classes use it.
4183 // We use the first one we find.
4184 const ClientClasses classes = ctx.query_->getClasses();
4185 if (!classes.empty()) {
4186 // Let's get class definitions
4187 const ClientClassDictionaryPtr& dict =
4188 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4189
4190 // Iterate over the assigned class definitions.
4191 for (auto const& name : classes) {
4192 ClientClassDefPtr cl = dict->findClass(name);
4193 if (cl && (!cl->getOfferLft().unspecified())) {
4194 offer_lft = cl->getOfferLft();
4195 break;
4196 }
4197 }
4198 }
4199
4200 // If no classes specified it, get it from the subnet.
4201 if (offer_lft.unspecified()) {
4202 offer_lft = ctx.subnet_->getOfferLft();
4203 }
4204
4205 return (offer_lft.unspecified() ? 0 : offer_lft.get());
4206}
4207
4208uint32_t
4210 // If it's BOOTP, use infinite valid lifetime.
4211 if (ctx.query_->inClass("BOOTP")) {
4212 return (Lease::INFINITY_LFT);
4213 }
4214
4215 // Use the dhcp-lease-time content from the client if it's there.
4216 uint32_t requested_lft = 0;
4217 OptionPtr opt = ctx.query_->getOption(DHO_DHCP_LEASE_TIME);
4218 if (opt) {
4219 OptionUint32Ptr opt_lft = boost::dynamic_pointer_cast<OptionInt<uint32_t> >(opt);
4220 if (opt_lft) {
4221 requested_lft = opt_lft->getValue();
4222 }
4223 }
4224
4225 // If the triplet is specified in one of our classes use it.
4226 // We use the first one we find.
4227 Triplet<uint32_t> candidate_lft;
4228 const ClientClasses classes = ctx.query_->getClasses();
4229 if (!classes.empty()) {
4230 // Let's get class definitions
4231 const ClientClassDictionaryPtr& dict =
4232 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4233
4234 // Iterate over the assigned class definitions.
4235 for (auto const& name : classes) {
4236 ClientClassDefPtr cl = dict->findClass(name);
4237 if (cl && (!cl->getValid().unspecified())) {
4238 candidate_lft = cl->getValid();
4239 break;
4240 }
4241 }
4242 }
4243
4244 // If no classes specified it, get it from the subnet.
4245 if (!candidate_lft) {
4246 candidate_lft = ctx.subnet_->getValid();
4247 }
4248
4249 // If client requested a value, use the value bounded by
4250 // the candidate triplet.
4251 if (requested_lft > 0) {
4252 return (candidate_lft.get(requested_lft));
4253 }
4254
4255 // Use the candidate's default value.
4256 return (candidate_lft.get());
4257}
4258
4260AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
4261 CalloutHandle::CalloutNextStep& callout_status) {
4262 if (!ctx.hwaddr_) {
4263 isc_throw(BadValue, "Can't create a lease with NULL HW address");
4264 }
4265 if (!ctx.subnet_) {
4266 isc_throw(BadValue, "Can't create a lease without a subnet");
4267 }
4268
4269 // Get the context appropriate lifetime.
4270 uint32_t valid_lft = (ctx.offer_lft_ ? ctx.offer_lft_ : getValidLft(ctx));
4271
4272 time_t now = time(NULL);
4273
4274 ClientIdPtr client_id;
4275 if (ctx.subnet_->getMatchClientId()) {
4276 client_id = ctx.clientid_;
4277 }
4278
4279 Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
4280 valid_lft, now, ctx.subnet_->getID()));
4281
4282 // Set FQDN specific lease parameters.
4283 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4284 lease->fqdn_rev_ = ctx.rev_dns_update_;
4285 lease->hostname_ = ctx.hostname_;
4286
4287 // Add (update) the extended information on the lease.
4288 static_cast<void>(updateLease4ExtendedInfo(lease, ctx));
4289
4290 // Let's execute all callouts registered for lease4_select
4291 if (ctx.callout_handle_ &&
4292 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4293
4294 // Use the RAII wrapper to make sure that the callout handle state is
4295 // reset when this object goes out of scope. All hook points must do
4296 // it to prevent possible circular dependency between the callout
4297 // handle and its arguments.
4298 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4299
4300 // Enable copying options from the packet within hook library.
4301 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4302
4303 // Pass necessary arguments
4304 // Pass the original client query
4305 ctx.callout_handle_->setArgument("query4", ctx.query_);
4306
4307 // Subnet from which we do the allocation (That's as far as we can go
4308 // with using SubnetPtr to point to Subnet4 object. Users should not
4309 // be confused with dynamic_pointer_casts. They should get a concrete
4310 // pointer (Subnet4Ptr) pointing to a Subnet4 object.
4311 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4312 ctx.callout_handle_->setArgument("subnet4", subnet4);
4313
4314 // Is this solicit (fake = true) or request (fake = false)
4315 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4316
4317 // Are we allocating on DISCOVER? (i.e. offer_lft > 0).
4318 ctx.callout_handle_->setArgument("offer_lft", ctx.offer_lft_);
4319
4320 // Pass the intended lease as well
4321 ctx.callout_handle_->setArgument("lease4", lease);
4322
4323 // This is the first callout, so no need to clear any arguments
4324 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4325
4326 callout_status = ctx.callout_handle_->getStatus();
4327
4328 // Callouts decided to skip the action. This means that the lease is not
4329 // assigned, so the client will get NoAddrAvail as a result. The lease
4330 // won't be inserted into the database.
4331 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4333 return (Lease4Ptr());
4334 }
4335
4336 // Let's use whatever callout returned. Hopefully it is the same lease
4337 // we handled to it.
4338 ctx.callout_handle_->getArgument("lease4", lease);
4339 }
4340
4341 if (ctx.fake_allocation_ && ctx.offer_lft_) {
4342 // Turn them off before we persist, so we'll see it as different when
4343 // we extend it in the REQUEST. This should cause us to do DDNS (if
4344 // it's enabled).
4345 lease->fqdn_fwd_ = false;
4346 lease->fqdn_rev_ = false;
4347 }
4348
4349 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4350 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, lease->addr_, false);
4351 if (pool) {
4352 lease->pool_id_ = pool->getID();
4353 }
4354 // That is a real (REQUEST) allocation
4355 bool status = LeaseMgrFactory::instance().addLease(lease);
4356 if (status) {
4357
4358 // The lease insertion succeeded, let's bump up the statistic.
4359 StatsMgr::instance().addValue(
4360 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4361 "assigned-addresses"),
4362 static_cast<int64_t>(1));
4363
4364 StatsMgr::instance().addValue(
4365 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4366 "cumulative-assigned-addresses"),
4367 static_cast<int64_t>(1));
4368
4369 if (pool) {
4370 StatsMgr::instance().addValue(
4371 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4372 StatsMgr::generateName("pool", pool->getID(),
4373 "assigned-addresses")),
4374 static_cast<int64_t>(1));
4375
4376 StatsMgr::instance().addValue(
4377 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4378 StatsMgr::generateName("pool", pool->getID(),
4379 "cumulative-assigned-addresses")),
4380 static_cast<int64_t>(1));
4381 }
4382
4383 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4384 static_cast<int64_t>(1));
4385
4386 return (lease);
4387 } else {
4388 // One of many failures with LeaseMgr (e.g. lost connection to the
4389 // database, database failed etc.). One notable case for that
4390 // is that we are working in multi-process mode and we lost a race
4391 // (some other process got that address first)
4392 return (Lease4Ptr());
4393 }
4394 } else {
4395 // That is only fake (DISCOVER) allocation
4396 // It is for OFFER only. We should not insert the lease and callers
4397 // have already verified the lease does not exist in the database.
4398 return (lease);
4399 }
4400}
4401
4403AllocEngine::renewLease4(const Lease4Ptr& lease,
4405 if (!lease) {
4406 isc_throw(BadValue, "null lease specified for renewLease4");
4407 }
4408
4409 // Let's keep the old data. This is essential if we are using memfile
4410 // (the lease returned points directly to the lease4 object in the database)
4411 // We'll need it if we want to skip update (i.e. roll back renewal)
4413 Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
4414 ctx.old_lease_.reset(new Lease4(*old_values));
4415
4416 // Update the lease with the information from the context.
4417 // If there was no significant changes, try reuse.
4418 lease->reuseable_valid_lft_ = 0;
4419 if (!updateLease4Information(lease, ctx)) {
4420 setLeaseReusable(lease, ctx);
4421 }
4422
4423 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4424 // If the lease is expired we have to reclaim it before
4425 // re-assigning it to the client. The lease reclamation
4426 // involves execution of hooks and DNS update.
4427 if (ctx.old_lease_->expired()) {
4428 reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
4429 }
4430
4431 lease->state_ = Lease::STATE_DEFAULT;
4432 }
4433
4434 bool skip = false;
4435 // Execute all callouts registered for lease4_renew.
4436 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_renew_)) {
4437
4438 // Use the RAII wrapper to make sure that the callout handle state is
4439 // reset when this object goes out of scope. All hook points must do
4440 // it to prevent possible circular dependency between the callout
4441 // handle and its arguments.
4442 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4443
4444 // Enable copying options from the packet within hook library.
4445 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4446
4447 // Subnet from which we do the allocation. Convert the general subnet
4448 // pointer to a pointer to a Subnet4. Note that because we are using
4449 // boost smart pointers here, we need to do the cast using the boost
4450 // version of dynamic_pointer_cast.
4451 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4452
4453 // Pass the parameters. Note the clientid is passed only if match-client-id
4454 // is set. This is done that way, because the lease4-renew hook point is
4455 // about renewing a lease and the configuration parameter says the
4456 // client-id should be ignored. Hence no clientid value if match-client-id
4457 // is false.
4458 ctx.callout_handle_->setArgument("query4", ctx.query_);
4459 ctx.callout_handle_->setArgument("subnet4", subnet4);
4460 ctx.callout_handle_->setArgument("clientid", subnet4->getMatchClientId() ?
4461 ctx.clientid_ : ClientIdPtr());
4462 ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
4463
4464 // Pass the lease to be updated
4465 ctx.callout_handle_->setArgument("lease4", lease);
4466
4467 // Call all installed callouts
4468 HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
4469 *ctx.callout_handle_);
4470
4471 // Callouts decided to skip the next processing step. The next
4472 // processing step would actually renew the lease, so skip at this
4473 // stage means "keep the old lease as it is".
4474 if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4475 skip = true;
4478 }
4479
4481 }
4482
4483 if ((!ctx.fake_allocation_ || ctx.offer_lft_) && !skip && (lease->reuseable_valid_lft_ == 0)) {
4484 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, lease->addr_, false);
4485 if (pool) {
4486 lease->pool_id_ = pool->getID();
4487 }
4488
4489 // for REQUEST we do update the lease
4490 LeaseMgrFactory::instance().updateLease4(lease);
4491
4492 // We need to account for the re-assignment of the lease.
4493 if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
4494 StatsMgr::instance().addValue(
4495 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4496 "assigned-addresses"),
4497 static_cast<int64_t>(1));
4498
4499 StatsMgr::instance().addValue(
4500 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4501 "cumulative-assigned-addresses"),
4502 static_cast<int64_t>(1));
4503
4504 if (pool) {
4505 StatsMgr::instance().addValue(
4506 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4507 StatsMgr::generateName("pool", pool->getID(),
4508 "assigned-addresses")),
4509 static_cast<int64_t>(1));
4510
4511 StatsMgr::instance().addValue(
4512 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4513 StatsMgr::generateName("pool", pool->getID(),
4514 "cumulative-assigned-addresses")),
4515 static_cast<int64_t>(1));
4516 }
4517
4518 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4519 static_cast<int64_t>(1));
4520 }
4521 }
4522 if (skip) {
4523 // Rollback changes (really useful only for memfile)
4525 *lease = *old_values;
4526 }
4527
4528 return (lease);
4529}
4530
4532AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
4534 CalloutHandle::CalloutNextStep& callout_status) {
4535 if (!expired) {
4536 isc_throw(BadValue, "null lease specified for reuseExpiredLease");
4537 }
4538
4539 if (!ctx.subnet_) {
4540 isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
4541 }
4542
4543 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4544 // The expired lease needs to be reclaimed before it can be reused.
4545 // This includes declined leases for which probation period has
4546 // elapsed.
4547 reclaimExpiredLease(expired, ctx.callout_handle_);
4548 expired->state_ = Lease::STATE_DEFAULT;
4549 }
4550
4551 expired->reuseable_valid_lft_ = 0;
4552 static_cast<void>(updateLease4Information(expired, ctx));
4553
4556 .arg(ctx.query_->getLabel())
4557 .arg(expired->toText());
4558
4559 // Let's execute all callouts registered for lease4_select
4560 if (ctx.callout_handle_ &&
4561 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4562
4563 // Enable copying options from the packet within hook library.
4564 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4565
4566 // Use the RAII wrapper to make sure that the callout handle state is
4567 // reset when this object goes out of scope. All hook points must do
4568 // it to prevent possible circular dependency between the callout
4569 // handle and its arguments.
4570 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4571
4572 // Pass necessary arguments
4573 // Pass the original client query
4574 ctx.callout_handle_->setArgument("query4", ctx.query_);
4575
4576 // Subnet from which we do the allocation. Convert the general subnet
4577 // pointer to a pointer to a Subnet4. Note that because we are using
4578 // boost smart pointers here, we need to do the cast using the boost
4579 // version of dynamic_pointer_cast.
4580 Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4581 ctx.callout_handle_->setArgument("subnet4", subnet4);
4582
4583 // Is this solicit (fake = true) or request (fake = false)
4584 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4585 ctx.callout_handle_->setArgument("offer_lft", ctx.offer_lft_);
4586
4587 // The lease that will be assigned to a client
4588 ctx.callout_handle_->setArgument("lease4", expired);
4589
4590 // Call the callouts
4591 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4592
4593 callout_status = ctx.callout_handle_->getStatus();
4594
4595 // Callouts decided to skip the action. This means that the lease is not
4596 // assigned, so the client will get NoAddrAvail as a result. The lease
4597 // won't be inserted into the database.
4598 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4601 return (Lease4Ptr());
4602 }
4603
4605
4606 // Let's use whatever callout returned. Hopefully it is the same lease
4607 // we handed to it.
4608 ctx.callout_handle_->getArgument("lease4", expired);
4609 }
4610
4611 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4612 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, expired->addr_, false);
4613 if (pool) {
4614 expired->pool_id_ = pool->getID();
4615 }
4616 // for REQUEST we do update the lease
4617 LeaseMgrFactory::instance().updateLease4(expired);
4618
4619 // We need to account for the re-assignment of the lease.
4620 StatsMgr::instance().addValue(
4621 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4622 "assigned-addresses"),
4623 static_cast<int64_t>(1));
4624
4625 StatsMgr::instance().addValue(
4626 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4627 "cumulative-assigned-addresses"),
4628 static_cast<int64_t>(1));
4629
4630 if (pool) {
4631 StatsMgr::instance().addValue(
4632 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4633 StatsMgr::generateName("pool", pool->getID(),
4634 "assigned-addresses")),
4635 static_cast<int64_t>(1));
4636
4637 StatsMgr::instance().addValue(
4638 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4639 StatsMgr::generateName("pool", pool->getID(),
4640 "cumulative-assigned-addresses")),
4641 static_cast<int64_t>(1));
4642 }
4643
4644 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4645 static_cast<int64_t>(1));
4646 }
4647
4648 // We do nothing for SOLICIT. We'll just update database when
4649 // the client gets back to us with REQUEST message.
4650
4651 // it's not really expired at this stage anymore - let's return it as
4652 // an updated lease
4653 return (expired);
4654}
4655
4657AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
4658 CalloutHandle::CalloutNextStep& callout_status) {
4659 ctx.conflicting_lease_.reset();
4660
4661 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4662 if (exist_lease) {
4663 if (exist_lease->expired()) {
4664 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4665 // reuseExpiredLease4() will reclaim the use which will
4666 // queue an NCR remove it needed. Clear the DNS fields in
4667 // the old lease to avoid a redundant remove in server logic.
4668 ctx.old_lease_->hostname_.clear();
4669 ctx.old_lease_->fqdn_fwd_ = false;
4670 ctx.old_lease_->fqdn_rev_ = false;
4671 return (reuseExpiredLease4(exist_lease, ctx, callout_status));
4672
4673 } else {
4674 // If there is a lease and it is not expired, pass this lease back
4675 // to the caller in the context. The caller may need to know
4676 // which lease we're conflicting with.
4677 ctx.conflicting_lease_ = exist_lease;
4678 }
4679
4680 } else {
4681 return (createLease4(ctx, candidate, callout_status));
4682 }
4683 return (Lease4Ptr());
4684}
4685
4687AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
4688 Lease4Ptr new_lease;
4689 Subnet4Ptr subnet = ctx.subnet_;
4690
4691 // Need to check if the subnet belongs to a shared network. If so,
4692 // we might be able to find a better subnet for lease allocation,
4693 // for which it is more likely that there are some leases available.
4694 // If we stick to the selected subnet, we may end up walking over
4695 // the entire subnet (or more subnets) to discover that the address
4696 // pools have been exhausted. Using a subnet from which an address
4697 // was assigned most recently is an optimization which increases
4698 // the likelihood of starting from the subnet which address pools
4699 // are not exhausted.
4700 SharedNetwork4Ptr network;
4701 ctx.subnet_->getSharedNetwork(network);
4702 if (network) {
4703 // This would try to find a subnet with the same set of classes
4704 // as the current subnet, but with the more recent "usage timestamp".
4705 // This timestamp is only updated for the allocations made with an
4706 // allocator (unreserved lease allocations), not the static
4707 // allocations or requested addresses.
4708 ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
4709 }
4710
4711 // We have the choice in the order checking the lease and
4712 // the reservation. The default is to begin by the lease
4713 // if the multi-threading is disabled.
4714 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
4715
4716 Subnet4Ptr original_subnet = subnet;
4717
4718 uint128_t total_attempts = 0;
4719
4720 // The following counter tracks the number of subnets with matching client
4721 // classes from which the allocation engine attempted to assign leases.
4722 uint64_t subnets_with_unavail_leases = 0;
4723 // The following counter tracks the number of subnets in which there were
4724 // no matching pools for the client.
4725 uint64_t subnets_with_unavail_pools = 0;
4726
4727 auto const& classes = ctx.query_->getClasses();
4728
4729 while (subnet) {
4730 ClientIdPtr client_id;
4731 if (subnet->getMatchClientId()) {
4732 client_id = ctx.clientid_;
4733 }
4734
4735 uint128_t const possible_attempts =
4736 subnet->getPoolCapacity(Lease::TYPE_V4, classes);
4737
4738 // If the number of tries specified in the allocation engine constructor
4739 // is set to 0 (unlimited) or the pools capacity is lower than that number,
4740 // let's use the pools capacity as the maximum number of tries. Trying
4741 // more than the actual pools capacity is a waste of time. If the specified
4742 // number of tries is lower than the pools capacity, use that number.
4743 uint128_t const max_attempts =
4744 (attempts_ == 0 || possible_attempts < attempts_) ?
4745 possible_attempts :
4746 attempts_;
4747
4748 if (max_attempts > 0) {
4749 // If max_attempts is greater than 0, there are some pools in this subnet
4750 // from which we can potentially get a lease.
4751 ++subnets_with_unavail_leases;
4752 } else {
4753 // If max_attempts is 0, it is an indication that there are no pools
4754 // in the subnet from which we can get a lease.
4755 ++subnets_with_unavail_pools;
4756 }
4757
4758 bool exclude_first_last_24 = ((subnet->get().second <= 24) &&
4759 CfgMgr::instance().getCurrentCfg()->getExcludeFirstLast24());
4760
4762
4763 for (uint128_t i = 0; i < max_attempts; ++i) {
4764
4765 ++total_attempts;
4766
4767 auto allocator = subnet->getAllocator(Lease::TYPE_V4);
4768 IOAddress candidate = allocator->pickAddress(classes,
4769 client_id,
4770 ctx.requested_address_);
4771
4772 // An allocator may return zero address when it has pools exhausted.
4773 if (candidate.isV4Zero()) {
4774 break;
4775 }
4776
4777 if (exclude_first_last_24) {
4778 // Exclude .0 and .255 addresses.
4779 auto const& bytes = candidate.toBytes();
4780 if ((bytes.size() != 4) ||
4781 (bytes[3] == 0) || (bytes[3] == 255U)) {
4782 // Don't allocate.
4783 continue;
4784 }
4785 }
4786
4787 // First check for reservation when it is the choice.
4788 if (check_reservation_first && addressReserved(candidate, ctx)) {
4789 // Don't allocate.
4790 continue;
4791 }
4792
4793 // Check if the resource is busy i.e. can be being allocated
4794 // by another thread to another client.
4795 ResourceHandler4 resource_handler;
4796 if (MultiThreadingMgr::instance().getMode() &&
4797 !resource_handler.tryLock4(candidate)) {
4798 // Don't allocate.
4799 continue;
4800 }
4801
4802 // Check for an existing lease for the candidate address.
4803 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4804 if (!exist_lease) {
4805 // No existing lease, is it reserved?
4806 if (check_reservation_first || !addressReserved(candidate, ctx)) {
4807 // Not reserved use it.
4808 new_lease = createLease4(ctx, candidate, callout_status);
4809 }
4810 } else {
4811 // An lease exists, is expired, and not reserved use it.
4812 if (exist_lease->expired() &&
4813 (check_reservation_first || !addressReserved(candidate, ctx))) {
4814 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4815 new_lease = reuseExpiredLease4(exist_lease, ctx, callout_status);
4816 }
4817 }
4818
4819 // We found a lease we can use, return it.
4820 if (new_lease) {
4821 return (new_lease);
4822 }
4823
4824 if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
4825 // Don't retry when the callout status is not continue.
4826 subnet.reset();
4827 break;
4828 }
4829 }
4830
4831 // This pointer may be set to NULL if hooks set SKIP status.
4832 if (subnet) {
4833 subnet = subnet->getNextSubnet(original_subnet, classes);
4834
4835 if (subnet) {
4836 ctx.subnet_ = subnet;
4837 }
4838 }
4839 }
4840
4841 if (network) {
4842 // The client is in the shared network. Let's log the high level message
4843 // indicating which shared network the client belongs to.
4845 .arg(ctx.query_->getLabel())
4846 .arg(network->getName())
4847 .arg(subnets_with_unavail_leases)
4848 .arg(subnets_with_unavail_pools);
4849 StatsMgr::instance().addValue("v4-allocation-fail-shared-network",
4850 static_cast<int64_t>(1));
4851 StatsMgr::instance().addValue(
4852 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4853 "v4-allocation-fail-shared-network"),
4854 static_cast<int64_t>(1));
4855 } else {
4856 // The client is not connected to a shared network. It is connected
4857 // to a subnet. Let's log some details about the subnet.
4858 std::string shared_network = ctx.subnet_->getSharedNetworkName();
4859 if (shared_network.empty()) {
4860 shared_network = "(none)";
4861 }
4863 .arg(ctx.query_->getLabel())
4864 .arg(ctx.subnet_->toText())
4865 .arg(ctx.subnet_->getID())
4866 .arg(shared_network);
4867 StatsMgr::instance().addValue("v4-allocation-fail-subnet",
4868 static_cast<int64_t>(1));
4869 StatsMgr::instance().addValue(
4870 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4871 "v4-allocation-fail-subnet"),
4872 static_cast<int64_t>(1));
4873 }
4874 if (total_attempts == 0) {
4875 // In this case, it seems that none of the pools in the subnets could
4876 // be used for that client, both in case the client is connected to
4877 // a shared network or to a single subnet. Apparently, the client was
4878 // rejected to use the pools because of the client classes' mismatch.
4880 .arg(ctx.query_->getLabel());
4881 StatsMgr::instance().addValue("v4-allocation-fail-no-pools",
4882 static_cast<int64_t>(1));
4883 StatsMgr::instance().addValue(
4884 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4885 "v4-allocation-fail-no-pools"),
4886 static_cast<int64_t>(1));
4887 } else {
4888 // This is an old log message which provides a number of attempts
4889 // made by the allocation engine to allocate a lease. The only case
4890 // when we don't want to log this message is when the number of
4891 // attempts is zero (condition above), because it would look silly.
4893 .arg(ctx.query_->getLabel())
4894 .arg(total_attempts);
4895 StatsMgr::instance().addValue("v4-allocation-fail",
4896 static_cast<int64_t>(1));
4897 StatsMgr::instance().addValue(
4898 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4899 "v4-allocation-fail"),
4900 static_cast<int64_t>(1));
4901 }
4902
4903 if (!classes.empty()) {
4905 .arg(ctx.query_->getLabel())
4906 .arg(classes.toText());
4907 StatsMgr::instance().addValue("v4-allocation-fail-classes",
4908 static_cast<int64_t>(1));
4909 StatsMgr::instance().addValue(
4910 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4911 "v4-allocation-fail-classes"),
4912 static_cast<int64_t>(1));
4913 }
4914
4915 return (new_lease);
4916}
4917
4918bool
4919AllocEngine::updateLease4Information(const Lease4Ptr& lease,
4920 AllocEngine::ClientContext4& ctx) const {
4921 bool changed = false;
4922 if (lease->subnet_id_ != ctx.subnet_->getID()) {
4923 changed = true;
4924 lease->subnet_id_ = ctx.subnet_->getID();
4925 }
4926 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
4927 (ctx.hwaddr_ &&
4928 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
4929 changed = true;
4930 lease->hwaddr_ = ctx.hwaddr_;
4931 }
4932 if (ctx.subnet_->getMatchClientId() && ctx.clientid_) {
4933 if (!lease->client_id_ || (*ctx.clientid_ != *lease->client_id_)) {
4934 changed = true;
4935 lease->client_id_ = ctx.clientid_;
4936 }
4937 } else if (lease->client_id_) {
4938 changed = true;
4939 lease->client_id_ = ClientIdPtr();
4940 }
4941 lease->cltt_ = time(NULL);
4942
4943 // Get the context appropriate valid lifetime.
4944 lease->valid_lft_ = (ctx.offer_lft_ ? ctx.offer_lft_ : getValidLft(ctx));
4945
4946 // Valid lifetime has changed.
4947 if (lease->valid_lft_ != lease->current_valid_lft_) {
4948 changed = true;
4949 }
4950
4951 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
4952 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
4953 (lease->hostname_ != ctx.hostname_)) {
4954 changed = true;
4955 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4956 lease->fqdn_rev_ = ctx.rev_dns_update_;
4957 lease->hostname_ = ctx.hostname_;
4958 }
4959
4960 // Add (update) the extended information on the lease.
4961 if (updateLease4ExtendedInfo(lease, ctx)) {
4962 changed = true;
4963 }
4964
4965 return (changed);
4966}
4967
4968bool
4970 const AllocEngine::ClientContext4& ctx) const {
4971 bool changed = false;
4972
4973 // If storage is not enabled then punt.
4974 if (!ctx.subnet_->getStoreExtendedInfo()) {
4975 return (changed);
4976 }
4977
4978 // Look for relay agent information option (option 82)
4979 OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS);
4980 if (!rai) {
4981 // Pkt4 doesn't have it, so nothing to store (or update).
4982 return (changed);
4983 }
4984
4985 // Check if the RAI was recovered from stashed agent options.
4986 ConstElementPtr sao = CfgMgr::instance().getCurrentCfg()->
4987 getConfiguredGlobal(CfgGlobals::STASH_AGENT_OPTIONS);
4988 if (sao && (sao->getType() == data::Element::boolean) &&
4989 sao->boolValue() && ctx.query_->inClass("STASH_AGENT_OPTIONS")) {
4990 return (changed);
4991 }
4992
4993 // Create a StringElement with the hex string for relay-agent-info.
4994 ElementPtr relay_agent(new StringElement(rai->toHexString()));
4995
4996 // Now we wrap the agent info in a map. This allows for future expansion.
4997 ElementPtr extended_info = Element::createMap();
4998 extended_info->set("sub-options", relay_agent);
4999
5000 OptionPtr remote_id = rai->getOption(RAI_OPTION_REMOTE_ID);
5001 if (remote_id) {
5002 std::vector<uint8_t> bytes = remote_id->toBinary();
5003 lease->remote_id_ = bytes;
5004 if (bytes.size() > 0) {
5005 extended_info->set("remote-id",
5007 }
5008 }
5009
5010 OptionPtr relay_id = rai->getOption(RAI_OPTION_RELAY_ID);
5011 if (relay_id) {
5012 std::vector<uint8_t> bytes = relay_id->toBinary(false);
5013 lease->relay_id_ = bytes;
5014 if (bytes.size() > 0) {
5015 extended_info->set("relay-id",
5017 }
5018 }
5019
5020 // Get a mutable copy of the lease's current user context.
5021 ConstElementPtr user_context = lease->getContext();
5022 ElementPtr mutable_user_context;
5023 if (user_context && (user_context->getType() == Element::map)) {
5024 mutable_user_context = copy(user_context, 0);
5025 } else {
5026 mutable_user_context = Element::createMap();
5027 }
5028
5029 // Get a mutable copy of the ISC entry.
5030 ConstElementPtr isc = mutable_user_context->get("ISC");
5031 ElementPtr mutable_isc;
5032 if (isc && (isc->getType() == Element::map)) {
5033 mutable_isc = copy(isc, 0);
5034 } else {
5035 mutable_isc = Element::createMap();
5036 }
5037
5038 // Add/replace the extended info entry.
5039 ConstElementPtr old_extended_info = mutable_isc->get("relay-agent-info");
5040 if (!old_extended_info || (*old_extended_info != *extended_info)) {
5041 changed = true;
5042 mutable_isc->set("relay-agent-info", extended_info);
5043 mutable_user_context->set("ISC", mutable_isc);
5044 }
5045
5046 // Update the lease's user_context.
5047 lease->setContext(mutable_user_context);
5048
5049 return (changed);
5050}
5051
5052void
5054 const AllocEngine::ClientContext6& ctx) const {
5055 // The extended info action is a transient value but be safe so reset it.
5056 lease->extended_info_action_ = Lease6::ACTION_IGNORE;
5057
5058 // If storage is not enabled then punt.
5059 if (!ctx.subnet_->getStoreExtendedInfo()) {
5060 return;
5061 }
5062
5063 // If we do not have relay information, then punt.
5064 if (ctx.query_->relay_info_.empty()) {
5065 return;
5066 }
5067
5068 // We need to convert the vector of RelayInfo instances in
5069 // into an Element hierarchy like this:
5070 // "relay-info": [
5071 // {
5072 // "hop": 123,
5073 // "link": "2001:db8::1",
5074 // "peer": "2001:db8::2",
5075 // "options": "0x..."
5076 // },..]
5077 //
5078 ElementPtr extended_info = Element::createList();
5079 for (auto const& relay : ctx.query_->relay_info_) {
5080 ElementPtr relay_elem = Element::createMap();
5081 relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
5082 relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
5083 relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
5084
5085 // If there are relay options, we'll pack them into a buffer and then
5086 // convert that into a hex string. If there are no options, we omit
5087 // then entry.
5088 if (!relay.options_.empty()) {
5089 OutputBuffer buf(128);
5090 LibDHCP::packOptions6(buf, relay.options_);
5091
5092 if (buf.getLength() > 0) {
5093 const uint8_t* cp = buf.getData();
5094 std::vector<uint8_t> bytes;
5095 std::stringstream ss;
5096
5097 bytes.assign(cp, cp + buf.getLength());
5098 ss << "0x" << encode::encodeHex(bytes);
5099 relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
5100 }
5101
5102 auto remote_id_it = relay.options_.find(D6O_REMOTE_ID);
5103 if (remote_id_it != relay.options_.end()) {
5104 OptionPtr remote_id = remote_id_it->second;
5105 if (remote_id) {
5106 std::vector<uint8_t> bytes = remote_id->toBinary();
5107 if (bytes.size() > 0) {
5108 relay_elem->set("remote-id",
5110 }
5111 }
5112 }
5113
5114 auto relay_id_it = relay.options_.find(D6O_RELAY_ID);
5115 if (relay_id_it != relay.options_.end()) {
5116 OptionPtr relay_id = relay_id_it->second;
5117 if (relay_id) {
5118 std::vector<uint8_t> bytes = relay_id->toBinary(false);
5119 if (bytes.size() > 0) {
5120 relay_elem->set("relay-id",
5122 }
5123 }
5124 }
5125 }
5126
5127 extended_info->add(relay_elem);
5128 }
5129
5130 // Get a mutable copy of the lease's current user context.
5131 ConstElementPtr user_context = lease->getContext();
5132 ElementPtr mutable_user_context;
5133 if (user_context && (user_context->getType() == Element::map)) {
5134 mutable_user_context = copy(user_context, 0);
5135 } else {
5136 mutable_user_context = Element::createMap();
5137 }
5138
5139 // Get a mutable copy of the ISC entry.
5140 ConstElementPtr isc = mutable_user_context->get("ISC");
5141 ElementPtr mutable_isc;
5142 if (isc && (isc->getType() == Element::map)) {
5143 mutable_isc = copy(isc, 0);
5144 } else {
5145 mutable_isc = Element::createMap();
5146 }
5147
5148 // Add/replace the extended info entry.
5149 ConstElementPtr old_extended_info = mutable_isc->get("relay-info");
5150 if (!old_extended_info || (*old_extended_info != *extended_info)) {
5151 lease->extended_info_action_ = Lease6::ACTION_UPDATE;
5152 mutable_isc->set("relay-info", extended_info);
5153 mutable_user_context->set("ISC", mutable_isc);
5154 }
5155
5156 // Update the lease's user context.
5157 lease->setContext(mutable_user_context);
5158}
5159
5160void
5161AllocEngine::setLeaseReusable(const Lease4Ptr& lease,
5162 const ClientContext4& ctx) const {
5163 // Sanity.
5164 lease->reuseable_valid_lft_ = 0;
5165 const Subnet4Ptr& subnet = ctx.subnet_;
5166 if (!subnet) {
5167 return;
5168 }
5169 if (lease->state_ != Lease::STATE_DEFAULT) {
5170 return;
5171 }
5172
5173 // Always reuse infinite lifetime leases.
5174 if (lease->valid_lft_ == Lease::INFINITY_LFT) {
5175 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
5176 return;
5177 }
5178
5179 // Refuse time not going forward.
5180 if (lease->cltt_ < lease->current_cltt_) {
5181 return;
5182 }
5183
5184 uint32_t age = lease->cltt_ - lease->current_cltt_;
5185 // Already expired.
5186 if (age >= lease->current_valid_lft_) {
5187 return;
5188 }
5189
5190 // Try cache max age.
5191 uint32_t max_age = 0;
5192 if (!subnet->getCacheMaxAge().unspecified()) {
5193 max_age = subnet->getCacheMaxAge().get();
5194 if ((max_age == 0) || (age > max_age)) {
5195 return;
5196 }
5197 }
5198
5199 // Try cache threshold.
5200 if (!subnet->getCacheThreshold().unspecified()) {
5201 double threshold = subnet->getCacheThreshold().get();
5202 if ((threshold <= 0.) || (threshold > 1.)) {
5203 return;
5204 }
5205 max_age = lease->valid_lft_ * threshold;
5206 if (age > max_age) {
5207 return;
5208 }
5209 }
5210
5211 // No cache.
5212 if (max_age == 0) {
5213 return;
5214 }
5215
5216 // Seems to be reusable.
5217 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
5218}
5219
5220void
5221AllocEngine::setLeaseReusable(const Lease6Ptr& lease,
5222 uint32_t current_preferred_lft,
5223 const ClientContext6& ctx) const {
5224 // Sanity.
5225 lease->reuseable_valid_lft_ = 0;
5226 lease->reuseable_preferred_lft_ = 0;
5227 const Subnet6Ptr& subnet = ctx.subnet_;
5228 if (!subnet) {
5229 return;
5230 }
5231 if (lease->state_ != Lease::STATE_DEFAULT) {
5232 return;
5233 }
5234
5235 // Refuse time not going forward.
5236 if (lease->cltt_ < lease->current_cltt_) {
5237 return;
5238 }
5239
5240 uint32_t age = lease->cltt_ - lease->current_cltt_;
5241 // Already expired.
5242 if (age >= lease->current_valid_lft_) {
5243 return;
5244 }
5245
5246 // Try cache max age.
5247 uint32_t max_age = 0;
5248 if (!subnet->getCacheMaxAge().unspecified()) {
5249 max_age = subnet->getCacheMaxAge().get();
5250 if ((max_age == 0) || (age > max_age)) {
5251 return;
5252 }
5253 }
5254
5255 // Try cache threshold.
5256 if (!subnet->getCacheThreshold().unspecified()) {
5257 double threshold = subnet->getCacheThreshold().get();
5258 if ((threshold <= 0.) || (threshold > 1.)) {
5259 return;
5260 }
5261 max_age = lease->valid_lft_ * threshold;
5262 if (age > max_age) {
5263 return;
5264 }
5265 }
5266
5267 // No cache.
5268 if (max_age == 0) {
5269 return;
5270 }
5271
5272 // Seems to be reusable.
5273 if ((current_preferred_lft == Lease::INFINITY_LFT) ||
5274 (current_preferred_lft == 0)) {
5275 // Keep these values.
5276 lease->reuseable_preferred_lft_ = current_preferred_lft;
5277 } else if (current_preferred_lft > age) {
5278 lease->reuseable_preferred_lft_ = current_preferred_lft - age;
5279 } else {
5280 // Can be a misconfiguration so stay safe...
5281 return;
5282 }
5283 if (lease->current_valid_lft_ == Lease::INFINITY_LFT) {
5284 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
5285 } else {
5286 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
5287 }
5288}
5289
5290} // namespace dhcp
5291} // 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.
A generic exception that is thrown if a function is called in a prohibited way.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
Notes: IntElement type is changed to int64_t.
Definition data.h:615
Multiple lease records found where one expected.
Defines a single hint.
static IPv6Resrv makeIPv6Resrv(const Lease6 &lease)
Creates an IPv6Resrv instance from a Lease6.
void updateLease6ExtendedInfo(const Lease6Ptr &lease, const ClientContext6 &ctx) const
Stores additional client query parameters on a V6 lease.
void reclaimExpiredLeases6Internal(const size_t max_leases, const uint16_t timeout, const bool remove_lease, const uint16_t max_unwarned_cycles=0)
Body of reclaimExpiredLeases6.
bool updateLease4ExtendedInfo(const Lease4Ptr &lease, const ClientContext4 &ctx) const
Stores additional client query parameters on a V4 lease.
static uint32_t getOfferLft(const ClientContext4 &ctx)
Returns the offer lifetime based on the v4 context.
static std::string labelNetworkOrSubnet(SubnetPtr subnet)
Generates a label for subnet or shared-network from subnet.
static ConstHostPtr findGlobalReservation(ClientContext6 &ctx)
Attempts to find the host reservation for the client.
AllocEngine(isc::util::uint128_t const &attempts)
Constructor.
std::pair< Host::IdentifierType, std::vector< uint8_t > > IdentifierPair
A tuple holding host identifier type and value.
void clearReclaimedExtendedInfo(const Lease4Ptr &lease) const
Clear extended info from a reclaimed V4 lease.
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)
void reclaimExpiredLeases4Internal(const size_t max_leases, const uint16_t timeout, const bool remove_lease, const uint16_t max_unwarned_cycles=0)
Body of reclaimExpiredLeases4.
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.
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.
PrefixLenMatchType
Type of preferred PD-pool prefix length selection criteria.
Definition allocator.h:61
static bool isValidPrefixPool(Allocator::PrefixLenMatchType prefix_length_match, PoolPtr pool, uint8_t hint_prefix_length)
Check if the pool matches the selection criteria relative to the provided hint prefix length.
Definition allocator.cc:39
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:25
Container for storing client class names.
Definition classify.h:108
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition srv_config.h:49
static HostMgr & instance()
Returns a sole instance of the HostMgr.
Definition host_mgr.cc:114
IPv6 reservation for a host.
Definition host.h:163
Type
Type of the reservation.
Definition host.h:169
static TrackingLeaseMgr & instance()
Return current lease manager.
Abstract Lease Manager.
Definition lease_mgr.h:248
virtual void updateLease4(const Lease4Ptr &lease4)=0
Updates IPv4 lease.
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.
static std::string makeLabel(const HWAddrPtr &hwaddr, const ClientIdPtr &client_id, const uint32_t transid)
Returns text representation of the given packet identifiers.
Definition pkt4.cc:397
static std::string makeLabel(const DuidPtr duid, const uint32_t transid, const HWAddrPtr &hwaddr)
Returns text representation of the given packet identifiers.
Definition pkt6.cc:691
Resource race avoidance RAII handler for DHCPv4.
Resource race avoidance RAII handler.
static bool subnetsIncludeMatchClientId(const Subnet4Ptr &first_subnet, const ClientClasses &client_classes)
Checks if the shared network includes a subnet with the match client ID flag set to true.
CalloutNextStep
Specifies allowed next steps.
@ NEXT_STEP_CONTINUE
continue normally
@ NEXT_STEP_SKIP
skip the next processing step
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static boost::shared_ptr< CalloutHandle > createCalloutHandle()
Return callout handle.
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
Wrapper class around callout handle which automatically resets handle's state.
Statistics Manager class.
static StatsMgr & instance()
Statistics Manager accessor method.
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
A template representing an optional value.
Definition optional.h:36
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:343
Utility class to measure code execution times.
Definition stopwatch.h:35
Write mutex RAII handler.
@ D6O_CLIENT_FQDN
Definition dhcp6.h:59
@ D6O_REMOTE_ID
Definition dhcp6.h:57
@ D6O_RELAY_ID
Definition dhcp6.h:73
@ DHCPV6_RENEW
Definition dhcp6.h:202
#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
#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:1420
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
Definition