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