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