Kea 2.7.6
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 ConstSubnet6Ptr 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_(),reused_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 ConstSubnet6Ptr 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 ConstSubnet6Ptr original_subnet = ctx.subnet_;
656
657 ConstSubnet6Ptr 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 ConstSubnet6Ptr 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 ConstSubnet6Ptr 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 (ConstSubnet6Ptr 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 ConstSubnet6Ptr 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 ConstSubnet6Ptr subnet =
2200 network->getSubnet(SubnetID(lease->subnet_id_));
2201 // Found the actual subnet this lease belongs to. Stick to this
2202 // subnet.
2203 if (subnet) {
2204 ctx.subnet_ = subnet;
2205 }
2206 }
2207 }
2208
2209 // If the lease is not global and it is either out of range (NAs only) or it
2210 // is not permitted by subnet client classification, delete it.
2211 if (!(ctx.hasGlobalReservation(makeIPv6Resrv(*lease))) &&
2212 (((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
2213 !ctx.subnet_->clientSupported(ctx.query_->getClasses()))) {
2214 // Oh dear, the lease is no longer valid. We need to get rid of it.
2215
2216 // Remove this lease from LeaseMgr
2217 if (!LeaseMgrFactory::instance().deleteLease(lease)) {
2218 // Concurrent delete performed by other instance which should
2219 // properly handle dns and stats updates.
2220 return;
2221 }
2222
2223 // Updated DNS if required.
2224 queueNCR(CHG_REMOVE, lease);
2225
2226 // Need to decrease statistic for assigned addresses.
2228 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2229 ctx.currentIA().type_ == Lease::TYPE_NA ?
2230 "assigned-nas" : "assigned-pds"),
2231 static_cast<int64_t>(-1));
2232
2233 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2234 if (pool) {
2236 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2237 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2238 "pool" : "pd-pool", pool->getID(),
2239 ctx.currentIA().type_ == Lease::TYPE_NA ?
2240 "assigned-nas" : "assigned-pds")),
2241 static_cast<int64_t>(-1));
2242 }
2243
2244 // Add it to the removed leases list.
2245 ctx.currentIA().old_leases_.push_back(lease);
2246
2247 return;
2248 }
2249
2252 .arg(ctx.query_->getLabel())
2253 .arg(lease->toText());
2254
2255 // Keep the old data in case the callout tells us to skip update.
2256 Lease6Ptr old_data(new Lease6(*lease));
2257
2258 bool changed = false;
2259
2260 // Calculate life times.
2261 uint32_t current_preferred_lft = lease->preferred_lft_;
2262 getLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2263
2264 // If either has changed set the changed flag.
2265 if ((lease->preferred_lft_ != current_preferred_lft) ||
2266 (lease->valid_lft_ != lease->current_valid_lft_)) {
2267 changed = true;
2268 }
2269
2270 lease->cltt_ = time(NULL);
2271 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2272 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
2273 (lease->hostname_ != ctx.hostname_)) {
2274 changed = true;
2275 lease->hostname_ = ctx.hostname_;
2276 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2277 lease->fqdn_rev_ = ctx.rev_dns_update_;
2278 }
2279 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
2280 (ctx.hwaddr_ &&
2281 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
2282 changed = true;
2283 lease->hwaddr_ = ctx.hwaddr_;
2284 }
2285 if (lease->state_ != Lease::STATE_DEFAULT) {
2286 changed = true;
2287 lease->state_ = Lease::STATE_DEFAULT;
2288 }
2291 .arg(ctx.query_->getLabel())
2292 .arg(lease->toText());
2293
2294 bool skip = false;
2295 // Get the callouts specific for the processed message and execute them.
2296 int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
2297 Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
2298 if (HooksManager::calloutsPresent(hook_point)) {
2299 CalloutHandlePtr callout_handle = ctx.callout_handle_;
2300
2301 // Use the RAII wrapper to make sure that the callout handle state is
2302 // reset when this object goes out of scope. All hook points must do
2303 // it to prevent possible circular dependency between the callout
2304 // handle and its arguments.
2305 ScopedCalloutHandleState callout_handle_state(callout_handle);
2306
2307 // Enable copying options from the packet within hook library.
2308 ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2309
2310 // Pass the original packet
2311 callout_handle->setArgument("query6", ctx.query_);
2312
2313 // Pass the lease to be updated
2314 callout_handle->setArgument("lease6", lease);
2315
2316 // Pass the IA option to be sent in response
2317 if (lease->type_ == Lease::TYPE_NA) {
2318 callout_handle->setArgument("ia_na", ctx.currentIA().ia_rsp_);
2319 } else {
2320 callout_handle->setArgument("ia_pd", ctx.currentIA().ia_rsp_);
2321 }
2322
2323 // Call all installed callouts
2324 HooksManager::callCallouts(hook_point, *callout_handle);
2325
2326 // Callouts decided to skip the next processing step. The next
2327 // processing step would actually renew the lease, so skip at this
2328 // stage means "keep the old lease as it is".
2329 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2330 skip = true;
2333 .arg(ctx.query_->getName());
2334 }
2335
2337 }
2338
2339 if (!skip) {
2340 bool update_stats = false;
2341
2342 // If the lease we're renewing has expired, we need to reclaim this
2343 // lease before we can renew it.
2344 if (old_data->expired()) {
2345 reclaimExpiredLease(old_data, ctx.callout_handle_);
2346
2347 // If the lease is in the current subnet we need to account
2348 // for the re-assignment of the lease.
2349 if (ctx.subnet_->inPool(ctx.currentIA().type_, old_data->addr_)) {
2350 update_stats = true;
2351 }
2352 changed = true;
2353 }
2354
2355 // @todo should we call storeLease6ExtendedInfo() here ?
2356 updateLease6ExtendedInfo(lease, ctx);
2357 if (lease->extended_info_action_ == Lease6::ACTION_UPDATE) {
2358 changed = true;
2359 }
2360
2361 // Try to reuse the lease.
2362 if (!changed) {
2363 setLeaseReusable(lease, current_preferred_lft, ctx);
2364 }
2365
2366 // Now that the lease has been reclaimed, we can go ahead and update it
2367 // in the lease database.
2368 if (lease->reuseable_valid_lft_ == 0) {
2369 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2370 if (pool) {
2371 lease->pool_id_ = pool->getID();
2372 }
2374 } else {
2375 // Server looks at changed_leases_ (i.e. old_data) when deciding
2376 // on DNS updates etc.
2377 old_data->reuseable_valid_lft_ = lease->reuseable_valid_lft_;
2378 }
2379
2380 if (update_stats) {
2382 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2383 ctx.currentIA().type_ == Lease::TYPE_NA ?
2384 "assigned-nas" : "assigned-pds"),
2385 static_cast<int64_t>(1));
2386
2388 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2389 ctx.currentIA().type_ == Lease::TYPE_NA ?
2390 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2391 static_cast<int64_t>(1));
2392
2393 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2394 if (pool) {
2396 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2397 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2398 "pool" : "pd-pool", pool->getID(),
2399 ctx.currentIA().type_ == Lease::TYPE_NA ?
2400 "assigned-nas" : "assigned-pds")),
2401 static_cast<int64_t>(1));
2402
2404 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2405 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2406 "pool" : "pd-pool", pool->getID(),
2407 ctx.currentIA().type_ == Lease::TYPE_NA ?
2408 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2409 static_cast<int64_t>(1));
2410 }
2411
2412 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2413 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2414 static_cast<int64_t>(1));
2415 }
2416
2417 } else {
2418 // Copy back the original date to the lease. For MySQL it doesn't make
2419 // much sense, but for memfile, the Lease6Ptr points to the actual lease
2420 // in memfile, so the actual update is performed when we manipulate
2421 // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
2422 *lease = *old_data;
2423 }
2424
2425 // Add the old lease to the changed lease list. This allows the server
2426 // to make decisions regarding DNS updates.
2427 ctx.currentIA().changed_leases_.push_back(old_data);
2428}
2429
2431AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
2432 Lease6Collection updated_leases;
2433 for (auto const& lease_it : leases) {
2434 Lease6Ptr lease(new Lease6(*lease_it));
2435 if (ctx.currentIA().isNewResource(lease->addr_, lease->prefixlen_)) {
2436 // This lease was just created so is already up to date.
2437 updated_leases.push_back(lease);
2438 continue;
2439 }
2440
2441 lease->reuseable_valid_lft_ = 0;
2442 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2443 lease->fqdn_rev_ = ctx.rev_dns_update_;
2444 lease->hostname_ = ctx.hostname_;
2445 uint32_t current_preferred_lft = lease->preferred_lft_;
2446 if (lease->valid_lft_ == 0) {
2447 // The lease was expired by a release: reset zero lifetimes.
2448 getLifetimes6(ctx, lease->preferred_lft_, lease->valid_lft_);
2449 }
2450 if (!ctx.fake_allocation_) {
2451 bool update_stats = false;
2452
2453 if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED || lease->state_ == Lease::STATE_RELEASED) {
2454 // Transition lease state to default (aka assigned)
2455 lease->state_ = Lease::STATE_DEFAULT;
2456
2457 // If the lease is in the current subnet we need to account
2458 // for the re-assignment of the lease.
2459 if (inAllowedPool(ctx, ctx.currentIA().type_,
2460 lease->addr_, true)) {
2461 update_stats = true;
2462 }
2463 }
2464
2465 bool fqdn_changed = ((lease->type_ != Lease::TYPE_PD) &&
2466 !(lease->hasIdenticalFqdn(*lease_it)));
2467
2468 lease->cltt_ = time(NULL);
2469 if (!fqdn_changed) {
2470 setLeaseReusable(lease, current_preferred_lft, ctx);
2471 }
2472
2473 if (lease->reuseable_valid_lft_ == 0) {
2474 ctx.currentIA().changed_leases_.push_back(lease_it);
2476 } else {
2477 // Server needs to know about resused leases to avoid DNS updates.
2478 ctx.currentIA().reused_leases_.push_back(lease_it);
2479 }
2480
2481 if (update_stats) {
2483 StatsMgr::generateName("subnet", lease->subnet_id_,
2484 ctx.currentIA().type_ == Lease::TYPE_NA ?
2485 "assigned-nas" : "assigned-pds"),
2486 static_cast<int64_t>(1));
2487
2489 StatsMgr::generateName("subnet", lease->subnet_id_,
2490 ctx.currentIA().type_ == Lease::TYPE_NA ?
2491 "cumulative-assigned-nas" : "cumulative-assigned-pds"),
2492 static_cast<int64_t>(1));
2493
2494 auto const& pool = ctx.subnet_->getPool(ctx.currentIA().type_, lease->addr_, false);
2495 if (pool) {
2497 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2498 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2499 "pool" : "pd-pool", pool->getID(),
2500 ctx.currentIA().type_ == Lease::TYPE_NA ?
2501 "assigned-nas" : "assigned-pds")),
2502 static_cast<int64_t>(1));
2503
2505 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2506 StatsMgr::generateName(ctx.currentIA().type_ == Lease::TYPE_NA ?
2507 "pool" : "pd-pool", pool->getID(),
2508 ctx.currentIA().type_ == Lease::TYPE_NA ?
2509 "cumulative-assigned-nas" : "cumulative-assigned-pds")),
2510 static_cast<int64_t>(1));
2511 }
2512
2513 StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2514 "cumulative-assigned-nas" : "cumulative-assigned-pds",
2515 static_cast<int64_t>(1));
2516 }
2517 }
2518
2519 updated_leases.push_back(lease);
2520 }
2521
2522 return (updated_leases);
2523}
2524
2525void
2527 const uint16_t timeout,
2528 const bool remove_lease,
2529 const uint16_t max_unwarned_cycles) {
2530
2533 .arg(max_leases)
2534 .arg(timeout);
2535
2536 try {
2537 reclaimExpiredLeases6Internal(max_leases, timeout, remove_lease,
2538 max_unwarned_cycles);
2539 } catch (const std::exception& ex) {
2542 .arg(ex.what());
2543 }
2544}
2545
2546void
2548 const uint16_t timeout,
2549 const bool remove_lease,
2550 const uint16_t max_unwarned_cycles) {
2551
2552 // Create stopwatch and automatically start it to measure the time
2553 // taken by the routine.
2554 util::Stopwatch stopwatch;
2555
2556 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2557
2558 // This value indicates if we have been able to deal with all expired
2559 // leases in this pass.
2560 bool incomplete_reclamation = false;
2561 Lease6Collection leases;
2562 // The value of 0 has a special meaning - reclaim all.
2563 if (max_leases > 0) {
2564 // If the value is non-zero, the caller has limited the number of
2565 // leases to reclaim. We obtain one lease more to see if there will
2566 // be still leases left after this pass.
2567 lease_mgr.getExpiredLeases6(leases, max_leases + 1);
2568 // There are more leases expired leases than we will process in this
2569 // pass, so we should mark it as an incomplete reclamation. We also
2570 // remove this extra lease (which we don't want to process anyway)
2571 // from the collection.
2572 if (leases.size() > max_leases) {
2573 leases.pop_back();
2574 incomplete_reclamation = true;
2575 }
2576
2577 } else {
2578 // If there is no limitation on the number of leases to reclaim,
2579 // we will try to process all. Hence, we don't mark it as incomplete
2580 // reclamation just yet.
2581 lease_mgr.getExpiredLeases6(leases, max_leases);
2582 }
2583
2584 // Do not initialize the callout handle until we know if there are any
2585 // lease6_expire callouts installed.
2586 CalloutHandlePtr callout_handle;
2587 if (!leases.empty() &&
2588 HooksManager::calloutsPresent(Hooks.hook_index_lease6_expire_)) {
2589 callout_handle = HooksManager::createCalloutHandle();
2590 }
2591
2592 size_t leases_processed = 0;
2593 for (auto const& lease : leases) {
2594
2595 try {
2596 // Reclaim the lease.
2597 if (MultiThreadingMgr::instance().getMode()) {
2598 // The reclamation is exclusive of packet processing.
2599 WriteLockGuard exclusive(rw_mutex_);
2600
2601 reclaimExpiredLease(lease, remove_lease, callout_handle);
2602 ++leases_processed;
2603 } else {
2604 reclaimExpiredLease(lease, remove_lease, callout_handle);
2605 ++leases_processed;
2606 }
2607
2608 } catch (const std::exception& ex) {
2610 .arg(lease->addr_.toText())
2611 .arg(ex.what());
2612 }
2613
2614 // Check if we have hit the timeout for running reclamation routine and
2615 // return if we have. We're checking it here, because we always want to
2616 // allow reclaiming at least one lease.
2617 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2618 // Timeout. This will likely mean that we haven't been able to process
2619 // all leases we wanted to process. The reclamation pass will be
2620 // probably marked as incomplete.
2621 if (!incomplete_reclamation) {
2622 if (leases_processed < leases.size()) {
2623 incomplete_reclamation = true;
2624 }
2625 }
2626
2629 .arg(timeout);
2630 break;
2631 }
2632 }
2633
2634 // Stop measuring the time.
2635 stopwatch.stop();
2636
2637 // Mark completion of the lease reclamation routine and present some stats.
2640 .arg(leases_processed)
2641 .arg(stopwatch.logFormatTotalDuration());
2642
2643 // Check if this was an incomplete reclamation and increase the number of
2644 // consecutive incomplete reclamations.
2645 if (incomplete_reclamation) {
2646 ++incomplete_v6_reclamations_;
2647 // If the number of incomplete reclamations is beyond the threshold, we
2648 // need to issue a warning.
2649 if ((max_unwarned_cycles > 0) &&
2650 (incomplete_v6_reclamations_ > max_unwarned_cycles)) {
2652 .arg(max_unwarned_cycles);
2653 // We issued a warning, so let's now reset the counter.
2654 incomplete_v6_reclamations_ = 0;
2655 }
2656
2657 } else {
2658 // This was a complete reclamation, so let's reset the counter.
2659 incomplete_v6_reclamations_ = 0;
2660
2663 }
2664}
2665
2666void
2670 .arg(secs);
2671
2672 uint64_t deleted_leases = 0;
2673 try {
2674 // Try to delete leases from the lease database.
2675 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2676 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
2677
2678 } catch (const std::exception& ex) {
2680 .arg(ex.what());
2681 }
2682
2685 .arg(deleted_leases);
2686}
2687
2688void
2690 const uint16_t timeout,
2691 const bool remove_lease,
2692 const uint16_t max_unwarned_cycles) {
2693
2696 .arg(max_leases)
2697 .arg(timeout);
2698
2699 try {
2700 reclaimExpiredLeases4Internal(max_leases, timeout, remove_lease,
2701 max_unwarned_cycles);
2702 } catch (const std::exception& ex) {
2705 .arg(ex.what());
2706 }
2707}
2708
2709void
2711 const uint16_t timeout,
2712 const bool remove_lease,
2713 const uint16_t max_unwarned_cycles) {
2714
2715 // Create stopwatch and automatically start it to measure the time
2716 // taken by the routine.
2717 util::Stopwatch stopwatch;
2718
2719 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2720
2721 // This value indicates if we have been able to deal with all expired
2722 // leases in this pass.
2723 bool incomplete_reclamation = false;
2724 Lease4Collection leases;
2725 // The value of 0 has a special meaning - reclaim all.
2726 if (max_leases > 0) {
2727 // If the value is non-zero, the caller has limited the number of
2728 // leases to reclaim. We obtain one lease more to see if there will
2729 // be still leases left after this pass.
2730 lease_mgr.getExpiredLeases4(leases, max_leases + 1);
2731 // There are more leases expired leases than we will process in this
2732 // pass, so we should mark it as an incomplete reclamation. We also
2733 // remove this extra lease (which we don't want to process anyway)
2734 // from the collection.
2735 if (leases.size() > max_leases) {
2736 leases.pop_back();
2737 incomplete_reclamation = true;
2738 }
2739
2740 } else {
2741 // If there is no limitation on the number of leases to reclaim,
2742 // we will try to process all. Hence, we don't mark it as incomplete
2743 // reclamation just yet.
2744 lease_mgr.getExpiredLeases4(leases, max_leases);
2745 }
2746
2747 // Do not initialize the callout handle until we know if there are any
2748 // lease4_expire callouts installed.
2749 CalloutHandlePtr callout_handle;
2750 if (!leases.empty() &&
2751 HooksManager::calloutsPresent(Hooks.hook_index_lease4_expire_)) {
2752 callout_handle = HooksManager::createCalloutHandle();
2753 }
2754
2755 size_t leases_processed = 0;
2756 for (auto const& lease : leases) {
2757
2758 try {
2759 // Reclaim the lease.
2760 if (MultiThreadingMgr::instance().getMode()) {
2761 // The reclamation is exclusive of packet processing.
2762 WriteLockGuard exclusive(rw_mutex_);
2763
2764 reclaimExpiredLease(lease, remove_lease, callout_handle);
2765 ++leases_processed;
2766 } else {
2767 reclaimExpiredLease(lease, remove_lease, callout_handle);
2768 ++leases_processed;
2769 }
2770
2771 } catch (const std::exception& ex) {
2773 .arg(lease->addr_.toText())
2774 .arg(ex.what());
2775 }
2776
2777 // Check if we have hit the timeout for running reclamation routine and
2778 // return if we have. We're checking it here, because we always want to
2779 // allow reclaiming at least one lease.
2780 if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2781 // Timeout. This will likely mean that we haven't been able to process
2782 // all leases we wanted to process. The reclamation pass will be
2783 // probably marked as incomplete.
2784 if (!incomplete_reclamation) {
2785 if (leases_processed < leases.size()) {
2786 incomplete_reclamation = true;
2787 }
2788 }
2789
2792 .arg(timeout);
2793 break;
2794 }
2795 }
2796
2797 // Stop measuring the time.
2798 stopwatch.stop();
2799
2800 // Mark completion of the lease reclamation routine and present some stats.
2803 .arg(leases_processed)
2804 .arg(stopwatch.logFormatTotalDuration());
2805
2806 // Check if this was an incomplete reclamation and increase the number of
2807 // consecutive incomplete reclamations.
2808 if (incomplete_reclamation) {
2809 ++incomplete_v4_reclamations_;
2810 // If the number of incomplete reclamations is beyond the threshold, we
2811 // need to issue a warning.
2812 if ((max_unwarned_cycles > 0) &&
2813 (incomplete_v4_reclamations_ > max_unwarned_cycles)) {
2815 .arg(max_unwarned_cycles);
2816 // We issued a warning, so let's now reset the counter.
2817 incomplete_v4_reclamations_ = 0;
2818 }
2819
2820 } else {
2821 // This was a complete reclamation, so let's reset the counter.
2822 incomplete_v4_reclamations_ = 0;
2823
2826 }
2827}
2828
2829template<typename LeasePtrType>
2830void
2831AllocEngine::reclaimExpiredLease(const LeasePtrType& lease, const bool remove_lease,
2832 const CalloutHandlePtr& callout_handle) {
2833 reclaimExpiredLease(lease, remove_lease ? DB_RECLAIM_REMOVE : DB_RECLAIM_UPDATE,
2834 callout_handle);
2835}
2836
2837template<typename LeasePtrType>
2838void
2839AllocEngine::reclaimExpiredLease(const LeasePtrType& lease,
2840 const CalloutHandlePtr& callout_handle) {
2841 // This variant of the method is used by the code which allocates or
2842 // renews leases. It may be the case that the lease has already been
2843 // reclaimed, so there is nothing to do.
2844 if (!lease->stateExpiredReclaimed()) {
2845 reclaimExpiredLease(lease, DB_RECLAIM_LEAVE_UNCHANGED, callout_handle);
2846 }
2847}
2848
2849void
2850AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
2851 const DbReclaimMode& reclaim_mode,
2852 const CalloutHandlePtr& callout_handle) {
2853
2856 .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
2857 .arg(lease->addr_.toText())
2858 .arg(static_cast<int>(lease->prefixlen_));
2859
2860 // The skip flag indicates if the callouts have taken responsibility
2861 // for reclaiming the lease. The callout will set this to true if
2862 // it reclaims the lease itself. In this case the reclamation routine
2863 // will not update DNS nor update the database.
2864 bool skipped = false;
2865 if (callout_handle) {
2866
2867 // Use the RAII wrapper to make sure that the callout handle state is
2868 // reset when this object goes out of scope. All hook points must do
2869 // it to prevent possible circular dependency between the callout
2870 // handle and its arguments.
2871 ScopedCalloutHandleState callout_handle_state(callout_handle);
2872
2873 callout_handle->deleteAllArguments();
2874 callout_handle->setArgument("lease6", lease);
2875 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2876
2877 HooksManager::callCallouts(Hooks.hook_index_lease6_expire_,
2878 *callout_handle);
2879
2880 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2881 }
2882
2885
2886 if (!skipped) {
2887
2888 // Generate removal name change request for D2, if required.
2889 // This will return immediately if the DNS wasn't updated
2890 // when the lease was created.
2891 queueNCR(CHG_REMOVE, lease);
2892
2893 // Let's check if the lease that just expired is in DECLINED state.
2894 // If it is, we need to perform a couple extra steps.
2895 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2896 if (lease->state_ == Lease::STATE_DECLINED) {
2897 // Do extra steps required for declined lease reclamation:
2898 // - call the recover hook
2899 // - bump decline-related stats
2900 // - log separate message
2901 // There's no point in keeping a declined lease after its
2902 // reclamation. A declined lease doesn't have any client
2903 // identifying information anymore. So we'll flag it for
2904 // removal unless the hook has set the skip flag.
2905 remove_lease = reclaimDeclined(lease);
2906 }
2907
2908 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2909 // Reclaim the lease - depending on the configuration, set the
2910 // expired-reclaimed state or simply remove it.
2911 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2912 reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
2913 std::bind(&LeaseMgr::updateLease6,
2914 &lease_mgr, ph::_1));
2915 }
2916 }
2917
2918 // Update statistics.
2919
2920 // Increase number of reclaimed leases for a subnet.
2922 lease->subnet_id_,
2923 "reclaimed-leases"),
2924 static_cast<int64_t>(1));
2925
2926 // Increase total number of reclaimed leases.
2927 StatsMgr::instance().addValue("reclaimed-leases", static_cast<int64_t>(1));
2928
2929 // Statistics must have been updated during the release.
2930 if (lease->state_ == Lease::STATE_RELEASED) {
2931 return;
2932 }
2933
2934 // Decrease number of assigned leases.
2935 if (lease->type_ == Lease::TYPE_NA) {
2936 // IA_NA
2938 lease->subnet_id_,
2939 "assigned-nas"),
2940 static_cast<int64_t>(-1));
2941
2942 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
2943 if (subnet) {
2944 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
2945 if (pool) {
2947 StatsMgr::generateName("subnet", subnet->getID(),
2948 StatsMgr::generateName("pool" , pool->getID(),
2949 "assigned-nas")),
2950 static_cast<int64_t>(-1));
2951
2953 StatsMgr::generateName("subnet", subnet->getID(),
2954 StatsMgr::generateName("pool" , pool->getID(),
2955 "reclaimed-leases")),
2956 static_cast<int64_t>(1));
2957 }
2958 }
2959
2960 } else if (lease->type_ == Lease::TYPE_PD) {
2961 // IA_PD
2963 lease->subnet_id_,
2964 "assigned-pds"),
2965 static_cast<int64_t>(-1));
2966
2967 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
2968 if (subnet) {
2969 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
2970 if (pool) {
2972 StatsMgr::generateName("subnet", subnet->getID(),
2973 StatsMgr::generateName("pd-pool" , pool->getID(),
2974 "assigned-pds")),
2975 static_cast<int64_t>(-1));
2976
2978 StatsMgr::generateName("subnet", subnet->getID(),
2979 StatsMgr::generateName("pd-pool" , pool->getID(),
2980 "reclaimed-leases")),
2981 static_cast<int64_t>(1));
2982 }
2983 }
2984 }
2985}
2986
2987void
2988AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
2989 const DbReclaimMode& reclaim_mode,
2990 const CalloutHandlePtr& callout_handle) {
2991
2994 .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
2995 .arg(lease->addr_.toText());
2996
2997 // The skip flag indicates if the callouts have taken responsibility
2998 // for reclaiming the lease. The callout will set this to true if
2999 // it reclaims the lease itself. In this case the reclamation routine
3000 // will not update DNS nor update the database.
3001 bool skipped = false;
3002 if (callout_handle) {
3003
3004 // Use the RAII wrapper to make sure that the callout handle state is
3005 // reset when this object goes out of scope. All hook points must do
3006 // it to prevent possible circular dependency between the callout
3007 // handle and its arguments.
3008 ScopedCalloutHandleState callout_handle_state(callout_handle);
3009
3010 callout_handle->setArgument("lease4", lease);
3011 callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
3012
3013 HooksManager::callCallouts(Hooks.hook_index_lease4_expire_,
3014 *callout_handle);
3015
3016 skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
3017 }
3018
3021
3022 if (!skipped) {
3023
3024 // Generate removal name change request for D2, if required.
3025 // This will return immediately if the DNS wasn't updated
3026 // when the lease was created.
3027 queueNCR(CHG_REMOVE, lease);
3028 // Clear DNS fields so we avoid redundant removes.
3029 lease->hostname_.clear();
3030 lease->fqdn_fwd_ = false;
3031 lease->fqdn_rev_ = false;
3032
3033 // Let's check if the lease that just expired is in DECLINED state.
3034 // If it is, we need to perform a couple extra steps.
3035 bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
3036 if (lease->state_ == Lease::STATE_DECLINED) {
3037 // Do extra steps required for declined lease reclamation:
3038 // - call the recover hook
3039 // - bump decline-related stats
3040 // - log separate message
3041 // There's no point in keeping a declined lease after its
3042 // reclamation. A declined lease doesn't have any client
3043 // identifying information anymore. So we'll flag it for
3044 // removal unless the hook has set the skip flag.
3045 remove_lease = reclaimDeclined(lease);
3046 }
3047
3048 if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
3049 // Reclaim the lease - depending on the configuration, set the
3050 // expired-reclaimed state or simply remove it.
3051 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3052 reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
3053 std::bind(&LeaseMgr::updateLease4,
3054 &lease_mgr, ph::_1));
3055 }
3056 }
3057
3058 // Update statistics.
3059
3060 // Increase total number of reclaimed leases.
3061 StatsMgr::instance().addValue("reclaimed-leases", static_cast<int64_t>(1));
3062
3063 // Increase number of reclaimed leases for a subnet.
3065 lease->subnet_id_,
3066 "reclaimed-leases"),
3067 static_cast<int64_t>(1));
3068
3069 // Statistics must have been updated during the release.
3070 if (lease->state_ == Lease4::STATE_RELEASED) {
3071 return;
3072 }
3073
3074 // Decrease number of assigned addresses.
3076 lease->subnet_id_,
3077 "assigned-addresses"),
3078 static_cast<int64_t>(-1));
3079
3080 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease->subnet_id_);
3081 if (subnet) {
3082 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3083 if (pool) {
3085 StatsMgr::generateName("subnet", subnet->getID(),
3086 StatsMgr::generateName("pool" , pool->getID(),
3087 "assigned-addresses")),
3088 static_cast<int64_t>(-1));
3089
3091 StatsMgr::generateName("subnet", subnet->getID(),
3092 StatsMgr::generateName("pool" , pool->getID(),
3093 "reclaimed-leases")),
3094 static_cast<int64_t>(1));
3095 }
3096 }
3097}
3098
3099void
3103 .arg(secs);
3104
3105 uint64_t deleted_leases = 0;
3106 try {
3107 // Try to delete leases from the lease database.
3108 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3109 deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
3110
3111 } catch (const std::exception& ex) {
3113 .arg(ex.what());
3114 }
3115
3118 .arg(deleted_leases);
3119}
3120
3121bool
3122AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
3123 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3124 return (true);
3125 }
3126
3127 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_recover_)) {
3129
3130 // Use the RAII wrapper to make sure that the callout handle state is
3131 // reset when this object goes out of scope. All hook points must do
3132 // it to prevent possible circular dependency between the callout
3133 // handle and its arguments.
3134 ScopedCalloutHandleState callout_handle_state(callout_handle);
3135
3136 // Pass necessary arguments
3137 callout_handle->setArgument("lease4", lease);
3138
3139 // Call the callouts
3140 HooksManager::callCallouts(Hooks.hook_index_lease4_recover_, *callout_handle);
3141
3142 // Callouts decided to skip the action. This means that the lease is not
3143 // assigned, so the client will get NoAddrAvail as a result. The lease
3144 // won't be inserted into the database.
3145 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3147 .arg(lease->addr_.toText());
3148 return (false);
3149 }
3150 }
3151
3153 .arg(lease->addr_.toText())
3154 .arg(lease->valid_lft_);
3155
3156 StatsMgr& stats_mgr = StatsMgr::instance();
3157
3158 // Decrease subnet specific counter for currently declined addresses
3159 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3160 "declined-addresses"),
3161 static_cast<int64_t>(-1));
3162
3163 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3164 "reclaimed-declined-addresses"),
3165 static_cast<int64_t>(1));
3166
3167 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(lease->subnet_id_);
3168 if (subnet) {
3169 auto const& pool = subnet->getPool(Lease::TYPE_V4, lease->addr_, false);
3170 if (pool) {
3171 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3172 StatsMgr::generateName("pool" , pool->getID(),
3173 "declined-addresses")),
3174 static_cast<int64_t>(-1));
3175
3176 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3177 StatsMgr::generateName("pool" , pool->getID(),
3178 "reclaimed-declined-addresses")),
3179 static_cast<int64_t>(1));
3180 }
3181 }
3182
3183 // Decrease global counter for declined addresses
3184 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3185
3186 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3187
3188 // Note that we do not touch assigned-addresses counters. Those are
3189 // modified in whatever code calls this method.
3190 return (true);
3191}
3192
3193bool
3194AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
3195 if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3196 return (true);
3197 }
3198
3199 if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_recover_)) {
3201
3202 // Use the RAII wrapper to make sure that the callout handle state is
3203 // reset when this object goes out of scope. All hook points must do
3204 // it to prevent possible circular dependency between the callout
3205 // handle and its arguments.
3206 ScopedCalloutHandleState callout_handle_state(callout_handle);
3207
3208 // Pass necessary arguments
3209 callout_handle->setArgument("lease6", lease);
3210
3211 // Call the callouts
3212 HooksManager::callCallouts(Hooks.hook_index_lease6_recover_, *callout_handle);
3213
3214 // Callouts decided to skip the action. This means that the lease is not
3215 // assigned, so the client will get NoAddrAvail as a result. The lease
3216 // won't be inserted into the database.
3217 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3219 .arg(lease->addr_.toText());
3220 return (false);
3221 }
3222 }
3223
3225 .arg(lease->addr_.toText())
3226 .arg(lease->valid_lft_);
3227
3228 StatsMgr& stats_mgr = StatsMgr::instance();
3229
3230 // Decrease subnet specific counter for currently declined addresses
3231 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3232 "declined-addresses"),
3233 static_cast<int64_t>(-1));
3234
3235 stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3236 "reclaimed-declined-addresses"),
3237 static_cast<int64_t>(1));
3238
3239 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getBySubnetId(lease->subnet_id_);
3240 if (subnet) {
3241 auto const& pool = subnet->getPool(lease->type_, lease->addr_, false);
3242 if (pool) {
3243 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3244 StatsMgr::generateName("pool" , pool->getID(),
3245 "declined-addresses")),
3246 static_cast<int64_t>(-1));
3247
3248 stats_mgr.addValue(StatsMgr::generateName("subnet", subnet->getID(),
3249 StatsMgr::generateName("pool" , pool->getID(),
3250 "reclaimed-declined-addresses")),
3251 static_cast<int64_t>(1));
3252 }
3253 }
3254
3255 // Decrease global counter for declined addresses
3256 stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3257
3258 stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3259
3260 // Note that we do not touch assigned-nas counters. Those are
3261 // modified in whatever code calls this method.
3262
3263 return (true);
3264}
3265
3266void
3268 lease->relay_id_.clear();
3269 lease->remote_id_.clear();
3270 if (lease->getContext()) {
3271 lease->setContext(ElementPtr());
3272 }
3273}
3274
3275void
3277 if (lease->getContext()) {
3278 lease->extended_info_action_ = Lease6::ACTION_DELETE;
3279 lease->setContext(ElementPtr());
3280 }
3281}
3282
3283template<typename LeasePtrType>
3284void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
3285 const bool remove_lease,
3286 const std::function<void (const LeasePtrType&)>&
3287 lease_update_fun) const {
3288
3289 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3290
3291 // Reclaim the lease - depending on the configuration, set the
3292 // expired-reclaimed state or simply remove it.
3293 if (remove_lease) {
3294 lease_mgr.deleteLease(lease);
3295 } else if (lease_update_fun) {
3296 // Clear FQDN information as we have already sent the
3297 // name change request to remove the DNS record.
3298 lease->reuseable_valid_lft_ = 0;
3299 lease->hostname_.clear();
3300 lease->fqdn_fwd_ = false;
3301 lease->fqdn_rev_ = false;
3302 lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
3304 lease_update_fun(lease);
3305
3306 } else {
3307 return;
3308 }
3309
3310 // Lease has been reclaimed.
3313 .arg(lease->addr_.toText());
3314}
3315
3316std::string
3318 if (!subnet) {
3319 return("<empty subnet>");
3320 }
3321
3322 SharedNetwork4Ptr network;
3323 subnet->getSharedNetwork(network);
3324 std::ostringstream ss;
3325 if (network) {
3326 ss << "shared-network: " << network->getName();
3327 } else {
3328 ss << "subnet id: " << subnet->getID();
3329 }
3330
3331 return(ss.str());
3332}
3333
3334} // namespace dhcp
3335} // namespace isc
3336
3337// ##########################################################################
3338// # DHCPv4 lease allocation code starts here.
3339// ##########################################################################
3340
3341namespace {
3342
3360bool
3361addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
3362 // When out-of-pool flag is true the server may assume that all host
3363 // reservations are for addresses that do not belong to the dynamic pool.
3364 // Therefore, it can skip the reservation checks when dealing with in-pool
3365 // addresses.
3366 if (ctx.subnet_ && ctx.subnet_->getReservationsInSubnet() &&
3367 (!ctx.subnet_->getReservationsOutOfPool() ||
3368 !ctx.subnet_->inPool(Lease::TYPE_V4, address))) {
3369 // The global parameter ip-reservations-unique controls whether it is allowed
3370 // to specify multiple reservations for the same IP address or delegated prefix
3371 // or IP reservations must be unique. Some host backends do not support the
3372 // former, thus we can't always use getAll4 calls to get the reservations
3373 // for the given IP. When we're in the default mode, when IP reservations
3374 // are unique, we should call get4 (supported by all backends). If we're in
3375 // the mode in which non-unique reservations are allowed the backends which
3376 // don't support it are not used and we can safely call getAll4.
3377 ConstHostCollection hosts;
3378 if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
3379 try {
3380 // Reservations are unique. It is safe to call get4 to get the unique host.
3381 ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
3382 if (host) {
3383 hosts.push_back(host);
3384 }
3385 } catch (const MultipleRecords& ex) {
3387 .arg(address)
3388 .arg(ctx.subnet_->getID())
3389 .arg(ex.what());
3390 throw;
3391 }
3392 } else {
3393 // Reservations can be non-unique. Need to get all reservations for that address.
3394 hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
3395 }
3396
3397 for (auto const& host : hosts) {
3398 for (const AllocEngine::IdentifierPair& id_pair : ctx.host_identifiers_) {
3399 // If we find the matching host we know that this address is reserved
3400 // for us and we can return immediately.
3401 if (id_pair.first == host->getIdentifierType() &&
3402 id_pair.second == host->getIdentifier()) {
3403 return (false);
3404 }
3405 }
3406 }
3407 // We didn't find a matching host. If there are any reservations it means that
3408 // address is reserved for another client or multiple clients. If there are
3409 // no reservations address is not reserved for another client.
3410 return (!hosts.empty());
3411 }
3412 return (false);
3413}
3414
3430bool
3431hasAddressReservation(AllocEngine::ClientContext4& ctx) {
3432 if (ctx.hosts_.empty()) {
3433 return (false);
3434 }
3435
3436 // Fetch the globally reserved address if there is one.
3437 auto global_host = ctx.hosts_.find(SUBNET_ID_GLOBAL);
3438 auto global_host_address = ((global_host != ctx.hosts_.end() && global_host->second) ?
3439 global_host->second->getIPv4Reservation() :
3441
3442 // Start with currently selected subnet.
3443 ConstSubnet4Ptr subnet = ctx.subnet_;
3444 while (subnet) {
3445 // If global reservations are enabled for this subnet and there is
3446 // globally reserved address and that address is feasible for this
3447 // subnet, update the selected subnet and return true.
3448 if (subnet->getReservationsGlobal() &&
3449 (global_host_address != IOAddress::IPV4_ZERO_ADDRESS()) &&
3450 (subnet->inRange(global_host_address))) {
3451 ctx.subnet_ = subnet;
3452 return (true);
3453 }
3454
3455 if (subnet->getReservationsInSubnet()) {
3456 auto host = ctx.hosts_.find(subnet->getID());
3457 // The out-of-pool flag indicates that no client should be assigned
3458 // reserved addresses from within the dynamic pool, and for that
3459 // reason look only for reservations that are outside the pools,
3460 // hence the inPool check.
3461 if (host != ctx.hosts_.end() && host->second) {
3462 auto reservation = host->second->getIPv4Reservation();
3463 if (!reservation.isV4Zero() &&
3464 (!subnet->getReservationsOutOfPool() ||
3465 !subnet->inPool(Lease::TYPE_V4, reservation))) {
3466 ctx.subnet_ = subnet;
3467 return (true);
3468 }
3469 }
3470 }
3471
3472 // No address reservation found here, so let's try another subnet
3473 // within the same shared network.
3474 subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3475 }
3476
3477 if (global_host_address != IOAddress::IPV4_ZERO_ADDRESS()) {
3480 .arg(ctx.query_->getLabel())
3481 .arg(ctx.currentHost()->getIPv4Reservation().toText())
3483 }
3484
3485 return (false);
3486}
3487
3503void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
3504 LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3505
3506 ConstSubnet4Ptr original_subnet = ctx.subnet_;
3507
3508 auto const& classes = ctx.query_->getClasses();
3509
3510 // Client identifier is optional. First check if we can try to lookup
3511 // by client-id.
3512 bool try_clientid_lookup = (ctx.clientid_ &&
3513 SharedNetwork4::subnetsIncludeMatchClientId(original_subnet, classes));
3514
3515 // If it is possible to use client identifier to try to find client's lease.
3516 if (try_clientid_lookup) {
3517 // Get all leases for this client identifier. When shared networks are
3518 // in use it is more efficient to make a single query rather than
3519 // multiple queries, one for each subnet.
3520 Lease4Collection leases_client_id = lease_mgr.getLease4(*ctx.clientid_);
3521
3522 // Iterate over the subnets within the shared network to see if any client's
3523 // lease belongs to them.
3524 for (ConstSubnet4Ptr subnet = original_subnet; subnet;
3525 subnet = subnet->getNextSubnet(original_subnet, classes)) {
3526
3527 // If client identifier has been supplied and the server wasn't
3528 // explicitly configured to ignore client identifiers for this subnet
3529 // check if there is a lease within this subnet.
3530 if (subnet->getMatchClientId()) {
3531 for (auto const& l : leases_client_id) {
3532 if (l->subnet_id_ == subnet->getID()) {
3533 // Lease found, so stick to this lease.
3534 client_lease = l;
3535 ctx.subnet_ = subnet;
3536 return;
3537 }
3538 }
3539 }
3540 }
3541 }
3542
3543 // If no lease found using the client identifier, try the lookup using
3544 // the HW address.
3545 if (!client_lease && ctx.hwaddr_) {
3546
3547 // Get all leases for this HW address.
3548 Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
3549
3550 for (ConstSubnet4Ptr subnet = original_subnet; subnet;
3551 subnet = subnet->getNextSubnet(original_subnet, classes)) {
3552 ClientIdPtr client_id;
3553 if (subnet->getMatchClientId()) {
3554 client_id = ctx.clientid_;
3555 }
3556
3557 // Try to find the lease that matches current subnet and belongs to
3558 // this client, so both HW address and client identifier match.
3559 for (auto const& client_lease_it : leases_hw_address) {
3560 Lease4Ptr existing_lease = client_lease_it;
3561 if ((existing_lease->subnet_id_ == subnet->getID()) &&
3562 existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
3563 // Found the lease of this client, so return it.
3564 client_lease = existing_lease;
3565 // We got a lease but the subnet it belongs to may differ from
3566 // the original subnet. Let's now stick to this subnet.
3567 ctx.subnet_ = subnet;
3568 return;
3569 }
3570 }
3571 }
3572 }
3573}
3574
3587bool
3588inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
3589 // If the subnet belongs to a shared network we will be iterating
3590 // over the subnets that belong to this shared network.
3591 ConstSubnet4Ptr current_subnet = ctx.subnet_;
3592 auto const& classes = ctx.query_->getClasses();
3593
3594 while (current_subnet) {
3595 if (current_subnet->inPool(Lease::TYPE_V4, address, classes)) {
3596 // We found a subnet that this address belongs to, so it
3597 // seems that this subnet is the good candidate for allocation.
3598 // Let's update the selected subnet.
3599 ctx.subnet_ = current_subnet;
3600 return (true);
3601 }
3602
3603 current_subnet = current_subnet->getNextSubnet(ctx.subnet_, classes);
3604 }
3605
3606 return (false);
3607}
3608
3609} // namespace
3610
3611namespace isc {
3612namespace dhcp {
3613
3615 : early_global_reservations_lookup_(false),
3616 subnet_(), clientid_(), hwaddr_(),
3617 requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
3618 fwd_dns_update_(false), rev_dns_update_(false),
3619 hostname_(""), callout_handle_(), fake_allocation_(false), offer_lft_(0),
3620 old_lease_(), new_lease_(), hosts_(), conflicting_lease_(),
3621 query_(), host_identifiers_(), unknown_requested_addr_(false),
3622 ddns_params_() {
3623
3624}
3625
3627 const ClientIdPtr& clientid,
3628 const HWAddrPtr& hwaddr,
3629 const asiolink::IOAddress& requested_addr,
3630 const bool fwd_dns_update,
3631 const bool rev_dns_update,
3632 const std::string& hostname,
3633 const bool fake_allocation,
3634 const uint32_t offer_lft)
3635 : early_global_reservations_lookup_(false),
3636 subnet_(subnet), clientid_(clientid), hwaddr_(hwaddr),
3637 requested_address_(requested_addr),
3638 fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
3639 hostname_(hostname), callout_handle_(),
3640 fake_allocation_(fake_allocation), offer_lft_(offer_lft), old_lease_(), new_lease_(),
3641 hosts_(), host_identifiers_(), unknown_requested_addr_(false),
3642 ddns_params_(new DdnsParams()) {
3643
3644 // Initialize host identifiers.
3645 if (hwaddr) {
3646 addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
3647 }
3648}
3649
3652 if (subnet_ && subnet_->getReservationsInSubnet()) {
3653 auto host = hosts_.find(subnet_->getID());
3654 if (host != hosts_.cend()) {
3655 return (host->second);
3656 }
3657 }
3658
3659 return (globalHost());
3660}
3661
3664 if (subnet_ && subnet_->getReservationsGlobal()) {
3665 auto host = hosts_.find(SUBNET_ID_GLOBAL);
3666 if (host != hosts_.cend()) {
3667 return (host->second);
3668 }
3669 }
3670
3671 return (ConstHostPtr());
3672}
3673
3676 // We already have it return it unless the context subnet has changed.
3677 if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
3678 return (ddns_params_);
3679 }
3680
3681 // Doesn't exist yet or is stale, (re)create it.
3682 if (subnet_) {
3683 ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
3684 return (ddns_params_);
3685 }
3686
3687 // Asked for it without a subnet? This case really shouldn't occur but
3688 // for now let's return an instance with default values.
3689 return (DdnsParamsPtr(new DdnsParams()));
3690}
3691
3694 // The NULL pointer indicates that the old lease didn't exist. It may
3695 // be later set to non NULL value if existing lease is found in the
3696 // database.
3697 ctx.old_lease_.reset();
3698 ctx.new_lease_.reset();
3699
3700 // Before we start allocation process, we need to make sure that the
3701 // selected subnet is allowed for this client. If not, we'll try to
3702 // use some other subnet within the shared network. If there are no
3703 // subnets allowed for this client within the shared network, we
3704 // can't allocate a lease.
3705 ConstSubnet4Ptr subnet = ctx.subnet_;
3706 auto const& classes = ctx.query_->getClasses();
3707 if (subnet && !subnet->clientSupported(classes)) {
3708 ctx.subnet_ = subnet->getNextSubnet(subnet, classes);
3709 }
3710
3711 try {
3712 if (!ctx.subnet_) {
3713 isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
3714 }
3715
3716 if (!ctx.hwaddr_) {
3717 isc_throw(BadValue, "HWAddr must be defined");
3718 }
3719
3720 if (ctx.fake_allocation_) {
3721 ctx.new_lease_ = discoverLease4(ctx);
3722 } else {
3723 ctx.new_lease_ = requestLease4(ctx);
3724 }
3725
3726 } catch (const isc::Exception& e) {
3727 // Some other error, return an empty lease.
3729 .arg(ctx.query_->getLabel())
3730 .arg(e.what());
3731 }
3732
3733 return (ctx.new_lease_);
3734}
3735
3736void
3738 // If there is no subnet, there is nothing to do.
3739 if (!ctx.subnet_) {
3740 return;
3741 }
3742
3743 auto subnet = ctx.subnet_;
3744
3745 // If already done just return.
3747 !subnet->getReservationsInSubnet()) {
3748 return;
3749 }
3750
3751 // @todo: This code can be trivially optimized.
3753 subnet->getReservationsGlobal()) {
3755 if (ghost) {
3756 ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
3757
3758 // If we had only to fetch global reservations it is done.
3759 if (!subnet->getReservationsInSubnet()) {
3760 return;
3761 }
3762 }
3763 }
3764
3765 std::map<SubnetID, ConstHostPtr> host_map;
3766 SharedNetwork4Ptr network;
3767 subnet->getSharedNetwork(network);
3768
3769 // If the subnet belongs to a shared network it is usually going to be
3770 // more efficient to make a query for all reservations for a particular
3771 // client rather than a query for each subnet within this shared network.
3772 // The only case when it is going to be less efficient is when there are
3773 // more host identifier types in use than subnets within a shared network.
3774 // As it breaks RADIUS use of host caching this can be disabled by the
3775 // host manager.
3776 const bool use_single_query = network &&
3778 (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
3779
3780 if (use_single_query) {
3781 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3782 ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
3783 &id_pair.second[0],
3784 id_pair.second.size());
3785 // Store the hosts in the temporary map, because some hosts may
3786 // belong to subnets outside of the shared network. We'll need
3787 // to eliminate them.
3788 for (auto const& host : hosts) {
3789 if (host->getIPv4SubnetID() != SUBNET_ID_GLOBAL) {
3790 host_map[host->getIPv4SubnetID()] = host;
3791 }
3792 }
3793 }
3794 }
3795
3796 auto const& classes = ctx.query_->getClasses();
3797 // We can only search for the reservation if a subnet has been selected.
3798 while (subnet) {
3799
3800 // Only makes sense to get reservations if the client has access
3801 // to the class and host reservations are enabled for this subnet.
3802 if (subnet->clientSupported(classes) && subnet->getReservationsInSubnet()) {
3803 // Iterate over configured identifiers in the order of preference
3804 // and try to use each of them to search for the reservations.
3805 if (use_single_query) {
3806 if (host_map.count(subnet->getID()) > 0) {
3807 ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
3808 }
3809 } else {
3810 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3811 // Attempt to find a host using a specified identifier.
3812 ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
3813 id_pair.first,
3814 &id_pair.second[0],
3815 id_pair.second.size());
3816 // If we found matching host for this subnet.
3817 if (host) {
3818 ctx.hosts_[subnet->getID()] = host;
3819 break;
3820 }
3821 }
3822 }
3823 }
3824
3825 // We need to get to the next subnet if this is a shared network. If it
3826 // is not (a plain subnet), getNextSubnet will return NULL and we're
3827 // done here.
3828 subnet = subnet->getNextSubnet(ctx.subnet_, classes);
3829 }
3830
3831 // The hosts can be used by the server to return reserved options to
3832 // the DHCP client. Such options must be encapsulated (i.e., they must
3833 // include suboptions).
3834 for (auto const& host : ctx.hosts_) {
3835 host.second->encapsulateOptions();
3836 }
3837}
3838
3841 ConstHostPtr host;
3842 for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3843 // Attempt to find a host using a specified identifier.
3844 host = HostMgr::instance().get4(SUBNET_ID_GLOBAL, id_pair.first,
3845 &id_pair.second[0], id_pair.second.size());
3846
3847 // If we found matching global host we're done.
3848 if (host) {
3849 break;
3850 }
3851 }
3852
3853 return (host);
3854}
3855
3857AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
3858 // Find an existing lease for this client. This function will return null
3859 // if there is a conflict with existing lease and the allocation should
3860 // not be continued.
3861 Lease4Ptr client_lease;
3862 findClientLease(ctx, client_lease);
3863
3864 // Fetch offer_lft to see if we're allocating on DISCOVER.
3865 ctx.offer_lft_ = getOfferLft(ctx);
3866
3867 // new_lease will hold the pointer to the lease that we will offer to the
3868 // caller.
3869 Lease4Ptr new_lease;
3870
3872
3873 // Check if there is a reservation for the client. If there is, we want to
3874 // assign the reserved address, rather than any other one.
3875 if (hasAddressReservation(ctx)) {
3876
3879 .arg(ctx.query_->getLabel())
3880 .arg(ctx.currentHost()->getIPv4Reservation().toText());
3881
3882 // If the client doesn't have a lease or the leased address is different
3883 // than the reserved one then let's try to allocate the reserved address.
3884 // Otherwise the address that the client has is the one for which it
3885 // has a reservation, so just renew it.
3886 if (!client_lease || (client_lease->addr_ != ctx.currentHost()->getIPv4Reservation())) {
3887 // The call below will return a pointer to the lease for the address
3888 // reserved to this client, if the lease is available, i.e. is not
3889 // currently assigned to any other client.
3890 // Note that we don't remove the existing client's lease at this point
3891 // because this is not a real allocation, we just offer what we can
3892 // allocate in the DHCPREQUEST time.
3893 new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
3894 callout_status);
3895 if (!new_lease) {
3897 .arg(ctx.query_->getLabel())
3898 .arg(ctx.currentHost()->getIPv4Reservation().toText())
3899 .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
3900 "(no lease info)");
3902 ctx.conflicting_lease_->subnet_id_,
3903 "v4-reservation-conflicts"),
3904 static_cast<int64_t>(1));
3905 StatsMgr::instance().addValue("v4-reservation-conflicts",
3906 static_cast<int64_t>(1));
3907 }
3908
3909 } else {
3910 new_lease = renewLease4(client_lease, ctx);
3911 }
3912 }
3913
3914 // Client does not have a reservation or the allocation of the reserved
3915 // address has failed, probably because the reserved address is in use
3916 // by another client. If the client has a lease, we will check if we can
3917 // offer this lease to the client. The lease can't be offered in the
3918 // situation when it is reserved for another client or when the address
3919 // is not in the dynamic pool. The former may be the result of adding the
3920 // new reservation for the address used by this client. The latter may
3921 // be due to the client using the reserved out-of-the pool address, for
3922 // which the reservation has just been removed.
3923 if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
3924 !addressReserved(client_lease->addr_, ctx)) {
3925
3928 .arg(ctx.query_->getLabel());
3929
3930 // If offer-lifetime is shorter than the existing expiration, reset
3931 // offer-lifetime to zero. This allows us to simply return the
3932 // existing lease without updating it in the lease store.
3933 if ((ctx.offer_lft_) &&
3934 (time(NULL) + ctx.offer_lft_ < client_lease->getExpirationTime())) {
3935 ctx.offer_lft_ = 0;
3936 }
3937
3938 new_lease = renewLease4(client_lease, ctx);
3939 }
3940
3941 // The client doesn't have any lease or the lease can't be offered
3942 // because it is either reserved for some other client or the
3943 // address is not in the dynamic pool.
3944 // Let's use the client's hint (requested IP address), if the client
3945 // has provided it, and try to offer it. This address must not be
3946 // reserved for another client, and must be in the range of the
3947 // dynamic pool.
3948 if (!new_lease && !ctx.requested_address_.isV4Zero() &&
3949 inAllowedPool(ctx, ctx.requested_address_) &&
3950 !addressReserved(ctx.requested_address_, ctx)) {
3951
3954 .arg(ctx.requested_address_.toText())
3955 .arg(ctx.query_->getLabel());
3956
3957 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3958 callout_status);
3959 }
3960
3961 // The allocation engine failed to allocate all of the candidate
3962 // addresses. We will now use the allocator to pick the address
3963 // from the dynamic pool.
3964 if (!new_lease) {
3965
3968 .arg(ctx.query_->getLabel());
3969
3970 new_lease = allocateUnreservedLease4(ctx);
3971 }
3972
3973 // Some of the methods like reuseExpiredLease4 may set the old lease to point
3974 // to the lease which they remove/override. If it is not set, but we have
3975 // found that the client has the lease the client's lease is the one
3976 // to return as an old lease.
3977 if (!ctx.old_lease_ && client_lease) {
3978 ctx.old_lease_ = client_lease;
3979 }
3980
3981 return (new_lease);
3982}
3983
3985AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
3986 // Find an existing lease for this client. This function will return null
3987 // if there is a conflict with existing lease and the allocation should
3988 // not be continued.
3989 Lease4Ptr client_lease;
3990 findClientLease(ctx, client_lease);
3991
3992 // When the client sends the DHCPREQUEST, it should always specify the
3993 // address which it is requesting or renewing. That is, the client should
3994 // either use the requested IP address option or set the ciaddr. However,
3995 // we try to be liberal and allow the clients to not specify an address
3996 // in which case the allocation engine will pick a suitable address
3997 // for the client.
3998 if (!ctx.requested_address_.isV4Zero()) {
3999 // If the client has specified an address, make sure this address
4000 // is not reserved for another client. If it is, stop here because
4001 // we can't allocate this address.
4002 if (addressReserved(ctx.requested_address_, ctx)) {
4003
4006 .arg(ctx.query_->getLabel())
4007 .arg(ctx.requested_address_.toText());
4008
4009 return (Lease4Ptr());
4010 }
4011
4012 } else if (hasAddressReservation(ctx)) {
4013 // The client hasn't specified an address to allocate, so the
4014 // allocation engine needs to find an appropriate address.
4015 // If there is a reservation for the client, let's try to
4016 // allocate the reserved address.
4017 ctx.requested_address_ = ctx.currentHost()->getIPv4Reservation();
4018
4021 .arg(ctx.query_->getLabel())
4022 .arg(ctx.requested_address_.toText());
4023 }
4024
4025 if (!ctx.requested_address_.isV4Zero()) {
4026 // There is a specific address to be allocated. Let's find out if
4027 // the address is in use.
4029 // If the address is in use (allocated and not expired), we check
4030 // if the address is in use by our client or another client.
4031 // If it is in use by another client, the address can't be
4032 // allocated.
4033 if (existing && !existing->expired() &&
4034 !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
4035 ctx.clientid_ : ClientIdPtr())) {
4036
4039 .arg(ctx.query_->getLabel())
4040 .arg(ctx.requested_address_.toText());
4041
4042 return (Lease4Ptr());
4043 }
4044
4045 // If the client has a reservation but it is requesting a different
4046 // address it is possible that the client was offered this different
4047 // address because the reserved address is in use. We will have to
4048 // check if the address is in use.
4049 if (hasAddressReservation(ctx) &&
4050 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) {
4051 existing =
4052 LeaseMgrFactory::instance().getLease4(ctx.currentHost()->getIPv4Reservation());
4053 // If the reserved address is not in use, i.e. the lease doesn't
4054 // exist or is expired, and the client is requesting a different
4055 // address, return NULL. The client should go back to the
4056 // DHCPDISCOVER and the reserved address will be offered.
4057 if (!existing || existing->expired()) {
4058
4061 .arg(ctx.query_->getLabel())
4062 .arg(ctx.currentHost()->getIPv4Reservation().toText())
4063 .arg(ctx.requested_address_.toText());
4064
4065 return (Lease4Ptr());
4066 }
4067 }
4068
4069 // The use of the out-of-pool addresses is only allowed when the requested
4070 // address is reserved for the client. If the address is not reserved one
4071 // and it doesn't belong to the dynamic pool, do not allocate it.
4072 if ((!hasAddressReservation(ctx) ||
4073 (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
4074 !inAllowedPool(ctx, ctx.requested_address_)) {
4075
4078 .arg(ctx.query_->getLabel())
4079 .arg(ctx.requested_address_);
4080
4081 ctx.unknown_requested_addr_ = true;
4082 return (Lease4Ptr());
4083 }
4084 }
4085
4086 // We have gone through all the checks, so we can now allocate the address
4087 // for the client.
4088
4089 // If the client is requesting an address which is assigned to the client
4090 // let's just renew this address. Also, renew this address if the client
4091 // doesn't request any specific address.
4092 // Added extra checks: the address is reserved for this client or belongs
4093 // to the dynamic pool for the case the pool class has changed before the
4094 // request.
4095 if (client_lease) {
4096 if (((client_lease->addr_ == ctx.requested_address_) ||
4098 ((hasAddressReservation(ctx) &&
4099 (ctx.currentHost()->getIPv4Reservation() == ctx.requested_address_)) ||
4100 inAllowedPool(ctx, client_lease->addr_))) {
4101
4104 .arg(ctx.query_->getLabel())
4105 .arg(ctx.requested_address_);
4106
4107 return (renewLease4(client_lease, ctx));
4108 }
4109 }
4110
4111 // new_lease will hold the pointer to the allocated lease if we allocate
4112 // successfully.
4113 Lease4Ptr new_lease;
4114
4115 // The client doesn't have the lease or it is requesting an address
4116 // which it doesn't have. Let's try to allocate the requested address.
4117 if (!ctx.requested_address_.isV4Zero()) {
4118
4121 .arg(ctx.query_->getLabel())
4122 .arg(ctx.requested_address_.toText());
4123
4124 // The call below will return a pointer to the lease allocated
4125 // for the client if there is no lease for the requested address,
4126 // or the existing lease has expired. If the allocation fails,
4127 // e.g. because the lease is in use, we will return NULL to
4128 // indicate that we were unable to allocate the lease.
4130 new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
4131 callout_status);
4132
4133 } else {
4134
4137 .arg(ctx.query_->getLabel());
4138
4139 // We will only get here if the client didn't specify which
4140 // address it wanted to be allocated. The allocation engine will
4141 // to pick the address from the dynamic pool.
4142 new_lease = allocateUnreservedLease4(ctx);
4143 }
4144
4145 // If we allocated the lease for the client, but the client already had a
4146 // lease, we will need to return the pointer to the previous lease and
4147 // the previous lease needs to be removed from the lease database.
4148 if (new_lease && client_lease) {
4149 ctx.old_lease_ = Lease4Ptr(new Lease4(*client_lease));
4150
4153 .arg(ctx.query_->getLabel())
4154 .arg(client_lease->addr_.toText());
4155
4156 if (LeaseMgrFactory::instance().deleteLease(client_lease) && (client_lease->state_ != Lease4::STATE_RELEASED)) {
4157 // Need to decrease statistic for assigned addresses.
4159 StatsMgr::generateName("subnet", client_lease->subnet_id_,
4160 "assigned-addresses"),
4161 static_cast<int64_t>(-1));
4162
4163 auto const& subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getBySubnetId(client_lease->subnet_id_);
4164 if (subnet) {
4165 auto const& pool = subnet->getPool(Lease::TYPE_V4, client_lease->addr_, false);
4166 if (pool) {
4168 StatsMgr::generateName("subnet", subnet->getID(),
4169 StatsMgr::generateName("pool", pool->getID(),
4170 "assigned-addresses")),
4171 static_cast<int64_t>(-1));
4172 }
4173 }
4174 }
4175 }
4176
4177 // Return the allocated lease or NULL pointer if allocation was
4178 // unsuccessful.
4179 return (new_lease);
4180}
4181
4182uint32_t
4184 // Not a DISCOVER or it's BOOTP, punt.
4185 if ((!ctx.fake_allocation_) || (ctx.query_->inClass("BOOTP"))) {
4186 return (0);
4187 }
4188
4189 util::Optional<uint32_t> offer_lft;
4190
4191 // If specified in one of our classes use it.
4192 // We use the first one we find.
4193 const ClientClasses classes = ctx.query_->getClasses();
4194 if (!classes.empty()) {
4195 // Let's get class definitions
4196 const ClientClassDictionaryPtr& dict =
4197 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4198
4199 // Iterate over the assigned class definitions.
4200 for (auto const& name : classes) {
4201 ClientClassDefPtr cl = dict->findClass(name);
4202 if (cl && (!cl->getOfferLft().unspecified())) {
4203 offer_lft = cl->getOfferLft();
4204 break;
4205 }
4206 }
4207 }
4208
4209 // If no classes specified it, get it from the subnet.
4210 if (offer_lft.unspecified()) {
4211 offer_lft = ctx.subnet_->getOfferLft();
4212 }
4213
4214 return (offer_lft.unspecified() ? 0 : offer_lft.get());
4215}
4216
4217uint32_t
4219 // If it's BOOTP, use infinite valid lifetime.
4220 if (ctx.query_->inClass("BOOTP")) {
4221 return (Lease::INFINITY_LFT);
4222 }
4223
4224 // Use the dhcp-lease-time content from the client if it's there.
4225 uint32_t requested_lft = 0;
4226 OptionPtr opt = ctx.query_->getOption(DHO_DHCP_LEASE_TIME);
4227 if (opt) {
4228 OptionUint32Ptr opt_lft = boost::dynamic_pointer_cast<OptionInt<uint32_t> >(opt);
4229 if (opt_lft) {
4230 requested_lft = opt_lft->getValue();
4231 }
4232 }
4233
4234 // If the triplet is specified in one of our classes use it.
4235 // We use the first one we find.
4236 Triplet<uint32_t> candidate_lft;
4237 const ClientClasses classes = ctx.query_->getClasses();
4238 if (!classes.empty()) {
4239 // Let's get class definitions
4240 const ClientClassDictionaryPtr& dict =
4241 CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
4242
4243 // Iterate over the assigned class definitions.
4244 for (auto const& name : classes) {
4245 ClientClassDefPtr cl = dict->findClass(name);
4246 if (cl && (!cl->getValid().unspecified())) {
4247 candidate_lft = cl->getValid();
4248 break;
4249 }
4250 }
4251 }
4252
4253 // If no classes specified it, get it from the subnet.
4254 if (!candidate_lft) {
4255 candidate_lft = ctx.subnet_->getValid();
4256 }
4257
4258 // If client requested a value, use the value bounded by
4259 // the candidate triplet.
4260 if (requested_lft > 0) {
4261 return (candidate_lft.get(requested_lft));
4262 }
4263
4264 // Use the candidate's default value.
4265 return (candidate_lft.get());
4266}
4267
4269AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
4270 CalloutHandle::CalloutNextStep& callout_status) {
4271 if (!ctx.hwaddr_) {
4272 isc_throw(BadValue, "Can't create a lease with NULL HW address");
4273 }
4274 if (!ctx.subnet_) {
4275 isc_throw(BadValue, "Can't create a lease without a subnet");
4276 }
4277
4278 // Get the context appropriate lifetime.
4279 uint32_t valid_lft = (ctx.offer_lft_ ? ctx.offer_lft_ : getValidLft(ctx));
4280
4281 time_t now = time(NULL);
4282
4283 ClientIdPtr client_id;
4284 if (ctx.subnet_->getMatchClientId()) {
4285 client_id = ctx.clientid_;
4286 }
4287
4288 Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
4289 valid_lft, now, ctx.subnet_->getID()));
4290
4291 // Set FQDN specific lease parameters.
4292 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4293 lease->fqdn_rev_ = ctx.rev_dns_update_;
4294 lease->hostname_ = ctx.hostname_;
4295
4296 // Add (update) the extended information on the lease.
4297 static_cast<void>(updateLease4ExtendedInfo(lease, ctx));
4298
4299 // Let's execute all callouts registered for lease4_select
4300 if (ctx.callout_handle_ &&
4301 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4302
4303 // Use the RAII wrapper to make sure that the callout handle state is
4304 // reset when this object goes out of scope. All hook points must do
4305 // it to prevent possible circular dependency between the callout
4306 // handle and its arguments.
4307 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4308
4309 // Enable copying options from the packet within hook library.
4310 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4311
4312 // Pass necessary arguments
4313 // Pass the original client query
4314 ctx.callout_handle_->setArgument("query4", ctx.query_);
4315
4316 // Subnet from which we do the allocation (That's as far as we can go
4317 // with using SubnetPtr to point to Subnet4 object. Users should not
4318 // be confused with dynamic_pointer_casts. They should get a concrete
4319 // pointer (ConstSubnet4Ptr) pointing to a Subnet4 object.
4320 ConstSubnet4Ptr subnet4 =
4321 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4322 ctx.callout_handle_->setArgument("subnet4", subnet4);
4323
4324 // Is this solicit (fake = true) or request (fake = false)
4325 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4326
4327 // Are we allocating on DISCOVER? (i.e. offer_lft > 0).
4328 ctx.callout_handle_->setArgument("offer_lft", ctx.offer_lft_);
4329
4330 // Pass the intended lease as well
4331 ctx.callout_handle_->setArgument("lease4", lease);
4332
4333 // This is the first callout, so no need to clear any arguments
4334 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4335
4336 callout_status = ctx.callout_handle_->getStatus();
4337
4338 // Callouts decided to skip the action. This means that the lease is not
4339 // assigned, so the client will get NoAddrAvail as a result. The lease
4340 // won't be inserted into the database.
4341 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4343 return (Lease4Ptr());
4344 }
4345
4346 // Let's use whatever callout returned. Hopefully it is the same lease
4347 // we handled to it.
4348 ctx.callout_handle_->getArgument("lease4", lease);
4349 }
4350
4351 if (ctx.fake_allocation_ && ctx.offer_lft_) {
4352 // Turn them off before we persist, so we'll see it as different when
4353 // we extend it in the REQUEST. This should cause us to do DDNS (if
4354 // it's enabled).
4355 lease->fqdn_fwd_ = false;
4356 lease->fqdn_rev_ = false;
4357 }
4358
4359 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4360 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, lease->addr_, false);
4361 if (pool) {
4362 lease->pool_id_ = pool->getID();
4363 }
4364 // That is a real (REQUEST) allocation
4365 bool status = LeaseMgrFactory::instance().addLease(lease);
4366 if (status) {
4367
4368 // The lease insertion succeeded, let's bump up the statistic.
4370 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4371 "assigned-addresses"),
4372 static_cast<int64_t>(1));
4373
4375 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4376 "cumulative-assigned-addresses"),
4377 static_cast<int64_t>(1));
4378
4379 if (pool) {
4381 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4382 StatsMgr::generateName("pool", pool->getID(),
4383 "assigned-addresses")),
4384 static_cast<int64_t>(1));
4385
4387 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4388 StatsMgr::generateName("pool", pool->getID(),
4389 "cumulative-assigned-addresses")),
4390 static_cast<int64_t>(1));
4391 }
4392
4393 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4394 static_cast<int64_t>(1));
4395
4396 return (lease);
4397 } else {
4398 // One of many failures with LeaseMgr (e.g. lost connection to the
4399 // database, database failed etc.). One notable case for that
4400 // is that we are working in multi-process mode and we lost a race
4401 // (some other process got that address first)
4402 return (Lease4Ptr());
4403 }
4404 } else {
4405 // That is only fake (DISCOVER) allocation
4406 // It is for OFFER only. We should not insert the lease and callers
4407 // have already verified the lease does not exist in the database.
4408 return (lease);
4409 }
4410}
4411
4413AllocEngine::renewLease4(const Lease4Ptr& lease,
4415 if (!lease) {
4416 isc_throw(BadValue, "null lease specified for renewLease4");
4417 }
4418
4419 // Let's keep the old data. This is essential if we are using memfile
4420 // (the lease returned points directly to the lease4 object in the database)
4421 // We'll need it if we want to skip update (i.e. roll back renewal)
4423 Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
4424 ctx.old_lease_.reset(new Lease4(*old_values));
4425
4426 // Update the lease with the information from the context.
4427 // If there was no significant changes, try reuse.
4428 lease->reuseable_valid_lft_ = 0;
4429 if (!updateLease4Information(lease, ctx)) {
4430 setLeaseReusable(lease, ctx);
4431 }
4432
4433 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4434 // If the lease is expired we have to reclaim it before
4435 // re-assigning it to the client. The lease reclamation
4436 // involves execution of hooks and DNS update.
4437 if (ctx.old_lease_->expired()) {
4438 reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
4439 }
4440
4441 lease->state_ = Lease::STATE_DEFAULT;
4442 }
4443
4444 bool skip = false;
4445 // Execute all callouts registered for lease4_renew.
4446 if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_renew_)) {
4447
4448 // Use the RAII wrapper to make sure that the callout handle state is
4449 // reset when this object goes out of scope. All hook points must do
4450 // it to prevent possible circular dependency between the callout
4451 // handle and its arguments.
4452 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4453
4454 // Enable copying options from the packet within hook library.
4455 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4456
4457 // Subnet from which we do the allocation. Convert the general subnet
4458 // pointer to a pointer to a Subnet4. Note that because we are using
4459 // boost smart pointers here, we need to do the cast using the boost
4460 // version of dynamic_pointer_cast.
4461 ConstSubnet4Ptr subnet4 =
4462 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4463
4464 // Pass the parameters. Note the clientid is passed only if match-client-id
4465 // is set. This is done that way, because the lease4-renew hook point is
4466 // about renewing a lease and the configuration parameter says the
4467 // client-id should be ignored. Hence no clientid value if match-client-id
4468 // is false.
4469 ctx.callout_handle_->setArgument("query4", ctx.query_);
4470 ctx.callout_handle_->setArgument("subnet4", subnet4);
4471 ctx.callout_handle_->setArgument("clientid", subnet4->getMatchClientId() ?
4472 ctx.clientid_ : ClientIdPtr());
4473 ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
4474
4475 // Pass the lease to be updated
4476 ctx.callout_handle_->setArgument("lease4", lease);
4477
4478 // Call all installed callouts
4479 HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
4480 *ctx.callout_handle_);
4481
4482 // Callouts decided to skip the next processing step. The next
4483 // processing step would actually renew the lease, so skip at this
4484 // stage means "keep the old lease as it is".
4485 if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4486 skip = true;
4489 }
4490
4492 }
4493
4494 if ((!ctx.fake_allocation_ || ctx.offer_lft_) && !skip && (lease->reuseable_valid_lft_ == 0)) {
4495 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, lease->addr_, false);
4496 if (pool) {
4497 lease->pool_id_ = pool->getID();
4498 }
4499
4500 // for REQUEST we do update the lease
4502
4503 // We need to account for the re-assignment of the lease.
4504 if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
4506 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4507 "assigned-addresses"),
4508 static_cast<int64_t>(1));
4509
4511 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4512 "cumulative-assigned-addresses"),
4513 static_cast<int64_t>(1));
4514
4515 if (pool) {
4517 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4518 StatsMgr::generateName("pool", pool->getID(),
4519 "assigned-addresses")),
4520 static_cast<int64_t>(1));
4521
4523 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4524 StatsMgr::generateName("pool", pool->getID(),
4525 "cumulative-assigned-addresses")),
4526 static_cast<int64_t>(1));
4527 }
4528
4529 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4530 static_cast<int64_t>(1));
4531 }
4532 }
4533 if (skip) {
4534 // Rollback changes (really useful only for memfile)
4536 *lease = *old_values;
4537 }
4538
4539 return (lease);
4540}
4541
4543AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
4545 CalloutHandle::CalloutNextStep& callout_status) {
4546 if (!expired) {
4547 isc_throw(BadValue, "null lease specified for reuseExpiredLease");
4548 }
4549
4550 if (!ctx.subnet_) {
4551 isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
4552 }
4553
4554 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4555 // The expired lease needs to be reclaimed before it can be reused.
4556 // This includes declined leases for which probation period has
4557 // elapsed.
4558 reclaimExpiredLease(expired, ctx.callout_handle_);
4559 expired->state_ = Lease::STATE_DEFAULT;
4560 }
4561
4562 expired->reuseable_valid_lft_ = 0;
4563 static_cast<void>(updateLease4Information(expired, ctx));
4564
4567 .arg(ctx.query_->getLabel())
4568 .arg(expired->toText());
4569
4570 // Let's execute all callouts registered for lease4_select
4571 if (ctx.callout_handle_ &&
4572 HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4573
4574 // Enable copying options from the packet within hook library.
4575 ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4576
4577 // Use the RAII wrapper to make sure that the callout handle state is
4578 // reset when this object goes out of scope. All hook points must do
4579 // it to prevent possible circular dependency between the callout
4580 // handle and its arguments.
4581 ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4582
4583 // Pass necessary arguments
4584 // Pass the original client query
4585 ctx.callout_handle_->setArgument("query4", ctx.query_);
4586
4587 // Subnet from which we do the allocation. Convert the general subnet
4588 // pointer to a pointer to a Subnet4. Note that because we are using
4589 // boost smart pointers here, we need to do the cast using the boost
4590 // version of dynamic_pointer_cast.
4591 ConstSubnet4Ptr subnet4 =
4592 boost::dynamic_pointer_cast<const Subnet4>(ctx.subnet_);
4593 ctx.callout_handle_->setArgument("subnet4", subnet4);
4594
4595 // Is this solicit (fake = true) or request (fake = false)
4596 ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4597 ctx.callout_handle_->setArgument("offer_lft", ctx.offer_lft_);
4598
4599 // The lease that will be assigned to a client
4600 ctx.callout_handle_->setArgument("lease4", expired);
4601
4602 // Call the callouts
4603 HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4604
4605 callout_status = ctx.callout_handle_->getStatus();
4606
4607 // Callouts decided to skip the action. This means that the lease is not
4608 // assigned, so the client will get NoAddrAvail as a result. The lease
4609 // won't be inserted into the database.
4610 if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4613 return (Lease4Ptr());
4614 }
4615
4617
4618 // Let's use whatever callout returned. Hopefully it is the same lease
4619 // we handed to it.
4620 ctx.callout_handle_->getArgument("lease4", expired);
4621 }
4622
4623 if (!ctx.fake_allocation_ || ctx.offer_lft_) {
4624 auto const& pool = ctx.subnet_->getPool(Lease::TYPE_V4, expired->addr_, false);
4625 if (pool) {
4626 expired->pool_id_ = pool->getID();
4627 }
4628 // for REQUEST we do update the lease
4630
4631 // We need to account for the re-assignment of the lease.
4633 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4634 "assigned-addresses"),
4635 static_cast<int64_t>(1));
4636
4638 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4639 "cumulative-assigned-addresses"),
4640 static_cast<int64_t>(1));
4641
4642 if (pool) {
4644 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4645 StatsMgr::generateName("pool", pool->getID(),
4646 "assigned-addresses")),
4647 static_cast<int64_t>(1));
4648
4650 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4651 StatsMgr::generateName("pool", pool->getID(),
4652 "cumulative-assigned-addresses")),
4653 static_cast<int64_t>(1));
4654 }
4655
4656 StatsMgr::instance().addValue("cumulative-assigned-addresses",
4657 static_cast<int64_t>(1));
4658 }
4659
4660 // We do nothing for SOLICIT. We'll just update database when
4661 // the client gets back to us with REQUEST message.
4662
4663 // it's not really expired at this stage anymore - let's return it as
4664 // an updated lease
4665 return (expired);
4666}
4667
4669AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
4670 CalloutHandle::CalloutNextStep& callout_status) {
4671 ctx.conflicting_lease_.reset();
4672
4673 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4674 if (exist_lease) {
4675 if (exist_lease->expired()) {
4676 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4677 // reuseExpiredLease4() will reclaim the use which will
4678 // queue an NCR remove it needed. Clear the DNS fields in
4679 // the old lease to avoid a redundant remove in server logic.
4680 ctx.old_lease_->hostname_.clear();
4681 ctx.old_lease_->fqdn_fwd_ = false;
4682 ctx.old_lease_->fqdn_rev_ = false;
4683 return (reuseExpiredLease4(exist_lease, ctx, callout_status));
4684
4685 } else {
4686 // If there is a lease and it is not expired, pass this lease back
4687 // to the caller in the context. The caller may need to know
4688 // which lease we're conflicting with.
4689 ctx.conflicting_lease_ = exist_lease;
4690 }
4691
4692 } else {
4693 return (createLease4(ctx, candidate, callout_status));
4694 }
4695 return (Lease4Ptr());
4696}
4697
4699AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
4700 Lease4Ptr new_lease;
4701 ConstSubnet4Ptr subnet = ctx.subnet_;
4702
4703 // Need to check if the subnet belongs to a shared network. If so,
4704 // we might be able to find a better subnet for lease allocation,
4705 // for which it is more likely that there are some leases available.
4706 // If we stick to the selected subnet, we may end up walking over
4707 // the entire subnet (or more subnets) to discover that the address
4708 // pools have been exhausted. Using a subnet from which an address
4709 // was assigned most recently is an optimization which increases
4710 // the likelihood of starting from the subnet which address pools
4711 // are not exhausted.
4712 SharedNetwork4Ptr network;
4713 ctx.subnet_->getSharedNetwork(network);
4714 if (network) {
4715 // This would try to find a subnet with the same set of classes
4716 // as the current subnet, but with the more recent "usage timestamp".
4717 // This timestamp is only updated for the allocations made with an
4718 // allocator (unreserved lease allocations), not the static
4719 // allocations or requested addresses.
4720 ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
4721 }
4722
4723 // We have the choice in the order checking the lease and
4724 // the reservation. The default is to begin by the lease
4725 // if the multi-threading is disabled.
4726 bool check_reservation_first = MultiThreadingMgr::instance().getMode();
4727
4728 ConstSubnet4Ptr original_subnet = subnet;
4729
4730 uint128_t total_attempts = 0;
4731
4732 // The following counter tracks the number of subnets with matching client
4733 // classes from which the allocation engine attempted to assign leases.
4734 uint64_t subnets_with_unavail_leases = 0;
4735 // The following counter tracks the number of subnets in which there were
4736 // no matching pools for the client.
4737 uint64_t subnets_with_unavail_pools = 0;
4738
4739 auto const& classes = ctx.query_->getClasses();
4740
4741 while (subnet) {
4742 ClientIdPtr client_id;
4743 if (subnet->getMatchClientId()) {
4744 client_id = ctx.clientid_;
4745 }
4746
4747 uint128_t const possible_attempts =
4748 subnet->getPoolCapacity(Lease::TYPE_V4, classes);
4749
4750 // If the number of tries specified in the allocation engine constructor
4751 // is set to 0 (unlimited) or the pools capacity is lower than that number,
4752 // let's use the pools capacity as the maximum number of tries. Trying
4753 // more than the actual pools capacity is a waste of time. If the specified
4754 // number of tries is lower than the pools capacity, use that number.
4755 uint128_t const max_attempts =
4756 (attempts_ == 0 || possible_attempts < attempts_) ?
4757 possible_attempts :
4758 attempts_;
4759
4760 if (max_attempts > 0) {
4761 // If max_attempts is greater than 0, there are some pools in this subnet
4762 // from which we can potentially get a lease.
4763 ++subnets_with_unavail_leases;
4764 } else {
4765 // If max_attempts is 0, it is an indication that there are no pools
4766 // in the subnet from which we can get a lease.
4767 ++subnets_with_unavail_pools;
4768 }
4769
4770 bool exclude_first_last_24 = ((subnet->get().second <= 24) &&
4771 CfgMgr::instance().getCurrentCfg()->getExcludeFirstLast24());
4772
4774
4775 for (uint128_t i = 0; i < max_attempts; ++i) {
4776
4777 ++total_attempts;
4778
4779 auto allocator = subnet->getAllocator(Lease::TYPE_V4);
4780 IOAddress candidate = allocator->pickAddress(classes,
4781 client_id,
4782 ctx.requested_address_);
4783
4784 // An allocator may return zero address when it has pools exhausted.
4785 if (candidate.isV4Zero()) {
4786 break;
4787 }
4788
4789 if (exclude_first_last_24) {
4790 // Exclude .0 and .255 addresses.
4791 auto const& bytes = candidate.toBytes();
4792 if ((bytes.size() != 4) ||
4793 (bytes[3] == 0) || (bytes[3] == 255U)) {
4794 // Don't allocate.
4795 continue;
4796 }
4797 }
4798
4799 // First check for reservation when it is the choice.
4800 if (check_reservation_first && addressReserved(candidate, ctx)) {
4801 // Don't allocate.
4802 continue;
4803 }
4804
4805 // Check if the resource is busy i.e. can be being allocated
4806 // by another thread to another client.
4807 ResourceHandler4 resource_handler;
4808 if (MultiThreadingMgr::instance().getMode() &&
4809 !resource_handler.tryLock4(candidate)) {
4810 // Don't allocate.
4811 continue;
4812 }
4813
4814 // Check for an existing lease for the candidate address.
4815 Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4816 if (!exist_lease) {
4817 // No existing lease, is it reserved?
4818 if (check_reservation_first || !addressReserved(candidate, ctx)) {
4819 // Not reserved use it.
4820 new_lease = createLease4(ctx, candidate, callout_status);
4821 }
4822 } else {
4823 // An lease exists, is expired, and not reserved use it.
4824 if (exist_lease->expired() &&
4825 (check_reservation_first || !addressReserved(candidate, ctx))) {
4826 ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4827 new_lease = reuseExpiredLease4(exist_lease, ctx, callout_status);
4828 }
4829 }
4830
4831 // We found a lease we can use, return it.
4832 if (new_lease) {
4833 return (new_lease);
4834 }
4835
4836 if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
4837 // Don't retry when the callout status is not continue.
4838 subnet.reset();
4839 break;
4840 }
4841 }
4842
4843 // This pointer may be set to NULL if hooks set SKIP status.
4844 if (subnet) {
4845 subnet = subnet->getNextSubnet(original_subnet, classes);
4846
4847 if (subnet) {
4848 ctx.subnet_ = subnet;
4849 }
4850 }
4851 }
4852
4853 if (network) {
4854 // The client is in the shared network. Let's log the high level message
4855 // indicating which shared network the client belongs to.
4857 .arg(ctx.query_->getLabel())
4858 .arg(network->getName())
4859 .arg(subnets_with_unavail_leases)
4860 .arg(subnets_with_unavail_pools);
4861 StatsMgr::instance().addValue("v4-allocation-fail-shared-network",
4862 static_cast<int64_t>(1));
4864 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4865 "v4-allocation-fail-shared-network"),
4866 static_cast<int64_t>(1));
4867 } else {
4868 // The client is not connected to a shared network. It is connected
4869 // to a subnet. Let's log some details about the subnet.
4870 std::string shared_network = ctx.subnet_->getSharedNetworkName();
4871 if (shared_network.empty()) {
4872 shared_network = "(none)";
4873 }
4875 .arg(ctx.query_->getLabel())
4876 .arg(ctx.subnet_->toText())
4877 .arg(ctx.subnet_->getID())
4878 .arg(shared_network);
4879 StatsMgr::instance().addValue("v4-allocation-fail-subnet",
4880 static_cast<int64_t>(1));
4882 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4883 "v4-allocation-fail-subnet"),
4884 static_cast<int64_t>(1));
4885 }
4886 if (total_attempts == 0) {
4887 // In this case, it seems that none of the pools in the subnets could
4888 // be used for that client, both in case the client is connected to
4889 // a shared network or to a single subnet. Apparently, the client was
4890 // rejected to use the pools because of the client classes' mismatch.
4892 .arg(ctx.query_->getLabel());
4893 StatsMgr::instance().addValue("v4-allocation-fail-no-pools",
4894 static_cast<int64_t>(1));
4896 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4897 "v4-allocation-fail-no-pools"),
4898 static_cast<int64_t>(1));
4899 } else {
4900 // This is an old log message which provides a number of attempts
4901 // made by the allocation engine to allocate a lease. The only case
4902 // when we don't want to log this message is when the number of
4903 // attempts is zero (condition above), because it would look silly.
4905 .arg(ctx.query_->getLabel())
4906 .arg(total_attempts);
4907 StatsMgr::instance().addValue("v4-allocation-fail",
4908 static_cast<int64_t>(1));
4910 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4911 "v4-allocation-fail"),
4912 static_cast<int64_t>(1));
4913 }
4914
4915 if (!classes.empty()) {
4917 .arg(ctx.query_->getLabel())
4918 .arg(classes.toText());
4919 StatsMgr::instance().addValue("v4-allocation-fail-classes",
4920 static_cast<int64_t>(1));
4922 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4923 "v4-allocation-fail-classes"),
4924 static_cast<int64_t>(1));
4925 }
4926
4927 return (new_lease);
4928}
4929
4930bool
4931AllocEngine::updateLease4Information(const Lease4Ptr& lease,
4932 AllocEngine::ClientContext4& ctx) const {
4933 bool changed = false;
4934 if (lease->subnet_id_ != ctx.subnet_->getID()) {
4935 changed = true;
4936 lease->subnet_id_ = ctx.subnet_->getID();
4937 }
4938 if ((!ctx.hwaddr_ && lease->hwaddr_) ||
4939 (ctx.hwaddr_ &&
4940 (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
4941 changed = true;
4942 lease->hwaddr_ = ctx.hwaddr_;
4943 }
4944 if (ctx.subnet_->getMatchClientId() && ctx.clientid_) {
4945 if (!lease->client_id_ || (*ctx.clientid_ != *lease->client_id_)) {
4946 changed = true;
4947 lease->client_id_ = ctx.clientid_;
4948 }
4949 } else if (lease->client_id_) {
4950 changed = true;
4951 lease->client_id_ = ClientIdPtr();
4952 }
4953 lease->cltt_ = time(NULL);
4954
4955 // Get the context appropriate valid lifetime.
4956 lease->valid_lft_ = (ctx.offer_lft_ ? ctx.offer_lft_ : getValidLft(ctx));
4957
4958 // Valid lifetime has changed.
4959 if (lease->valid_lft_ != lease->current_valid_lft_) {
4960 changed = true;
4961 }
4962
4963 if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
4964 (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
4965 (lease->hostname_ != ctx.hostname_)) {
4966 changed = true;
4967 lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4968 lease->fqdn_rev_ = ctx.rev_dns_update_;
4969 lease->hostname_ = ctx.hostname_;
4970 }
4971
4972 // Add (update) the extended information on the lease.
4973 if (updateLease4ExtendedInfo(lease, ctx)) {
4974 changed = true;
4975 }
4976
4977 return (changed);
4978}
4979
4980bool
4982 const AllocEngine::ClientContext4& ctx) const {
4983 bool changed = false;
4984
4985 // If storage is not enabled then punt.
4986 if (!ctx.subnet_->getStoreExtendedInfo()) {
4987 return (changed);
4988 }
4989
4990 // Look for relay agent information option (option 82)
4991 OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS);
4992 if (!rai) {
4993 // Pkt4 doesn't have it, so nothing to store (or update).
4994 return (changed);
4995 }
4996
4997 // Check if the RAI was recovered from stashed agent options.
4999 getConfiguredGlobal(CfgGlobals::STASH_AGENT_OPTIONS);
5000 if (sao && (sao->getType() == data::Element::boolean) &&
5001 sao->boolValue() && ctx.query_->inClass("STASH_AGENT_OPTIONS")) {
5002 return (changed);
5003 }
5004
5005 // Create a StringElement with the hex string for relay-agent-info.
5006 ElementPtr relay_agent(new StringElement(rai->toHexString()));
5007
5008 // Now we wrap the agent info in a map. This allows for future expansion.
5009 ElementPtr extended_info = Element::createMap();
5010 extended_info->set("sub-options", relay_agent);
5011
5012 OptionPtr remote_id = rai->getOption(RAI_OPTION_REMOTE_ID);
5013 if (remote_id) {
5014 std::vector<uint8_t> bytes = remote_id->toBinary();
5015 lease->remote_id_ = bytes;
5016 if (bytes.size() > 0) {
5017 extended_info->set("remote-id",
5019 }
5020 }
5021
5022 OptionPtr relay_id = rai->getOption(RAI_OPTION_RELAY_ID);
5023 if (relay_id) {
5024 std::vector<uint8_t> bytes = relay_id->toBinary(false);
5025 lease->relay_id_ = bytes;
5026 if (bytes.size() > 0) {
5027 extended_info->set("relay-id",
5029 }
5030 }
5031
5032 // Get a mutable copy of the lease's current user context.
5033 ConstElementPtr user_context = lease->getContext();
5034 ElementPtr mutable_user_context;
5035 if (user_context && (user_context->getType() == Element::map)) {
5036 mutable_user_context = copy(user_context, 0);
5037 } else {
5038 mutable_user_context = Element::createMap();
5039 }
5040
5041 // Get a mutable copy of the ISC entry.
5042 ConstElementPtr isc = mutable_user_context->get("ISC");
5043 ElementPtr mutable_isc;
5044 if (isc && (isc->getType() == Element::map)) {
5045 mutable_isc = copy(isc, 0);
5046 } else {
5047 mutable_isc = Element::createMap();
5048 }
5049
5050 // Add/replace the extended info entry.
5051 ConstElementPtr old_extended_info = mutable_isc->get("relay-agent-info");
5052 if (!old_extended_info || (*old_extended_info != *extended_info)) {
5053 changed = true;
5054 mutable_isc->set("relay-agent-info", extended_info);
5055 mutable_user_context->set("ISC", mutable_isc);
5056 }
5057
5058 // Update the lease's user_context.
5059 lease->setContext(mutable_user_context);
5060
5061 return (changed);
5062}
5063
5064void
5066 const AllocEngine::ClientContext6& ctx) const {
5067 // The extended info action is a transient value but be safe so reset it.
5068 lease->extended_info_action_ = Lease6::ACTION_IGNORE;
5069
5070 // If storage is not enabled then punt.
5071 if (!ctx.subnet_->getStoreExtendedInfo()) {
5072 return;
5073 }
5074
5075 // If we do not have relay information, then punt.
5076 if (ctx.query_->relay_info_.empty()) {
5077 return;
5078 }
5079
5080 // We need to convert the vector of RelayInfo instances in
5081 // into an Element hierarchy like this:
5082 // "relay-info": [
5083 // {
5084 // "hop": 123,
5085 // "link": "2001:db8::1",
5086 // "peer": "2001:db8::2",
5087 // "options": "0x..."
5088 // },..]
5089 //
5090 ElementPtr extended_info = Element::createList();
5091 for (auto const& relay : ctx.query_->relay_info_) {
5092 ElementPtr relay_elem = Element::createMap();
5093 relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
5094 relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
5095 relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
5096
5097 // If there are relay options, we'll pack them into a buffer and then
5098 // convert that into a hex string. If there are no options, we omit
5099 // then entry.
5100 if (!relay.options_.empty()) {
5101 OutputBuffer buf(128);
5102 LibDHCP::packOptions6(buf, relay.options_);
5103
5104 if (buf.getLength() > 0) {
5105 const uint8_t* cp = buf.getData();
5106 std::vector<uint8_t> bytes;
5107 std::stringstream ss;
5108
5109 bytes.assign(cp, cp + buf.getLength());
5110 ss << "0x" << encode::encodeHex(bytes);
5111 relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
5112 }
5113
5114 auto remote_id_it = relay.options_.find(D6O_REMOTE_ID);
5115 if (remote_id_it != relay.options_.end()) {
5116 OptionPtr remote_id = remote_id_it->second;
5117 if (remote_id) {
5118 std::vector<uint8_t> bytes = remote_id->toBinary();
5119 if (bytes.size() > 0) {
5120 relay_elem->set("remote-id",
5122 }
5123 }
5124 }
5125
5126 auto relay_id_it = relay.options_.find(D6O_RELAY_ID);
5127 if (relay_id_it != relay.options_.end()) {
5128 OptionPtr relay_id = relay_id_it->second;
5129 if (relay_id) {
5130 std::vector<uint8_t> bytes = relay_id->toBinary(false);
5131 if (bytes.size() > 0) {
5132 relay_elem->set("relay-id",
5134 }
5135 }
5136 }
5137 }
5138
5139 extended_info->add(relay_elem);
5140 }
5141
5142 // Get a mutable copy of the lease's current user context.
5143 ConstElementPtr user_context = lease->getContext();
5144 ElementPtr mutable_user_context;
5145 if (user_context && (user_context->getType() == Element::map)) {
5146 mutable_user_context = copy(user_context, 0);
5147 } else {
5148 mutable_user_context = Element::createMap();
5149 }
5150
5151 // Get a mutable copy of the ISC entry.
5152 ConstElementPtr isc = mutable_user_context->get("ISC");
5153 ElementPtr mutable_isc;
5154 if (isc && (isc->getType() == Element::map)) {
5155 mutable_isc = copy(isc, 0);
5156 } else {
5157 mutable_isc = Element::createMap();
5158 }
5159
5160 // Add/replace the extended info entry.
5161 ConstElementPtr old_extended_info = mutable_isc->get("relay-info");
5162 if (!old_extended_info || (*old_extended_info != *extended_info)) {
5163 lease->extended_info_action_ = Lease6::ACTION_UPDATE;
5164 mutable_isc->set("relay-info", extended_info);
5165 mutable_user_context->set("ISC", mutable_isc);
5166 }
5167
5168 // Update the lease's user context.
5169 lease->setContext(mutable_user_context);
5170}
5171
5172void
5173AllocEngine::setLeaseReusable(const Lease4Ptr& lease,
5174 const ClientContext4& ctx) const {
5175 // Sanity.
5176 lease->reuseable_valid_lft_ = 0;
5177 const ConstSubnet4Ptr& subnet = ctx.subnet_;
5178 if (!subnet) {
5179 return;
5180 }
5181 if (lease->state_ != Lease::STATE_DEFAULT) {
5182 return;
5183 }
5184
5185 // Always reuse infinite lifetime leases.
5186 if (lease->valid_lft_ == Lease::INFINITY_LFT) {
5187 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
5188 return;
5189 }
5190
5191 // Refuse time not going forward.
5192 if (lease->cltt_ < lease->current_cltt_) {
5193 return;
5194 }
5195
5196 uint32_t age = lease->cltt_ - lease->current_cltt_;
5197 // Already expired.
5198 if (age >= lease->current_valid_lft_) {
5199 return;
5200 }
5201
5202 // Try cache max age.
5203 uint32_t max_age = 0;
5204 if (!subnet->getCacheMaxAge().unspecified()) {
5205 max_age = subnet->getCacheMaxAge().get();
5206 if ((max_age == 0) || (age > max_age)) {
5207 return;
5208 }
5209 }
5210
5211 // Try cache threshold.
5212 if (!subnet->getCacheThreshold().unspecified()) {
5213 double threshold = subnet->getCacheThreshold().get();
5214 if ((threshold <= 0.) || (threshold > 1.)) {
5215 return;
5216 }
5217 max_age = lease->valid_lft_ * threshold;
5218 if (age > max_age) {
5219 return;
5220 }
5221 }
5222
5223 // No cache.
5224 if (max_age == 0) {
5225 return;
5226 }
5227
5228 // Seems to be reusable.
5229 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
5230}
5231
5232void
5233AllocEngine::setLeaseReusable(const Lease6Ptr& lease,
5234 uint32_t current_preferred_lft,
5235 const ClientContext6& ctx) const {
5236 // Sanity.
5237 lease->reuseable_valid_lft_ = 0;
5238 lease->reuseable_preferred_lft_ = 0;
5239 const ConstSubnet6Ptr& subnet = ctx.subnet_;
5240 if (!subnet) {
5241 return;
5242 }
5243 if (lease->state_ != Lease::STATE_DEFAULT) {
5244 return;
5245 }
5246
5247 // Refuse time not going forward.
5248 if (lease->cltt_ < lease->current_cltt_) {
5249 return;
5250 }
5251
5252 uint32_t age = lease->cltt_ - lease->current_cltt_;
5253 // Already expired.
5254 if (age >= lease->current_valid_lft_) {
5255 return;
5256 }
5257
5258 // Try cache max age.
5259 uint32_t max_age = 0;
5260 if (!subnet->getCacheMaxAge().unspecified()) {
5261 max_age = subnet->getCacheMaxAge().get();
5262 if ((max_age == 0) || (age > max_age)) {
5263 return;
5264 }
5265 }
5266
5267 // Try cache threshold.
5268 if (!subnet->getCacheThreshold().unspecified()) {
5269 double threshold = subnet->getCacheThreshold().get();
5270 if ((threshold <= 0.) || (threshold > 1.)) {
5271 return;
5272 }
5273 max_age = lease->valid_lft_ * threshold;
5274 if (age > max_age) {
5275 return;
5276 }
5277 }
5278
5279 // No cache.
5280 if (max_age == 0) {
5281 return;
5282 }
5283
5284 // Seems to be reusable.
5285 if ((current_preferred_lft == Lease::INFINITY_LFT) ||
5286 (current_preferred_lft == 0)) {
5287 // Keep these values.
5288 lease->reuseable_preferred_lft_ = current_preferred_lft;
5289 } else if (current_preferred_lft > age) {
5290 lease->reuseable_preferred_lft_ = current_preferred_lft - age;
5291 } else {
5292 // Can be a misconfiguration so stay safe...
5293 return;
5294 }
5295 if (lease->current_valid_lft_ == Lease::INFINITY_LFT) {
5296 lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
5297 } else {
5298 lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
5299 }
5300}
5301
5302} // namespace dhcp
5303} // namespace isc
CtrlAgentHooks Hooks
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
Notes: IntElement type is changed to int64_t.
Definition data.h:615
Multiple lease records found where one expected.
Defines a single hint.
static IPv6Resrv makeIPv6Resrv(const Lease6 &lease)
Creates an IPv6Resrv instance from a Lease6.
void updateLease6ExtendedInfo(const Lease6Ptr &lease, const ClientContext6 &ctx) const
Stores additional client query parameters on a V6 lease.
void reclaimExpiredLeases6Internal(const size_t max_leases, const uint16_t timeout, const bool remove_lease, const uint16_t max_unwarned_cycles=0)
Body of reclaimExpiredLeases6.
bool updateLease4ExtendedInfo(const Lease4Ptr &lease, const ClientContext4 &ctx) const
Stores additional client query parameters on a V4 lease.
static uint32_t getOfferLft(const ClientContext4 &ctx)
Returns the offer lifetime based on the v4 context.
static ConstHostPtr findGlobalReservation(ClientContext6 &ctx)
Attempts to find the host reservation for the client.
std::pair< Host::IdentifierType, std::vector< uint8_t > > IdentifierPair
A tuple holding host identifier type and value.
AllocEngine(isc::util::uint128_t const &attempts)
Constructor.
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.
static std::string labelNetworkOrSubnet(ConstSubnetPtr subnet)
Generates a label for subnet or shared-network from subnet.
void reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout, const bool remove_lease, const uint16_t max_unwarned_cycles=0)
Reclaims expired IPv4 leases.
Lease4Ptr allocateLease4(ClientContext4 &ctx)
Returns IPv4 lease.
void deleteExpiredReclaimedLeases6(const uint32_t secs)
Deletes reclaimed leases expired more than specified amount of time ago.
Lease6Collection allocateLeases6(ClientContext6 &ctx)
Allocates IPv6 leases for a given IA container.
Lease6Collection renewLeases6(ClientContext6 &ctx)
Renews existing DHCPv6 leases for a given IA.
PrefixLenMatchType
Type of preferred PD-pool prefix length selection criteria.
Definition allocator.h:61
static bool isValidPrefixPool(Allocator::PrefixLenMatchType prefix_length_match, PoolPtr pool, uint8_t hint_prefix_length)
Check if the pool matches the selection criteria relative to the provided hint prefix length.
Definition allocator.cc:38
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition cfgmgr.cc:68
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:28
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:115
Container for storing client class names.
Definition classify.h:109
bool empty() const
Check if classes is empty.
Definition classify.h:169
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition srv_config.h:49
ConstHostCollection getAll6(const SubnetID &subnet_id, const HostMgrOperationTarget target) const
Return all hosts in a DHCPv6 subnet.
Definition host_mgr.cc:171
ConstHostCollection getAll4(const SubnetID &subnet_id, const HostMgrOperationTarget target) const
Return all hosts in a DHCPv4 subnet.
Definition host_mgr.cc:151
ConstHostCollection getAll(const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, const HostMgrOperationTarget target) const
Return all hosts connected to any subnet for which reservations have been made using a specified iden...
Definition host_mgr.cc:123
bool getDisableSingleQuery() const
Returns the disable single query flag.
Definition host_mgr.h:796
static HostMgr & instance()
Returns a sole instance of the HostMgr.
Definition host_mgr.cc:114
ConstHostPtr get4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, const HostMgrOperationTarget target) const
Returns a host connected to the IPv4 subnet.
Definition host_mgr.cc:465
ConstHostPtr get6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, const HostMgrOperationTarget target) const
Returns a host connected to the IPv6 subnet.
Definition host_mgr.cc:650
IPv6 reservation for a host.
Definition host.h:163
Type
Type of the reservation.
Definition host.h:169
static TrackingLeaseMgr & instance()
Return current lease manager.
Abstract Lease Manager.
Definition lease_mgr.h:248
virtual Lease6Collection getLeases6(Lease::Type type, const DUID &duid, uint32_t iaid) const =0
Returns existing IPv6 leases for a given DUID+IA combination.
virtual void getExpiredLeases6(Lease6Collection &expired_leases, const size_t max_leases) const =0
Returns a collection of expired DHCPv6 leases.
virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs)=0
Deletes all expired and reclaimed DHCPv6 leases.
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const =0
Returns an IPv4 lease for specified IPv4 address.
virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs)=0
Deletes all expired and reclaimed DHCPv4 leases.
virtual bool addLease(const Lease4Ptr &lease)=0
Adds an IPv4 lease.
virtual bool deleteLease(const Lease4Ptr &lease)=0
Deletes an IPv4 lease.
virtual void getExpiredLeases4(Lease4Collection &expired_leases, const size_t max_leases) const =0
Returns a collection of expired DHCPv4 leases.
virtual void updateLease4(const Lease4Ptr &lease4)=0
Updates IPv4 lease.
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const =0
Returns existing IPv6 lease for a given IPv6 address.
virtual void updateLease6(const Lease6Ptr &lease6)=0
Updates IPv6 lease.
static void packOptions6(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv6 options in a buffer.
static std::string makeLabel(const HWAddrPtr &hwaddr, const ClientIdPtr &client_id, const uint32_t transid)
Returns text representation of the given packet identifiers.
Definition pkt4.cc:397
static std::string makeLabel(const DuidPtr duid, const uint32_t transid, const HWAddrPtr &hwaddr)
Returns text representation of the given packet identifiers.
Definition pkt6.cc:691
Resource race avoidance RAII handler for DHCPv4.
bool tryLock4(const asiolink::IOAddress &addr)
Tries to acquires a resource.
Resource race avoidance RAII handler.
bool tryLock(Lease::Type type, const asiolink::IOAddress &addr)
Tries to acquires a resource.
RAII object enabling copying options retrieved from the packet.
Definition pkt.h:46
static bool subnetsIncludeMatchClientId(const ConstSubnet4Ptr &first_subnet, const ClientClasses &client_classes)
Checks if the shared network includes a subnet with the match client ID flag set to true.
CalloutNextStep
Specifies allowed next steps.
@ NEXT_STEP_CONTINUE
continue normally
@ NEXT_STEP_SKIP
skip the next processing step
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static boost::shared_ptr< CalloutHandle > createCalloutHandle()
Return callout handle.
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
Wrapper class around callout handle which automatically resets handle's state.
Statistics Manager class.
static StatsMgr & instance()
Statistics Manager accessor method.
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
bool getMode() const
Get the multi-threading mode.
A template representing an optional value.
Definition optional.h:36
T get() const
Retrieves the encapsulated value.
Definition optional.h:114
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition optional.h:136
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:343
const uint8_t * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition buffer.h:395
size_t getLength() const
Return the length of data written in the buffer.
Definition buffer.h:409
Utility class to measure code execution times.
Definition stopwatch.h:35
long getTotalMilliseconds() const
Retrieves the total measured duration in milliseconds.
Definition stopwatch.cc:59
void stop()
Stops the stopwatch.
Definition stopwatch.cc:34
std::string logFormatTotalDuration() const
Returns the total measured duration in the format directly usable in the log messages.
Definition stopwatch.cc:79
This template specifies a parameter value.
Definition triplet.h:37
T get(T hint) const
Returns value with a hint.
Definition triplet.h:99
Write mutex RAII handler.
@ D6O_CLIENT_FQDN
Definition dhcp6.h:59
@ D6O_REMOTE_ID
Definition dhcp6.h:57
@ D6O_RELAY_ID
Definition dhcp6.h:73
@ DHCPV6_RENEW
Definition dhcp6.h:202
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< OptionUint32 > OptionUint32Ptr
Definition option_int.h:35
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition data.cc:1420
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_NO_LEASES_HR
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_INVALID
const isc::log::MessageID ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition dhcpsrv_log.h:56
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_NO_POOLS
const isc::log::MessageID ALLOC_ENGINE_IGNORING_UNSUITABLE_GLOBAL_ADDRESS6
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_RECOVER_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_EXTEND_LEASE
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_IN_USE
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_HR_LEASE_EXISTS
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED
const isc::log::MessageID ALLOC_ENGINE_V6_RENEW_HR
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_LEASE_DATA
const isc::log::MessageID ALLOC_ENGINE_V4_REUSE_EXPIRED_LEASE_DATA
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_LEASE
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE
const isc::log::MessageID ALLOC_ENGINE_V6_REUSE_EXPIRED_LEASE_DATA
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_NO_V6_HR
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_ERROR
const isc::log::MessageID ALLOC_ENGINE_V6_HINT_RESERVED
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_OUT_OF_POOL
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_SLOW
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_ADDRESS_RESERVED
isc::log::Logger alloc_engine_logger("alloc-engine")
Logger for the AllocEngine.
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_REQUESTED_LEASE
const isc::log::MessageID ALLOC_ENGINE_LEASE_RECLAIMED
@ DHO_DHCP_AGENT_OPTIONS
Definition dhcp4.h:151
@ DHO_DHCP_LEASE_TIME
Definition dhcp4.h:120
const int ALLOC_ENGINE_DBG_TRACE
Logging levels for the AllocEngine.
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:623
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_COMPLETE
const isc::log::MessageID ALLOC_ENGINE_IGNORING_UNSUITABLE_GLOBAL_ADDRESS
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_COMPLETE
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition host.h:843
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:458
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_ERROR
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_LEASES_HR
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_ALLOC_UNRESERVED
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition lease.h:508
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition lease.h:673
const isc::log::MessageID ALLOC_ENGINE_V4_DECLINED_RECOVERED
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_SELECT_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_SLOW
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_USE_HR
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_TIMEOUT
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
boost::shared_ptr< DdnsParams > DdnsParamsPtr
Defines a pointer for DdnsParams instances.
Definition srv_config.h:204
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_START
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
const isc::log::MessageID ALLOC_ENGINE_V6_RENEW_REMOVE_RESERVED
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition host.h:273
const isc::log::MessageID ALLOC_ENGINE_V6_LEASE_RECLAIM
const isc::log::MessageID ALLOC_ENGINE_V4_LEASE_RECLAIM
const isc::log::MessageID ALLOC_ENGINE_V4_DISCOVER_HR
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_TIMEOUT
const isc::log::MessageID DHCPSRV_CFGMGR_IP_RESERVATIONS_UNIQUE_DUPLICATES_DETECTED
const isc::log::MessageID ALLOC_ENGINE_V6_HR_ADDR_GRANTED
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_CLASSES
const isc::log::MessageID ALLOC_ENGINE_V6_NO_MORE_EXPIRED_LEASES
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_NEW_LEASE_DATA
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_ERROR
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_EXISTING_LEASE
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_NO_POOLS
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_SHARED_NETWORK
const isc::log::MessageID ALLOC_ENGINE_V6_EXPIRED_HINT_RESERVED
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
const int ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA
Records detailed results of various operations.
const int DHCPSRV_DBG_HOOKS
Definition dhcpsrv_log.h:46
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_FAILED
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_COMPLETE
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition subnet_id.h:25
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition duid.h:216
const isc::log::MessageID ALLOC_ENGINE_V4_NO_MORE_EXPIRED_LEASES
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_EXTEND_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_PICK_ADDRESS
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_NEW_LEASE
boost::shared_ptr< Option6IAAddr > Option6IAAddrPtr
A pointer to the isc::dhcp::Option6IAAddr object.
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_SELECT_SKIP
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition host.h:840
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_RECOVER_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_COMPLETE
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_FAILED
const isc::log::MessageID ALLOC_ENGINE_V6_CALCULATED_PREFERRED_LIFETIME
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_UNRESERVED
const isc::log::MessageID ALLOC_ENGINE_V6_DECLINED_RECOVERED
boost::shared_ptr< const Subnet > ConstSubnetPtr
A generic pointer to either const Subnet4 or const Subnet6 object.
Definition subnet.h:452
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_START
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
const isc::log::MessageID ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED
@ RAI_OPTION_RELAY_ID
Definition dhcp4.h:276
@ RAI_OPTION_REMOTE_ID
Definition dhcp4.h:266
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition lease.h:500
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_ALLOC_REQUESTED
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_LEASES_NO_HR
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_SUBNET
const isc::log::MessageID ALLOC_ENGINE_V4_DISCOVER_ADDRESS_CONFLICT
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_RENEW_SKIP
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition lease.h:295
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_REMOVE_LEASE
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_SUBNET
const int ALLOC_ENGINE_DBG_TRACE_DETAIL
Record detailed traces.
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_SHARED_NETWORK
const isc::log::MessageID ALLOC_ENGINE_V6_HR_PREFIX_GRANTED
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_CLASSES
boost::shared_ptr< Pool6 > Pool6Ptr
a pointer an IPv6 Pool
Definition pool.h:295
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
string encodeHex(const vector< uint8_t > &binary)
Encode binary data in the base16 format.
Definition encode.cc:361
boost::multiprecision::checked_uint128_t uint128_t
Definition bigints.h:21
Defines the logger used by the top-level component of kea-lfc.
This file provides the classes needed to embody, compose, and decompose DNS update requests that are ...
Context information for the DHCPv4 lease allocation.
ClientIdPtr clientid_
Client identifier from the DHCP message.
DdnsParamsPtr getDdnsParams()
Returns the set of DDNS behavioral parameters based on the selected subnet.
bool early_global_reservations_lookup_
Indicates if early global reservation is enabled.
ConstHostPtr currentHost() const
Returns host for currently selected subnet.
Pkt4Ptr query_
A pointer to the client's message.
Lease4Ptr new_lease_
A pointer to a newly allocated lease.
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
ConstSubnet4Ptr subnet_
Subnet selected for the client by the server.
bool rev_dns_update_
Perform reverse DNS update.
uint32_t offer_lft_
If not zero, then we will allocate on DISCOVER for this amount of time.
bool fake_allocation_
Indicates if this is a real or fake allocation.
hooks::CalloutHandlePtr callout_handle_
Callout handle associated with the client's message.
bool unknown_requested_addr_
True when the address DHCPREQUEST'ed by client is not within a dynamic pool the server knows about.
Lease4Ptr old_lease_
A pointer to an old lease that the client had before update.
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
bool fwd_dns_update_
Perform forward DNS update.
asiolink::IOAddress requested_address_
An address that the client desires.
Lease4Ptr conflicting_lease_
A pointer to the object representing a lease in conflict.
void addHostIdentifier(const Host::IdentifierType &id_type, const std::vector< uint8_t > &identifier)
Convenience function adding host identifier into host_identifiers_ list.
IdentifierList host_identifiers_
A list holding host identifiers extracted from a message received by the server.
HWAddrPtr hwaddr_
HW address from the DHCP message.
bool isNewResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128) const
Checks if specified address or prefix was new.
void addHint(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128, const uint32_t preferred=0, const uint32_t valid=0)
Convenience method adding new hint.
void addNewResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128)
Convenience method adding new prefix or address.
uint32_t iaid_
The IAID field from IA_NA or IA_PD that is being processed.
Context information for the DHCPv6 leases allocation.
IAContext & currentIA()
Returns IA specific context for the currently processed IA.
void addHostIdentifier(const Host::IdentifierType &id_type, const std::vector< uint8_t > &identifier)
Convenience function adding host identifier into host_identifiers_ list.
ConstHostPtr currentHost() const
Returns host from the most preferred subnet.
DuidPtr duid_
Client identifier.
void addAllocatedResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128)
Convenience method adding allocated prefix or address.
Lease6Collection new_leases_
A collection of newly allocated leases.
bool isAllocated(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128) const
Checks if specified address or prefix was allocated.
bool hasGlobalReservation(const IPv6Resrv &resv) const
Determines if a global reservation exists.
ResourceContainer allocated_resources_
Holds addresses and prefixes allocated for all IAs.
DdnsParamsPtr getDdnsParams()
Returns the set of DDNS behavioral parameters based on the selected subnet.
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
Pkt6Ptr query_
A pointer to the client's message.
bool early_global_reservations_lookup_
Indicates if early global reservation is enabled.
ConstSubnet6Ptr host_subnet_
Subnet from which host reservations should be retrieved.
IdentifierList host_identifiers_
A list holding host identifiers extracted from a message received by the server.
ConstSubnet6Ptr subnet_
Subnet selected for the client by the server.
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
Structure that holds a lease for IPv4 address.
Definition lease.h:303
Structure that holds a lease for IPv6 address and/or prefix.
Definition lease.h:516
@ ACTION_UPDATE
update extended info tables.
Definition lease.h:556
@ ACTION_DELETE
delete reference to the lease
Definition lease.h:555
@ ACTION_IGNORE
ignore extended info,
Definition lease.h:554
a common structure for IPv4 and IPv6 leases
Definition lease.h:31
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire)
Definition lease.h:34
static const uint32_t STATE_DEFAULT
A lease in the default state.
Definition lease.h:69
static const uint32_t STATE_DECLINED
Declined lease.
Definition lease.h:72
static const uint32_t STATE_RELEASED
Released lease held in the database for lease affinity.
Definition lease.h:78
static const uint32_t STATE_EXPIRED_RECLAIMED
Expired and reclaimed lease.
Definition lease.h:75
Type
Type of lease or pool.
Definition lease.h:46
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition lease.h:49
@ TYPE_V4
IPv4 lease.
Definition lease.h:50
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition lease.h:47