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