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