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