Kea  1.9.9-git
alloc_engine.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2021 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 
9 #include <dhcp/dhcp6.h>
10 #include <dhcp/pkt4.h>
11 #include <dhcp/pkt6.h>
12 #include <dhcp/option_int.h>
13 #include <dhcp_ddns/ncr_msg.h>
14 #include <dhcpsrv/alloc_engine.h>
16 #include <dhcpsrv/cfgmgr.h>
17 #include <dhcpsrv/dhcpsrv_log.h>
18 #include <dhcpsrv/host_mgr.h>
19 #include <dhcpsrv/host.h>
21 #include <dhcpsrv/ncr_generator.h>
22 #include <dhcpsrv/network.h>
24 #include <dhcpsrv/shared_network.h>
25 #include <hooks/callout_handle.h>
26 #include <hooks/hooks_manager.h>
28 #include <stats/stats_mgr.h>
29 #include <util/encode/hex.h>
30 #include <util/stopwatch.h>
31 #include <hooks/server_hooks.h>
32 
33 #include <boost/foreach.hpp>
34 #include <boost/make_shared.hpp>
35 
36 #include <algorithm>
37 #include <cstring>
38 #include <limits>
39 #include <sstream>
40 #include <stdint.h>
41 #include <string.h>
42 #include <utility>
43 #include <vector>
44 
45 using namespace isc::asiolink;
46 using namespace isc::dhcp;
47 using namespace isc::dhcp_ddns;
48 using namespace isc::hooks;
49 using namespace isc::stats;
50 using namespace isc::util;
51 using namespace isc::data;
52 namespace ph = std::placeholders;
53 
54 namespace {
55 
57 struct AllocEngineHooks {
58  int hook_index_lease4_select_;
59  int hook_index_lease4_renew_;
60  int hook_index_lease4_expire_;
61  int hook_index_lease4_recover_;
62  int hook_index_lease6_select_;
63  int hook_index_lease6_renew_;
64  int hook_index_lease6_rebind_;
65  int hook_index_lease6_expire_;
66  int hook_index_lease6_recover_;
67 
69  AllocEngineHooks() {
70  hook_index_lease4_select_ = HooksManager::registerHook("lease4_select");
71  hook_index_lease4_renew_ = HooksManager::registerHook("lease4_renew");
72  hook_index_lease4_expire_ = HooksManager::registerHook("lease4_expire");
73  hook_index_lease4_recover_= HooksManager::registerHook("lease4_recover");
74  hook_index_lease6_select_ = HooksManager::registerHook("lease6_select");
75  hook_index_lease6_renew_ = HooksManager::registerHook("lease6_renew");
76  hook_index_lease6_rebind_ = HooksManager::registerHook("lease6_rebind");
77  hook_index_lease6_expire_ = HooksManager::registerHook("lease6_expire");
78  hook_index_lease6_recover_= HooksManager::registerHook("lease6_recover");
79  }
80 };
81 
82 // Declare a Hooks object. As this is outside any function or method, it
83 // will be instantiated (and the constructor run) when the module is loaded.
84 // As a result, the hook indexes will be defined before any method in this
85 // module is called.
86 AllocEngineHooks Hooks;
87 
88 } // namespace
89 
90 namespace isc {
91 namespace dhcp {
92 
93 AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
94  : Allocator(lease_type) {
95 }
96 
99  const uint8_t prefix_len) {
100  if (!prefix.isV6()) {
101  isc_throw(BadValue, "Prefix operations are for IPv6 only (attempted to "
102  "increase prefix " << prefix << ")");
103  }
104 
105  // Get a buffer holding an address.
106  const std::vector<uint8_t>& vec = prefix.toBytes();
107 
108  if (prefix_len < 1 || prefix_len > 128) {
109  isc_throw(BadValue, "Cannot increase prefix: invalid prefix length: "
110  << prefix_len);
111  }
112 
113  uint8_t n_bytes = (prefix_len - 1)/8;
114  uint8_t n_bits = 8 - (prefix_len - n_bytes*8);
115  uint8_t mask = 1 << n_bits;
116 
117  // Explanation: n_bytes specifies number of full bytes that are in-prefix.
118  // They can also be used as an offset for the first byte that is not in
119  // prefix. n_bits specifies number of bits on the last byte that is
120  // (often partially) in prefix. For example for a /125 prefix, the values
121  // are 15 and 3, respectively. Mask is a bitmask that has the least
122  // significant bit from the prefix set.
123 
124  uint8_t packed[V6ADDRESS_LEN];
125 
126  // Copy the address. It must be V6, but we already checked that.
127  std::memcpy(packed, &vec[0], V6ADDRESS_LEN);
128 
129  // Can we safely increase only the last byte in prefix without overflow?
130  if (packed[n_bytes] + uint16_t(mask) < 256u) {
131  packed[n_bytes] += mask;
132  return (IOAddress::fromBytes(AF_INET6, packed));
133  }
134 
135  // Overflow (done on uint8_t, but the sum is greater than 255)
136  packed[n_bytes] += mask;
137 
138  // Deal with the overflow. Start increasing the least significant byte
139  for (int i = n_bytes - 1; i >= 0; --i) {
140  ++packed[i];
141  // If we haven't overflowed (0xff->0x0) the next byte, then we are done
142  if (packed[i] != 0) {
143  break;
144  }
145  }
146 
147  return (IOAddress::fromBytes(AF_INET6, packed));
148 }
149 
152  bool prefix,
153  const uint8_t prefix_len) {
154  if (!prefix) {
155  return (IOAddress::increase(address));
156  } else {
157  return (increasePrefix(address, prefix_len));
158  }
159 }
160 
162 AllocEngine::IterativeAllocator::pickAddressInternal(const SubnetPtr& subnet,
163  const ClientClasses& client_classes,
164  const DuidPtr&,
165  const IOAddress&) {
166  // Is this prefix allocation?
167  bool prefix = pool_type_ == Lease::TYPE_PD;
168  uint8_t prefix_len = 0;
169 
170  // Let's get the last allocated address. It is usually set correctly,
171  // but there are times when it won't be (like after removing a pool or
172  // perhaps restarting the server).
173  IOAddress last = subnet->getLastAllocated(pool_type_);
174  bool valid = true;
175  bool retrying = false;
176 
177  const PoolCollection& pools = subnet->getPools(pool_type_);
178 
179  if (pools.empty()) {
180  isc_throw(AllocFailed, "No pools defined in selected subnet");
181  }
182 
183  // first we need to find a pool the last address belongs to.
184  PoolCollection::const_iterator it;
185  PoolCollection::const_iterator first = pools.end();
186  PoolPtr first_pool;
187  for (it = pools.begin(); it != pools.end(); ++it) {
188  if (!(*it)->clientSupported(client_classes)) {
189  continue;
190  }
191  if (first == pools.end()) {
192  first = it;
193  }
194  if ((*it)->inRange(last)) {
195  break;
196  }
197  }
198 
199  // Caller checked this cannot happen
200  if (first == pools.end()) {
201  isc_throw(AllocFailed, "No allowed pools defined in selected subnet");
202  }
203 
204  // last one was bogus for one of several reasons:
205  // - we just booted up and that's the first address we're allocating
206  // - a subnet was removed or other reconfiguration just completed
207  // - perhaps allocation algorithm was changed
208  // - last pool does not allow this client
209  if (it == pools.end()) {
210  it = first;
211  }
212 
213  for (;;) {
214  // Trying next pool
215  if (retrying) {
216  for (; it != pools.end(); ++it) {
217  if ((*it)->clientSupported(client_classes)) {
218  break;
219  }
220  }
221  if (it == pools.end()) {
222  // Really out of luck today. That was the last pool.
223  break;
224  }
225  }
226 
227  last = (*it)->getLastAllocated();
228  valid = (*it)->isLastAllocatedValid();
229  if (!valid && (last == (*it)->getFirstAddress())) {
230  // Pool was (re)initialized
231  (*it)->setLastAllocated(last);
232  subnet->setLastAllocated(pool_type_, last);
233  return (last);
234  }
235  // still can be bogus
236  if (valid && !(*it)->inRange(last)) {
237  valid = false;
238  (*it)->resetLastAllocated();
239  (*it)->setLastAllocated((*it)->getFirstAddress());
240  }
241 
242  if (valid) {
243  // Ok, we have a pool that the last address belonged to, let's use it.
244  if (prefix) {
245  Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(*it);
246 
247  if (!pool6) {
248  // Something is gravely wrong here
249  isc_throw(Unexpected, "Wrong type of pool: "
250  << (*it)->toText()
251  << " is not Pool6");
252  }
253  // Get the prefix length
254  prefix_len = pool6->getLength();
255  }
256 
257  IOAddress next = increaseAddress(last, prefix, prefix_len);
258  if ((*it)->inRange(next)) {
259  // the next one is in the pool as well, so we haven't hit
260  // pool boundary yet
261  (*it)->setLastAllocated(next);
262  subnet->setLastAllocated(pool_type_, next);
263  return (next);
264  }
265 
266  valid = false;
267  (*it)->resetLastAllocated();
268  }
269  // We hit pool boundary, let's try to jump to the next pool and try again
270  ++it;
271  retrying = true;
272  }
273 
274  // Let's rewind to the beginning.
275  for (it = first; it != pools.end(); ++it) {
276  if ((*it)->clientSupported(client_classes)) {
277  (*it)->setLastAllocated((*it)->getFirstAddress());
278  (*it)->resetLastAllocated();
279  }
280  }
281 
282  // ok to access first element directly. We checked that pools is non-empty
283  last = (*first)->getLastAllocated();
284  (*first)->setLastAllocated(last);
285  subnet->setLastAllocated(pool_type_, last);
286  return (last);
287 }
288 
290  : Allocator(lease_type) {
291  isc_throw(NotImplemented, "Hashed allocator is not implemented");
292 }
293 
295 AllocEngine::HashedAllocator::pickAddressInternal(const SubnetPtr&,
296  const ClientClasses&,
297  const DuidPtr&,
298  const IOAddress&) {
299  isc_throw(NotImplemented, "Hashed allocator is not implemented");
300 }
301 
303  : Allocator(lease_type) {
304  isc_throw(NotImplemented, "Random allocator is not implemented");
305 }
306 
308 AllocEngine::RandomAllocator::pickAddressInternal(const SubnetPtr&,
309  const ClientClasses&,
310  const DuidPtr&,
311  const IOAddress&) {
312  isc_throw(NotImplemented, "Random allocator is not implemented");
313 }
314 
315 AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts,
316  bool ipv6)
317  : attempts_(attempts), incomplete_v4_reclamations_(0),
318  incomplete_v6_reclamations_(0) {
319 
320  // Choose the basic (normal address) lease type
321  Lease::Type basic_type = ipv6 ? Lease::TYPE_NA : Lease::TYPE_V4;
322 
323  // Initialize normal address allocators
324  switch (engine_type) {
325  case ALLOC_ITERATIVE:
326  allocators_[basic_type] = AllocatorPtr(new IterativeAllocator(basic_type));
327  break;
328  case ALLOC_HASHED:
329  allocators_[basic_type] = AllocatorPtr(new HashedAllocator(basic_type));
330  break;
331  case ALLOC_RANDOM:
332  allocators_[basic_type] = AllocatorPtr(new RandomAllocator(basic_type));
333  break;
334  default:
335  isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
336  }
337 
338  // If this is IPv6 allocation engine, initialize also temporary addrs
339  // and prefixes
340  if (ipv6) {
341  switch (engine_type) {
342  case ALLOC_ITERATIVE:
345  break;
346  case ALLOC_HASHED:
349  break;
350  case ALLOC_RANDOM:
353  break;
354  default:
355  isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
356  }
357  }
358 
359  // Register hook points
360  hook_index_lease4_select_ = Hooks.hook_index_lease4_select_;
361  hook_index_lease6_select_ = Hooks.hook_index_lease6_select_;
362 }
363 
365  std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
366 
367  if (alloc == allocators_.end()) {
368  isc_throw(BadValue, "No allocator initialized for pool type "
369  << Lease::typeToText(type));
370  }
371  return (alloc->second);
372 }
373 
374 } // end of namespace isc::dhcp
375 } // end of namespace isc
376 
377 namespace {
378 
388 getIPv6Resrv(const SubnetID& subnet_id, const IOAddress& address) {
389  ConstHostCollection reserved;
390  // The global parameter ip-reservations-unique controls whether it is allowed
391  // to specify multiple reservations for the same IP address or delegated prefix
392  // or IP reservations must be unique. Some host backends do not support the
393  // former, thus we can't always use getAll6 calls to get the reservations
394  // for the given IP. When we're in the default mode, when IP reservations
395  // are unique, we should call get6 (supported by all backends). If we're in
396  // the mode in which non-unique reservations are allowed the backends which
397  // don't support it are not used and we can safely call getAll6.
398  if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
399  auto host = HostMgr::instance().get6(subnet_id, address);
400  if (host) {
401  reserved.push_back(host);
402  }
403  } else {
404  auto hosts = HostMgr::instance().getAll6(subnet_id, address);
405  reserved.insert(reserved.end(), hosts.begin(), hosts.end());
406  }
407  return (reserved);
408 }
409 
422 bool
423 inAllowedPool(AllocEngine::ClientContext6& ctx, const Lease::Type& lease_type,
424  const IOAddress& address, bool check_subnet) {
425  // If the subnet belongs to a shared network we will be iterating
426  // over the subnets that belong to this shared network.
427  Subnet6Ptr current_subnet = ctx.subnet_;
428  while (current_subnet) {
429 
430  if (current_subnet->clientSupported(ctx.query_->getClasses())) {
431  if (check_subnet) {
432  if (current_subnet->inPool(lease_type, address)) {
433  return (true);
434  }
435  } else {
436  if (current_subnet->inPool(lease_type, address,
437  ctx.query_->getClasses())) {
438  return (true);
439  }
440  }
441  }
442 
443  current_subnet = current_subnet->getNextSubnet(ctx.subnet_);
444  }
445 
446  return (false);
447 }
448 
449 }
450 
451 // ##########################################################################
452 // # DHCPv6 lease allocation code starts here.
453 // ##########################################################################
454 
455 namespace isc {
456 namespace dhcp {
457 
459  : query_(), fake_allocation_(false), subnet_(), host_subnet_(), duid_(),
460  hwaddr_(), host_identifiers_(), hosts_(), fwd_dns_update_(false),
461  rev_dns_update_(false), hostname_(), callout_handle_(), ias_(),
462  ddns_params_() {
463 }
464 
466  const DuidPtr& duid,
467  const bool fwd_dns,
468  const bool rev_dns,
469  const std::string& hostname,
470  const bool fake_allocation,
471  const Pkt6Ptr& query,
472  const CalloutHandlePtr& callout_handle)
473  : query_(query), fake_allocation_(fake_allocation), subnet_(subnet),
474  duid_(duid), hwaddr_(), host_identifiers_(), hosts_(),
475  fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname),
476  callout_handle_(callout_handle), allocated_resources_(), new_leases_(),
477  ias_(), ddns_params_() {
478 
479  // Initialize host identifiers.
480  if (duid) {
481  addHostIdentifier(Host::IDENT_DUID, duid->getDuid());
482  }
483 }
484 
486  : iaid_(0), type_(Lease::TYPE_NA), hints_(), old_leases_(),
487  changed_leases_(), new_resources_(), ia_rsp_() {
488 }
489 
490 void
493  const uint8_t prefix_len,
494  const uint32_t preferred,
495  const uint32_t valid) {
496  hints_.push_back(Resource(prefix, prefix_len, preferred, valid));
497 }
498 
499 void
502  if (!iaaddr) {
503  isc_throw(BadValue, "IAADDR option pointer is null.");
504  }
505  addHint(iaaddr->getAddress(), 128,
506  iaaddr->getPreferred(), iaaddr->getValid());
507 }
508 
509 void
512  if (!iaprefix) {
513  isc_throw(BadValue, "IAPREFIX option pointer is null.");
514  }
515  addHint(iaprefix->getAddress(), iaprefix->getLength(),
516  iaprefix->getPreferred(), iaprefix->getValid());
517 }
518 
519 void
522  const uint8_t prefix_len) {
523  static_cast<void>(new_resources_.insert(Resource(prefix, prefix_len)));
524 }
525 
526 bool
529  const uint8_t prefix_len) const {
530  return (static_cast<bool>(new_resources_.count(Resource(prefix,
531  prefix_len))));
532 }
533 
534 void
537  const uint8_t prefix_len) {
538  static_cast<void>(allocated_resources_.insert(Resource(prefix,
539  prefix_len)));
540 }
541 
542 bool
544 isAllocated(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const {
545  return (static_cast<bool>
546  (allocated_resources_.count(Resource(prefix, prefix_len))));
547 }
548 
552  if (subnet && subnet->getReservationsInSubnet()) {
553  auto host = hosts_.find(subnet->getID());
554  if (host != hosts_.cend()) {
555  return (host->second);
556  }
557  }
558 
559  return (globalHost());
560 }
561 
565  if (subnet && subnet_->getReservationsGlobal()) {
566  auto host = hosts_.find(SUBNET_ID_GLOBAL);
567  if (host != hosts_.cend()) {
568  return (host->second);
569  }
570  }
571 
572  return (ConstHostPtr());
573 }
574 
575 bool
577  ConstHostPtr ghost = globalHost();
578  return (ghost && ghost->hasReservation(resv));
579 }
580 
583  // We already have it return it unless the context subnet has changed.
584  if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
585  return (ddns_params_);
586  }
587 
588  // Doesn't exist yet or is stale, (re)create it.
589  if (subnet_) {
590  ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
591  return (ddns_params_);
592  }
593 
594  // Asked for it without a subnet? This case really shouldn't occur but
595  // for now let's return an instance with default values.
596  return (DdnsParamsPtr(new DdnsParams()));
597 }
598 
599 void
601  ctx.hosts_.clear();
602 
603  // If there is no subnet, there is nothing to do.
604  if (!ctx.subnet_) {
605  return;
606  }
607 
608  auto subnet = ctx.subnet_;
609 
610  std::map<SubnetID, ConstHostPtr> host_map;
611  SharedNetwork6Ptr network;
612  subnet->getSharedNetwork(network);
613 
614  // @todo: This code can be trivially optimized.
615  if (subnet->getReservationsGlobal()) {
616  ConstHostPtr ghost = findGlobalReservation(ctx);
617  if (ghost) {
618  ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
619 
620  // If we had only to fetch global reservations it is done.
621  if (!subnet->getReservationsInSubnet()) {
622  return;
623  }
624  }
625  }
626 
627  // If the subnet belongs to a shared network it is usually going to be
628  // more efficient to make a query for all reservations for a particular
629  // client rather than a query for each subnet within this shared network.
630  // The only case when it is going to be less efficient is when there are
631  // more host identifier types in use than subnets within a shared network.
632  // As it breaks RADIUS use of host caching this can be disabled by the
633  // host manager.
634  const bool use_single_query = network &&
636  (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
637 
638  if (use_single_query) {
639  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
640  ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
641  &id_pair.second[0],
642  id_pair.second.size());
643  // Store the hosts in the temporary map, because some hosts may
644  // belong to subnets outside of the shared network. We'll need
645  // to eliminate them.
646  for (auto host = hosts.begin(); host != hosts.end(); ++host) {
647  if ((*host)->getIPv6SubnetID() != SUBNET_ID_GLOBAL) {
648  host_map[(*host)->getIPv6SubnetID()] = *host;
649  }
650  }
651  }
652  }
653 
654  // We can only search for the reservation if a subnet has been selected.
655  while (subnet) {
656 
657  // Only makes sense to get reservations if the client has access
658  // to the class and host reservations are enabled for this subnet.
659  if (subnet->clientSupported(ctx.query_->getClasses()) &&
660  subnet->getReservationsInSubnet()) {
661  // Iterate over configured identifiers in the order of preference
662  // and try to use each of them to search for the reservations.
663  if (use_single_query) {
664  if (host_map.count(subnet->getID()) > 0) {
665  ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
666  }
667  } else {
668  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
669  // Attempt to find a host using a specified identifier.
670  ConstHostPtr host = HostMgr::instance().get6(subnet->getID(),
671  id_pair.first,
672  &id_pair.second[0],
673  id_pair.second.size());
674  // If we found matching host for this subnet.
675  if (host) {
676  ctx.hosts_[subnet->getID()] = host;
677  break;
678  }
679  }
680  }
681  }
682 
683  // We need to get to the next subnet if this is a shared network. If it
684  // is not (a plain subnet), getNextSubnet will return NULL and we're
685  // done here.
686  subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
687  }
688 }
689 
692  ConstHostPtr host;
693  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
694  // Attempt to find a host using a specified identifier.
695  host = HostMgr::instance().get6(SUBNET_ID_GLOBAL, id_pair.first,
696  &id_pair.second[0], id_pair.second.size());
697 
698  // If we found matching global host we're done.
699  if (host) {
700  break;
701  }
702  }
703 
704  return (host);
705 }
706 
709 
710  try {
711  if (!ctx.subnet_) {
712  isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation");
713  } else
714  if (!ctx.duid_) {
715  isc_throw(InvalidOperation, "DUID is mandatory for IPv6 lease allocation");
716  }
717 
718  // Check if there are existing leases for that shared network and
719  // DUID/IAID.
720  Subnet6Ptr subnet = ctx.subnet_;
721  Lease6Collection all_leases =
723  *ctx.duid_,
724  ctx.currentIA().iaid_);
725 
726  // Iterate over the leases and eliminate those that are outside of
727  // our shared network.
728  Lease6Collection leases;
729  while (subnet) {
730  for (auto l : all_leases) {
731  if ((l)->subnet_id_ == subnet->getID()) {
732  leases.push_back(l);
733  }
734  }
735 
736  subnet = subnet->getNextSubnet(ctx.subnet_);
737  }
738 
739  // Now do the checks:
740  // Case 1. if there are no leases, and there are reservations...
741  // 1.1. are the reserved addresses are used by someone else?
742  // yes: we have a problem
743  // no: assign them => done
744  // Case 2. if there are leases and there are no reservations...
745  // 2.1 are the leases reserved for someone else?
746  // yes: release them, assign something else
747  // no: renew them => done
748  // Case 3. if there are leases and there are reservations...
749  // 3.1 are the leases matching reservations?
750  // yes: renew them => done
751  // no: release existing leases, assign new ones based on reservations
752  // Case 4/catch-all. if there are no leases and no reservations...
753  // assign new leases
754 
755  // Case 1: There are no leases and there's a reservation for this host.
756  if (leases.empty() && !ctx.hosts_.empty()) {
757 
760  .arg(ctx.query_->getLabel());
761 
762  // Try to allocate leases that match reservations. Typically this will
763  // succeed, except cases where the reserved addresses are used by
764  // someone else.
765  allocateReservedLeases6(ctx, leases);
766 
767  leases = updateLeaseData(ctx, leases);
768 
769  // If not, we'll need to continue and will eventually fall into case 4:
770  // getting a regular lease. That could happen when we're processing
771  // request from client X, there's a reserved address A for X, but
772  // A is currently used by client Y. We can't immediately reassign A
773  // from X to Y, because Y keeps using it, so X would send Decline right
774  // away. Need to wait till Y renews, then we can release A, so it
775  // will become available for X.
776 
777  // Case 2: There are existing leases and there are no reservations.
778  //
779  // There is at least one lease for this client and there are no reservations.
780  // We will return these leases for the client, but we may need to update
781  // FQDN information.
782  } else if (!leases.empty() && ctx.hosts_.empty()) {
783 
786  .arg(ctx.query_->getLabel());
787 
788  // Check if the existing leases are reserved for someone else.
789  // If they're not, we're ok to keep using them.
790  removeNonmatchingReservedLeases6(ctx, leases);
791 
792  leases = updateLeaseData(ctx, leases);
793 
794  // If leases are empty at this stage, it means that we used to have
795  // leases for this client, but we checked and those leases are reserved
796  // for someone else, so we lost them. We will need to continue and
797  // will finally end up in case 4 (no leases, no reservations), so we'll
798  // assign something new.
799 
800  // Case 3: There are leases and there are reservations.
801  } else if (!leases.empty() && !ctx.hosts_.empty()) {
802 
805  .arg(ctx.query_->getLabel());
806 
807  // First, check if have leases matching reservations, and add new
808  // leases if we don't have them.
809  allocateReservedLeases6(ctx, leases);
810 
811  // leases now contain both existing and new leases that were created
812  // from reservations.
813 
814  // Second, let's remove leases that are reserved for someone else.
815  // This applies to any existing leases. This will not happen frequently,
816  // but it may happen with the following chain of events:
817  // 1. client A gets address X;
818  // 2. reservation for client B for address X is made by a administrator;
819  // 3. client A reboots
820  // 4. client A requests the address (X) he got previously
821  removeNonmatchingReservedLeases6(ctx, leases);
822 
823  // leases now contain existing and new leases, but we removed those
824  // leases that are reserved for someone else (non-matching reserved).
825 
826  // There's one more check to do. Let's remove leases that are not
827  // matching reservations, i.e. if client X has address A, but there's
828  // a reservation for address B, we should release A and reassign B.
829  // Caveat: do this only if we have at least one reserved address.
830  removeNonreservedLeases6(ctx, leases);
831 
832  // All checks are done. Let's hope we have some leases left.
833 
834  // Update any leases we have left.
835  leases = updateLeaseData(ctx, leases);
836 
837  // If we don't have any leases at this stage, it means that we hit
838  // one of the following cases:
839  // - we have a reservation, but it's not for this IAID/ia-type and
840  // we had to return the address we were using
841  // - we have a reservation for this iaid/ia-type, but the reserved
842  // address is currently used by someone else. We can't assign it
843  // yet.
844  // - we had an address, but we just discovered that it's reserved for
845  // someone else, so we released it.
846  }
847 
848  if (leases.empty()) {
849  // Case 4/catch-all: One of the following is true:
850  // - we don't have leases and there are no reservations
851  // - we used to have leases, but we lost them, because they are now
852  // reserved for someone else
853  // - we have a reservation, but it is not usable yet, because the address
854  // is still used by someone else
855  //
856  // In any case, we need to go through normal lease assignment process
857  // for now. This is also a catch-all or last resort approach, when we
858  // couldn't find any reservations (or couldn't use them).
859 
862  .arg(ctx.query_->getLabel());
863 
864  leases = allocateUnreservedLeases6(ctx);
865  }
866 
867  if (!leases.empty()) {
868  // If there are any leases allocated, let's store in them in the
869  // IA context so as they are available when we process subsequent
870  // IAs.
871  BOOST_FOREACH(Lease6Ptr lease, leases) {
872  ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
873  ctx.new_leases_.push_back(lease);
874  }
875  return (leases);
876  }
877 
878  } catch (const isc::Exception& e) {
879 
880  // Some other error, return an empty lease.
882  .arg(ctx.query_->getLabel())
883  .arg(e.what());
884  }
885 
886  return (Lease6Collection());
887 }
888 
890 AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
891 
892  AllocatorPtr allocator = getAllocator(ctx.currentIA().type_);
893 
894  if (!allocator) {
895  isc_throw(InvalidOperation, "No allocator specified for "
896  << Lease6::typeToText(ctx.currentIA().type_));
897  }
898 
899  Lease6Collection leases;
900 
902  if (!ctx.currentIA().hints_.empty()) {
904  hint = ctx.currentIA().hints_[0].getAddress();
905  }
906 
907  Subnet6Ptr original_subnet = ctx.subnet_;
908  Subnet6Ptr subnet = ctx.subnet_;
909 
910  Pool6Ptr pool;
911 
912  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
913 
914  for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
915 
916  if (!subnet->clientSupported(ctx.query_->getClasses())) {
917  continue;
918  }
919 
920  ctx.subnet_ = subnet;
921 
922  // check if the hint is in pool and is available
923  // This is equivalent of subnet->inPool(hint), but returns the pool
924  pool = boost::dynamic_pointer_cast<Pool6>
925  (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(),
926  hint));
927 
928  // check if the pool is allowed
929  if (!pool || !pool->clientSupported(ctx.query_->getClasses())) {
930  continue;
931  }
932 
933  bool in_subnet = subnet->getReservationsInSubnet();
934 
936  Lease6Ptr lease =
937  LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, hint);
938  if (!lease) {
939 
940  // In-pool reservations: Check if this address is reserved for someone
941  // else. There is no need to check for whom it is reserved, because if
942  // it has been reserved for us we would have already allocated a lease.
943 
944  ConstHostCollection hosts;
945  // When out-of-pool flag is true the server may assume that all host
946  // reservations are for addresses that do not belong to the dynamic
947  // pool. Therefore, it can skip the reservation checks when dealing
948  // with in-pool addresses.
949  if (in_subnet &&
950  (!subnet->getReservationsOutOfPool() ||
951  !subnet->inPool(ctx.currentIA().type_, hint))) {
952  hosts = getIPv6Resrv(subnet->getID(), hint);
953  }
954 
955  if (hosts.empty()) {
956  // If the in-pool reservations are disabled, or there is no
957  // reservation for a given hint, we're good to go.
958 
959  // The hint is valid and not currently used, let's create a
960  // lease for it
961  lease = createLease6(ctx, hint, pool->getLength(), callout_status);
962 
963  // It can happen that the lease allocation failed (we could
964  // have lost the race condition. That means that the hint is
965  // no longer usable and we need to continue the regular
966  // allocation path.
967  if (lease) {
968 
970  Lease6Collection collection;
971  collection.push_back(lease);
972  return (collection);
973  }
974  } else {
977  .arg(ctx.query_->getLabel())
978  .arg(hint.toText());
979  }
980 
981  } else if (lease->expired()) {
982 
983  // If the lease is expired, we may likely reuse it, but...
984  ConstHostCollection hosts;
985  // When out-of-pool flag is true the server may assume that all host
986  // reservations are for addresses that do not belong to the dynamic
987  // pool. Therefore, it can skip the reservation checks when dealing
988  // with in-pool addresses.
989  if (in_subnet &&
990  (!subnet->getReservationsOutOfPool() ||
991  !subnet->inPool(ctx.currentIA().type_, hint))) {
992  hosts = getIPv6Resrv(subnet->getID(), hint);
993  }
994 
995  // Let's check if there is a reservation for this address.
996  if (hosts.empty()) {
997 
998  // Copy an existing, expired lease so as it can be returned
999  // to the caller.
1000  Lease6Ptr old_lease(new Lease6(*lease));
1001  ctx.currentIA().old_leases_.push_back(old_lease);
1002 
1004  lease = reuseExpiredLease(lease, ctx, pool->getLength(),
1005  callout_status);
1006 
1008  leases.push_back(lease);
1009  return (leases);
1010 
1011  } else {
1014  .arg(ctx.query_->getLabel())
1015  .arg(hint.toText());
1016  }
1017  }
1018  }
1019 
1020  // We have the choice in the order checking the lease and
1021  // the reservation. The default is to begin by the lease
1022  // if the multi-threading is disabled.
1023  bool check_reservation_first = MultiThreadingMgr::instance().getMode();
1024 
1025  uint64_t total_attempts = 0;
1026 
1027  // Need to check if the subnet belongs to a shared network. If so,
1028  // we might be able to find a better subnet for lease allocation,
1029  // for which it is more likely that there are some leases available.
1030  // If we stick to the selected subnet, we may end up walking over
1031  // the entire subnet (or more subnets) to discover that the pools
1032  // have been exhausted. Using a subnet from which a lease was
1033  // assigned most recently is an optimization which increases
1034  // the likelihood of starting from the subnet which pools are not
1035  // exhausted.
1036  SharedNetwork6Ptr network;
1037  original_subnet->getSharedNetwork(network);
1038  if (network) {
1039  // This would try to find a subnet with the same set of classes
1040  // as the current subnet, but with the more recent "usage timestamp".
1041  // This timestamp is only updated for the allocations made with an
1042  // allocator (unreserved lease allocations), not the static
1043  // allocations or requested addresses.
1044  original_subnet = network->getPreferredSubnet(original_subnet, ctx.currentIA().type_);
1045  }
1046 
1047  ctx.subnet_ = subnet = original_subnet;
1048 
1049  // The following counter tracks the number of subnets with matching client
1050  // classes from which the allocation engine attempted to assign leases.
1051  uint64_t subnets_with_unavail_leases = 0;
1052  // The following counter tracks the number of subnets in which there were
1053  // no matching pools for the client.
1054  uint64_t subnets_with_unavail_pools = 0;
1055 
1056  for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
1057 
1058  if (!subnet->clientSupported(ctx.query_->getClasses())) {
1059  continue;
1060  }
1061 
1062  // The hint was useless (it was not provided at all, was used by someone else,
1063  // was out of pool or reserved for someone else). Search the pool until first
1064  // of the following occurs:
1065  // - we find a free address
1066  // - we find an address for which the lease has expired
1067  // - we exhaust number of tries
1068  uint64_t possible_attempts =
1069  subnet->getPoolCapacity(ctx.currentIA().type_,
1070  ctx.query_->getClasses());
1071 
1072  // If the number of tries specified in the allocation engine constructor
1073  // is set to 0 (unlimited) or the pools capacity is lower than that number,
1074  // let's use the pools capacity as the maximum number of tries. Trying
1075  // more than the actual pools capacity is a waste of time. If the specified
1076  // number of tries is lower than the pools capacity, use that number.
1077  uint64_t max_attempts = ((attempts_ == 0) || (possible_attempts < attempts_)) ? possible_attempts : attempts_;
1078 
1079  if (max_attempts > 0) {
1080  // If max_attempts is greater than 0, there are some pools in this subnet
1081  // from which we can potentially get a lease.
1082  ++subnets_with_unavail_leases;
1083  } else {
1084  // If max_attempts is 0, it is an indication that there are no pools
1085  // in the subnet from which we can get a lease.
1086  ++subnets_with_unavail_pools;
1087  continue;
1088  }
1089 
1090  bool in_subnet = subnet->getReservationsInSubnet();
1091  bool out_of_pool = subnet->getReservationsOutOfPool();
1092 
1093  // Set the default status code in case the lease6_select callouts
1094  // do not exist and the callout handle has a status returned by
1095  // any of the callouts already invoked for this packet.
1096  if (ctx.callout_handle_) {
1097  ctx.callout_handle_->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
1098  }
1099 
1100  for (uint64_t i = 0; i < max_attempts; ++i) {
1101 
1102  ++total_attempts;
1103 
1104  IOAddress candidate = allocator->pickAddress(subnet,
1105  ctx.query_->getClasses(),
1106  ctx.duid_,
1107  hint);
1108  // The first step is to find out prefix length. It is 128 for
1109  // non-PD leases.
1110  uint8_t prefix_len = 128;
1111  if (ctx.currentIA().type_ == Lease::TYPE_PD) {
1112  pool = boost::dynamic_pointer_cast<Pool6>(
1113  subnet->getPool(ctx.currentIA().type_,
1114  ctx.query_->getClasses(),
1115  candidate));
1116  if (pool) {
1117  prefix_len = pool->getLength();
1118  }
1119  }
1120 
1121  // First check for reservation when it is the choice.
1122  if (check_reservation_first && in_subnet && !out_of_pool) {
1123  auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1124  if (!hosts.empty()) {
1125  // Don't allocate.
1126  continue;
1127  }
1128  }
1129 
1130  // Check if the resource is busy i.e. can be being allocated
1131  // by another thread to another client.
1132  ResourceHandler resource_handler;
1133  if (MultiThreadingMgr::instance().getMode() &&
1134  !resource_handler.tryLock(ctx.currentIA().type_, candidate)) {
1135  // Don't allocate.
1136  continue;
1137  }
1138 
1139  // Look for an existing lease for the candidate.
1140  Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1141  candidate);
1142 
1143  if (!existing) {
1147  if (!check_reservation_first && in_subnet && !out_of_pool) {
1148  auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1149  if (!hosts.empty()) {
1150  // Don't allocate.
1151  continue;
1152  }
1153  }
1154 
1155  // there's no existing lease for selected candidate, so it is
1156  // free. Let's allocate it.
1157 
1158  ctx.subnet_ = subnet;
1159  Lease6Ptr lease = createLease6(ctx, candidate, prefix_len, callout_status);
1160  if (lease) {
1161  // We are allocating a new lease (not renewing). So, the
1162  // old lease should be NULL.
1163  ctx.currentIA().old_leases_.clear();
1164 
1165  leases.push_back(lease);
1166  return (leases);
1167 
1168  } else if (ctx.callout_handle_ &&
1169  (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
1170  // Don't retry when the callout status is not continue.
1171  break;
1172  }
1173 
1174  // Although the address was free just microseconds ago, it may have
1175  // been taken just now. If the lease insertion fails, we continue
1176  // allocation attempts.
1177  } else if (existing->expired()) {
1178  // Make sure it's not reserved.
1179  if (!check_reservation_first && in_subnet && !out_of_pool) {
1180  auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1181  if (!hosts.empty()) {
1182  // Don't allocate.
1183  continue;
1184  }
1185  }
1186 
1187  // Copy an existing, expired lease so as it can be returned
1188  // to the caller.
1189  Lease6Ptr old_lease(new Lease6(*existing));
1190  ctx.currentIA().old_leases_.push_back(old_lease);
1191 
1192  ctx.subnet_ = subnet;
1193  existing = reuseExpiredLease(existing, ctx, prefix_len,
1194  callout_status);
1195 
1196  leases.push_back(existing);
1197  return (leases);
1198  }
1199  }
1200  }
1201 
1202  if (network) {
1203  // The client is in the shared network. Let's log the high level message
1204  // indicating which shared network the client belongs to.
1206  .arg(ctx.query_->getLabel())
1207  .arg(network->getName())
1208  .arg(subnets_with_unavail_leases)
1209  .arg(subnets_with_unavail_pools);
1210 
1211  } else {
1212  // The client is not connected to a shared network. It is connected
1213  // to a subnet. Let's log the ID of that subnet.
1215  .arg(ctx.query_->getLabel())
1216  .arg(ctx.subnet_->getID());
1217  }
1218  if (total_attempts == 0) {
1219  // In this case, it seems that none of the pools in the subnets could
1220  // be used for that client, both in case the client is connected to
1221  // a shared network or to a single subnet. Apparently, the client was
1222  // rejected to use the pools because of the client classes' mismatch.
1224  .arg(ctx.query_->getLabel());
1225  } else {
1226  // This is an old log message which provides a number of attempts
1227  // made by the allocation engine to allocate a lease. The only case
1228  // when we don't want to log this message is when the number of
1229  // attempts is zero (condition above), because it would look silly.
1231  .arg(ctx.query_->getLabel())
1232  .arg(total_attempts);
1233  }
1234 
1235  // We failed to allocate anything. Let's return empty collection.
1236  return (Lease6Collection());
1237 }
1238 
1239 void
1240 AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
1241  Lease6Collection& existing_leases) {
1242 
1243  // If there are no reservations or the reservation is v4, there's nothing to do.
1244  if (ctx.hosts_.empty()) {
1247  .arg(ctx.query_->getLabel());
1248  return;
1249  }
1250 
1251  // Let's convert this from Lease::Type to IPv6Reserv::Type
1252  IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1254 
1255  // We want to avoid allocating new lease for an IA if there is already
1256  // a valid lease for which client has reservation. So, we first check if
1257  // we already have a lease for a reserved address or prefix.
1258  BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
1259  if ((lease->valid_lft_ != 0)) {
1260  if ((ctx.hosts_.count(lease->subnet_id_) > 0) &&
1261  ctx.hosts_[lease->subnet_id_]->hasReservation(makeIPv6Resrv(*lease))) {
1262  // We found existing lease for a reserved address or prefix.
1263  // We'll simply extend the lifetime of the lease.
1266  .arg(ctx.query_->getLabel())
1267  .arg(lease->typeToText(lease->type_))
1268  .arg(lease->addr_.toText());
1269 
1270  // Besides IP reservations we're also going to return other reserved
1271  // parameters, such as hostname. We want to hand out the hostname value
1272  // from the same reservation entry as IP addresses. Thus, let's see if
1273  // there is any hostname reservation.
1274  if (!ctx.host_subnet_) {
1275  SharedNetwork6Ptr network;
1276  ctx.subnet_->getSharedNetwork(network);
1277  if (network) {
1278  // Remember the subnet that holds this preferred host
1279  // reservation. The server will use it to return appropriate
1280  // FQDN, classes etc.
1281  ctx.host_subnet_ = network->getSubnet(lease->subnet_id_);
1282  ConstHostPtr host = ctx.hosts_[lease->subnet_id_];
1283  // If there is a hostname reservation here we should stick
1284  // to this reservation. By updating the hostname in the
1285  // context we make sure that the database is updated with
1286  // this new value and the server doesn't need to do it and
1287  // its processing performance is not impacted by the hostname
1288  // updates.
1289  if (host && !host->getHostname().empty()) {
1290  // We have to determine whether the hostname is generated
1291  // in response to client's FQDN or not. If yes, we will
1292  // need to qualify the hostname. Otherwise, we just use
1293  // the hostname as it is specified for the reservation.
1294  OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1295  ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1296  qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1297  static_cast<bool>(fqdn));
1298  }
1299  }
1300  }
1301 
1302  // Got a lease for a reservation in this IA.
1303  return;
1304  }
1305  }
1306  }
1307 
1308  // There is no lease for a reservation in this IA. So, let's now iterate
1309  // over reservations specified and try to allocate one of them for the IA.
1310 
1311  for (Subnet6Ptr subnet = ctx.subnet_; subnet;
1312  subnet = subnet->getNextSubnet(ctx.subnet_)) {
1313 
1314  SubnetID subnet_id = subnet->getID();
1315 
1316  // No hosts for this subnet or the subnet not supported.
1317  if (!subnet->clientSupported(ctx.query_->getClasses()) ||
1318  ctx.hosts_.count(subnet_id) == 0) {
1319  continue;
1320  }
1321 
1322  ConstHostPtr host = ctx.hosts_[subnet_id];
1323 
1324  bool in_subnet = subnet->getReservationsInSubnet();
1325 
1326  // Get the IPv6 reservations of specified type.
1327  const IPv6ResrvRange& reservs = host->getIPv6Reservations(type);
1328  BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
1329  // We do have a reservation for address or prefix.
1330  const IOAddress& addr = type_lease_tuple.second.getPrefix();
1331  uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1332 
1333  // We have allocated this address/prefix while processing one of the
1334  // previous IAs, so let's try another reservation.
1335  if (ctx.isAllocated(addr, prefix_len)) {
1336  continue;
1337  }
1338 
1339  // The out-of-pool flag indicates that no client should be assigned
1340  // reserved addresses from within the dynamic pool, and for that
1341  // reason look only for reservations that are outside the pools,
1342  // hence the inPool check.
1343  if (!in_subnet ||
1344  (subnet->getReservationsOutOfPool() &&
1345  subnet->inPool(ctx.currentIA().type_, addr))) {
1346  continue;
1347  }
1348 
1349  // If there's a lease for this address, let's not create it.
1350  // It doesn't matter whether it is for this client or for someone else.
1351  if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
1352  addr)) {
1353 
1354  // Let's remember the subnet from which the reserved address has been
1355  // allocated. We'll use this subnet for allocating other reserved
1356  // resources.
1357  ctx.subnet_ = subnet;
1358 
1359  if (!ctx.host_subnet_) {
1360  ctx.host_subnet_ = subnet;
1361  if (!host->getHostname().empty()) {
1362  // If there is a hostname reservation here we should stick
1363  // to this reservation. By updating the hostname in the
1364  // context we make sure that the database is updated with
1365  // this new value and the server doesn't need to do it and
1366  // its processing performance is not impacted by the hostname
1367  // updates.
1368 
1369  // We have to determine whether the hostname is generated
1370  // in response to client's FQDN or not. If yes, we will
1371  // need to qualify the hostname. Otherwise, we just use
1372  // the hostname as it is specified for the reservation.
1373  OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1374  ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1375  qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1376  static_cast<bool>(fqdn));
1377  }
1378  }
1379 
1380  // Ok, let's create a new lease...
1381  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
1382  Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1383 
1384  // ... and add it to the existing leases list.
1385  existing_leases.push_back(lease);
1386 
1387  if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1389  .arg(addr.toText())
1390  .arg(ctx.query_->getLabel());
1391  } else {
1393  .arg(addr.toText())
1394  .arg(static_cast<int>(prefix_len))
1395  .arg(ctx.query_->getLabel());
1396  }
1397 
1398  // We found a lease for this client and this IA. Let's return.
1399  // Returning after the first lease was assigned is useful if we
1400  // have multiple reservations for the same client. If the client
1401  // sends 2 IAs, the first time we call allocateReservedLeases6 will
1402  // use the first reservation and return. The second time, we'll
1403  // go over the first reservation, but will discover that there's
1404  // a lease corresponding to it and will skip it and then pick
1405  // the second reservation and turn it into the lease. This approach
1406  // would work for any number of reservations.
1407  return;
1408  }
1409  }
1410  }
1411 
1412  // Found no subnet reservations so now try the global reservation.
1413  allocateGlobalReservedLeases6(ctx, existing_leases);
1414 }
1415 
1416 void
1417 AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
1418  Lease6Collection& existing_leases) {
1419  // Get the global host
1420  ConstHostPtr ghost = ctx.globalHost();
1421  if (!ghost) {
1422  return;
1423  }
1424 
1425  // We want to avoid allocating a new lease for an IA if there is already
1426  // a valid lease for which client has reservation. So, we first check if
1427  // we already have a lease for a reserved address or prefix.
1428  BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
1429  if ((lease->valid_lft_ != 0) &&
1430  (ghost->hasReservation(makeIPv6Resrv(*lease)))) {
1431  // We found existing lease for a reserved address or prefix.
1432  // We'll simply extend the lifetime of the lease.
1435  .arg(ctx.query_->getLabel())
1436  .arg(lease->typeToText(lease->type_))
1437  .arg(lease->addr_.toText());
1438 
1439  // Besides IP reservations we're also going to return other reserved
1440  // parameters, such as hostname. We want to hand out the hostname value
1441  // from the same reservation entry as IP addresses. Thus, let's see if
1442  // there is any hostname reservation.
1443  if (!ghost->getHostname().empty()) {
1444  // We have to determine whether the hostname is generated
1445  // in response to client's FQDN or not. If yes, we will
1446  // need to qualify the hostname. Otherwise, we just use
1447  // the hostname as it is specified for the reservation.
1448  OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1449  ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1450  qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1451  static_cast<bool>(fqdn));
1452  }
1453 
1454  // Got a lease for a reservation in this IA.
1455  return;
1456  }
1457  }
1458 
1459  // There is no lease for a reservation in this IA. So, let's now iterate
1460  // over reservations specified and try to allocate one of them for the IA.
1461 
1462  // Let's convert this from Lease::Type to IPv6Reserv::Type
1463  IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1465 
1466  const IPv6ResrvRange& reservs = ghost->getIPv6Reservations(type);
1467  BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
1468  // We do have a reservation for address or prefix.
1469  const IOAddress& addr = type_lease_tuple.second.getPrefix();
1470  uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1471 
1472  // We have allocated this address/prefix while processing one of the
1473  // previous IAs, so let's try another reservation.
1474  if (ctx.isAllocated(addr, prefix_len)) {
1475  continue;
1476  }
1477 
1478  // If there's a lease for this address, let's not create it.
1479  // It doesn't matter whether it is for this client or for someone else.
1480  if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
1481 
1482  if (!ghost->getHostname().empty()) {
1483  // If there is a hostname reservation here we should stick
1484  // to this reservation. By updating the hostname in the
1485  // context we make sure that the database is updated with
1486  // this new value and the server doesn't need to do it and
1487  // its processing performance is not impacted by the hostname
1488  // updates.
1489 
1490  // We have to determine whether the hostname is generated
1491  // in response to client's FQDN or not. If yes, we will
1492  // need to qualify the hostname. Otherwise, we just use
1493  // the hostname as it is specified for the reservation.
1494  OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1495  ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
1496  qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1497  static_cast<bool>(fqdn));
1498  }
1499 
1500  // Ok, let's create a new lease...
1501  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
1502  Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1503 
1504  // ... and add it to the existing leases list.
1505  existing_leases.push_back(lease);
1506 
1507  if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1509  .arg(addr.toText())
1510  .arg(ctx.query_->getLabel());
1511  } else {
1513  .arg(addr.toText())
1514  .arg(static_cast<int>(prefix_len))
1515  .arg(ctx.query_->getLabel());
1516  }
1517 
1518  // We found a lease for this client and this IA. Let's return.
1519  // Returning after the first lease was assigned is useful if we
1520  // have multiple reservations for the same client. If the client
1521  // sends 2 IAs, the first time we call allocateReservedLeases6 will
1522  // use the first reservation and return. The second time, we'll
1523  // go over the first reservation, but will discover that there's
1524  // a lease corresponding to it and will skip it and then pick
1525  // the second reservation and turn it into the lease. This approach
1526  // would work for any number of reservations.
1527  return;
1528  }
1529  }
1530 }
1531 
1532 void
1533 AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
1534  Lease6Collection& existing_leases) {
1535  // If there are no leases (so nothing to remove) just return.
1536  if (existing_leases.empty() || !ctx.subnet_) {
1537  return;
1538  }
1539  // If host reservation is disabled (so there are no reserved leases)
1540  // use the simplified version.
1541  if (!ctx.subnet_->getReservationsInSubnet() &&
1542  !ctx.subnet_->getReservationsGlobal()) {
1543  removeNonmatchingReservedNoHostLeases6(ctx, existing_leases);
1544  return;
1545  }
1546 
1547  // We need a copy, so we won't be iterating over a container and
1548  // removing from it at the same time. It's only a copy of pointers,
1549  // so the operation shouldn't be that expensive.
1550  Lease6Collection copy = existing_leases;
1551 
1552  BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
1553  // If we have reservation we should check if the reservation is for
1554  // the candidate lease. If so, we simply accept the lease.
1555  IPv6Resrv resv = makeIPv6Resrv(*candidate);
1556  if ((ctx.hasGlobalReservation(resv)) ||
1557  ((ctx.hosts_.count(candidate->subnet_id_) > 0) &&
1558  (ctx.hosts_[candidate->subnet_id_]->hasReservation(resv)))) {
1559  // We have a subnet reservation
1560  continue;
1561  }
1562 
1563  // The candidate address doesn't appear to be reserved for us.
1564  // We have to make a bit more expensive operation here to retrieve
1565  // the reservation for the candidate lease and see if it is
1566  // reserved for someone else.
1567  auto hosts = getIPv6Resrv(ctx.subnet_->getID(), candidate->addr_);
1568  // If lease is not reserved to someone else, it means that it can
1569  // be allocated to us from a dynamic pool, but we must check if
1570  // this lease belongs to any pool. If it does, we can proceed to
1571  // checking the next lease.
1572  if (hosts.empty() && inAllowedPool(ctx, candidate->type_,
1573  candidate->addr_, false)) {
1574  continue;
1575  }
1576 
1577  if (!hosts.empty()) {
1578  // Ok, we have a problem. This host has a lease that is reserved
1579  // for someone else. We need to recover from this.
1580  if (hosts.size() == 1) {
1581  if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1583  .arg(candidate->addr_.toText())
1584  .arg(ctx.duid_->toText())
1585  .arg(hosts.front()->getIdentifierAsText());
1586  } else {
1588  .arg(candidate->addr_.toText())
1589  .arg(static_cast<int>(candidate->prefixlen_))
1590  .arg(ctx.duid_->toText())
1591  .arg(hosts.front()->getIdentifierAsText());
1592  }
1593  } else {
1594  if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1596  .arg(candidate->addr_.toText())
1597  .arg(ctx.duid_->toText())
1598  .arg(hosts.size());
1599  } else {
1601  .arg(candidate->addr_.toText())
1602  .arg(static_cast<int>(candidate->prefixlen_))
1603  .arg(ctx.duid_->toText())
1604  .arg(hosts.size());
1605  }
1606  }
1607  }
1608 
1609  // Remove this lease from LeaseMgr as it is reserved to someone
1610  // else or doesn't belong to a pool.
1611  if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1612  // Concurrent delete performed by other instance which should
1613  // properly handle dns and stats updates.
1614  continue;
1615  }
1616 
1617  // Update DNS if needed.
1618  queueNCR(CHG_REMOVE, candidate);
1619 
1620  // Need to decrease statistic for assigned addresses.
1621  StatsMgr::instance().addValue(
1622  StatsMgr::generateName("subnet", candidate->subnet_id_,
1623  ctx.currentIA().type_ == Lease::TYPE_NA ?
1624  "assigned-nas" : "assigned-pds"),
1625  static_cast<int64_t>(-1));
1626 
1627  // In principle, we could trigger a hook here, but we will do this
1628  // only if we get serious complaints from actual users. We want the
1629  // conflict resolution procedure to really work and user libraries
1630  // should not interfere with it.
1631 
1632  // Add this to the list of removed leases.
1633  ctx.currentIA().old_leases_.push_back(candidate);
1634 
1635  // Let's remove this candidate from existing leases
1636  removeLeases(existing_leases, candidate->addr_);
1637  }
1638 }
1639 
1640 void
1641 AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
1642  Lease6Collection& existing_leases) {
1643  // We need a copy, so we won't be iterating over a container and
1644  // removing from it at the same time. It's only a copy of pointers,
1645  // so the operation shouldn't be that expensive.
1646  Lease6Collection copy = existing_leases;
1647 
1648  BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
1649  // Lease can be allocated to us from a dynamic pool, but we must
1650  // check if this lease belongs to any allowed pool. If it does,
1651  // we can proceed to checking the next lease.
1652  if (inAllowedPool(ctx, candidate->type_,
1653  candidate->addr_, false)) {
1654  continue;
1655  }
1656 
1657  // Remove this lease from LeaseMgr as it doesn't belong to a pool.
1658  if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1659  // Concurrent delete performed by other instance which should
1660  // properly handle dns and stats updates.
1661  continue;
1662  }
1663 
1664  // Update DNS if needed.
1665  queueNCR(CHG_REMOVE, candidate);
1666 
1667  // Need to decrease statistic for assigned addresses.
1668  StatsMgr::instance().addValue(
1669  StatsMgr::generateName("subnet", candidate->subnet_id_,
1670  ctx.currentIA().type_ == Lease::TYPE_NA ?
1671  "assigned-nas" : "assigned-pds"),
1672  static_cast<int64_t>(-1));
1673 
1674  // Add this to the list of removed leases.
1675  ctx.currentIA().old_leases_.push_back(candidate);
1676 
1677  // Let's remove this candidate from existing leases
1678  removeLeases(existing_leases, candidate->addr_);
1679  }
1680 }
1681 
1682 bool
1683 AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
1684 
1685  bool removed = false;
1686  for (Lease6Collection::iterator lease = container.begin();
1687  lease != container.end(); ++lease) {
1688  if ((*lease)->addr_ == addr) {
1689  lease->reset();
1690  removed = true;
1691  }
1692  }
1693 
1694  // Remove all elements that have NULL value
1695  container.erase(std::remove(container.begin(), container.end(), Lease6Ptr()),
1696  container.end());
1697 
1698  return (removed);
1699 }
1700 
1701 void
1702 AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
1703  Lease6Collection& existing_leases) {
1704  // This method removes leases that are not reserved for this host.
1705  // It will keep at least one lease, though, as a fallback.
1706  int total = existing_leases.size();
1707  if (total <= 1) {
1708  return;
1709  }
1710 
1711  // This is officially not scary code anymore. iterates and marks specified
1712  // leases for deletion, by setting appropriate pointers to NULL.
1713  for (Lease6Collection::iterator lease = existing_leases.begin();
1714  lease != existing_leases.end(); ++lease) {
1715 
1716  // If there is reservation for this keep it.
1717  IPv6Resrv resv = makeIPv6Resrv(*(*lease));
1718  if (ctx.hasGlobalReservation(resv) ||
1719  ((ctx.hosts_.count((*lease)->subnet_id_) > 0) &&
1720  (ctx.hosts_[(*lease)->subnet_id_]->hasReservation(resv)))) {
1721  continue;
1722  }
1723 
1724  // @todo - If this is for a fake_allocation, we should probably
1725  // not be deleting the lease or removing DNS entries. We should
1726  // simply remove it from the list.
1727  // We have reservations, but not for this lease. Release it.
1728  // Remove this lease from LeaseMgr
1729  if (!LeaseMgrFactory::instance().deleteLease(*lease)) {
1730  // Concurrent delete performed by other instance which should
1731  // properly handle dns and stats updates.
1732  continue;
1733  }
1734 
1735  // Update DNS if required.
1736  queueNCR(CHG_REMOVE, *lease);
1737 
1738  // Need to decrease statistic for assigned addresses.
1739  StatsMgr::instance().addValue(
1740  StatsMgr::generateName("subnet", (*lease)->subnet_id_,
1741  ctx.currentIA().type_ == Lease::TYPE_NA ?
1742  "assigned-nas" : "assigned-pds"),
1743  static_cast<int64_t>(-1));
1744 
1746 
1747  // Add this to the list of removed leases.
1748  ctx.currentIA().old_leases_.push_back(*lease);
1749 
1750  // Set this pointer to NULL. The pointer is still valid. We're just
1751  // setting the Lease6Ptr to NULL value. We'll remove all NULL
1752  // pointers once the loop is finished.
1753  lease->reset();
1754 
1755  if (--total == 1) {
1756  // If there's only one lease left, break the loop.
1757  break;
1758  }
1759 
1760  }
1761 
1762  // Remove all elements that we previously marked for deletion (those that
1763  // have NULL value).
1764  existing_leases.erase(std::remove(existing_leases.begin(),
1765  existing_leases.end(), Lease6Ptr()), existing_leases.end());
1766 }
1767 
1768 Lease6Ptr
1769 AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
1770  uint8_t prefix_len,
1771  CalloutHandle::CalloutNextStep& callout_status) {
1772 
1773  if (!expired->expired()) {
1774  isc_throw(BadValue, "Attempt to recycle lease that is still valid");
1775  }
1776 
1777  if (expired->type_ != Lease::TYPE_PD) {
1778  prefix_len = 128; // non-PD lease types must be always /128
1779  }
1780 
1781  if (!ctx.fake_allocation_) {
1782  // The expired lease needs to be reclaimed before it can be reused.
1783  // This includes declined leases for which probation period has
1784  // elapsed.
1785  reclaimExpiredLease(expired, ctx.callout_handle_);
1786  }
1787 
1788  // address, lease type and prefixlen (0) stay the same
1789  expired->iaid_ = ctx.currentIA().iaid_;
1790  expired->duid_ = ctx.duid_;
1791  // Use subnet's preferred triplet to conditionally determine
1792  // preferred lifetime based on hint
1793  if (!ctx.currentIA().hints_.empty() &&
1794  ctx.currentIA().hints_[0].getPreferred()) {
1795  uint32_t preferred = ctx.currentIA().hints_[0].getPreferred();
1796  expired->preferred_lft_ = ctx.subnet_->getPreferred().get(preferred);
1797  } else {
1798  expired->preferred_lft_ = ctx.subnet_->getPreferred();
1799  }
1800  // Use subnet's valid triplet to conditionally determine
1801  // valid lifetime based on hint
1802  expired->reuseable_valid_lft_ = 0;
1803  if (!ctx.currentIA().hints_.empty() &&
1804  ctx.currentIA().hints_[0].getValid()) {
1805  uint32_t valid = ctx.currentIA().hints_[0].getValid();
1806  expired->valid_lft_ = ctx.subnet_->getValid().get(valid);
1807  } else {
1808  expired->valid_lft_ = ctx.subnet_->getValid();
1809  }
1810  expired->cltt_ = time(NULL);
1811  expired->subnet_id_ = ctx.subnet_->getID();
1812  expired->hostname_ = ctx.hostname_;
1813  expired->fqdn_fwd_ = ctx.fwd_dns_update_;
1814  expired->fqdn_rev_ = ctx.rev_dns_update_;
1815  expired->prefixlen_ = prefix_len;
1816  expired->state_ = Lease::STATE_DEFAULT;
1817 
1820  .arg(ctx.query_->getLabel())
1821  .arg(expired->toText());
1822 
1823  // Let's execute all callouts registered for lease6_select
1824  if (ctx.callout_handle_ &&
1825  HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1826 
1827  // Use the RAII wrapper to make sure that the callout handle state is
1828  // reset when this object goes out of scope. All hook points must do
1829  // it to prevent possible circular dependency between the callout
1830  // handle and its arguments.
1831  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1832 
1833  // Enable copying options from the packet within hook library.
1834  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
1835 
1836  // Pass necessary arguments
1837 
1838  // Pass the original packet
1839  ctx.callout_handle_->setArgument("query6", ctx.query_);
1840 
1841  // Subnet from which we do the allocation
1842  ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
1843 
1844  // Is this solicit (fake = true) or request (fake = false)
1845  ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
1846 
1847  // The lease that will be assigned to a client
1848  ctx.callout_handle_->setArgument("lease6", expired);
1849 
1850  // Call the callouts
1851  HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
1852 
1853  callout_status = ctx.callout_handle_->getStatus();
1854 
1855  // Callouts decided to skip the action. This means that the lease is not
1856  // assigned, so the client will get NoAddrAvail as a result. The lease
1857  // won't be inserted into the database.
1858  if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
1860  return (Lease6Ptr());
1861  }
1862 
1867 
1868  // Let's use whatever callout returned. Hopefully it is the same lease
1869  // we handed to it.
1870  ctx.callout_handle_->getArgument("lease6", expired);
1871  }
1872 
1873  if (!ctx.fake_allocation_) {
1874  // Add(update) the extended information on the lease.
1875  static_cast<void>(updateLease6ExtendedInfo(expired, ctx));
1876 
1877  // for REQUEST we do update the lease
1879 
1880  // If the lease is in the current subnet we need to account
1881  // for the re-assignment of The lease.
1882  if (ctx.subnet_->inPool(ctx.currentIA().type_, expired->addr_)) {
1883  StatsMgr::instance().addValue(
1884  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1885  ctx.currentIA().type_ == Lease::TYPE_NA ?
1886  "assigned-nas" : "assigned-pds"),
1887  static_cast<int64_t>(1));
1888  StatsMgr::instance().addValue(
1889  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1890  ctx.currentIA().type_ == Lease::TYPE_NA ?
1891  "cumulative-assigned-nas" :
1892  "cumulative-assigned-pds"),
1893  static_cast<int64_t>(1));
1894  StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1895  "cumulative-assigned-nas" :
1896  "cumulative-assigned-pds",
1897  static_cast<int64_t>(1));
1898  }
1899  }
1900 
1901  // We do nothing for SOLICIT. We'll just update database when
1902  // the client gets back to us with REQUEST message.
1903 
1904  // it's not really expired at this stage anymore - let's return it as
1905  // an updated lease
1906  return (expired);
1907 }
1908 
1909 Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
1910  const IOAddress& addr,
1911  uint8_t prefix_len,
1912  CalloutHandle::CalloutNextStep& callout_status) {
1913 
1914  if (ctx.currentIA().type_ != Lease::TYPE_PD) {
1915  prefix_len = 128; // non-PD lease types must be always /128
1916  }
1917 
1918  uint32_t preferred = ctx.subnet_->getPreferred();
1919  if (!ctx.currentIA().hints_.empty() &&
1920  ctx.currentIA().hints_[0].getPreferred()) {
1921  preferred = ctx.currentIA().hints_[0].getPreferred();
1922  preferred = ctx.subnet_->getPreferred().get(preferred);
1923  }
1924  uint32_t valid = ctx.subnet_->getValid();
1925  if (!ctx.currentIA().hints_.empty() &&
1926  ctx.currentIA().hints_[0].getValid()) {
1927  valid = ctx.currentIA().hints_[0].getValid();
1928  valid = ctx.subnet_->getValid().get(valid);
1929  }
1930  Lease6Ptr lease(new Lease6(ctx.currentIA().type_, addr, ctx.duid_,
1931  ctx.currentIA().iaid_, preferred,
1932  valid, ctx.subnet_->getID(),
1933  ctx.hwaddr_, prefix_len));
1934 
1935  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
1936  lease->fqdn_rev_ = ctx.rev_dns_update_;
1937  lease->hostname_ = ctx.hostname_;
1938 
1939  // Let's execute all callouts registered for lease6_select
1940  if (ctx.callout_handle_ &&
1941  HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1942 
1943  // Use the RAII wrapper to make sure that the callout handle state is
1944  // reset when this object goes out of scope. All hook points must do
1945  // it to prevent possible circular dependency between the callout
1946  // handle and its arguments.
1947  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1948 
1949  // Enable copying options from the packet within hook library.
1950  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
1951 
1952  // Pass necessary arguments
1953 
1954  // Pass the original packet
1955  ctx.callout_handle_->setArgument("query6", ctx.query_);
1956 
1957  // Subnet from which we do the allocation
1958  ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
1959 
1960  // Is this solicit (fake = true) or request (fake = false)
1961  ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
1962 
1963  // The lease that will be assigned to a client
1964  ctx.callout_handle_->setArgument("lease6", lease);
1965 
1966  // This is the first callout, so no need to clear any arguments
1967  HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
1968 
1969  callout_status = ctx.callout_handle_->getStatus();
1970 
1971  // Callouts decided to skip the action. This means that the lease is not
1972  // assigned, so the client will get NoAddrAvail as a result. The lease
1973  // won't be inserted into the database.
1974  if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
1976  return (Lease6Ptr());
1977  }
1978 
1979  // Let's use whatever callout returned. Hopefully it is the same lease
1980  // we handed to it.
1981  ctx.callout_handle_->getArgument("lease6", lease);
1982  }
1983 
1984  if (!ctx.fake_allocation_) {
1985  // Add(update) the extended information on the lease.
1986  static_cast<void>(updateLease6ExtendedInfo(lease, ctx));
1987 
1988  // That is a real (REQUEST) allocation
1989  bool status = LeaseMgrFactory::instance().addLease(lease);
1990 
1991  if (status) {
1992  // The lease insertion succeeded - if the lease is in the
1993  // current subnet lets bump up the statistic.
1994  if (ctx.subnet_->inPool(ctx.currentIA().type_, addr)) {
1995  StatsMgr::instance().addValue(
1996  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1997  ctx.currentIA().type_ == Lease::TYPE_NA ?
1998  "assigned-nas" : "assigned-pds"),
1999  static_cast<int64_t>(1));
2000  StatsMgr::instance().addValue(
2001  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2002  ctx.currentIA().type_ == Lease::TYPE_NA ?
2003  "cumulative-assigned-nas" :
2004  "cumulative-assigned-pds"),
2005  static_cast<int64_t>(1));
2006  StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2007  "cumulative-assigned-nas" :
2008  "cumulative-assigned-pds",
2009  static_cast<int64_t>(1));
2010  }
2011 
2012  // Record it so it won't be updated twice.
2013  ctx.currentIA().addNewResource(addr, prefix_len);
2014 
2015  return (lease);
2016  } else {
2017  // One of many failures with LeaseMgr (e.g. lost connection to the
2018  // database, database failed etc.). One notable case for that
2019  // is that we are working in multi-process mode and we lost a race
2020  // (some other process got that address first)
2021  return (Lease6Ptr());
2022  }
2023  } else {
2024  // That is only fake (SOLICIT without rapid-commit) allocation
2025 
2026  // It is for advertise only. We should not insert the lease and callers
2027  // have already verified the lease does not exist in the database.
2028  return (lease);
2029  }
2030 }
2031 
2034  try {
2035  if (!ctx.subnet_) {
2036  isc_throw(InvalidOperation, "Subnet is required for allocation");
2037  }
2038 
2039  if (!ctx.duid_) {
2040  isc_throw(InvalidOperation, "DUID is mandatory for allocation");
2041  }
2042 
2043  // Check if there are any leases for this client.
2044  Subnet6Ptr subnet = ctx.subnet_;
2045  Lease6Collection leases;
2046  while (subnet) {
2047  Lease6Collection leases_subnet =
2049  *ctx.duid_,
2050  ctx.currentIA().iaid_,
2051  subnet->getID());
2052  leases.insert(leases.end(), leases_subnet.begin(), leases_subnet.end());
2053 
2054  subnet = subnet->getNextSubnet(ctx.subnet_);
2055  }
2056 
2057  if (!leases.empty()) {
2060  .arg(ctx.query_->getLabel());
2061 
2062  // Check if the existing leases are reserved for someone else.
2063  // If they're not, we're ok to keep using them.
2064  removeNonmatchingReservedLeases6(ctx, leases);
2065  }
2066 
2067  if (!ctx.hosts_.empty()) {
2068 
2071  .arg(ctx.query_->getLabel());
2072 
2073  // If we have host reservation, allocate those leases.
2074  allocateReservedLeases6(ctx, leases);
2075 
2076  // There's one more check to do. Let's remove leases that are not
2077  // matching reservations, i.e. if client X has address A, but there's
2078  // a reservation for address B, we should release A and reassign B.
2079  // Caveat: do this only if we have at least one reserved address.
2080  removeNonreservedLeases6(ctx, leases);
2081  }
2082 
2083  // If we happen to removed all leases, get something new for this guy.
2084  // Depending on the configuration, we may enable or disable granting
2085  // new leases during renewals. This is controlled with the
2086  // allow_new_leases_in_renewals_ field.
2087  if (leases.empty()) {
2088 
2091  .arg(ctx.query_->getLabel());
2092 
2093  leases = allocateUnreservedLeases6(ctx);
2094  }
2095 
2096  // Extend all existing leases that passed all checks.
2097  for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
2098  if (ctx.currentIA().isNewResource((*l)->addr_,
2099  (*l)->prefixlen_)) {
2100  // This lease was just created so is already extended.
2101  continue;
2102  }
2105  .arg(ctx.query_->getLabel())
2106  .arg((*l)->typeToText((*l)->type_))
2107  .arg((*l)->addr_);
2108  extendLease6(ctx, *l);
2109  }
2110 
2111  if (!leases.empty()) {
2112  // If there are any leases allocated, let's store in them in the
2113  // IA context so as they are available when we process subsequent
2114  // IAs.
2115  BOOST_FOREACH(Lease6Ptr lease, leases) {
2116  ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
2117  ctx.new_leases_.push_back(lease);
2118  }
2119  }
2120 
2121  return (leases);
2122 
2123  } catch (const isc::Exception& e) {
2124 
2125  // Some other error, return an empty lease.
2127  .arg(ctx.query_->getLabel())
2128  .arg(e.what());
2129  }
2130 
2131  return (Lease6Collection());
2132 }
2133 
2134 void
2135 AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
2136 
2137  if (!lease || !ctx.subnet_) {
2138  return;
2139  }
2140 
2141  // It is likely that the lease for which we're extending the lifetime doesn't
2142  // belong to the current but a sibling subnet.
2143  if (ctx.subnet_->getID() != lease->subnet_id_) {
2144  SharedNetwork6Ptr network;
2145  ctx.subnet_->getSharedNetwork(network);
2146  if (network) {
2147  Subnet6Ptr subnet = network->getSubnet(SubnetID(lease->subnet_id_));
2148  // Found the actual subnet this lease belongs to. Stick to this
2149  // subnet.
2150  if (subnet) {
2151  ctx.subnet_ = subnet;
2152  }
2153  }
2154  }
2155 
2156  // If the lease is not global and it is either out of range (NAs only) or it
2157  // is not permitted by subnet client classification, delete it.
2158  if (!(ctx.hasGlobalReservation(makeIPv6Resrv(*lease))) &&
2159  (((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
2160  !ctx.subnet_->clientSupported(ctx.query_->getClasses()))) {
2161  // Oh dear, the lease is no longer valid. We need to get rid of it.
2162 
2163  // Remove this lease from LeaseMgr
2164  if (!LeaseMgrFactory::instance().deleteLease(lease)) {
2165  // Concurrent delete performed by other instance which should
2166  // properly handle dns and stats updates.
2167  return;
2168  }
2169 
2170  // Updated DNS if required.
2171  queueNCR(CHG_REMOVE, lease);
2172 
2173  // Need to decrease statistic for assigned addresses.
2174  StatsMgr::instance().addValue(
2175  StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-nas"),
2176  static_cast<int64_t>(-1));
2177 
2178  // Add it to the removed leases list.
2179  ctx.currentIA().old_leases_.push_back(lease);
2180 
2181  return;
2182  }
2183 
2186  .arg(ctx.query_->getLabel())
2187  .arg(lease->toText());
2188 
2189  // Keep the old data in case the callout tells us to skip update.
2190  Lease6Ptr old_data(new Lease6(*lease));
2191 
2192  bool changed = false;
2193  uint32_t current_preferred_lft = lease->preferred_lft_;
2194  if (!ctx.currentIA().hints_.empty() &&
2195  ctx.currentIA().hints_[0].getPreferred()) {
2196  uint32_t preferred = ctx.currentIA().hints_[0].getPreferred();
2197  lease->preferred_lft_ = ctx.subnet_->getPreferred().get(preferred);
2198  } else {
2199  lease->preferred_lft_ = ctx.subnet_->getPreferred();
2200  }
2201  if (lease->preferred_lft_ < current_preferred_lft) {
2202  changed = true;
2203  }
2204  lease->reuseable_valid_lft_ = 0;
2205  if (!ctx.currentIA().hints_.empty() &&
2206  ctx.currentIA().hints_[0].getValid()) {
2207  uint32_t valid = ctx.currentIA().hints_[0].getValid();
2208  lease->valid_lft_ = ctx.subnet_->getValid().get(valid);
2209  } else {
2210  lease->valid_lft_ = ctx.subnet_->getValid();
2211  }
2212  if (lease->valid_lft_ < lease->current_valid_lft_) {
2213  changed = true;
2214  }
2215 
2216  lease->cltt_ = time(NULL);
2217  if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2218  (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
2219  (lease->hostname_ != ctx.hostname_)) {
2220  changed = true;
2221  lease->hostname_ = ctx.hostname_;
2222  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2223  lease->fqdn_rev_ = ctx.rev_dns_update_;
2224  }
2225  if ((!ctx.hwaddr_ && lease->hwaddr_) ||
2226  (ctx.hwaddr_ &&
2227  (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
2228  changed = true;
2229  lease->hwaddr_ = ctx.hwaddr_;
2230  }
2231  if (lease->state_ != Lease::STATE_DEFAULT) {
2232  changed = true;
2233  lease->state_ = Lease::STATE_DEFAULT;
2234  }
2237  .arg(ctx.query_->getLabel())
2238  .arg(lease->toText());
2239 
2240  bool skip = false;
2241  // Get the callouts specific for the processed message and execute them.
2242  int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
2243  Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
2244  if (HooksManager::calloutsPresent(hook_point)) {
2245  CalloutHandlePtr callout_handle = ctx.callout_handle_;
2246 
2247  // Use the RAII wrapper to make sure that the callout handle state is
2248  // reset when this object goes out of scope. All hook points must do
2249  // it to prevent possible circular dependency between the callout
2250  // handle and its arguments.
2251  ScopedCalloutHandleState callout_handle_state(callout_handle);
2252 
2253  // Enable copying options from the packet within hook library.
2254  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2255 
2256  // Pass the original packet
2257  callout_handle->setArgument("query6", ctx.query_);
2258 
2259  // Pass the lease to be updated
2260  callout_handle->setArgument("lease6", lease);
2261 
2262  // Pass the IA option to be sent in response
2263  if (lease->type_ == Lease::TYPE_NA) {
2264  callout_handle->setArgument("ia_na", ctx.currentIA().ia_rsp_);
2265  } else {
2266  callout_handle->setArgument("ia_pd", ctx.currentIA().ia_rsp_);
2267  }
2268 
2269  // Call all installed callouts
2270  HooksManager::callCallouts(hook_point, *callout_handle);
2271 
2272  // Callouts decided to skip the next processing step. The next
2273  // processing step would actually renew the lease, so skip at this
2274  // stage means "keep the old lease as it is".
2275  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2276  skip = true;
2279  .arg(ctx.query_->getName());
2280  }
2281 
2283  }
2284 
2285  if (!skip) {
2286  bool update_stats = false;
2287 
2288  // If the lease we're renewing has expired, we need to reclaim this
2289  // lease before we can renew it.
2290  if (old_data->expired()) {
2291  reclaimExpiredLease(old_data, ctx.callout_handle_);
2292 
2293  // If the lease is in the current subnet we need to account
2294  // for the re-assignment of the lease.
2295  if (ctx.subnet_->inPool(ctx.currentIA().type_, old_data->addr_)) {
2296  update_stats = true;
2297  }
2298  changed = true;
2299  }
2300 
2301  // @todo should we call storeLease6ExtendedInfo() here ?
2302  if (updateLease6ExtendedInfo(lease, ctx)) {
2303  changed = true;
2304  }
2305 
2306  // Try to reuse the lease.
2307  if (!changed) {
2308  setLeaseReusable(lease, current_preferred_lft, ctx);
2309  }
2310 
2311 
2312  // Now that the lease has been reclaimed, we can go ahead and update it
2313  // in the lease database.
2314  if (lease->reuseable_valid_lft_ == 0) {
2316  }
2317 
2318  if (update_stats) {
2319  StatsMgr::instance().addValue(
2320  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2321  ctx.currentIA().type_ == Lease::TYPE_NA ?
2322  "assigned-nas" : "assigned-pds"),
2323  static_cast<int64_t>(1));
2324  StatsMgr::instance().addValue(
2325  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2326  ctx.currentIA().type_ == Lease::TYPE_NA ?
2327  "cumulative-assigned-nas" :
2328  "cumulative-assigned-pds"),
2329  static_cast<int64_t>(1));
2330  StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2331  "cumulative-assigned-nas" :
2332  "cumulative-assigned-pds",
2333  static_cast<int64_t>(1));
2334  }
2335 
2336  } else {
2337  // Copy back the original date to the lease. For MySQL it doesn't make
2338  // much sense, but for memfile, the Lease6Ptr points to the actual lease
2339  // in memfile, so the actual update is performed when we manipulate
2340  // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
2341  *lease = *old_data;
2342  }
2343 
2344  // Add the old lease to the changed lease list. This allows the server
2345  // to make decisions regarding DNS updates.
2346  ctx.currentIA().changed_leases_.push_back(old_data);
2347 }
2348 
2350 AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
2351  Lease6Collection updated_leases;
2352  for (Lease6Collection::const_iterator lease_it = leases.begin();
2353  lease_it != leases.end(); ++lease_it) {
2354  Lease6Ptr lease(new Lease6(**lease_it));
2355  if (ctx.currentIA().isNewResource(lease->addr_, lease->prefixlen_)) {
2356  // This lease was just created so is already up to date.
2357  updated_leases.push_back(lease);
2358  continue;
2359  }
2360 
2361  lease->reuseable_valid_lft_ = 0;
2362  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2363  lease->fqdn_rev_ = ctx.rev_dns_update_;
2364  lease->hostname_ = ctx.hostname_;
2365  if (!ctx.fake_allocation_) {
2366  bool update_stats = false;
2367 
2368  if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
2369  // Transition lease state to default (aka assigned)
2370  lease->state_ = Lease::STATE_DEFAULT;
2371 
2372  // If the lease is in the current subnet we need to account
2373  // for the re-assignment of the lease.
2374  if (inAllowedPool(ctx, ctx.currentIA().type_,
2375  lease->addr_, true)) {
2376  update_stats = true;
2377  }
2378  }
2379 
2380  bool fqdn_changed = ((lease->type_ != Lease::TYPE_PD) &&
2381  !(lease->hasIdenticalFqdn(**lease_it)));
2382 
2383  lease->cltt_ = time(NULL);
2384  if (!fqdn_changed) {
2385  uint32_t current_preferred_lft = lease->preferred_lft_;
2386  setLeaseReusable(lease, current_preferred_lft, ctx);
2387  }
2388  if (lease->reuseable_valid_lft_ == 0) {
2389  ctx.currentIA().changed_leases_.push_back(*lease_it);
2391  }
2392 
2393  if (update_stats) {
2394  StatsMgr::instance().addValue(
2395  StatsMgr::generateName("subnet", lease->subnet_id_,
2396  ctx.currentIA().type_ == Lease::TYPE_NA ?
2397  "assigned-nas" : "assigned-pds"),
2398  static_cast<int64_t>(1));
2399  StatsMgr::instance().addValue(
2400  StatsMgr::generateName("subnet", lease->subnet_id_,
2401  ctx.currentIA().type_ == Lease::TYPE_NA ?
2402  "cumulative-assigned-nas" :
2403  "cumulative-assigned-pds"),
2404  static_cast<int64_t>(1));
2405  StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2406  "cumulative-assigned-nas" :
2407  "cumulative-assigned-pds",
2408  static_cast<int64_t>(1));
2409  }
2410  }
2411 
2412  updated_leases.push_back(lease);
2413  }
2414 
2415  return (updated_leases);
2416 }
2417 
2418 void
2419 AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout,
2420  const bool remove_lease,
2421  const uint16_t max_unwarned_cycles) {
2422 
2425  .arg(max_leases)
2426  .arg(timeout);
2427 
2428  // Create stopwatch and automatically start it to measure the time
2429  // taken by the routine.
2430  util::Stopwatch stopwatch;
2431 
2432  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2433 
2434  // This value indicates if we have been able to deal with all expired
2435  // leases in this pass.
2436  bool incomplete_reclamation = false;
2437  Lease6Collection leases;
2438  // The value of 0 has a special meaning - reclaim all.
2439  if (max_leases > 0) {
2440  // If the value is non-zero, the caller has limited the number of
2441  // leases to reclaim. We obtain one lease more to see if there will
2442  // be still leases left after this pass.
2443  lease_mgr.getExpiredLeases6(leases, max_leases + 1);
2444  // There are more leases expired leases than we will process in this
2445  // pass, so we should mark it as an incomplete reclamation. We also
2446  // remove this extra lease (which we don't want to process anyway)
2447  // from the collection.
2448  if (leases.size() > max_leases) {
2449  leases.pop_back();
2450  incomplete_reclamation = true;
2451  }
2452 
2453  } else {
2454  // If there is no limitation on the number of leases to reclaim,
2455  // we will try to process all. Hence, we don't mark it as incomplete
2456  // reclamation just yet.
2457  lease_mgr.getExpiredLeases6(leases, max_leases);
2458  }
2459 
2460  // Do not initialize the callout handle until we know if there are any
2461  // lease6_expire callouts installed.
2462  CalloutHandlePtr callout_handle;
2463  if (!leases.empty() &&
2464  HooksManager::calloutsPresent(Hooks.hook_index_lease6_expire_)) {
2465  callout_handle = HooksManager::createCalloutHandle();
2466  }
2467 
2468  size_t leases_processed = 0;
2469  BOOST_FOREACH(Lease6Ptr lease, leases) {
2470 
2471  try {
2472  // Reclaim the lease.
2473  if (MultiThreadingMgr::instance().getMode()) {
2474  // The reclamation is exclusive of packet processing.
2475  WriteLockGuard exclusive(rw_mutex_);
2476 
2477  reclaimExpiredLease(lease, remove_lease, callout_handle);
2478  ++leases_processed;
2479  } else {
2480  reclaimExpiredLease(lease, remove_lease, callout_handle);
2481  ++leases_processed;
2482  }
2483 
2484  } catch (const std::exception& ex) {
2486  .arg(lease->addr_.toText())
2487  .arg(ex.what());
2488  }
2489 
2490  // Check if we have hit the timeout for running reclamation routine and
2491  // return if we have. We're checking it here, because we always want to
2492  // allow reclaiming at least one lease.
2493  if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2494  // Timeout. This will likely mean that we haven't been able to process
2495  // all leases we wanted to process. The reclamation pass will be
2496  // probably marked as incomplete.
2497  if (!incomplete_reclamation) {
2498  if (leases_processed < leases.size()) {
2499  incomplete_reclamation = true;
2500  }
2501  }
2502 
2505  .arg(timeout);
2506  break;
2507  }
2508  }
2509 
2510  // Stop measuring the time.
2511  stopwatch.stop();
2512 
2513  // Mark completion of the lease reclamation routine and present some stats.
2516  .arg(leases_processed)
2517  .arg(stopwatch.logFormatTotalDuration());
2518 
2519  // Check if this was an incomplete reclamation and increase the number of
2520  // consecutive incomplete reclamations.
2521  if (incomplete_reclamation) {
2522  ++incomplete_v6_reclamations_;
2523  // If the number of incomplete reclamations is beyond the threshold, we
2524  // need to issue a warning.
2525  if ((max_unwarned_cycles > 0) &&
2526  (incomplete_v6_reclamations_ > max_unwarned_cycles)) {
2528  .arg(max_unwarned_cycles);
2529  // We issued a warning, so let's now reset the counter.
2530  incomplete_v6_reclamations_ = 0;
2531  }
2532 
2533  } else {
2534  // This was a complete reclamation, so let's reset the counter.
2535  incomplete_v6_reclamations_ = 0;
2536 
2539  }
2540 }
2541 
2542 void
2546  .arg(secs);
2547 
2548  uint64_t deleted_leases = 0;
2549  try {
2550  // Try to delete leases from the lease database.
2551  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2552  deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
2553 
2554  } catch (const std::exception& ex) {
2556  .arg(ex.what());
2557  }
2558 
2561  .arg(deleted_leases);
2562 }
2563 
2564 void
2565 AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout,
2566  const bool remove_lease,
2567  const uint16_t max_unwarned_cycles) {
2568 
2571  .arg(max_leases)
2572  .arg(timeout);
2573 
2574  // Create stopwatch and automatically start it to measure the time
2575  // taken by the routine.
2576  util::Stopwatch stopwatch;
2577 
2578  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2579 
2580  // This value indicates if we have been able to deal with all expired
2581  // leases in this pass.
2582  bool incomplete_reclamation = false;
2583  Lease4Collection leases;
2584  // The value of 0 has a special meaning - reclaim all.
2585  if (max_leases > 0) {
2586  // If the value is non-zero, the caller has limited the number of
2587  // leases to reclaim. We obtain one lease more to see if there will
2588  // be still leases left after this pass.
2589  lease_mgr.getExpiredLeases4(leases, max_leases + 1);
2590  // There are more leases expired leases than we will process in this
2591  // pass, so we should mark it as an incomplete reclamation. We also
2592  // remove this extra lease (which we don't want to process anyway)
2593  // from the collection.
2594  if (leases.size() > max_leases) {
2595  leases.pop_back();
2596  incomplete_reclamation = true;
2597  }
2598 
2599  } else {
2600  // If there is no limitation on the number of leases to reclaim,
2601  // we will try to process all. Hence, we don't mark it as incomplete
2602  // reclamation just yet.
2603  lease_mgr.getExpiredLeases4(leases, max_leases);
2604  }
2605 
2606  // Do not initialize the callout handle until we know if there are any
2607  // lease4_expire callouts installed.
2608  CalloutHandlePtr callout_handle;
2609  if (!leases.empty() &&
2610  HooksManager::calloutsPresent(Hooks.hook_index_lease4_expire_)) {
2611  callout_handle = HooksManager::createCalloutHandle();
2612  }
2613 
2614  size_t leases_processed = 0;
2615  BOOST_FOREACH(Lease4Ptr lease, leases) {
2616 
2617  try {
2618  // Reclaim the lease.
2619  if (MultiThreadingMgr::instance().getMode()) {
2620  // The reclamation is exclusive of packet processing.
2621  WriteLockGuard exclusive(rw_mutex_);
2622 
2623  reclaimExpiredLease(lease, remove_lease, callout_handle);
2624  ++leases_processed;
2625  } else {
2626  reclaimExpiredLease(lease, remove_lease, callout_handle);
2627  ++leases_processed;
2628  }
2629 
2630  } catch (const std::exception& ex) {
2632  .arg(lease->addr_.toText())
2633  .arg(ex.what());
2634  }
2635 
2636  // Check if we have hit the timeout for running reclamation routine and
2637  // return if we have. We're checking it here, because we always want to
2638  // allow reclaiming at least one lease.
2639  if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2640  // Timeout. This will likely mean that we haven't been able to process
2641  // all leases we wanted to process. The reclamation pass will be
2642  // probably marked as incomplete.
2643  if (!incomplete_reclamation) {
2644  if (leases_processed < leases.size()) {
2645  incomplete_reclamation = true;
2646  }
2647  }
2648 
2651  .arg(timeout);
2652  break;
2653  }
2654  }
2655 
2656  // Stop measuring the time.
2657  stopwatch.stop();
2658 
2659  // Mark completion of the lease reclamation routine and present some stats.
2662  .arg(leases_processed)
2663  .arg(stopwatch.logFormatTotalDuration());
2664 
2665  // Check if this was an incomplete reclamation and increase the number of
2666  // consecutive incomplete reclamations.
2667  if (incomplete_reclamation) {
2668  ++incomplete_v4_reclamations_;
2669  // If the number of incomplete reclamations is beyond the threshold, we
2670  // need to issue a warning.
2671  if ((max_unwarned_cycles > 0) &&
2672  (incomplete_v4_reclamations_ > max_unwarned_cycles)) {
2674  .arg(max_unwarned_cycles);
2675  // We issued a warning, so let's now reset the counter.
2676  incomplete_v4_reclamations_ = 0;
2677  }
2678 
2679  } else {
2680  // This was a complete reclamation, so let's reset the counter.
2681  incomplete_v4_reclamations_ = 0;
2682 
2685  }
2686 }
2687 
2688 template<typename LeasePtrType>
2689 void
2690 AllocEngine::reclaimExpiredLease(const LeasePtrType& lease, const bool remove_lease,
2691  const CalloutHandlePtr& callout_handle) {
2692  reclaimExpiredLease(lease, remove_lease ? DB_RECLAIM_REMOVE : DB_RECLAIM_UPDATE,
2693  callout_handle);
2694 }
2695 
2696 template<typename LeasePtrType>
2697 void
2698 AllocEngine::reclaimExpiredLease(const LeasePtrType& lease,
2699  const CalloutHandlePtr& callout_handle) {
2700  // This variant of the method is used by the code which allocates or
2701  // renews leases. It may be the case that the lease has already been
2702  // reclaimed, so there is nothing to do.
2703  if (!lease->stateExpiredReclaimed()) {
2704  reclaimExpiredLease(lease, DB_RECLAIM_LEAVE_UNCHANGED, callout_handle);
2705  }
2706 }
2707 
2708 void
2709 AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
2710  const DbReclaimMode& reclaim_mode,
2711  const CalloutHandlePtr& callout_handle) {
2712 
2715  .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
2716  .arg(lease->addr_.toText())
2717  .arg(static_cast<int>(lease->prefixlen_));
2718 
2719  // The skip flag indicates if the callouts have taken responsibility
2720  // for reclaiming the lease. The callout will set this to true if
2721  // it reclaims the lease itself. In this case the reclamation routine
2722  // will not update DNS nor update the database.
2723  bool skipped = false;
2724  if (callout_handle) {
2725 
2726  // Use the RAII wrapper to make sure that the callout handle state is
2727  // reset when this object goes out of scope. All hook points must do
2728  // it to prevent possible circular dependency between the callout
2729  // handle and its arguments.
2730  ScopedCalloutHandleState callout_handle_state(callout_handle);
2731 
2732  callout_handle->deleteAllArguments();
2733  callout_handle->setArgument("lease6", lease);
2734  callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2735 
2736  HooksManager::callCallouts(Hooks.hook_index_lease6_expire_,
2737  *callout_handle);
2738 
2739  skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2740  }
2741 
2744 
2745  if (!skipped) {
2746 
2747  // Generate removal name change request for D2, if required.
2748  // This will return immediately if the DNS wasn't updated
2749  // when the lease was created.
2750  queueNCR(CHG_REMOVE, lease);
2751 
2752  // Let's check if the lease that just expired is in DECLINED state.
2753  // If it is, we need to perform a couple extra steps.
2754  bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2755  if (lease->state_ == Lease::STATE_DECLINED) {
2756  // Do extra steps required for declined lease reclamation:
2757  // - call the recover hook
2758  // - bump decline-related stats
2759  // - log separate message
2760  // There's no point in keeping a declined lease after its
2761  // reclamation. A declined lease doesn't have any client
2762  // identifying information anymore. So we'll flag it for
2763  // removal unless the hook has set the skip flag.
2764  remove_lease = reclaimDeclined(lease);
2765  }
2766 
2767  if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2768  // Reclaim the lease - depending on the configuration, set the
2769  // expired-reclaimed state or simply remove it.
2770  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2771  reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
2772  std::bind(&LeaseMgr::updateLease6,
2773  &lease_mgr, ph::_1));
2774  }
2775  }
2776 
2777  // Update statistics.
2778 
2779  // Decrease number of assigned leases.
2780  if (lease->type_ == Lease::TYPE_NA) {
2781  // IA_NA
2782  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2783  lease->subnet_id_,
2784  "assigned-nas"),
2785  int64_t(-1));
2786 
2787  } else if (lease->type_ == Lease::TYPE_PD) {
2788  // IA_PD
2789  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2790  lease->subnet_id_,
2791  "assigned-pds"),
2792  int64_t(-1));
2793 
2794  }
2795 
2796  // Increase total number of reclaimed leases.
2797  StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
2798 
2799  // Increase number of reclaimed leases for a subnet.
2800  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2801  lease->subnet_id_,
2802  "reclaimed-leases"),
2803  int64_t(1));
2804 }
2805 
2806 void
2807 AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
2808  const DbReclaimMode& reclaim_mode,
2809  const CalloutHandlePtr& callout_handle) {
2810 
2813  .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
2814  .arg(lease->addr_.toText());
2815 
2816  // The skip flag indicates if the callouts have taken responsibility
2817  // for reclaiming the lease. The callout will set this to true if
2818  // it reclaims the lease itself. In this case the reclamation routine
2819  // will not update DNS nor update the database.
2820  bool skipped = false;
2821  if (callout_handle) {
2822 
2823  // Use the RAII wrapper to make sure that the callout handle state is
2824  // reset when this object goes out of scope. All hook points must do
2825  // it to prevent possible circular dependency between the callout
2826  // handle and its arguments.
2827  ScopedCalloutHandleState callout_handle_state(callout_handle);
2828 
2829  callout_handle->setArgument("lease4", lease);
2830  callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2831 
2832  HooksManager::callCallouts(Hooks.hook_index_lease4_expire_,
2833  *callout_handle);
2834 
2835  skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2836  }
2837 
2840 
2841  if (!skipped) {
2842 
2843  // Generate removal name change request for D2, if required.
2844  // This will return immediately if the DNS wasn't updated
2845  // when the lease was created.
2846  queueNCR(CHG_REMOVE, lease);
2847  // Clear DNS fields so we avoid redundant removes.
2848  lease->hostname_.clear();
2849  lease->fqdn_fwd_ = false;
2850  lease->fqdn_rev_ = false;
2851 
2852  // Let's check if the lease that just expired is in DECLINED state.
2853  // If it is, we need to perform a couple extra steps.
2854  bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2855  if (lease->state_ == Lease::STATE_DECLINED) {
2856  // Do extra steps required for declined lease reclamation:
2857  // - call the recover hook
2858  // - bump decline-related stats
2859  // - log separate message
2860  // There's no point in keeping a declined lease after its
2861  // reclamation. A declined lease doesn't have any client
2862  // identifying information anymore. So we'll flag it for
2863  // removal unless the hook has set the skip flag.
2864  remove_lease = reclaimDeclined(lease);
2865  }
2866 
2867  if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2868  // Reclaim the lease - depending on the configuration, set the
2869  // expired-reclaimed state or simply remove it.
2870  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2871  reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
2872  std::bind(&LeaseMgr::updateLease4,
2873  &lease_mgr, ph::_1));
2874  }
2875  }
2876 
2877  // Update statistics.
2878 
2879  // Decrease number of assigned addresses.
2880  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2881  lease->subnet_id_,
2882  "assigned-addresses"),
2883  int64_t(-1));
2884 
2885  // Increase total number of reclaimed leases.
2886  StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
2887 
2888  // Increase number of reclaimed leases for a subnet.
2889  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2890  lease->subnet_id_,
2891  "reclaimed-leases"),
2892  int64_t(1));
2893 }
2894 
2895 void
2899  .arg(secs);
2900 
2901  uint64_t deleted_leases = 0;
2902  try {
2903  // Try to delete leases from the lease database.
2904  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2905  deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
2906 
2907  } catch (const std::exception& ex) {
2909  .arg(ex.what());
2910  }
2911 
2914  .arg(deleted_leases);
2915 }
2916 
2917 bool
2918 AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
2919  if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
2920  return (true);
2921  }
2922 
2923  if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_recover_)) {
2924  CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
2925 
2926  // Use the RAII wrapper to make sure that the callout handle state is
2927  // reset when this object goes out of scope. All hook points must do
2928  // it to prevent possible circular dependency between the callout
2929  // handle and its arguments.
2930  ScopedCalloutHandleState callout_handle_state(callout_handle);
2931 
2932  // Pass necessary arguments
2933  callout_handle->setArgument("lease4", lease);
2934 
2935  // Call the callouts
2936  HooksManager::callCallouts(Hooks.hook_index_lease4_recover_, *callout_handle);
2937 
2938  // Callouts decided to skip the action. This means that the lease is not
2939  // assigned, so the client will get NoAddrAvail as a result. The lease
2940  // won't be inserted into the database.
2941  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2943  .arg(lease->addr_.toText());
2944  return (false);
2945  }
2946  }
2947 
2949  .arg(lease->addr_.toText())
2950  .arg(lease->valid_lft_);
2951 
2952  StatsMgr& stats_mgr = StatsMgr::instance();
2953 
2954  // Decrease subnet specific counter for currently declined addresses
2955  stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
2956  "declined-addresses"), static_cast<int64_t>(-1));
2957 
2958  // Decrease global counter for declined addresses
2959  stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
2960 
2961  stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
2962 
2963  stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
2964  "reclaimed-declined-addresses"), static_cast<int64_t>(1));
2965 
2966  // Note that we do not touch assigned-addresses counters. Those are
2967  // modified in whatever code calls this method.
2968  return (true);
2969 }
2970 
2971 bool
2972 AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
2973  if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
2974  return (true);
2975  }
2976 
2977  if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_recover_)) {
2978  CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
2979 
2980  // Use the RAII wrapper to make sure that the callout handle state is
2981  // reset when this object goes out of scope. All hook points must do
2982  // it to prevent possible circular dependency between the callout
2983  // handle and its arguments.
2984  ScopedCalloutHandleState callout_handle_state(callout_handle);
2985 
2986  // Pass necessary arguments
2987  callout_handle->setArgument("lease6", lease);
2988 
2989  // Call the callouts
2990  HooksManager::callCallouts(Hooks.hook_index_lease6_recover_, *callout_handle);
2991 
2992  // Callouts decided to skip the action. This means that the lease is not
2993  // assigned, so the client will get NoAddrAvail as a result. The lease
2994  // won't be inserted into the database.
2995  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2997  .arg(lease->addr_.toText());
2998  return (false);
2999  }
3000  }
3001 
3003  .arg(lease->addr_.toText())
3004  .arg(lease->valid_lft_);
3005 
3006  StatsMgr& stats_mgr = StatsMgr::instance();
3007 
3008  // Decrease subnet specific counter for currently declined addresses
3009  stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3010  "declined-addresses"), static_cast<int64_t>(-1));
3011 
3012  // Decrease global counter for declined addresses
3013  stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3014 
3015  stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3016 
3017  stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3018  "reclaimed-declined-addresses"), static_cast<int64_t>(1));
3019 
3020  // Note that we do not touch assigned-nas counters. Those are
3021  // modified in whatever code calls this method.
3022 
3023  return (true);
3024 }
3025 
3026 template<typename LeasePtrType>
3027 void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
3028  const bool remove_lease,
3029  const std::function<void (const LeasePtrType&)>&
3030  lease_update_fun) const {
3031 
3032  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3033 
3034  // Reclaim the lease - depending on the configuration, set the
3035  // expired-reclaimed state or simply remove it.
3036  if (remove_lease) {
3037  lease_mgr.deleteLease(lease);
3038  } else if (lease_update_fun) {
3039  // Clear FQDN information as we have already sent the
3040  // name change request to remove the DNS record.
3041  lease->reuseable_valid_lft_ = 0;
3042  lease->hostname_.clear();
3043  lease->fqdn_fwd_ = false;
3044  lease->fqdn_rev_ = false;
3045  lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
3046  lease_update_fun(lease);
3047 
3048  } else {
3049  return;
3050  }
3051 
3052  // Lease has been reclaimed.
3055  .arg(lease->addr_.toText());
3056 }
3057 
3058 } // namespace dhcp
3059 } // namespace isc
3060 
3061 // ##########################################################################
3062 // # DHCPv4 lease allocation code starts here.
3063 // ##########################################################################
3064 
3065 namespace {
3066 
3084 bool
3085 addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
3086  // When out-of-pool flag is true the server may assume that all host
3087  // reservations are for addresses that do not belong to the dynamic pool.
3088  // Therefore, it can skip the reservation checks when dealing with in-pool
3089  // addresses.
3090  if (ctx.subnet_ && ctx.subnet_->getReservationsInSubnet() &&
3091  (!ctx.subnet_->getReservationsOutOfPool() ||
3092  !ctx.subnet_->inPool(Lease::TYPE_V4, address))) {
3093  // The global parameter ip-reservations-unique controls whether it is allowed
3094  // to specify multiple reservations for the same IP address or delegated prefix
3095  // or IP reservations must be unique. Some host backends do not support the
3096  // former, thus we can't always use getAll4 calls to get the reservations
3097  // for the given IP. When we're in the default mode, when IP reservations
3098  // are unique, we should call get4 (supported by all backends). If we're in
3099  // the mode in which non-unique reservations are allowed the backends which
3100  // don't support it are not used and we can safely call getAll4.
3101  ConstHostCollection hosts;
3102  if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
3103  // Reservations are unique. It is safe to call get4 to get the unique host.
3104  ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
3105  if (host) {
3106  hosts.push_back(host);
3107  }
3108  } else {
3109  // Reservations can be non-unique. Need to get all reservations for that address.
3110  hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
3111  }
3112 
3113  for (auto host : hosts) {
3114  for (const AllocEngine::IdentifierPair& id_pair : ctx.host_identifiers_) {
3115  // If we find the matching host we know that this address is reserved
3116  // for us and we can return immediately.
3117  if (id_pair.first == host->getIdentifierType() &&
3118  id_pair.second == host->getIdentifier()) {
3119  return (false);
3120  }
3121  }
3122  }
3123  // We didn't find a matching host. If there are any reservations it means that
3124  // address is reserved for another client or multiple clients. If there are
3125  // no reservations address is not reserved for another client.
3126  return (!hosts.empty());
3127  }
3128  return (false);
3129 }
3130 
3146 bool
3147 hasAddressReservation(AllocEngine::ClientContext4& ctx) {
3148  if (ctx.hosts_.empty()) {
3149  return (false);
3150  }
3151 
3152  // Flag used to perform search for global reservations only once.
3153  bool search_global_done = false;
3154 
3155  Subnet4Ptr subnet = ctx.subnet_;
3156  while (subnet) {
3157  // Skip search if the global reservations have already been examined.
3158  if (!search_global_done && subnet->getReservationsGlobal()) {
3159  auto host = ctx.hosts_.find(SUBNET_ID_GLOBAL);
3160  // if we want global + other modes we would need to
3161  // return only if true, else continue
3162  if (host != ctx.hosts_.end() && host->second &&
3163  !host->second->getIPv4Reservation().isV4Zero()) {
3164  return (true);
3165  }
3166  // No need to perform this search again as there are no global
3167  // reservations.
3168  search_global_done = true;
3169  }
3170 
3171  if (subnet->getReservationsInSubnet()) {
3172  auto host = ctx.hosts_.find(subnet->getID());
3173  // The out-of-pool flag indicates that no client should be assigned
3174  // reserved addresses from within the dynamic pool, and for that
3175  // reason look only for reservations that are outside the pools,
3176  // hence the inPool check.
3177  if (host != ctx.hosts_.end() && host->second) {
3178  auto reservation = host->second->getIPv4Reservation();
3179  if (!reservation.isV4Zero() &&
3180  (!subnet->getReservationsOutOfPool() ||
3181  !subnet->inPool(Lease::TYPE_V4, reservation))) {
3182  ctx.subnet_ = subnet;
3183  return (true);
3184  }
3185  }
3186  }
3187 
3188  // No address reservation found here, so let's try another subnet
3189  // within the same shared network.
3190  subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3191  }
3192 
3193  return (false);
3194 }
3195 
3211 void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
3212  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3213 
3214  Subnet4Ptr original_subnet = ctx.subnet_;
3215 
3216  // Client identifier is optional. First check if we can try to lookup
3217  // by client-id.
3218  bool try_clientid_lookup = (ctx.clientid_ &&
3220  ctx.query_->getClasses()));
3221 
3222  // If it is possible to use client identifier to try to find client's lease.
3223  if (try_clientid_lookup) {
3224  // Get all leases for this client identifier. When shared networks are
3225  // in use it is more efficient to make a single query rather than
3226  // multiple queries, one for each subnet.
3227  Lease4Collection leases_client_id = lease_mgr.getLease4(*ctx.clientid_);
3228 
3229  // Iterate over the subnets within the shared network to see if any client's
3230  // lease belongs to them.
3231  for (Subnet4Ptr subnet = original_subnet; subnet;
3232  subnet = subnet->getNextSubnet(original_subnet,
3233  ctx.query_->getClasses())) {
3234 
3235  // If client identifier has been supplied and the server wasn't
3236  // explicitly configured to ignore client identifiers for this subnet
3237  // check if there is a lease within this subnet.
3238  if (subnet->getMatchClientId()) {
3239  for (auto l = leases_client_id.begin(); l != leases_client_id.end(); ++l) {
3240  if ((*l)->subnet_id_ == subnet->getID()) {
3241  // Lease found, so stick to this lease.
3242  client_lease = (*l);
3243  ctx.subnet_ = subnet;
3244  return;
3245  }
3246  }
3247  }
3248  }
3249  }
3250 
3251  // If no lease found using the client identifier, try the lookup using
3252  // the HW address.
3253  if (!client_lease && ctx.hwaddr_) {
3254 
3255  // Get all leases for this HW address.
3256  Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
3257 
3258  for (Subnet4Ptr subnet = original_subnet; subnet;
3259  subnet = subnet->getNextSubnet(original_subnet,
3260  ctx.query_->getClasses())) {
3261  ClientIdPtr client_id;
3262  if (subnet->getMatchClientId()) {
3263  client_id = ctx.clientid_;
3264  }
3265 
3266  // Try to find the lease that matches current subnet and belongs to
3267  // this client, so both HW address and client identifier match.
3268  for (Lease4Collection::const_iterator client_lease_it = leases_hw_address.begin();
3269  client_lease_it != leases_hw_address.end(); ++client_lease_it) {
3270  Lease4Ptr existing_lease = *client_lease_it;
3271  if ((existing_lease->subnet_id_ == subnet->getID()) &&
3272  existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
3273  // Found the lease of this client, so return it.
3274  client_lease = existing_lease;
3275  // We got a lease but the subnet it belongs to may differ from
3276  // the original subnet. Let's now stick to this subnet.
3277  ctx.subnet_ = subnet;
3278  return;
3279  }
3280  }
3281  }
3282  }
3283 }
3284 
3297 bool
3298 inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
3299  // If the subnet belongs to a shared network we will be iterating
3300  // over the subnets that belong to this shared network.
3301  Subnet4Ptr current_subnet = ctx.subnet_;
3302  while (current_subnet) {
3303 
3304  if (current_subnet->inPool(Lease::TYPE_V4, address,
3305  ctx.query_->getClasses())) {
3306  // We found a subnet that this address belongs to, so it
3307  // seems that this subnet is the good candidate for allocation.
3308  // Let's update the selected subnet.
3309  ctx.subnet_ = current_subnet;
3310  return (true);
3311  }
3312 
3313  current_subnet = current_subnet->getNextSubnet(ctx.subnet_,
3314  ctx.query_->getClasses());
3315  }
3316 
3317  return (false);
3318 }
3319 
3320 } // namespace
3321 
3322 namespace isc {
3323 namespace dhcp {
3324 
3326  : subnet_(), clientid_(), hwaddr_(),
3327  requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
3328  fwd_dns_update_(false), rev_dns_update_(false),
3329  hostname_(""), callout_handle_(), fake_allocation_(false),
3330  old_lease_(), new_lease_(), hosts_(), conflicting_lease_(),
3331  query_(), host_identifiers_(),
3332  ddns_params_() {
3333 }
3334 
3336  const ClientIdPtr& clientid,
3337  const HWAddrPtr& hwaddr,
3338  const asiolink::IOAddress& requested_addr,
3339  const bool fwd_dns_update,
3340  const bool rev_dns_update,
3341  const std::string& hostname,
3342  const bool fake_allocation)
3343  : subnet_(subnet), clientid_(clientid), hwaddr_(hwaddr),
3344  requested_address_(requested_addr),
3345  fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
3346  hostname_(hostname), callout_handle_(),
3347  fake_allocation_(fake_allocation), old_lease_(), new_lease_(),
3348  hosts_(), host_identifiers_(),
3349  ddns_params_(new DdnsParams()) {
3350 
3351  // Initialize host identifiers.
3352  if (hwaddr) {
3353  addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
3354  }
3355 }
3356 
3359  if (subnet_ && subnet_->getReservationsInSubnet()) {
3360  auto host = hosts_.find(subnet_->getID());
3361  if (host != hosts_.cend()) {
3362  return (host->second);
3363  }
3364  }
3365 
3366  return (globalHost());
3367 }
3368 
3371  if (subnet_ && subnet_->getReservationsGlobal()) {
3372  auto host = hosts_.find(SUBNET_ID_GLOBAL);
3373  if (host != hosts_.cend()) {
3374  return (host->second);
3375  }
3376  }
3377 
3378  return (ConstHostPtr());
3379 }
3380 
3383  // We already have it return it unless the context subnet has changed.
3384  if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
3385  return (ddns_params_);
3386  }
3387 
3388  // Doesn't exist yet or is stale, (re)create it.
3389  if (subnet_) {
3390  ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
3391  return (ddns_params_);
3392  }
3393 
3394  // Asked for it without a subnet? This case really shouldn't occur but
3395  // for now let's return an instance with default values.
3396  return (DdnsParamsPtr(new DdnsParams()));
3397 }
3398 
3399 Lease4Ptr
3401  // The NULL pointer indicates that the old lease didn't exist. It may
3402  // be later set to non NULL value if existing lease is found in the
3403  // database.
3404  ctx.old_lease_.reset();
3405  ctx.new_lease_.reset();
3406 
3407  // Before we start allocation process, we need to make sure that the
3408  // selected subnet is allowed for this client. If not, we'll try to
3409  // use some other subnet within the shared network. If there are no
3410  // subnets allowed for this client within the shared network, we
3411  // can't allocate a lease.
3412  Subnet4Ptr subnet = ctx.subnet_;
3413  if (subnet && !subnet->clientSupported(ctx.query_->getClasses())) {
3414  ctx.subnet_ = subnet->getNextSubnet(subnet, ctx.query_->getClasses());
3415  }
3416 
3417  try {
3418  if (!ctx.subnet_) {
3419  isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
3420  }
3421 
3422  if (!ctx.hwaddr_) {
3423  isc_throw(BadValue, "HWAddr must be defined");
3424  }
3425 
3426  if (ctx.fake_allocation_) {
3427  return (discoverLease4(ctx));
3428 
3429  } else {
3430  ctx.new_lease_ = requestLease4(ctx);
3431  }
3432 
3433  } catch (const isc::Exception& e) {
3434  // Some other error, return an empty lease.
3436  .arg(ctx.query_->getLabel())
3437  .arg(e.what());
3438  }
3439 
3440  return (ctx.new_lease_);
3441 }
3442 
3443 void
3445  ctx.hosts_.clear();
3446 
3447  // If there is no subnet, there is nothing to do.
3448  if (!ctx.subnet_) {
3449  return;
3450  }
3451 
3452  auto subnet = ctx.subnet_;
3453 
3454  std::map<SubnetID, ConstHostPtr> host_map;
3455  SharedNetwork4Ptr network;
3456  subnet->getSharedNetwork(network);
3457 
3458  // @todo: This code can be trivially optimized.
3459  if (subnet->getReservationsGlobal()) {
3460  ConstHostPtr ghost = findGlobalReservation(ctx);
3461  if (ghost) {
3462  ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
3463 
3464  // If we had only to fetch global reservations it is done.
3465  if (!subnet->getReservationsInSubnet()) {
3466  return;
3467  }
3468  }
3469  }
3470 
3471  // If the subnet belongs to a shared network it is usually going to be
3472  // more efficient to make a query for all reservations for a particular
3473  // client rather than a query for each subnet within this shared network.
3474  // The only case when it is going to be less efficient is when there are
3475  // more host identifier types in use than subnets within a shared network.
3476  // As it breaks RADIUS use of host caching this can be disabled by the
3477  // host manager.
3478  const bool use_single_query = network &&
3480  (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
3481 
3482  if (use_single_query) {
3483  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3484  ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
3485  &id_pair.second[0],
3486  id_pair.second.size());
3487  // Store the hosts in the temporary map, because some hosts may
3488  // belong to subnets outside of the shared network. We'll need
3489  // to eliminate them.
3490  for (auto host = hosts.begin(); host != hosts.end(); ++host) {
3491  if ((*host)->getIPv4SubnetID() != SUBNET_ID_GLOBAL) {
3492  host_map[(*host)->getIPv4SubnetID()] = *host;
3493  }
3494  }
3495  }
3496  }
3497 
3498  // We can only search for the reservation if a subnet has been selected.
3499  while (subnet) {
3500 
3501  // Only makes sense to get reservations if the client has access
3502  // to the class and host reservations are enabled for this subnet.
3503  if (subnet->clientSupported(ctx.query_->getClasses()) &&
3504  subnet->getReservationsInSubnet()) {
3505  // Iterate over configured identifiers in the order of preference
3506  // and try to use each of them to search for the reservations.
3507  if (use_single_query) {
3508  if (host_map.count(subnet->getID()) > 0) {
3509  ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
3510  }
3511  } else {
3512  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3513  // Attempt to find a host using a specified identifier.
3514  ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
3515  id_pair.first,
3516  &id_pair.second[0],
3517  id_pair.second.size());
3518  // If we found matching host for this subnet.
3519  if (host) {
3520  ctx.hosts_[subnet->getID()] = host;
3521  break;
3522  }
3523  }
3524  }
3525  }
3526 
3527  // We need to get to the next subnet if this is a shared network. If it
3528  // is not (a plain subnet), getNextSubnet will return NULL and we're
3529  // done here.
3530  subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3531  }
3532 }
3533 
3536  ConstHostPtr host;
3537  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3538  // Attempt to find a host using a specified identifier.
3539  host = HostMgr::instance().get4(SUBNET_ID_GLOBAL, id_pair.first,
3540  &id_pair.second[0], id_pair.second.size());
3541 
3542  // If we found matching global host we're done.
3543  if (host) {
3544  break;
3545  }
3546  }
3547 
3548  return (host);
3549 }
3550 
3551 Lease4Ptr
3552 AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
3553  // Find an existing lease for this client. This function will return true
3554  // if there is a conflict with existing lease and the allocation should
3555  // not be continued.
3556  Lease4Ptr client_lease;
3557  findClientLease(ctx, client_lease);
3558 
3559  // new_lease will hold the pointer to the lease that we will offer to the
3560  // caller.
3561  Lease4Ptr new_lease;
3562 
3563  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
3564 
3565  // Check if there is a reservation for the client. If there is, we want to
3566  // assign the reserved address, rather than any other one.
3567  if (hasAddressReservation(ctx)) {
3568 
3571  .arg(ctx.query_->getLabel())
3572  .arg(ctx.currentHost()->getIPv4Reservation().toText());
3573 
3574  // If the client doesn't have a lease or the leased address is different
3575  // than the reserved one then let's try to allocate the reserved address.
3576  // Otherwise the address that the client has is the one for which it
3577  // has a reservation, so just renew it.
3578  if (!client_lease || (client_lease->addr_ != ctx.currentHost()->getIPv4Reservation())) {
3579  // The call below will return a pointer to the lease for the address
3580  // reserved to this client, if the lease is available, i.e. is not
3581  // currently assigned to any other client.
3582  // Note that we don't remove the existing client's lease at this point
3583  // because this is not a real allocation, we just offer what we can
3584  // allocate in the DHCPREQUEST time.
3585  new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
3586  callout_status);
3587  if (!new_lease) {
3589  .arg(ctx.query_->getLabel())
3590  .arg(ctx.currentHost()->getIPv4Reservation().toText())
3591  .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
3592  "(no lease info)");
3593  }
3594 
3595  } else {
3596  new_lease = renewLease4(client_lease, ctx);
3597  }
3598  }
3599 
3600  // Client does not have a reservation or the allocation of the reserved
3601  // address has failed, probably because the reserved address is in use
3602  // by another client. If the client has a lease, we will check if we can
3603  // offer this lease to the client. The lease can't be offered in the
3604  // situation when it is reserved for another client or when the address
3605  // is not in the dynamic pool. The former may be the result of adding the
3606  // new reservation for the address used by this client. The latter may
3607  // be due to the client using the reserved out-of-the pool address, for
3608  // which the reservation has just been removed.
3609  if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
3610  !addressReserved(client_lease->addr_, ctx)) {
3611 
3614  .arg(ctx.query_->getLabel());
3615 
3616  new_lease = renewLease4(client_lease, ctx);
3617  }
3618 
3619  // The client doesn't have any lease or the lease can't be offered
3620  // because it is either reserved for some other client or the
3621  // address is not in the dynamic pool.
3622  // Let's use the client's hint (requested IP address), if the client
3623  // has provided it, and try to offer it. This address must not be
3624  // reserved for another client, and must be in the range of the
3625  // dynamic pool.
3626  if (!new_lease && !ctx.requested_address_.isV4Zero() &&
3627  inAllowedPool(ctx, ctx.requested_address_) &&
3628  !addressReserved(ctx.requested_address_, ctx)) {
3629 
3632  .arg(ctx.requested_address_.toText())
3633  .arg(ctx.query_->getLabel());
3634 
3635  new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3636  callout_status);
3637  }
3638 
3639  // The allocation engine failed to allocate all of the candidate
3640  // addresses. We will now use the allocator to pick the address
3641  // from the dynamic pool.
3642  if (!new_lease) {
3643 
3646  .arg(ctx.query_->getLabel());
3647 
3648  new_lease = allocateUnreservedLease4(ctx);
3649  }
3650 
3651  // Some of the methods like reuseExpiredLease4 may set the old lease to point
3652  // to the lease which they remove/override. If it is not set, but we have
3653  // found that the client has the lease the client's lease is the one
3654  // to return as an old lease.
3655  if (!ctx.old_lease_ && client_lease) {
3656  ctx.old_lease_ = client_lease;
3657  }
3658 
3659  return (new_lease);
3660 }
3661 
3662 Lease4Ptr
3663 AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
3664  // Find an existing lease for this client. This function will return true
3665  // if there is a conflict with existing lease and the allocation should
3666  // not be continued.
3667  Lease4Ptr client_lease;
3668  findClientLease(ctx, client_lease);
3669 
3670  // When the client sends the DHCPREQUEST, it should always specify the
3671  // address which it is requesting or renewing. That is, the client should
3672  // either use the requested IP address option or set the ciaddr. However,
3673  // we try to be liberal and allow the clients to not specify an address
3674  // in which case the allocation engine will pick a suitable address
3675  // for the client.
3676  if (!ctx.requested_address_.isV4Zero()) {
3677  // If the client has specified an address, make sure this address
3678  // is not reserved for another client. If it is, stop here because
3679  // we can't allocate this address.
3680  if (addressReserved(ctx.requested_address_, ctx)) {
3681 
3684  .arg(ctx.query_->getLabel())
3685  .arg(ctx.requested_address_.toText());
3686 
3687  return (Lease4Ptr());
3688  }
3689 
3690  } else if (hasAddressReservation(ctx)) {
3691  // The client hasn't specified an address to allocate, so the
3692  // allocation engine needs to find an appropriate address.
3693  // If there is a reservation for the client, let's try to
3694  // allocate the reserved address.
3695  ctx.requested_address_ = ctx.currentHost()->getIPv4Reservation();
3696 
3699  .arg(ctx.query_->getLabel())
3700  .arg(ctx.requested_address_.toText());
3701  }
3702 
3703  if (!ctx.requested_address_.isV4Zero()) {
3704  // There is a specific address to be allocated. Let's find out if
3705  // the address is in use.
3707  // If the address is in use (allocated and not expired), we check
3708  // if the address is in use by our client or another client.
3709  // If it is in use by another client, the address can't be
3710  // allocated.
3711  if (existing && !existing->expired() &&
3712  !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
3713  ctx.clientid_ : ClientIdPtr())) {
3714 
3717  .arg(ctx.query_->getLabel())
3718  .arg(ctx.requested_address_.toText());
3719 
3720  return (Lease4Ptr());
3721  }
3722 
3723  // If the client has a reservation but it is requesting a different
3724  // address it is possible that the client was offered this different
3725  // address because the reserved address is in use. We will have to
3726  // check if the address is in use.
3727  if (hasAddressReservation(ctx) &&
3728  (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) {
3729  existing =
3730  LeaseMgrFactory::instance().getLease4(ctx.currentHost()->getIPv4Reservation());
3731  // If the reserved address is not in use, i.e. the lease doesn't
3732  // exist or is expired, and the client is requesting a different
3733  // address, return NULL. The client should go back to the
3734  // DHCPDISCOVER and the reserved address will be offered.
3735  if (!existing || existing->expired()) {
3736 
3739  .arg(ctx.query_->getLabel())
3740  .arg(ctx.currentHost()->getIPv4Reservation().toText())
3741  .arg(ctx.requested_address_.toText());
3742 
3743  return (Lease4Ptr());
3744  }
3745  }
3746 
3747  // The use of the out-of-pool addresses is only allowed when the requested
3748  // address is reserved for the client. If the address is not reserved one
3749  // and it doesn't belong to the dynamic pool, do not allocate it.
3750  if ((!hasAddressReservation(ctx) ||
3751  (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
3752  !inAllowedPool(ctx, ctx.requested_address_)) {
3753 
3756  .arg(ctx.query_->getLabel())
3757  .arg(ctx.requested_address_);
3758 
3759  return (Lease4Ptr());
3760  }
3761  }
3762 
3763  // We have gone through all the checks, so we can now allocate the address
3764  // for the client.
3765 
3766  // If the client is requesting an address which is assigned to the client
3767  // let's just renew this address. Also, renew this address if the client
3768  // doesn't request any specific address.
3769  // Added extra checks: the address is reserved or belongs to the dynamic
3770  // pool for the case the pool class has changed before the request.
3771  if (client_lease) {
3772  if (((client_lease->addr_ == ctx.requested_address_) ||
3773  ctx.requested_address_.isV4Zero()) &&
3774  (hasAddressReservation(ctx) ||
3775  inAllowedPool(ctx, client_lease->addr_))) {
3776 
3779  .arg(ctx.query_->getLabel())
3780  .arg(ctx.requested_address_);
3781 
3782  return (renewLease4(client_lease, ctx));
3783  }
3784  }
3785 
3786  // new_lease will hold the pointer to the allocated lease if we allocate
3787  // successfully.
3788  Lease4Ptr new_lease;
3789 
3790  // The client doesn't have the lease or it is requesting an address
3791  // which it doesn't have. Let's try to allocate the requested address.
3792  if (!ctx.requested_address_.isV4Zero()) {
3793 
3796  .arg(ctx.query_->getLabel())
3797  .arg(ctx.requested_address_.toText());
3798 
3799  // The call below will return a pointer to the lease allocated
3800  // for the client if there is no lease for the requested address,
3801  // or the existing lease has expired. If the allocation fails,
3802  // e.g. because the lease is in use, we will return NULL to
3803  // indicate that we were unable to allocate the lease.
3804  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
3805  new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3806  callout_status);
3807 
3808  } else {
3809 
3812  .arg(ctx.query_->getLabel());
3813 
3814  // We will only get here if the client didn't specify which
3815  // address it wanted to be allocated. The allocation engine will
3816  // to pick the address from the dynamic pool.
3817  new_lease = allocateUnreservedLease4(ctx);
3818  }
3819 
3820  // If we allocated the lease for the client, but the client already had a
3821  // lease, we will need to return the pointer to the previous lease and
3822  // the previous lease needs to be removed from the lease database.
3823  if (new_lease && client_lease) {
3824  ctx.old_lease_ = Lease4Ptr(new Lease4(*client_lease));
3825 
3828  .arg(ctx.query_->getLabel())
3829  .arg(client_lease->addr_.toText());
3830 
3831  if (LeaseMgrFactory::instance().deleteLease(client_lease)) {
3832  // Need to decrease statistic for assigned addresses.
3833  StatsMgr::instance().addValue(
3834  StatsMgr::generateName("subnet", client_lease->subnet_id_,
3835  "assigned-addresses"),
3836  static_cast<int64_t>(-1));
3837  }
3838  }
3839 
3840  // Return the allocated lease or NULL pointer if allocation was
3841  // unsuccessful.
3842  return (new_lease);
3843 }
3844 
3845 uint32_t
3847 
3848  // If it's BOOTP, use infinite valid lifetime.
3849  if (ctx.query_->inClass("BOOTP")) {
3850  return (Lease::INFINITY_LFT);
3851  }
3852 
3853  // Use the dhcp-lease-time content from the client if it's there.
3854  uint32_t requested_lft = 0;
3855  OptionPtr opt = ctx.query_->getOption(DHO_DHCP_LEASE_TIME);
3856  if (opt) {
3857  OptionUint32Ptr opt_lft = boost::dynamic_pointer_cast<OptionInt<uint32_t> >(opt);
3858  if (opt_lft) {
3859  requested_lft = opt_lft->getValue();
3860  }
3861  }
3862 
3863  // If the triplet is specified in one of our classes use it.
3864  // We use the first one we find.
3865  Triplet<uint32_t> candidate_lft;
3866  const ClientClasses classes = ctx.query_->getClasses();
3867  if (!classes.empty()) {
3868  // Let's get class definitions
3869  const ClientClassDictionaryPtr& dict =
3870  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3871 
3872  // Iterate over the assigned class defintions.
3873  for (ClientClasses::const_iterator name = classes.cbegin();
3874  name != classes.cend(); ++name) {
3875  ClientClassDefPtr cl = dict->findClass(*name);
3876  if (cl && (!cl->getValid().unspecified())) {
3877  candidate_lft = cl->getValid();
3878  break;
3879  }
3880  }
3881  }
3882 
3883  // If no classes specified it, get it from the subnet.
3884  if (!candidate_lft) {
3885  candidate_lft = ctx.subnet_->getValid();
3886  }
3887 
3888  // If client requested a value, use the value bounded by
3889  // the candidate triplet.
3890  if (requested_lft > 0) {
3891  return (candidate_lft.get(requested_lft));
3892  }
3893 
3894  // Use the candidate's default value.
3895  return (candidate_lft.get());
3896 }
3897 
3898 Lease4Ptr
3899 AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
3900  CalloutHandle::CalloutNextStep& callout_status) {
3901  if (!ctx.hwaddr_) {
3902  isc_throw(BadValue, "Can't create a lease with NULL HW address");
3903  }
3904  if (!ctx.subnet_) {
3905  isc_throw(BadValue, "Can't create a lease without a subnet");
3906  }
3907 
3908  // Get the context appropriate valid lifetime.
3909  uint32_t valid_lft = getValidLft(ctx);
3910 
3911  time_t now = time(NULL);
3912 
3913  ClientIdPtr client_id;
3914  if (ctx.subnet_->getMatchClientId()) {
3915  client_id = ctx.clientid_;
3916  }
3917 
3918  Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
3919  valid_lft, now, ctx.subnet_->getID()));
3920 
3921  // Set FQDN specific lease parameters.
3922  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
3923  lease->fqdn_rev_ = ctx.rev_dns_update_;
3924  lease->hostname_ = ctx.hostname_;
3925 
3926  // Add(update) the extended information on the lease.
3927  static_cast<void>(updateLease4ExtendedInfo(lease, ctx));
3928 
3929  // Let's execute all callouts registered for lease4_select
3930  if (ctx.callout_handle_ &&
3931  HooksManager::calloutsPresent(hook_index_lease4_select_)) {
3932 
3933  // Use the RAII wrapper to make sure that the callout handle state is
3934  // reset when this object goes out of scope. All hook points must do
3935  // it to prevent possible circular dependency between the callout
3936  // handle and its arguments.
3937  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
3938 
3939  // Enable copying options from the packet within hook library.
3940  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
3941 
3942  // Pass necessary arguments
3943  // Pass the original client query
3944  ctx.callout_handle_->setArgument("query4", ctx.query_);
3945 
3946  // Subnet from which we do the allocation (That's as far as we can go
3947  // with using SubnetPtr to point to Subnet4 object. Users should not
3948  // be confused with dynamic_pointer_casts. They should get a concrete
3949  // pointer (Subnet4Ptr) pointing to a Subnet4 object.
3950  Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
3951  ctx.callout_handle_->setArgument("subnet4", subnet4);
3952 
3953  // Is this solicit (fake = true) or request (fake = false)
3954  ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
3955 
3956  // Pass the intended lease as well
3957  ctx.callout_handle_->setArgument("lease4", lease);
3958 
3959  // This is the first callout, so no need to clear any arguments
3960  HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
3961 
3962  callout_status = ctx.callout_handle_->getStatus();
3963 
3964  // Callouts decided to skip the action. This means that the lease is not
3965  // assigned, so the client will get NoAddrAvail as a result. The lease
3966  // won't be inserted into the database.
3967  if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
3969  return (Lease4Ptr());
3970  }
3971 
3972  // Let's use whatever callout returned. Hopefully it is the same lease
3973  // we handled to it.
3974  ctx.callout_handle_->getArgument("lease4", lease);
3975  }
3976 
3977  if (!ctx.fake_allocation_) {
3978  // That is a real (REQUEST) allocation
3979  bool status = LeaseMgrFactory::instance().addLease(lease);
3980  if (status) {
3981 
3982  // The lease insertion succeeded, let's bump up the statistic.
3983  StatsMgr::instance().addValue(
3984  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
3985  "assigned-addresses"),
3986  static_cast<int64_t>(1));
3987  StatsMgr::instance().addValue(
3988  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
3989  "cumulative-assigned-addresses"),
3990  static_cast<int64_t>(1));
3991  StatsMgr::instance().addValue("cumulative-assigned-addresses",
3992  static_cast<int64_t>(1));
3993 
3994  return (lease);
3995  } else {
3996  // One of many failures with LeaseMgr (e.g. lost connection to the
3997  // database, database failed etc.). One notable case for that
3998  // is that we are working in multi-process mode and we lost a race
3999  // (some other process got that address first)
4000  return (Lease4Ptr());
4001  }
4002  } else {
4003  // That is only fake (DISCOVER) allocation
4004 
4005  // It is for OFFER only. We should not insert the lease and callers
4006  // have already verified the lease does not exist in the database.
4007  return (lease);
4008  }
4009 }
4010 
4011 Lease4Ptr
4012 AllocEngine::renewLease4(const Lease4Ptr& lease,
4014  if (!lease) {
4015  isc_throw(BadValue, "null lease specified for renewLease4");
4016  }
4017 
4018  // Let's keep the old data. This is essential if we are using memfile
4019  // (the lease returned points directly to the lease4 object in the database)
4020  // We'll need it if we want to skip update (i.e. roll back renewal)
4022  Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
4023  ctx.old_lease_.reset(new Lease4(*old_values));
4024 
4025  // Update the lease with the information from the context.
4026  // If there was no significant changes, try reuse.
4027  lease->reuseable_valid_lft_ = 0;
4028  if (!updateLease4Information(lease, ctx)) {
4029  setLeaseReusable(lease, ctx);
4030  }
4031 
4032  if (!ctx.fake_allocation_) {
4033  // If the lease is expired we have to reclaim it before
4034  // re-assigning it to the client. The lease reclamation
4035  // involves execution of hooks and DNS update.
4036  if (ctx.old_lease_->expired()) {
4037  reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
4038  }
4039 
4040  lease->state_ = Lease::STATE_DEFAULT;
4041  }
4042 
4043  bool skip = false;
4044  // Execute all callouts registered for lease4_renew.
4045  if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_renew_)) {
4046 
4047  // Use the RAII wrapper to make sure that the callout handle state is
4048  // reset when this object goes out of scope. All hook points must do
4049  // it to prevent possible circular dependency between the callout
4050  // handle and its arguments.
4051  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4052 
4053  // Enable copying options from the packet within hook library.
4054  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4055 
4056  // Subnet from which we do the allocation. Convert the general subnet
4057  // pointer to a pointer to a Subnet4. Note that because we are using
4058  // boost smart pointers here, we need to do the cast using the boost
4059  // version of dynamic_pointer_cast.
4060  Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4061 
4062  // Pass the parameters. Note the clientid is passed only if match-client-id
4063  // is set. This is done that way, because the lease4-renew hook point is
4064  // about renewing a lease and the configuration parameter says the
4065  // client-id should be ignored. Hence no clientid value if match-client-id
4066  // is false.
4067  ctx.callout_handle_->setArgument("query4", ctx.query_);
4068  ctx.callout_handle_->setArgument("subnet4", subnet4);
4069  ctx.callout_handle_->setArgument("clientid", subnet4->getMatchClientId() ?
4070  ctx.clientid_ : ClientIdPtr());
4071  ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
4072 
4073  // Pass the lease to be updated
4074  ctx.callout_handle_->setArgument("lease4", lease);
4075 
4076  // Call all installed callouts
4077  HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
4078  *ctx.callout_handle_);
4079 
4080  // Callouts decided to skip the next processing step. The next
4081  // processing step would actually renew the lease, so skip at this
4082  // stage means "keep the old lease as it is".
4083  if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4084  skip = true;
4087  }
4088 
4090  }
4091 
4092  if (!ctx.fake_allocation_ && !skip && (lease->reuseable_valid_lft_ == 0)) {
4093  // for REQUEST we do update the lease
4095 
4096  // We need to account for the re-assignment of The lease.
4097  if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
4098  StatsMgr::instance().addValue(
4099  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4100  "assigned-addresses"),
4101  static_cast<int64_t>(1));
4102  StatsMgr::instance().addValue(
4103  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4104  "cumulative-assigned-addresses"),
4105  static_cast<int64_t>(1));
4106  StatsMgr::instance().addValue("cumulative-assigned-addresses",
4107  static_cast<int64_t>(1));
4108  }
4109  }
4110  if (skip) {
4111  // Rollback changes (really useful only for memfile)
4113  *lease = *old_values;
4114  }
4115 
4116  return (lease);
4117 }
4118 
4119 Lease4Ptr
4120 AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
4122  CalloutHandle::CalloutNextStep& callout_status) {
4123  if (!expired) {
4124  isc_throw(BadValue, "null lease specified for reuseExpiredLease");
4125  }
4126 
4127  if (!ctx.subnet_) {
4128  isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
4129  }
4130 
4131  if (!ctx.fake_allocation_) {
4132  // The expired lease needs to be reclaimed before it can be reused.
4133  // This includes declined leases for which probation period has
4134  // elapsed.
4135  reclaimExpiredLease(expired, ctx.callout_handle_);
4136  expired->state_ = Lease::STATE_DEFAULT;
4137  }
4138 
4139  expired->reuseable_valid_lft_ = 0;
4140  static_cast<void>(updateLease4Information(expired, ctx));
4141 
4144  .arg(ctx.query_->getLabel())
4145  .arg(expired->toText());
4146 
4147  // Let's execute all callouts registered for lease4_select
4148  if (ctx.callout_handle_ &&
4149  HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4150 
4151  // Enable copying options from the packet within hook library.
4152  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4153 
4154  // Use the RAII wrapper to make sure that the callout handle state is
4155  // reset when this object goes out of scope. All hook points must do
4156  // it to prevent possible circular dependency between the callout
4157  // handle and its arguments.
4158  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4159 
4160  // Pass necessary arguments
4161  // Pass the original client query
4162  ctx.callout_handle_->setArgument("query4", ctx.query_);
4163 
4164  // Subnet from which we do the allocation. Convert the general subnet
4165  // pointer to a pointer to a Subnet4. Note that because we are using
4166  // boost smart pointers here, we need to do the cast using the boost
4167  // version of dynamic_pointer_cast.
4168  Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4169  ctx.callout_handle_->setArgument("subnet4", subnet4);
4170 
4171  // Is this solicit (fake = true) or request (fake = false)
4172  ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4173 
4174  // The lease that will be assigned to a client
4175  ctx.callout_handle_->setArgument("lease4", expired);
4176 
4177  // Call the callouts
4178  HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4179 
4180  callout_status = ctx.callout_handle_->getStatus();
4181 
4182  // Callouts decided to skip the action. This means that the lease is not
4183  // assigned, so the client will get NoAddrAvail as a result. The lease
4184  // won't be inserted into the database.
4185  if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4188  return (Lease4Ptr());
4189  }
4190 
4192 
4193  // Let's use whatever callout returned. Hopefully it is the same lease
4194  // we handed to it.
4195  ctx.callout_handle_->getArgument("lease4", expired);
4196  }
4197 
4198  if (!ctx.fake_allocation_) {
4199  // for REQUEST we do update the lease
4201 
4202  // We need to account for the re-assignment of The lease.
4203  StatsMgr::instance().addValue(
4204  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4205  "assigned-addresses"),
4206  static_cast<int64_t>(1));
4207  StatsMgr::instance().addValue(
4208  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4209  "cumulative-assigned-addresses"),
4210  static_cast<int64_t>(1));
4211  StatsMgr::instance().addValue("cumulative-assigned-addresses",
4212  static_cast<int64_t>(1));
4213  }
4214 
4215  // We do nothing for SOLICIT. We'll just update database when
4216  // the client gets back to us with REQUEST message.
4217 
4218  // it's not really expired at this stage anymore - let's return it as
4219  // an updated lease
4220  return (expired);
4221 }
4222 
4223 Lease4Ptr
4224 AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
4225  CalloutHandle::CalloutNextStep& callout_status) {
4226  ctx.conflicting_lease_.reset();
4227 
4228  Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4229  if (exist_lease) {
4230  if (exist_lease->expired()) {
4231  ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4232  // reuseExpiredLease4() will reclaim the use which will
4233  // queue an NCR remove it needed. Clear the DNS fields in
4234  // the old lease to avoid a redundant remove in server logic.
4235  ctx.old_lease_->hostname_.clear();
4236  ctx.old_lease_->fqdn_fwd_ = false;
4237  ctx.old_lease_->fqdn_rev_ = false;
4238  return (reuseExpiredLease4(exist_lease, ctx, callout_status));
4239 
4240  } else {
4241  // If there is a lease and it is not expired, pass this lease back
4242  // to the caller in the context. The caller may need to know
4243  // which lease we're conflicting with.
4244  ctx.conflicting_lease_ = exist_lease;
4245  }
4246 
4247  } else {
4248  return (createLease4(ctx, candidate, callout_status));
4249  }
4250  return (Lease4Ptr());
4251 }
4252 
4253 Lease4Ptr
4254 AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
4255  Lease4Ptr new_lease;
4257  Subnet4Ptr subnet = ctx.subnet_;
4258 
4259  // Need to check if the subnet belongs to a shared network. If so,
4260  // we might be able to find a better subnet for lease allocation,
4261  // for which it is more likely that there are some leases available.
4262  // If we stick to the selected subnet, we may end up walking over
4263  // the entire subnet (or more subnets) to discover that the address
4264  // pools have been exhausted. Using a subnet from which an address
4265  // was assigned most recently is an optimization which increases
4266  // the likelihood of starting from the subnet which address pools
4267  // are not exhausted.
4268  SharedNetwork4Ptr network;
4269  ctx.subnet_->getSharedNetwork(network);
4270  if (network) {
4271  // This would try to find a subnet with the same set of classes
4272  // as the current subnet, but with the more recent "usage timestamp".
4273  // This timestamp is only updated for the allocations made with an
4274  // allocator (unreserved lease allocations), not the static
4275  // allocations or requested addresses.
4276  ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
4277  }
4278 
4279  // We have the choice in the order checking the lease and
4280  // the reservation. The default is to begin by the lease
4281  // if the multi-threading is disabled.
4282  bool check_reservation_first = MultiThreadingMgr::instance().getMode();
4283 
4284  Subnet4Ptr original_subnet = subnet;
4285 
4286  uint64_t total_attempts = 0;
4287 
4288  // The following counter tracks the number of subnets with matching client
4289  // classes from which the allocation engine attempted to assign leases.
4290  uint64_t subnets_with_unavail_leases = 0;
4291  // The following counter tracks the number of subnets in which there were
4292  // no matching pools for the client.
4293  uint64_t subnets_with_unavail_pools = 0;
4294 
4295  while (subnet) {
4296  ClientIdPtr client_id;
4297  if (subnet->getMatchClientId()) {
4298  client_id = ctx.clientid_;
4299  }
4300 
4301  uint64_t possible_attempts =
4302  subnet->getPoolCapacity(Lease::TYPE_V4,
4303  ctx.query_->getClasses());
4304 
4305  // If the number of tries specified in the allocation engine constructor
4306  // is set to 0 (unlimited) or the pools capacity is lower than that number,
4307  // let's use the pools capacity as the maximum number of tries. Trying
4308  // more than the actual pools capacity is a waste of time. If the specified
4309  // number of tries is lower than the pools capacity, use that number.
4310  uint64_t max_attempts = ((attempts_ == 0 || possible_attempts < attempts_) ? possible_attempts : attempts_);
4311 
4312  if (max_attempts > 0) {
4313  // If max_attempts is greater than 0, there are some pools in this subnet
4314  // from which we can potentially get a lease.
4315  ++subnets_with_unavail_leases;
4316  } else {
4317  // If max_attempts is 0, it is an indication that there are no pools
4318  // in the subnet from which we can get a lease.
4319  ++subnets_with_unavail_pools;
4320  }
4321 
4322  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
4323 
4324  for (uint64_t i = 0; i < max_attempts; ++i) {
4325 
4326  ++total_attempts;
4327 
4328  IOAddress candidate = allocator->pickAddress(subnet,
4329  ctx.query_->getClasses(),
4330  client_id,
4331  ctx.requested_address_);
4332  // First check for reservation when it is the choice.
4333  if (check_reservation_first && addressReserved(candidate, ctx)) {
4334  // Don't allocate.
4335  continue;
4336  }
4337 
4338  // Check if the resource is busy i.e. can be being allocated
4339  // by another thread to another client.
4340  ResourceHandler4 resource_handler;
4341  if (MultiThreadingMgr::instance().getMode() &&
4342  !resource_handler.tryLock4(candidate)) {
4343  // Don't allocate.
4344  continue;
4345  }
4346 
4347  // Check for an existing lease for the candidate address.
4348  Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4349  if (!exist_lease) {
4350  // No existing lease, is it reserved?
4351  if (check_reservation_first || !addressReserved(candidate, ctx)) {
4352  // Not reserved use it.
4353  new_lease = createLease4(ctx, candidate, callout_status);
4354  }
4355  } else {
4356  // An lease exists, is expired, and not reserved use it.
4357  if (exist_lease->expired() &&
4358  (check_reservation_first || !addressReserved(candidate, ctx))) {
4359  ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4360  new_lease = reuseExpiredLease4(exist_lease, ctx, callout_status);
4361  }
4362  }
4363 
4364  // We found a lease we can use, return it.
4365  if (new_lease) {
4366  return (new_lease);
4367  }
4368 
4369  if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
4370  // Don't retry when the callout status is not continue.
4371  subnet.reset();
4372  break;
4373  }
4374  }
4375 
4376  // This pointer may be set to NULL if hooks set SKIP status.
4377  if (subnet) {
4378  subnet = subnet->getNextSubnet(original_subnet, ctx.query_->getClasses());
4379 
4380  if (subnet) {
4381  ctx.subnet_ = subnet;
4382  }
4383  }
4384  }
4385 
4386  if (network) {
4387  // The client is in the shared network. Let's log the high level message
4388  // indicating which shared network the client belongs to.
4390  .arg(ctx.query_->getLabel())
4391  .arg(network->getName())
4392  .arg(subnets_with_unavail_leases)
4393  .arg(subnets_with_unavail_pools);
4394 
4395  } else {
4396  // The client is not connected to a shared network. It is connected
4397  // to a subnet. Let's log the ID of that subnet.
4399  .arg(ctx.query_->getLabel())
4400  .arg(ctx.subnet_->getID());
4401  }
4402  if (total_attempts == 0) {
4403  // In this case, it seems that none of the pools in the subnets could
4404  // be used for that client, both in case the client is connected to
4405  // a shared network or to a single subnet. Apparently, the client was
4406  // rejected to use the pools because of the client classes' mismatch.
4408  .arg(ctx.query_->getLabel());
4409  } else {
4410  // This is an old log message which provides a number of attempts
4411  // made by the allocation engine to allocate a lease. The only case
4412  // when we don't want to log this message is when the number of
4413  // attempts is zero (condition above), because it would look silly.
4415  .arg(ctx.query_->getLabel())
4416  .arg(total_attempts);
4417  }
4418 
4419  const ClientClasses& classes = ctx.query_->getClasses();
4420  if (!classes.empty()) {
4422  .arg(ctx.query_->getLabel())
4423  .arg(classes.toText());
4424  }
4425 
4426  return (new_lease);
4427 }
4428 
4429 bool
4430 AllocEngine::updateLease4Information(const Lease4Ptr& lease,
4431  AllocEngine::ClientContext4& ctx) const {
4432  bool changed = false;
4433  if (lease->subnet_id_ != ctx.subnet_->getID()) {
4434  changed = true;
4435  lease->subnet_id_ = ctx.subnet_->getID();
4436  }
4437  if ((!ctx.hwaddr_ && lease->hwaddr_) ||
4438  (ctx.hwaddr_ &&
4439  (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
4440  changed = true;
4441  lease->hwaddr_ = ctx.hwaddr_;
4442  }
4443  if (ctx.subnet_->getMatchClientId() && ctx.clientid_) {
4444  if (!lease->client_id_ || (*ctx.clientid_ != *lease->client_id_)) {
4445  changed = true;
4446  lease->client_id_ = ctx.clientid_;
4447  }
4448  } else if (lease->client_id_) {
4449  changed = true;
4450  lease->client_id_ = ClientIdPtr();
4451  }
4452  lease->cltt_ = time(NULL);
4453 
4454  // Get the context appropriate valid lifetime.
4455  lease->valid_lft_ = getValidLft(ctx);
4456 
4457  // Reduced valid lifetime is a significant change.
4458  if (lease->valid_lft_ < lease->current_valid_lft_) {
4459  changed = true;
4460  }
4461 
4462  if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
4463  (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
4464  (lease->hostname_ != ctx.hostname_)) {
4465  changed = true;
4466  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4467  lease->fqdn_rev_ = ctx.rev_dns_update_;
4468  lease->hostname_ = ctx.hostname_;
4469  }
4470 
4471  // Add(update) the extended information on the lease.
4472  if (updateLease4ExtendedInfo(lease, ctx)) {
4473  changed = true;
4474  }
4475 
4476  return (changed);
4477 }
4478 
4479 bool
4481  const AllocEngine::ClientContext4& ctx) const {
4482  bool changed = false;
4483 
4484  // If storage is not enabled then punt.
4485  if (!ctx.subnet_->getStoreExtendedInfo()) {
4486  return (changed);
4487  }
4488 
4489  // Look for relay agent information option (option 82)
4490  OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS);
4491  if (!rai) {
4492  // Pkt4 doesn't have it, so nothing to store (or update).
4493  return (changed);
4494  }
4495 
4496  // Create a StringElement with the hex string for relay-agent-info.
4497  ElementPtr relay_agent(new StringElement(rai->toHexString()));
4498 
4499  // Now we wrap the agent info in a map. This allows for future expansion.
4500  ElementPtr extended_info = Element::createMap();
4501  extended_info->set("relay-agent-info", relay_agent);
4502 
4503  // Get a writable copy of the lease's current user context.
4504  ElementPtr user_context;
4505  if (lease->getContext()) {
4506  user_context = UserContext::toElement(lease->getContext());
4507  } else {
4508  user_context = Element::createMap();
4509  }
4510 
4511  // Add/replace the extended info entry.
4512  ConstElementPtr old_extended_info = user_context->get("ISC");
4513  if (!old_extended_info || (*old_extended_info != *extended_info)) {
4514  changed = true;
4515  user_context->set("ISC", extended_info);
4516  }
4517 
4518  // Update the lease's user_context.
4519  lease->setContext(user_context);
4520 
4521  return (changed);
4522 }
4523 
4524 bool
4526  const AllocEngine::ClientContext6& ctx) const {
4527  bool changed = false;
4528 
4529  // If storage is not enabled then punt.
4530  if (!ctx.subnet_->getStoreExtendedInfo()) {
4531  return (changed);
4532  }
4533 
4534  // If we do not have relay information, then punt.
4535  if (ctx.query_->relay_info_.empty()) {
4536  return (changed);
4537  }
4538 
4539  // We need to convert the vector of RelayInfo instances in
4540  // into an Element hierarchy like this:
4541  // "relay-info": [
4542  // {
4543  // "hop": 123,
4544  // "link": "2001:db8::1",
4545  // "peer": "2001:db8::2",
4546  // "options": "0x..."
4547  // },..]
4548  //
4549  ElementPtr relay_list = Element::createList();
4550  for (auto relay : ctx.query_->relay_info_) {
4551  ElementPtr relay_elem = Element::createMap();
4552  relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
4553  relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
4554  relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
4555 
4556  // If there are relay options, we'll pack them into a buffer and then
4557  // convert that into a hex string. If there are no options, we omit
4558  // then entry.
4559  if (!relay.options_.empty()) {
4560  OutputBuffer buf(128);
4561  LibDHCP::packOptions6(buf, relay.options_);
4562 
4563  if (buf.getLength() > 0) {
4564  const uint8_t* cp = static_cast<const uint8_t*>(buf.getData());
4565  std::vector<uint8_t>bytes;
4566  std::stringstream ss;
4567 
4568  bytes.assign(cp, cp + buf.getLength());
4569  ss << "0x" << encode::encodeHex(bytes);
4570  relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
4571  }
4572  }
4573 
4574  relay_list->add(relay_elem);
4575  }
4576 
4577  // Now we wrap the list of relays in a map. This allows for future expansion.
4578  ElementPtr extended_info = Element::createMap();
4579  extended_info->set("relays", relay_list);
4580 
4581  // Get a writable copy of the lease's current user context.
4582  ElementPtr user_context;
4583  if (lease->getContext()) {
4584  user_context = UserContext::toElement(lease->getContext());
4585  } else {
4586  user_context = Element::createMap();
4587  }
4588 
4589  // Add/replace the extended info entry.
4590  ConstElementPtr old_extended_info = user_context->get("ISC");
4591  if (!old_extended_info || (*old_extended_info != *extended_info)) {
4592  changed = true;
4593  user_context->set("ISC", extended_info);
4594  }
4595 
4596  // Update the lease's user_context.
4597  lease->setContext(user_context);
4598 
4599  return (changed);
4600 }
4601 
4602 void
4603 AllocEngine::setLeaseReusable(const Lease4Ptr& lease,
4604  const ClientContext4& ctx) const {
4605  // Sanity.
4606  lease->reuseable_valid_lft_ = 0;
4607  const Subnet4Ptr& subnet = ctx.subnet_;
4608  if (!subnet) {
4609  return;
4610  }
4611  if (lease->state_ != Lease::STATE_DEFAULT) {
4612  return;
4613  }
4614 
4615  // Always reuse infinite lifetime leases.
4616  if (lease->valid_lft_ == Lease::INFINITY_LFT) {
4617  lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
4618  return;
4619  }
4620 
4621  // Refuse time not going forward.
4622  if (lease->cltt_ < lease->current_cltt_) {
4623  return;
4624  }
4625 
4626  uint32_t age = lease->cltt_ - lease->current_cltt_;
4627  // Already expired.
4628  if (age >= lease->current_valid_lft_) {
4629  return;
4630  }
4631 
4632  // Try cache max age.
4633  uint32_t max_age = 0;
4634  if (!subnet->getCacheMaxAge().unspecified()) {
4635  max_age = subnet->getCacheMaxAge().get();
4636  if ((max_age == 0) || (age > max_age)) {
4637  return;
4638  }
4639  }
4640 
4641  // Try cache threshold.
4642  if (!subnet->getCacheThreshold().unspecified()) {
4643  double threshold = subnet->getCacheThreshold().get();
4644  if ((threshold <= 0.) || (threshold > 1.)) {
4645  return;
4646  }
4647  max_age = lease->valid_lft_ * threshold;
4648  if (age > max_age) {
4649  return;
4650  }
4651  }
4652 
4653  // No cache.
4654  if (max_age == 0) {
4655  return;
4656  }
4657 
4658  // Seems to be reusable.
4659  lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
4660 }
4661 
4662 void
4663 AllocEngine::setLeaseReusable(const Lease6Ptr& lease,
4664  uint32_t current_preferred_lft,
4665  const ClientContext6& ctx) const {
4666  // Sanity.
4667  lease->reuseable_valid_lft_ = 0;
4668  lease->reuseable_preferred_lft_ = 0;
4669  const Subnet6Ptr& subnet = ctx.subnet_;
4670  if (!subnet) {
4671  return;
4672  }
4673  if (lease->state_ != Lease::STATE_DEFAULT) {
4674  return;
4675  }
4676 
4677  // Refuse time not going forward.
4678  if (lease->cltt_ < lease->current_cltt_) {
4679  return;
4680  }
4681 
4682  uint32_t age = lease->cltt_ - lease->current_cltt_;
4683  // Already expired.
4684  if (age >= lease->current_valid_lft_) {
4685  return;
4686  }
4687 
4688  // Try cache max age.
4689  uint32_t max_age = 0;
4690  if (!subnet->getCacheMaxAge().unspecified()) {
4691  max_age = subnet->getCacheMaxAge().get();
4692  if ((max_age == 0) || (age > max_age)) {
4693  return;
4694  }
4695  }
4696 
4697  // Try cache threshold.
4698  if (!subnet->getCacheThreshold().unspecified()) {
4699  double threshold = subnet->getCacheThreshold().get();
4700  if ((threshold <= 0.) || (threshold > 1.)) {
4701  return;
4702  }
4703  max_age = lease->valid_lft_ * threshold;
4704  if (age > max_age) {
4705  return;
4706  }
4707  }
4708 
4709  // No cache.
4710  if (max_age == 0) {
4711  return;
4712  }
4713 
4714  // Seems to be reusable.
4715  if ((current_preferred_lft == Lease::INFINITY_LFT) ||
4716  (current_preferred_lft == 0)) {
4717  // Keep these values.
4718  lease->reuseable_preferred_lft_ = current_preferred_lft;
4719  } else if (current_preferred_lft > age) {
4720  lease->reuseable_preferred_lft_ = current_preferred_lft - age;
4721  } else {
4722  // Can be a misconfiguration so stay safe...
4723  return;
4724  }
4725  if (lease->current_valid_lft_ == Lease::INFINITY_LFT) {
4726  lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
4727  } else {
4728  lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
4729  }
4730 }
4731 
4732 } // namespace dhcp
4733 } // namespace isc
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_SELECT_SKIP
static HostMgr & instance()
Returns a sole instance of the HostMgr.
Definition: host_mgr.cc:105
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_COMPLETE
bool fake_allocation_
Indicates if this is a real or fake allocation.
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_EXTEND_LEASE
virtual void updateLease4(const Lease4Ptr &lease4)=0
Updates IPv4 lease.
bool updateLease4ExtendedInfo(const Lease4Ptr &lease, const ClientContext4 &ctx) const
Stores additional client query parameters on a V4 lease.
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_COMPLETE
const int ALLOC_ENGINE_DBG_TRACE
Logging levels for the AllocEngine.
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...
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:394
Defines a single hint.
Definition: alloc_engine.h:324
A generic exception that is thrown when a function is not implemented.
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
static const uint32_t STATE_EXPIRED_RECLAIMED
Expired and reclaimed lease.
Definition: lease.h:79
Structure that holds a lease for IPv4 address.
Definition: lease.h:294
virtual ConstHostCollection getAll4(const SubnetID &subnet_id) const
Return all hosts in a DHCPv4 subnet.
Definition: host_mgr.cc:129
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_IN_USE
Abstract Lease Manager.
Definition: lease_mgr.h:222
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
ConstHostPtr currentHost() const
Returns host from the most preferred subnet.
static void packOptions6(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv6 options in a buffer.
Definition: libdhcp++.cc:862
static void findReservation(ClientContext6 &ctx)
Attempts to find appropriate host reservation.
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
isc::util::ReadWriteMutex rw_mutex_
The read-write mutex.
boost::shared_ptr< Pool6 > Pool6Ptr
a pointer an IPv6 Pool
Definition: pool.h:312
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_START
static std::string typeToText(Type type)
returns text representation of a lease type
Definition: lease.cc:52
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_ERROR
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_TIMEOUT
void addAllocatedResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128)
Convenience method adding allocated prefix or address.
static IPv6Resrv makeIPv6Resrv(const Lease6 &lease)
Creates an IPv6Resrv instance from a Lease6.
Definition: alloc_engine.h:990
static isc::asiolink::IOAddress increaseAddress(const isc::asiolink::IOAddress &address, bool prefix, const uint8_t prefix_len)
Returns the next address or prefix.
const isc::log::MessageID ALLOC_ENGINE_V6_RENEW_REMOVE_RESERVED
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_SLOW
long getTotalMilliseconds() const
Retrieves the total measured duration in milliseconds.
Definition: stopwatch.cc:60
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_RENEW_SKIP
static ConstHostPtr findGlobalReservation(ClientContext6 &ctx)
Attempts to find the host reservation for the client.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL
Write mutex RAII handler.
const isc::log::MessageID ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED
const isc::log::MessageID ALLOC_ENGINE_V6_HR_ADDR_GRANTED
const int DHCPSRV_DBG_HOOKS
Definition: dhcpsrv_log.h:46
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_UNRESERVED
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_LEASES_HR
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition: lease.h:487
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const isc::log::MessageID ALLOC_ENGINE_V4_NO_MORE_EXPIRED_LEASES
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:522
const isc::log::MessageID ALLOC_ENGINE_V4_DISCOVER_HR
Resource race avoidance RAII handler for DHCPv4.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
the lease contains IPv6 prefix (for prefix delegation)
Definition: lease.h:53
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
const int ALLOC_ENGINE_DBG_TRACE_DETAIL
Record detailed traces.
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs)=0
Deletes all expired and reclaimed DHCPv4 leases.
DuidPtr duid_
Client identifier.
Definition: alloc_engine.h:485
std::vector< PoolPtr > PoolCollection
a container for either IPv4 or IPv6 Pools
Definition: pool.h:508
Lease4Ptr allocateLease4(ClientContext4 &ctx)
Returns IPv4 lease.
A configuration holder for IPv4 subnet.
Definition: subnet.h:529
Forward declaration to OptionInt.
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_TIMEOUT
boost::shared_ptr< Pool > PoolPtr
a pointer to either IPv4 or IPv6 Pool
Definition: pool.h:505
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_ADDRESS_RESERVED
IPv6 reservation for a host.
Definition: host.h:161
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_ERROR
AllocEngine(AllocType engine_type, uint64_t attempts, bool ipv6=true)
Constructor.
boost::shared_ptr< OptionUint32 > OptionUint32Ptr
Definition: option_int.h:35
hooks::CalloutHandlePtr callout_handle_
Callout handle associated with the client's message.
DdnsParamsPtr getDdnsParams()
Returns the set of DDNS behavioral parameters based on the selected subnet.
RAII object enabling copying options retrieved from the packet.
Definition: pkt.h:40
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_SHARED_NETWORK
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_LEASES_NO_HR
virtual ConstHostCollection getAll(const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Return all hosts connected to any subnet for which reservations have been made using a specified iden...
Definition: host_mgr.cc:114
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_NEW_LEASE
IdentifierList host_identifiers_
A list holding host identifiers extracted from a message received by the server.
An exception that is thrown when allocation module fails (e.g.
Definition: alloc_engine.h:43
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_RECOVER_SKIP
virtual void getExpiredLeases6(Lease6Collection &expired_leases, const size_t max_leases) const =0
Returns a collection of expired DHCPv6 leases.
Statistics Manager class.
boost::shared_ptr< DdnsParams > DdnsParamsPtr
Defines a pointer for DdnsParams instances.
Definition: srv_config.h:162
asiolink::IOAddress requested_address_
An address that the client desires.
Resource race avoidance RAII handler.
void deleteExpiredReclaimedLeases4(const uint32_t secs)
Deletes reclaimed leases expired more than specified amount of time ago.
const isc::log::MessageID ALLOC_ENGINE_V6_HR_PREFIX_GRANTED
void addNewResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128)
Convenience method adding new prefix or address.
AllocType
Specifies allocation type.
Definition: alloc_engine.h:268
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_SHARED_NETWORK
bool updateLease6ExtendedInfo(const Lease6Ptr &lease, const ClientContext6 &ctx) const
Stores additional client query parameters on a V6 lease.
bool tryLock(Lease::Type type, const asiolink::IOAddress &addr)
Tries to acquires a resource.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
const isc::log::MessageID ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
void stop()
Stops the stopwatch.
Definition: stopwatch.cc:35
void queueNCR(const NameChangeType &chg_type, const Lease4Ptr &lease)
Creates name change request from the DHCPv4 lease.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
Subnet6Ptr subnet_
Subnet selected for the client by the server.
Definition: alloc_engine.h:477
ResourceContainer allocated_resources_
Holds addresses and prefixes allocated for all IAs.
Definition: alloc_engine.h:521
Definition: edns.h:19
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:243
Pool information for IPv6 addresses and prefixes.
Definition: pool.h:321
std::list< ClientClass >::const_iterator const_iterator
Type of iterators.
Definition: classify.h:47
const int ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA
Records detailed results of various operations.
Lease4Ptr conflicting_lease_
A pointer to the object representing a lease in conflict.
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_RECOVER_SKIP
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1097
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:283
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL
const isc::log::MessageID ALLOC_ENGINE_V6_HINT_RESERVED
const_iterator cend() const
Iterator to the past the end element.
Definition: classify.h:91
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:791
Lease4Ptr old_lease_
A pointer to an old lease that the client had before update.
ConstHostPtr currentHost() const
Returns host for currently selected subnet.
Utility class to measure code execution times.
Definition: stopwatch.h:35
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_LEASE_DATA
std::map< SubnetID, ConstHostPtr > hosts_
Holds a map of hosts belonging to the client within different subnets.
Definition: alloc_engine.h:499
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_OUT_OF_POOL
Random allocator that picks address randomly.
Definition: alloc_engine.h:238
Context information for the DHCPv6 leases allocation.
Definition: alloc_engine.h:459
const isc::log::MessageID ALLOC_ENGINE_V4_REUSE_EXPIRED_LEASE_DATA
Subnet6Ptr host_subnet_
Subnet from which host reservations should be retrieved.
Definition: alloc_engine.h:482
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
Notes: IntElement type is changed to int64_t.
Definition: data.h:544
Lease6Collection renewLeases6(ClientContext6 &ctx)
Renews existing DHCPv6 leases for a given IA.
Wrapper class around callout handle which automatically resets handle's state.
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.
Subnet4Ptr subnet_
Subnet selected for the client by the server.
IPv4 lease.
Definition: lease.h:54
Pkt4Ptr query_
A pointer to the client's message.
const isc::log::MessageID ALLOC_ENGINE_V4_LEASE_RECLAIM
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_START
CalloutNextStep
Specifies allowed next steps.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
virtual ConstHostPtr get6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv6 subnet.
Definition: host_mgr.cc:498
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:788
void addHostIdentifier(const Host::IdentifierType &id_type, const std::vector< uint8_t > &identifier)
Convenience function adding host identifier into host_identifiers_ list.
Definition: alloc_engine.h:646
bool hasGlobalReservation(const IPv6Resrv &resv) const
Determines if a global reservation exists.
Structure that holds a lease for IPv6 address and/or prefix.
Definition: lease.h:503
bool getDisableSingleQuery() const
Returns the disable single query flag.
Definition: host_mgr.h:596
Address/prefix allocator that iterates over all addresses.
Definition: alloc_engine.h:148
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.
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_NO_POOLS
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_SLOW
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition: lease.h:640
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const =0
Returns existing IPv6 lease for a given IPv6 address.
const isc::log::MessageID ALLOC_ENGINE_V6_REUSE_EXPIRED_LEASE_DATA
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition: duid.h:103
Type
Type of the reservation.
Definition: host.h:167
virtual void getExpiredLeases4(Lease4Collection &expired_leases, const size_t max_leases) const =0
Returns a collection of expired DHCPv4 leases.
the lease contains temporary IPv6 address
Definition: lease.h:52
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition: srv_config.h:46
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
void addHostIdentifier(const Host::IdentifierType &id_type, const std::vector< uint8_t > &identifier)
Convenience function adding host identifier into host_identifiers_ list.
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
the lease contains non-temporary IPv6 address
Definition: lease.h:51
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_SUBNET
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_EXISTING_LEASE
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
const isc::log::MessageID ALLOC_ENGINE_V6_RENEW_HR
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_CLASSES
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-dhcp-ddns.
const isc::log::MessageID ALLOC_ENGINE_V6_EXPIRED_HINT_RESERVED
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:585
Lease6Collection new_leases_
A collection of newly allocated leases.
Definition: alloc_engine.h:524
T get(T hint) const
Returns value with a hint.
Definition: triplet.h:99
string encodeHex(const vector< uint8_t > &binary)
Encode binary data in the base16 ('hex') format.
Definition: base_n.cc:469
ClientIdPtr clientid_
Client identifier from the DHCP message.
uint32_t iaid_
The IAID field from IA_NA or IA_PD that is being processed.
Definition: alloc_engine.h:533
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_NO_V6_HR
const isc::log::MessageID ALLOC_ENGINE_V6_LEASE_RECLAIM
Lease::Type type_
Lease type (IA or PD)
Definition: alloc_engine.h:536
boost::shared_ptr< Allocator > AllocatorPtr
defines a pointer to allocator
Definition: alloc_engine.h:140
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_COMPLETE
Address/prefix allocator that gets an address based on a hash.
Definition: alloc_engine.h:208
Pkt6Ptr query_
A pointer to the client's message.
Definition: alloc_engine.h:467
std::string logFormatTotalDuration() const
Returns the total measured duration in the format directly usable in the log messages.
Definition: stopwatch.cc:80
bool isNewResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128) const
Checks if specified address or prefix was new.
Lease6Collection allocateLeases6(ClientContext6 &ctx)
Allocates IPv6 leases for a given IA container.
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_NO_POOLS
CtrlAgentHooks Hooks
Lease4Ptr new_lease_
A pointer to a newly allocated lease.
static const uint32_t INFINITY_LFT
Infinity (means static, i.e. never expire)
Definition: lease.h:38
const isc::log::MessageID ALLOC_ENGINE_V4_LEASES_RECLAMATION_COMPLETE
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_NEW_LEASE_DATA
HashedAllocator(Lease::Type type)
Default constructor (does nothing)
a common structure for IPv4 and IPv6 leases
Definition: lease.h:35
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_ALLOC_UNRESERVED
Type
Type of lease or pool.
Definition: lease.h:50
const isc::log::MessageID DHCPSRV_HOOK_LEASE4_SELECT_SKIP
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_REMOVE_LEASE
bool fwd_dns_update_
Perform forward DNS update.
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_SUBNET
isc::log::Logger alloc_engine_logger("alloc-engine")
Logger for the AllocEngine.
const isc::log::MessageID DHCPSRV_HOOK_LEASE6_EXTEND_SKIP
Base class for all address/prefix allocation algorithms.
Definition: alloc_engine.h:70
A generic exception that is thrown if a function is called in a prohibited way.
static uint32_t getValidLft(const ClientContext4 &ctx)
Returns the valid lifetime based on the v4 context.
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_REQUESTED_LEASE
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
void deleteExpiredReclaimedLeases6(const uint32_t secs)
Deletes reclaimed leases expired more than specified amount of time ago.
const isc::log::MessageID ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE
virtual bool addLease(const Lease4Ptr &lease)=0
Adds an IPv4 lease.
IAContext & currentIA()
Returns IA specific context for the currently processed IA.
Definition: alloc_engine.h:656
static const uint32_t STATE_DEFAULT
A lease in the default state.
Definition: lease.h:73
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
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.
const isc::log::MessageID ALLOC_ENGINE_V4_DECLINED_RECOVERED
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const =0
Returns an IPv4 lease for specified IPv4 address.
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition: cfgmgr.cc:66
virtual bool deleteLease(const Lease4Ptr &lease)=0
Deletes an IPv4 lease.
virtual ConstHostPtr get4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv4 subnet.
Definition: host_mgr.cc:368
const isc::log::MessageID ALLOC_ENGINE_LEASE_RECLAIMED
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
static const uint32_t STATE_DECLINED
Declined lease.
Definition: lease.h:76
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_PICK_ADDRESS
boost::shared_ptr< Subnet6 > Subnet6Ptr
A pointer to a Subnet6 object.
Definition: subnet.h:670
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_ERROR
const isc::log::MessageID ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE
bool empty() const
Check if classes is empty.
Definition: classify.h:73
static isc::asiolink::IOAddress increasePrefix(const isc::asiolink::IOAddress &prefix, const uint8_t prefix_len)
Returns the next prefix.
Definition: alloc_engine.cc:98
std::pair< IPv6Resrv::Type, IPv6Resrv > IPv6ResrvTuple
Definition: host.h:242
HWAddrPtr hwaddr_
HW address from the DHCP message.
bool isAllocated(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128) const
Checks if specified address or prefix was allocated.
static LeaseMgr & instance()
Return current lease manager.
T getValue() const
Return option value.
Definition: option_int.h:190
virtual Lease6Collection getLeases6(Lease::Type type, const DUID &duid, uint32_t iaid) const =0
Returns existing IPv6 leases for a given DUID+IA combination.
RandomAllocator(Lease::Type type)
Default constructor (does nothing)
Context information for the DHCPv4 lease allocation.
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
const_iterator cbegin() const
Iterator to the first element.
Definition: classify.h:86
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_INVALID
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition: lease.h:492
virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs)=0
Deletes all expired and reclaimed DHCPv6 leases.
This file provides the classes needed to embody, compose, and decompose DNS update requests that are ...
std::pair< Host::IdentifierType, std::vector< uint8_t > > IdentifierPair
A tuple holding host identifier type and value.
Definition: alloc_engine.h:432
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_ALLOC_REQUESTED
Container for storing client class names.
Definition: classify.h:43
boost::shared_ptr< Option6IAAddr > Option6IAAddrPtr
A pointer to the isc::dhcp::Option6IAAddr object.
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_HR_LEASE_EXISTS
bool rev_dns_update_
Perform reverse DNS update.
boost::shared_ptr< Subnet > SubnetPtr
A generic pointer to either Subnet4 or Subnet6 object.
Definition: subnet.h:513
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_USE_HR
const isc::log::MessageID ALLOC_ENGINE_V6_EXTEND_LEASE
virtual void updateLease6(const Lease6Ptr &lease6)=0
Updates IPv6 lease.
const isc::log::MessageID ALLOC_ENGINE_V6_NO_MORE_EXPIRED_LEASES
DdnsParamsPtr getDdnsParams()
Returns the set of DDNS behavioral parameters based on the selected subnet.
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_NO_LEASES_HR
AllocatorPtr getAllocator(Lease::Type type)
Returns allocator for a given pool type.
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
IdentifierList host_identifiers_
A list holding host identifiers extracted from a message received by the server.
Definition: alloc_engine.h:492
const isc::log::MessageID ALLOC_ENGINE_V4_DISCOVER_ADDRESS_CONFLICT
ClientContext6()
Default constructor.
bool tryLock4(const asiolink::IOAddress &addr)
Tries to acquires a resource.
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition: classify.cc:40
const isc::log::MessageID ALLOC_ENGINE_V6_DECLINED_RECOVERED