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