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