Kea 3.3.1
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 if (in_subnet && !host->hasIPv6Reservation()) {
1244 ctx.subnet_ = subnet;
1245 ctx.host_subnet_ = subnet;
1246 if (!host->getHostname().empty()) {
1247 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1248 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1249 qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1250 static_cast<bool>(fqdn));
1251 }
1252
1253 return;
1254 }
1255
1256 // Get the IPv6 reservations of specified type.
1257 const IPv6ResrvRange& reservs = host->getIPv6Reservations(type);
1258 BOOST_FOREACH(auto const& type_lease_tuple, reservs) {
1259 // We do have a reservation for address or prefix.
1260 const IOAddress& addr = type_lease_tuple.second.getPrefix();
1261 uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1262
1263 // We have allocated this address/prefix while processing one of the
1264 // previous IAs, so let's try another reservation.
1265 if (ctx.isAllocated(addr, prefix_len)) {
1266 continue;
1267 }
1268
1269 // The out-of-pool flag indicates that no client should be assigned
1270 // reserved addresses from within the dynamic pool, and for that
1271 // reason look only for reservations that are outside the pools,
1272 // hence the inPool check.
1273 if (!in_subnet ||
1274 (subnet->getReservationsOutOfPool() &&
1275 subnet->inPool(ctx.currentIA().type_, addr))) {
1276 continue;
1277 }
1278
1279 // If there's a lease for this address, let's not create it.
1280 // It doesn't matter whether it is for this client or for someone else.
1281 if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1282 addr)) {
1283
1284 // Let's remember the subnet from which the reserved address has been
1285 // allocated. We'll use this subnet for allocating other reserved
1286 // resources.
1287 ctx.subnet_ = subnet;
1288
1289 if (!ctx.host_subnet_) {
1290 ctx.host_subnet_ = subnet;
1291 if (!host->getHostname().empty()) {
1292 // If there is a hostname reservation here we should stick
1293 // to this reservation. By updating the hostname in the
1294 // context we make sure that the database is updated with
1295 // this new value and the server doesn't need to do it and
1296 // its processing performance is not impacted by the hostname
1297 // updates.
1298
1299 // We have to determine whether the hostname is generated
1300 // in response to client's FQDN or not. If yes, we will
1301 // need to qualify the hostname. Otherwise, we just use
1302 // the hostname as it is specified for the reservation.
1303 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1304 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1305 qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1306 static_cast<bool>(fqdn));
1307 }
1308 }
1309
1310 // Ok, let's create a new lease...
1312 Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1313
1314 if (!lease) {
1315 continue;
1316 }
1317
1318 // ... and add it to the existing leases list.
1319 existing_leases.push_back(lease);
1320
1321 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1323 .arg(addr.toText())
1324 .arg(ctx.query_->getLabel());
1325 } else {
1327 .arg(addr.toText())
1328 .arg(static_cast<int>(prefix_len))
1329 .arg(ctx.query_->getLabel());
1330 }
1331
1332 // We found a lease for this client and this IA. Let's return.
1333 // Returning after the first lease was assigned is useful if we
1334 // have multiple reservations for the same client. If the client
1335 // sends 2 IAs, the first time we call allocateReservedLeases6 will
1336 // use the first reservation and return. The second time, we'll
1337 // go over the first reservation, but will discover that there's
1338 // a lease corresponding to it and will skip it and then pick
1339 // the second reservation and turn it into the lease. This approach
1340 // would work for any number of reservations.
1341 return;
1342 }
1343 }
1344 }
1345
1346 // Found no subnet reservations so now try the global reservation.
1347 allocateGlobalReservedLeases6(ctx, existing_leases);
1348}
1349
1350void
1351AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
1352 Lease6Collection& existing_leases) {
1353 // Get the global host
1354 ConstHostPtr ghost = ctx.globalHost();
1355 if (!ghost) {
1356 return;
1357 }
1358
1359 // We want to avoid allocating a new lease for an IA if there is already
1360 // a valid lease for which client has reservation. So, we first check if
1361 // we already have a lease for a reserved address or prefix.
1362 for (auto const& lease : existing_leases) {
1363 if ((lease->valid_lft_ != 0) &&
1364 (ghost->hasReservation(makeIPv6Resrv(*lease)))) {
1365 // We found existing lease for a reserved address or prefix.
1366 // We'll simply extend the lifetime of the lease.
1369 .arg(ctx.query_->getLabel())
1370 .arg(lease->typeToText(lease->type_))
1371 .arg(lease->addr_.toText());
1372
1373 // Besides IP reservations we're also going to return other reserved
1374 // parameters, such as hostname. We want to hand out the hostname value
1375 // from the same reservation entry as IP addresses. Thus, let's see if
1376 // there is any hostname reservation.
1377 if (!ghost->getHostname().empty()) {
1378 // We have to determine whether the hostname is generated
1379 // in response to client's FQDN or not. If yes, we will
1380 // need to qualify the hostname. Otherwise, we just use
1381 // the hostname as it is specified for the reservation.
1382 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1383 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1384 qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1385 static_cast<bool>(fqdn));
1386 }
1387
1388 // Got a lease for a reservation in this IA.
1389 return;
1390 }
1391 }
1392
1393 // There is no lease for a reservation in this IA. So, let's now iterate
1394 // over reservations specified and try to allocate one of them for the IA.
1395
1396 // Let's convert this from Lease::Type to IPv6Reserv::Type
1397 IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1399
1400 const IPv6ResrvRange& reservs = ghost->getIPv6Reservations(type);
1401 BOOST_FOREACH(auto const& type_lease_tuple, reservs) {
1402 // We do have a reservation for address or prefix.
1403 const IOAddress& addr = type_lease_tuple.second.getPrefix();
1404 uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1405
1406 // We have allocated this address/prefix while processing one of the
1407 // previous IAs, so let's try another reservation.
1408 if (ctx.isAllocated(addr, prefix_len)) {
1409 continue;
1410 }
1411
1412 // If there's a lease for this address, let's not create it.
1413 // It doesn't matter whether it is for this client or for someone else.
1414 if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
1415
1416 // Check the feasibility of this address within this shared-network.
1417 // Assign the context's subnet accordingly.
1418 // Only necessary for IA_NA
1419 if (type == IPv6Resrv::TYPE_NA) {
1420 bool valid_subnet = false;
1421 auto subnet = ctx.subnet_;
1422 while (subnet) {
1423 if (subnet->inRange(addr)) {
1424 valid_subnet = true;
1425 break;
1426 }
1427
1428 subnet = subnet->getNextSubnet(ctx.subnet_);
1429 }
1430
1431 if (!valid_subnet) {
1434 .arg(ctx.query_->getLabel())
1435 .arg(addr.toText())
1436 .arg(labelNetworkOrSubnet(ctx.subnet_));
1437 continue;
1438 }
1439
1440 ctx.subnet_ = subnet;
1441 }
1442
1443 if (!ghost->getHostname().empty()) {
1444 // If there is a hostname reservation here we should stick
1445 // to this reservation. By updating the hostname in the
1446 // context we make sure that the database is updated with
1447 // this new value and the server doesn't need to do it and
1448 // its processing performance is not impacted by the hostname
1449 // updates.
1450
1451 // We have to determine whether the hostname is generated
1452 // in response to client's FQDN or not. If yes, we will
1453 // need to qualify the hostname. Otherwise, we just use
1454 // the hostname as it is specified for the reservation.
1455 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1456 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1457 qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1458 static_cast<bool>(fqdn));
1459 }
1460
1461 // Ok, let's create a new lease...
1463 Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1464
1465 if (!lease) {
1466 continue;
1467 }
1468
1469 // ... and add it to the existing leases list.
1470 existing_leases.push_back(lease);
1471
1472 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1474 .arg(addr.toText())
1475 .arg(ctx.query_->getLabel());
1476 } else {
1478 .arg(addr.toText())
1479 .arg(static_cast<int>(prefix_len))
1480 .arg(ctx.query_->getLabel());
1481 }
1482
1483 // We found a lease for this client and this IA. Let's return.
1484 // Returning after the first lease was assigned is useful if we
1485 // have multiple reservations for the same client. If the client
1486 // sends 2 IAs, the first time we call allocateReservedLeases6 will
1487 // use the first reservation and return. The second time, we'll
1488 // go over the first reservation, but will discover that there's
1489 // a lease corresponding to it and will skip it and then pick
1490 // the second reservation and turn it into the lease. This approach
1491 // would work for any number of reservations.
1492 return;
1493 }
1494 }
1495}
1496
1497void
1498AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
1499 Lease6Collection& existing_leases) {
1500 // If there are no leases (so nothing to remove) just return.
1501 if (existing_leases.empty() || !ctx.subnet_) {
1502 return;
1503 }
1504 // If host reservation is disabled (so there are no reserved leases)
1505 // use the simplified version.
1506 if (!ctx.subnet_->getReservationsInSubnet() &&
1507 !ctx.subnet_->getReservationsGlobal()) {
1508 removeNonmatchingReservedNoHostLeases6(ctx, existing_leases);
1509 return;
1510 }
1511
1512 // We need a copy, so we won't be iterating over a container and
1513 // removing from it at the same time. It's only a copy of pointers,
1514 // so the operation shouldn't be that expensive.
1515 Lease6Collection copy = existing_leases;
1516
1517 for (auto const& candidate : copy) {
1518 // If we have reservation we should check if the reservation is for
1519 // the candidate lease. If so, we simply accept the lease.
1520 IPv6Resrv resv = makeIPv6Resrv(*candidate);
1521 if ((ctx.hasGlobalReservation(resv)) ||
1522 ((ctx.hosts_.count(candidate->subnet_id_) > 0) &&
1523 (ctx.hosts_[candidate->subnet_id_]->hasReservation(resv)))) {
1524 // We have a subnet reservation
1525 continue;
1526 }
1527
1528 // The candidate address doesn't appear to be reserved for us.
1529 // We have to make a bit more expensive operation here to retrieve
1530 // the reservation for the candidate lease and see if it is
1531 // reserved for someone else.
1532 auto hosts = getIPv6Resrv(ctx.subnet_->getID(), candidate->addr_);
1533 // If lease is not reserved to someone else, it means that it can
1534 // be allocated to us from a dynamic pool, but we must check if
1535 // this lease belongs to any pool. If it does, we can proceed to
1536 // checking the next lease.
1537 if (hosts.empty() && inAllowedPool(ctx, candidate->type_,
1538 candidate->addr_, false)) {
1539 continue;
1540 }
1541
1542 if (!hosts.empty()) {
1543 // Ok, we have a problem. This host has a lease that is reserved
1544 // for someone else. We need to recover from this.
1545 if (hosts.size() == 1) {
1546 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1548 .arg(ctx.query_->getLabel())
1549 .arg(candidate->addr_.toText())
1550 .arg(ctx.duid_->toText())
1551 .arg(hosts.front()->getIdentifierAsText());
1552 } else {
1554 .arg(ctx.query_->getLabel())
1555 .arg(candidate->addr_.toText())
1556 .arg(static_cast<int>(candidate->prefixlen_))
1557 .arg(ctx.duid_->toText())
1558 .arg(hosts.front()->getIdentifierAsText());
1559 }
1560 } else {
1561 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1563 .arg(ctx.query_->getLabel())
1564 .arg(candidate->addr_.toText())
1565 .arg(ctx.duid_->toText())
1566 .arg(hosts.size());
1567 } else {
1569 .arg(ctx.query_->getLabel())
1570 .arg(candidate->addr_.toText())
1571 .arg(static_cast<int>(candidate->prefixlen_))
1572 .arg(ctx.duid_->toText())
1573 .arg(hosts.size());
1574 }
1575 }
1576 }
1577
1578 // Remove this lease from LeaseMgr as it is reserved to someone
1579 // else or doesn't belong to a pool.
1580 if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1581 // Concurrent delete performed by other instance which should
1582 // properly handle dns and stats updates.
1583 continue;
1584 }
1585
1586 // Update DNS if needed.
1587 queueNCR(CHG_REMOVE, candidate);
1588
1589 // Need to decrease statistic for assigned addresses.
1590 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1591 "assigned-nas" : "assigned-pds",
1592 static_cast<int64_t>(-1));
1593
1595 StatsMgr::generateName("subnet", candidate->subnet_id_,
1596 ctx.currentIA().type_ == Lease::TYPE_NA ?
1597 "assigned-nas" : "assigned-pds"),
1598 static_cast<int64_t>(-1));
1599
1600 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(candidate->subnet_id_);
1601 if (subnet) {
1602 auto const& pool = subnet->getPool(ctx.currentIA().type_, candidate->addr_, false);
1603 if (pool) {
1605 StatsMgr::generateName("subnet", subnet->getID(),
1606 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
1607 "pool" : "pd-pool", pool->getID(),
1608 ctx.currentIA().type_ == Lease::TYPE_NA ?
1609 "assigned-nas" : "assigned-pds")),
1610 static_cast<int64_t>(-1));
1611 }
1612 }
1613
1614 // In principle, we could trigger a hook here, but we will do this
1615 // only if we get serious complaints from actual users. We want the
1616 // conflict resolution procedure to really work and user libraries
1617 // should not interfere with it.
1618
1619 // Add this to the list of removed leases.
1620 ctx.currentIA().old_leases_.push_back(candidate);
1621
1622 // Let's remove this candidate from existing leases
1623 removeLeases(existing_leases, candidate->addr_);
1624 }
1625}
1626
1627void
1628AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
1629 Lease6Collection& existing_leases) {
1630 // We need a copy, so we won't be iterating over a container and
1631 // removing from it at the same time. It's only a copy of pointers,
1632 // so the operation shouldn't be that expensive.
1633 Lease6Collection copy = existing_leases;
1634
1635 for (auto const& candidate : copy) {
1636 // Lease can be allocated to us from a dynamic pool, but we must
1637 // check if this lease belongs to any allowed pool. If it does,
1638 // we can proceed to checking the next lease.
1639 if (inAllowedPool(ctx, candidate->type_,
1640 candidate->addr_, false)) {
1641 continue;
1642 }
1643
1644 // Remove this lease from LeaseMgr as it doesn't belong to a pool.
1645 if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1646 // Concurrent delete performed by other instance which should
1647 // properly handle dns and stats updates.
1648 continue;
1649 }
1650
1651 // Update DNS if needed.
1652 queueNCR(CHG_REMOVE, candidate);
1653
1654 // Need to decrease statistic for assigned addresses.
1655 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1656 "assigned-nas" : "assigned-pds",
1657 static_cast<int64_t>(-1));
1658
1660 StatsMgr::generateName("subnet", candidate->subnet_id_,
1661 ctx.currentIA().type_ == Lease::TYPE_NA ?
1662 "assigned-nas" : "assigned-pds"),
1663 static_cast<int64_t>(-1));
1664
1665 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(candidate->subnet_id_);
1666 if (subnet) {
1667 auto const& pool = subnet->getPool(candidate->type_, candidate->addr_, false);
1668 if (pool) {
1670 StatsMgr::generateName("subnet", subnet->getID(),
1671 StatsMgr::generateName(candidate->type_ == Lease::TYPE_NA ?
1672 "pool" : "pd-pool", pool->getID(),
1673 candidate->type_ == Lease::TYPE_NA ?
1674 "assigned-nas" : "assigned-pds")),
1675 static_cast<int64_t>(-1));
1676 }
1677 }
1678
1679 // Add this to the list of removed leases.
1680 ctx.currentIA().old_leases_.push_back(candidate);
1681
1682 // Let's remove this candidate from existing leases
1683 removeLeases(existing_leases, candidate->addr_);
1684 }
1685}
1686
1687bool
1688AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
1689
1690 bool removed = false;
1691 for (Lease6Collection::iterator lease = container.begin();
1692 lease != container.end(); ++lease) {
1693 if ((*lease)->addr_ == addr) {
1694 lease->reset();
1695 removed = true;
1696 }
1697 }
1698
1699 // Remove all elements that have NULL value
1700 container.erase(std::remove(container.begin(), container.end(), Lease6Ptr()),
1701 container.end());
1702
1703 return (removed);
1704}
1705
1706void
1707AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
1708 Lease6Collection& existing_leases) {
1709 // This method removes leases that are not reserved for this host.
1710 // It will keep at least one lease, though, as a fallback.
1711 int total = existing_leases.size();
1712 if (total <= 1) {
1713 return;
1714 }
1715
1716 // This is officially not scary code anymore. iterates and marks specified
1717 // leases for deletion, by setting appropriate pointers to NULL.
1718 for (Lease6Collection::iterator lease = existing_leases.begin();
1719 lease != existing_leases.end(); ++lease) {
1720
1721 // If there is reservation for this keep it.
1722 IPv6Resrv resv = makeIPv6Resrv(**lease);
1723 if (ctx.hasGlobalReservation(resv) ||
1724 ((ctx.hosts_.count((*lease)->subnet_id_) > 0) &&
1725 (ctx.hosts_[(*lease)->subnet_id_]->hasReservation(resv)))) {
1726 continue;
1727 }
1728
1729 // @todo - If this is for a fake_allocation, we should probably
1730 // not be deleting the lease or removing DNS entries. We should
1731 // simply remove it from the list.
1732 // We have reservations, but not for this lease. Release it.
1733 // Remove this lease from LeaseMgr
1734 if (!LeaseMgrFactory::instance().deleteLease(*lease)) {
1735 // Concurrent delete performed by other instance which should
1736 // properly handle dns and stats updates.
1737 continue;
1738 }
1739
1740 // Update DNS if required.
1741 queueNCR(CHG_REMOVE, *lease);
1742
1743 // Need to decrease statistic for assigned addresses.
1744 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1745 "assigned-nas" : "assigned-pds",
1746 static_cast<int64_t>(-1));
1747
1749 StatsMgr::generateName("subnet", (*lease)->subnet_id_,
1750 ctx.currentIA().type_ == Lease::TYPE_NA ?
1751 "assigned-nas" : "assigned-pds"),
1752 static_cast<int64_t>(-1));
1753
1754 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId((*lease)->subnet_id_);
1755 if (subnet) {
1756 auto const& pool = subnet->getPool(ctx.currentIA().type_, (*lease)->addr_, false);
1757 if (pool) {
1759 StatsMgr::generateName("subnet", subnet->getID(),
1760 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
1761 "pool" : "pd-pool", pool->getID(),
1762 ctx.currentIA().type_ == Lease::TYPE_NA ?
1763 "assigned-nas" : "assigned-pds")),
1764 static_cast<int64_t>(-1));
1765 }
1766 }
1767
1769
1770 // Add this to the list of removed leases.
1771 ctx.currentIA().old_leases_.push_back(*lease);
1772
1773 // Set this pointer to NULL. The pointer is still valid. We're just
1774 // setting the Lease6Ptr to NULL value. We'll remove all NULL
1775 // pointers once the loop is finished.
1776 lease->reset();
1777
1778 if (--total == 1) {
1779 // If there's only one lease left, break the loop.
1780 break;
1781 }
1782 }
1783
1784 // Remove all elements that we previously marked for deletion (those that
1785 // have NULL value).
1786 existing_leases.erase(std::remove(existing_leases.begin(),
1787 existing_leases.end(), Lease6Ptr()), existing_leases.end());
1788}
1789
1790namespace {
1791bool
1792useMinLifetimes6(AllocEngine::ClientContext6& ctx, const IOAddress& addr,
1793 uint8_t prefix_len) {
1794 auto const& threshold = ctx.subnet_->getAdaptiveLeaseTimeThreshold();
1795 if (!threshold.unspecified() && (threshold < 1.0)) {
1796 auto const& occupancy = ctx.subnet_->getAllocator(Lease::TYPE_PD)->
1797 getOccupancyRate(addr, prefix_len, ctx.query_->getClasses());
1798 if (occupancy >= threshold) {
1799 return (true);
1800 }
1801 }
1802 return (false);
1803}
1804} // end of anonymous namespace.
1805
1807AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
1808 uint8_t prefix_len,
1809 CalloutHandle::CalloutNextStep& callout_status) {
1810
1811 if (!expired->expired()) {
1812 isc_throw(BadValue, "Attempt to recycle lease that is still valid");
1813 }
1814
1815 if (expired->state_ == Lease::STATE_REGISTERED) {
1816 isc_throw(BadValue, "Attempt to recycle registered address");
1817 }
1818
1819 if (expired->type_ != Lease::TYPE_PD) {
1820 prefix_len = 128; // non-PD lease types must be always /128
1821 }
1822
1823 if (!ctx.fake_allocation_) {
1824 // The expired lease needs to be reclaimed before it can be reused.
1825 // This includes declined leases for which probation period has
1826 // elapsed.
1827 reclaimExpiredLease(expired, ctx.callout_handle_);
1828 }
1829
1830 // address, lease type and prefixlen (0) stay the same
1831 expired->iaid_ = ctx.currentIA().iaid_;
1832 expired->duid_ = ctx.duid_;
1833 expired->hwaddr_ = ctx.hwaddr_;
1834
1835 // Calculate life times.
1836 expired->preferred_lft_ = 0;
1837 expired->valid_lft_ = 0;
1838 if ((expired->type_ == Lease::TYPE_PD) &&
1839 useMinLifetimes6(ctx, expired->addr_, prefix_len)) {
1840 getMinLifetimes6(ctx, expired->preferred_lft_, expired->valid_lft_);
1841 } else {
1842 getLifetimes6(ctx, expired->preferred_lft_, expired->valid_lft_);
1843 }
1844 expired->reuseable_valid_lft_ = 0;
1845
1846 expired->cltt_ = time(0);
1847 expired->subnet_id_ = ctx.subnet_->getID();
1848 expired->hostname_ = ctx.hostname_;
1849 expired->fqdn_fwd_ = ctx.fwd_dns_update_;
1850 expired->fqdn_rev_ = ctx.rev_dns_update_;
1851 expired->prefixlen_ = prefix_len;
1852 expired->state_ = Lease::STATE_DEFAULT;
1853
1856 .arg(ctx.query_->getLabel())
1857 .arg(expired->toText());
1858
1859 // Let's execute all callouts registered for lease6_select
1860 if (ctx.callout_handle_ &&
1861 HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1862
1863 // Use the RAII wrapper to make sure that the callout handle state is
1864 // reset when this object goes out of scope. All hook points must do
1865 // it to prevent possible circular dependency between the callout
1866 // handle and its arguments.
1867 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1868
1869 // Enable copying options from the packet within hook library.
1870 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
1871
1872 // Pass necessary arguments
1873
1874 // Pass the original packet
1875 ctx.callout_handle_->setArgument("query6", ctx.query_);
1876
1877 // Subnet from which we do the allocation
1878 ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
1879
1880 // Is this solicit (fake = true) or request (fake = false)
1881 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
1882
1883 // The lease that will be assigned to a client
1884 ctx.callout_handle_->setArgument("lease6", expired);
1885
1886 // Call the callouts
1887 HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
1888
1889 callout_status = ctx.callout_handle_->getStatus();
1890
1891 // Callouts decided to skip the action. This means that the lease is not
1892 // assigned, so the client will get NoAddrAvail as a result. The lease
1893 // won't be inserted into the database.
1894 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
1896 return (Lease6Ptr());
1897 }
1898
1903
1904 // Let's use whatever callout returned. Hopefully it is the same lease
1905 // we handed to it.
1906 ctx.callout_handle_->getArgument("lease6", expired);
1907 }
1908
1909 if (!ctx.fake_allocation_) {
1910 // Add (update) the extended information on the lease.
1911 updateLease6ExtendedInfo(expired, ctx);
1912
1913 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, expired->addr_, false);
1914 if (pool) {
1915 expired->pool_id_ = pool->getID();
1916 }
1917
1918 // for REQUEST we do update the lease
1920
1921 // If the lease is in the current subnet we need to account
1922 // for the re-assignment of The lease.
1923 if (ctx.subnet_->inPool(ctx.currentIA().type_, expired->addr_)) {
1925 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1926 ctx.currentIA().type_ == Lease::TYPE_NA ?
1927 "assigned-nas" : "assigned-pds"),
1928 static_cast<int64_t>(1));
1929
1931 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1932 ctx.currentIA().type_ == Lease::TYPE_NA ?
1933 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
1934 static_cast<int64_t>(1));
1935
1936 if (pool) {
1938 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1939 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
1940 "pool" : "pd-pool", pool->getID(),
1941 ctx.currentIA().type_ == Lease::TYPE_NA ?
1942 "assigned-nas" : "assigned-pds")),
1943 static_cast<int64_t>(1));
1944
1946 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1947 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
1948 "pool" : "pd-pool", pool->getID(),
1949 ctx.currentIA().type_ == Lease::TYPE_NA ?
1950 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
1951 static_cast<int64_t>(1));
1952 }
1953
1954 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1955 "assigned-nas" : "assigned-pds",
1956 static_cast<int64_t>(1));
1957
1958 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1959 "cumulative-assigned-nas" : "cumulative-assigned-pds",
1960 static_cast<int64_t>(1));
1961 }
1962 }
1963
1964 // We do nothing for SOLICIT. We'll just update database when
1965 // the client gets back to us with REQUEST message.
1966
1967 // it's not really expired at this stage anymore - let's return it as
1968 // an updated lease
1969 return (expired);
1970}
1971
1972namespace {
1973void sanitizeLifetimes6(AllocEngine::ClientContext6& ctx,
1974 uint32_t& preferred, uint32_t& valid) {
1975 // If preferred isn't set or insane, calculate it as valid_lft * 0.625.
1976 if (!preferred || preferred > valid) {
1977 preferred = ((valid * 5)/8);
1980 .arg(ctx.query_->getLabel())
1981 .arg(preferred);
1982 }
1983}
1984} // end of anonymous namespace.
1985
1986void
1987AllocEngine::getLifetimes6(ClientContext6& ctx, uint32_t& preferred, uint32_t& valid) {
1988 // If the triplets are specified in one of our classes use it.
1989 // We use the first one we find for each lifetime.
1990 Triplet<uint32_t> candidate_preferred;
1991 Triplet<uint32_t> candidate_valid;
1992 const ClientClasses classes = ctx.query_->getClasses();
1993 if (!classes.empty()) {
1994 // Let's get class definitions
1995 const ClientClassDictionaryPtr& dict =
1996 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
1997
1998 // Iterate over the assigned class definitions.
1999 int have_both = 0;
2000 for (auto const& name : classes) {
2001 ClientClassDefPtr cl = dict->findClass(name);
2002 if (candidate_preferred.unspecified() &&
2003 (cl && (!cl->getPreferred().unspecified()))) {
2004 candidate_preferred = cl->getPreferred();
2005 ++have_both;
2006 }
2007
2008 if (candidate_valid.unspecified() &&
2009 (cl && (!cl->getValid().unspecified()))) {
2010 candidate_valid = cl->getValid();
2011 ++have_both;
2012 }
2013 if (have_both == 2) {
2014 break;
2015 }
2016 }
2017 }
2018
2019 // If no classes specified preferred lifetime, get it from the subnet.
2020 if (!candidate_preferred) {
2021 candidate_preferred = ctx.subnet_->getPreferred();
2022 }
2023
2024 // If no classes specified valid lifetime, get it from the subnet.
2025 if (!candidate_valid) {
2026 candidate_valid = ctx.subnet_->getValid();
2027 }
2028
2029 // Set the outbound parameters to the values we have so far.
2030 preferred = candidate_preferred;
2031 valid = candidate_valid;
2032
2033 // If client requested either value, use the requested value(s) bounded by
2034 // the candidate triplet(s).
2035 if (!ctx.currentIA().hints_.empty()) {
2036 if (ctx.currentIA().hints_[0].getPreferred()) {
2037 preferred = candidate_preferred.get(ctx.currentIA().hints_[0].getPreferred());
2038 }
2039
2040 if (ctx.currentIA().hints_[0].getValid()) {
2041 valid = candidate_valid.get(ctx.currentIA().hints_[0].getValid());
2042 }
2043 }
2044
2045 sanitizeLifetimes6(ctx, preferred, valid);
2046}
2047
2048void
2050 uint32_t& valid) {
2051 // If the triplets are specified in one of our classes use it.
2052 // We use the first one we find for each lifetime.
2053 Triplet<uint32_t> candidate_preferred;
2054 Triplet<uint32_t> candidate_valid;
2055 const ClientClasses classes = ctx.query_->getClasses();
2056 if (!classes.empty()) {
2057 // Let's get class definitions
2058 const ClientClassDictionaryPtr& dict =
2059 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
2060
2061 // Iterate over the assigned class definitions.
2062 int have_both = 0;
2063 for (auto const& name : classes) {
2064 ClientClassDefPtr cl = dict->findClass(name);
2065 if (candidate_preferred.unspecified() &&
2066 (cl && (!cl->getPreferred().unspecified()))) {
2067 candidate_preferred = cl->getPreferred();
2068 ++have_both;
2069 }
2070
2071 if (candidate_valid.unspecified() &&
2072 (cl && (!cl->getValid().unspecified()))) {
2073 candidate_valid = cl->getValid();
2074 ++have_both;
2075 }
2076 if (have_both == 2) {
2077 break;
2078 }
2079 }
2080 }
2081
2082 // If no classes specified preferred lifetime, get it from the subnet.
2083 if (!candidate_preferred) {
2084 candidate_preferred = ctx.subnet_->getPreferred();
2085 }
2086
2087 // If no classes specified valid lifetime, get it from the subnet.
2088 if (!candidate_valid) {
2089 candidate_valid = ctx.subnet_->getValid();
2090 }
2091
2092 // Save remaining values.
2093 uint32_t remain_preferred(preferred);
2094 uint32_t remain_valid(valid);
2095
2096 // Set the outbound parameters to the minimal values.
2097 preferred = candidate_preferred.getMin();
2098 valid = candidate_valid.getMin();
2099
2100 // Return at least the remaining values.
2101 if (remain_preferred > preferred) {
2102 preferred = remain_preferred;
2103 }
2104 if (remain_valid > valid) {
2105 valid = remain_valid;
2106 }
2107
2108 sanitizeLifetimes6(ctx, preferred, valid);
2109}
2110
2111Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
2112 const IOAddress& addr,
2113 uint8_t prefix_len,
2114 CalloutHandle::CalloutNextStep& callout_status) {
2115
2116 if (ctx.currentIA().type_ != Lease::TYPE_PD) {
2117 prefix_len = 128; // non-PD lease types must be always /128
2118 }
2119
2120 uint32_t preferred = 0;
2121 uint32_t valid = 0;
2122 if ((ctx.currentIA().type_ == Lease::TYPE_PD) &&
2123 useMinLifetimes6(ctx, addr, prefix_len)) {
2124 getMinLifetimes6(ctx, preferred, valid);
2125 } else {
2126 getLifetimes6(ctx, preferred, valid);
2127 }
2128
2129 Lease6Ptr lease(new Lease6(ctx.currentIA().type_, addr, ctx.duid_,
2130 ctx.currentIA().iaid_, preferred,
2131 valid, ctx.subnet_->getID(),
2132 ctx.hwaddr_, prefix_len));
2133
2134 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2135 lease->fqdn_rev_ = ctx.rev_dns_update_;
2136 lease->hostname_ = ctx.hostname_;
2137
2138 // Let's execute all callouts registered for lease6_select
2139 if (ctx.callout_handle_ &&
2140 HooksManager::calloutsPresent(hook_index_lease6_select_)) {
2141
2142 // Use the RAII wrapper to make sure that the callout handle state is
2143 // reset when this object goes out of scope. All hook points must do
2144 // it to prevent possible circular dependency between the callout
2145 // handle and its arguments.
2146 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
2147
2148 // Enable copying options from the packet within hook library.
2149 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2150
2151 // Pass necessary arguments
2152
2153 // Pass the original packet
2154 ctx.callout_handle_->setArgument("query6", ctx.query_);
2155
2156 // Subnet from which we do the allocation
2157 ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
2158
2159 // Is this solicit (fake = true) or request (fake = false)
2160 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
2161
2162 // The lease that will be assigned to a client
2163 ctx.callout_handle_->setArgument("lease6", lease);
2164
2165 // This is the first callout, so no need to clear any arguments
2166 HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
2167
2168 callout_status = ctx.callout_handle_->getStatus();
2169
2170 // Callouts decided to skip the action. This means that the lease is not
2171 // assigned, so the client will get NoAddrAvail as a result. The lease
2172 // won't be inserted into the database.
2173 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
2175 return (Lease6Ptr());
2176 }
2177
2178 // Let's use whatever callout returned. Hopefully it is the same lease
2179 // we handed to it.
2180 ctx.callout_handle_->getArgument("lease6", lease);
2181 }
2182
2183 if (!ctx.fake_allocation_) {
2184 // Add (update) the extended information on the lease.
2185 updateLease6ExtendedInfo(lease, ctx);
2186
2187 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2188 if (pool) {
2189 lease->pool_id_ = pool->getID();
2190 }
2191
2192 // That is a real (REQUEST) allocation
2193 bool status = LeaseMgrFactory::instance().addLease(lease);
2194
2195 if (status) {
2196 // The lease insertion succeeded - if the lease is in the
2197 // current subnet lets bump up the statistic.
2198 if (ctx.subnet_->inPool(ctx.currentIA().type_, addr)) {
2200 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2201 ctx.currentIA().type_ == Lease::TYPE_NA ?
2202 "assigned-nas" : "assigned-pds"),
2203 static_cast<int64_t>(1));
2204
2206 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2207 ctx.currentIA().type_ == Lease::TYPE_NA ?
2208 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2209 static_cast<int64_t>(1));
2210
2211 if (pool) {
2213 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2214 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2215 "pool" : "pd-pool", pool->getID(),
2216 ctx.currentIA().type_ == Lease::TYPE_NA ?
2217 "assigned-nas" : "assigned-pds")),
2218 static_cast<int64_t>(1));
2219
2221 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2222 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2223 "pool" : "pd-pool", pool->getID(),
2224 ctx.currentIA().type_ == Lease::TYPE_NA ?
2225 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2226 static_cast<int64_t>(1));
2227 }
2228
2229 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2230 "assigned-nas" : "assigned-pds",
2231 static_cast<int64_t>(1));
2232
2233 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2234 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2235 static_cast<int64_t>(1));
2236 }
2237
2238 // Record it so it won't be updated twice.
2239 ctx.currentIA().addNewResource(addr, prefix_len);
2240
2241 return (lease);
2242 } else {
2243 // One of many failures with LeaseMgr (e.g. lost connection to the
2244 // database, database failed etc.). One notable case for that
2245 // is that we are working in multi-process mode and we lost a race
2246 // (some other process got that address first)
2247 return (Lease6Ptr());
2248 }
2249 } else {
2250 // That is only fake (SOLICIT without rapid-commit) allocation
2251
2252 // It is for advertise only. We should not insert the lease and callers
2253 // have already verified the lease does not exist in the database.
2254 return (lease);
2255 }
2256}
2257
2258void
2259AllocEngine::getRemaining(const Lease6Ptr& lease, uint32_t& preferred,
2260 uint32_t& valid) {
2261 valid = 0;
2262 preferred = 0;
2263 if (!lease || (lease->state_ != Lease::STATE_DEFAULT)) {
2264 return;
2265 }
2266 time_t now = time(0);
2267 // Refuse time not going forward.
2268 if (lease->cltt_ > now) {
2269 return;
2270 }
2271 uint32_t age = now - lease->cltt_;
2272 // Already expired.
2273 if (age >= lease->valid_lft_) {
2274 return;
2275 }
2276 valid = lease->valid_lft_ - age;
2277 if (age >= lease->preferred_lft_) {
2278 return;
2279 }
2280 preferred = lease->preferred_lft_ - age;
2281}
2282
2285 try {
2286 if (!ctx.subnet_) {
2287 isc_throw(InvalidOperation, "Subnet is required for allocation");
2288 }
2289
2290 if (!ctx.duid_) {
2291 isc_throw(InvalidOperation, "DUID is mandatory for allocation");
2292 }
2293
2294 // Check if there are any leases for this client.
2295 ConstSubnet6Ptr subnet = ctx.subnet_;
2296 Lease6Collection leases;
2297 while (subnet) {
2298 Lease6Collection leases_subnet =
2300 *ctx.duid_,
2301 ctx.currentIA().iaid_,
2302 subnet->getID());
2303 for (auto const& l : leases_subnet) {
2304 if (l->state_ != Lease::STATE_REGISTERED) {
2305 leases.push_back(l);
2306 }
2307 }
2308 subnet = subnet->getNextSubnet(ctx.subnet_);
2309 }
2310
2311 if (!leases.empty()) {
2314 .arg(ctx.query_->getLabel());
2315
2316 // Check if the existing leases are reserved for someone else.
2317 // If they're not, we're ok to keep using them.
2318 removeNonmatchingReservedLeases6(ctx, leases);
2319 }
2320
2321 if (!ctx.hosts_.empty()) {
2322
2325 .arg(ctx.query_->getLabel());
2326
2327 // If we have host reservation, allocate those leases.
2328 allocateReservedLeases6(ctx, leases);
2329
2330 // There's one more check to do. Let's remove leases that are not
2331 // matching reservations, i.e. if client X has address A, but there's
2332 // a reservation for address B, we should release A and reassign B.
2333 // Caveat: do this only if we have at least one reserved address.
2334 removeNonreservedLeases6(ctx, leases);
2335 }
2336
2337 // If we happen to removed all leases, get something new for this guy.
2338 // Depending on the configuration, we may enable or disable granting
2339 // new leases during renewals. This is controlled with the
2340 // allow_new_leases_in_renewals_ field.
2341 if (leases.empty()) {
2342
2345 .arg(ctx.query_->getLabel());
2346
2347 leases = allocateUnreservedLeases6(ctx);
2348 }
2349
2350 // Extend all existing leases that passed all checks.
2351 for (auto const& l : leases) {
2352 if (ctx.currentIA().isNewResource(l->addr_,
2353 l->prefixlen_)) {
2354 // This lease was just created so is already extended.
2355 continue;
2356 }
2359 .arg(ctx.query_->getLabel())
2360 .arg(l->typeToText(l->type_))
2361 .arg(l->addr_);
2362 extendLease6(ctx, l);
2363 }
2364
2365 if (!leases.empty()) {
2366 // If there are any leases allocated, let's store in them in the
2367 // IA context so as they are available when we process subsequent
2368 // IAs.
2369 for (auto const& lease : leases) {
2370 ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
2371 ctx.new_leases_.push_back(lease);
2372 }
2373 }
2374
2375 return (leases);
2376
2377 } catch (const NoSuchLease& e) {
2378 isc_throw(NoSuchLease, "detected data race in AllocEngine::renewLeases6: " << e.what());
2379
2380 } catch (const isc::Exception& e) {
2381
2382 // Some other error, return an empty lease.
2384 .arg(ctx.query_->getLabel())
2385 .arg(e.what());
2386 }
2387
2388 return (Lease6Collection());
2389}
2390
2391void
2392AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
2393
2394 if (!lease || !ctx.subnet_) {
2395 return;
2396 }
2397
2398 // It is likely that the lease for which we're extending the lifetime doesn't
2399 // belong to the current but a sibling subnet.
2400 if (ctx.subnet_->getID() != lease->subnet_id_) {
2401 SharedNetwork6Ptr network;
2402 ctx.subnet_->getSharedNetwork(network);
2403 if (network) {
2404 ConstSubnet6Ptr subnet =
2405 network->getSubnet(SubnetID(lease->subnet_id_));
2406 // Found the actual subnet this lease belongs to. Stick to this
2407 // subnet.
2408 if (subnet) {
2409 ctx.subnet_ = subnet;
2410 }
2411 }
2412 }
2413
2414 // If the lease is not global and it is either out of range (NAs only) or it
2415 // is not permitted by subnet client classification, delete it.
2416 if (!(ctx.hasGlobalReservation(makeIPv6Resrv(*lease))) &&
2417 (((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
2418 !ctx.subnet_->clientSupported(ctx.query_->getClasses()))) {
2419 // Oh dear, the lease is no longer valid. We need to get rid of it.
2420
2421 // Remove this lease from LeaseMgr
2422 if (!LeaseMgrFactory::instance().deleteLease(lease)) {
2423 // Concurrent delete performed by other instance which should
2424 // properly handle dns and stats updates.
2425 return;
2426 }
2427
2428 // Updated DNS if required.
2429 queueNCR(CHG_REMOVE, lease);
2430
2431 // Need to decrease statistic for assigned addresses.
2432 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2433 "assigned-nas" : "assigned-pds", static_cast<int64_t>(-1));
2434
2436 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2437 ctx.currentIA().type_ == Lease::TYPE_NA ?
2438 "assigned-nas" : "assigned-pds"),
2439 static_cast<int64_t>(-1));
2440
2441 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2442 if (pool) {
2444 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2445 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2446 "pool" : "pd-pool", pool->getID(),
2447 ctx.currentIA().type_ == Lease::TYPE_NA ?
2448 "assigned-nas" : "assigned-pds")),
2449 static_cast<int64_t>(-1));
2450 }
2451
2452 // Add it to the removed leases list.
2453 ctx.currentIA().old_leases_.push_back(lease);
2454
2455 return;
2456 }
2457
2460 .arg(ctx.query_->getLabel())
2461 .arg(lease->toText());
2462
2463 // Keep the old data in case the callout tells us to skip update.
2464 Lease6Ptr old_data(new Lease6(*lease));
2465
2466 bool changed = false;
2467
2468 // Calculate life times.
2469 uint32_t current_preferred_lft = lease->preferred_lft_;
2470 if ((lease->type_ == Lease::TYPE_PD) &&
2471 useMinLifetimes6(ctx, lease->addr_, lease->prefixlen_)) {
2472 uint32_t remain_preferred_lft(0);
2473 uint32_t remain_valid_lft(0);
2474 getRemaining(lease, remain_preferred_lft, remain_valid_lft);
2475 lease->preferred_lft_ = remain_preferred_lft;
2476 lease->valid_lft_ = remain_valid_lft;
2477 getMinLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2478 } else {
2479 getLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2480 }
2481
2482 // If either has changed set the changed flag.
2483 if ((lease->preferred_lft_ != current_preferred_lft) ||
2484 (lease->valid_lft_ != lease->current_valid_lft_)) {
2485 changed = true;
2486 }
2487
2488 lease->cltt_ = time(NULL);
2489 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2490 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
2491 (lease->hostname_ != ctx.hostname_)) {
2492 changed = true;
2493 lease->hostname_ = ctx.hostname_;
2494 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2495 lease->fqdn_rev_ = ctx.rev_dns_update_;
2496 }
2497 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
2498 (ctx.hwaddr_ &&
2499 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
2500 changed = true;
2501 lease->hwaddr_ = ctx.hwaddr_;
2502 }
2503 if (lease->state_ != Lease::STATE_DEFAULT) {
2504 changed = true;
2505 lease->state_ = Lease::STATE_DEFAULT;
2506 }
2509 .arg(ctx.query_->getLabel())
2510 .arg(lease->toText());
2511
2512 bool skip = false;
2513 // Get the callouts specific for the processed message and execute them.
2514 int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
2515 Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
2516 if (HooksManager::calloutsPresent(hook_point)) {
2517 CalloutHandlePtr callout_handle = ctx.callout_handle_;
2518
2519 // Use the RAII wrapper to make sure that the callout handle state is
2520 // reset when this object goes out of scope. All hook points must do
2521 // it to prevent possible circular dependency between the callout
2522 // handle and its arguments.
2523 ScopedCalloutHandleState callout_handle_state(callout_handle);
2524
2525 // Enable copying options from the packet within hook library.
2526 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2527
2528 // Pass the original packet
2529 callout_handle->setArgument("query6", ctx.query_);
2530
2531 // Pass the lease to be updated
2532 callout_handle->setArgument("lease6", lease);
2533
2534 // Pass the IA option to be sent in response
2535 if (lease->type_ == Lease::TYPE_NA) {
2536 callout_handle->setArgument("ia_na", ctx.currentIA().ia_rsp_);
2537 } else {
2538 callout_handle->setArgument("ia_pd", ctx.currentIA().ia_rsp_);
2539 }
2540
2541 // Call all installed callouts
2542 HooksManager::callCallouts(hook_point, *callout_handle);
2543
2544 // Callouts decided to skip the next processing step. The next
2545 // processing step would actually renew the lease, so skip at this
2546 // stage means "keep the old lease as it is".
2547 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2548 skip = true;
2551 .arg(ctx.query_->getName());
2552 }
2553
2555 }
2556
2557 if (!skip) {
2558 bool update_stats = false;
2559
2560 // If the lease we're renewing has expired, we need to reclaim this
2561 // lease before we can renew it.
2562 if (old_data->expired()) {
2563 reclaimExpiredLease(old_data, ctx.callout_handle_);
2564
2565 // If the lease is in the current subnet we need to account
2566 // for the re-assignment of the lease.
2567 if (ctx.subnet_->inPool(ctx.currentIA().type_, old_data->addr_)) {
2568 update_stats = true;
2569 }
2570 changed = true;
2571 }
2572
2573 // @todo should we call storeLease6ExtendedInfo() here ?
2574 updateLease6ExtendedInfo(lease, ctx);
2575 if (lease->extended_info_action_ == Lease6::ACTION_UPDATE) {
2576 changed = true;
2577 }
2578
2579 // Try to reuse the lease.
2580 if (!changed) {
2581 setLeaseReusable(lease, current_preferred_lft, ctx);
2582 }
2583
2584 // Now that the lease has been reclaimed, we can go ahead and update it
2585 // in the lease database.
2586 if (lease->reuseable_valid_lft_ == 0) {
2587 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2588 if (pool) {
2589 lease->pool_id_ = pool->getID();
2590 }
2592 } else {
2593 // Server looks at changed_leases_ (i.e. old_data) when deciding
2594 // on DNS updates etc.
2595 old_data->reuseable_valid_lft_ = lease->reuseable_valid_lft_;
2596 }
2597
2598 if (update_stats) {
2600 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2601 ctx.currentIA().type_ == Lease::TYPE_NA ?
2602 "assigned-nas" : "assigned-pds"),
2603 static_cast<int64_t>(1));
2604
2606 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2607 ctx.currentIA().type_ == Lease::TYPE_NA ?
2608 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2609 static_cast<int64_t>(1));
2610
2611 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2612 if (pool) {
2614 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2615 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2616 "pool" : "pd-pool", pool->getID(),
2617 ctx.currentIA().type_ == Lease::TYPE_NA ?
2618 "assigned-nas" : "assigned-pds")),
2619 static_cast<int64_t>(1));
2620
2622 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2623 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2624 "pool" : "pd-pool", pool->getID(),
2625 ctx.currentIA().type_ == Lease::TYPE_NA ?
2626 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2627 static_cast<int64_t>(1));
2628 }
2629
2630 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2631 "assigned-nas" : "assigned-pds",
2632 static_cast<int64_t>(1));
2633
2634
2635 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2636 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2637 static_cast<int64_t>(1));
2638 }
2639
2640 } else {
2641 // Copy back the original date to the lease. For MySQL it doesn't make
2642 // much sense, but for memfile, the Lease6Ptr points to the actual lease
2643 // in memfile, so the actual update is performed when we manipulate
2644 // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
2645 *lease = *old_data;
2646 }
2647
2648 // Add the old lease to the changed lease list. This allows the server
2649 // to make decisions regarding DNS updates.
2650 ctx.currentIA().changed_leases_.push_back(old_data);
2651}
2652
2654AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
2655 Lease6Collection updated_leases;
2656 for (auto const& lease_it : leases) {
2657 Lease6Ptr lease(new Lease6(*lease_it));
2658 if (ctx.currentIA().isNewResource(lease->addr_, lease->prefixlen_)) {
2659 // This lease was just created so is already up to date.
2660 updated_leases.push_back(lease);
2661 continue;
2662 }
2663
2664 lease->reuseable_valid_lft_ = 0;
2665 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2666 lease->fqdn_rev_ = ctx.rev_dns_update_;
2667 lease->hostname_ = ctx.hostname_;
2668 uint32_t current_preferred_lft = lease->preferred_lft_;
2669 if (lease->valid_lft_ == 0) {
2670 // The lease was expired by a release: reset zero lifetimes.
2671 lease->preferred_lft_ = 0;
2672 if ((lease->type_ == Lease::TYPE_PD) &&
2673 useMinLifetimes6(ctx, lease->addr_, lease->prefixlen_)) {
2674 getMinLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2675 } else {
2676 getLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2677 }
2678 }
2679 if (!ctx.fake_allocation_) {
2680 bool update_stats = false;
2681
2682 if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED || lease->state_ == Lease::STATE_RELEASED) {
2683 // Transition lease state to default (aka assigned)
2684 lease->state_ = Lease::STATE_DEFAULT;
2685
2686 // If the lease is in the current subnet we need to account
2687 // for the re-assignment of the lease.
2688 if (inAllowedPool(ctx, ctx.currentIA().type_,
2689 lease->addr_, true)) {
2690 update_stats = true;
2691 }
2692 }
2693
2694 bool fqdn_changed = ((lease->type_ != Lease::TYPE_PD) &&
2695 !(lease->hasIdenticalFqdn(*lease_it)));
2696
2697 lease->cltt_ = time(NULL);
2698 if (!fqdn_changed) {
2699 setLeaseReusable(lease, current_preferred_lft, ctx);
2700 }
2701
2702 if (lease->reuseable_valid_lft_ == 0) {
2703 ctx.currentIA().changed_leases_.push_back(lease_it);
2705 } else {
2706 // Server needs to know about resused leases to avoid DNS updates.
2707 ctx.currentIA().reused_leases_.push_back(lease_it);
2708 }
2709
2710 if (update_stats) {
2712 StatsMgr::generateName("subnet", lease->subnet_id_,
2713 ctx.currentIA().type_ == Lease::TYPE_NA ?
2714 "assigned-nas" : "assigned-pds"),
2715 static_cast<int64_t>(1));
2716
2718 StatsMgr::generateName("subnet", lease->subnet_id_,
2719 ctx.currentIA().type_ == Lease::TYPE_NA ?
2720 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2721 static_cast<int64_t>(1));
2722
2723 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2724 if (pool) {
2726 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2727 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2728 "pool" : "pd-pool", pool->getID(),
2729 ctx.currentIA().type_ == Lease::TYPE_NA ?
2730 "assigned-nas" : "assigned-pds")),
2731 static_cast<int64_t>(1));
2732
2734 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2735 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2736 "pool" : "pd-pool", pool->getID(),
2737 ctx.currentIA().type_ == Lease::TYPE_NA ?
2738 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2739 static_cast<int64_t>(1));
2740 }
2741
2742 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2743 "assigned-nas" : "assigned-pds",
2744 static_cast<int64_t>(1));
2745
2746 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2747 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2748 static_cast<int64_t>(1));
2749 }
2750 }
2751
2752 updated_leases.push_back(lease);
2753 }
2754
2755 return (updated_leases);
2756}
2757
2758void
2760 const uint16_t timeout,
2761 const bool remove_lease,
2762 const uint16_t max_unwarned_cycles) {
2763
2766 .arg(max_leases)
2767 .arg(timeout);
2768
2769 try {
2770 reclaimExpiredLeases6Internal(max_leases, timeout, remove_lease,
2771 max_unwarned_cycles);
2772 } catch (const std::exception& ex) {
2775 .arg(ex.what());
2776 }
2777}
2778
2779void
2781 const uint16_t timeout,
2782 const bool remove_lease,
2783 const uint16_t max_unwarned_cycles) {
2784
2785 // Create stopwatch and automatically start it to measure the time
2786 // taken by the routine.
2787 util::Stopwatch stopwatch;
2788
2789 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2790
2791 // This value indicates if we have been able to deal with all expired
2792 // leases in this pass.
2793 bool incomplete_reclamation = false;
2794 Lease6Collection leases;
2795 // The value of 0 has a special meaning - reclaim all.
2796 if (max_leases > 0) {
2797 // If the value is non-zero, the caller has limited the number of
2798 // leases to reclaim. We obtain one lease more to see if there will
2799 // be still leases left after this pass.
2800 lease_mgr.getExpiredLeases6(leases, max_leases + 1);
2801 // There are more leases expired leases than we will process in this
2802 // pass, so we should mark it as an incomplete reclamation. We also
2803 // remove this extra lease (which we don't want to process anyway)
2804 // from the collection.
2805 if (leases.size() > max_leases) {
2806 leases.pop_back();
2807 incomplete_reclamation = true;
2808 }
2809
2810 } else {
2811 // If there is no limitation on the number of leases to reclaim,
2812 // we will try to process all. Hence, we don't mark it as incomplete
2813 // reclamation just yet.
2814 lease_mgr.getExpiredLeases6(leases, max_leases);
2815 }
2816
2817 // Do not initialize the callout handle until we know if there are any
2818 // lease6_expire callouts installed.
2819 CalloutHandlePtr callout_handle;
2820 if (!leases.empty() &&
2821 HooksManager::calloutsPresent(Hooks.hook_index_lease6_expire_)) {
2822 callout_handle = HooksManager::createCalloutHandle();
2823 }
2824
2825 size_t leases_processed = 0;
2826 for (auto const& lease : leases) {
2827
2828 try {
2829 // Reclaim the lease.
2830 if (MultiThreadingMgr::instance().getMode()) {
2831 // The reclamation is exclusive of packet processing.
2832 WriteLockGuard exclusive(rw_mutex_);
2833
2834 reclaimExpiredLease(lease, remove_lease, callout_handle);
2835 ++leases_processed;
2836 } else {
2837 reclaimExpiredLease(lease, remove_lease, callout_handle);
2838 ++leases_processed;
2839 }
2840
2841 } catch (const std::exception& ex) {
2843 .arg(lease->addr_.toText())
2844 .arg(ex.what());
2845 }
2846
2847 // Check if we have hit the timeout for running reclamation routine and
2848 // return if we have. We're checking it here, because we always want to
2849 // allow reclaiming at least one lease.
2850 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2851 // Timeout. This will likely mean that we haven't been able to process
2852 // all leases we wanted to process. The reclamation pass will be
2853 // probably marked as incomplete.
2854 if (!incomplete_reclamation) {
2855 if (leases_processed < leases.size()) {
2856 incomplete_reclamation = true;
2857 }
2858 }
2859
2862 .arg(timeout);
2863 break;
2864 }
2865 }
2866
2867 // Stop measuring the time.
2868 stopwatch.stop();
2869
2870 // Mark completion of the lease reclamation routine and present some stats.
2873 .arg(leases_processed)
2874 .arg(stopwatch.logFormatTotalDuration());
2875
2876 // Check if this was an incomplete reclamation and increase the number of
2877 // consecutive incomplete reclamations.
2878 if (incomplete_reclamation) {
2879 ++incomplete_v6_reclamations_;
2880 // If the number of incomplete reclamations is beyond the threshold, we
2881 // need to issue a warning.
2882 if ((max_unwarned_cycles > 0) &&
2883 (incomplete_v6_reclamations_ > max_unwarned_cycles)) {
2885 .arg(max_unwarned_cycles);
2886 // We issued a warning, so let's now reset the counter.
2887 incomplete_v6_reclamations_ = 0;
2888 }
2889
2890 } else {
2891 // This was a complete reclamation, so let's reset the counter.
2892 incomplete_v6_reclamations_ = 0;
2893
2896 }
2897}
2898
2899void
2903 .arg(secs);
2904
2905 uint64_t deleted_leases = 0;
2906 try {
2907 // Try to delete leases from the lease database.
2908 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2909 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
2910
2911 } catch (const std::exception& ex) {
2913 .arg(ex.what());
2914 }
2915
2918 .arg(deleted_leases);
2919}
2920
2921void
2923 const uint16_t timeout,
2924 const bool remove_lease,
2925 const uint16_t max_unwarned_cycles) {
2926
2929 .arg(max_leases)
2930 .arg(timeout);
2931
2932 try {
2933 reclaimExpiredLeases4Internal(max_leases, timeout, remove_lease,
2934 max_unwarned_cycles);
2935 } catch (const std::exception& ex) {
2938 .arg(ex.what());
2939 }
2940}
2941
2942void
2944 const uint16_t timeout,
2945 const bool remove_lease,
2946 const uint16_t max_unwarned_cycles) {
2947
2948 // Create stopwatch and automatically start it to measure the time
2949 // taken by the routine.
2950 util::Stopwatch stopwatch;
2951
2952 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2953
2954 // This value indicates if we have been able to deal with all expired
2955 // leases in this pass.
2956 bool incomplete_reclamation = false;
2957 Lease4Collection leases;
2958 // The value of 0 has a special meaning - reclaim all.
2959 if (max_leases > 0) {
2960 // If the value is non-zero, the caller has limited the number of
2961 // leases to reclaim. We obtain one lease more to see if there will
2962 // be still leases left after this pass.
2963 lease_mgr.getExpiredLeases4(leases, max_leases + 1);
2964 // There are more leases expired leases than we will process in this
2965 // pass, so we should mark it as an incomplete reclamation. We also
2966 // remove this extra lease (which we don't want to process anyway)
2967 // from the collection.
2968 if (leases.size() > max_leases) {
2969 leases.pop_back();
2970 incomplete_reclamation = true;
2971 }
2972
2973 } else {
2974 // If there is no limitation on the number of leases to reclaim,
2975 // we will try to process all. Hence, we don't mark it as incomplete
2976 // reclamation just yet.
2977 lease_mgr.getExpiredLeases4(leases, max_leases);
2978 }
2979
2980 // Do not initialize the callout handle until we know if there are any
2981 // lease4_expire callouts installed.
2982 CalloutHandlePtr callout_handle;
2983 if (!leases.empty() &&
2984 HooksManager::calloutsPresent(Hooks.hook_index_lease4_expire_)) {
2985 callout_handle = HooksManager::createCalloutHandle();
2986 }
2987
2988 size_t leases_processed = 0;
2989 for (auto const& lease : leases) {
2990
2991 try {
2992 // Reclaim the lease.
2993 if (MultiThreadingMgr::instance().getMode()) {
2994 // The reclamation is exclusive of packet processing.
2995 WriteLockGuard exclusive(rw_mutex_);
2996
2997 reclaimExpiredLease(lease, remove_lease, callout_handle);
2998 ++leases_processed;
2999 } else {
3000 reclaimExpiredLease(lease, remove_lease, callout_handle);
3001 ++leases_processed;
3002 }
3003
3004 } catch (const std::exception& ex) {
3006 .arg(lease->addr_.toText())
3007 .arg(ex.what());
3008 }
3009
3010 // Check if we have hit the timeout for running reclamation routine and
3011 // return if we have. We're checking it here, because we always want to
3012 // allow reclaiming at least one lease.
3013 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
3014 // Timeout. This will likely mean that we haven't been able to process
3015 // all leases we wanted to process. The reclamation pass will be
3016 // probably marked as incomplete.
3017 if (!incomplete_reclamation) {
3018 if (leases_processed < leases.size()) {
3019 incomplete_reclamation = true;
3020 }
3021 }
3022
3025 .arg(timeout);
3026 break;
3027 }
3028 }
3029
3030 // Stop measuring the time.
3031 stopwatch.stop();
3032
3033 // Mark completion of the lease reclamation routine and present some stats.
3036 .arg(leases_processed)
3037 .arg(stopwatch.logFormatTotalDuration());
3038
3039 // Check if this was an incomplete reclamation and increase the number of
3040 // consecutive incomplete reclamations.
3041 if (incomplete_reclamation) {
3042 ++incomplete_v4_reclamations_;
3043 // If the number of incomplete reclamations is beyond the threshold, we
3044 // need to issue a warning.
3045 if ((max_unwarned_cycles > 0) &&
3046 (incomplete_v4_reclamations_ > max_unwarned_cycles)) {
3048 .arg(max_unwarned_cycles);
3049 // We issued a warning, so let's now reset the counter.
3050 incomplete_v4_reclamations_ = 0;
3051 }
3052
3053 } else {
3054 // This was a complete reclamation, so let's reset the counter.
3055 incomplete_v4_reclamations_ = 0;
3056
3059 }
3060}
3061
3062template<typename LeasePtrType>
3063void
3064AllocEngine::reclaimExpiredLease(const LeasePtrType& lease, const bool remove_lease,
3065 const CalloutHandlePtr& callout_handle) {
3066 reclaimExpiredLease(lease, remove_lease ? DB_RECLAIM_REMOVE : DB_RECLAIM_UPDATE,
3067 callout_handle);
3068}
3069
3070template<typename LeasePtrType>
3071void
3072AllocEngine::reclaimExpiredLease(const LeasePtrType& lease,
3073 const CalloutHandlePtr& callout_handle) {
3074 // This variant of the method is used by the code which allocates or
3075 // renews leases. It may be the case that the lease has already been
3076 // reclaimed, so there is nothing to do.
3077 if (!lease->stateExpiredReclaimed()) {
3078 reclaimExpiredLease(lease, DB_RECLAIM_LEAVE_UNCHANGED, callout_handle);
3079 }
3080}
3081
3082void
3083AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
3084 const DbReclaimMode& reclaim_mode,
3085 const CalloutHandlePtr& callout_handle) {
3086
3089 .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
3090 .arg(lease->addr_.toText())
3091 .arg(static_cast<int>(lease->prefixlen_));
3092
3093 // The skip flag indicates if the callouts have taken responsibility
3094 // for reclaiming the lease. The callout will set this to true if
3095 // it reclaims the lease itself. In this case the reclamation routine
3096 // will not update DNS nor update the database.
3097 bool skipped = false;
3098 bool released = (lease->state_ == Lease::STATE_RELEASED);
3099 bool registered = (lease->state_ == Lease::STATE_REGISTERED);
3100 if (callout_handle) {
3101
3102 // Use the RAII wrapper to make sure that the callout handle state is
3103 // reset when this object goes out of scope. All hook points must do
3104 // it to prevent possible circular dependency between the callout
3105 // handle and its arguments.
3106 ScopedCalloutHandleState callout_handle_state(callout_handle);
3107
3108 callout_handle->deleteAllArguments();
3109 callout_handle->setArgument("lease6", lease);
3110 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
3111
3112 HooksManager::callCallouts(Hooks.hook_index_lease6_expire_,
3113 *callout_handle);
3114
3115 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
3116 }
3117
3120
3121 if (!skipped) {
3122
3123 // Generate removal name change request for D2, if required.
3124 // This will return immediately if the DNS wasn't updated
3125 // when the lease was created.
3126 queueNCR(CHG_REMOVE, lease);
3127
3128 // Let's check if the lease that just expired is in DECLINED state.
3129 // If it is, we need to perform a couple extra steps.
3130 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
3131 if (lease->state_ == Lease::STATE_DECLINED) {
3132 // Do extra steps required for declined lease reclamation:
3133 // - call the recover hook
3134 // - bump decline-related stats
3135 // - log separate message
3136 // There's no point in keeping a declined lease after its
3137 // reclamation. A declined lease doesn't have any client
3138 // identifying information anymore. So we'll flag it for
3139 // removal unless the hook has set the skip flag.
3140 remove_lease = reclaimDeclined(lease);
3141 } else if (lease->state_ == Lease::STATE_REGISTERED) {
3142 if (reclaim_mode == DB_RECLAIM_LEAVE_UNCHANGED) {
3143 isc_throw(Unexpected, "attempt to reuse a registered lease");
3144 }
3145 // Remove (vs reclaim) expired registered leases.
3146 remove_lease = true;
3147 }
3148
3149 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
3150 // Reclaim the lease - depending on the configuration, set the
3151 // expired-reclaimed state or simply remove it.
3152 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3153 reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
3154 std::bind(&LeaseMgr::updateLease6,
3155 &lease_mgr, ph::_1));
3156 }
3157 }
3158
3159 // Update statistics.
3160
3161 // Increase total number of reclaimed leases.
3162 StatsMgr::instance().addValue("reclaimed-leases", static_cast<int64_t>(1));
3163
3164 // Increase number of reclaimed leases for a subnet.
3166 lease->subnet_id_,
3167 "reclaimed-leases"),
3168 static_cast<int64_t>(1));
3169
3170 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3171
3172 if (lease->type_ == Lease::TYPE_NA || lease->type_ == Lease::TYPE_PD) {
3173 if (subnet) {
3174 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
3175 if (pool) {
3177 StatsMgr::generateName("subnet", subnet->getID(),
3178 StatsMgr::generateName(lease->type_ == Lease::TYPE_NA ?
3179 "pool" : "pd-pool",
3180 pool->getID(), "reclaimed-leases")),
3181 static_cast<int64_t>(1));
3182 }
3183 }
3184 }
3185
3186 // Statistics must have been updated during the release.
3187 if (released) {
3188 return;
3189 }
3190
3191 // Decrease number of registered or assigned leases.
3192
3193 if (registered) {
3195 lease->subnet_id_,
3196 "registered-nas"),
3197 static_cast<int64_t>(-1));
3198 } else if (lease->type_ == Lease::TYPE_NA || lease->type_ == Lease::TYPE_PD) {
3199 // Decrease number of assigned addresses.
3200 StatsMgr::instance().addValue(lease->type_ == Lease::TYPE_NA ?
3201 "assigned-nas" : "assigned-pds",
3202 static_cast<int64_t>(-1));
3203
3205 lease->subnet_id_,
3206 lease->type_ == Lease::TYPE_NA ?
3207 "assigned-nas" : "assigned-pds"),
3208 static_cast<int64_t>(-1));
3209
3210 if (subnet) {
3211 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
3212 if (pool) {
3214 StatsMgr::generateName("subnet", subnet->getID(),
3215 StatsMgr::generateName(lease->type_ == Lease::TYPE_NA ?
3216 "pool" : "pd-pool", pool->getID(),
3217 lease->type_ == Lease::TYPE_NA ?
3218 "assigned-nas" : "assigned-pds")),
3219 static_cast<int64_t>(-1));
3220 }
3221 }
3222 }
3223}
3224
3225void
3226AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
3227 const DbReclaimMode& reclaim_mode,
3228 const CalloutHandlePtr& callout_handle) {
3229
3232 .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
3233 .arg(lease->addr_.toText());
3234
3235 // The skip flag indicates if the callouts have taken responsibility
3236 // for reclaiming the lease. The callout will set this to true if
3237 // it reclaims the lease itself. In this case the reclamation routine
3238 // will not update DNS nor update the database.
3239 bool skipped = false;
3240 bool released = (lease->state_ == Lease::STATE_RELEASED);
3241 if (callout_handle) {
3242
3243 // Use the RAII wrapper to make sure that the callout handle state is
3244 // reset when this object goes out of scope. All hook points must do
3245 // it to prevent possible circular dependency between the callout
3246 // handle and its arguments.
3247 ScopedCalloutHandleState callout_handle_state(callout_handle);
3248
3249 callout_handle->setArgument("lease4", lease);
3250 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
3251
3252 HooksManager::callCallouts(Hooks.hook_index_lease4_expire_,
3253 *callout_handle);
3254
3255 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
3256 }
3257
3260
3261 if (!skipped) {
3262
3263 // Generate removal name change request for D2, if required.
3264 // This will return immediately if the DNS wasn't updated
3265 // when the lease was created.
3266 queueNCR(CHG_REMOVE, lease);
3267 // Clear DNS fields so we avoid redundant removes.
3268 lease->hostname_.clear();
3269 lease->fqdn_fwd_ = false;
3270 lease->fqdn_rev_ = false;
3271
3272 // Let's check if the lease that just expired is in DECLINED state.
3273 // If it is, we need to perform a couple extra steps.
3274 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
3275 if (lease->state_ == Lease::STATE_DECLINED) {
3276 // Do extra steps required for declined lease reclamation:
3277 // - call the recover hook
3278 // - bump decline-related stats
3279 // - log separate message
3280 // There's no point in keeping a declined lease after its
3281 // reclamation. A declined lease doesn't have any client
3282 // identifying information anymore. So we'll flag it for
3283 // removal unless the hook has set the skip flag.
3284 remove_lease = reclaimDeclined(lease);
3285 }
3286
3287 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
3288 // Reclaim the lease - depending on the configuration, set the
3289 // expired-reclaimed state or simply remove it.
3290 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3291 reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
3292 std::bind(&LeaseMgr::updateLease4,
3293 &lease_mgr, ph::_1));
3294 }
3295 }
3296
3297 // Update statistics.
3298
3299 // Increase total number of reclaimed leases.
3300 StatsMgr::instance().addValue("reclaimed-leases", static_cast<int64_t>(1));
3301
3302 // Increase number of reclaimed leases for a subnet.
3304 lease->subnet_id_,
3305 "reclaimed-leases"),
3306 static_cast<int64_t>(1));
3307
3308 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease->subnet_id_);
3309
3310 if (subnet) {
3311 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3312 if (pool) {
3314 StatsMgr::generateName("subnet", subnet->getID(),
3315 StatsMgr::generateName("pool" , pool->getID(),
3316 "reclaimed-leases")),
3317 static_cast<int64_t>(1));
3318 }
3319 }
3320
3321 // Statistics must have been updated during the release.
3322 if (released) {
3323 return;
3324 }
3325
3326 // Decrease number of assigned addresses.
3327 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(-1));
3328
3330 lease->subnet_id_,
3331 "assigned-addresses"),
3332 static_cast<int64_t>(-1));
3333
3334 if (subnet) {
3335 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3336 if (pool) {
3338 StatsMgr::generateName("subnet", subnet->getID(),
3339 StatsMgr::generateName("pool" , pool->getID(),
3340 "assigned-addresses")),
3341 static_cast<int64_t>(-1));
3342 }
3343 }
3344}
3345
3346void
3350 .arg(secs);
3351
3352 uint64_t deleted_leases = 0;
3353 try {
3354 // Try to delete leases from the lease database.
3355 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3356 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
3357
3358 } catch (const std::exception& ex) {
3360 .arg(ex.what());
3361 }
3362
3365 .arg(deleted_leases);
3366}
3367
3368bool
3369AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
3370 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3371 return (true);
3372 }
3373
3374 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_recover_)) {
3376
3377 // Use the RAII wrapper to make sure that the callout handle state is
3378 // reset when this object goes out of scope. All hook points must do
3379 // it to prevent possible circular dependency between the callout
3380 // handle and its arguments.
3381 ScopedCalloutHandleState callout_handle_state(callout_handle);
3382
3383 // Pass necessary arguments
3384 callout_handle->setArgument("lease4", lease);
3385
3386 // Call the callouts
3387 HooksManager::callCallouts(Hooks.hook_index_lease4_recover_, *callout_handle);
3388
3389 // Callouts decided to skip the action. This means that the lease is not
3390 // assigned, so the client will get NoAddrAvail as a result. The lease
3391 // won't be inserted into the database.
3392 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3394 .arg(lease->addr_.toText());
3395 return (false);
3396 }
3397 }
3398
3400 .arg(lease->addr_.toText())
3401 .arg(lease->valid_lft_);
3402
3403 StatsMgr& stats_mgr = StatsMgr::instance();
3404
3405 // Decrease subnet specific counter for currently declined addresses
3406 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3407 "declined-addresses"),
3408 static_cast<int64_t>(-1));
3409
3410 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3411 "reclaimed-declined-addresses"),
3412 static_cast<int64_t>(1));
3413
3414 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease->subnet_id_);
3415 if (subnet) {
3416 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3417 if (pool) {
3418 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3419 StatsMgr::generateName("pool" , pool->getID(),
3420 "declined-addresses")),
3421 static_cast<int64_t>(-1));
3422
3423 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3424 StatsMgr::generateName("pool" , pool->getID(),
3425 "reclaimed-declined-addresses")),
3426 static_cast<int64_t>(1));
3427 }
3428 }
3429
3430 // Decrease global counter for declined addresses
3431 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3432
3433 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3434
3435 // Note that we do not touch assigned-addresses counters. Those are
3436 // modified in whatever code calls this method.
3437 return (true);
3438}
3439
3440bool
3441AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
3442 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3443 return (true);
3444 }
3445
3446 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_recover_)) {
3448
3449 // Use the RAII wrapper to make sure that the callout handle state is
3450 // reset when this object goes out of scope. All hook points must do
3451 // it to prevent possible circular dependency between the callout
3452 // handle and its arguments.
3453 ScopedCalloutHandleState callout_handle_state(callout_handle);
3454
3455 // Pass necessary arguments
3456 callout_handle->setArgument("lease6", lease);
3457
3458 // Call the callouts
3459 HooksManager::callCallouts(Hooks.hook_index_lease6_recover_, *callout_handle);
3460
3461 // Callouts decided to skip the action. This means that the lease is not
3462 // assigned, so the client will get NoAddrAvail as a result. The lease
3463 // won't be inserted into the database.
3464 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3466 .arg(lease->addr_.toText());
3467 return (false);
3468 }
3469 }
3470
3472 .arg(lease->addr_.toText())
3473 .arg(lease->valid_lft_);
3474
3475 StatsMgr& stats_mgr = StatsMgr::instance();
3476
3477 // Decrease subnet specific counter for currently declined addresses
3478 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3479 "declined-addresses"),
3480 static_cast<int64_t>(-1));
3481
3482 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3483 "reclaimed-declined-addresses"),
3484 static_cast<int64_t>(1));
3485
3486 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3487 if (subnet) {
3488 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
3489 if (pool) {
3490 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3491 StatsMgr::generateName("pool" , pool->getID(),
3492 "declined-addresses")),
3493 static_cast<int64_t>(-1));
3494
3495 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3496 StatsMgr::generateName("pool" , pool->getID(),
3497 "reclaimed-declined-addresses")),
3498 static_cast<int64_t>(1));
3499 }
3500 }
3501
3502 // Decrease global counter for declined addresses
3503 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3504
3505 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3506
3507 // Note that we do not touch assigned-nas counters. Those are
3508 // modified in whatever code calls this method.
3509 return (true);
3510}
3511
3512void
3514 lease->relay_id_.clear();
3515 lease->remote_id_.clear();
3516 if (lease->getContext()) {
3517 lease->setContext(ElementPtr());
3518 }
3519}
3520
3521void
3523 if (lease->getContext()) {
3524 lease->extended_info_action_ = Lease6::ACTION_DELETE;
3525 lease->setContext(ElementPtr());
3526 }
3527}
3528
3529template<typename LeasePtrType>
3530void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
3531 const bool remove_lease,
3532 const std::function<void (const LeasePtrType&)>&
3533 lease_update_fun) const {
3534
3535 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3536
3537 // Reclaim the lease - depending on the configuration, set the
3538 // expired-reclaimed state or simply remove it.
3539 if (remove_lease) {
3540 static_cast<void>(lease_mgr.deleteLease(lease));
3541 } else if (lease_update_fun) {
3542 // Clear FQDN information as we have already sent the
3543 // name change request to remove the DNS record.
3544 lease->reuseable_valid_lft_ = 0;
3545 lease->hostname_.clear();
3546 lease->fqdn_fwd_ = false;
3547 lease->fqdn_rev_ = false;
3548 lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
3550 lease_update_fun(lease);
3551
3552 } else {
3553 return;
3554 }
3555
3556 // Lease has been reclaimed.
3559 .arg(lease->addr_.toText());
3560}
3561
3562std::string
3564 if (!subnet) {
3565 return("<empty subnet>");
3566 }
3567
3568 SharedNetwork4Ptr network;
3569 subnet->getSharedNetwork(network);
3570 std::ostringstream ss;
3571 if (network) {
3572 ss << "shared-network: " << network->getName();
3573 } else {
3574 ss << "subnet id: " << subnet->getID();
3575 }
3576
3577 return(ss.str());
3578}
3579
3580} // namespace dhcp
3581} // namespace isc
3582
3583// ##########################################################################
3584// # DHCPv4 lease allocation code starts here.
3585// ##########################################################################
3586
3587namespace {
3588
3606bool
3607addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
3608 // When out-of-pool flag is true the server may assume that all host
3609 // reservations are for addresses that do not belong to the dynamic pool.
3610 // Therefore, it can skip the reservation checks when dealing with in-pool
3611 // addresses.
3612 if (ctx.subnet_ && ctx.subnet_->getReservationsInSubnet() &&
3613 (!ctx.subnet_->getReservationsOutOfPool() ||
3614 !ctx.subnet_->inPool(Lease::TYPE_V4, address))) {
3615 // The global parameter ip-reservations-unique controls whether it is allowed
3616 // to specify multiple reservations for the same IP address or delegated prefix
3617 // or IP reservations must be unique. Some host backends do not support the
3618 // former, thus we can't always use getAll4 calls to get the reservations
3619 // for the given IP. When we're in the default mode, when IP reservations
3620 // are unique, we should call get4 (supported by all backends). If we're in
3621 // the mode in which non-unique reservations are allowed the backends which
3622 // don't support it are not used and we can safely call getAll4.
3623 ConstHostCollection hosts;
3624 if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
3625 try {
3626 // Reservations are unique. It is safe to call get4 to get the unique host.
3627 ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
3628 if (host) {
3629 hosts.push_back(host);
3630 }
3631 } catch (const MultipleRecords& ex) {
3633 .arg(address)
3634 .arg(ctx.subnet_->getID())
3635 .arg(ex.what());
3636 throw;
3637 }
3638 } else {
3639 // Reservations can be non-unique. Need to get all reservations for that address.
3640 hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
3641 }
3642
3643 for (auto const& host : hosts) {
3644 for (const AllocEngine::IdentifierPair& id_pair : ctx.host_identifiers_) {
3645 // If we find the matching host we know that this address is reserved
3646 // for us and we can return immediately.
3647 if (id_pair.first == host->getIdentifierType() &&
3648 id_pair.second == host->getIdentifier()) {
3649 return (false);
3650 }
3651 }
3652 }
3653 // We didn't find a matching host. If there are any reservations it means that
3654 // address is reserved for another client or multiple clients. If there are
3655 // no reservations address is not reserved for another client.
3656 return (!hosts.empty());
3657 }
3658 return (false);
3659}
3660
3678bool
3679hasAddressReservation(AllocEngine::ClientContext4& ctx) {
3680 if (ctx.hosts_.empty()) {
3681 return (false);
3682 }
3683
3684 // Save list of subnets checked so we can skip rechecking
3685 // client classes for global HRs.
3686 std::list<ConstSubnet4Ptr> eligible_subnets;
3687
3688 // Start with currently selected subnet.
3689 ConstSubnet4Ptr subnet = ctx.subnet_;
3690 while (subnet) {
3691 eligible_subnets.push_back(subnet);
3692 if (subnet->getReservationsInSubnet()) {
3693 auto host = ctx.hosts_.find(subnet->getID());
3694 // The out-of-pool flag indicates that no client should be assigned
3695 // reserved addresses from within the dynamic pool, and for that
3696 // reason look only for reservations that are outside the pools,
3697 // hence the inPool check.
3698 if (host != ctx.hosts_.end() && host->second) {
3699 auto reservation = host->second->getIPv4Reservation();
3700 if (!reservation.isV4Zero() &&
3701 (!subnet->getReservationsOutOfPool() ||
3702 !subnet->inPool(Lease::TYPE_V4, reservation))) {
3703 ctx.subnet_ = subnet;
3704 return (true);
3705 }
3706
3707 // Do we have a dynamic subnet reservation? If so use it to select
3708 // the subnet and return. This overrides a global reserved address.
3709 if (reservation.isV4Zero()) {
3710 ctx.subnet_ = subnet;
3711 return(false);
3712 }
3713 }
3714 }
3715
3716 // No address reservation found here, so let's try another subnet
3717 // within the same shared network.
3718 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3719 }
3720
3721 // No subnet HR so fetch the globally reserved address if there is one.
3722 auto global_host = ctx.hosts_.find(SUBNET_ID_GLOBAL);
3723 auto global_host_address = ((global_host != ctx.hosts_.end() && global_host->second) ?
3724 global_host->second->getIPv4Reservation() :
3726 if (global_host_address == IOAddress::IPV4_ZERO_ADDRESS()) {
3727 return (false);
3728 }
3729
3730 // Iterate over the list of eligible subnets (starting with
3731 // with the currently selected subnet) looking for a subnet
3732 // in which the global host address is valid.
3733 for (auto eligible_subnet : eligible_subnets) {
3734 // If global reservations are enabled for this subnet and there is
3735 // globally reserved address and that address is feasible for this
3736 // subnet, update the selected subnet and return true.
3737 if (eligible_subnet->getReservationsGlobal() &&
3738 (global_host_address != IOAddress::IPV4_ZERO_ADDRESS()) &&
3739 (eligible_subnet->inRange(global_host_address))) {
3740 ctx.subnet_ = eligible_subnet;
3741 return (true);
3742 }
3743 }
3744
3745 if (global_host_address != IOAddress::IPV4_ZERO_ADDRESS()) {
3748 .arg(ctx.query_->getLabel())
3749 .arg(global_host_address.toText())
3751 }
3752
3753 return (false);
3754}
3755
3771void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
3772 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3773
3774 ConstSubnet4Ptr original_subnet = ctx.subnet_;
3775
3776 auto const& classes = ctx.query_->getClasses();
3777
3778 // Client identifier is optional. First check if we can try to lookup
3779 // by client-id.
3780 bool try_clientid_lookup = (ctx.clientid_ &&
3781 SharedNetwork4::subnetsIncludeMatchClientId(original_subnet, classes));
3782
3783 // If it is possible to use client identifier to try to find client's lease.
3784 if (try_clientid_lookup) {
3785 // Get all leases for this client identifier. When shared networks are
3786 // in use it is more efficient to make a single query rather than
3787 // multiple queries, one for each subnet.
3788 Lease4Collection leases_client_id = lease_mgr.getLease4(*ctx.clientid_);
3789
3790 // Iterate over the subnets within the shared network to see if any client's
3791 // lease belongs to them.
3792 for (ConstSubnet4Ptr subnet = original_subnet; subnet;
3793 subnet = subnet->getNextSubnet(original_subnet, classes)) {
3794
3795 // If client identifier has been supplied and the server wasn't
3796 // explicitly configured to ignore client identifiers for this subnet
3797 // check if there is a lease within this subnet.
3798 if (subnet->getMatchClientId()) {
3799 for (auto const& l : leases_client_id) {
3800 if (l->subnet_id_ == subnet->getID()) {
3801 // Lease found, so stick to this lease.
3802 client_lease = l;
3803 ctx.subnet_ = subnet;
3804 return;
3805 }
3806 }
3807 }
3808 }
3809 }
3810
3811 // If no lease found using the client identifier, try the lookup using
3812 // the HW address.
3813 if (!client_lease && ctx.hwaddr_) {
3814
3815 // Get all leases for this HW address.
3816 Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
3817
3818 for (ConstSubnet4Ptr subnet = original_subnet; subnet;
3819 subnet = subnet->getNextSubnet(original_subnet, classes)) {
3820 ClientIdPtr client_id;
3821 if (subnet->getMatchClientId()) {
3822 client_id = ctx.clientid_;
3823 }
3824
3825 // Try to find the lease that matches current subnet and belongs to
3826 // this client, so both HW address and client identifier match.
3827 for (auto const& client_lease_it : leases_hw_address) {
3828 Lease4Ptr existing_lease = client_lease_it;
3829 if ((existing_lease->subnet_id_ == subnet->getID()) &&
3830 existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
3831 // Found the lease of this client, so return it.
3832 client_lease = existing_lease;
3833 // We got a lease but the subnet it belongs to may differ from
3834 // the original subnet. Let's now stick to this subnet.
3835 ctx.subnet_ = subnet;
3836 return;
3837 }
3838 }
3839 }
3840 }
3841}
3842
3855bool
3856inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
3857 // If the subnet belongs to a shared network we will be iterating
3858 // over the subnets that belong to this shared network.
3859 ConstSubnet4Ptr current_subnet = ctx.subnet_;
3860 auto const& classes = ctx.query_->getClasses();
3861
3862 while (current_subnet) {
3863 if (current_subnet->inPool(Lease::TYPE_V4, address, classes)) {
3864 // We found a subnet that this address belongs to, so it
3865 // seems that this subnet is the good candidate for allocation.
3866 // Let's update the selected subnet.
3867 ctx.subnet_ = current_subnet;
3868 return (true);
3869 }
3870
3871 current_subnet = current_subnet->getNextSubnet(ctx.subnet_, classes);
3872 }
3873
3874 return (false);
3875}
3876
3877} // namespace
3878
3879namespace isc {
3880namespace dhcp {
3881
3893
3895 const ClientIdPtr& clientid,
3896 const HWAddrPtr& hwaddr,
3897 const asiolink::IOAddress& requested_addr,
3898 const bool fwd_dns_update,
3899 const bool rev_dns_update,
3900 const std::string& hostname,
3901 const bool fake_allocation,
3902 const uint32_t offer_lft)
3904 subnet_(subnet), clientid_(clientid), hwaddr_(hwaddr),
3905 requested_address_(requested_addr),
3906 fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
3907 hostname_(hostname), callout_handle_(),
3908 fake_allocation_(fake_allocation), offer_lft_(offer_lft), old_lease_(), new_lease_(),
3910 ddns_params_(new DdnsParams()) {
3911
3912 // Initialize host identifiers.
3913 if (hwaddr) {
3914 addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
3915 }
3916}
3917
3920 if (subnet_ && subnet_->getReservationsInSubnet()) {
3921 auto host = hosts_.find(subnet_->getID());
3922 if (host != hosts_.cend()) {
3923 return (host->second);
3924 }
3925 }
3926
3927 return (globalHost());
3928}
3929
3932 if (subnet_ && subnet_->getReservationsGlobal()) {
3933 auto host = hosts_.find(SUBNET_ID_GLOBAL);
3934 if (host != hosts_.cend()) {
3935 return (host->second);
3936 }
3937 }
3938
3939 return (ConstHostPtr());
3940}
3941
3944 // We already have it return it unless the context subnet has changed.
3945 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
3946 return (ddns_params_);
3947 }
3948
3949 // Doesn't exist yet or is stale, (re)create it.
3950 if (subnet_) {
3951 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
3952 return (ddns_params_);
3953 }
3954
3955 // Asked for it without a subnet? This case really shouldn't occur but
3956 // for now let's return an instance with default values.
3957 return (DdnsParamsPtr(new DdnsParams()));
3958}
3959
3962 // The NULL pointer indicates that the old lease didn't exist. It may
3963 // be later set to non NULL value if existing lease is found in the
3964 // database.
3965 ctx.old_lease_.reset();
3966 ctx.new_lease_.reset();
3967
3968 // Before we start allocation process, we need to make sure that the
3969 // selected subnet is allowed for this client. If not, we'll try to
3970 // use some other subnet within the shared network. If there are no
3971 // subnets allowed for this client within the shared network, we
3972 // can't allocate a lease.
3973 ConstSubnet4Ptr subnet = ctx.subnet_;
3974 auto const& classes = ctx.query_->getClasses();
3975 if (subnet && !subnet->clientSupported(classes)) {
3976 ctx.subnet_ = subnet->getNextSubnet(subnet, classes);
3977 }
3978
3979 try {
3980 if (!ctx.subnet_) {
3981 isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
3982 }
3983
3984 if (!ctx.hwaddr_) {
3985 isc_throw(BadValue, "HWAddr must be defined");
3986 }
3987
3988 if (ctx.fake_allocation_) {
3989 ctx.new_lease_ = discoverLease4(ctx);
3990 } else {
3991 ctx.new_lease_ = requestLease4(ctx);
3992 }
3993
3994 } catch (const NoSuchLease& e) {
3995 isc_throw(NoSuchLease, "detected data race in AllocEngine::allocateLease4: " << e.what());
3996
3997 } catch (const isc::Exception& e) {
3998 // Some other error, return an empty lease.
4000 .arg(ctx.query_->getLabel())
4001 .arg(e.what());
4002 }
4003
4004 return (ctx.new_lease_);
4005}
4006
4007void
4009 // If there is no subnet, there is nothing to do.
4010 if (!ctx.subnet_) {
4011 return;
4012 }
4013
4014 auto subnet = ctx.subnet_;
4015
4016 // If already done just return.
4018 !subnet->getReservationsInSubnet()) {
4019 return;
4020 }
4021
4022 // @todo: This code can be trivially optimized.
4024 subnet->getReservationsGlobal()) {
4026 if (ghost) {
4027 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
4028
4029 // If we had only to fetch global reservations it is done.
4030 if (!subnet->getReservationsInSubnet()) {
4031 return;
4032 }
4033 }
4034 }
4035
4036 std::map<SubnetID, ConstHostPtr> host_map;
4037 SharedNetwork4Ptr network;
4038 subnet->getSharedNetwork(network);
4039
4040 // If the subnet belongs to a shared network it is usually going to be
4041 // more efficient to make a query for all reservations for a particular
4042 // client rather than a query for each subnet within this shared network.
4043 // The only case when it is going to be less efficient is when there are
4044 // more host identifier types in use than subnets within a shared network.
4045 // As it breaks RADIUS use of host caching this can be disabled by the
4046 // host manager.
4047 const bool use_single_query = network &&
4049 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
4050
4051 if (use_single_query) {
4052 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
4053 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
4054 &id_pair.second[0],
4055 id_pair.second.size());
4056 // Store the hosts in the temporary map, because some hosts may
4057 // belong to subnets outside of the shared network. We'll need
4058 // to eliminate them.
4059 for (auto const& host : hosts) {
4060 if (host->getIPv4SubnetID() != SUBNET_ID_GLOBAL) {
4061 host_map[host->getIPv4SubnetID()] = host;
4062 }
4063 }
4064 }
4065 }
4066
4067 auto const& classes = ctx.query_->getClasses();
4068 // We can only search for the reservation if a subnet has been selected.
4069 while (subnet) {
4070
4071 // Only makes sense to get reservations if the client has access
4072 // to the class and host reservations are enabled for this subnet.
4073 if (subnet->clientSupported(classes) && subnet->getReservationsInSubnet()) {
4074 // Iterate over configured identifiers in the order of preference
4075 // and try to use each of them to search for the reservations.
4076 if (use_single_query) {
4077 if (host_map.count(subnet->getID()) > 0) {
4078 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
4079 }
4080 } else {
4081 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
4082 // Attempt to find a host using a specified identifier.
4083 ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
4084 id_pair.first,
4085 &id_pair.second[0],
4086 id_pair.second.size());
4087 // If we found matching host for this subnet.
4088 if (host) {
4089 ctx.hosts_[subnet->getID()] = host;
4090 break;
4091 }
4092 }
4093 }
4094 }
4095
4096 // We need to get to the next subnet if this is a shared network. If it
4097 // is not (a plain subnet), getNextSubnet will return NULL and we're
4098 // done here.
4099 subnet = subnet->getNextSubnet(ctx.subnet_, classes);
4100 }
4101
4102 // The hosts can be used by the server to return reserved options to
4103 // the DHCP client. Such options must be encapsulated (i.e., they must
4104 // include suboptions).
4105 for (auto const& host : ctx.hosts_) {
4106 host.second->encapsulateOptions();
4107 }
4108}
4109
4112 ConstHostPtr host;
4113 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
4114 // Attempt to find a host using a specified identifier.
4115 host = HostMgr::instance().get4(SUBNET_ID_GLOBAL, id_pair.first,
4116 &id_pair.second[0], id_pair.second.size());
4117
4118 // If we found matching global host we're done.
4119 if (host) {
4120 break;
4121 }
4122 }
4123
4124 return (host);
4125}
4126
4128AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
4129 // Find an existing lease for this client. This function will return null
4130 // if there is a conflict with existing lease and the allocation should
4131 // not be continued.
4132 Lease4Ptr client_lease;
4133 findClientLease(ctx, client_lease);
4134
4135 // Fetch offer_lft to see if we're allocating on DISCOVER.
4136 ctx.offer_lft_ = getOfferLft(ctx);
4137
4138 // new_lease will hold the pointer to the lease that we will offer to the
4139 // caller.
4140 Lease4Ptr new_lease;
4141
4143
4144 // Check if there is a reservation for the client. If there is, we want to
4145 // assign the reserved address, rather than any other one.
4146 if (hasAddressReservation(ctx)) {
4147
4150 .arg(ctx.query_->getLabel())
4151 .arg(ctx.currentHost()->getIPv4Reservation().toText());
4152
4153 // If the client doesn't have a lease or the leased address is different
4154 // than the reserved one then let's try to allocate the reserved address.
4155 // Otherwise the address that the client has is the one for which it
4156 // has a reservation, so just renew it.
4157 if (!client_lease || (client_lease->addr_ != ctx.currentHost()->getIPv4Reservation())) {
4158 // The call below will return a pointer to the lease for the address
4159 // reserved to this client, if the lease is available, i.e. is not
4160 // currently assigned to any other client.
4161 // Note that we don't remove the existing client's lease at this point
4162 // because this is not a real allocation, we just offer what we can
4163 // allocate in the DHCPREQUEST time.
4164 new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
4165 callout_status);
4166 if (!new_lease) {
4168 .arg(ctx.query_->getLabel())
4169 .arg(ctx.currentHost()->getIPv4Reservation().toText())
4170 .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
4171 "(no lease info)");
4172 if (ctx.conflicting_lease_) {
4174 ctx.conflicting_lease_->subnet_id_,
4175 "v4-reservation-conflicts"),
4176 static_cast<int64_t>(1));
4177 StatsMgr::instance().addValue("v4-reservation-conflicts",
4178 static_cast<int64_t>(1));
4179 }
4180 }
4181
4182 } else {
4183 new_lease = renewLease4(client_lease, ctx);
4184 }
4185 }
4186
4187 // Client does not have a reservation or the allocation of the reserved
4188 // address has failed, probably because the reserved address is in use
4189 // by another client. If the client has a lease, we will check if we can
4190 // offer this lease to the client. The lease can't be offered in the
4191 // situation when it is reserved for another client or when the address
4192 // is not in the dynamic pool. The former may be the result of adding the
4193 // new reservation for the address used by this client. The latter may
4194 // be due to the client using the reserved out-of-the pool address, for
4195 // which the reservation has just been removed.
4196 if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
4197 !addressReserved(client_lease->addr_, ctx)) {
4198
4201 .arg(ctx.query_->getLabel());
4202
4203 // If offer-lifetime is shorter than the existing expiration, reset
4204 // offer-lifetime to zero. This allows us to simply return the
4205 // existing lease without updating it in the lease store.
4206 if ((ctx.offer_lft_) &&
4207 (time(NULL) + ctx.offer_lft_ < client_lease->getExpirationTime())) {
4208 ctx.offer_lft_ = 0;
4209 }
4210
4211 new_lease = renewLease4(client_lease, ctx);
4212 }
4213
4214 // The client doesn't have any lease or the lease can't be offered
4215 // because it is either reserved for some other client or the
4216 // address is not in the dynamic pool.
4217 // Let's use the client's hint (requested IP address), if the client
4218 // has provided it, and try to offer it. This address must not be
4219 // reserved for another client, and must be in the range of the
4220 // dynamic pool.
4221 if (!new_lease && !ctx.requested_address_.isV4Zero() &&
4222 inAllowedPool(ctx, ctx.requested_address_) &&
4223 !addressReserved(ctx.requested_address_, ctx)) {
4224
4227 .arg(ctx.requested_address_.toText())
4228 .arg(ctx.query_->getLabel());
4229
4230 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
4231 callout_status);
4232 }
4233
4234 // The allocation engine failed to allocate all of the candidate
4235 // addresses. We will now use the allocator to pick the address
4236 // from the dynamic pool.
4237 if (!new_lease) {
4238
4241 .arg(ctx.query_->getLabel());
4242
4243 new_lease = allocateUnreservedLease4(ctx);
4244 }
4245
4246 // Some of the methods like reuseExpiredLease4 may set the old lease to point
4247 // to the lease which they remove/override. If it is not set, but we have
4248 // found that the client has the lease the client's lease is the one
4249 // to return as an old lease.
4250 if (!ctx.old_lease_ && client_lease) {
4251 ctx.old_lease_ = client_lease;
4252 }
4253
4254 return (new_lease);
4255}
4256
4258 if (LeaseMgrFactory::instance().deleteLease(lease) &&
4259 (lease->state_ != Lease4::STATE_RELEASED)) {
4260 // Need to decrease statistic for assigned addresses.
4261 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(-1));
4262
4263 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
4264 "assigned-addresses"),
4265 static_cast<int64_t>(-1));
4266
4267 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()
4268 ->getBySubnetId(lease->subnet_id_);
4269 if (subnet) {
4270 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
4271 if (pool) {
4272 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", subnet->getID(),
4273 StatsMgr::generateName("pool", pool->getID(),
4274 "assigned-addresses")),
4275 static_cast<int64_t>(-1));
4276 }
4277 }
4278 }
4279}
4280
4282AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
4283 // Find an existing lease for this client. This function will return null
4284 // if there is a conflict with existing lease and the allocation should
4285 // not be continued.
4286 Lease4Ptr client_lease;
4287 findClientLease(ctx, client_lease);
4288
4289 // When the client sends the DHCPREQUEST, it should always specify the
4290 // address which it is requesting or renewing. That is, the client should
4291 // either use the requested IP address option or set the ciaddr. However,
4292 // we try to be liberal and allow the clients to not specify an address
4293 // in which case the allocation engine will pick a suitable address
4294 // for the client.
4295 if (!ctx.requested_address_.isV4Zero()) {
4296 // If the client has specified an address, make sure this address
4297 // is not reserved for another client. If it is, stop here because
4298 // we can't allocate this address.
4299 if (addressReserved(ctx.requested_address_, ctx)) {
4300
4303 .arg(ctx.query_->getLabel())
4304 .arg(ctx.requested_address_.toText());
4305
4306 return (Lease4Ptr());
4307 }
4308
4309 } else if (hasAddressReservation(ctx)) {
4310 // The client hasn't specified an address to allocate, so the
4311 // allocation engine needs to find an appropriate address.
4312 // If there is a reservation for the client, let's try to
4313 // allocate the reserved address.
4314 ctx.requested_address_ = ctx.currentHost()->getIPv4Reservation();
4315
4318 .arg(ctx.query_->getLabel())
4319 .arg(ctx.requested_address_.toText());
4320 }
4321
4322 if (!ctx.requested_address_.isV4Zero()) {
4323 // There is a specific address to be allocated. Let's find out if
4324 // the address is in use.
4326 // If the address is in use (allocated and not expired), we check
4327 // if the address is in use by our client or another client.
4328 // If it is in use by another client, the address can't be
4329 // allocated.
4330 if (existing && !existing->expired() &&
4331 !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
4332 ctx.clientid_ : ClientIdPtr())) {
4333
4336 .arg(ctx.query_->getLabel())
4337 .arg(ctx.requested_address_.toText());
4338
4339 return (Lease4Ptr());
4340 }
4341
4342 // If the client has a reservation but it is requesting a different
4343 // address it is possible that the client was offered this different
4344 // address because the reserved address is in use. We will have to
4345 // check if the address is in use.
4346 if (hasAddressReservation(ctx) &&
4347 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) {
4348 existing =
4349 LeaseMgrFactory::instance().getLease4(ctx.currentHost()->getIPv4Reservation());
4350 // If the reserved address is not in use, i.e. the lease doesn't
4351 // exist or is expired, and the client is requesting a different
4352 // address, return NULL. The client should go back to the
4353 // DHCPDISCOVER and the reserved address will be offered.
4354 if (!existing || existing->expired()) {
4355
4358 .arg(ctx.query_->getLabel())
4359 .arg(ctx.currentHost()->getIPv4Reservation().toText())
4360 .arg(ctx.requested_address_.toText());
4361
4362 return (Lease4Ptr());
4363 }
4364 }
4365
4366 // The use of the out-of-pool addresses is only allowed when the requested
4367 // address is reserved for the client. If the address is not reserved one
4368 // and it doesn't belong to the dynamic pool, do not allocate it.
4369 if ((!hasAddressReservation(ctx) ||
4370 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
4371 !inAllowedPool(ctx, ctx.requested_address_)) {
4372
4375 .arg(ctx.query_->getLabel())
4376 .arg(ctx.requested_address_);
4377
4378 ctx.unknown_requested_addr_ = true;
4379 return (Lease4Ptr());
4380 }
4381
4382 // During a prior discover the allocation engine deemed that the
4383 // client's current lease (client_lease) should no longer be used and
4384 // so offered a different lease (existing) that was temporarily
4385 // allocated because offer-lifetime is greater than zero. We need to
4386 // delete the unusable lease and renew the temporary lease.
4387 if (((client_lease && existing) && (client_lease->addr_ != existing->addr_) &&
4388 (existing->addr_ == ctx.requested_address_) &&
4389 (existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
4390 ctx.clientid_ : ClientIdPtr())))
4391 && getOfferLft(ctx, false)) {
4392 auto conflicted_lease = client_lease;
4393 client_lease = existing;
4394 deleteAssignedLease(conflicted_lease);
4395 }
4396 }
4397
4398 // We have gone through all the checks, so we can now allocate the address
4399 // for the client.
4400
4401 // If the client is requesting an address which is assigned to the client
4402 // let's just renew this address. Also, renew this address if the client
4403 // doesn't request any specific address.
4404 // Added extra checks: the address is reserved for this client or belongs
4405 // to the dynamic pool for the case the pool class has changed before the
4406 // request.
4407 if (client_lease) {
4408 if (((client_lease->addr_ == ctx.requested_address_) ||
4410 ((hasAddressReservation(ctx) &&
4411 (ctx.currentHost()->getIPv4Reservation() == ctx.requested_address_)) ||
4412 inAllowedPool(ctx, client_lease->addr_))) {
4413
4416 .arg(ctx.query_->getLabel())
4417 .arg(ctx.requested_address_);
4418 return (renewLease4(client_lease, ctx));
4419 }
4420 }
4421
4422 // new_lease will hold the pointer to the allocated lease if we allocate
4423 // successfully.
4424 Lease4Ptr new_lease;
4425
4426 // The client doesn't have the lease or it is requesting an address
4427 // which it doesn't have. Let's try to allocate the requested address.
4428 if (!ctx.requested_address_.isV4Zero()) {
4429
4432 .arg(ctx.query_->getLabel())
4433 .arg(ctx.requested_address_.toText());
4434
4435 // The call below will return a pointer to the lease allocated
4436 // for the client if there is no lease for the requested address,
4437 // or the existing lease has expired. If the allocation fails,
4438 // e.g. because the lease is in use, we will return NULL to
4439 // indicate that we were unable to allocate the lease.
4441 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
4442 callout_status);
4443 } else {
4444
4447 .arg(ctx.query_->getLabel());
4448
4449 // We will only get here if the client didn't specify which
4450 // address it wanted to be allocated. The allocation engine will
4451 // to pick the address from the dynamic pool.
4452 new_lease = allocateUnreservedLease4(ctx);
4453 }
4454
4455 // If we allocated the lease for the client, but the client already had a
4456 // lease, we will need to return the pointer to the previous lease and
4457 // the previous lease needs to be removed from the lease database.
4458 if (new_lease && client_lease) {
4459 ctx.old_lease_ = Lease4Ptr(new Lease4(*client_lease));
4460
4463 .arg(ctx.query_->getLabel())
4464 .arg(client_lease->addr_.toText());
4465 deleteAssignedLease(client_lease);
4466 }
4467
4468 // Return the allocated lease or NULL pointer if allocation was
4469 // unsuccessful.
4470 return (new_lease);
4471}
4472
4473uint32_t
4474AllocEngine::getOfferLft(const ClientContext4& ctx, bool only_on_discover /* = true */) {
4475 // Not a DISCOVER or it's BOOTP, punt.
4476 if (only_on_discover && ((!ctx.fake_allocation_) || (ctx.query_->inClass("BOOTP")))) {
4477 return (0);
4478 }
4479
4480 util::Optional<uint32_t> offer_lft;
4481
4482 // If specified in one of our classes use it.
4483 // We use the first one we find.
4484 const ClientClasses classes = ctx.query_->getClasses();
4485 if (!classes.empty()) {
4486 // Let's get class definitions
4487 const ClientClassDictionaryPtr& dict =
4488 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4489
4490 // Iterate over the assigned class definitions.
4491 for (auto const& name : classes) {
4492 ClientClassDefPtr cl = dict->findClass(name);
4493 if (cl && (!cl->getOfferLft().unspecified())) {
4494 offer_lft = cl->getOfferLft();
4495 break;
4496 }
4497 }
4498 }
4499
4500 // If no classes specified it, get it from the subnet.
4501 if (offer_lft.unspecified()) {
4502 offer_lft = ctx.subnet_->getOfferLft();
4503 }
4504
4505 return (offer_lft.unspecified() ? 0 : offer_lft.get());
4506}
4507
4508uint32_t
4510 // If it's BOOTP, use infinite valid lifetime.
4511 if (ctx.query_->inClass("BOOTP")) {
4512 return (Lease::INFINITY_LFT);
4513 }
4514
4515 // Use the dhcp-lease-time content from the client if it's there.
4516 uint32_t requested_lft = 0;
4517 OptionPtr opt = ctx.query_->getOption(DHO_DHCP_LEASE_TIME);
4518 if (opt) {
4519 OptionUint32Ptr opt_lft = boost::dynamic_pointer_cast<OptionInt<uint32_t> >(opt);
4520 if (opt_lft) {
4521 requested_lft = opt_lft->getValue();
4522 }
4523 }
4524
4525 // If the triplet is specified in one of our classes use it.
4526 // We use the first one we find.
4527 Triplet<uint32_t> candidate_lft;
4528 const ClientClasses classes = ctx.query_->getClasses();
4529 if (!classes.empty()) {
4530 // Let's get class definitions
4531 const ClientClassDictionaryPtr& dict =
4532 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4533
4534 // Iterate over the assigned class definitions.
4535 for (auto const& name : classes) {
4536 ClientClassDefPtr cl = dict->findClass(name);
4537 if (cl && (!cl->getValid().unspecified())) {
4538 candidate_lft = cl->getValid();
4539 break;
4540 }
4541 }
4542 }
4543
4544 // If no classes specified it, get it from the subnet.
4545 if (!candidate_lft) {
4546 candidate_lft = ctx.subnet_->getValid();
4547 }
4548
4549 // If client requested a value, use the value bounded by
4550 // the candidate triplet.
4551 if (requested_lft > 0) {
4552 return (candidate_lft.get(requested_lft));
4553 }
4554
4555 // Use the candidate's default value.
4556 return (candidate_lft.get());
4557}
4558
4559void
4560AllocEngine::getMinValidLft(const ClientContext4& ctx, uint32_t& valid) {
4561 // If it's BOOTP, use infinite valid lifetime.
4562 if (ctx.query_->inClass("BOOTP")) {
4563 valid = Lease::INFINITY_LFT;
4564 return;
4565 }
4566
4567 // If the triplet is specified in one of our classes use it.
4568 // We use the first one we find.
4569 Triplet<uint32_t> candidate_lft;
4570 const ClientClasses classes = ctx.query_->getClasses();
4571 if (!classes.empty()) {
4572 // Let's get class definitions
4573 const ClientClassDictionaryPtr& dict =
4574 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4575
4576 // Iterate over the assigned class definitions.
4577 for (auto const& name : classes) {
4578 ClientClassDefPtr cl = dict->findClass(name);
4579 if (cl && (!cl->getValid().unspecified())) {
4580 candidate_lft = cl->getValid();
4581 break;
4582 }
4583 }
4584 }
4585
4586 // If no classes specified it, get it from the subnet.
4587 if (!candidate_lft) {
4588 candidate_lft = ctx.subnet_->getValid();
4589 }
4590
4591 // Save remaining value.
4592 uint32_t remain(valid);
4593
4594 // Set to the minimal value.
4595 valid = candidate_lft.getMin();
4596
4597 // Return at least the remaining value.
4598 if (remain > valid) {
4599 valid = remain;
4600 }
4601}
4602
4603namespace {
4604bool
4605useMinValidLft(const AllocEngine::ClientContext4& ctx, const IOAddress&addr) {
4606 auto const& threshold = ctx.subnet_->getAdaptiveLeaseTimeThreshold();
4607 if (!threshold.unspecified() && (threshold < 1.0)) {
4608 auto const& occupancy = ctx.subnet_->getAllocator(Lease::TYPE_V4)->
4609 getOccupancyRate(addr, ctx.query_->getClasses());
4610 if (occupancy >= threshold) {
4611 return (true);
4612 }
4613 }
4614 return (false);
4615}
4616} // end of anonymous namespace.
4617
4619AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
4620 CalloutHandle::CalloutNextStep& callout_status) {
4621 if (!ctx.hwaddr_) {
4622 isc_throw(BadValue, "Can't create a lease with NULL HW address");
4623 }
4624 if (!ctx.subnet_) {
4625 isc_throw(BadValue, "Can't create a lease without a subnet");
4626 }
4627
4628 // Get the context appropriate lifetime.
4629 uint32_t valid_lft = ctx.offer_lft_;
4630 if (!valid_lft) {
4631 if (useMinValidLft(ctx, addr)) {
4632 getMinValidLft(ctx, valid_lft);
4633 } else {
4634 valid_lft = getValidLft(ctx);
4635 }
4636 }
4637
4638 time_t now = time(0);
4639
4640 ClientIdPtr client_id;
4641 if (ctx.subnet_->getMatchClientId()) {
4642 client_id = ctx.clientid_;
4643 }
4644
4645 Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
4646 valid_lft, now, ctx.subnet_->getID()));
4647
4648 // Set FQDN specific lease parameters.
4649 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4650 lease->fqdn_rev_ = ctx.rev_dns_update_;
4651 lease->hostname_ = ctx.hostname_;
4652
4653 // Add (update) the extended information on the lease.
4654 static_cast<void>(updateLease4ExtendedInfo(lease, ctx));
4655
4656 // Let's execute all callouts registered for lease4_select
4657 if (ctx.callout_handle_ &&
4658 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4659
4660 // Use the RAII wrapper to make sure that the callout handle state is
4661 // reset when this object goes out of scope. All hook points must do
4662 // it to prevent possible circular dependency between the callout
4663 // handle and its arguments.
4664 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4665
4666 // Enable copying options from the packet within hook library.
4667 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4668
4669 // Pass necessary arguments
4670 // Pass the original client query
4671 ctx.callout_handle_->setArgument("query4", ctx.query_);
4672
4673 // Subnet from which we do the allocation (That's as far as we can go
4674 // with using SubnetPtr to point to Subnet4 object. Users should not
4675 // be confused with dynamic_pointer_casts. They should get a concrete
4676 // pointer (ConstSubnet4Ptr) pointing to a Subnet4 object.
4677 ConstSubnet4Ptr subnet4 =
4678 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4679 ctx.callout_handle_->setArgument("subnet4", subnet4);
4680
4681 // Is this solicit (fake = true) or request (fake = false)
4682 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4683
4684 // Are we allocating on DISCOVER? (i.e. offer_lft > 0).
4685 ctx.callout_handle_->setArgument("offer_lft", ctx.offer_lft_);
4686
4687 // Pass the intended lease as well
4688 ctx.callout_handle_->setArgument("lease4", lease);
4689
4690 // This is the first callout, so no need to clear any arguments
4691 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4692
4693 callout_status = ctx.callout_handle_->getStatus();
4694
4695 // Callouts decided to skip the action. This means that the lease is not
4696 // assigned, so the client will get NoAddrAvail as a result. The lease
4697 // won't be inserted into the database.
4698 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4700 return (Lease4Ptr());
4701 }
4702
4703 // Let's use whatever callout returned. Hopefully it is the same lease
4704 // we handled to it.
4705 ctx.callout_handle_->getArgument("lease4", lease);
4706 }
4707
4708 if (ctx.fake_allocation_ && ctx.offer_lft_) {
4709 // Turn them off before we persist, so we'll see it as different when
4710 // we extend it in the REQUEST. This should cause us to do DDNS (if
4711 // it's enabled).
4712 lease->fqdn_fwd_ = false;
4713 lease->fqdn_rev_ = false;
4714 }
4715
4716 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4717 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, lease->addr_, false);
4718 if (pool) {
4719 lease->pool_id_ = pool->getID();
4720 }
4721 // That is a real (REQUEST) allocation
4722 bool status = LeaseMgrFactory::instance().addLease(lease);
4723 if (status) {
4724
4725 // The lease insertion succeeded, let's bump up the statistic.
4727 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4728 "assigned-addresses"),
4729 static_cast<int64_t>(1));
4730
4732 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4733 "cumulative-assigned-addresses"),
4734 static_cast<int64_t>(1));
4735
4736 if (pool) {
4738 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4739 StatsMgr::generateName("pool", pool->getID(),
4740 "assigned-addresses")),
4741 static_cast<int64_t>(1));
4742
4744 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4745 StatsMgr::generateName("pool", pool->getID(),
4746 "cumulative-assigned-addresses")),
4747 static_cast<int64_t>(1));
4748 }
4749
4750
4751 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(1));
4752
4753 StatsMgr::instance().addValue("cumulative-assigned-addresses", static_cast<int64_t>(1));
4754
4755 return (lease);
4756 } else {
4757 // One of many failures with LeaseMgr (e.g. lost connection to the
4758 // database, database failed etc.). One notable case for that
4759 // is that we are working in multi-process mode and we lost a race
4760 // (some other process got that address first)
4761 return (Lease4Ptr());
4762 }
4763 } else {
4764 // That is only fake (DISCOVER) allocation
4765 // It is for OFFER only. We should not insert the lease and callers
4766 // have already verified the lease does not exist in the database.
4767 return (lease);
4768 }
4769}
4770
4771void
4772AllocEngine::getRemaining(const Lease4Ptr& lease, uint32_t& valid) {
4773 valid = 0;
4774 if (!lease || (lease->state_ != Lease::STATE_DEFAULT)) {
4775 return;
4776 }
4777 // Always remain infinite lifetime leases.
4778 if (lease->valid_lft_ == Lease::INFINITY_LFT) {
4779 valid = Lease::INFINITY_LFT;
4780 return;
4781 }
4782 time_t now = time(0);
4783 // Refuse time not going forward.
4784 if (lease->cltt_ > now) {
4785 return;
4786 }
4787 uint32_t age = now - lease->cltt_;
4788 // Already expired.
4789 if (age >= lease->valid_lft_) {
4790 return;
4791 }
4792 valid = lease->valid_lft_ - age;
4793}
4794
4796AllocEngine::renewLease4(const Lease4Ptr& lease,
4798 if (!lease) {
4799 isc_throw(BadValue, "null lease specified for renewLease4");
4800 }
4801
4802 // Let's keep the old data. This is essential if we are using memfile
4803 // (the lease returned points directly to the lease4 object in the database)
4804 // We'll need it if we want to skip update (i.e. roll back renewal)
4806 Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
4807 ctx.old_lease_.reset(new Lease4(*old_values));
4808
4809 // Update the lease with the information from the context.
4810 // If there was no significant changes, try reuse.
4811 lease->reuseable_valid_lft_ = 0;
4812 if (!updateLease4Information(lease, ctx)) {
4813 setLeaseReusable(lease, ctx);
4814 }
4815
4816 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4817 // If the lease is expired we have to reclaim it before
4818 // re-assigning it to the client. The lease reclamation
4819 // involves execution of hooks and DNS update.
4820 if (ctx.old_lease_->expired()) {
4821 reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
4822 }
4823
4824 lease->state_ = Lease::STATE_DEFAULT;
4825 }
4826
4827 bool skip = false;
4828 // Execute all callouts registered for lease4_renew.
4829 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_renew_)) {
4830
4831 // Use the RAII wrapper to make sure that the callout handle state is
4832 // reset when this object goes out of scope. All hook points must do
4833 // it to prevent possible circular dependency between the callout
4834 // handle and its arguments.
4835 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4836
4837 // Enable copying options from the packet within hook library.
4838 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4839
4840 // Subnet from which we do the allocation. Convert the general subnet
4841 // pointer to a pointer to a Subnet4. Note that because we are using
4842 // boost smart pointers here, we need to do the cast using the boost
4843 // version of dynamic_pointer_cast.
4844 ConstSubnet4Ptr subnet4 =
4845 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4846
4847 // Pass the parameters. Note the clientid is passed only if match-client-id
4848 // is set. This is done that way, because the lease4-renew hook point is
4849 // about renewing a lease and the configuration parameter says the
4850 // client-id should be ignored. Hence no clientid value if match-client-id
4851 // is false.
4852 ctx.callout_handle_->setArgument("query4", ctx.query_);
4853 ctx.callout_handle_->setArgument("subnet4", subnet4);
4854 ctx.callout_handle_->setArgument("clientid", subnet4->getMatchClientId() ?
4855 ctx.clientid_ : ClientIdPtr());
4856 ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
4857
4858 // Pass the lease to be updated
4859 ctx.callout_handle_->setArgument("lease4", lease);
4860
4861 // Call all installed callouts
4862 HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
4863 *ctx.callout_handle_);
4864
4865 // Callouts decided to skip the next processing step. The next
4866 // processing step would actually renew the lease, so skip at this
4867 // stage means "keep the old lease as it is".
4868 if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4869 skip = true;
4872 }
4873
4875 }
4876
4877 if ((!ctx.fake_allocation_ || ctx.offer_lft_) && !skip && (lease->reuseable_valid_lft_ == 0)) {
4878 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, lease->addr_, false);
4879 if (pool) {
4880 lease->pool_id_ = pool->getID();
4881 }
4882
4883 // for REQUEST we do update the lease
4885
4886 // We need to account for the re-assignment of the lease.
4887 if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
4889 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4890 "assigned-addresses"),
4891 static_cast<int64_t>(1));
4892
4894 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4895 "cumulative-assigned-addresses"),
4896 static_cast<int64_t>(1));
4897
4898 if (pool) {
4900 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4901 StatsMgr::generateName("pool", pool->getID(),
4902 "assigned-addresses")),
4903 static_cast<int64_t>(1));
4904
4906 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4907 StatsMgr::generateName("pool", pool->getID(),
4908 "cumulative-assigned-addresses")),
4909 static_cast<int64_t>(1));
4910 }
4911
4912 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(1));
4913
4914 StatsMgr::instance().addValue("cumulative-assigned-addresses", static_cast<int64_t>(1));
4915 }
4916 }
4917 if (skip) {
4918 // Rollback changes (really useful only for memfile)
4920 *lease = *old_values;
4921 }
4922
4923 return (lease);
4924}
4925
4927AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
4928 AllocEngine::ClientContext4& ctx,
4929 CalloutHandle::CalloutNextStep& callout_status) {
4930 if (!expired) {
4931 isc_throw(BadValue, "null lease specified for reuseExpiredLease");
4932 }
4933
4934 if (!ctx.subnet_) {
4935 isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
4936 }
4937
4938 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4939 // The expired lease needs to be reclaimed before it can be reused.
4940 // This includes declined leases for which probation period has
4941 // elapsed.
4942 reclaimExpiredLease(expired, ctx.callout_handle_);
4943 expired->state_ = Lease::STATE_DEFAULT;
4944 }
4945
4946 expired->reuseable_valid_lft_ = 0;
4947 static_cast<void>(updateLease4Information(expired, ctx));
4948
4951 .arg(ctx.query_->getLabel())
4952 .arg(expired->toText());
4953
4954 // Let's execute all callouts registered for lease4_select
4955 if (ctx.callout_handle_ &&
4956 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4957
4958 // Enable copying options from the packet within hook library.
4959 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4960
4961 // Use the RAII wrapper to make sure that the callout handle state is
4962 // reset when this object goes out of scope. All hook points must do
4963 // it to prevent possible circular dependency between the callout
4964 // handle and its arguments.
4965 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4966
4967 // Pass necessary arguments
4968 // Pass the original client query
4969 ctx.callout_handle_->setArgument("query4", ctx.query_);
4970
4971 // Subnet from which we do the allocation. Convert the general subnet
4972 // pointer to a pointer to a Subnet4. Note that because we are using
4973 // boost smart pointers here, we need to do the cast using the boost
4974 // version of dynamic_pointer_cast.
4975 ConstSubnet4Ptr subnet4 =
4976 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4977 ctx.callout_handle_->setArgument("subnet4", subnet4);
4978
4979 // Is this solicit (fake = true) or request (fake = false)
4980 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4981 ctx.callout_handle_->setArgument("offer_lft", ctx.offer_lft_);
4982
4983 // The lease that will be assigned to a client
4984 ctx.callout_handle_->setArgument("lease4", expired);
4985
4986 // Call the callouts
4987 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4988
4989 callout_status = ctx.callout_handle_->getStatus();
4990
4991 // Callouts decided to skip the action. This means that the lease is not
4992 // assigned, so the client will get NoAddrAvail as a result. The lease
4993 // won't be inserted into the database.
4994 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4997 return (Lease4Ptr());
4998 }
4999
5001
5002 // Let's use whatever callout returned. Hopefully it is the same lease
5003 // we handed to it.
5004 ctx.callout_handle_->getArgument("lease4", expired);
5005 }
5006
5007 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
5008 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, expired->addr_, false);
5009 if (pool) {
5010 expired->pool_id_ = pool->getID();
5011 }
5012 // for REQUEST we do update the lease
5014
5015 // We need to account for the re-assignment of the lease.
5017 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5018 "assigned-addresses"),
5019 static_cast<int64_t>(1));
5020
5022 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5023 "cumulative-assigned-addresses"),
5024 static_cast<int64_t>(1));
5025
5026 if (pool) {
5028 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5029 StatsMgr::generateName("pool", pool->getID(),
5030 "assigned-addresses")),
5031 static_cast<int64_t>(1));
5032
5034 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5035 StatsMgr::generateName("pool", pool->getID(),
5036 "cumulative-assigned-addresses")),
5037 static_cast<int64_t>(1));
5038 }
5039
5040 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(1));
5041
5042 StatsMgr::instance().addValue("cumulative-assigned-addresses", static_cast<int64_t>(1));
5043 }
5044
5045 // We do nothing for SOLICIT. We'll just update database when
5046 // the client gets back to us with REQUEST message.
5047
5048 // it's not really expired at this stage anymore - let's return it as
5049 // an updated lease
5050 return (expired);
5051}
5052
5054AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
5055 CalloutHandle::CalloutNextStep& callout_status) {
5056 ctx.conflicting_lease_.reset();
5057
5058 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
5059 if (exist_lease) {
5060 if (exist_lease->expired()) {
5061 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
5062 // reuseExpiredLease4() will reclaim the use which will
5063 // queue an NCR remove it needed. Clear the DNS fields in
5064 // the old lease to avoid a redundant remove in server logic.
5065 ctx.old_lease_->hostname_.clear();
5066 ctx.old_lease_->fqdn_fwd_ = false;
5067 ctx.old_lease_->fqdn_rev_ = false;
5068 return (reuseExpiredLease4(exist_lease, ctx, callout_status));
5069
5070 } else {
5071 // If there is a lease and it is not expired, pass this lease back
5072 // to the caller in the context. The caller may need to know
5073 // which lease we're conflicting with.
5074 ctx.conflicting_lease_ = exist_lease;
5075 }
5076
5077 } else {
5078 return (createLease4(ctx, candidate, callout_status));
5079 }
5080 return (Lease4Ptr());
5081}
5082
5084AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
5085 Lease4Ptr new_lease;
5086 ConstSubnet4Ptr subnet = ctx.subnet_;
5087
5088 // Need to check if the subnet belongs to a shared network. If so,
5089 // we might be able to find a better subnet for lease allocation,
5090 // for which it is more likely that there are some leases available.
5091 // If we stick to the selected subnet, we may end up walking over
5092 // the entire subnet (or more subnets) to discover that the address
5093 // pools have been exhausted. Using a subnet from which an address
5094 // was assigned most recently is an optimization which increases
5095 // the likelihood of starting from the subnet which address pools
5096 // are not exhausted.
5097 SharedNetwork4Ptr network;
5098 ctx.subnet_->getSharedNetwork(network);
5099 if (network) {
5100 // This would try to find a subnet with the same set of classes
5101 // as the current subnet, but with the more recent "usage timestamp".
5102 // This timestamp is only updated for the allocations made with an
5103 // allocator (unreserved lease allocations), not the static
5104 // allocations or requested addresses.
5105 ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
5106 }
5107
5108 // We have the choice in the order checking the lease and
5109 // the reservation. The default is to begin by the lease
5110 // if the multi-threading is disabled.
5111 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
5112
5113 ConstSubnet4Ptr original_subnet = subnet;
5114
5115 uint128_t total_attempts = 0;
5116
5117 // The following counter tracks the number of subnets with matching client
5118 // classes from which the allocation engine attempted to assign leases.
5119 uint64_t subnets_with_unavail_leases = 0;
5120 // The following counter tracks the number of subnets in which there were
5121 // no matching pools for the client.
5122 uint64_t subnets_with_unavail_pools = 0;
5123
5124 auto const& classes = ctx.query_->getClasses();
5125
5126 while (subnet) {
5127 ClientIdPtr client_id;
5128 if (subnet->getMatchClientId()) {
5129 client_id = ctx.clientid_;
5130 }
5131
5132 uint128_t const possible_attempts =
5133 subnet->getPoolCapacity(Lease::TYPE_V4, classes);
5134
5135 // If the number of tries specified in the allocation engine constructor
5136 // is set to 0 (unlimited) or the pools capacity is lower than that number,
5137 // let's use the pools capacity as the maximum number of tries. Trying
5138 // more than the actual pools capacity is a waste of time. If the specified
5139 // number of tries is lower than the pools capacity, use that number.
5140 uint128_t const max_attempts =
5141 (attempts_ == 0 || possible_attempts < attempts_) ?
5142 possible_attempts :
5143 attempts_;
5144
5145 if (max_attempts > 0) {
5146 // If max_attempts is greater than 0, there are some pools in this subnet
5147 // from which we can potentially get a lease.
5148 ++subnets_with_unavail_leases;
5149 } else {
5150 // If max_attempts is 0, it is an indication that there are no pools
5151 // in the subnet from which we can get a lease.
5152 ++subnets_with_unavail_pools;
5153 }
5154
5155 bool exclude_first_last_24 = ((subnet->get().second <= 24) &&
5156 CfgMgr::instance().getCurrentCfg()->getExcludeFirstLast24());
5157
5159
5160 for (uint128_t i = 0; i < max_attempts; ++i) {
5161
5162 ++total_attempts;
5163
5164 auto allocator = subnet->getAllocator(Lease::TYPE_V4);
5165 IOAddress candidate = allocator->pickAddress(classes,
5166 client_id,
5167 ctx.requested_address_);
5168
5169 // An allocator may return zero address when it has pools exhausted.
5170 if (candidate.isV4Zero()) {
5171 break;
5172 }
5173
5174 if (exclude_first_last_24) {
5175 // Exclude .0 and .255 addresses.
5176 auto const& bytes = candidate.toBytes();
5177 if ((bytes.size() != 4) ||
5178 (bytes[3] == 0) || (bytes[3] == 255U)) {
5179 // Don't allocate.
5180 continue;
5181 }
5182 }
5183
5184 // First check for reservation when it is the choice.
5185 if (check_reservation_first && addressReserved(candidate, ctx)) {
5186 // Don't allocate.
5187 continue;
5188 }
5189
5190 // Check if the resource is busy i.e. can be being allocated
5191 // by another thread to another client.
5192 ResourceHandler4 resource_handler;
5193 if (MultiThreadingMgr::instance().getMode() &&
5194 !resource_handler.tryLock4(candidate)) {
5195 // Don't allocate.
5196 continue;
5197 }
5198
5199 // Check for an existing lease for the candidate address.
5200 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
5201 if (!exist_lease) {
5202 // No existing lease, is it reserved?
5203 if (check_reservation_first || !addressReserved(candidate, ctx)) {
5204 // Not reserved use it.
5205 new_lease = createLease4(ctx, candidate, callout_status);
5206 }
5207 } else {
5208 // An lease exists, is expired, and not reserved use it.
5209 if (exist_lease->expired() &&
5210 (check_reservation_first || !addressReserved(candidate, ctx))) {
5211 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
5212 new_lease = reuseExpiredLease4(exist_lease, ctx, callout_status);
5213 }
5214 }
5215
5216 // We found a lease we can use, return it.
5217 if (new_lease) {
5218 return (new_lease);
5219 }
5220
5221 if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
5222 // Don't retry when the callout status is not continue.
5223 subnet.reset();
5224 break;
5225 }
5226 }
5227
5228 // This pointer may be set to NULL if hooks set SKIP status.
5229 if (subnet) {
5230 subnet = subnet->getNextSubnet(original_subnet, classes);
5231
5232 if (subnet) {
5233 ctx.subnet_ = subnet;
5234 }
5235 }
5236 }
5237
5238 if (network) {
5239 // The client is in the shared network. Let's log the high level message
5240 // indicating which shared network the client belongs to.
5242 .arg(ctx.query_->getLabel())
5243 .arg(network->getName())
5244 .arg(subnets_with_unavail_leases)
5245 .arg(subnets_with_unavail_pools);
5246 StatsMgr::instance().addValue("v4-allocation-fail-shared-network",
5247 static_cast<int64_t>(1));
5249 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5250 "v4-allocation-fail-shared-network"),
5251 static_cast<int64_t>(1));
5252 } else {
5253 // The client is not connected to a shared network. It is connected
5254 // to a subnet. Let's log some details about the subnet.
5255 std::string shared_network = ctx.subnet_->getSharedNetworkName();
5256 if (shared_network.empty()) {
5257 shared_network = "(none)";
5258 }
5260 .arg(ctx.query_->getLabel())
5261 .arg(ctx.subnet_->toText())
5262 .arg(ctx.subnet_->getID())
5263 .arg(shared_network);
5264 StatsMgr::instance().addValue("v4-allocation-fail-subnet",
5265 static_cast<int64_t>(1));
5267 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5268 "v4-allocation-fail-subnet"),
5269 static_cast<int64_t>(1));
5270 }
5271 if (total_attempts == 0) {
5272 // In this case, it seems that none of the pools in the subnets could
5273 // be used for that client, both in case the client is connected to
5274 // a shared network or to a single subnet. Apparently, the client was
5275 // rejected to use the pools because of the client classes' mismatch.
5277 .arg(ctx.query_->getLabel());
5278 StatsMgr::instance().addValue("v4-allocation-fail-no-pools",
5279 static_cast<int64_t>(1));
5281 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5282 "v4-allocation-fail-no-pools"),
5283 static_cast<int64_t>(1));
5284 } else {
5285 // This is an old log message which provides a number of attempts
5286 // made by the allocation engine to allocate a lease. The only case
5287 // when we don't want to log this message is when the number of
5288 // attempts is zero (condition above), because it would look silly.
5290 .arg(ctx.query_->getLabel())
5291 .arg(total_attempts);
5292 StatsMgr::instance().addValue("v4-allocation-fail",
5293 static_cast<int64_t>(1));
5295 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5296 "v4-allocation-fail"),
5297 static_cast<int64_t>(1));
5298 }
5299
5300 if (!classes.empty()) {
5302 .arg(ctx.query_->getLabel())
5303 .arg(classes.toText());
5304 StatsMgr::instance().addValue("v4-allocation-fail-classes",
5305 static_cast<int64_t>(1));
5307 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5308 "v4-allocation-fail-classes"),
5309 static_cast<int64_t>(1));
5310 }
5311
5312 return (new_lease);
5313}
5314
5315bool
5316AllocEngine::updateLease4Information(const Lease4Ptr& lease,
5317 AllocEngine::ClientContext4& ctx) const {
5318 bool changed = false;
5319 if (lease->subnet_id_ != ctx.subnet_->getID()) {
5320 changed = true;
5321 lease->subnet_id_ = ctx.subnet_->getID();
5322 }
5323 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
5324 (ctx.hwaddr_ &&
5325 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
5326 changed = true;
5327 lease->hwaddr_ = ctx.hwaddr_;
5328 }
5329 if (ctx.subnet_->getMatchClientId() && ctx.clientid_) {
5330 if (!lease->client_id_ || (*ctx.clientid_ != *lease->client_id_)) {
5331 changed = true;
5332 lease->client_id_ = ctx.clientid_;
5333 }
5334 } else if (lease->client_id_) {
5335 changed = true;
5336 lease->client_id_ = ClientIdPtr();
5337 }
5338 uint32_t remain_lft(0);
5339 getRemaining(lease, remain_lft);
5340 lease->cltt_ = time(0);
5341
5342 // Get the context appropriate valid lifetime.
5343 lease->valid_lft_ = ctx.offer_lft_;
5344 if (!lease->valid_lft_) {
5345 if (useMinValidLft(ctx, lease->addr_)) {
5346 lease->valid_lft_ = remain_lft;
5347 getMinValidLft(ctx, lease->valid_lft_);
5348 } else {
5349 lease->valid_lft_ = getValidLft(ctx);
5350 }
5351 }
5352
5353 // Valid lifetime has changed.
5354 if (lease->valid_lft_ != lease->current_valid_lft_) {
5355 changed = true;
5356 }
5357
5358 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
5359 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
5360 (lease->hostname_ != ctx.hostname_)) {
5361 changed = true;
5362 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
5363 lease->fqdn_rev_ = ctx.rev_dns_update_;
5364 lease->hostname_ = ctx.hostname_;
5365 }
5366
5367 // Add (update) the extended information on the lease.
5368 if (updateLease4ExtendedInfo(lease, ctx)) {
5369 changed = true;
5370 }
5371
5372 return (changed);
5373}
5374
5375bool
5377 const AllocEngine::ClientContext4& ctx) const {
5378 bool changed = false;
5379
5380 // If storage is not enabled then punt.
5381 if (!ctx.subnet_->getStoreExtendedInfo()) {
5382 return (changed);
5383 }
5384
5385 // Look for relay agent information option (option 82)
5386 OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS);
5387 if (!rai) {
5388 // Pkt4 doesn't have it, so nothing to store (or update).
5389 return (changed);
5390 }
5391
5392 // Check if the RAI was recovered from stashed agent options.
5394 getConfiguredGlobal(CfgGlobals::STASH_AGENT_OPTIONS);
5395 if (sao && (sao->getType() == data::Element::boolean) &&
5396 sao->boolValue() && ctx.query_->inClass("STASH_AGENT_OPTIONS")) {
5397 return (changed);
5398 }
5399
5400 // Create a StringElement with the hex string for relay-agent-info.
5401 ElementPtr relay_agent(new StringElement(rai->toHexString()));
5402
5403 // Now we wrap the agent info in a map. This allows for future expansion.
5404 ElementPtr extended_info = Element::createMap();
5405 extended_info->set("sub-options", relay_agent);
5406
5407 OptionPtr remote_id = rai->getOption(RAI_OPTION_REMOTE_ID);
5408 if (remote_id) {
5409 std::vector<uint8_t> bytes = remote_id->toBinary();
5410 lease->remote_id_ = bytes;
5411 if (bytes.size() > 0) {
5412 extended_info->set("remote-id",
5414 }
5415 }
5416
5417 OptionPtr relay_id = rai->getOption(RAI_OPTION_RELAY_ID);
5418 if (relay_id) {
5419 std::vector<uint8_t> bytes = relay_id->toBinary(false);
5420 lease->relay_id_ = bytes;
5421 if (bytes.size() > 0) {
5422 extended_info->set("relay-id",
5424 }
5425 }
5426
5427 // Return true if the extended-info on the lease changed.
5428 return (lease->updateUserContextISC("relay-agent-info", extended_info));
5429}
5430
5431void
5433 const AllocEngine::ClientContext6& ctx) const {
5434 // The extended info action is a transient value but be safe so reset it.
5435 lease->extended_info_action_ = Lease6::ACTION_IGNORE;
5436
5437 // If storage is not enabled then punt.
5438 if (!ctx.subnet_->getStoreExtendedInfo()) {
5439 return;
5440 }
5441
5442 // If we do not have relay information, then punt.
5443 if (ctx.query_->relay_info_.empty()) {
5444 return;
5445 }
5446
5447 // We need to convert the vector of RelayInfo instances in
5448 // into an Element hierarchy like this:
5449 // "relay-info": [
5450 // {
5451 // "hop": 123,
5452 // "link": "2001:db8::1",
5453 // "peer": "2001:db8::2",
5454 // "options": "0x..."
5455 // },..]
5456 //
5457 ElementPtr extended_info = Element::createList();
5458 for (auto const& relay : ctx.query_->relay_info_) {
5459 ElementPtr relay_elem = Element::createMap();
5460 relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
5461 relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
5462 relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
5463
5464 // If there are relay options, we'll pack them into a buffer and then
5465 // convert that into a hex string. If there are no options, we omit
5466 // then entry.
5467 if (!relay.options_.empty()) {
5468 OutputBuffer buf(128);
5469 LibDHCP::packOptions6(buf, relay.options_);
5470
5471 if (buf.getLength() > 0) {
5472 const uint8_t* cp = buf.getData();
5473 std::vector<uint8_t> bytes;
5474 std::stringstream ss;
5475
5476 bytes.assign(cp, cp + buf.getLength());
5477 ss << "0x" << encode::encodeHex(bytes);
5478 relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
5479 }
5480
5481 auto remote_id_it = relay.options_.find(D6O_REMOTE_ID);
5482 if (remote_id_it != relay.options_.end()) {
5483 OptionPtr remote_id = remote_id_it->second;
5484 if (remote_id) {
5485 std::vector<uint8_t> bytes = remote_id->toBinary();
5486 if (bytes.size() > 0) {
5487 relay_elem->set("remote-id",
5489 }
5490 }
5491 }
5492
5493 auto relay_id_it = relay.options_.find(D6O_RELAY_ID);
5494 if (relay_id_it != relay.options_.end()) {
5495 OptionPtr relay_id = relay_id_it->second;
5496 if (relay_id) {
5497 std::vector<uint8_t> bytes = relay_id->toBinary(false);
5498 if (bytes.size() > 0) {
5499 relay_elem->set("relay-id",
5501 }
5502 }
5503 }
5504 }
5505
5506 extended_info->add(relay_elem);
5507 }
5508
5509 // If extended info changed set the action to UPDATE.
5510 if (lease->updateUserContextISC("relay-info", extended_info)) {
5511 lease->extended_info_action_ = Lease6::ACTION_UPDATE;
5512 }
5513}
5514
5515void
5516AllocEngine::setLeaseReusable(const Lease4Ptr& lease,
5517 const ClientContext4& ctx) const {
5518 // Sanity.
5519 lease->reuseable_valid_lft_ = 0;
5520 const ConstSubnet4Ptr& subnet = ctx.subnet_;
5521 if (!subnet) {
5522 return;
5523 }
5524 if (lease->state_ != Lease::STATE_DEFAULT) {
5525 return;
5526 }
5527
5528 // Always reuse infinite lifetime leases.
5529 if (lease->valid_lft_ == Lease::INFINITY_LFT) {
5530 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
5531 return;
5532 }
5533
5534 // Refuse time not going forward.
5535 if (lease->cltt_ < lease->current_cltt_) {
5536 return;
5537 }
5538
5539 uint32_t age = lease->cltt_ - lease->current_cltt_;
5540 // Already expired.
5541 if (age >= lease->current_valid_lft_) {
5542 return;
5543 }
5544
5545 // Try cache max age.
5546 uint32_t max_age = 0;
5547 if (!subnet->getCacheMaxAge().unspecified()) {
5548 max_age = subnet->getCacheMaxAge().get();
5549 if ((max_age == 0) || (age > max_age)) {
5550 return;
5551 }
5552 }
5553
5554 // Try cache threshold.
5555 if (!subnet->getCacheThreshold().unspecified()) {
5556 double threshold = subnet->getCacheThreshold().get();
5557 if ((threshold <= 0.) || (threshold > 1.)) {
5558 return;
5559 }
5560 max_age = lease->valid_lft_ * threshold;
5561 if (age > max_age) {
5562 return;
5563 }
5564 }
5565
5566 // No cache.
5567 if (max_age == 0) {
5568 return;
5569 }
5570
5571 // Seems to be reusable.
5572 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
5573}
5574
5575void
5576AllocEngine::setLeaseReusable(const Lease6Ptr& lease,
5577 uint32_t current_preferred_lft,
5578 const ClientContext6& ctx) const {
5579 // Sanity.
5580 lease->reuseable_valid_lft_ = 0;
5581 lease->reuseable_preferred_lft_ = 0;
5582 const ConstSubnet6Ptr& subnet = ctx.subnet_;
5583 if (!subnet) {
5584 return;
5585 }
5586 if (lease->state_ != Lease::STATE_DEFAULT) {
5587 return;
5588 }
5589
5590 // Refuse time not going forward.
5591 if (lease->cltt_ < lease->current_cltt_) {
5592 return;
5593 }
5594
5595 uint32_t age = lease->cltt_ - lease->current_cltt_;
5596 // Already expired.
5597 if (age >= lease->current_valid_lft_) {
5598 return;
5599 }
5600
5601 // Try cache max age.
5602 uint32_t max_age = 0;
5603 if (!subnet->getCacheMaxAge().unspecified()) {
5604 max_age = subnet->getCacheMaxAge().get();
5605 if ((max_age == 0) || (age > max_age)) {
5606 return;
5607 }
5608 }
5609
5610 // Try cache threshold.
5611 if (!subnet->getCacheThreshold().unspecified()) {
5612 double threshold = subnet->getCacheThreshold().get();
5613 if ((threshold <= 0.) || (threshold > 1.)) {
5614 return;
5615 }
5616 max_age = lease->valid_lft_ * threshold;
5617 if (age > max_age) {
5618 return;
5619 }
5620 }
5621
5622 // No cache.
5623 if (max_age == 0) {
5624 return;
5625 }
5626
5627 // Seems to be reusable.
5628 if ((current_preferred_lft == Lease::INFINITY_LFT) ||
5629 (current_preferred_lft == 0)) {
5630 // Keep these values.
5631 lease->reuseable_preferred_lft_ = current_preferred_lft;
5632 } else if (current_preferred_lft > age) {
5633 lease->reuseable_preferred_lft_ = current_preferred_lft - age;
5634 } else {
5635 // Can be a misconfiguration so stay safe...
5636 return;
5637 }
5638 if (lease->current_valid_lft_ == Lease::INFINITY_LFT) {
5639 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
5640 } else {
5641 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
5642 }
5643}
5644
5645} // namespace dhcp
5646} // 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:300
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:355
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:350
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:111
bool empty() const
Check if classes is empty.
Definition classify.h:171
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:703
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:355
const uint8_t * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition buffer.h:407
size_t getLength() const
Return the length of data written in the buffer.
Definition buffer.h:421
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:1543
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