Kea 3.3.3
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 if (lease->expired()) {
2665 // Force refresh of expired leases.
2666 lease->valid_lft_ = 0;
2667 lease->preferred_lft_ = 0;
2668 }
2669
2670 lease->reuseable_valid_lft_ = 0;
2671 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2672 lease->fqdn_rev_ = ctx.rev_dns_update_;
2673 lease->hostname_ = ctx.hostname_;
2674 uint32_t current_preferred_lft = lease->preferred_lft_;
2675 if (lease->valid_lft_ == 0) {
2676 // The lease was expired by a release: reset zero lifetimes.
2677 lease->preferred_lft_ = 0;
2678 if ((lease->type_ == Lease::TYPE_PD) &&
2679 useMinLifetimes6(ctx, lease->addr_, lease->prefixlen_)) {
2680 getMinLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2681 } else {
2682 getLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2683 }
2684 }
2685 if (!ctx.fake_allocation_) {
2686 bool update_stats = false;
2687
2688 if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED || lease->state_ == Lease::STATE_RELEASED) {
2689 // Transition lease state to default (aka assigned)
2690 lease->state_ = Lease::STATE_DEFAULT;
2691
2692 // If the lease is in the current subnet we need to account
2693 // for the re-assignment of the lease.
2694 if (inAllowedPool(ctx, ctx.currentIA().type_,
2695 lease->addr_, true)) {
2696 update_stats = true;
2697 }
2698 }
2699
2700 bool fqdn_changed = ((lease->type_ != Lease::TYPE_PD) &&
2701 !(lease->hasIdenticalFqdn(*lease_it)));
2702
2703 lease->cltt_ = time(NULL);
2704 if (!fqdn_changed) {
2705 setLeaseReusable(lease, current_preferred_lft, ctx);
2706 }
2707
2708 if (lease->reuseable_valid_lft_ == 0) {
2709 ctx.currentIA().changed_leases_.push_back(lease_it);
2711 } else {
2712 // Server needs to know about resused leases to avoid DNS updates.
2713 ctx.currentIA().reused_leases_.push_back(lease_it);
2714 }
2715
2716 if (update_stats) {
2718 StatsMgr::generateName("subnet", lease->subnet_id_,
2719 ctx.currentIA().type_ == Lease::TYPE_NA ?
2720 "assigned-nas" : "assigned-pds"),
2721 static_cast<int64_t>(1));
2722
2724 StatsMgr::generateName("subnet", lease->subnet_id_,
2725 ctx.currentIA().type_ == Lease::TYPE_NA ?
2726 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2727 static_cast<int64_t>(1));
2728
2729 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2730 if (pool) {
2732 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2733 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2734 "pool" : "pd-pool", pool->getID(),
2735 ctx.currentIA().type_ == Lease::TYPE_NA ?
2736 "assigned-nas" : "assigned-pds")),
2737 static_cast<int64_t>(1));
2738
2740 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2741 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2742 "pool" : "pd-pool", pool->getID(),
2743 ctx.currentIA().type_ == Lease::TYPE_NA ?
2744 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2745 static_cast<int64_t>(1));
2746 }
2747
2748 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2749 "assigned-nas" : "assigned-pds",
2750 static_cast<int64_t>(1));
2751
2752 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2753 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2754 static_cast<int64_t>(1));
2755 }
2756 }
2757
2758 updated_leases.push_back(lease);
2759 }
2760
2761 return (updated_leases);
2762}
2763
2764void
2766 const uint16_t timeout,
2767 const bool remove_lease,
2768 const uint16_t max_unwarned_cycles) {
2769
2772 .arg(max_leases)
2773 .arg(timeout);
2774
2775 try {
2776 reclaimExpiredLeases6Internal(max_leases, timeout, remove_lease,
2777 max_unwarned_cycles);
2778 } catch (const std::exception& ex) {
2781 .arg(ex.what());
2782 }
2783}
2784
2785void
2787 const uint16_t timeout,
2788 const bool remove_lease,
2789 const uint16_t max_unwarned_cycles) {
2790
2791 // Create stopwatch and automatically start it to measure the time
2792 // taken by the routine.
2793 util::Stopwatch stopwatch;
2794
2795 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2796
2797 // This value indicates if we have been able to deal with all expired
2798 // leases in this pass.
2799 bool incomplete_reclamation = false;
2800 Lease6Collection leases;
2801 // The value of 0 has a special meaning - reclaim all.
2802 if (max_leases > 0) {
2803 // If the value is non-zero, the caller has limited the number of
2804 // leases to reclaim. We obtain one lease more to see if there will
2805 // be still leases left after this pass.
2806 lease_mgr.getExpiredLeases6(leases, max_leases + 1);
2807 // There are more leases expired leases than we will process in this
2808 // pass, so we should mark it as an incomplete reclamation. We also
2809 // remove this extra lease (which we don't want to process anyway)
2810 // from the collection.
2811 if (leases.size() > max_leases) {
2812 leases.pop_back();
2813 incomplete_reclamation = true;
2814 }
2815
2816 } else {
2817 // If there is no limitation on the number of leases to reclaim,
2818 // we will try to process all. Hence, we don't mark it as incomplete
2819 // reclamation just yet.
2820 lease_mgr.getExpiredLeases6(leases, max_leases);
2821 }
2822
2823 // Do not initialize the callout handle until we know if there are any
2824 // lease6_expire callouts installed.
2825 CalloutHandlePtr callout_handle;
2826 if (!leases.empty() &&
2827 HooksManager::calloutsPresent(Hooks.hook_index_lease6_expire_)) {
2828 callout_handle = HooksManager::createCalloutHandle();
2829 }
2830
2831 size_t leases_processed = 0;
2832 for (auto const& lease : leases) {
2833
2834 try {
2835 // Reclaim the lease.
2836 if (MultiThreadingMgr::instance().getMode()) {
2837 // The reclamation is exclusive of packet processing.
2838 WriteLockGuard exclusive(rw_mutex_);
2839
2840 reclaimExpiredLease(lease, remove_lease, callout_handle);
2841 ++leases_processed;
2842 } else {
2843 reclaimExpiredLease(lease, remove_lease, callout_handle);
2844 ++leases_processed;
2845 }
2846
2847 } catch (const std::exception& ex) {
2849 .arg(lease->addr_.toText())
2850 .arg(ex.what());
2851 }
2852
2853 // Check if we have hit the timeout for running reclamation routine and
2854 // return if we have. We're checking it here, because we always want to
2855 // allow reclaiming at least one lease.
2856 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2857 // Timeout. This will likely mean that we haven't been able to process
2858 // all leases we wanted to process. The reclamation pass will be
2859 // probably marked as incomplete.
2860 if (!incomplete_reclamation) {
2861 if (leases_processed < leases.size()) {
2862 incomplete_reclamation = true;
2863 }
2864 }
2865
2868 .arg(timeout);
2869 break;
2870 }
2871 }
2872
2873 // Stop measuring the time.
2874 stopwatch.stop();
2875
2876 // Mark completion of the lease reclamation routine and present some stats.
2879 .arg(leases_processed)
2880 .arg(stopwatch.logFormatTotalDuration());
2881
2882 // Check if this was an incomplete reclamation and increase the number of
2883 // consecutive incomplete reclamations.
2884 if (incomplete_reclamation) {
2885 ++incomplete_v6_reclamations_;
2886 // If the number of incomplete reclamations is beyond the threshold, we
2887 // need to issue a warning.
2888 if ((max_unwarned_cycles > 0) &&
2889 (incomplete_v6_reclamations_ > max_unwarned_cycles)) {
2891 .arg(max_unwarned_cycles);
2892 // We issued a warning, so let's now reset the counter.
2893 incomplete_v6_reclamations_ = 0;
2894 }
2895
2896 } else {
2897 // This was a complete reclamation, so let's reset the counter.
2898 incomplete_v6_reclamations_ = 0;
2899
2902 }
2903}
2904
2905void
2909 .arg(secs);
2910
2911 uint64_t deleted_leases = 0;
2912 try {
2913 // Try to delete leases from the lease database.
2914 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2915 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
2916
2917 } catch (const std::exception& ex) {
2919 .arg(ex.what());
2920 }
2921
2924 .arg(deleted_leases);
2925}
2926
2927void
2929 const uint16_t timeout,
2930 const bool remove_lease,
2931 const uint16_t max_unwarned_cycles) {
2932
2935 .arg(max_leases)
2936 .arg(timeout);
2937
2938 try {
2939 reclaimExpiredLeases4Internal(max_leases, timeout, remove_lease,
2940 max_unwarned_cycles);
2941 } catch (const std::exception& ex) {
2944 .arg(ex.what());
2945 }
2946}
2947
2948void
2950 const uint16_t timeout,
2951 const bool remove_lease,
2952 const uint16_t max_unwarned_cycles) {
2953
2954 // Create stopwatch and automatically start it to measure the time
2955 // taken by the routine.
2956 util::Stopwatch stopwatch;
2957
2958 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2959
2960 // This value indicates if we have been able to deal with all expired
2961 // leases in this pass.
2962 bool incomplete_reclamation = false;
2963 Lease4Collection leases;
2964 // The value of 0 has a special meaning - reclaim all.
2965 if (max_leases > 0) {
2966 // If the value is non-zero, the caller has limited the number of
2967 // leases to reclaim. We obtain one lease more to see if there will
2968 // be still leases left after this pass.
2969 lease_mgr.getExpiredLeases4(leases, max_leases + 1);
2970 // There are more leases expired leases than we will process in this
2971 // pass, so we should mark it as an incomplete reclamation. We also
2972 // remove this extra lease (which we don't want to process anyway)
2973 // from the collection.
2974 if (leases.size() > max_leases) {
2975 leases.pop_back();
2976 incomplete_reclamation = true;
2977 }
2978
2979 } else {
2980 // If there is no limitation on the number of leases to reclaim,
2981 // we will try to process all. Hence, we don't mark it as incomplete
2982 // reclamation just yet.
2983 lease_mgr.getExpiredLeases4(leases, max_leases);
2984 }
2985
2986 // Do not initialize the callout handle until we know if there are any
2987 // lease4_expire callouts installed.
2988 CalloutHandlePtr callout_handle;
2989 if (!leases.empty() &&
2990 HooksManager::calloutsPresent(Hooks.hook_index_lease4_expire_)) {
2991 callout_handle = HooksManager::createCalloutHandle();
2992 }
2993
2994 size_t leases_processed = 0;
2995 for (auto const& lease : leases) {
2996
2997 try {
2998 // Reclaim the lease.
2999 if (MultiThreadingMgr::instance().getMode()) {
3000 // The reclamation is exclusive of packet processing.
3001 WriteLockGuard exclusive(rw_mutex_);
3002
3003 reclaimExpiredLease(lease, remove_lease, callout_handle);
3004 ++leases_processed;
3005 } else {
3006 reclaimExpiredLease(lease, remove_lease, callout_handle);
3007 ++leases_processed;
3008 }
3009
3010 } catch (const std::exception& ex) {
3012 .arg(lease->addr_.toText())
3013 .arg(ex.what());
3014 }
3015
3016 // Check if we have hit the timeout for running reclamation routine and
3017 // return if we have. We're checking it here, because we always want to
3018 // allow reclaiming at least one lease.
3019 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
3020 // Timeout. This will likely mean that we haven't been able to process
3021 // all leases we wanted to process. The reclamation pass will be
3022 // probably marked as incomplete.
3023 if (!incomplete_reclamation) {
3024 if (leases_processed < leases.size()) {
3025 incomplete_reclamation = true;
3026 }
3027 }
3028
3031 .arg(timeout);
3032 break;
3033 }
3034 }
3035
3036 // Stop measuring the time.
3037 stopwatch.stop();
3038
3039 // Mark completion of the lease reclamation routine and present some stats.
3042 .arg(leases_processed)
3043 .arg(stopwatch.logFormatTotalDuration());
3044
3045 // Check if this was an incomplete reclamation and increase the number of
3046 // consecutive incomplete reclamations.
3047 if (incomplete_reclamation) {
3048 ++incomplete_v4_reclamations_;
3049 // If the number of incomplete reclamations is beyond the threshold, we
3050 // need to issue a warning.
3051 if ((max_unwarned_cycles > 0) &&
3052 (incomplete_v4_reclamations_ > max_unwarned_cycles)) {
3054 .arg(max_unwarned_cycles);
3055 // We issued a warning, so let's now reset the counter.
3056 incomplete_v4_reclamations_ = 0;
3057 }
3058
3059 } else {
3060 // This was a complete reclamation, so let's reset the counter.
3061 incomplete_v4_reclamations_ = 0;
3062
3065 }
3066}
3067
3068template<typename LeasePtrType>
3069void
3070AllocEngine::reclaimExpiredLease(const LeasePtrType& lease, const bool remove_lease,
3071 const CalloutHandlePtr& callout_handle) {
3072 reclaimExpiredLease(lease, remove_lease ? DB_RECLAIM_REMOVE : DB_RECLAIM_UPDATE,
3073 callout_handle);
3074}
3075
3076template<typename LeasePtrType>
3077void
3078AllocEngine::reclaimExpiredLease(const LeasePtrType& lease,
3079 const CalloutHandlePtr& callout_handle) {
3080 // This variant of the method is used by the code which allocates or
3081 // renews leases. It may be the case that the lease has already been
3082 // reclaimed, so there is nothing to do.
3083 if (!lease->stateExpiredReclaimed()) {
3084 reclaimExpiredLease(lease, DB_RECLAIM_LEAVE_UNCHANGED, callout_handle);
3085 }
3086}
3087
3088void
3089AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
3090 const DbReclaimMode& reclaim_mode,
3091 const CalloutHandlePtr& callout_handle) {
3092
3095 .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
3096 .arg(lease->addr_.toText())
3097 .arg(static_cast<int>(lease->prefixlen_));
3098
3099 // The skip flag indicates if the callouts have taken responsibility
3100 // for reclaiming the lease. The callout will set this to true if
3101 // it reclaims the lease itself. In this case the reclamation routine
3102 // will not update DNS nor update the database.
3103 bool skipped = false;
3104 bool released = (lease->state_ == Lease::STATE_RELEASED);
3105 bool registered = (lease->state_ == Lease::STATE_REGISTERED);
3106 if (callout_handle) {
3107
3108 // Use the RAII wrapper to make sure that the callout handle state is
3109 // reset when this object goes out of scope. All hook points must do
3110 // it to prevent possible circular dependency between the callout
3111 // handle and its arguments.
3112 ScopedCalloutHandleState callout_handle_state(callout_handle);
3113
3114 callout_handle->deleteAllArguments();
3115 callout_handle->setArgument("lease6", lease);
3116 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
3117
3118 HooksManager::callCallouts(Hooks.hook_index_lease6_expire_,
3119 *callout_handle);
3120
3121 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
3122 }
3123
3126
3127 if (!skipped) {
3128
3129 // Generate removal name change request for D2, if required.
3130 // This will return immediately if the DNS wasn't updated
3131 // when the lease was created.
3132 queueNCR(CHG_REMOVE, lease);
3133
3134 // Let's check if the lease that just expired is in DECLINED state.
3135 // If it is, we need to perform a couple extra steps.
3136 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
3137 if (lease->state_ == Lease::STATE_DECLINED) {
3138 // Do extra steps required for declined lease reclamation:
3139 // - call the recover hook
3140 // - bump decline-related stats
3141 // - log separate message
3142 // There's no point in keeping a declined lease after its
3143 // reclamation. A declined lease doesn't have any client
3144 // identifying information anymore. So we'll flag it for
3145 // removal unless the hook has set the skip flag.
3146 remove_lease = reclaimDeclined(lease);
3147 } else if (lease->state_ == Lease::STATE_REGISTERED) {
3148 if (reclaim_mode == DB_RECLAIM_LEAVE_UNCHANGED) {
3149 isc_throw(Unexpected, "attempt to reuse a registered lease");
3150 }
3151 // Remove (vs reclaim) expired registered leases.
3152 remove_lease = true;
3153 }
3154
3155 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
3156 // Reclaim the lease - depending on the configuration, set the
3157 // expired-reclaimed state or simply remove it.
3158 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3159 reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
3160 std::bind(&LeaseMgr::updateLease6,
3161 &lease_mgr, ph::_1));
3162 }
3163 }
3164
3165 // Update statistics.
3166
3167 // Increase total number of reclaimed leases.
3168 StatsMgr::instance().addValue("reclaimed-leases", static_cast<int64_t>(1));
3169
3170 // Increase number of reclaimed leases for a subnet.
3172 lease->subnet_id_,
3173 "reclaimed-leases"),
3174 static_cast<int64_t>(1));
3175
3176 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3177
3178 if (lease->type_ == Lease::TYPE_NA || lease->type_ == Lease::TYPE_PD) {
3179 if (subnet) {
3180 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
3181 if (pool) {
3183 StatsMgr::generateName("subnet", subnet->getID(),
3184 StatsMgr::generateName(lease->type_ == Lease::TYPE_NA ?
3185 "pool" : "pd-pool",
3186 pool->getID(), "reclaimed-leases")),
3187 static_cast<int64_t>(1));
3188 }
3189 }
3190 }
3191
3192 // Statistics must have been updated during the release.
3193 if (released) {
3194 return;
3195 }
3196
3197 // Decrease number of registered or assigned leases.
3198
3199 if (registered) {
3201 lease->subnet_id_,
3202 "registered-nas"),
3203 static_cast<int64_t>(-1));
3204 } else if (lease->type_ == Lease::TYPE_NA || lease->type_ == Lease::TYPE_PD) {
3205 // Decrease number of assigned addresses.
3206 StatsMgr::instance().addValue(lease->type_ == Lease::TYPE_NA ?
3207 "assigned-nas" : "assigned-pds",
3208 static_cast<int64_t>(-1));
3209
3211 lease->subnet_id_,
3212 lease->type_ == Lease::TYPE_NA ?
3213 "assigned-nas" : "assigned-pds"),
3214 static_cast<int64_t>(-1));
3215
3216 if (subnet) {
3217 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
3218 if (pool) {
3220 StatsMgr::generateName("subnet", subnet->getID(),
3221 StatsMgr::generateName(lease->type_ == Lease::TYPE_NA ?
3222 "pool" : "pd-pool", pool->getID(),
3223 lease->type_ == Lease::TYPE_NA ?
3224 "assigned-nas" : "assigned-pds")),
3225 static_cast<int64_t>(-1));
3226 }
3227 }
3228 }
3229}
3230
3231void
3232AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
3233 const DbReclaimMode& reclaim_mode,
3234 const CalloutHandlePtr& callout_handle) {
3235
3238 .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
3239 .arg(lease->addr_.toText());
3240
3241 // The skip flag indicates if the callouts have taken responsibility
3242 // for reclaiming the lease. The callout will set this to true if
3243 // it reclaims the lease itself. In this case the reclamation routine
3244 // will not update DNS nor update the database.
3245 bool skipped = false;
3246 bool released = (lease->state_ == Lease::STATE_RELEASED);
3247 if (callout_handle) {
3248
3249 // Use the RAII wrapper to make sure that the callout handle state is
3250 // reset when this object goes out of scope. All hook points must do
3251 // it to prevent possible circular dependency between the callout
3252 // handle and its arguments.
3253 ScopedCalloutHandleState callout_handle_state(callout_handle);
3254
3255 callout_handle->setArgument("lease4", lease);
3256 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
3257
3258 HooksManager::callCallouts(Hooks.hook_index_lease4_expire_,
3259 *callout_handle);
3260
3261 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
3262 }
3263
3266
3267 if (!skipped) {
3268
3269 // Generate removal name change request for D2, if required.
3270 // This will return immediately if the DNS wasn't updated
3271 // when the lease was created.
3272 queueNCR(CHG_REMOVE, lease);
3273 // Clear DNS fields so we avoid redundant removes.
3274 lease->hostname_.clear();
3275 lease->fqdn_fwd_ = false;
3276 lease->fqdn_rev_ = false;
3277
3278 // Let's check if the lease that just expired is in DECLINED state.
3279 // If it is, we need to perform a couple extra steps.
3280 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
3281 if (lease->state_ == Lease::STATE_DECLINED) {
3282 // Do extra steps required for declined lease reclamation:
3283 // - call the recover hook
3284 // - bump decline-related stats
3285 // - log separate message
3286 // There's no point in keeping a declined lease after its
3287 // reclamation. A declined lease doesn't have any client
3288 // identifying information anymore. So we'll flag it for
3289 // removal unless the hook has set the skip flag.
3290 remove_lease = reclaimDeclined(lease);
3291 }
3292
3293 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
3294 // Reclaim the lease - depending on the configuration, set the
3295 // expired-reclaimed state or simply remove it.
3296 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3297 reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
3298 std::bind(&LeaseMgr::updateLease4,
3299 &lease_mgr, ph::_1));
3300 }
3301 }
3302
3303 // Update statistics.
3304
3305 // Increase total number of reclaimed leases.
3306 StatsMgr::instance().addValue("reclaimed-leases", static_cast<int64_t>(1));
3307
3308 // Increase number of reclaimed leases for a subnet.
3310 lease->subnet_id_,
3311 "reclaimed-leases"),
3312 static_cast<int64_t>(1));
3313
3314 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease->subnet_id_);
3315
3316 if (subnet) {
3317 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3318 if (pool) {
3320 StatsMgr::generateName("subnet", subnet->getID(),
3321 StatsMgr::generateName("pool" , pool->getID(),
3322 "reclaimed-leases")),
3323 static_cast<int64_t>(1));
3324 }
3325 }
3326
3327 // Statistics must have been updated during the release.
3328 if (released) {
3329 return;
3330 }
3331
3332 // Decrease number of assigned addresses.
3333 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(-1));
3334
3336 lease->subnet_id_,
3337 "assigned-addresses"),
3338 static_cast<int64_t>(-1));
3339
3340 if (subnet) {
3341 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3342 if (pool) {
3344 StatsMgr::generateName("subnet", subnet->getID(),
3345 StatsMgr::generateName("pool" , pool->getID(),
3346 "assigned-addresses")),
3347 static_cast<int64_t>(-1));
3348 }
3349 }
3350}
3351
3352void
3356 .arg(secs);
3357
3358 uint64_t deleted_leases = 0;
3359 try {
3360 // Try to delete leases from the lease database.
3361 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3362 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
3363
3364 } catch (const std::exception& ex) {
3366 .arg(ex.what());
3367 }
3368
3371 .arg(deleted_leases);
3372}
3373
3374bool
3375AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
3376 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3377 return (true);
3378 }
3379
3380 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_recover_)) {
3382
3383 // Use the RAII wrapper to make sure that the callout handle state is
3384 // reset when this object goes out of scope. All hook points must do
3385 // it to prevent possible circular dependency between the callout
3386 // handle and its arguments.
3387 ScopedCalloutHandleState callout_handle_state(callout_handle);
3388
3389 // Pass necessary arguments
3390 callout_handle->setArgument("lease4", lease);
3391
3392 // Call the callouts
3393 HooksManager::callCallouts(Hooks.hook_index_lease4_recover_, *callout_handle);
3394
3395 // Callouts decided to skip the action. This means that the lease is not
3396 // assigned, so the client will get NoAddrAvail as a result. The lease
3397 // won't be inserted into the database.
3398 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3400 .arg(lease->addr_.toText());
3401 return (false);
3402 }
3403 }
3404
3406 .arg(lease->addr_.toText())
3407 .arg(lease->valid_lft_);
3408
3409 StatsMgr& stats_mgr = StatsMgr::instance();
3410
3411 // Decrease subnet specific counter for currently declined addresses
3412 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3413 "declined-addresses"),
3414 static_cast<int64_t>(-1));
3415
3416 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3417 "reclaimed-declined-addresses"),
3418 static_cast<int64_t>(1));
3419
3420 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease->subnet_id_);
3421 if (subnet) {
3422 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3423 if (pool) {
3424 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3425 StatsMgr::generateName("pool" , pool->getID(),
3426 "declined-addresses")),
3427 static_cast<int64_t>(-1));
3428
3429 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3430 StatsMgr::generateName("pool" , pool->getID(),
3431 "reclaimed-declined-addresses")),
3432 static_cast<int64_t>(1));
3433 }
3434 }
3435
3436 // Decrease global counter for declined addresses
3437 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3438
3439 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3440
3441 // Note that we do not touch assigned-addresses counters. Those are
3442 // modified in whatever code calls this method.
3443 return (true);
3444}
3445
3446bool
3447AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
3448 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3449 return (true);
3450 }
3451
3452 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_recover_)) {
3454
3455 // Use the RAII wrapper to make sure that the callout handle state is
3456 // reset when this object goes out of scope. All hook points must do
3457 // it to prevent possible circular dependency between the callout
3458 // handle and its arguments.
3459 ScopedCalloutHandleState callout_handle_state(callout_handle);
3460
3461 // Pass necessary arguments
3462 callout_handle->setArgument("lease6", lease);
3463
3464 // Call the callouts
3465 HooksManager::callCallouts(Hooks.hook_index_lease6_recover_, *callout_handle);
3466
3467 // Callouts decided to skip the action. This means that the lease is not
3468 // assigned, so the client will get NoAddrAvail as a result. The lease
3469 // won't be inserted into the database.
3470 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3472 .arg(lease->addr_.toText());
3473 return (false);
3474 }
3475 }
3476
3478 .arg(lease->addr_.toText())
3479 .arg(lease->valid_lft_);
3480
3481 StatsMgr& stats_mgr = StatsMgr::instance();
3482
3483 // Decrease subnet specific counter for currently declined addresses
3484 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3485 "declined-addresses"),
3486 static_cast<int64_t>(-1));
3487
3488 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3489 "reclaimed-declined-addresses"),
3490 static_cast<int64_t>(1));
3491
3492 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3493 if (subnet) {
3494 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
3495 if (pool) {
3496 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3497 StatsMgr::generateName("pool" , pool->getID(),
3498 "declined-addresses")),
3499 static_cast<int64_t>(-1));
3500
3501 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3502 StatsMgr::generateName("pool" , pool->getID(),
3503 "reclaimed-declined-addresses")),
3504 static_cast<int64_t>(1));
3505 }
3506 }
3507
3508 // Decrease global counter for declined addresses
3509 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3510
3511 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3512
3513 // Note that we do not touch assigned-nas counters. Those are
3514 // modified in whatever code calls this method.
3515 return (true);
3516}
3517
3518void
3520 lease->relay_id_.clear();
3521 lease->remote_id_.clear();
3522 if (lease->getContext()) {
3523 lease->setContext(ElementPtr());
3524 }
3525}
3526
3527void
3529 if (lease->getContext()) {
3530 lease->extended_info_action_ = Lease6::ACTION_DELETE;
3531 lease->setContext(ElementPtr());
3532 }
3533}
3534
3535template<typename LeasePtrType>
3536void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
3537 const bool remove_lease,
3538 const std::function<void (const LeasePtrType&)>&
3539 lease_update_fun) const {
3540
3541 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3542
3543 // Reclaim the lease - depending on the configuration, set the
3544 // expired-reclaimed state or simply remove it.
3545 if (remove_lease) {
3546 static_cast<void>(lease_mgr.deleteLease(lease));
3547 } else if (lease_update_fun) {
3548 // Clear FQDN information as we have already sent the
3549 // name change request to remove the DNS record.
3550 lease->reuseable_valid_lft_ = 0;
3551 lease->hostname_.clear();
3552 lease->fqdn_fwd_ = false;
3553 lease->fqdn_rev_ = false;
3554 lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
3556 lease_update_fun(lease);
3557
3558 } else {
3559 return;
3560 }
3561
3562 // Lease has been reclaimed.
3565 .arg(lease->addr_.toText());
3566}
3567
3568std::string
3570 if (!subnet) {
3571 return("<empty subnet>");
3572 }
3573
3574 SharedNetwork4Ptr network;
3575 subnet->getSharedNetwork(network);
3576 std::ostringstream ss;
3577 if (network) {
3578 ss << "shared-network: " << network->getName();
3579 } else {
3580 ss << "subnet id: " << subnet->getID();
3581 }
3582
3583 return(ss.str());
3584}
3585
3586} // namespace dhcp
3587} // namespace isc
3588
3589// ##########################################################################
3590// # DHCPv4 lease allocation code starts here.
3591// ##########################################################################
3592
3593namespace {
3594
3612bool
3613addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
3614 // When out-of-pool flag is true the server may assume that all host
3615 // reservations are for addresses that do not belong to the dynamic pool.
3616 // Therefore, it can skip the reservation checks when dealing with in-pool
3617 // addresses.
3618 if (ctx.subnet_ && ctx.subnet_->getReservationsInSubnet() &&
3619 (!ctx.subnet_->getReservationsOutOfPool() ||
3620 !ctx.subnet_->inPool(Lease::TYPE_V4, address))) {
3621 // The global parameter ip-reservations-unique controls whether it is allowed
3622 // to specify multiple reservations for the same IP address or delegated prefix
3623 // or IP reservations must be unique. Some host backends do not support the
3624 // former, thus we can't always use getAll4 calls to get the reservations
3625 // for the given IP. When we're in the default mode, when IP reservations
3626 // are unique, we should call get4 (supported by all backends). If we're in
3627 // the mode in which non-unique reservations are allowed the backends which
3628 // don't support it are not used and we can safely call getAll4.
3629 ConstHostCollection hosts;
3630 if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
3631 try {
3632 // Reservations are unique. It is safe to call get4 to get the unique host.
3633 ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
3634 if (host) {
3635 hosts.push_back(host);
3636 }
3637 } catch (const MultipleRecords& ex) {
3639 .arg(address)
3640 .arg(ctx.subnet_->getID())
3641 .arg(ex.what());
3642 throw;
3643 }
3644 } else {
3645 // Reservations can be non-unique. Need to get all reservations for that address.
3646 hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
3647 }
3648
3649 for (auto const& host : hosts) {
3650 for (const AllocEngine::IdentifierPair& id_pair : ctx.host_identifiers_) {
3651 // If we find the matching host we know that this address is reserved
3652 // for us and we can return immediately.
3653 if (id_pair.first == host->getIdentifierType() &&
3654 id_pair.second == host->getIdentifier()) {
3655 return (false);
3656 }
3657 }
3658 }
3659 // We didn't find a matching host. If there are any reservations it means that
3660 // address is reserved for another client or multiple clients. If there are
3661 // no reservations address is not reserved for another client.
3662 return (!hosts.empty());
3663 }
3664 return (false);
3665}
3666
3684bool
3685hasAddressReservation(AllocEngine::ClientContext4& ctx) {
3686 if (ctx.hosts_.empty()) {
3687 return (false);
3688 }
3689
3690 // Save list of subnets checked so we can skip rechecking
3691 // client classes for global HRs.
3692 std::list<ConstSubnet4Ptr> eligible_subnets;
3693
3694 // Start with currently selected subnet.
3695 ConstSubnet4Ptr subnet = ctx.subnet_;
3696 while (subnet) {
3697 eligible_subnets.push_back(subnet);
3698 if (subnet->getReservationsInSubnet()) {
3699 auto host = ctx.hosts_.find(subnet->getID());
3700 // The out-of-pool flag indicates that no client should be assigned
3701 // reserved addresses from within the dynamic pool, and for that
3702 // reason look only for reservations that are outside the pools,
3703 // hence the inPool check.
3704 if (host != ctx.hosts_.end() && host->second) {
3705 auto reservation = host->second->getIPv4Reservation();
3706 if (!reservation.isV4Zero() &&
3707 (!subnet->getReservationsOutOfPool() ||
3708 !subnet->inPool(Lease::TYPE_V4, reservation))) {
3709 ctx.subnet_ = subnet;
3710 return (true);
3711 }
3712
3713 // Do we have a dynamic subnet reservation? If so use it to select
3714 // the subnet and return. This overrides a global reserved address.
3715 if (reservation.isV4Zero()) {
3716 ctx.subnet_ = subnet;
3717 return(false);
3718 }
3719 }
3720 }
3721
3722 // No address reservation found here, so let's try another subnet
3723 // within the same shared network.
3724 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3725 }
3726
3727 // No subnet HR so fetch the globally reserved address if there is one.
3728 auto global_host = ctx.hosts_.find(SUBNET_ID_GLOBAL);
3729 auto global_host_address = ((global_host != ctx.hosts_.end() && global_host->second) ?
3730 global_host->second->getIPv4Reservation() :
3732 if (global_host_address == IOAddress::IPV4_ZERO_ADDRESS()) {
3733 return (false);
3734 }
3735
3736 // Iterate over the list of eligible subnets (starting with
3737 // with the currently selected subnet) looking for a subnet
3738 // in which the global host address is valid.
3739 for (auto eligible_subnet : eligible_subnets) {
3740 // If global reservations are enabled for this subnet and there is
3741 // globally reserved address and that address is feasible for this
3742 // subnet, update the selected subnet and return true.
3743 if (eligible_subnet->getReservationsGlobal() &&
3744 (global_host_address != IOAddress::IPV4_ZERO_ADDRESS()) &&
3745 (eligible_subnet->inRange(global_host_address))) {
3746 ctx.subnet_ = eligible_subnet;
3747 return (true);
3748 }
3749 }
3750
3751 if (global_host_address != IOAddress::IPV4_ZERO_ADDRESS()) {
3754 .arg(ctx.query_->getLabel())
3755 .arg(global_host_address.toText())
3757 }
3758
3759 return (false);
3760}
3761
3777void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
3778 // At least the client identifier or the hardware address can be used
3779 // to identify the client.
3780 bool hwaddr_empty = (!ctx.hwaddr_ || ctx.hwaddr_->hwaddr_.empty());
3781
3782 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3783
3784 ConstSubnet4Ptr original_subnet = ctx.subnet_;
3785
3786 auto const& classes = ctx.query_->getClasses();
3787
3788 // Client identifier is optional. First check if we can try to lookup
3789 // by client-id.
3790 bool try_clientid_lookup = (ctx.clientid_ &&
3791 SharedNetwork4::subnetsIncludeMatchClientId(original_subnet, classes));
3792
3793 // If it is possible to use client identifier to try to find client's lease.
3794 if (try_clientid_lookup || hwaddr_empty) {
3795 // Get all leases for this client identifier. When shared networks are
3796 // in use it is more efficient to make a single query rather than
3797 // multiple queries, one for each subnet.
3798 Lease4Collection leases_client_id = lease_mgr.getLease4(*ctx.clientid_);
3799
3800 // Iterate over the subnets within the shared network to see if any client's
3801 // lease belongs to them.
3802 for (ConstSubnet4Ptr subnet = original_subnet; subnet;
3803 subnet = subnet->getNextSubnet(original_subnet, classes)) {
3804
3805 // If client identifier has been supplied and the server wasn't
3806 // explicitly configured to ignore client identifiers for this subnet
3807 // check if there is a lease within this subnet.
3808 if (subnet->getMatchClientId() || hwaddr_empty) {
3809 for (auto const& l : leases_client_id) {
3810 if (l->subnet_id_ == subnet->getID()) {
3811 // Lease found, so stick to this lease.
3812 client_lease = l;
3813 ctx.subnet_ = subnet;
3814 return;
3815 }
3816 }
3817 }
3818 }
3819 }
3820
3821 // If no lease found using the client identifier, try the lookup using
3822 // the HW address if not empty.
3823 if (!client_lease && !hwaddr_empty) {
3824
3825 // Get all leases for this HW address.
3826 Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
3827
3828 for (ConstSubnet4Ptr subnet = original_subnet; subnet;
3829 subnet = subnet->getNextSubnet(original_subnet, classes)) {
3830 ClientIdPtr client_id;
3831 if (subnet->getMatchClientId()) {
3832 client_id = ctx.clientid_;
3833 }
3834
3835 // Try to find the lease that matches current subnet and belongs to
3836 // this client, so both HW address and client identifier match.
3837 for (auto const& client_lease_it : leases_hw_address) {
3838 Lease4Ptr existing_lease = client_lease_it;
3839 if ((existing_lease->subnet_id_ == subnet->getID()) &&
3840 existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
3841 // Found the lease of this client, so return it.
3842 client_lease = existing_lease;
3843 // We got a lease but the subnet it belongs to may differ from
3844 // the original subnet. Let's now stick to this subnet.
3845 ctx.subnet_ = subnet;
3846 return;
3847 }
3848 }
3849 }
3850 }
3851}
3852
3865bool
3866inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
3867 // If the subnet belongs to a shared network we will be iterating
3868 // over the subnets that belong to this shared network.
3869 ConstSubnet4Ptr current_subnet = ctx.subnet_;
3870 auto const& classes = ctx.query_->getClasses();
3871
3872 while (current_subnet) {
3873 if (current_subnet->inPool(Lease::TYPE_V4, address, classes)) {
3874 // We found a subnet that this address belongs to, so it
3875 // seems that this subnet is the good candidate for allocation.
3876 // Let's update the selected subnet.
3877 ctx.subnet_ = current_subnet;
3878 return (true);
3879 }
3880
3881 current_subnet = current_subnet->getNextSubnet(ctx.subnet_, classes);
3882 }
3883
3884 return (false);
3885}
3886
3887} // namespace
3888
3889namespace isc {
3890namespace dhcp {
3891
3903
3905 const ClientIdPtr& clientid,
3906 const HWAddrPtr& hwaddr,
3907 const asiolink::IOAddress& requested_addr,
3908 const bool fwd_dns_update,
3909 const bool rev_dns_update,
3910 const std::string& hostname,
3911 const bool fake_allocation,
3912 const uint32_t offer_lft)
3914 subnet_(subnet), clientid_(clientid), hwaddr_(hwaddr),
3915 requested_address_(requested_addr),
3916 fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
3917 hostname_(hostname), callout_handle_(),
3918 fake_allocation_(fake_allocation), offer_lft_(offer_lft), old_lease_(), new_lease_(),
3920 ddns_params_(new DdnsParams()) {
3921
3922 // Initialize host identifiers.
3923 if (hwaddr && !hwaddr->hwaddr_.empty()) {
3924 addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
3925 }
3926}
3927
3930 if (subnet_ && subnet_->getReservationsInSubnet()) {
3931 auto host = hosts_.find(subnet_->getID());
3932 if (host != hosts_.cend()) {
3933 return (host->second);
3934 }
3935 }
3936
3937 return (globalHost());
3938}
3939
3942 if (subnet_ && subnet_->getReservationsGlobal()) {
3943 auto host = hosts_.find(SUBNET_ID_GLOBAL);
3944 if (host != hosts_.cend()) {
3945 return (host->second);
3946 }
3947 }
3948
3949 return (ConstHostPtr());
3950}
3951
3954 // We already have it return it unless the context subnet has changed.
3955 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
3956 return (ddns_params_);
3957 }
3958
3959 // Doesn't exist yet or is stale, (re)create it.
3960 if (subnet_) {
3961 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
3962 return (ddns_params_);
3963 }
3964
3965 // Asked for it without a subnet? This case really shouldn't occur but
3966 // for now let's return an instance with default values.
3967 return (DdnsParamsPtr(new DdnsParams()));
3968}
3969
3972 // The NULL pointer indicates that the old lease didn't exist. It may
3973 // be later set to non NULL value if existing lease is found in the
3974 // database.
3975 ctx.old_lease_.reset();
3976 ctx.new_lease_.reset();
3977
3978 // Before we start allocation process, we need to make sure that the
3979 // selected subnet is allowed for this client. If not, we'll try to
3980 // use some other subnet within the shared network. If there are no
3981 // subnets allowed for this client within the shared network, we
3982 // can't allocate a lease.
3983 ConstSubnet4Ptr subnet = ctx.subnet_;
3984 auto const& classes = ctx.query_->getClasses();
3985 if (subnet && !subnet->clientSupported(classes)) {
3986 ctx.subnet_ = subnet->getNextSubnet(subnet, classes);
3987 }
3988
3989 try {
3990 if (!ctx.subnet_) {
3991 isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
3992 }
3993
3994 if (!ctx.hwaddr_) {
3995 isc_throw(BadValue, "HWAddr must be defined");
3996 }
3997
3998 if (ctx.fake_allocation_) {
3999 ctx.new_lease_ = discoverLease4(ctx);
4000 } else {
4001 ctx.new_lease_ = requestLease4(ctx);
4002 }
4003
4004 } catch (const NoSuchLease& e) {
4005 isc_throw(NoSuchLease, "detected data race in AllocEngine::allocateLease4: " << e.what());
4006
4007 } catch (const isc::Exception& e) {
4008 // Some other error, return an empty lease.
4010 .arg(ctx.query_->getLabel())
4011 .arg(e.what());
4012 }
4013
4014 return (ctx.new_lease_);
4015}
4016
4017void
4019 // If there is no subnet, there is nothing to do.
4020 if (!ctx.subnet_) {
4021 return;
4022 }
4023
4024 auto subnet = ctx.subnet_;
4025
4026 // If already done just return.
4028 !subnet->getReservationsInSubnet()) {
4029 return;
4030 }
4031
4032 // @todo: This code can be trivially optimized.
4034 subnet->getReservationsGlobal()) {
4036 if (ghost) {
4037 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
4038
4039 // If we had only to fetch global reservations it is done.
4040 if (!subnet->getReservationsInSubnet()) {
4041 return;
4042 }
4043 }
4044 }
4045
4046 std::map<SubnetID, ConstHostPtr> host_map;
4047 SharedNetwork4Ptr network;
4048 subnet->getSharedNetwork(network);
4049
4050 // If the subnet belongs to a shared network it is usually going to be
4051 // more efficient to make a query for all reservations for a particular
4052 // client rather than a query for each subnet within this shared network.
4053 // The only case when it is going to be less efficient is when there are
4054 // more host identifier types in use than subnets within a shared network.
4055 // As it breaks RADIUS use of host caching this can be disabled by the
4056 // host manager.
4057 const bool use_single_query = network &&
4059 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
4060
4061 if (use_single_query) {
4062 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
4063 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
4064 &id_pair.second[0],
4065 id_pair.second.size());
4066 // Store the hosts in the temporary map, because some hosts may
4067 // belong to subnets outside of the shared network. We'll need
4068 // to eliminate them.
4069 for (auto const& host : hosts) {
4070 if (host->getIPv4SubnetID() != SUBNET_ID_GLOBAL) {
4071 host_map[host->getIPv4SubnetID()] = host;
4072 }
4073 }
4074 }
4075 }
4076
4077 auto const& classes = ctx.query_->getClasses();
4078 // We can only search for the reservation if a subnet has been selected.
4079 while (subnet) {
4080
4081 // Only makes sense to get reservations if the client has access
4082 // to the class and host reservations are enabled for this subnet.
4083 if (subnet->clientSupported(classes) && subnet->getReservationsInSubnet()) {
4084 // Iterate over configured identifiers in the order of preference
4085 // and try to use each of them to search for the reservations.
4086 if (use_single_query) {
4087 if (host_map.count(subnet->getID()) > 0) {
4088 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
4089 }
4090 } else {
4091 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
4092 // Attempt to find a host using a specified identifier.
4093 ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
4094 id_pair.first,
4095 &id_pair.second[0],
4096 id_pair.second.size());
4097 // If we found matching host for this subnet.
4098 if (host) {
4099 ctx.hosts_[subnet->getID()] = host;
4100 break;
4101 }
4102 }
4103 }
4104 }
4105
4106 // We need to get to the next subnet if this is a shared network. If it
4107 // is not (a plain subnet), getNextSubnet will return NULL and we're
4108 // done here.
4109 subnet = subnet->getNextSubnet(ctx.subnet_, classes);
4110 }
4111
4112 // The hosts can be used by the server to return reserved options to
4113 // the DHCP client. Such options must be encapsulated (i.e., they must
4114 // include suboptions).
4115 for (auto const& host : ctx.hosts_) {
4116 host.second->encapsulateOptions();
4117 }
4118}
4119
4122 ConstHostPtr host;
4123 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
4124 // Attempt to find a host using a specified identifier.
4125 host = HostMgr::instance().get4(SUBNET_ID_GLOBAL, id_pair.first,
4126 &id_pair.second[0], id_pair.second.size());
4127
4128 // If we found matching global host we're done.
4129 if (host) {
4130 break;
4131 }
4132 }
4133
4134 return (host);
4135}
4136
4138AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
4139 // Find an existing lease for this client. This function will return null
4140 // if there is a conflict with existing lease and the allocation should
4141 // not be continued.
4142 Lease4Ptr client_lease;
4143 findClientLease(ctx, client_lease);
4144
4145 // Fetch offer_lft to see if we're allocating on DISCOVER.
4146 ctx.offer_lft_ = getOfferLft(ctx);
4147
4148 // new_lease will hold the pointer to the lease that we will offer to the
4149 // caller.
4150 Lease4Ptr new_lease;
4151
4153
4154 // Check if there is a reservation for the client. If there is, we want to
4155 // assign the reserved address, rather than any other one.
4156 if (hasAddressReservation(ctx)) {
4157
4160 .arg(ctx.query_->getLabel())
4161 .arg(ctx.currentHost()->getIPv4Reservation().toText());
4162
4163 // If the client doesn't have a lease or the leased address is different
4164 // than the reserved one then let's try to allocate the reserved address.
4165 // Otherwise the address that the client has is the one for which it
4166 // has a reservation, so just renew it.
4167 if (!client_lease || (client_lease->addr_ != ctx.currentHost()->getIPv4Reservation())) {
4168 // The call below will return a pointer to the lease for the address
4169 // reserved to this client, if the lease is available, i.e. is not
4170 // currently assigned to any other client.
4171 // Note that we don't remove the existing client's lease at this point
4172 // because this is not a real allocation, we just offer what we can
4173 // allocate in the DHCPREQUEST time.
4174 new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
4175 callout_status);
4176 if (!new_lease) {
4178 .arg(ctx.query_->getLabel())
4179 .arg(ctx.currentHost()->getIPv4Reservation().toText())
4180 .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
4181 "(no lease info)");
4182 if (ctx.conflicting_lease_) {
4184 ctx.conflicting_lease_->subnet_id_,
4185 "v4-reservation-conflicts"),
4186 static_cast<int64_t>(1));
4187 StatsMgr::instance().addValue("v4-reservation-conflicts",
4188 static_cast<int64_t>(1));
4189 }
4190 }
4191
4192 } else {
4193 new_lease = renewLease4(client_lease, ctx);
4194 }
4195 }
4196
4197 // Client does not have a reservation or the allocation of the reserved
4198 // address has failed, probably because the reserved address is in use
4199 // by another client. If the client has a lease, we will check if we can
4200 // offer this lease to the client. The lease can't be offered in the
4201 // situation when it is reserved for another client or when the address
4202 // is not in the dynamic pool. The former may be the result of adding the
4203 // new reservation for the address used by this client. The latter may
4204 // be due to the client using the reserved out-of-the pool address, for
4205 // which the reservation has just been removed.
4206 if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
4207 !addressReserved(client_lease->addr_, ctx)) {
4208
4211 .arg(ctx.query_->getLabel());
4212
4213 // If offer-lifetime is shorter than the existing expiration, reset
4214 // offer-lifetime to zero. This allows us to simply return the
4215 // existing lease without updating it in the lease store.
4216 if ((ctx.offer_lft_) &&
4217 (time(NULL) + ctx.offer_lft_ < client_lease->getExpirationTime())) {
4218 ctx.offer_lft_ = 0;
4219 }
4220
4221 new_lease = renewLease4(client_lease, ctx);
4222 }
4223
4224 // The client doesn't have any lease or the lease can't be offered
4225 // because it is either reserved for some other client or the
4226 // address is not in the dynamic pool.
4227 // Let's use the client's hint (requested IP address), if the client
4228 // has provided it, and try to offer it. This address must not be
4229 // reserved for another client, and must be in the range of the
4230 // dynamic pool.
4231 if (!new_lease && !ctx.requested_address_.isV4Zero() &&
4232 inAllowedPool(ctx, ctx.requested_address_) &&
4233 !addressReserved(ctx.requested_address_, ctx)) {
4234
4237 .arg(ctx.requested_address_.toText())
4238 .arg(ctx.query_->getLabel());
4239
4240 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
4241 callout_status);
4242 }
4243
4244 // The allocation engine failed to allocate all of the candidate
4245 // addresses. We will now use the allocator to pick the address
4246 // from the dynamic pool.
4247 if (!new_lease) {
4248
4251 .arg(ctx.query_->getLabel());
4252
4253 new_lease = allocateUnreservedLease4(ctx);
4254 }
4255
4256 // Some of the methods like reuseExpiredLease4 may set the old lease to point
4257 // to the lease which they remove/override. If it is not set, but we have
4258 // found that the client has the lease the client's lease is the one
4259 // to return as an old lease.
4260 if (!ctx.old_lease_ && client_lease) {
4261 ctx.old_lease_ = client_lease;
4262 }
4263
4264 return (new_lease);
4265}
4266
4268 if (LeaseMgrFactory::instance().deleteLease(lease) &&
4269 (lease->state_ != Lease4::STATE_RELEASED)) {
4270 // Need to decrease statistic for assigned addresses.
4271 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(-1));
4272
4273 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
4274 "assigned-addresses"),
4275 static_cast<int64_t>(-1));
4276
4277 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()
4278 ->getBySubnetId(lease->subnet_id_);
4279 if (subnet) {
4280 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
4281 if (pool) {
4282 StatsMgr::instance().addValue(StatsMgr::generateName("subnet", subnet->getID(),
4283 StatsMgr::generateName("pool", pool->getID(),
4284 "assigned-addresses")),
4285 static_cast<int64_t>(-1));
4286 }
4287 }
4288 }
4289}
4290
4292AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
4293 // Find an existing lease for this client. This function will return null
4294 // if there is a conflict with existing lease and the allocation should
4295 // not be continued.
4296 Lease4Ptr client_lease;
4297 findClientLease(ctx, client_lease);
4298
4299 // When the client sends the DHCPREQUEST, it should always specify the
4300 // address which it is requesting or renewing. That is, the client should
4301 // either use the requested IP address option or set the ciaddr. However,
4302 // we try to be liberal and allow the clients to not specify an address
4303 // in which case the allocation engine will pick a suitable address
4304 // for the client.
4305 if (!ctx.requested_address_.isV4Zero()) {
4306 // If the client has specified an address, make sure this address
4307 // is not reserved for another client. If it is, stop here because
4308 // we can't allocate this address.
4309 if (addressReserved(ctx.requested_address_, ctx)) {
4310
4313 .arg(ctx.query_->getLabel())
4314 .arg(ctx.requested_address_.toText());
4315
4316 return (Lease4Ptr());
4317 }
4318
4319 } else if (hasAddressReservation(ctx)) {
4320 // The client hasn't specified an address to allocate, so the
4321 // allocation engine needs to find an appropriate address.
4322 // If there is a reservation for the client, let's try to
4323 // allocate the reserved address.
4324 ctx.requested_address_ = ctx.currentHost()->getIPv4Reservation();
4325
4328 .arg(ctx.query_->getLabel())
4329 .arg(ctx.requested_address_.toText());
4330 }
4331
4332 if (!ctx.requested_address_.isV4Zero()) {
4333 // There is a specific address to be allocated. Let's find out if
4334 // the address is in use.
4336 // If the address is in use (allocated and not expired), we check
4337 // if the address is in use by our client or another client.
4338 // If it is in use by another client, the address can't be
4339 // allocated.
4340 if (existing && !existing->expired() &&
4341 !existing->belongsToClient(ctx.hwaddr_, ctx.getClientId())) {
4342
4345 .arg(ctx.query_->getLabel())
4346 .arg(ctx.requested_address_.toText());
4347
4348 return (Lease4Ptr());
4349 }
4350
4351 // If the client has a reservation but it is requesting a different
4352 // address it is possible that the client was offered this different
4353 // address because the reserved address is in use. We will have to
4354 // check if the address is in use.
4355 if (hasAddressReservation(ctx) &&
4356 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) {
4357 existing =
4358 LeaseMgrFactory::instance().getLease4(ctx.currentHost()->getIPv4Reservation());
4359 // If the reserved address is not in use, i.e. the lease doesn't
4360 // exist or is expired, and the client is requesting a different
4361 // address, return NULL. The client should go back to the
4362 // DHCPDISCOVER and the reserved address will be offered.
4363 if (!existing || existing->expired()) {
4364
4367 .arg(ctx.query_->getLabel())
4368 .arg(ctx.currentHost()->getIPv4Reservation().toText())
4369 .arg(ctx.requested_address_.toText());
4370
4371 return (Lease4Ptr());
4372 }
4373 }
4374
4375 // The use of the out-of-pool addresses is only allowed when the requested
4376 // address is reserved for the client. If the address is not reserved one
4377 // and it doesn't belong to the dynamic pool, do not allocate it.
4378 if ((!hasAddressReservation(ctx) ||
4379 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
4380 !inAllowedPool(ctx, ctx.requested_address_)) {
4381
4384 .arg(ctx.query_->getLabel())
4385 .arg(ctx.requested_address_);
4386
4387 ctx.unknown_requested_addr_ = true;
4388 return (Lease4Ptr());
4389 }
4390
4391 // During a prior discover the allocation engine deemed that the
4392 // client's current lease (client_lease) should no longer be used and
4393 // so offered a different lease (existing) that was temporarily
4394 // allocated because offer-lifetime is greater than zero. We need to
4395 // delete the unusable lease and renew the temporary lease.
4396 if ((client_lease && existing) &&
4397 (client_lease->addr_ != existing->addr_) &&
4398 (existing->addr_ == ctx.requested_address_) &&
4399 existing->belongsToClient(ctx.hwaddr_, ctx.getClientId()) &&
4400 getOfferLft(ctx, false)) {
4401
4402 auto conflicted_lease = client_lease;
4403 client_lease = existing;
4404 deleteAssignedLease(conflicted_lease);
4405 }
4406 }
4407
4408 // We have gone through all the checks, so we can now allocate the address
4409 // for the client.
4410
4411 // If the client is requesting an address which is assigned to the client
4412 // let's just renew this address. Also, renew this address if the client
4413 // doesn't request any specific address.
4414 // Added extra checks: the address is reserved for this client or belongs
4415 // to the dynamic pool for the case the pool class has changed before the
4416 // request.
4417 if (client_lease) {
4418 if (((client_lease->addr_ == ctx.requested_address_) ||
4420 ((hasAddressReservation(ctx) &&
4421 (ctx.currentHost()->getIPv4Reservation() == ctx.requested_address_)) ||
4422 inAllowedPool(ctx, client_lease->addr_))) {
4423
4426 .arg(ctx.query_->getLabel())
4427 .arg(ctx.requested_address_);
4428 return (renewLease4(client_lease, ctx));
4429 }
4430 }
4431
4432 // new_lease will hold the pointer to the allocated lease if we allocate
4433 // successfully.
4434 Lease4Ptr new_lease;
4435
4436 // The client doesn't have the lease or it is requesting an address
4437 // which it doesn't have. Let's try to allocate the requested address.
4438 if (!ctx.requested_address_.isV4Zero()) {
4439
4442 .arg(ctx.query_->getLabel())
4443 .arg(ctx.requested_address_.toText());
4444
4445 // The call below will return a pointer to the lease allocated
4446 // for the client if there is no lease for the requested address,
4447 // or the existing lease has expired. If the allocation fails,
4448 // e.g. because the lease is in use, we will return NULL to
4449 // indicate that we were unable to allocate the lease.
4451 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
4452 callout_status);
4453 } else {
4454
4457 .arg(ctx.query_->getLabel());
4458
4459 // We will only get here if the client didn't specify which
4460 // address it wanted to be allocated. The allocation engine will
4461 // to pick the address from the dynamic pool.
4462 new_lease = allocateUnreservedLease4(ctx);
4463 }
4464
4465 // If we allocated the lease for the client, but the client already had a
4466 // lease, we will need to return the pointer to the previous lease and
4467 // the previous lease needs to be removed from the lease database.
4468 if (new_lease && client_lease) {
4469 ctx.old_lease_ = Lease4Ptr(new Lease4(*client_lease));
4470
4473 .arg(ctx.query_->getLabel())
4474 .arg(client_lease->addr_.toText());
4475 deleteAssignedLease(client_lease);
4476 }
4477
4478 // Return the allocated lease or NULL pointer if allocation was
4479 // unsuccessful.
4480 return (new_lease);
4481}
4482
4483uint32_t
4484AllocEngine::getOfferLft(const ClientContext4& ctx, bool only_on_discover /* = true */) {
4485 // Not a DISCOVER or it's BOOTP, punt.
4486 if (only_on_discover && ((!ctx.fake_allocation_) || (ctx.query_->inClass("BOOTP")))) {
4487 return (0);
4488 }
4489
4490 util::Optional<uint32_t> offer_lft;
4491
4492 // If specified in one of our classes use it.
4493 // We use the first one we find.
4494 const ClientClasses classes = ctx.query_->getClasses();
4495 if (!classes.empty()) {
4496 // Let's get class definitions
4497 const ClientClassDictionaryPtr& dict =
4498 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4499
4500 // Iterate over the assigned class definitions.
4501 for (auto const& name : classes) {
4502 ClientClassDefPtr cl = dict->findClass(name);
4503 if (cl && (!cl->getOfferLft().unspecified())) {
4504 offer_lft = cl->getOfferLft();
4505 break;
4506 }
4507 }
4508 }
4509
4510 // If no classes specified it, get it from the subnet.
4511 if (offer_lft.unspecified()) {
4512 offer_lft = ctx.subnet_->getOfferLft();
4513 }
4514
4515 return (offer_lft.unspecified() ? 0 : offer_lft.get());
4516}
4517
4518uint32_t
4520 // If it's BOOTP, use infinite valid lifetime.
4521 if (ctx.query_->inClass("BOOTP")) {
4522 return (Lease::INFINITY_LFT);
4523 }
4524
4525 // Use the dhcp-lease-time content from the client if it's there.
4526 uint32_t requested_lft = 0;
4527 OptionPtr opt = ctx.query_->getOption(DHO_DHCP_LEASE_TIME);
4528 if (opt) {
4529 OptionUint32Ptr opt_lft = boost::dynamic_pointer_cast<OptionInt<uint32_t> >(opt);
4530 if (opt_lft) {
4531 requested_lft = opt_lft->getValue();
4532 }
4533 }
4534
4535 // If the triplet is specified in one of our classes use it.
4536 // We use the first one we find.
4537 Triplet<uint32_t> candidate_lft;
4538 const ClientClasses classes = ctx.query_->getClasses();
4539 if (!classes.empty()) {
4540 // Let's get class definitions
4541 const ClientClassDictionaryPtr& dict =
4542 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4543
4544 // Iterate over the assigned class definitions.
4545 for (auto const& name : classes) {
4546 ClientClassDefPtr cl = dict->findClass(name);
4547 if (cl && (!cl->getValid().unspecified())) {
4548 candidate_lft = cl->getValid();
4549 break;
4550 }
4551 }
4552 }
4553
4554 // If no classes specified it, get it from the subnet.
4555 if (!candidate_lft) {
4556 candidate_lft = ctx.subnet_->getValid();
4557 }
4558
4559 // If client requested a value, use the value bounded by
4560 // the candidate triplet.
4561 if (requested_lft > 0) {
4562 return (candidate_lft.get(requested_lft));
4563 }
4564
4565 // Use the candidate's default value.
4566 return (candidate_lft.get());
4567}
4568
4569void
4570AllocEngine::getMinValidLft(const ClientContext4& ctx, uint32_t& valid) {
4571 // If it's BOOTP, use infinite valid lifetime.
4572 if (ctx.query_->inClass("BOOTP")) {
4573 valid = Lease::INFINITY_LFT;
4574 return;
4575 }
4576
4577 // If the triplet is specified in one of our classes use it.
4578 // We use the first one we find.
4579 Triplet<uint32_t> candidate_lft;
4580 const ClientClasses classes = ctx.query_->getClasses();
4581 if (!classes.empty()) {
4582 // Let's get class definitions
4583 const ClientClassDictionaryPtr& dict =
4584 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4585
4586 // Iterate over the assigned class definitions.
4587 for (auto const& name : classes) {
4588 ClientClassDefPtr cl = dict->findClass(name);
4589 if (cl && (!cl->getValid().unspecified())) {
4590 candidate_lft = cl->getValid();
4591 break;
4592 }
4593 }
4594 }
4595
4596 // If no classes specified it, get it from the subnet.
4597 if (!candidate_lft) {
4598 candidate_lft = ctx.subnet_->getValid();
4599 }
4600
4601 // Save remaining value.
4602 uint32_t remain(valid);
4603
4604 // Set to the minimal value.
4605 valid = candidate_lft.getMin();
4606
4607 // Return at least the remaining value.
4608 if (remain > valid) {
4609 valid = remain;
4610 }
4611}
4612
4613namespace {
4614bool
4615useMinValidLft(const AllocEngine::ClientContext4& ctx, const IOAddress&addr) {
4616 auto const& threshold = ctx.subnet_->getAdaptiveLeaseTimeThreshold();
4617 if (!threshold.unspecified() && (threshold < 1.0)) {
4618 auto const& occupancy = ctx.subnet_->getAllocator(Lease::TYPE_V4)->
4619 getOccupancyRate(addr, ctx.query_->getClasses());
4620 if (occupancy >= threshold) {
4621 return (true);
4622 }
4623 }
4624 return (false);
4625}
4626} // end of anonymous namespace.
4627
4629AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
4630 CalloutHandle::CalloutNextStep& callout_status) {
4631 if (!ctx.hwaddr_) {
4632 isc_throw(BadValue, "Can't create a lease with NULL HW address");
4633 }
4634 if (!ctx.subnet_) {
4635 isc_throw(BadValue, "Can't create a lease without a subnet");
4636 }
4637
4638 // Get the context appropriate lifetime.
4639 uint32_t valid_lft = ctx.offer_lft_;
4640 if (!valid_lft) {
4641 if (useMinValidLft(ctx, addr)) {
4642 getMinValidLft(ctx, valid_lft);
4643 } else {
4644 valid_lft = getValidLft(ctx);
4645 }
4646 }
4647
4648 time_t now = time(0);
4649
4650 ClientIdPtr client_id = ctx.getClientId();
4651
4652 Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
4653 valid_lft, now, ctx.subnet_->getID()));
4654
4655 // Set FQDN specific lease parameters.
4656 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4657 lease->fqdn_rev_ = ctx.rev_dns_update_;
4658 lease->hostname_ = ctx.hostname_;
4659
4660 // Add (update) the extended information on the lease.
4661 static_cast<void>(updateLease4ExtendedInfo(lease, ctx));
4662
4663 // Let's execute all callouts registered for lease4_select
4664 if (ctx.callout_handle_ &&
4665 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4666
4667 // Use the RAII wrapper to make sure that the callout handle state is
4668 // reset when this object goes out of scope. All hook points must do
4669 // it to prevent possible circular dependency between the callout
4670 // handle and its arguments.
4671 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4672
4673 // Enable copying options from the packet within hook library.
4674 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4675
4676 // Pass necessary arguments
4677 // Pass the original client query
4678 ctx.callout_handle_->setArgument("query4", ctx.query_);
4679
4680 // Subnet from which we do the allocation (That's as far as we can go
4681 // with using SubnetPtr to point to Subnet4 object. Users should not
4682 // be confused with dynamic_pointer_casts. They should get a concrete
4683 // pointer (ConstSubnet4Ptr) pointing to a Subnet4 object.
4684 ConstSubnet4Ptr subnet4 =
4685 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4686 ctx.callout_handle_->setArgument("subnet4", subnet4);
4687
4688 // Is this solicit (fake = true) or request (fake = false)
4689 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4690
4691 // Are we allocating on DISCOVER? (i.e. offer_lft > 0).
4692 ctx.callout_handle_->setArgument("offer_lft", ctx.offer_lft_);
4693
4694 // Pass the intended lease as well
4695 ctx.callout_handle_->setArgument("lease4", lease);
4696
4697 // This is the first callout, so no need to clear any arguments
4698 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4699
4700 callout_status = ctx.callout_handle_->getStatus();
4701
4702 // Callouts decided to skip the action. This means that the lease is not
4703 // assigned, so the client will get NoAddrAvail as a result. The lease
4704 // won't be inserted into the database.
4705 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4707 return (Lease4Ptr());
4708 }
4709
4710 // Let's use whatever callout returned. Hopefully it is the same lease
4711 // we handled to it.
4712 ctx.callout_handle_->getArgument("lease4", lease);
4713 }
4714
4715 if (ctx.fake_allocation_ && ctx.offer_lft_) {
4716 // Turn them off before we persist, so we'll see it as different when
4717 // we extend it in the REQUEST. This should cause us to do DDNS (if
4718 // it's enabled).
4719 lease->fqdn_fwd_ = false;
4720 lease->fqdn_rev_ = false;
4721 }
4722
4723 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4724 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, lease->addr_, false);
4725 if (pool) {
4726 lease->pool_id_ = pool->getID();
4727 }
4728 // That is a real (REQUEST) allocation
4729 bool status = LeaseMgrFactory::instance().addLease(lease);
4730 if (status) {
4731
4732 // The lease insertion succeeded, let's bump up the statistic.
4734 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4735 "assigned-addresses"),
4736 static_cast<int64_t>(1));
4737
4739 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4740 "cumulative-assigned-addresses"),
4741 static_cast<int64_t>(1));
4742
4743 if (pool) {
4745 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4746 StatsMgr::generateName("pool", pool->getID(),
4747 "assigned-addresses")),
4748 static_cast<int64_t>(1));
4749
4751 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4752 StatsMgr::generateName("pool", pool->getID(),
4753 "cumulative-assigned-addresses")),
4754 static_cast<int64_t>(1));
4755 }
4756
4757
4758 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(1));
4759
4760 StatsMgr::instance().addValue("cumulative-assigned-addresses", static_cast<int64_t>(1));
4761
4762 return (lease);
4763 } else {
4764 // One of many failures with LeaseMgr (e.g. lost connection to the
4765 // database, database failed etc.). One notable case for that
4766 // is that we are working in multi-process mode and we lost a race
4767 // (some other process got that address first)
4768 return (Lease4Ptr());
4769 }
4770 } else {
4771 // That is only fake (DISCOVER) allocation
4772 // It is for OFFER only. We should not insert the lease and callers
4773 // have already verified the lease does not exist in the database.
4774 return (lease);
4775 }
4776}
4777
4778void
4779AllocEngine::getRemaining(const Lease4Ptr& lease, uint32_t& valid) {
4780 valid = 0;
4781 if (!lease || (lease->state_ != Lease::STATE_DEFAULT)) {
4782 return;
4783 }
4784 // Always remain infinite lifetime leases.
4785 if (lease->valid_lft_ == Lease::INFINITY_LFT) {
4786 valid = Lease::INFINITY_LFT;
4787 return;
4788 }
4789 time_t now = time(0);
4790 // Refuse time not going forward.
4791 if (lease->cltt_ > now) {
4792 return;
4793 }
4794 uint32_t age = now - lease->cltt_;
4795 // Already expired.
4796 if (age >= lease->valid_lft_) {
4797 return;
4798 }
4799 valid = lease->valid_lft_ - age;
4800}
4801
4803AllocEngine::renewLease4(const Lease4Ptr& lease,
4805 if (!lease) {
4806 isc_throw(BadValue, "null lease specified for renewLease4");
4807 }
4808
4809 // Let's keep the old data. This is essential if we are using memfile
4810 // (the lease returned points directly to the lease4 object in the database)
4811 // We'll need it if we want to skip update (i.e. roll back renewal)
4813 Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
4814 ctx.old_lease_.reset(new Lease4(*old_values));
4815
4816 // Update the lease with the information from the context.
4817 // If there was no significant changes, try reuse.
4818 lease->reuseable_valid_lft_ = 0;
4819 if (!updateLease4Information(lease, ctx)) {
4820 setLeaseReusable(lease, ctx);
4821 }
4822
4823 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4824 // If the lease is expired we have to reclaim it before
4825 // re-assigning it to the client. The lease reclamation
4826 // involves execution of hooks and DNS update.
4827 if (ctx.old_lease_->expired()) {
4828 reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
4829 }
4830
4831 lease->state_ = Lease::STATE_DEFAULT;
4832 }
4833
4834 bool skip = false;
4835 // Execute all callouts registered for lease4_renew.
4836 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_renew_)) {
4837
4838 // Use the RAII wrapper to make sure that the callout handle state is
4839 // reset when this object goes out of scope. All hook points must do
4840 // it to prevent possible circular dependency between the callout
4841 // handle and its arguments.
4842 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4843
4844 // Enable copying options from the packet within hook library.
4845 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4846
4847 // Subnet from which we do the allocation. Convert the general subnet
4848 // pointer to a pointer to a Subnet4. Note that because we are using
4849 // boost smart pointers here, we need to do the cast using the boost
4850 // version of dynamic_pointer_cast.
4851 ConstSubnet4Ptr subnet4 =
4852 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4853
4854 // Pass the parameters. Note the clientid is passed only if match-client-id
4855 // is set. This is done that way, because the lease4-renew hook point is
4856 // about renewing a lease and the configuration parameter says the
4857 // client-id should be ignored. Hence no clientid value if match-client-id
4858 // is false.
4859 ctx.callout_handle_->setArgument("query4", ctx.query_);
4860 ctx.callout_handle_->setArgument("subnet4", subnet4);
4861 ctx.callout_handle_->setArgument("clientid", ctx.getClientId());
4862 ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
4863
4864 // Pass the lease to be updated
4865 ctx.callout_handle_->setArgument("lease4", lease);
4866
4867 // Call all installed callouts
4868 HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
4869 *ctx.callout_handle_);
4870
4871 // Callouts decided to skip the next processing step. The next
4872 // processing step would actually renew the lease, so skip at this
4873 // stage means "keep the old lease as it is".
4874 if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4875 skip = true;
4878 }
4879
4881 }
4882
4883 if ((!ctx.fake_allocation_ || ctx.offer_lft_) && !skip && (lease->reuseable_valid_lft_ == 0)) {
4884 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, lease->addr_, false);
4885 if (pool) {
4886 lease->pool_id_ = pool->getID();
4887 }
4888
4889 // for REQUEST we do update the lease
4891
4892 // We need to account for the re-assignment of the lease.
4893 if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
4895 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4896 "assigned-addresses"),
4897 static_cast<int64_t>(1));
4898
4900 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4901 "cumulative-assigned-addresses"),
4902 static_cast<int64_t>(1));
4903
4904 if (pool) {
4906 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4907 StatsMgr::generateName("pool", pool->getID(),
4908 "assigned-addresses")),
4909 static_cast<int64_t>(1));
4910
4912 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4913 StatsMgr::generateName("pool", pool->getID(),
4914 "cumulative-assigned-addresses")),
4915 static_cast<int64_t>(1));
4916 }
4917
4918 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(1));
4919
4920 StatsMgr::instance().addValue("cumulative-assigned-addresses", static_cast<int64_t>(1));
4921 }
4922 }
4923 if (skip) {
4924 // Rollback changes (really useful only for memfile)
4926 *lease = *old_values;
4927 }
4928
4929 return (lease);
4930}
4931
4933AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
4934 AllocEngine::ClientContext4& ctx,
4935 CalloutHandle::CalloutNextStep& callout_status) {
4936 if (!expired) {
4937 isc_throw(BadValue, "null lease specified for reuseExpiredLease");
4938 }
4939
4940 if (!ctx.subnet_) {
4941 isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
4942 }
4943
4944 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4945 // The expired lease needs to be reclaimed before it can be reused.
4946 // This includes declined leases for which probation period has
4947 // elapsed.
4948 reclaimExpiredLease(expired, ctx.callout_handle_);
4949 expired->state_ = Lease::STATE_DEFAULT;
4950 }
4951
4952 expired->reuseable_valid_lft_ = 0;
4953 static_cast<void>(updateLease4Information(expired, ctx));
4954
4957 .arg(ctx.query_->getLabel())
4958 .arg(expired->toText());
4959
4960 // Let's execute all callouts registered for lease4_select
4961 if (ctx.callout_handle_ &&
4962 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4963
4964 // Enable copying options from the packet within hook library.
4965 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4966
4967 // Use the RAII wrapper to make sure that the callout handle state is
4968 // reset when this object goes out of scope. All hook points must do
4969 // it to prevent possible circular dependency between the callout
4970 // handle and its arguments.
4971 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4972
4973 // Pass necessary arguments
4974 // Pass the original client query
4975 ctx.callout_handle_->setArgument("query4", ctx.query_);
4976
4977 // Subnet from which we do the allocation. Convert the general subnet
4978 // pointer to a pointer to a Subnet4. Note that because we are using
4979 // boost smart pointers here, we need to do the cast using the boost
4980 // version of dynamic_pointer_cast.
4981 ConstSubnet4Ptr subnet4 =
4982 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4983 ctx.callout_handle_->setArgument("subnet4", subnet4);
4984
4985 // Is this solicit (fake = true) or request (fake = false)
4986 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4987 ctx.callout_handle_->setArgument("offer_lft", ctx.offer_lft_);
4988
4989 // The lease that will be assigned to a client
4990 ctx.callout_handle_->setArgument("lease4", expired);
4991
4992 // Call the callouts
4993 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4994
4995 callout_status = ctx.callout_handle_->getStatus();
4996
4997 // Callouts decided to skip the action. This means that the lease is not
4998 // assigned, so the client will get NoAddrAvail as a result. The lease
4999 // won't be inserted into the database.
5000 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
5003 return (Lease4Ptr());
5004 }
5005
5007
5008 // Let's use whatever callout returned. Hopefully it is the same lease
5009 // we handed to it.
5010 ctx.callout_handle_->getArgument("lease4", expired);
5011 }
5012
5013 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
5014 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, expired->addr_, false);
5015 if (pool) {
5016 expired->pool_id_ = pool->getID();
5017 }
5018 // for REQUEST we do update the lease
5020
5021 // We need to account for the re-assignment of the lease.
5023 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5024 "assigned-addresses"),
5025 static_cast<int64_t>(1));
5026
5028 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5029 "cumulative-assigned-addresses"),
5030 static_cast<int64_t>(1));
5031
5032 if (pool) {
5034 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5035 StatsMgr::generateName("pool", pool->getID(),
5036 "assigned-addresses")),
5037 static_cast<int64_t>(1));
5038
5040 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5041 StatsMgr::generateName("pool", pool->getID(),
5042 "cumulative-assigned-addresses")),
5043 static_cast<int64_t>(1));
5044 }
5045
5046 StatsMgr::instance().addValue("assigned-addresses", static_cast<int64_t>(1));
5047
5048 StatsMgr::instance().addValue("cumulative-assigned-addresses", static_cast<int64_t>(1));
5049 }
5050
5051 // We do nothing for SOLICIT. We'll just update database when
5052 // the client gets back to us with REQUEST message.
5053
5054 // it's not really expired at this stage anymore - let's return it as
5055 // an updated lease
5056 return (expired);
5057}
5058
5060AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
5061 CalloutHandle::CalloutNextStep& callout_status) {
5062 ctx.conflicting_lease_.reset();
5063
5064 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
5065 if (exist_lease) {
5066 if (exist_lease->expired()) {
5067 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
5068 // reuseExpiredLease4() will reclaim the lease which will
5069 // queue an NCR remove if needed. Clear the DNS fields in
5070 // the old lease to avoid a redundant remove in server logic.
5071 ctx.old_lease_->hostname_.clear();
5072 ctx.old_lease_->fqdn_fwd_ = false;
5073 ctx.old_lease_->fqdn_rev_ = false;
5074 return (reuseExpiredLease4(exist_lease, ctx, callout_status));
5075
5076 } else {
5077 // If there is a lease and it is not expired, pass this lease back
5078 // to the caller in the context. The caller may need to know
5079 // which lease we're conflicting with.
5080 ctx.conflicting_lease_ = exist_lease;
5081 }
5082
5083 } else {
5084 return (createLease4(ctx, candidate, callout_status));
5085 }
5086 return (Lease4Ptr());
5087}
5088
5090AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
5091 Lease4Ptr new_lease;
5092 ConstSubnet4Ptr subnet = ctx.subnet_;
5093
5094 // Need to check if the subnet belongs to a shared network. If so,
5095 // we might be able to find a better subnet for lease allocation,
5096 // for which it is more likely that there are some leases available.
5097 // If we stick to the selected subnet, we may end up walking over
5098 // the entire subnet (or more subnets) to discover that the address
5099 // pools have been exhausted. Using a subnet from which an address
5100 // was assigned most recently is an optimization which increases
5101 // the likelihood of starting from the subnet which address pools
5102 // are not exhausted.
5103 SharedNetwork4Ptr network;
5104 ctx.subnet_->getSharedNetwork(network);
5105 if (network) {
5106 // This would try to find a subnet with the same set of classes
5107 // as the current subnet, but with the more recent "usage timestamp".
5108 // This timestamp is only updated for the allocations made with an
5109 // allocator (unreserved lease allocations), not the static
5110 // allocations or requested addresses.
5111 ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
5112 }
5113
5114 // We have the choice in the order checking the lease and
5115 // the reservation. The default is to begin by the lease
5116 // if the multi-threading is disabled.
5117 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
5118
5119 ConstSubnet4Ptr original_subnet = subnet;
5120
5121 uint128_t total_attempts = 0;
5122
5123 // The following counter tracks the number of subnets with matching client
5124 // classes from which the allocation engine attempted to assign leases.
5125 uint64_t subnets_with_unavail_leases = 0;
5126 // The following counter tracks the number of subnets in which there were
5127 // no matching pools for the client.
5128 uint64_t subnets_with_unavail_pools = 0;
5129
5130 auto const& classes = ctx.query_->getClasses();
5131
5132 // At least the client identifier or the hardware address can be used
5133 // to identify the client.
5134 bool hwaddr_empty = (!ctx.hwaddr_ || ctx.hwaddr_->hwaddr_.empty());
5135
5136 while (subnet) {
5137 ClientIdPtr client_id;
5138 if (subnet->getMatchClientId() || hwaddr_empty) {
5139 client_id = ctx.clientid_;
5140 }
5141
5142 uint128_t const possible_attempts =
5143 subnet->getPoolCapacity(Lease::TYPE_V4, classes);
5144
5145 // If the number of tries specified in the allocation engine constructor
5146 // is set to 0 (unlimited) or the pools capacity is lower than that number,
5147 // let's use the pools capacity as the maximum number of tries. Trying
5148 // more than the actual pools capacity is a waste of time. If the specified
5149 // number of tries is lower than the pools capacity, use that number.
5150 uint128_t const max_attempts =
5151 (attempts_ == 0 || possible_attempts < attempts_) ?
5152 possible_attempts :
5153 attempts_;
5154
5155 if (max_attempts > 0) {
5156 // If max_attempts is greater than 0, there are some pools in this subnet
5157 // from which we can potentially get a lease.
5158 ++subnets_with_unavail_leases;
5159 } else {
5160 // If max_attempts is 0, it is an indication that there are no pools
5161 // in the subnet from which we can get a lease.
5162 ++subnets_with_unavail_pools;
5163 }
5164
5165 bool exclude_first_last_24 = ((subnet->get().second <= 24) &&
5166 CfgMgr::instance().getCurrentCfg()->getExcludeFirstLast24());
5167
5169
5170 for (uint128_t i = 0; i < max_attempts; ++i) {
5171
5172 ++total_attempts;
5173
5174 auto allocator = subnet->getAllocator(Lease::TYPE_V4);
5175 IOAddress candidate = allocator->pickAddress(classes,
5176 client_id,
5177 ctx.requested_address_);
5178
5179 // An allocator may return zero address when it has pools exhausted.
5180 if (candidate.isV4Zero()) {
5181 break;
5182 }
5183
5184 if (exclude_first_last_24) {
5185 // Exclude .0 and .255 addresses.
5186 auto const& bytes = candidate.toBytes();
5187 if ((bytes.size() != 4) ||
5188 (bytes[3] == 0) || (bytes[3] == 255U)) {
5189 // Don't allocate.
5190 continue;
5191 }
5192 }
5193
5194 // First check for reservation when it is the choice.
5195 if (check_reservation_first && addressReserved(candidate, ctx)) {
5196 // Don't allocate.
5197 continue;
5198 }
5199
5200 // Check if the resource is busy i.e. can be being allocated
5201 // by another thread to another client.
5202 ResourceHandler4 resource_handler;
5203 if (MultiThreadingMgr::instance().getMode() &&
5204 !resource_handler.tryLock4(candidate)) {
5205 // Don't allocate.
5206 continue;
5207 }
5208
5209 // Check for an existing lease for the candidate address.
5210 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
5211 if (!exist_lease) {
5212 // No existing lease, is it reserved?
5213 if (check_reservation_first || !addressReserved(candidate, ctx)) {
5214 // Not reserved use it.
5215 new_lease = createLease4(ctx, candidate, callout_status);
5216 }
5217 } else {
5218 // An lease exists, is expired, and not reserved use it.
5219 if (exist_lease->expired() &&
5220 (check_reservation_first || !addressReserved(candidate, ctx))) {
5221 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
5222 // reuseExpiredLease4() will reclaim the lease which will
5223 // queue an NCR remove if needed. Clear the DNS fields in
5224 // the old lease to avoid a redundant remove in server logic.
5225 ctx.old_lease_->hostname_.clear();
5226 ctx.old_lease_->fqdn_fwd_ = false;
5227 ctx.old_lease_->fqdn_rev_ = false;
5228 new_lease = reuseExpiredLease4(exist_lease, ctx, callout_status);
5229 }
5230 }
5231
5232 // We found a lease we can use, return it.
5233 if (new_lease) {
5234 return (new_lease);
5235 }
5236
5237 if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
5238 // Don't retry when the callout status is not continue.
5239 subnet.reset();
5240 break;
5241 }
5242 }
5243
5244 // This pointer may be set to NULL if hooks set SKIP status.
5245 if (subnet) {
5246 subnet = subnet->getNextSubnet(original_subnet, classes);
5247
5248 if (subnet) {
5249 ctx.subnet_ = subnet;
5250 }
5251 }
5252 }
5253
5254 if (network) {
5255 // The client is in the shared network. Let's log the high level message
5256 // indicating which shared network the client belongs to.
5258 .arg(ctx.query_->getLabel())
5259 .arg(network->getName())
5260 .arg(subnets_with_unavail_leases)
5261 .arg(subnets_with_unavail_pools);
5262 StatsMgr::instance().addValue("v4-allocation-fail-shared-network",
5263 static_cast<int64_t>(1));
5265 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5266 "v4-allocation-fail-shared-network"),
5267 static_cast<int64_t>(1));
5268 } else {
5269 // The client is not connected to a shared network. It is connected
5270 // to a subnet. Let's log some details about the subnet.
5271 std::string shared_network = ctx.subnet_->getSharedNetworkName();
5272 if (shared_network.empty()) {
5273 shared_network = "(none)";
5274 }
5276 .arg(ctx.query_->getLabel())
5277 .arg(ctx.subnet_->toText())
5278 .arg(ctx.subnet_->getID())
5279 .arg(shared_network);
5280 StatsMgr::instance().addValue("v4-allocation-fail-subnet",
5281 static_cast<int64_t>(1));
5283 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5284 "v4-allocation-fail-subnet"),
5285 static_cast<int64_t>(1));
5286 }
5287 if (total_attempts == 0) {
5288 // In this case, it seems that none of the pools in the subnets could
5289 // be used for that client, both in case the client is connected to
5290 // a shared network or to a single subnet. Apparently, the client was
5291 // rejected to use the pools because of the client classes' mismatch.
5293 .arg(ctx.query_->getLabel());
5294 StatsMgr::instance().addValue("v4-allocation-fail-no-pools",
5295 static_cast<int64_t>(1));
5297 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5298 "v4-allocation-fail-no-pools"),
5299 static_cast<int64_t>(1));
5300 } else {
5301 // This is an old log message which provides a number of attempts
5302 // made by the allocation engine to allocate a lease. The only case
5303 // when we don't want to log this message is when the number of
5304 // attempts is zero (condition above), because it would look silly.
5306 .arg(ctx.query_->getLabel())
5307 .arg(total_attempts);
5308 StatsMgr::instance().addValue("v4-allocation-fail",
5309 static_cast<int64_t>(1));
5311 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5312 "v4-allocation-fail"),
5313 static_cast<int64_t>(1));
5314 }
5315
5316 if (!classes.empty()) {
5318 .arg(ctx.query_->getLabel())
5319 .arg(classes.toText());
5320 StatsMgr::instance().addValue("v4-allocation-fail-classes",
5321 static_cast<int64_t>(1));
5323 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
5324 "v4-allocation-fail-classes"),
5325 static_cast<int64_t>(1));
5326 }
5327
5328 return (new_lease);
5329}
5330
5331bool
5332AllocEngine::updateLease4Information(const Lease4Ptr& lease,
5333 AllocEngine::ClientContext4& ctx) const {
5334 bool changed = false;
5335 if (lease->subnet_id_ != ctx.subnet_->getID()) {
5336 changed = true;
5337 lease->subnet_id_ = ctx.subnet_->getID();
5338 }
5339 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
5340 (ctx.hwaddr_ &&
5341 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
5342 changed = true;
5343 lease->hwaddr_ = ctx.hwaddr_;
5344 }
5345 auto clientid = ctx.getClientId();
5346 if (clientid) {
5347 if (!lease->client_id_ || (*clientid != *lease->client_id_)) {
5348 changed = true;
5349 lease->client_id_ = clientid;
5350 }
5351 } else if (lease->client_id_) {
5352 changed = true;
5353 lease->client_id_ = ClientIdPtr();
5354 }
5355 uint32_t remain_lft(0);
5356 getRemaining(lease, remain_lft);
5357 lease->cltt_ = time(0);
5358
5359 // Get the context appropriate valid lifetime.
5360 lease->valid_lft_ = ctx.offer_lft_;
5361 if (!lease->valid_lft_) {
5362 if (useMinValidLft(ctx, lease->addr_)) {
5363 lease->valid_lft_ = remain_lft;
5364 getMinValidLft(ctx, lease->valid_lft_);
5365 } else {
5366 lease->valid_lft_ = getValidLft(ctx);
5367 }
5368 }
5369
5370 // Valid lifetime has changed.
5371 if (lease->valid_lft_ != lease->current_valid_lft_) {
5372 changed = true;
5373 }
5374
5375 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
5376 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
5377 (lease->hostname_ != ctx.hostname_)) {
5378 changed = true;
5379 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
5380 lease->fqdn_rev_ = ctx.rev_dns_update_;
5381 lease->hostname_ = ctx.hostname_;
5382 }
5383
5384 // Add (update) the extended information on the lease.
5385 if (updateLease4ExtendedInfo(lease, ctx)) {
5386 changed = true;
5387 }
5388
5389 return (changed);
5390}
5391
5392bool
5394 const AllocEngine::ClientContext4& ctx) const {
5395 bool changed = false;
5396
5397 // If storage is not enabled then punt.
5398 if (!ctx.subnet_->getStoreExtendedInfo()) {
5399 return (changed);
5400 }
5401
5402 // Look for relay agent information option (option 82)
5403 OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS);
5404 if (!rai) {
5405 // Pkt4 doesn't have it, so nothing to store (or update).
5406 return (changed);
5407 }
5408
5409 // Check if the RAI was recovered from stashed agent options.
5411 getConfiguredGlobal(CfgGlobals::STASH_AGENT_OPTIONS);
5412 if (sao && (sao->getType() == data::Element::boolean) &&
5413 sao->boolValue() && ctx.query_->inClass("STASH_AGENT_OPTIONS")) {
5414 return (changed);
5415 }
5416
5417 // Create a StringElement with the hex string for relay-agent-info.
5418 ElementPtr relay_agent(new StringElement(rai->toHexString()));
5419
5420 // Now we wrap the agent info in a map. This allows for future expansion.
5421 ElementPtr extended_info = Element::createMap();
5422 extended_info->set("sub-options", relay_agent);
5423
5424 OptionPtr remote_id = rai->getOption(RAI_OPTION_REMOTE_ID);
5425 if (remote_id) {
5426 std::vector<uint8_t> bytes = remote_id->toBinary();
5427 lease->remote_id_ = bytes;
5428 if (bytes.size() > 0) {
5429 extended_info->set("remote-id",
5431 }
5432 }
5433
5434 OptionPtr relay_id = rai->getOption(RAI_OPTION_RELAY_ID);
5435 if (relay_id) {
5436 std::vector<uint8_t> bytes = relay_id->toBinary(false);
5437 lease->relay_id_ = bytes;
5438 if (bytes.size() > 0) {
5439 extended_info->set("relay-id",
5441 }
5442 }
5443
5444 // Return true if the extended-info on the lease changed.
5445 return (lease->updateUserContextISC("relay-agent-info", extended_info));
5446}
5447
5448void
5450 const AllocEngine::ClientContext6& ctx) const {
5451 // The extended info action is a transient value but be safe so reset it.
5452 lease->extended_info_action_ = Lease6::ACTION_IGNORE;
5453
5454 // If storage is not enabled then punt.
5455 if (!ctx.subnet_->getStoreExtendedInfo()) {
5456 return;
5457 }
5458
5459 // If we do not have relay information, then punt.
5460 if (ctx.query_->relay_info_.empty()) {
5461 return;
5462 }
5463
5464 // We need to convert the vector of RelayInfo instances in
5465 // into an Element hierarchy like this:
5466 // "relay-info": [
5467 // {
5468 // "hop": 123,
5469 // "link": "2001:db8::1",
5470 // "peer": "2001:db8::2",
5471 // "options": "0x..."
5472 // },..]
5473 //
5474 ElementPtr extended_info = Element::createList();
5475 for (auto const& relay : ctx.query_->relay_info_) {
5476 ElementPtr relay_elem = Element::createMap();
5477 relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
5478 relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
5479 relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
5480
5481 // If there are relay options, we'll pack them into a buffer and then
5482 // convert that into a hex string. If there are no options, we omit
5483 // then entry.
5484 if (!relay.options_.empty()) {
5485 OutputBuffer buf(128);
5486 LibDHCP::packOptions6(buf, relay.options_);
5487
5488 if (buf.getLength() > 0) {
5489 const uint8_t* cp = buf.getData();
5490 std::vector<uint8_t> bytes;
5491 std::stringstream ss;
5492
5493 bytes.assign(cp, cp + buf.getLength());
5494 ss << "0x" << encode::encodeHex(bytes);
5495 relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
5496 }
5497
5498 auto remote_id_it = relay.options_.find(D6O_REMOTE_ID);
5499 if (remote_id_it != relay.options_.end()) {
5500 OptionPtr remote_id = remote_id_it->second;
5501 if (remote_id) {
5502 std::vector<uint8_t> bytes = remote_id->toBinary();
5503 if (bytes.size() > 0) {
5504 relay_elem->set("remote-id",
5506 }
5507 }
5508 }
5509
5510 auto relay_id_it = relay.options_.find(D6O_RELAY_ID);
5511 if (relay_id_it != relay.options_.end()) {
5512 OptionPtr relay_id = relay_id_it->second;
5513 if (relay_id) {
5514 std::vector<uint8_t> bytes = relay_id->toBinary(false);
5515 if (bytes.size() > 0) {
5516 relay_elem->set("relay-id",
5518 }
5519 }
5520 }
5521 }
5522
5523 extended_info->add(relay_elem);
5524 }
5525
5526 // If extended info changed set the action to UPDATE.
5527 if (lease->updateUserContextISC("relay-info", extended_info)) {
5528 lease->extended_info_action_ = Lease6::ACTION_UPDATE;
5529 }
5530}
5531
5532void
5533AllocEngine::setLeaseReusable(const Lease4Ptr& lease,
5534 const ClientContext4& ctx) const {
5535 // Sanity.
5536 lease->reuseable_valid_lft_ = 0;
5537 const ConstSubnet4Ptr& subnet = ctx.subnet_;
5538 if (!subnet) {
5539 return;
5540 }
5541 if (lease->state_ != Lease::STATE_DEFAULT) {
5542 return;
5543 }
5544
5545 // Always reuse infinite lifetime leases.
5546 if (lease->valid_lft_ == Lease::INFINITY_LFT) {
5547 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
5548 return;
5549 }
5550
5551 // Refuse time not going forward.
5552 if (lease->cltt_ < lease->current_cltt_) {
5553 return;
5554 }
5555
5556 uint32_t age = lease->cltt_ - lease->current_cltt_;
5557 // Already expired.
5558 if (age >= lease->current_valid_lft_) {
5559 return;
5560 }
5561
5562 // Try cache max age.
5563 uint32_t max_age = 0;
5564 if (!subnet->getCacheMaxAge().unspecified()) {
5565 max_age = subnet->getCacheMaxAge().get();
5566 if ((max_age == 0) || (age > max_age)) {
5567 return;
5568 }
5569 }
5570
5571 // Try cache threshold.
5572 if (!subnet->getCacheThreshold().unspecified()) {
5573 double threshold = subnet->getCacheThreshold().get();
5574 if ((threshold <= 0.) || (threshold > 1.)) {
5575 return;
5576 }
5577 max_age = lease->valid_lft_ * threshold;
5578 if (age > max_age) {
5579 return;
5580 }
5581 }
5582
5583 // No cache.
5584 if (max_age == 0) {
5585 return;
5586 }
5587
5588 // Seems to be reusable.
5589 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
5590}
5591
5592void
5593AllocEngine::setLeaseReusable(const Lease6Ptr& lease,
5594 uint32_t current_preferred_lft,
5595 const ClientContext6& ctx) const {
5596 // Sanity.
5597 lease->reuseable_valid_lft_ = 0;
5598 lease->reuseable_preferred_lft_ = 0;
5599 const ConstSubnet6Ptr& subnet = ctx.subnet_;
5600 if (!subnet) {
5601 return;
5602 }
5603 if (lease->state_ != Lease::STATE_DEFAULT) {
5604 return;
5605 }
5606
5607 // Refuse time not going forward.
5608 if (lease->cltt_ < lease->current_cltt_) {
5609 return;
5610 }
5611
5612 uint32_t age = lease->cltt_ - lease->current_cltt_;
5613 // Already expired.
5614 if (age >= lease->current_valid_lft_) {
5615 return;
5616 }
5617
5618 // Try cache max age.
5619 uint32_t max_age = 0;
5620 if (!subnet->getCacheMaxAge().unspecified()) {
5621 max_age = subnet->getCacheMaxAge().get();
5622 if ((max_age == 0) || (age > max_age)) {
5623 return;
5624 }
5625 }
5626
5627 // Try cache threshold.
5628 if (!subnet->getCacheThreshold().unspecified()) {
5629 double threshold = subnet->getCacheThreshold().get();
5630 if ((threshold <= 0.) || (threshold > 1.)) {
5631 return;
5632 }
5633 max_age = lease->valid_lft_ * threshold;
5634 if (age > max_age) {
5635 return;
5636 }
5637 }
5638
5639 // No cache.
5640 if (max_age == 0) {
5641 return;
5642 }
5643
5644 // Seems to be reusable.
5645 if ((current_preferred_lft == Lease::INFINITY_LFT) ||
5646 (current_preferred_lft == 0)) {
5647 // Keep these values.
5648 lease->reuseable_preferred_lft_ = current_preferred_lft;
5649 } else if (current_preferred_lft > age) {
5650 lease->reuseable_preferred_lft_ = current_preferred_lft - age;
5651 } else {
5652 // Can be a misconfiguration so stay safe...
5653 return;
5654 }
5655 if (lease->current_valid_lft_ == Lease::INFINITY_LFT) {
5656 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
5657 } else {
5658 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
5659 }
5660}
5661
5662} // namespace dhcp
5663} // 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:64
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:798
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:166
Type
Type of the reservation.
Definition host.h:172
static TrackingLeaseMgr & instance()
Return current lease manager.
Abstract Lease Manager.
Definition lease_mgr.h:276
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
@ RAI_OPTION_RELAY_ID
Definition dhcp4.h:276
@ RAI_OPTION_REMOTE_ID
Definition dhcp4.h:266
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 NameChangeRequest from the DHCPv4 lease and queues it to be sent to D2.
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_REQUESTED_LEASE
const isc::log::MessageID ALLOC_ENGINE_LEASE_RECLAIMED
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
@ DHO_DHCP_AGENT_OPTIONS
Definition dhcp4.h:151
@ DHO_DHCP_LEASE_TIME
Definition dhcp4.h:120
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:846
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:530
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition lease.h:695
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:276
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:166
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:843
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
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition lease.h:522
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:317
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:38
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.
ClientIdPtr getClientId() const
Returns the client identifier when the subnet match-client-id flag is true or the HW address empty.
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.
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:325
@ ACTION_UPDATE
update extended info tables.
Definition lease.h:578
@ ACTION_DELETE
delete reference to the lease
Definition lease.h:577
@ ACTION_IGNORE
ignore extended info,
Definition lease.h:576
a common structure for IPv4 and IPv6 leases
Definition lease.h:33
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire).
Definition lease.h:36
static constexpr uint32_t STATE_DEFAULT
A lease in the default state.
Definition lease.h:71
static constexpr uint32_t STATE_EXPIRED_RECLAIMED
Expired and reclaimed lease.
Definition lease.h:77
static constexpr uint32_t STATE_DECLINED
Declined lease.
Definition lease.h:74
static constexpr uint32_t STATE_REGISTERED
Registered self-generated lease.
Definition lease.h:83
static constexpr uint32_t STATE_RELEASED
Released lease held in the database for lease affinity.
Definition lease.h:80
Type
Type of lease or pool.
Definition lease.h:48
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition lease.h:51
@ TYPE_V4
IPv4 lease.
Definition lease.h:52
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition lease.h:49