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