Kea  2.1.6-git
alloc_engine.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2022 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),
460  early_global_reservations_lookup_(false), subnet_(), host_subnet_(),
461  duid_(), hwaddr_(), host_identifiers_(), hosts_(),
462  fwd_dns_update_(false), rev_dns_update_(false), hostname_(),
463  callout_handle_(), ias_(), ddns_params_() {
464 }
465 
467  const DuidPtr& duid,
468  const bool fwd_dns,
469  const bool rev_dns,
470  const std::string& hostname,
471  const bool fake_allocation,
472  const Pkt6Ptr& query,
473  const CalloutHandlePtr& callout_handle)
474  : query_(query), fake_allocation_(fake_allocation),
476  duid_(duid), hwaddr_(), host_identifiers_(), hosts_(),
477  fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname),
478  callout_handle_(callout_handle), allocated_resources_(), new_leases_(),
479  ias_(), ddns_params_() {
480 
481  // Initialize host identifiers.
482  if (duid) {
483  addHostIdentifier(Host::IDENT_DUID, duid->getDuid());
484  }
485 }
486 
488  : iaid_(0), type_(Lease::TYPE_NA), hints_(), old_leases_(),
489  changed_leases_(), new_resources_(), ia_rsp_() {
490 }
491 
492 void
495  const uint8_t prefix_len,
496  const uint32_t preferred,
497  const uint32_t valid) {
498  hints_.push_back(Resource(prefix, prefix_len, preferred, valid));
499 }
500 
501 void
504  if (!iaaddr) {
505  isc_throw(BadValue, "IAADDR option pointer is null.");
506  }
507  addHint(iaaddr->getAddress(), 128,
508  iaaddr->getPreferred(), iaaddr->getValid());
509 }
510 
511 void
514  if (!iaprefix) {
515  isc_throw(BadValue, "IAPREFIX option pointer is null.");
516  }
517  addHint(iaprefix->getAddress(), iaprefix->getLength(),
518  iaprefix->getPreferred(), iaprefix->getValid());
519 }
520 
521 void
524  const uint8_t prefix_len) {
525  static_cast<void>(new_resources_.insert(Resource(prefix, prefix_len)));
526 }
527 
528 bool
531  const uint8_t prefix_len) const {
532  return (static_cast<bool>(new_resources_.count(Resource(prefix,
533  prefix_len))));
534 }
535 
536 void
539  const uint8_t prefix_len) {
540  static_cast<void>(allocated_resources_.insert(Resource(prefix,
541  prefix_len)));
542 }
543 
544 bool
546 isAllocated(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const {
547  return (static_cast<bool>
548  (allocated_resources_.count(Resource(prefix, prefix_len))));
549 }
550 
554  if (subnet && subnet->getReservationsInSubnet()) {
555  auto host = hosts_.find(subnet->getID());
556  if (host != hosts_.cend()) {
557  return (host->second);
558  }
559  }
560 
561  return (globalHost());
562 }
563 
567  if (subnet && subnet_->getReservationsGlobal()) {
568  auto host = hosts_.find(SUBNET_ID_GLOBAL);
569  if (host != hosts_.cend()) {
570  return (host->second);
571  }
572  }
573 
574  return (ConstHostPtr());
575 }
576 
577 bool
579  ConstHostPtr ghost = globalHost();
580  return (ghost && ghost->hasReservation(resv));
581 }
582 
585  // We already have it return it unless the context subnet has changed.
586  if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
587  return (ddns_params_);
588  }
589 
590  // Doesn't exist yet or is stale, (re)create it.
591  if (subnet_) {
592  ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
593  return (ddns_params_);
594  }
595 
596  // Asked for it without a subnet? This case really shouldn't occur but
597  // for now let's return an instance with default values.
598  return (DdnsParamsPtr(new DdnsParams()));
599 }
600 
601 void
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  // If already done just return.
612  !subnet->getReservationsInSubnet()) {
613  return;
614  }
615 
616  // @todo: This code can be trivially optimized.
618  subnet->getReservationsGlobal()) {
619  ConstHostPtr ghost = findGlobalReservation(ctx);
620  if (ghost) {
621  ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
622 
623  // If we had only to fetch global reservations it is done.
624  if (!subnet->getReservationsInSubnet()) {
625  return;
626  }
627  }
628  }
629 
630  std::map<SubnetID, ConstHostPtr> host_map;
631  SharedNetwork6Ptr network;
632  subnet->getSharedNetwork(network);
633 
634  // If the subnet belongs to a shared network it is usually going to be
635  // more efficient to make a query for all reservations for a particular
636  // client rather than a query for each subnet within this shared network.
637  // The only case when it is going to be less efficient is when there are
638  // more host identifier types in use than subnets within a shared network.
639  // As it breaks RADIUS use of host caching this can be disabled by the
640  // host manager.
641  const bool use_single_query = network &&
643  (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
644 
645  if (use_single_query) {
646  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
647  ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
648  &id_pair.second[0],
649  id_pair.second.size());
650  // Store the hosts in the temporary map, because some hosts may
651  // belong to subnets outside of the shared network. We'll need
652  // to eliminate them.
653  for (auto host = hosts.begin(); host != hosts.end(); ++host) {
654  if ((*host)->getIPv6SubnetID() != SUBNET_ID_GLOBAL) {
655  host_map[(*host)->getIPv6SubnetID()] = *host;
656  }
657  }
658  }
659  }
660 
661  // We can only search for the reservation if a subnet has been selected.
662  while (subnet) {
663 
664  // Only makes sense to get reservations if the client has access
665  // to the class and host reservations are enabled for this subnet.
666  if (subnet->clientSupported(ctx.query_->getClasses()) &&
667  subnet->getReservationsInSubnet()) {
668  // Iterate over configured identifiers in the order of preference
669  // and try to use each of them to search for the reservations.
670  if (use_single_query) {
671  if (host_map.count(subnet->getID()) > 0) {
672  ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
673  }
674  } else {
675  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
676  // Attempt to find a host using a specified identifier.
677  ConstHostPtr host = HostMgr::instance().get6(subnet->getID(),
678  id_pair.first,
679  &id_pair.second[0],
680  id_pair.second.size());
681  // If we found matching host for this subnet.
682  if (host) {
683  ctx.hosts_[subnet->getID()] = host;
684  break;
685  }
686  }
687  }
688  }
689 
690  // We need to get to the next subnet if this is a shared network. If it
691  // is not (a plain subnet), getNextSubnet will return NULL and we're
692  // done here.
693  subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
694  }
695 }
696 
699  ConstHostPtr host;
700  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
701  // Attempt to find a host using a specified identifier.
702  host = HostMgr::instance().get6(SUBNET_ID_GLOBAL, id_pair.first,
703  &id_pair.second[0], id_pair.second.size());
704 
705  // If we found matching global host we're done.
706  if (host) {
707  break;
708  }
709  }
710 
711  return (host);
712 }
713 
716 
717  try {
718  if (!ctx.subnet_) {
719  isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation");
720  } else
721  if (!ctx.duid_) {
722  isc_throw(InvalidOperation, "DUID is mandatory for IPv6 lease allocation");
723  }
724 
725  // Check if there are existing leases for that shared network and
726  // DUID/IAID.
727  Subnet6Ptr subnet = ctx.subnet_;
728  Lease6Collection all_leases =
730  *ctx.duid_,
731  ctx.currentIA().iaid_);
732 
733  // Iterate over the leases and eliminate those that are outside of
734  // our shared network.
735  Lease6Collection leases;
736  while (subnet) {
737  for (auto l : all_leases) {
738  if ((l)->subnet_id_ == subnet->getID()) {
739  leases.push_back(l);
740  }
741  }
742 
743  subnet = subnet->getNextSubnet(ctx.subnet_);
744  }
745 
746  // Now do the checks:
747  // Case 1. if there are no leases, and there are reservations...
748  // 1.1. are the reserved addresses are used by someone else?
749  // yes: we have a problem
750  // no: assign them => done
751  // Case 2. if there are leases and there are no reservations...
752  // 2.1 are the leases reserved for someone else?
753  // yes: release them, assign something else
754  // no: renew them => done
755  // Case 3. if there are leases and there are reservations...
756  // 3.1 are the leases matching reservations?
757  // yes: renew them => done
758  // no: release existing leases, assign new ones based on reservations
759  // Case 4/catch-all. if there are no leases and no reservations...
760  // assign new leases
761 
762  // Case 1: There are no leases and there's a reservation for this host.
763  if (leases.empty() && !ctx.hosts_.empty()) {
764 
767  .arg(ctx.query_->getLabel());
768 
769  // Try to allocate leases that match reservations. Typically this will
770  // succeed, except cases where the reserved addresses are used by
771  // someone else.
772  allocateReservedLeases6(ctx, leases);
773 
774  leases = updateLeaseData(ctx, leases);
775 
776  // If not, we'll need to continue and will eventually fall into case 4:
777  // getting a regular lease. That could happen when we're processing
778  // request from client X, there's a reserved address A for X, but
779  // A is currently used by client Y. We can't immediately reassign A
780  // from X to Y, because Y keeps using it, so X would send Decline right
781  // away. Need to wait till Y renews, then we can release A, so it
782  // will become available for X.
783 
784  // Case 2: There are existing leases and there are no reservations.
785  //
786  // There is at least one lease for this client and there are no reservations.
787  // We will return these leases for the client, but we may need to update
788  // FQDN information.
789  } else if (!leases.empty() && ctx.hosts_.empty()) {
790 
793  .arg(ctx.query_->getLabel());
794 
795  // Check if the existing leases are reserved for someone else.
796  // If they're not, we're ok to keep using them.
797  removeNonmatchingReservedLeases6(ctx, leases);
798 
799  leases = updateLeaseData(ctx, leases);
800 
801  // If leases are empty at this stage, it means that we used to have
802  // leases for this client, but we checked and those leases are reserved
803  // for someone else, so we lost them. We will need to continue and
804  // will finally end up in case 4 (no leases, no reservations), so we'll
805  // assign something new.
806 
807  // Case 3: There are leases and there are reservations.
808  } else if (!leases.empty() && !ctx.hosts_.empty()) {
809 
812  .arg(ctx.query_->getLabel());
813 
814  // First, check if have leases matching reservations, and add new
815  // leases if we don't have them.
816  allocateReservedLeases6(ctx, leases);
817 
818  // leases now contain both existing and new leases that were created
819  // from reservations.
820 
821  // Second, let's remove leases that are reserved for someone else.
822  // This applies to any existing leases. This will not happen frequently,
823  // but it may happen with the following chain of events:
824  // 1. client A gets address X;
825  // 2. reservation for client B for address X is made by a administrator;
826  // 3. client A reboots
827  // 4. client A requests the address (X) he got previously
828  removeNonmatchingReservedLeases6(ctx, leases);
829 
830  // leases now contain existing and new leases, but we removed those
831  // leases that are reserved for someone else (non-matching reserved).
832 
833  // There's one more check to do. Let's remove leases that are not
834  // matching reservations, i.e. if client X has address A, but there's
835  // a reservation for address B, we should release A and reassign B.
836  // Caveat: do this only if we have at least one reserved address.
837  removeNonreservedLeases6(ctx, leases);
838 
839  // All checks are done. Let's hope we have some leases left.
840 
841  // Update any leases we have left.
842  leases = updateLeaseData(ctx, leases);
843 
844  // If we don't have any leases at this stage, it means that we hit
845  // one of the following cases:
846  // - we have a reservation, but it's not for this IAID/ia-type and
847  // we had to return the address we were using
848  // - we have a reservation for this iaid/ia-type, but the reserved
849  // address is currently used by someone else. We can't assign it
850  // yet.
851  // - we had an address, but we just discovered that it's reserved for
852  // someone else, so we released it.
853  }
854 
855  if (leases.empty()) {
856  // Case 4/catch-all: One of the following is true:
857  // - we don't have leases and there are no reservations
858  // - we used to have leases, but we lost them, because they are now
859  // reserved for someone else
860  // - we have a reservation, but it is not usable yet, because the address
861  // is still used by someone else
862  //
863  // In any case, we need to go through normal lease assignment process
864  // for now. This is also a catch-all or last resort approach, when we
865  // couldn't find any reservations (or couldn't use them).
866 
869  .arg(ctx.query_->getLabel());
870 
871  leases = allocateUnreservedLeases6(ctx);
872  }
873 
874  if (!leases.empty()) {
875  // If there are any leases allocated, let's store in them in the
876  // IA context so as they are available when we process subsequent
877  // IAs.
878  BOOST_FOREACH(Lease6Ptr lease, leases) {
879  ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
880  ctx.new_leases_.push_back(lease);
881  }
882  return (leases);
883  }
884 
885  } catch (const isc::Exception& e) {
886 
887  // Some other error, return an empty lease.
889  .arg(ctx.query_->getLabel())
890  .arg(e.what());
891  }
892 
893  return (Lease6Collection());
894 }
895 
897 AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
898 
899  AllocatorPtr allocator = getAllocator(ctx.currentIA().type_);
900 
901  if (!allocator) {
902  isc_throw(InvalidOperation, "No allocator specified for "
904  }
905 
906  Lease6Collection leases;
907 
909  if (!ctx.currentIA().hints_.empty()) {
911  hint = ctx.currentIA().hints_[0].getAddress();
912  }
913 
914  Subnet6Ptr original_subnet = ctx.subnet_;
915  Subnet6Ptr subnet = ctx.subnet_;
916 
917  Pool6Ptr pool;
918 
919  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
920 
921  for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
922 
923  if (!subnet->clientSupported(ctx.query_->getClasses())) {
924  continue;
925  }
926 
927  ctx.subnet_ = subnet;
928 
929  // check if the hint is in pool and is available
930  // This is equivalent of subnet->inPool(hint), but returns the pool
931  pool = boost::dynamic_pointer_cast<Pool6>
932  (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(),
933  hint));
934 
935  // check if the pool is allowed
936  if (!pool || !pool->clientSupported(ctx.query_->getClasses())) {
937  continue;
938  }
939 
940  bool in_subnet = subnet->getReservationsInSubnet();
941 
943  Lease6Ptr lease =
945  if (!lease) {
946 
947  // In-pool reservations: Check if this address is reserved for someone
948  // else. There is no need to check for whom it is reserved, because if
949  // it has been reserved for us we would have already allocated a lease.
950 
951  ConstHostCollection hosts;
952  // When out-of-pool flag is true the server may assume that all host
953  // reservations are for addresses that do not belong to the dynamic
954  // pool. Therefore, it can skip the reservation checks when dealing
955  // with in-pool addresses.
956  if (in_subnet &&
957  (!subnet->getReservationsOutOfPool() ||
958  !subnet->inPool(ctx.currentIA().type_, hint))) {
959  hosts = getIPv6Resrv(subnet->getID(), hint);
960  }
961 
962  if (hosts.empty()) {
963  // If the in-pool reservations are disabled, or there is no
964  // reservation for a given hint, we're good to go.
965 
966  // The hint is valid and not currently used, let's create a
967  // lease for it
968  lease = createLease6(ctx, hint, pool->getLength(), callout_status);
969 
970  // It can happen that the lease allocation failed (we could
971  // have lost the race condition. That means that the hint is
972  // no longer usable and we need to continue the regular
973  // allocation path.
974  if (lease) {
975 
977  Lease6Collection collection;
978  collection.push_back(lease);
979  return (collection);
980  }
981  } else {
984  .arg(ctx.query_->getLabel())
985  .arg(hint.toText());
986  }
987 
988  } else if (lease->expired()) {
989 
990  // If the lease is expired, we may likely reuse it, but...
991  ConstHostCollection hosts;
992  // When out-of-pool flag is true the server may assume that all host
993  // reservations are for addresses that do not belong to the dynamic
994  // pool. Therefore, it can skip the reservation checks when dealing
995  // with in-pool addresses.
996  if (in_subnet &&
997  (!subnet->getReservationsOutOfPool() ||
998  !subnet->inPool(ctx.currentIA().type_, hint))) {
999  hosts = getIPv6Resrv(subnet->getID(), hint);
1000  }
1001 
1002  // Let's check if there is a reservation for this address.
1003  if (hosts.empty()) {
1004 
1005  // Copy an existing, expired lease so as it can be returned
1006  // to the caller.
1007  Lease6Ptr old_lease(new Lease6(*lease));
1008  ctx.currentIA().old_leases_.push_back(old_lease);
1009 
1011  lease = reuseExpiredLease(lease, ctx, pool->getLength(),
1012  callout_status);
1013 
1015  leases.push_back(lease);
1016  return (leases);
1017 
1018  } else {
1021  .arg(ctx.query_->getLabel())
1022  .arg(hint.toText());
1023  }
1024  }
1025  }
1026 
1027  // We have the choice in the order checking the lease and
1028  // the reservation. The default is to begin by the lease
1029  // if the multi-threading is disabled.
1030  bool check_reservation_first = MultiThreadingMgr::instance().getMode();
1031  // If multi-threading is disabled, honor the configured order for host
1032  // reservations lookup.
1033  if (!check_reservation_first) {
1034  check_reservation_first = CfgMgr::instance().getCurrentCfg()->getReservationsLookupFirst();
1035  }
1036 
1037  uint64_t total_attempts = 0;
1038 
1039  // Need to check if the subnet belongs to a shared network. If so,
1040  // we might be able to find a better subnet for lease allocation,
1041  // for which it is more likely that there are some leases available.
1042  // If we stick to the selected subnet, we may end up walking over
1043  // the entire subnet (or more subnets) to discover that the pools
1044  // have been exhausted. Using a subnet from which a lease was
1045  // assigned most recently is an optimization which increases
1046  // the likelihood of starting from the subnet which pools are not
1047  // exhausted.
1048  SharedNetwork6Ptr network;
1049  original_subnet->getSharedNetwork(network);
1050  if (network) {
1051  // This would try to find a subnet with the same set of classes
1052  // as the current subnet, but with the more recent "usage timestamp".
1053  // This timestamp is only updated for the allocations made with an
1054  // allocator (unreserved lease allocations), not the static
1055  // allocations or requested addresses.
1056  original_subnet = network->getPreferredSubnet(original_subnet, ctx.currentIA().type_);
1057  }
1058 
1059  ctx.subnet_ = subnet = original_subnet;
1060 
1061  // The following counter tracks the number of subnets with matching client
1062  // classes from which the allocation engine attempted to assign leases.
1063  uint64_t subnets_with_unavail_leases = 0;
1064  // The following counter tracks the number of subnets in which there were
1065  // no matching pools for the client.
1066  uint64_t subnets_with_unavail_pools = 0;
1067 
1068  for (; subnet; subnet = subnet->getNextSubnet(original_subnet)) {
1069 
1070  if (!subnet->clientSupported(ctx.query_->getClasses())) {
1071  continue;
1072  }
1073 
1074  // The hint was useless (it was not provided at all, was used by someone else,
1075  // was out of pool or reserved for someone else). Search the pool until first
1076  // of the following occurs:
1077  // - we find a free address
1078  // - we find an address for which the lease has expired
1079  // - we exhaust number of tries
1080  uint64_t possible_attempts =
1081  subnet->getPoolCapacity(ctx.currentIA().type_,
1082  ctx.query_->getClasses());
1083 
1084  // If the number of tries specified in the allocation engine constructor
1085  // is set to 0 (unlimited) or the pools capacity is lower than that number,
1086  // let's use the pools capacity as the maximum number of tries. Trying
1087  // more than the actual pools capacity is a waste of time. If the specified
1088  // number of tries is lower than the pools capacity, use that number.
1089  uint64_t max_attempts = ((attempts_ == 0) || (possible_attempts < attempts_)) ? possible_attempts : attempts_;
1090 
1091  if (max_attempts > 0) {
1092  // If max_attempts is greater than 0, there are some pools in this subnet
1093  // from which we can potentially get a lease.
1094  ++subnets_with_unavail_leases;
1095  } else {
1096  // If max_attempts is 0, it is an indication that there are no pools
1097  // in the subnet from which we can get a lease.
1098  ++subnets_with_unavail_pools;
1099  continue;
1100  }
1101 
1102  bool in_subnet = subnet->getReservationsInSubnet();
1103  bool out_of_pool = subnet->getReservationsOutOfPool();
1104 
1105  // Set the default status code in case the lease6_select callouts
1106  // do not exist and the callout handle has a status returned by
1107  // any of the callouts already invoked for this packet.
1108  if (ctx.callout_handle_) {
1109  ctx.callout_handle_->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
1110  }
1111 
1112  for (uint64_t i = 0; i < max_attempts; ++i) {
1113 
1114  ++total_attempts;
1115 
1116  IOAddress candidate = allocator->pickAddress(subnet,
1117  ctx.query_->getClasses(),
1118  ctx.duid_,
1119  hint);
1120  // The first step is to find out prefix length. It is 128 for
1121  // non-PD leases.
1122  uint8_t prefix_len = 128;
1123  if (ctx.currentIA().type_ == Lease::TYPE_PD) {
1124  pool = boost::dynamic_pointer_cast<Pool6>(
1125  subnet->getPool(ctx.currentIA().type_,
1126  ctx.query_->getClasses(),
1127  candidate));
1128  if (pool) {
1129  prefix_len = pool->getLength();
1130  }
1131  }
1132 
1133  // First check for reservation when it is the choice.
1134  if (check_reservation_first && in_subnet && !out_of_pool) {
1135  auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1136  if (!hosts.empty()) {
1137  // Don't allocate.
1138  continue;
1139  }
1140  }
1141 
1142  // Check if the resource is busy i.e. can be being allocated
1143  // by another thread to another client.
1144  ResourceHandler resource_handler;
1145  if (MultiThreadingMgr::instance().getMode() &&
1146  !resource_handler.tryLock(ctx.currentIA().type_, candidate)) {
1147  // Don't allocate.
1148  continue;
1149  }
1150 
1151  // Look for an existing lease for the candidate.
1153  candidate);
1154 
1155  if (!existing) {
1159  if (!check_reservation_first && in_subnet && !out_of_pool) {
1160  auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1161  if (!hosts.empty()) {
1162  // Don't allocate.
1163  continue;
1164  }
1165  }
1166 
1167  // there's no existing lease for selected candidate, so it is
1168  // free. Let's allocate it.
1169 
1170  ctx.subnet_ = subnet;
1171  Lease6Ptr lease = createLease6(ctx, candidate, prefix_len, callout_status);
1172  if (lease) {
1173  // We are allocating a new lease (not renewing). So, the
1174  // old lease should be NULL.
1175  ctx.currentIA().old_leases_.clear();
1176 
1177  leases.push_back(lease);
1178  return (leases);
1179 
1180  } else if (ctx.callout_handle_ &&
1181  (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
1182  // Don't retry when the callout status is not continue.
1183  break;
1184  }
1185 
1186  // Although the address was free just microseconds ago, it may have
1187  // been taken just now. If the lease insertion fails, we continue
1188  // allocation attempts.
1189  } else if (existing->expired()) {
1190  // Make sure it's not reserved.
1191  if (!check_reservation_first && in_subnet && !out_of_pool) {
1192  auto hosts = getIPv6Resrv(subnet->getID(), candidate);
1193  if (!hosts.empty()) {
1194  // Don't allocate.
1195  continue;
1196  }
1197  }
1198 
1199  // Copy an existing, expired lease so as it can be returned
1200  // to the caller.
1201  Lease6Ptr old_lease(new Lease6(*existing));
1202  ctx.currentIA().old_leases_.push_back(old_lease);
1203 
1204  ctx.subnet_ = subnet;
1205  existing = reuseExpiredLease(existing, ctx, prefix_len,
1206  callout_status);
1207 
1208  leases.push_back(existing);
1209  return (leases);
1210  }
1211  }
1212  }
1213 
1214  if (network) {
1215  // The client is in the shared network. Let's log the high level message
1216  // indicating which shared network the client belongs to.
1218  .arg(ctx.query_->getLabel())
1219  .arg(network->getName())
1220  .arg(subnets_with_unavail_leases)
1221  .arg(subnets_with_unavail_pools);
1222  StatsMgr::instance().addValue("v6-allocation-fail-shared-network",
1223  static_cast<int64_t>(1));
1224  StatsMgr::instance().addValue(
1225  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1226  "v6-allocation-fail-shared-network"),
1227  static_cast<int64_t>(1));
1228  } else {
1229  // The client is not connected to a shared network. It is connected
1230  // to a subnet. Let's log the ID of that subnet.
1232  .arg(ctx.query_->getLabel())
1233  .arg(ctx.subnet_->getID());
1234  StatsMgr::instance().addValue("v6-allocation-fail-subnet",
1235  static_cast<int64_t>(1));
1236  StatsMgr::instance().addValue(
1237  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1238  "v6-allocation-fail-subnet"),
1239  static_cast<int64_t>(1));
1240  }
1241  if (total_attempts == 0) {
1242  // In this case, it seems that none of the pools in the subnets could
1243  // be used for that client, both in case the client is connected to
1244  // a shared network or to a single subnet. Apparently, the client was
1245  // rejected to use the pools because of the client classes' mismatch.
1247  .arg(ctx.query_->getLabel());
1248  StatsMgr::instance().addValue("v6-allocation-fail-no-pools",
1249  static_cast<int64_t>(1));
1250  StatsMgr::instance().addValue(
1251  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1252  "v6-allocation-fail-no-pools"),
1253  static_cast<int64_t>(1));
1254  } else {
1255  // This is an old log message which provides a number of attempts
1256  // made by the allocation engine to allocate a lease. The only case
1257  // when we don't want to log this message is when the number of
1258  // attempts is zero (condition above), because it would look silly.
1260  .arg(ctx.query_->getLabel())
1261  .arg(total_attempts);
1262  StatsMgr::instance().addValue("v6-allocation-fail",
1263  static_cast<int64_t>(1));
1264  StatsMgr::instance().addValue(
1265  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1266  "v6-allocation-fail"),
1267  static_cast<int64_t>(1));
1268  }
1269 
1270  const ClientClasses& classes = ctx.query_->getClasses();
1271  if (!classes.empty()) {
1273  .arg(ctx.query_->getLabel())
1274  .arg(classes.toText());
1275  StatsMgr::instance().addValue("v6-allocation-fail-classes",
1276  static_cast<int64_t>(1));
1277  StatsMgr::instance().addValue(
1278  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1279  "v6-allocation-fail-classes"),
1280  static_cast<int64_t>(1));
1281  }
1282 
1283  // We failed to allocate anything. Let's return empty collection.
1284  return (Lease6Collection());
1285 }
1286 
1287 void
1288 AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
1289  Lease6Collection& existing_leases) {
1290 
1291  // If there are no reservations or the reservation is v4, there's nothing to do.
1292  if (ctx.hosts_.empty()) {
1295  .arg(ctx.query_->getLabel());
1296  return;
1297  }
1298 
1299  // Let's convert this from Lease::Type to IPv6Reserv::Type
1300  IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1302 
1303  // We want to avoid allocating new lease for an IA if there is already
1304  // a valid lease for which client has reservation. So, we first check if
1305  // we already have a lease for a reserved address or prefix.
1306  BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
1307  if ((lease->valid_lft_ != 0)) {
1308  if ((ctx.hosts_.count(lease->subnet_id_) > 0) &&
1309  ctx.hosts_[lease->subnet_id_]->hasReservation(makeIPv6Resrv(*lease))) {
1310  // We found existing lease for a reserved address or prefix.
1311  // We'll simply extend the lifetime of the lease.
1314  .arg(ctx.query_->getLabel())
1315  .arg(lease->typeToText(lease->type_))
1316  .arg(lease->addr_.toText());
1317 
1318  // Besides IP reservations we're also going to return other reserved
1319  // parameters, such as hostname. We want to hand out the hostname value
1320  // from the same reservation entry as IP addresses. Thus, let's see if
1321  // there is any hostname reservation.
1322  if (!ctx.host_subnet_) {
1323  SharedNetwork6Ptr network;
1324  ctx.subnet_->getSharedNetwork(network);
1325  if (network) {
1326  // Remember the subnet that holds this preferred host
1327  // reservation. The server will use it to return appropriate
1328  // FQDN, classes etc.
1329  ctx.host_subnet_ = network->getSubnet(lease->subnet_id_);
1330  ConstHostPtr host = ctx.hosts_[lease->subnet_id_];
1331  // If there is a hostname reservation here we should stick
1332  // to this reservation. By updating the hostname in the
1333  // context we make sure that the database is updated with
1334  // this new value and the server doesn't need to do it and
1335  // its processing performance is not impacted by the hostname
1336  // updates.
1337  if (host && !host->getHostname().empty()) {
1338  // We have to determine whether the hostname is generated
1339  // in response to client's FQDN or not. If yes, we will
1340  // need to qualify the hostname. Otherwise, we just use
1341  // the hostname as it is specified for the reservation.
1342  OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1344  qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1345  static_cast<bool>(fqdn));
1346  }
1347  }
1348  }
1349 
1350  // Got a lease for a reservation in this IA.
1351  return;
1352  }
1353  }
1354  }
1355 
1356  // There is no lease for a reservation in this IA. So, let's now iterate
1357  // over reservations specified and try to allocate one of them for the IA.
1358 
1359  for (Subnet6Ptr subnet = ctx.subnet_; subnet;
1360  subnet = subnet->getNextSubnet(ctx.subnet_)) {
1361 
1362  SubnetID subnet_id = subnet->getID();
1363 
1364  // No hosts for this subnet or the subnet not supported.
1365  if (!subnet->clientSupported(ctx.query_->getClasses()) ||
1366  ctx.hosts_.count(subnet_id) == 0) {
1367  continue;
1368  }
1369 
1370  ConstHostPtr host = ctx.hosts_[subnet_id];
1371 
1372  bool in_subnet = subnet->getReservationsInSubnet();
1373 
1374  // Get the IPv6 reservations of specified type.
1375  const IPv6ResrvRange& reservs = host->getIPv6Reservations(type);
1376  BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
1377  // We do have a reservation for address or prefix.
1378  const IOAddress& addr = type_lease_tuple.second.getPrefix();
1379  uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1380 
1381  // We have allocated this address/prefix while processing one of the
1382  // previous IAs, so let's try another reservation.
1383  if (ctx.isAllocated(addr, prefix_len)) {
1384  continue;
1385  }
1386 
1387  // The out-of-pool flag indicates that no client should be assigned
1388  // reserved addresses from within the dynamic pool, and for that
1389  // reason look only for reservations that are outside the pools,
1390  // hence the inPool check.
1391  if (!in_subnet ||
1392  (subnet->getReservationsOutOfPool() &&
1393  subnet->inPool(ctx.currentIA().type_, addr))) {
1394  continue;
1395  }
1396 
1397  // If there's a lease for this address, let's not create it.
1398  // It doesn't matter whether it is for this client or for someone else.
1400  addr)) {
1401 
1402  // Let's remember the subnet from which the reserved address has been
1403  // allocated. We'll use this subnet for allocating other reserved
1404  // resources.
1405  ctx.subnet_ = subnet;
1406 
1407  if (!ctx.host_subnet_) {
1408  ctx.host_subnet_ = subnet;
1409  if (!host->getHostname().empty()) {
1410  // If there is a hostname reservation here we should stick
1411  // to this reservation. By updating the hostname in the
1412  // context we make sure that the database is updated with
1413  // this new value and the server doesn't need to do it and
1414  // its processing performance is not impacted by the hostname
1415  // updates.
1416 
1417  // We have to determine whether the hostname is generated
1418  // in response to client's FQDN or not. If yes, we will
1419  // need to qualify the hostname. Otherwise, we just use
1420  // the hostname as it is specified for the reservation.
1421  OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1423  qualifyName(host->getHostname(), *ctx.getDdnsParams(),
1424  static_cast<bool>(fqdn));
1425  }
1426  }
1427 
1428  // Ok, let's create a new lease...
1429  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
1430  Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1431 
1432  // ... and add it to the existing leases list.
1433  existing_leases.push_back(lease);
1434 
1435  if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1437  .arg(addr.toText())
1438  .arg(ctx.query_->getLabel());
1439  } else {
1441  .arg(addr.toText())
1442  .arg(static_cast<int>(prefix_len))
1443  .arg(ctx.query_->getLabel());
1444  }
1445 
1446  // We found a lease for this client and this IA. Let's return.
1447  // Returning after the first lease was assigned is useful if we
1448  // have multiple reservations for the same client. If the client
1449  // sends 2 IAs, the first time we call allocateReservedLeases6 will
1450  // use the first reservation and return. The second time, we'll
1451  // go over the first reservation, but will discover that there's
1452  // a lease corresponding to it and will skip it and then pick
1453  // the second reservation and turn it into the lease. This approach
1454  // would work for any number of reservations.
1455  return;
1456  }
1457  }
1458  }
1459 
1460  // Found no subnet reservations so now try the global reservation.
1461  allocateGlobalReservedLeases6(ctx, existing_leases);
1462 }
1463 
1464 void
1465 AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
1466  Lease6Collection& existing_leases) {
1467  // Get the global host
1468  ConstHostPtr ghost = ctx.globalHost();
1469  if (!ghost) {
1470  return;
1471  }
1472 
1473  // We want to avoid allocating a new lease for an IA if there is already
1474  // a valid lease for which client has reservation. So, we first check if
1475  // we already have a lease for a reserved address or prefix.
1476  BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
1477  if ((lease->valid_lft_ != 0) &&
1478  (ghost->hasReservation(makeIPv6Resrv(*lease)))) {
1479  // We found existing lease for a reserved address or prefix.
1480  // We'll simply extend the lifetime of the lease.
1483  .arg(ctx.query_->getLabel())
1484  .arg(lease->typeToText(lease->type_))
1485  .arg(lease->addr_.toText());
1486 
1487  // Besides IP reservations we're also going to return other reserved
1488  // parameters, such as hostname. We want to hand out the hostname value
1489  // from the same reservation entry as IP addresses. Thus, let's see if
1490  // there is any hostname reservation.
1491  if (!ghost->getHostname().empty()) {
1492  // We have to determine whether the hostname is generated
1493  // in response to client's FQDN or not. If yes, we will
1494  // need to qualify the hostname. Otherwise, we just use
1495  // the hostname as it is specified for the reservation.
1496  OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1498  qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1499  static_cast<bool>(fqdn));
1500  }
1501 
1502  // Got a lease for a reservation in this IA.
1503  return;
1504  }
1505  }
1506 
1507  // There is no lease for a reservation in this IA. So, let's now iterate
1508  // over reservations specified and try to allocate one of them for the IA.
1509 
1510  // Let's convert this from Lease::Type to IPv6Reserv::Type
1511  IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
1513 
1514  const IPv6ResrvRange& reservs = ghost->getIPv6Reservations(type);
1515  BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
1516  // We do have a reservation for address or prefix.
1517  const IOAddress& addr = type_lease_tuple.second.getPrefix();
1518  uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
1519 
1520  // We have allocated this address/prefix while processing one of the
1521  // previous IAs, so let's try another reservation.
1522  if (ctx.isAllocated(addr, prefix_len)) {
1523  continue;
1524  }
1525 
1526  // If there's a lease for this address, let's not create it.
1527  // It doesn't matter whether it is for this client or for someone else.
1528  if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
1529 
1530  if (!ghost->getHostname().empty()) {
1531  // If there is a hostname reservation here we should stick
1532  // to this reservation. By updating the hostname in the
1533  // context we make sure that the database is updated with
1534  // this new value and the server doesn't need to do it and
1535  // its processing performance is not impacted by the hostname
1536  // updates.
1537 
1538  // We have to determine whether the hostname is generated
1539  // in response to client's FQDN or not. If yes, we will
1540  // need to qualify the hostname. Otherwise, we just use
1541  // the hostname as it is specified for the reservation.
1542  OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
1544  qualifyName(ghost->getHostname(), *ctx.getDdnsParams(),
1545  static_cast<bool>(fqdn));
1546  }
1547 
1548  // Ok, let's create a new lease...
1549  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
1550  Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
1551 
1552  // ... and add it to the existing leases list.
1553  existing_leases.push_back(lease);
1554 
1555  if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1557  .arg(addr.toText())
1558  .arg(ctx.query_->getLabel());
1559  } else {
1561  .arg(addr.toText())
1562  .arg(static_cast<int>(prefix_len))
1563  .arg(ctx.query_->getLabel());
1564  }
1565 
1566  // We found a lease for this client and this IA. Let's return.
1567  // Returning after the first lease was assigned is useful if we
1568  // have multiple reservations for the same client. If the client
1569  // sends 2 IAs, the first time we call allocateReservedLeases6 will
1570  // use the first reservation and return. The second time, we'll
1571  // go over the first reservation, but will discover that there's
1572  // a lease corresponding to it and will skip it and then pick
1573  // the second reservation and turn it into the lease. This approach
1574  // would work for any number of reservations.
1575  return;
1576  }
1577  }
1578 }
1579 
1580 void
1581 AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
1582  Lease6Collection& existing_leases) {
1583  // If there are no leases (so nothing to remove) just return.
1584  if (existing_leases.empty() || !ctx.subnet_) {
1585  return;
1586  }
1587  // If host reservation is disabled (so there are no reserved leases)
1588  // use the simplified version.
1589  if (!ctx.subnet_->getReservationsInSubnet() &&
1590  !ctx.subnet_->getReservationsGlobal()) {
1591  removeNonmatchingReservedNoHostLeases6(ctx, existing_leases);
1592  return;
1593  }
1594 
1595  // We need a copy, so we won't be iterating over a container and
1596  // removing from it at the same time. It's only a copy of pointers,
1597  // so the operation shouldn't be that expensive.
1598  Lease6Collection copy = existing_leases;
1599 
1600  BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
1601  // If we have reservation we should check if the reservation is for
1602  // the candidate lease. If so, we simply accept the lease.
1603  IPv6Resrv resv = makeIPv6Resrv(*candidate);
1604  if ((ctx.hasGlobalReservation(resv)) ||
1605  ((ctx.hosts_.count(candidate->subnet_id_) > 0) &&
1606  (ctx.hosts_[candidate->subnet_id_]->hasReservation(resv)))) {
1607  // We have a subnet reservation
1608  continue;
1609  }
1610 
1611  // The candidate address doesn't appear to be reserved for us.
1612  // We have to make a bit more expensive operation here to retrieve
1613  // the reservation for the candidate lease and see if it is
1614  // reserved for someone else.
1615  auto hosts = getIPv6Resrv(ctx.subnet_->getID(), candidate->addr_);
1616  // If lease is not reserved to someone else, it means that it can
1617  // be allocated to us from a dynamic pool, but we must check if
1618  // this lease belongs to any pool. If it does, we can proceed to
1619  // checking the next lease.
1620  if (hosts.empty() && inAllowedPool(ctx, candidate->type_,
1621  candidate->addr_, false)) {
1622  continue;
1623  }
1624 
1625  if (!hosts.empty()) {
1626  // Ok, we have a problem. This host has a lease that is reserved
1627  // for someone else. We need to recover from this.
1628  if (hosts.size() == 1) {
1629  if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1631  .arg(candidate->addr_.toText())
1632  .arg(ctx.duid_->toText())
1633  .arg(hosts.front()->getIdentifierAsText());
1634  } else {
1636  .arg(candidate->addr_.toText())
1637  .arg(static_cast<int>(candidate->prefixlen_))
1638  .arg(ctx.duid_->toText())
1639  .arg(hosts.front()->getIdentifierAsText());
1640  }
1641  } else {
1642  if (ctx.currentIA().type_ == Lease::TYPE_NA) {
1644  .arg(candidate->addr_.toText())
1645  .arg(ctx.duid_->toText())
1646  .arg(hosts.size());
1647  } else {
1649  .arg(candidate->addr_.toText())
1650  .arg(static_cast<int>(candidate->prefixlen_))
1651  .arg(ctx.duid_->toText())
1652  .arg(hosts.size());
1653  }
1654  }
1655  }
1656 
1657  // Remove this lease from LeaseMgr as it is reserved to someone
1658  // else or doesn't belong to a pool.
1659  if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1660  // Concurrent delete performed by other instance which should
1661  // properly handle dns and stats updates.
1662  continue;
1663  }
1664 
1665  // Update DNS if needed.
1666  queueNCR(CHG_REMOVE, candidate);
1667 
1668  // Need to decrease statistic for assigned addresses.
1669  StatsMgr::instance().addValue(
1670  StatsMgr::generateName("subnet", candidate->subnet_id_,
1671  ctx.currentIA().type_ == Lease::TYPE_NA ?
1672  "assigned-nas" : "assigned-pds"),
1673  static_cast<int64_t>(-1));
1674 
1675  // In principle, we could trigger a hook here, but we will do this
1676  // only if we get serious complaints from actual users. We want the
1677  // conflict resolution procedure to really work and user libraries
1678  // should not interfere with it.
1679 
1680  // Add this to the list of removed leases.
1681  ctx.currentIA().old_leases_.push_back(candidate);
1682 
1683  // Let's remove this candidate from existing leases
1684  removeLeases(existing_leases, candidate->addr_);
1685  }
1686 }
1687 
1688 void
1689 AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
1690  Lease6Collection& existing_leases) {
1691  // We need a copy, so we won't be iterating over a container and
1692  // removing from it at the same time. It's only a copy of pointers,
1693  // so the operation shouldn't be that expensive.
1694  Lease6Collection copy = existing_leases;
1695 
1696  BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
1697  // Lease can be allocated to us from a dynamic pool, but we must
1698  // check if this lease belongs to any allowed pool. If it does,
1699  // we can proceed to checking the next lease.
1700  if (inAllowedPool(ctx, candidate->type_,
1701  candidate->addr_, false)) {
1702  continue;
1703  }
1704 
1705  // Remove this lease from LeaseMgr as it doesn't belong to a pool.
1706  if (!LeaseMgrFactory::instance().deleteLease(candidate)) {
1707  // Concurrent delete performed by other instance which should
1708  // properly handle dns and stats updates.
1709  continue;
1710  }
1711 
1712  // Update DNS if needed.
1713  queueNCR(CHG_REMOVE, candidate);
1714 
1715  // Need to decrease statistic for assigned addresses.
1716  StatsMgr::instance().addValue(
1717  StatsMgr::generateName("subnet", candidate->subnet_id_,
1718  ctx.currentIA().type_ == Lease::TYPE_NA ?
1719  "assigned-nas" : "assigned-pds"),
1720  static_cast<int64_t>(-1));
1721 
1722  // Add this to the list of removed leases.
1723  ctx.currentIA().old_leases_.push_back(candidate);
1724 
1725  // Let's remove this candidate from existing leases
1726  removeLeases(existing_leases, candidate->addr_);
1727  }
1728 }
1729 
1730 bool
1731 AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
1732 
1733  bool removed = false;
1734  for (Lease6Collection::iterator lease = container.begin();
1735  lease != container.end(); ++lease) {
1736  if ((*lease)->addr_ == addr) {
1737  lease->reset();
1738  removed = true;
1739  }
1740  }
1741 
1742  // Remove all elements that have NULL value
1743  container.erase(std::remove(container.begin(), container.end(), Lease6Ptr()),
1744  container.end());
1745 
1746  return (removed);
1747 }
1748 
1749 void
1750 AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
1751  Lease6Collection& existing_leases) {
1752  // This method removes leases that are not reserved for this host.
1753  // It will keep at least one lease, though, as a fallback.
1754  int total = existing_leases.size();
1755  if (total <= 1) {
1756  return;
1757  }
1758 
1759  // This is officially not scary code anymore. iterates and marks specified
1760  // leases for deletion, by setting appropriate pointers to NULL.
1761  for (Lease6Collection::iterator lease = existing_leases.begin();
1762  lease != existing_leases.end(); ++lease) {
1763 
1764  // If there is reservation for this keep it.
1765  IPv6Resrv resv = makeIPv6Resrv(*(*lease));
1766  if (ctx.hasGlobalReservation(resv) ||
1767  ((ctx.hosts_.count((*lease)->subnet_id_) > 0) &&
1768  (ctx.hosts_[(*lease)->subnet_id_]->hasReservation(resv)))) {
1769  continue;
1770  }
1771 
1772  // @todo - If this is for a fake_allocation, we should probably
1773  // not be deleting the lease or removing DNS entries. We should
1774  // simply remove it from the list.
1775  // We have reservations, but not for this lease. Release it.
1776  // Remove this lease from LeaseMgr
1777  if (!LeaseMgrFactory::instance().deleteLease(*lease)) {
1778  // Concurrent delete performed by other instance which should
1779  // properly handle dns and stats updates.
1780  continue;
1781  }
1782 
1783  // Update DNS if required.
1784  queueNCR(CHG_REMOVE, *lease);
1785 
1786  // Need to decrease statistic for assigned addresses.
1787  StatsMgr::instance().addValue(
1788  StatsMgr::generateName("subnet", (*lease)->subnet_id_,
1789  ctx.currentIA().type_ == Lease::TYPE_NA ?
1790  "assigned-nas" : "assigned-pds"),
1791  static_cast<int64_t>(-1));
1792 
1794 
1795  // Add this to the list of removed leases.
1796  ctx.currentIA().old_leases_.push_back(*lease);
1797 
1798  // Set this pointer to NULL. The pointer is still valid. We're just
1799  // setting the Lease6Ptr to NULL value. We'll remove all NULL
1800  // pointers once the loop is finished.
1801  lease->reset();
1802 
1803  if (--total == 1) {
1804  // If there's only one lease left, break the loop.
1805  break;
1806  }
1807 
1808  }
1809 
1810  // Remove all elements that we previously marked for deletion (those that
1811  // have NULL value).
1812  existing_leases.erase(std::remove(existing_leases.begin(),
1813  existing_leases.end(), Lease6Ptr()), existing_leases.end());
1814 }
1815 
1816 Lease6Ptr
1817 AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
1818  uint8_t prefix_len,
1819  CalloutHandle::CalloutNextStep& callout_status) {
1820 
1821  if (!expired->expired()) {
1822  isc_throw(BadValue, "Attempt to recycle lease that is still valid");
1823  }
1824 
1825  if (expired->type_ != Lease::TYPE_PD) {
1826  prefix_len = 128; // non-PD lease types must be always /128
1827  }
1828 
1829  if (!ctx.fake_allocation_) {
1830  // The expired lease needs to be reclaimed before it can be reused.
1831  // This includes declined leases for which probation period has
1832  // elapsed.
1833  reclaimExpiredLease(expired, ctx.callout_handle_);
1834  }
1835 
1836  // address, lease type and prefixlen (0) stay the same
1837  expired->iaid_ = ctx.currentIA().iaid_;
1838  expired->duid_ = ctx.duid_;
1839  // Use subnet's preferred triplet to conditionally determine
1840  // preferred lifetime based on hint
1841  if (!ctx.currentIA().hints_.empty() &&
1842  ctx.currentIA().hints_[0].getPreferred()) {
1843  uint32_t preferred = ctx.currentIA().hints_[0].getPreferred();
1844  expired->preferred_lft_ = ctx.subnet_->getPreferred().get(preferred);
1845  } else {
1846  expired->preferred_lft_ = ctx.subnet_->getPreferred();
1847  }
1848  // Use subnet's valid triplet to conditionally determine
1849  // valid lifetime based on hint
1850  expired->reuseable_valid_lft_ = 0;
1851  if (!ctx.currentIA().hints_.empty() &&
1852  ctx.currentIA().hints_[0].getValid()) {
1853  uint32_t valid = ctx.currentIA().hints_[0].getValid();
1854  expired->valid_lft_ = ctx.subnet_->getValid().get(valid);
1855  } else {
1856  expired->valid_lft_ = ctx.subnet_->getValid();
1857  }
1858  expired->cltt_ = time(NULL);
1859  expired->subnet_id_ = ctx.subnet_->getID();
1860  expired->hostname_ = ctx.hostname_;
1861  expired->fqdn_fwd_ = ctx.fwd_dns_update_;
1862  expired->fqdn_rev_ = ctx.rev_dns_update_;
1863  expired->prefixlen_ = prefix_len;
1864  expired->state_ = Lease::STATE_DEFAULT;
1865 
1868  .arg(ctx.query_->getLabel())
1869  .arg(expired->toText());
1870 
1871  // Let's execute all callouts registered for lease6_select
1872  if (ctx.callout_handle_ &&
1873  HooksManager::calloutsPresent(hook_index_lease6_select_)) {
1874 
1875  // Use the RAII wrapper to make sure that the callout handle state is
1876  // reset when this object goes out of scope. All hook points must do
1877  // it to prevent possible circular dependency between the callout
1878  // handle and its arguments.
1879  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
1880 
1881  // Enable copying options from the packet within hook library.
1882  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
1883 
1884  // Pass necessary arguments
1885 
1886  // Pass the original packet
1887  ctx.callout_handle_->setArgument("query6", ctx.query_);
1888 
1889  // Subnet from which we do the allocation
1890  ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
1891 
1892  // Is this solicit (fake = true) or request (fake = false)
1893  ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
1894 
1895  // The lease that will be assigned to a client
1896  ctx.callout_handle_->setArgument("lease6", expired);
1897 
1898  // Call the callouts
1899  HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
1900 
1901  callout_status = ctx.callout_handle_->getStatus();
1902 
1903  // Callouts decided to skip the action. This means that the lease is not
1904  // assigned, so the client will get NoAddrAvail as a result. The lease
1905  // won't be inserted into the database.
1906  if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
1908  return (Lease6Ptr());
1909  }
1910 
1915 
1916  // Let's use whatever callout returned. Hopefully it is the same lease
1917  // we handed to it.
1918  ctx.callout_handle_->getArgument("lease6", expired);
1919  }
1920 
1921  if (!ctx.fake_allocation_) {
1922  // Add(update) the extended information on the lease.
1923  static_cast<void>(updateLease6ExtendedInfo(expired, ctx));
1924 
1925  // for REQUEST we do update the lease
1927 
1928  // If the lease is in the current subnet we need to account
1929  // for the re-assignment of The lease.
1930  if (ctx.subnet_->inPool(ctx.currentIA().type_, expired->addr_)) {
1931  StatsMgr::instance().addValue(
1932  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1933  ctx.currentIA().type_ == Lease::TYPE_NA ?
1934  "assigned-nas" : "assigned-pds"),
1935  static_cast<int64_t>(1));
1936  StatsMgr::instance().addValue(
1937  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
1938  ctx.currentIA().type_ == Lease::TYPE_NA ?
1939  "cumulative-assigned-nas" :
1940  "cumulative-assigned-pds"),
1941  static_cast<int64_t>(1));
1942  StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
1943  "cumulative-assigned-nas" :
1944  "cumulative-assigned-pds",
1945  static_cast<int64_t>(1));
1946  }
1947  }
1948 
1949  // We do nothing for SOLICIT. We'll just update database when
1950  // the client gets back to us with REQUEST message.
1951 
1952  // it's not really expired at this stage anymore - let's return it as
1953  // an updated lease
1954  return (expired);
1955 }
1956 
1957 void
1958 AllocEngine::getLifetimes6(ClientContext6& ctx, uint32_t& preferred, uint32_t& valid) {
1959  // If the triplets are specified in one of our classes use it.
1960  // We use the first one we find for each lifetime.
1961  Triplet<uint32_t> candidate_preferred;
1962  Triplet<uint32_t> candidate_valid;
1963  const ClientClasses classes = ctx.query_->getClasses();
1964  if (!classes.empty()) {
1965  // Let's get class definitions
1966  const ClientClassDictionaryPtr& dict =
1967  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
1968 
1969  // Iterate over the assigned class defintions.
1970  int have_both = 0;
1971  for (auto name = classes.cbegin();
1972  name != classes.cend() && have_both < 2; ++name) {
1973  ClientClassDefPtr cl = dict->findClass(*name);
1974  if (candidate_preferred.unspecified() &&
1975  (cl && (!cl->getPreferred().unspecified()))) {
1976  candidate_preferred = cl->getPreferred();
1977  ++have_both;
1978  }
1979 
1980  if (candidate_valid.unspecified() &&
1981  (cl && (!cl->getValid().unspecified()))) {
1982  candidate_valid = cl->getValid();
1983  ++have_both;
1984  }
1985  }
1986  }
1987 
1988  // If no classes specified preferred lifetime, get it from the subnet.
1989  if (!candidate_preferred) {
1990  candidate_preferred = ctx.subnet_->getPreferred();
1991  }
1992 
1993  // If no classes specified valid lifetime, get it from the subnet.
1994  if (!candidate_valid) {
1995  candidate_valid = ctx.subnet_->getValid();
1996  }
1997 
1998  // Set the outbound parameters to the values we have so far.
1999  preferred = candidate_preferred;
2000  valid = candidate_valid;
2001 
2002  // If client requested either value, use the requested value(s) bounded by
2003  // the candidate triplet(s).
2004  if (!ctx.currentIA().hints_.empty()) {
2005  if (ctx.currentIA().hints_[0].getPreferred()) {
2006  preferred = candidate_preferred.get(ctx.currentIA().hints_[0].getPreferred());
2007  }
2008 
2009  if (ctx.currentIA().hints_[0].getValid()) {
2010  valid = candidate_valid.get(ctx.currentIA().hints_[0].getValid());
2011  }
2012  }
2013 }
2014 
2015 Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
2016  const IOAddress& addr,
2017  uint8_t prefix_len,
2018  CalloutHandle::CalloutNextStep& callout_status) {
2019 
2020  if (ctx.currentIA().type_ != Lease::TYPE_PD) {
2021  prefix_len = 128; // non-PD lease types must be always /128
2022  }
2023 
2024  uint32_t preferred = 0;
2025  uint32_t valid = 0;
2026  getLifetimes6(ctx, preferred, valid);
2027 
2028  Lease6Ptr lease(new Lease6(ctx.currentIA().type_, addr, ctx.duid_,
2029  ctx.currentIA().iaid_, preferred,
2030  valid, ctx.subnet_->getID(),
2031  ctx.hwaddr_, prefix_len));
2032 
2033  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2034  lease->fqdn_rev_ = ctx.rev_dns_update_;
2035  lease->hostname_ = ctx.hostname_;
2036 
2037  // Let's execute all callouts registered for lease6_select
2038  if (ctx.callout_handle_ &&
2039  HooksManager::calloutsPresent(hook_index_lease6_select_)) {
2040 
2041  // Use the RAII wrapper to make sure that the callout handle state is
2042  // reset when this object goes out of scope. All hook points must do
2043  // it to prevent possible circular dependency between the callout
2044  // handle and its arguments.
2045  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
2046 
2047  // Enable copying options from the packet within hook library.
2048  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2049 
2050  // Pass necessary arguments
2051 
2052  // Pass the original packet
2053  ctx.callout_handle_->setArgument("query6", ctx.query_);
2054 
2055  // Subnet from which we do the allocation
2056  ctx.callout_handle_->setArgument("subnet6", ctx.subnet_);
2057 
2058  // Is this solicit (fake = true) or request (fake = false)
2059  ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
2060 
2061  // The lease that will be assigned to a client
2062  ctx.callout_handle_->setArgument("lease6", lease);
2063 
2064  // This is the first callout, so no need to clear any arguments
2065  HooksManager::callCallouts(hook_index_lease6_select_, *ctx.callout_handle_);
2066 
2067  callout_status = ctx.callout_handle_->getStatus();
2068 
2069  // Callouts decided to skip the action. This means that the lease is not
2070  // assigned, so the client will get NoAddrAvail as a result. The lease
2071  // won't be inserted into the database.
2072  if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
2074  return (Lease6Ptr());
2075  }
2076 
2077  // Let's use whatever callout returned. Hopefully it is the same lease
2078  // we handed to it.
2079  ctx.callout_handle_->getArgument("lease6", lease);
2080  }
2081 
2082  if (!ctx.fake_allocation_) {
2083  // Add(update) the extended information on the lease.
2084  static_cast<void>(updateLease6ExtendedInfo(lease, ctx));
2085 
2086  // That is a real (REQUEST) allocation
2087  bool status = LeaseMgrFactory::instance().addLease(lease);
2088 
2089  if (status) {
2090  // The lease insertion succeeded - if the lease is in the
2091  // current subnet lets bump up the statistic.
2092  if (ctx.subnet_->inPool(ctx.currentIA().type_, addr)) {
2093  StatsMgr::instance().addValue(
2094  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2095  ctx.currentIA().type_ == Lease::TYPE_NA ?
2096  "assigned-nas" : "assigned-pds"),
2097  static_cast<int64_t>(1));
2098  StatsMgr::instance().addValue(
2099  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2100  ctx.currentIA().type_ == Lease::TYPE_NA ?
2101  "cumulative-assigned-nas" :
2102  "cumulative-assigned-pds"),
2103  static_cast<int64_t>(1));
2104  StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2105  "cumulative-assigned-nas" :
2106  "cumulative-assigned-pds",
2107  static_cast<int64_t>(1));
2108  }
2109 
2110  // Record it so it won't be updated twice.
2111  ctx.currentIA().addNewResource(addr, prefix_len);
2112 
2113  return (lease);
2114  } else {
2115  // One of many failures with LeaseMgr (e.g. lost connection to the
2116  // database, database failed etc.). One notable case for that
2117  // is that we are working in multi-process mode and we lost a race
2118  // (some other process got that address first)
2119  return (Lease6Ptr());
2120  }
2121  } else {
2122  // That is only fake (SOLICIT without rapid-commit) allocation
2123 
2124  // It is for advertise only. We should not insert the lease and callers
2125  // have already verified the lease does not exist in the database.
2126  return (lease);
2127  }
2128 }
2129 
2132  try {
2133  if (!ctx.subnet_) {
2134  isc_throw(InvalidOperation, "Subnet is required for allocation");
2135  }
2136 
2137  if (!ctx.duid_) {
2138  isc_throw(InvalidOperation, "DUID is mandatory for allocation");
2139  }
2140 
2141  // Check if there are any leases for this client.
2142  Subnet6Ptr subnet = ctx.subnet_;
2143  Lease6Collection leases;
2144  while (subnet) {
2145  Lease6Collection leases_subnet =
2147  *ctx.duid_,
2148  ctx.currentIA().iaid_,
2149  subnet->getID());
2150  leases.insert(leases.end(), leases_subnet.begin(), leases_subnet.end());
2151 
2152  subnet = subnet->getNextSubnet(ctx.subnet_);
2153  }
2154 
2155  if (!leases.empty()) {
2158  .arg(ctx.query_->getLabel());
2159 
2160  // Check if the existing leases are reserved for someone else.
2161  // If they're not, we're ok to keep using them.
2162  removeNonmatchingReservedLeases6(ctx, leases);
2163  }
2164 
2165  if (!ctx.hosts_.empty()) {
2166 
2169  .arg(ctx.query_->getLabel());
2170 
2171  // If we have host reservation, allocate those leases.
2172  allocateReservedLeases6(ctx, leases);
2173 
2174  // There's one more check to do. Let's remove leases that are not
2175  // matching reservations, i.e. if client X has address A, but there's
2176  // a reservation for address B, we should release A and reassign B.
2177  // Caveat: do this only if we have at least one reserved address.
2178  removeNonreservedLeases6(ctx, leases);
2179  }
2180 
2181  // If we happen to removed all leases, get something new for this guy.
2182  // Depending on the configuration, we may enable or disable granting
2183  // new leases during renewals. This is controlled with the
2184  // allow_new_leases_in_renewals_ field.
2185  if (leases.empty()) {
2186 
2189  .arg(ctx.query_->getLabel());
2190 
2191  leases = allocateUnreservedLeases6(ctx);
2192  }
2193 
2194  // Extend all existing leases that passed all checks.
2195  for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
2196  if (ctx.currentIA().isNewResource((*l)->addr_,
2197  (*l)->prefixlen_)) {
2198  // This lease was just created so is already extended.
2199  continue;
2200  }
2203  .arg(ctx.query_->getLabel())
2204  .arg((*l)->typeToText((*l)->type_))
2205  .arg((*l)->addr_);
2206  extendLease6(ctx, *l);
2207  }
2208 
2209  if (!leases.empty()) {
2210  // If there are any leases allocated, let's store in them in the
2211  // IA context so as they are available when we process subsequent
2212  // IAs.
2213  BOOST_FOREACH(Lease6Ptr lease, leases) {
2214  ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
2215  ctx.new_leases_.push_back(lease);
2216  }
2217  }
2218 
2219  return (leases);
2220 
2221  } catch (const isc::Exception& e) {
2222 
2223  // Some other error, return an empty lease.
2225  .arg(ctx.query_->getLabel())
2226  .arg(e.what());
2227  }
2228 
2229  return (Lease6Collection());
2230 }
2231 
2232 void
2233 AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
2234 
2235  if (!lease || !ctx.subnet_) {
2236  return;
2237  }
2238 
2239  // It is likely that the lease for which we're extending the lifetime doesn't
2240  // belong to the current but a sibling subnet.
2241  if (ctx.subnet_->getID() != lease->subnet_id_) {
2242  SharedNetwork6Ptr network;
2243  ctx.subnet_->getSharedNetwork(network);
2244  if (network) {
2245  Subnet6Ptr subnet = network->getSubnet(SubnetID(lease->subnet_id_));
2246  // Found the actual subnet this lease belongs to. Stick to this
2247  // subnet.
2248  if (subnet) {
2249  ctx.subnet_ = subnet;
2250  }
2251  }
2252  }
2253 
2254  // If the lease is not global and it is either out of range (NAs only) or it
2255  // is not permitted by subnet client classification, delete it.
2256  if (!(ctx.hasGlobalReservation(makeIPv6Resrv(*lease))) &&
2257  (((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
2258  !ctx.subnet_->clientSupported(ctx.query_->getClasses()))) {
2259  // Oh dear, the lease is no longer valid. We need to get rid of it.
2260 
2261  // Remove this lease from LeaseMgr
2262  if (!LeaseMgrFactory::instance().deleteLease(lease)) {
2263  // Concurrent delete performed by other instance which should
2264  // properly handle dns and stats updates.
2265  return;
2266  }
2267 
2268  // Updated DNS if required.
2269  queueNCR(CHG_REMOVE, lease);
2270 
2271  // Need to decrease statistic for assigned addresses.
2272  StatsMgr::instance().addValue(
2273  StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-nas"),
2274  static_cast<int64_t>(-1));
2275 
2276  // Add it to the removed leases list.
2277  ctx.currentIA().old_leases_.push_back(lease);
2278 
2279  return;
2280  }
2281 
2284  .arg(ctx.query_->getLabel())
2285  .arg(lease->toText());
2286 
2287  // Keep the old data in case the callout tells us to skip update.
2288  Lease6Ptr old_data(new Lease6(*lease));
2289 
2290  bool changed = false;
2291  uint32_t current_preferred_lft = lease->preferred_lft_;
2292  if (!ctx.currentIA().hints_.empty() &&
2293  ctx.currentIA().hints_[0].getPreferred()) {
2294  uint32_t preferred = ctx.currentIA().hints_[0].getPreferred();
2295  lease->preferred_lft_ = ctx.subnet_->getPreferred().get(preferred);
2296  } else {
2297  lease->preferred_lft_ = ctx.subnet_->getPreferred();
2298  }
2299  if (lease->preferred_lft_ < current_preferred_lft) {
2300  changed = true;
2301  }
2302  lease->reuseable_valid_lft_ = 0;
2303  if (!ctx.currentIA().hints_.empty() &&
2304  ctx.currentIA().hints_[0].getValid()) {
2305  uint32_t valid = ctx.currentIA().hints_[0].getValid();
2306  lease->valid_lft_ = ctx.subnet_->getValid().get(valid);
2307  } else {
2308  lease->valid_lft_ = ctx.subnet_->getValid();
2309  }
2310  if (lease->valid_lft_ < lease->current_valid_lft_) {
2311  changed = true;
2312  }
2313 
2314  lease->cltt_ = time(NULL);
2315  if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
2316  (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
2317  (lease->hostname_ != ctx.hostname_)) {
2318  changed = true;
2319  lease->hostname_ = ctx.hostname_;
2320  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2321  lease->fqdn_rev_ = ctx.rev_dns_update_;
2322  }
2323  if ((!ctx.hwaddr_ && lease->hwaddr_) ||
2324  (ctx.hwaddr_ &&
2325  (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
2326  changed = true;
2327  lease->hwaddr_ = ctx.hwaddr_;
2328  }
2329  if (lease->state_ != Lease::STATE_DEFAULT) {
2330  changed = true;
2331  lease->state_ = Lease::STATE_DEFAULT;
2332  }
2335  .arg(ctx.query_->getLabel())
2336  .arg(lease->toText());
2337 
2338  bool skip = false;
2339  // Get the callouts specific for the processed message and execute them.
2340  int hook_point = ctx.query_->getType() == DHCPV6_RENEW ?
2341  Hooks.hook_index_lease6_renew_ : Hooks.hook_index_lease6_rebind_;
2342  if (HooksManager::calloutsPresent(hook_point)) {
2343  CalloutHandlePtr callout_handle = ctx.callout_handle_;
2344 
2345  // Use the RAII wrapper to make sure that the callout handle state is
2346  // reset when this object goes out of scope. All hook points must do
2347  // it to prevent possible circular dependency between the callout
2348  // handle and its arguments.
2349  ScopedCalloutHandleState callout_handle_state(callout_handle);
2350 
2351  // Enable copying options from the packet within hook library.
2352  ScopedEnableOptionsCopy<Pkt6> query6_options_copy(ctx.query_);
2353 
2354  // Pass the original packet
2355  callout_handle->setArgument("query6", ctx.query_);
2356 
2357  // Pass the lease to be updated
2358  callout_handle->setArgument("lease6", lease);
2359 
2360  // Pass the IA option to be sent in response
2361  if (lease->type_ == Lease::TYPE_NA) {
2362  callout_handle->setArgument("ia_na", ctx.currentIA().ia_rsp_);
2363  } else {
2364  callout_handle->setArgument("ia_pd", ctx.currentIA().ia_rsp_);
2365  }
2366 
2367  // Call all installed callouts
2368  HooksManager::callCallouts(hook_point, *callout_handle);
2369 
2370  // Callouts decided to skip the next processing step. The next
2371  // processing step would actually renew the lease, so skip at this
2372  // stage means "keep the old lease as it is".
2373  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
2374  skip = true;
2377  .arg(ctx.query_->getName());
2378  }
2379 
2381  }
2382 
2383  if (!skip) {
2384  bool update_stats = false;
2385 
2386  // If the lease we're renewing has expired, we need to reclaim this
2387  // lease before we can renew it.
2388  if (old_data->expired()) {
2389  reclaimExpiredLease(old_data, ctx.callout_handle_);
2390 
2391  // If the lease is in the current subnet we need to account
2392  // for the re-assignment of the lease.
2393  if (ctx.subnet_->inPool(ctx.currentIA().type_, old_data->addr_)) {
2394  update_stats = true;
2395  }
2396  changed = true;
2397  }
2398 
2399  // @todo should we call storeLease6ExtendedInfo() here ?
2400  if (updateLease6ExtendedInfo(lease, ctx)) {
2401  changed = true;
2402  }
2403 
2404  // Try to reuse the lease.
2405  if (!changed) {
2406  setLeaseReusable(lease, current_preferred_lft, ctx);
2407  }
2408 
2409 
2410  // Now that the lease has been reclaimed, we can go ahead and update it
2411  // in the lease database.
2412  if (lease->reuseable_valid_lft_ == 0) {
2414  }
2415 
2416  if (update_stats) {
2417  StatsMgr::instance().addValue(
2418  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2419  ctx.currentIA().type_ == Lease::TYPE_NA ?
2420  "assigned-nas" : "assigned-pds"),
2421  static_cast<int64_t>(1));
2422  StatsMgr::instance().addValue(
2423  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
2424  ctx.currentIA().type_ == Lease::TYPE_NA ?
2425  "cumulative-assigned-nas" :
2426  "cumulative-assigned-pds"),
2427  static_cast<int64_t>(1));
2428  StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2429  "cumulative-assigned-nas" :
2430  "cumulative-assigned-pds",
2431  static_cast<int64_t>(1));
2432  }
2433 
2434  } else {
2435  // Copy back the original date to the lease. For MySQL it doesn't make
2436  // much sense, but for memfile, the Lease6Ptr points to the actual lease
2437  // in memfile, so the actual update is performed when we manipulate
2438  // fields of returned Lease6Ptr, the actual updateLease6() is no-op.
2439  *lease = *old_data;
2440  }
2441 
2442  // Add the old lease to the changed lease list. This allows the server
2443  // to make decisions regarding DNS updates.
2444  ctx.currentIA().changed_leases_.push_back(old_data);
2445 }
2446 
2448 AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
2449  Lease6Collection updated_leases;
2450  for (Lease6Collection::const_iterator lease_it = leases.begin();
2451  lease_it != leases.end(); ++lease_it) {
2452  Lease6Ptr lease(new Lease6(**lease_it));
2453  if (ctx.currentIA().isNewResource(lease->addr_, lease->prefixlen_)) {
2454  // This lease was just created so is already up to date.
2455  updated_leases.push_back(lease);
2456  continue;
2457  }
2458 
2459  lease->reuseable_valid_lft_ = 0;
2460  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
2461  lease->fqdn_rev_ = ctx.rev_dns_update_;
2462  lease->hostname_ = ctx.hostname_;
2463  if (!ctx.fake_allocation_) {
2464  bool update_stats = false;
2465 
2466  if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
2467  // Transition lease state to default (aka assigned)
2468  lease->state_ = Lease::STATE_DEFAULT;
2469 
2470  // If the lease is in the current subnet we need to account
2471  // for the re-assignment of the lease.
2472  if (inAllowedPool(ctx, ctx.currentIA().type_,
2473  lease->addr_, true)) {
2474  update_stats = true;
2475  }
2476  }
2477 
2478  bool fqdn_changed = ((lease->type_ != Lease::TYPE_PD) &&
2479  !(lease->hasIdenticalFqdn(**lease_it)));
2480 
2481  lease->cltt_ = time(NULL);
2482  if (!fqdn_changed) {
2483  uint32_t current_preferred_lft = lease->preferred_lft_;
2484  setLeaseReusable(lease, current_preferred_lft, ctx);
2485  }
2486  if (lease->reuseable_valid_lft_ == 0) {
2487  ctx.currentIA().changed_leases_.push_back(*lease_it);
2489  }
2490 
2491  if (update_stats) {
2492  StatsMgr::instance().addValue(
2493  StatsMgr::generateName("subnet", lease->subnet_id_,
2494  ctx.currentIA().type_ == Lease::TYPE_NA ?
2495  "assigned-nas" : "assigned-pds"),
2496  static_cast<int64_t>(1));
2497  StatsMgr::instance().addValue(
2498  StatsMgr::generateName("subnet", lease->subnet_id_,
2499  ctx.currentIA().type_ == Lease::TYPE_NA ?
2500  "cumulative-assigned-nas" :
2501  "cumulative-assigned-pds"),
2502  static_cast<int64_t>(1));
2503  StatsMgr::instance().addValue(ctx.currentIA().type_ == Lease::TYPE_NA ?
2504  "cumulative-assigned-nas" :
2505  "cumulative-assigned-pds",
2506  static_cast<int64_t>(1));
2507  }
2508  }
2509 
2510  updated_leases.push_back(lease);
2511  }
2512 
2513  return (updated_leases);
2514 }
2515 
2516 void
2517 AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout,
2518  const bool remove_lease,
2519  const uint16_t max_unwarned_cycles) {
2520 
2523  .arg(max_leases)
2524  .arg(timeout);
2525 
2526  // Create stopwatch and automatically start it to measure the time
2527  // taken by the routine.
2528  util::Stopwatch stopwatch;
2529 
2530  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2531 
2532  // This value indicates if we have been able to deal with all expired
2533  // leases in this pass.
2534  bool incomplete_reclamation = false;
2535  Lease6Collection leases;
2536  // The value of 0 has a special meaning - reclaim all.
2537  if (max_leases > 0) {
2538  // If the value is non-zero, the caller has limited the number of
2539  // leases to reclaim. We obtain one lease more to see if there will
2540  // be still leases left after this pass.
2541  lease_mgr.getExpiredLeases6(leases, max_leases + 1);
2542  // There are more leases expired leases than we will process in this
2543  // pass, so we should mark it as an incomplete reclamation. We also
2544  // remove this extra lease (which we don't want to process anyway)
2545  // from the collection.
2546  if (leases.size() > max_leases) {
2547  leases.pop_back();
2548  incomplete_reclamation = true;
2549  }
2550 
2551  } else {
2552  // If there is no limitation on the number of leases to reclaim,
2553  // we will try to process all. Hence, we don't mark it as incomplete
2554  // reclamation just yet.
2555  lease_mgr.getExpiredLeases6(leases, max_leases);
2556  }
2557 
2558  // Do not initialize the callout handle until we know if there are any
2559  // lease6_expire callouts installed.
2560  CalloutHandlePtr callout_handle;
2561  if (!leases.empty() &&
2562  HooksManager::calloutsPresent(Hooks.hook_index_lease6_expire_)) {
2563  callout_handle = HooksManager::createCalloutHandle();
2564  }
2565 
2566  size_t leases_processed = 0;
2567  BOOST_FOREACH(Lease6Ptr lease, leases) {
2568 
2569  try {
2570  // Reclaim the lease.
2571  if (MultiThreadingMgr::instance().getMode()) {
2572  // The reclamation is exclusive of packet processing.
2573  WriteLockGuard exclusive(rw_mutex_);
2574 
2575  reclaimExpiredLease(lease, remove_lease, callout_handle);
2576  ++leases_processed;
2577  } else {
2578  reclaimExpiredLease(lease, remove_lease, callout_handle);
2579  ++leases_processed;
2580  }
2581 
2582  } catch (const std::exception& ex) {
2584  .arg(lease->addr_.toText())
2585  .arg(ex.what());
2586  }
2587 
2588  // Check if we have hit the timeout for running reclamation routine and
2589  // return if we have. We're checking it here, because we always want to
2590  // allow reclaiming at least one lease.
2591  if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2592  // Timeout. This will likely mean that we haven't been able to process
2593  // all leases we wanted to process. The reclamation pass will be
2594  // probably marked as incomplete.
2595  if (!incomplete_reclamation) {
2596  if (leases_processed < leases.size()) {
2597  incomplete_reclamation = true;
2598  }
2599  }
2600 
2603  .arg(timeout);
2604  break;
2605  }
2606  }
2607 
2608  // Stop measuring the time.
2609  stopwatch.stop();
2610 
2611  // Mark completion of the lease reclamation routine and present some stats.
2614  .arg(leases_processed)
2615  .arg(stopwatch.logFormatTotalDuration());
2616 
2617  // Check if this was an incomplete reclamation and increase the number of
2618  // consecutive incomplete reclamations.
2619  if (incomplete_reclamation) {
2620  ++incomplete_v6_reclamations_;
2621  // If the number of incomplete reclamations is beyond the threshold, we
2622  // need to issue a warning.
2623  if ((max_unwarned_cycles > 0) &&
2624  (incomplete_v6_reclamations_ > max_unwarned_cycles)) {
2626  .arg(max_unwarned_cycles);
2627  // We issued a warning, so let's now reset the counter.
2628  incomplete_v6_reclamations_ = 0;
2629  }
2630 
2631  } else {
2632  // This was a complete reclamation, so let's reset the counter.
2633  incomplete_v6_reclamations_ = 0;
2634 
2637  }
2638 }
2639 
2640 void
2644  .arg(secs);
2645 
2646  uint64_t deleted_leases = 0;
2647  try {
2648  // Try to delete leases from the lease database.
2649  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2650  deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
2651 
2652  } catch (const std::exception& ex) {
2654  .arg(ex.what());
2655  }
2656 
2659  .arg(deleted_leases);
2660 }
2661 
2662 void
2663 AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout,
2664  const bool remove_lease,
2665  const uint16_t max_unwarned_cycles) {
2666 
2669  .arg(max_leases)
2670  .arg(timeout);
2671 
2672  // Create stopwatch and automatically start it to measure the time
2673  // taken by the routine.
2674  util::Stopwatch stopwatch;
2675 
2676  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2677 
2678  // This value indicates if we have been able to deal with all expired
2679  // leases in this pass.
2680  bool incomplete_reclamation = false;
2681  Lease4Collection leases;
2682  // The value of 0 has a special meaning - reclaim all.
2683  if (max_leases > 0) {
2684  // If the value is non-zero, the caller has limited the number of
2685  // leases to reclaim. We obtain one lease more to see if there will
2686  // be still leases left after this pass.
2687  lease_mgr.getExpiredLeases4(leases, max_leases + 1);
2688  // There are more leases expired leases than we will process in this
2689  // pass, so we should mark it as an incomplete reclamation. We also
2690  // remove this extra lease (which we don't want to process anyway)
2691  // from the collection.
2692  if (leases.size() > max_leases) {
2693  leases.pop_back();
2694  incomplete_reclamation = true;
2695  }
2696 
2697  } else {
2698  // If there is no limitation on the number of leases to reclaim,
2699  // we will try to process all. Hence, we don't mark it as incomplete
2700  // reclamation just yet.
2701  lease_mgr.getExpiredLeases4(leases, max_leases);
2702  }
2703 
2704  // Do not initialize the callout handle until we know if there are any
2705  // lease4_expire callouts installed.
2706  CalloutHandlePtr callout_handle;
2707  if (!leases.empty() &&
2708  HooksManager::calloutsPresent(Hooks.hook_index_lease4_expire_)) {
2709  callout_handle = HooksManager::createCalloutHandle();
2710  }
2711 
2712  size_t leases_processed = 0;
2713  BOOST_FOREACH(Lease4Ptr lease, leases) {
2714 
2715  try {
2716  // Reclaim the lease.
2717  if (MultiThreadingMgr::instance().getMode()) {
2718  // The reclamation is exclusive of packet processing.
2719  WriteLockGuard exclusive(rw_mutex_);
2720 
2721  reclaimExpiredLease(lease, remove_lease, callout_handle);
2722  ++leases_processed;
2723  } else {
2724  reclaimExpiredLease(lease, remove_lease, callout_handle);
2725  ++leases_processed;
2726  }
2727 
2728  } catch (const std::exception& ex) {
2730  .arg(lease->addr_.toText())
2731  .arg(ex.what());
2732  }
2733 
2734  // Check if we have hit the timeout for running reclamation routine and
2735  // return if we have. We're checking it here, because we always want to
2736  // allow reclaiming at least one lease.
2737  if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) {
2738  // Timeout. This will likely mean that we haven't been able to process
2739  // all leases we wanted to process. The reclamation pass will be
2740  // probably marked as incomplete.
2741  if (!incomplete_reclamation) {
2742  if (leases_processed < leases.size()) {
2743  incomplete_reclamation = true;
2744  }
2745  }
2746 
2749  .arg(timeout);
2750  break;
2751  }
2752  }
2753 
2754  // Stop measuring the time.
2755  stopwatch.stop();
2756 
2757  // Mark completion of the lease reclamation routine and present some stats.
2760  .arg(leases_processed)
2761  .arg(stopwatch.logFormatTotalDuration());
2762 
2763  // Check if this was an incomplete reclamation and increase the number of
2764  // consecutive incomplete reclamations.
2765  if (incomplete_reclamation) {
2766  ++incomplete_v4_reclamations_;
2767  // If the number of incomplete reclamations is beyond the threshold, we
2768  // need to issue a warning.
2769  if ((max_unwarned_cycles > 0) &&
2770  (incomplete_v4_reclamations_ > max_unwarned_cycles)) {
2772  .arg(max_unwarned_cycles);
2773  // We issued a warning, so let's now reset the counter.
2774  incomplete_v4_reclamations_ = 0;
2775  }
2776 
2777  } else {
2778  // This was a complete reclamation, so let's reset the counter.
2779  incomplete_v4_reclamations_ = 0;
2780 
2783  }
2784 }
2785 
2786 template<typename LeasePtrType>
2787 void
2788 AllocEngine::reclaimExpiredLease(const LeasePtrType& lease, const bool remove_lease,
2789  const CalloutHandlePtr& callout_handle) {
2790  reclaimExpiredLease(lease, remove_lease ? DB_RECLAIM_REMOVE : DB_RECLAIM_UPDATE,
2791  callout_handle);
2792 }
2793 
2794 template<typename LeasePtrType>
2795 void
2796 AllocEngine::reclaimExpiredLease(const LeasePtrType& lease,
2797  const CalloutHandlePtr& callout_handle) {
2798  // This variant of the method is used by the code which allocates or
2799  // renews leases. It may be the case that the lease has already been
2800  // reclaimed, so there is nothing to do.
2801  if (!lease->stateExpiredReclaimed()) {
2802  reclaimExpiredLease(lease, DB_RECLAIM_LEAVE_UNCHANGED, callout_handle);
2803  }
2804 }
2805 
2806 void
2807 AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
2808  const DbReclaimMode& reclaim_mode,
2809  const CalloutHandlePtr& callout_handle) {
2810 
2813  .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
2814  .arg(lease->addr_.toText())
2815  .arg(static_cast<int>(lease->prefixlen_));
2816 
2817  // The skip flag indicates if the callouts have taken responsibility
2818  // for reclaiming the lease. The callout will set this to true if
2819  // it reclaims the lease itself. In this case the reclamation routine
2820  // will not update DNS nor update the database.
2821  bool skipped = false;
2822  if (callout_handle) {
2823 
2824  // Use the RAII wrapper to make sure that the callout handle state is
2825  // reset when this object goes out of scope. All hook points must do
2826  // it to prevent possible circular dependency between the callout
2827  // handle and its arguments.
2828  ScopedCalloutHandleState callout_handle_state(callout_handle);
2829 
2830  callout_handle->deleteAllArguments();
2831  callout_handle->setArgument("lease6", lease);
2832  callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2833 
2834  HooksManager::callCallouts(Hooks.hook_index_lease6_expire_,
2835  *callout_handle);
2836 
2837  skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2838  }
2839 
2842 
2843  if (!skipped) {
2844 
2845  // Generate removal name change request for D2, if required.
2846  // This will return immediately if the DNS wasn't updated
2847  // when the lease was created.
2848  queueNCR(CHG_REMOVE, lease);
2849 
2850  // Let's check if the lease that just expired is in DECLINED state.
2851  // If it is, we need to perform a couple extra steps.
2852  bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2853  if (lease->state_ == Lease::STATE_DECLINED) {
2854  // Do extra steps required for declined lease reclamation:
2855  // - call the recover hook
2856  // - bump decline-related stats
2857  // - log separate message
2858  // There's no point in keeping a declined lease after its
2859  // reclamation. A declined lease doesn't have any client
2860  // identifying information anymore. So we'll flag it for
2861  // removal unless the hook has set the skip flag.
2862  remove_lease = reclaimDeclined(lease);
2863  }
2864 
2865  if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2866  // Reclaim the lease - depending on the configuration, set the
2867  // expired-reclaimed state or simply remove it.
2868  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2869  reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
2870  std::bind(&LeaseMgr::updateLease6,
2871  &lease_mgr, ph::_1));
2872  }
2873  }
2874 
2875  // Update statistics.
2876 
2877  // Decrease number of assigned leases.
2878  if (lease->type_ == Lease::TYPE_NA) {
2879  // IA_NA
2880  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2881  lease->subnet_id_,
2882  "assigned-nas"),
2883  int64_t(-1));
2884 
2885  } else if (lease->type_ == Lease::TYPE_PD) {
2886  // IA_PD
2887  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2888  lease->subnet_id_,
2889  "assigned-pds"),
2890  int64_t(-1));
2891 
2892  }
2893 
2894  // Increase total number of reclaimed leases.
2895  StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
2896 
2897  // Increase number of reclaimed leases for a subnet.
2898  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2899  lease->subnet_id_,
2900  "reclaimed-leases"),
2901  int64_t(1));
2902 }
2903 
2904 void
2905 AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
2906  const DbReclaimMode& reclaim_mode,
2907  const CalloutHandlePtr& callout_handle) {
2908 
2911  .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
2912  .arg(lease->addr_.toText());
2913 
2914  // The skip flag indicates if the callouts have taken responsibility
2915  // for reclaiming the lease. The callout will set this to true if
2916  // it reclaims the lease itself. In this case the reclamation routine
2917  // will not update DNS nor update the database.
2918  bool skipped = false;
2919  if (callout_handle) {
2920 
2921  // Use the RAII wrapper to make sure that the callout handle state is
2922  // reset when this object goes out of scope. All hook points must do
2923  // it to prevent possible circular dependency between the callout
2924  // handle and its arguments.
2925  ScopedCalloutHandleState callout_handle_state(callout_handle);
2926 
2927  callout_handle->setArgument("lease4", lease);
2928  callout_handle->setArgument("remove_lease", reclaim_mode == DB_RECLAIM_REMOVE);
2929 
2930  HooksManager::callCallouts(Hooks.hook_index_lease4_expire_,
2931  *callout_handle);
2932 
2933  skipped = callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP;
2934  }
2935 
2938 
2939  if (!skipped) {
2940 
2941  // Generate removal name change request for D2, if required.
2942  // This will return immediately if the DNS wasn't updated
2943  // when the lease was created.
2944  queueNCR(CHG_REMOVE, lease);
2945  // Clear DNS fields so we avoid redundant removes.
2946  lease->hostname_.clear();
2947  lease->fqdn_fwd_ = false;
2948  lease->fqdn_rev_ = false;
2949 
2950  // Let's check if the lease that just expired is in DECLINED state.
2951  // If it is, we need to perform a couple extra steps.
2952  bool remove_lease = (reclaim_mode == DB_RECLAIM_REMOVE);
2953  if (lease->state_ == Lease::STATE_DECLINED) {
2954  // Do extra steps required for declined lease reclamation:
2955  // - call the recover hook
2956  // - bump decline-related stats
2957  // - log separate message
2958  // There's no point in keeping a declined lease after its
2959  // reclamation. A declined lease doesn't have any client
2960  // identifying information anymore. So we'll flag it for
2961  // removal unless the hook has set the skip flag.
2962  remove_lease = reclaimDeclined(lease);
2963  }
2964 
2965  if (reclaim_mode != DB_RECLAIM_LEAVE_UNCHANGED) {
2966  // Reclaim the lease - depending on the configuration, set the
2967  // expired-reclaimed state or simply remove it.
2968  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
2969  reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
2970  std::bind(&LeaseMgr::updateLease4,
2971  &lease_mgr, ph::_1));
2972  }
2973  }
2974 
2975  // Update statistics.
2976 
2977  // Decrease number of assigned addresses.
2978  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2979  lease->subnet_id_,
2980  "assigned-addresses"),
2981  int64_t(-1));
2982 
2983  // Increase total number of reclaimed leases.
2984  StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
2985 
2986  // Increase number of reclaimed leases for a subnet.
2987  StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
2988  lease->subnet_id_,
2989  "reclaimed-leases"),
2990  int64_t(1));
2991 }
2992 
2993 void
2997  .arg(secs);
2998 
2999  uint64_t deleted_leases = 0;
3000  try {
3001  // Try to delete leases from the lease database.
3002  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3003  deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
3004 
3005  } catch (const std::exception& ex) {
3007  .arg(ex.what());
3008  }
3009 
3012  .arg(deleted_leases);
3013 }
3014 
3015 bool
3016 AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
3017  if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3018  return (true);
3019  }
3020 
3021  if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_recover_)) {
3022  CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
3023 
3024  // Use the RAII wrapper to make sure that the callout handle state is
3025  // reset when this object goes out of scope. All hook points must do
3026  // it to prevent possible circular dependency between the callout
3027  // handle and its arguments.
3028  ScopedCalloutHandleState callout_handle_state(callout_handle);
3029 
3030  // Pass necessary arguments
3031  callout_handle->setArgument("lease4", lease);
3032 
3033  // Call the callouts
3034  HooksManager::callCallouts(Hooks.hook_index_lease4_recover_, *callout_handle);
3035 
3036  // Callouts decided to skip the action. This means that the lease is not
3037  // assigned, so the client will get NoAddrAvail as a result. The lease
3038  // won't be inserted into the database.
3039  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3041  .arg(lease->addr_.toText());
3042  return (false);
3043  }
3044  }
3045 
3047  .arg(lease->addr_.toText())
3048  .arg(lease->valid_lft_);
3049 
3050  StatsMgr& stats_mgr = StatsMgr::instance();
3051 
3052  // Decrease subnet specific counter for currently declined addresses
3053  stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3054  "declined-addresses"), static_cast<int64_t>(-1));
3055 
3056  // Decrease global counter for declined addresses
3057  stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3058 
3059  stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3060 
3061  stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3062  "reclaimed-declined-addresses"), static_cast<int64_t>(1));
3063 
3064  // Note that we do not touch assigned-addresses counters. Those are
3065  // modified in whatever code calls this method.
3066  return (true);
3067 }
3068 
3069 bool
3070 AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
3071  if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
3072  return (true);
3073  }
3074 
3075  if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_recover_)) {
3076  CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
3077 
3078  // Use the RAII wrapper to make sure that the callout handle state is
3079  // reset when this object goes out of scope. All hook points must do
3080  // it to prevent possible circular dependency between the callout
3081  // handle and its arguments.
3082  ScopedCalloutHandleState callout_handle_state(callout_handle);
3083 
3084  // Pass necessary arguments
3085  callout_handle->setArgument("lease6", lease);
3086 
3087  // Call the callouts
3088  HooksManager::callCallouts(Hooks.hook_index_lease6_recover_, *callout_handle);
3089 
3090  // Callouts decided to skip the action. This means that the lease is not
3091  // assigned, so the client will get NoAddrAvail as a result. The lease
3092  // won't be inserted into the database.
3093  if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
3095  .arg(lease->addr_.toText());
3096  return (false);
3097  }
3098  }
3099 
3101  .arg(lease->addr_.toText())
3102  .arg(lease->valid_lft_);
3103 
3104  StatsMgr& stats_mgr = StatsMgr::instance();
3105 
3106  // Decrease subnet specific counter for currently declined addresses
3107  stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3108  "declined-addresses"), static_cast<int64_t>(-1));
3109 
3110  // Decrease global counter for declined addresses
3111  stats_mgr.addValue("declined-addresses", static_cast<int64_t>(-1));
3112 
3113  stats_mgr.addValue("reclaimed-declined-addresses", static_cast<int64_t>(1));
3114 
3115  stats_mgr.addValue(StatsMgr::generateName("subnet", lease->subnet_id_,
3116  "reclaimed-declined-addresses"), static_cast<int64_t>(1));
3117 
3118  // Note that we do not touch assigned-nas counters. Those are
3119  // modified in whatever code calls this method.
3120 
3121  return (true);
3122 }
3123 
3124 template<typename LeasePtrType>
3125 void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
3126  const bool remove_lease,
3127  const std::function<void (const LeasePtrType&)>&
3128  lease_update_fun) const {
3129 
3130  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3131 
3132  // Reclaim the lease - depending on the configuration, set the
3133  // expired-reclaimed state or simply remove it.
3134  if (remove_lease) {
3135  lease_mgr.deleteLease(lease);
3136  } else if (lease_update_fun) {
3137  // Clear FQDN information as we have already sent the
3138  // name change request to remove the DNS record.
3139  lease->reuseable_valid_lft_ = 0;
3140  lease->hostname_.clear();
3141  lease->fqdn_fwd_ = false;
3142  lease->fqdn_rev_ = false;
3143  lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
3144  lease_update_fun(lease);
3145 
3146  } else {
3147  return;
3148  }
3149 
3150  // Lease has been reclaimed.
3153  .arg(lease->addr_.toText());
3154 }
3155 
3156 } // namespace dhcp
3157 } // namespace isc
3158 
3159 // ##########################################################################
3160 // # DHCPv4 lease allocation code starts here.
3161 // ##########################################################################
3162 
3163 namespace {
3164 
3182 bool
3183 addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
3184  // When out-of-pool flag is true the server may assume that all host
3185  // reservations are for addresses that do not belong to the dynamic pool.
3186  // Therefore, it can skip the reservation checks when dealing with in-pool
3187  // addresses.
3188  if (ctx.subnet_ && ctx.subnet_->getReservationsInSubnet() &&
3189  (!ctx.subnet_->getReservationsOutOfPool() ||
3190  !ctx.subnet_->inPool(Lease::TYPE_V4, address))) {
3191  // The global parameter ip-reservations-unique controls whether it is allowed
3192  // to specify multiple reservations for the same IP address or delegated prefix
3193  // or IP reservations must be unique. Some host backends do not support the
3194  // former, thus we can't always use getAll4 calls to get the reservations
3195  // for the given IP. When we're in the default mode, when IP reservations
3196  // are unique, we should call get4 (supported by all backends). If we're in
3197  // the mode in which non-unique reservations are allowed the backends which
3198  // don't support it are not used and we can safely call getAll4.
3199  ConstHostCollection hosts;
3200  if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
3201  // Reservations are unique. It is safe to call get4 to get the unique host.
3202  ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
3203  if (host) {
3204  hosts.push_back(host);
3205  }
3206  } else {
3207  // Reservations can be non-unique. Need to get all reservations for that address.
3208  hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
3209  }
3210 
3211  for (auto host : hosts) {
3212  for (const AllocEngine::IdentifierPair& id_pair : ctx.host_identifiers_) {
3213  // If we find the matching host we know that this address is reserved
3214  // for us and we can return immediately.
3215  if (id_pair.first == host->getIdentifierType() &&
3216  id_pair.second == host->getIdentifier()) {
3217  return (false);
3218  }
3219  }
3220  }
3221  // We didn't find a matching host. If there are any reservations it means that
3222  // address is reserved for another client or multiple clients. If there are
3223  // no reservations address is not reserved for another client.
3224  return (!hosts.empty());
3225  }
3226  return (false);
3227 }
3228 
3244 bool
3245 hasAddressReservation(AllocEngine::ClientContext4& ctx) {
3246  if (ctx.hosts_.empty()) {
3247  return (false);
3248  }
3249 
3250  // Flag used to perform search for global reservations only once.
3251  bool search_global_done = false;
3252 
3253  Subnet4Ptr subnet = ctx.subnet_;
3254  while (subnet) {
3255  // Skip search if the global reservations have already been examined.
3256  if (!search_global_done && subnet->getReservationsGlobal()) {
3257  auto host = ctx.hosts_.find(SUBNET_ID_GLOBAL);
3258  // if we want global + other modes we would need to
3259  // return only if true, else continue
3260  if (host != ctx.hosts_.end() && host->second &&
3261  !host->second->getIPv4Reservation().isV4Zero()) {
3262  return (true);
3263  }
3264  // No need to perform this search again as there are no global
3265  // reservations.
3266  search_global_done = true;
3267  }
3268 
3269  if (subnet->getReservationsInSubnet()) {
3270  auto host = ctx.hosts_.find(subnet->getID());
3271  // The out-of-pool flag indicates that no client should be assigned
3272  // reserved addresses from within the dynamic pool, and for that
3273  // reason look only for reservations that are outside the pools,
3274  // hence the inPool check.
3275  if (host != ctx.hosts_.end() && host->second) {
3276  auto reservation = host->second->getIPv4Reservation();
3277  if (!reservation.isV4Zero() &&
3278  (!subnet->getReservationsOutOfPool() ||
3279  !subnet->inPool(Lease::TYPE_V4, reservation))) {
3280  ctx.subnet_ = subnet;
3281  return (true);
3282  }
3283  }
3284  }
3285 
3286  // No address reservation found here, so let's try another subnet
3287  // within the same shared network.
3288  subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3289  }
3290 
3291  return (false);
3292 }
3293 
3309 void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
3310  LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
3311 
3312  Subnet4Ptr original_subnet = ctx.subnet_;
3313 
3314  // Client identifier is optional. First check if we can try to lookup
3315  // by client-id.
3316  bool try_clientid_lookup = (ctx.clientid_ &&
3318  ctx.query_->getClasses()));
3319 
3320  // If it is possible to use client identifier to try to find client's lease.
3321  if (try_clientid_lookup) {
3322  // Get all leases for this client identifier. When shared networks are
3323  // in use it is more efficient to make a single query rather than
3324  // multiple queries, one for each subnet.
3325  Lease4Collection leases_client_id = lease_mgr.getLease4(*ctx.clientid_);
3326 
3327  // Iterate over the subnets within the shared network to see if any client's
3328  // lease belongs to them.
3329  for (Subnet4Ptr subnet = original_subnet; subnet;
3330  subnet = subnet->getNextSubnet(original_subnet,
3331  ctx.query_->getClasses())) {
3332 
3333  // If client identifier has been supplied and the server wasn't
3334  // explicitly configured to ignore client identifiers for this subnet
3335  // check if there is a lease within this subnet.
3336  if (subnet->getMatchClientId()) {
3337  for (auto l = leases_client_id.begin(); l != leases_client_id.end(); ++l) {
3338  if ((*l)->subnet_id_ == subnet->getID()) {
3339  // Lease found, so stick to this lease.
3340  client_lease = (*l);
3341  ctx.subnet_ = subnet;
3342  return;
3343  }
3344  }
3345  }
3346  }
3347  }
3348 
3349  // If no lease found using the client identifier, try the lookup using
3350  // the HW address.
3351  if (!client_lease && ctx.hwaddr_) {
3352 
3353  // Get all leases for this HW address.
3354  Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
3355 
3356  for (Subnet4Ptr subnet = original_subnet; subnet;
3357  subnet = subnet->getNextSubnet(original_subnet,
3358  ctx.query_->getClasses())) {
3359  ClientIdPtr client_id;
3360  if (subnet->getMatchClientId()) {
3361  client_id = ctx.clientid_;
3362  }
3363 
3364  // Try to find the lease that matches current subnet and belongs to
3365  // this client, so both HW address and client identifier match.
3366  for (Lease4Collection::const_iterator client_lease_it = leases_hw_address.begin();
3367  client_lease_it != leases_hw_address.end(); ++client_lease_it) {
3368  Lease4Ptr existing_lease = *client_lease_it;
3369  if ((existing_lease->subnet_id_ == subnet->getID()) &&
3370  existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
3371  // Found the lease of this client, so return it.
3372  client_lease = existing_lease;
3373  // We got a lease but the subnet it belongs to may differ from
3374  // the original subnet. Let's now stick to this subnet.
3375  ctx.subnet_ = subnet;
3376  return;
3377  }
3378  }
3379  }
3380  }
3381 }
3382 
3395 bool
3396 inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
3397  // If the subnet belongs to a shared network we will be iterating
3398  // over the subnets that belong to this shared network.
3399  Subnet4Ptr current_subnet = ctx.subnet_;
3400  while (current_subnet) {
3401 
3402  if (current_subnet->inPool(Lease::TYPE_V4, address,
3403  ctx.query_->getClasses())) {
3404  // We found a subnet that this address belongs to, so it
3405  // seems that this subnet is the good candidate for allocation.
3406  // Let's update the selected subnet.
3407  ctx.subnet_ = current_subnet;
3408  return (true);
3409  }
3410 
3411  current_subnet = current_subnet->getNextSubnet(ctx.subnet_,
3412  ctx.query_->getClasses());
3413  }
3414 
3415  return (false);
3416 }
3417 
3418 } // namespace
3419 
3420 namespace isc {
3421 namespace dhcp {
3422 
3425  subnet_(), clientid_(), hwaddr_(),
3426  requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
3427  fwd_dns_update_(false), rev_dns_update_(false),
3429  old_lease_(), new_lease_(), hosts_(), conflicting_lease_(),
3430  query_(), host_identifiers_(), unknown_requested_addr_(false),
3431  ddns_params_() {
3432 }
3433 
3435  const ClientIdPtr& clientid,
3436  const HWAddrPtr& hwaddr,
3437  const asiolink::IOAddress& requested_addr,
3438  const bool fwd_dns_update,
3439  const bool rev_dns_update,
3440  const std::string& hostname,
3441  const bool fake_allocation)
3443  subnet_(subnet), clientid_(clientid), hwaddr_(hwaddr),
3444  requested_address_(requested_addr),
3445  fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
3446  hostname_(hostname), callout_handle_(),
3447  fake_allocation_(fake_allocation), old_lease_(), new_lease_(),
3449  ddns_params_(new DdnsParams()) {
3450 
3451  // Initialize host identifiers.
3452  if (hwaddr) {
3453  addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
3454  }
3455 }
3456 
3459  if (subnet_ && subnet_->getReservationsInSubnet()) {
3460  auto host = hosts_.find(subnet_->getID());
3461  if (host != hosts_.cend()) {
3462  return (host->second);
3463  }
3464  }
3465 
3466  return (globalHost());
3467 }
3468 
3471  if (subnet_ && subnet_->getReservationsGlobal()) {
3472  auto host = hosts_.find(SUBNET_ID_GLOBAL);
3473  if (host != hosts_.cend()) {
3474  return (host->second);
3475  }
3476  }
3477 
3478  return (ConstHostPtr());
3479 }
3480 
3483  // We already have it return it unless the context subnet has changed.
3484  if (ddns_params_ && subnet_ && (subnet_->getID() == ddns_params_->getSubnetId())) {
3485  return (ddns_params_);
3486  }
3487 
3488  // Doesn't exist yet or is stale, (re)create it.
3489  if (subnet_) {
3490  ddns_params_ = CfgMgr::instance().getCurrentCfg()->getDdnsParams(subnet_);
3491  return (ddns_params_);
3492  }
3493 
3494  // Asked for it without a subnet? This case really shouldn't occur but
3495  // for now let's return an instance with default values.
3496  return (DdnsParamsPtr(new DdnsParams()));
3497 }
3498 
3499 Lease4Ptr
3501  // The NULL pointer indicates that the old lease didn't exist. It may
3502  // be later set to non NULL value if existing lease is found in the
3503  // database.
3504  ctx.old_lease_.reset();
3505  ctx.new_lease_.reset();
3506 
3507  // Before we start allocation process, we need to make sure that the
3508  // selected subnet is allowed for this client. If not, we'll try to
3509  // use some other subnet within the shared network. If there are no
3510  // subnets allowed for this client within the shared network, we
3511  // can't allocate a lease.
3512  Subnet4Ptr subnet = ctx.subnet_;
3513  if (subnet && !subnet->clientSupported(ctx.query_->getClasses())) {
3514  ctx.subnet_ = subnet->getNextSubnet(subnet, ctx.query_->getClasses());
3515  }
3516 
3517  try {
3518  if (!ctx.subnet_) {
3519  isc_throw(BadValue, "Can't allocate IPv4 address without subnet");
3520  }
3521 
3522  if (!ctx.hwaddr_) {
3523  isc_throw(BadValue, "HWAddr must be defined");
3524  }
3525 
3526  if (ctx.fake_allocation_) {
3527  return (discoverLease4(ctx));
3528 
3529  } else {
3530  ctx.new_lease_ = requestLease4(ctx);
3531  }
3532 
3533  } catch (const isc::Exception& e) {
3534  // Some other error, return an empty lease.
3536  .arg(ctx.query_->getLabel())
3537  .arg(e.what());
3538  }
3539 
3540  return (ctx.new_lease_);
3541 }
3542 
3543 void
3545  // If there is no subnet, there is nothing to do.
3546  if (!ctx.subnet_) {
3547  return;
3548  }
3549 
3550  auto subnet = ctx.subnet_;
3551 
3552  // If already done just return.
3554  !subnet->getReservationsInSubnet()) {
3555  return;
3556  }
3557 
3558  // @todo: This code can be trivially optimized.
3560  subnet->getReservationsGlobal()) {
3561  ConstHostPtr ghost = findGlobalReservation(ctx);
3562  if (ghost) {
3563  ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
3564 
3565  // If we had only to fetch global reservations it is done.
3566  if (!subnet->getReservationsInSubnet()) {
3567  return;
3568  }
3569  }
3570  }
3571 
3572  std::map<SubnetID, ConstHostPtr> host_map;
3573  SharedNetwork4Ptr network;
3574  subnet->getSharedNetwork(network);
3575 
3576  // If the subnet belongs to a shared network it is usually going to be
3577  // more efficient to make a query for all reservations for a particular
3578  // client rather than a query for each subnet within this shared network.
3579  // The only case when it is going to be less efficient is when there are
3580  // more host identifier types in use than subnets within a shared network.
3581  // As it breaks RADIUS use of host caching this can be disabled by the
3582  // host manager.
3583  const bool use_single_query = network &&
3585  (network->getAllSubnets()->size() > ctx.host_identifiers_.size());
3586 
3587  if (use_single_query) {
3588  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3589  ConstHostCollection hosts = HostMgr::instance().getAll(id_pair.first,
3590  &id_pair.second[0],
3591  id_pair.second.size());
3592  // Store the hosts in the temporary map, because some hosts may
3593  // belong to subnets outside of the shared network. We'll need
3594  // to eliminate them.
3595  for (auto host = hosts.begin(); host != hosts.end(); ++host) {
3596  if ((*host)->getIPv4SubnetID() != SUBNET_ID_GLOBAL) {
3597  host_map[(*host)->getIPv4SubnetID()] = *host;
3598  }
3599  }
3600  }
3601  }
3602 
3603  // We can only search for the reservation if a subnet has been selected.
3604  while (subnet) {
3605 
3606  // Only makes sense to get reservations if the client has access
3607  // to the class and host reservations are enabled for this subnet.
3608  if (subnet->clientSupported(ctx.query_->getClasses()) &&
3609  subnet->getReservationsInSubnet()) {
3610  // Iterate over configured identifiers in the order of preference
3611  // and try to use each of them to search for the reservations.
3612  if (use_single_query) {
3613  if (host_map.count(subnet->getID()) > 0) {
3614  ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
3615  }
3616  } else {
3617  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3618  // Attempt to find a host using a specified identifier.
3619  ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
3620  id_pair.first,
3621  &id_pair.second[0],
3622  id_pair.second.size());
3623  // If we found matching host for this subnet.
3624  if (host) {
3625  ctx.hosts_[subnet->getID()] = host;
3626  break;
3627  }
3628  }
3629  }
3630  }
3631 
3632  // We need to get to the next subnet if this is a shared network. If it
3633  // is not (a plain subnet), getNextSubnet will return NULL and we're
3634  // done here.
3635  subnet = subnet->getNextSubnet(ctx.subnet_, ctx.query_->getClasses());
3636  }
3637 }
3638 
3641  ConstHostPtr host;
3642  for (const IdentifierPair& id_pair : ctx.host_identifiers_) {
3643  // Attempt to find a host using a specified identifier.
3644  host = HostMgr::instance().get4(SUBNET_ID_GLOBAL, id_pair.first,
3645  &id_pair.second[0], id_pair.second.size());
3646 
3647  // If we found matching global host we're done.
3648  if (host) {
3649  break;
3650  }
3651  }
3652 
3653  return (host);
3654 }
3655 
3656 Lease4Ptr
3657 AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
3658  // Find an existing lease for this client. This function will return null
3659  // if there is a conflict with existing lease and the allocation should
3660  // not be continued.
3661  Lease4Ptr client_lease;
3662  findClientLease(ctx, client_lease);
3663 
3664  // new_lease will hold the pointer to the lease that we will offer to the
3665  // caller.
3666  Lease4Ptr new_lease;
3667 
3668  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
3669 
3670  // Check if there is a reservation for the client. If there is, we want to
3671  // assign the reserved address, rather than any other one.
3672  if (hasAddressReservation(ctx)) {
3673 
3676  .arg(ctx.query_->getLabel())
3677  .arg(ctx.currentHost()->getIPv4Reservation().toText());
3678 
3679  // If the client doesn't have a lease or the leased address is different
3680  // than the reserved one then let's try to allocate the reserved address.
3681  // Otherwise the address that the client has is the one for which it
3682  // has a reservation, so just renew it.
3683  if (!client_lease || (client_lease->addr_ != ctx.currentHost()->getIPv4Reservation())) {
3684  // The call below will return a pointer to the lease for the address
3685  // reserved to this client, if the lease is available, i.e. is not
3686  // currently assigned to any other client.
3687  // Note that we don't remove the existing client's lease at this point
3688  // because this is not a real allocation, we just offer what we can
3689  // allocate in the DHCPREQUEST time.
3690  new_lease = allocateOrReuseLease4(ctx.currentHost()->getIPv4Reservation(), ctx,
3691  callout_status);
3692  if (!new_lease) {
3694  .arg(ctx.query_->getLabel())
3695  .arg(ctx.currentHost()->getIPv4Reservation().toText())
3696  .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
3697  "(no lease info)");
3698  }
3699 
3700  } else {
3701  new_lease = renewLease4(client_lease, ctx);
3702  }
3703  }
3704 
3705  // Client does not have a reservation or the allocation of the reserved
3706  // address has failed, probably because the reserved address is in use
3707  // by another client. If the client has a lease, we will check if we can
3708  // offer this lease to the client. The lease can't be offered in the
3709  // situation when it is reserved for another client or when the address
3710  // is not in the dynamic pool. The former may be the result of adding the
3711  // new reservation for the address used by this client. The latter may
3712  // be due to the client using the reserved out-of-the pool address, for
3713  // which the reservation has just been removed.
3714  if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
3715  !addressReserved(client_lease->addr_, ctx)) {
3716 
3719  .arg(ctx.query_->getLabel());
3720 
3721  new_lease = renewLease4(client_lease, ctx);
3722  }
3723 
3724  // The client doesn't have any lease or the lease can't be offered
3725  // because it is either reserved for some other client or the
3726  // address is not in the dynamic pool.
3727  // Let's use the client's hint (requested IP address), if the client
3728  // has provided it, and try to offer it. This address must not be
3729  // reserved for another client, and must be in the range of the
3730  // dynamic pool.
3731  if (!new_lease && !ctx.requested_address_.isV4Zero() &&
3732  inAllowedPool(ctx, ctx.requested_address_) &&
3733  !addressReserved(ctx.requested_address_, ctx)) {
3734 
3737  .arg(ctx.requested_address_.toText())
3738  .arg(ctx.query_->getLabel());
3739 
3740  new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3741  callout_status);
3742  }
3743 
3744  // The allocation engine failed to allocate all of the candidate
3745  // addresses. We will now use the allocator to pick the address
3746  // from the dynamic pool.
3747  if (!new_lease) {
3748 
3751  .arg(ctx.query_->getLabel());
3752 
3753  new_lease = allocateUnreservedLease4(ctx);
3754  }
3755 
3756  // Some of the methods like reuseExpiredLease4 may set the old lease to point
3757  // to the lease which they remove/override. If it is not set, but we have
3758  // found that the client has the lease the client's lease is the one
3759  // to return as an old lease.
3760  if (!ctx.old_lease_ && client_lease) {
3761  ctx.old_lease_ = client_lease;
3762  }
3763 
3764  return (new_lease);
3765 }
3766 
3767 Lease4Ptr
3768 AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
3769  // Find an existing lease for this client. This function will return null
3770  // if there is a conflict with existing lease and the allocation should
3771  // not be continued.
3772  Lease4Ptr client_lease;
3773  findClientLease(ctx, client_lease);
3774 
3775  // When the client sends the DHCPREQUEST, it should always specify the
3776  // address which it is requesting or renewing. That is, the client should
3777  // either use the requested IP address option or set the ciaddr. However,
3778  // we try to be liberal and allow the clients to not specify an address
3779  // in which case the allocation engine will pick a suitable address
3780  // for the client.
3781  if (!ctx.requested_address_.isV4Zero()) {
3782  // If the client has specified an address, make sure this address
3783  // is not reserved for another client. If it is, stop here because
3784  // we can't allocate this address.
3785  if (addressReserved(ctx.requested_address_, ctx)) {
3786 
3789  .arg(ctx.query_->getLabel())
3790  .arg(ctx.requested_address_.toText());
3791 
3792  return (Lease4Ptr());
3793  }
3794 
3795  } else if (hasAddressReservation(ctx)) {
3796  // The client hasn't specified an address to allocate, so the
3797  // allocation engine needs to find an appropriate address.
3798  // If there is a reservation for the client, let's try to
3799  // allocate the reserved address.
3800  ctx.requested_address_ = ctx.currentHost()->getIPv4Reservation();
3801 
3804  .arg(ctx.query_->getLabel())
3805  .arg(ctx.requested_address_.toText());
3806  }
3807 
3808  if (!ctx.requested_address_.isV4Zero()) {
3809  // There is a specific address to be allocated. Let's find out if
3810  // the address is in use.
3812  // If the address is in use (allocated and not expired), we check
3813  // if the address is in use by our client or another client.
3814  // If it is in use by another client, the address can't be
3815  // allocated.
3816  if (existing && !existing->expired() &&
3817  !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
3818  ctx.clientid_ : ClientIdPtr())) {
3819 
3822  .arg(ctx.query_->getLabel())
3823  .arg(ctx.requested_address_.toText());
3824 
3825  return (Lease4Ptr());
3826  }
3827 
3828  // If the client has a reservation but it is requesting a different
3829  // address it is possible that the client was offered this different
3830  // address because the reserved address is in use. We will have to
3831  // check if the address is in use.
3832  if (hasAddressReservation(ctx) &&
3833  (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) {
3834  existing =
3835  LeaseMgrFactory::instance().getLease4(ctx.currentHost()->getIPv4Reservation());
3836  // If the reserved address is not in use, i.e. the lease doesn't
3837  // exist or is expired, and the client is requesting a different
3838  // address, return NULL. The client should go back to the
3839  // DHCPDISCOVER and the reserved address will be offered.
3840  if (!existing || existing->expired()) {
3841 
3844  .arg(ctx.query_->getLabel())
3845  .arg(ctx.currentHost()->getIPv4Reservation().toText())
3846  .arg(ctx.requested_address_.toText());
3847 
3848  return (Lease4Ptr());
3849  }
3850  }
3851 
3852  // The use of the out-of-pool addresses is only allowed when the requested
3853  // address is reserved for the client. If the address is not reserved one
3854  // and it doesn't belong to the dynamic pool, do not allocate it.
3855  if ((!hasAddressReservation(ctx) ||
3856  (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
3857  !inAllowedPool(ctx, ctx.requested_address_)) {
3858 
3861  .arg(ctx.query_->getLabel())
3862  .arg(ctx.requested_address_);
3863 
3864  ctx.unknown_requested_addr_ = true;
3865  return (Lease4Ptr());
3866  }
3867  }
3868 
3869  // We have gone through all the checks, so we can now allocate the address
3870  // for the client.
3871 
3872  // If the client is requesting an address which is assigned to the client
3873  // let's just renew this address. Also, renew this address if the client
3874  // doesn't request any specific address.
3875  // Added extra checks: the address is reserved for this client or belongs
3876  // to the dynamic pool for the case the pool class has changed before the
3877  // request.
3878  if (client_lease) {
3879  if (((client_lease->addr_ == ctx.requested_address_) ||
3880  ctx.requested_address_.isV4Zero()) &&
3881  ((hasAddressReservation(ctx) &&
3882  (ctx.currentHost()->getIPv4Reservation() == ctx.requested_address_)) ||
3883  inAllowedPool(ctx, client_lease->addr_))) {
3884 
3887  .arg(ctx.query_->getLabel())
3888  .arg(ctx.requested_address_);
3889 
3890  return (renewLease4(client_lease, ctx));
3891  }
3892  }
3893 
3894  // new_lease will hold the pointer to the allocated lease if we allocate
3895  // successfully.
3896  Lease4Ptr new_lease;
3897 
3898  // The client doesn't have the lease or it is requesting an address
3899  // which it doesn't have. Let's try to allocate the requested address.
3900  if (!ctx.requested_address_.isV4Zero()) {
3901 
3904  .arg(ctx.query_->getLabel())
3905  .arg(ctx.requested_address_.toText());
3906 
3907  // The call below will return a pointer to the lease allocated
3908  // for the client if there is no lease for the requested address,
3909  // or the existing lease has expired. If the allocation fails,
3910  // e.g. because the lease is in use, we will return NULL to
3911  // indicate that we were unable to allocate the lease.
3912  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
3913  new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
3914  callout_status);
3915 
3916  } else {
3917 
3920  .arg(ctx.query_->getLabel());
3921 
3922  // We will only get here if the client didn't specify which
3923  // address it wanted to be allocated. The allocation engine will
3924  // to pick the address from the dynamic pool.
3925  new_lease = allocateUnreservedLease4(ctx);
3926  }
3927 
3928  // If we allocated the lease for the client, but the client already had a
3929  // lease, we will need to return the pointer to the previous lease and
3930  // the previous lease needs to be removed from the lease database.
3931  if (new_lease && client_lease) {
3932  ctx.old_lease_ = Lease4Ptr(new Lease4(*client_lease));
3933 
3936  .arg(ctx.query_->getLabel())
3937  .arg(client_lease->addr_.toText());
3938 
3939  if (LeaseMgrFactory::instance().deleteLease(client_lease)) {
3940  // Need to decrease statistic for assigned addresses.
3941  StatsMgr::instance().addValue(
3942  StatsMgr::generateName("subnet", client_lease->subnet_id_,
3943  "assigned-addresses"),
3944  static_cast<int64_t>(-1));
3945  }
3946  }
3947 
3948  // Return the allocated lease or NULL pointer if allocation was
3949  // unsuccessful.
3950  return (new_lease);
3951 }
3952 
3953 uint32_t
3955 
3956  // If it's BOOTP, use infinite valid lifetime.
3957  if (ctx.query_->inClass("BOOTP")) {
3958  return (Lease::INFINITY_LFT);
3959  }
3960 
3961  // Use the dhcp-lease-time content from the client if it's there.
3962  uint32_t requested_lft = 0;
3963  OptionPtr opt = ctx.query_->getOption(DHO_DHCP_LEASE_TIME);
3964  if (opt) {
3965  OptionUint32Ptr opt_lft = boost::dynamic_pointer_cast<OptionInt<uint32_t> >(opt);
3966  if (opt_lft) {
3967  requested_lft = opt_lft->getValue();
3968  }
3969  }
3970 
3971  // If the triplet is specified in one of our classes use it.
3972  // We use the first one we find.
3973  Triplet<uint32_t> candidate_lft;
3974  const ClientClasses classes = ctx.query_->getClasses();
3975  if (!classes.empty()) {
3976  // Let's get class definitions
3977  const ClientClassDictionaryPtr& dict =
3978  CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
3979 
3980  // Iterate over the assigned class defintions.
3981  for (ClientClasses::const_iterator name = classes.cbegin();
3982  name != classes.cend(); ++name) {
3983  ClientClassDefPtr cl = dict->findClass(*name);
3984  if (cl && (!cl->getValid().unspecified())) {
3985  candidate_lft = cl->getValid();
3986  break;
3987  }
3988  }
3989  }
3990 
3991  // If no classes specified it, get it from the subnet.
3992  if (!candidate_lft) {
3993  candidate_lft = ctx.subnet_->getValid();
3994  }
3995 
3996  // If client requested a value, use the value bounded by
3997  // the candidate triplet.
3998  if (requested_lft > 0) {
3999  return (candidate_lft.get(requested_lft));
4000  }
4001 
4002  // Use the candidate's default value.
4003  return (candidate_lft.get());
4004 }
4005 
4006 Lease4Ptr
4007 AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
4008  CalloutHandle::CalloutNextStep& callout_status) {
4009  if (!ctx.hwaddr_) {
4010  isc_throw(BadValue, "Can't create a lease with NULL HW address");
4011  }
4012  if (!ctx.subnet_) {
4013  isc_throw(BadValue, "Can't create a lease without a subnet");
4014  }
4015 
4016  // Get the context appropriate valid lifetime.
4017  uint32_t valid_lft = getValidLft(ctx);
4018 
4019  time_t now = time(NULL);
4020 
4021  ClientIdPtr client_id;
4022  if (ctx.subnet_->getMatchClientId()) {
4023  client_id = ctx.clientid_;
4024  }
4025 
4026  Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
4027  valid_lft, now, ctx.subnet_->getID()));
4028 
4029  // Set FQDN specific lease parameters.
4030  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4031  lease->fqdn_rev_ = ctx.rev_dns_update_;
4032  lease->hostname_ = ctx.hostname_;
4033 
4034  // Add(update) the extended information on the lease.
4035  static_cast<void>(updateLease4ExtendedInfo(lease, ctx));
4036 
4037  // Let's execute all callouts registered for lease4_select
4038  if (ctx.callout_handle_ &&
4039  HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4040 
4041  // Use the RAII wrapper to make sure that the callout handle state is
4042  // reset when this object goes out of scope. All hook points must do
4043  // it to prevent possible circular dependency between the callout
4044  // handle and its arguments.
4045  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4046 
4047  // Enable copying options from the packet within hook library.
4048  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4049 
4050  // Pass necessary arguments
4051  // Pass the original client query
4052  ctx.callout_handle_->setArgument("query4", ctx.query_);
4053 
4054  // Subnet from which we do the allocation (That's as far as we can go
4055  // with using SubnetPtr to point to Subnet4 object. Users should not
4056  // be confused with dynamic_pointer_casts. They should get a concrete
4057  // pointer (Subnet4Ptr) pointing to a Subnet4 object.
4058  Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4059  ctx.callout_handle_->setArgument("subnet4", subnet4);
4060 
4061  // Is this solicit (fake = true) or request (fake = false)
4062  ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4063 
4064  // Pass the intended lease as well
4065  ctx.callout_handle_->setArgument("lease4", lease);
4066 
4067  // This is the first callout, so no need to clear any arguments
4068  HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4069 
4070  callout_status = ctx.callout_handle_->getStatus();
4071 
4072  // Callouts decided to skip the action. This means that the lease is not
4073  // assigned, so the client will get NoAddrAvail as a result. The lease
4074  // won't be inserted into the database.
4075  if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4077  return (Lease4Ptr());
4078  }
4079 
4080  // Let's use whatever callout returned. Hopefully it is the same lease
4081  // we handled to it.
4082  ctx.callout_handle_->getArgument("lease4", lease);
4083  }
4084 
4085  if (!ctx.fake_allocation_) {
4086  // That is a real (REQUEST) allocation
4087  bool status = LeaseMgrFactory::instance().addLease(lease);
4088  if (status) {
4089 
4090  // The lease insertion succeeded, let's bump up the statistic.
4091  StatsMgr::instance().addValue(
4092  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4093  "assigned-addresses"),
4094  static_cast<int64_t>(1));
4095  StatsMgr::instance().addValue(
4096  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4097  "cumulative-assigned-addresses"),
4098  static_cast<int64_t>(1));
4099  StatsMgr::instance().addValue("cumulative-assigned-addresses",
4100  static_cast<int64_t>(1));
4101 
4102  return (lease);
4103  } else {
4104  // One of many failures with LeaseMgr (e.g. lost connection to the
4105  // database, database failed etc.). One notable case for that
4106  // is that we are working in multi-process mode and we lost a race
4107  // (some other process got that address first)
4108  return (Lease4Ptr());
4109  }
4110  } else {
4111  // That is only fake (DISCOVER) allocation
4112 
4113  // It is for OFFER only. We should not insert the lease and callers
4114  // have already verified the lease does not exist in the database.
4115  return (lease);
4116  }
4117 }
4118 
4119 Lease4Ptr
4120 AllocEngine::renewLease4(const Lease4Ptr& lease,
4122  if (!lease) {
4123  isc_throw(BadValue, "null lease specified for renewLease4");
4124  }
4125 
4126  // Let's keep the old data. This is essential if we are using memfile
4127  // (the lease returned points directly to the lease4 object in the database)
4128  // We'll need it if we want to skip update (i.e. roll back renewal)
4130  Lease4Ptr old_values = boost::make_shared<Lease4>(*lease);
4131  ctx.old_lease_.reset(new Lease4(*old_values));
4132 
4133  // Update the lease with the information from the context.
4134  // If there was no significant changes, try reuse.
4135  lease->reuseable_valid_lft_ = 0;
4136  if (!updateLease4Information(lease, ctx)) {
4137  setLeaseReusable(lease, ctx);
4138  }
4139 
4140  if (!ctx.fake_allocation_) {
4141  // If the lease is expired we have to reclaim it before
4142  // re-assigning it to the client. The lease reclamation
4143  // involves execution of hooks and DNS update.
4144  if (ctx.old_lease_->expired()) {
4145  reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
4146  }
4147 
4148  lease->state_ = Lease::STATE_DEFAULT;
4149  }
4150 
4151  bool skip = false;
4152  // Execute all callouts registered for lease4_renew.
4153  if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_renew_)) {
4154 
4155  // Use the RAII wrapper to make sure that the callout handle state is
4156  // reset when this object goes out of scope. All hook points must do
4157  // it to prevent possible circular dependency between the callout
4158  // handle and its arguments.
4159  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4160 
4161  // Enable copying options from the packet within hook library.
4162  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(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 
4170  // Pass the parameters. Note the clientid is passed only if match-client-id
4171  // is set. This is done that way, because the lease4-renew hook point is
4172  // about renewing a lease and the configuration parameter says the
4173  // client-id should be ignored. Hence no clientid value if match-client-id
4174  // is false.
4175  ctx.callout_handle_->setArgument("query4", ctx.query_);
4176  ctx.callout_handle_->setArgument("subnet4", subnet4);
4177  ctx.callout_handle_->setArgument("clientid", subnet4->getMatchClientId() ?
4178  ctx.clientid_ : ClientIdPtr());
4179  ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
4180 
4181  // Pass the lease to be updated
4182  ctx.callout_handle_->setArgument("lease4", lease);
4183 
4184  // Call all installed callouts
4185  HooksManager::callCallouts(Hooks.hook_index_lease4_renew_,
4186  *ctx.callout_handle_);
4187 
4188  // Callouts decided to skip the next processing step. The next
4189  // processing step would actually renew the lease, so skip at this
4190  // stage means "keep the old lease as it is".
4191  if (ctx.callout_handle_->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
4192  skip = true;
4195  }
4196 
4198  }
4199 
4200  if (!ctx.fake_allocation_ && !skip && (lease->reuseable_valid_lft_ == 0)) {
4201  // for REQUEST we do update the lease
4203 
4204  // We need to account for the re-assignment of The lease.
4205  if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
4206  StatsMgr::instance().addValue(
4207  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4208  "assigned-addresses"),
4209  static_cast<int64_t>(1));
4210  StatsMgr::instance().addValue(
4211  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4212  "cumulative-assigned-addresses"),
4213  static_cast<int64_t>(1));
4214  StatsMgr::instance().addValue("cumulative-assigned-addresses",
4215  static_cast<int64_t>(1));
4216  }
4217  }
4218  if (skip) {
4219  // Rollback changes (really useful only for memfile)
4221  *lease = *old_values;
4222  }
4223 
4224  return (lease);
4225 }
4226 
4227 Lease4Ptr
4228 AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
4230  CalloutHandle::CalloutNextStep& callout_status) {
4231  if (!expired) {
4232  isc_throw(BadValue, "null lease specified for reuseExpiredLease");
4233  }
4234 
4235  if (!ctx.subnet_) {
4236  isc_throw(BadValue, "null subnet specified for the reuseExpiredLease");
4237  }
4238 
4239  if (!ctx.fake_allocation_) {
4240  // The expired lease needs to be reclaimed before it can be reused.
4241  // This includes declined leases for which probation period has
4242  // elapsed.
4243  reclaimExpiredLease(expired, ctx.callout_handle_);
4244  expired->state_ = Lease::STATE_DEFAULT;
4245  }
4246 
4247  expired->reuseable_valid_lft_ = 0;
4248  static_cast<void>(updateLease4Information(expired, ctx));
4249 
4252  .arg(ctx.query_->getLabel())
4253  .arg(expired->toText());
4254 
4255  // Let's execute all callouts registered for lease4_select
4256  if (ctx.callout_handle_ &&
4257  HooksManager::calloutsPresent(hook_index_lease4_select_)) {
4258 
4259  // Enable copying options from the packet within hook library.
4260  ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
4261 
4262  // Use the RAII wrapper to make sure that the callout handle state is
4263  // reset when this object goes out of scope. All hook points must do
4264  // it to prevent possible circular dependency between the callout
4265  // handle and its arguments.
4266  ScopedCalloutHandleState callout_handle_state(ctx.callout_handle_);
4267 
4268  // Pass necessary arguments
4269  // Pass the original client query
4270  ctx.callout_handle_->setArgument("query4", ctx.query_);
4271 
4272  // Subnet from which we do the allocation. Convert the general subnet
4273  // pointer to a pointer to a Subnet4. Note that because we are using
4274  // boost smart pointers here, we need to do the cast using the boost
4275  // version of dynamic_pointer_cast.
4276  Subnet4Ptr subnet4 = boost::dynamic_pointer_cast<Subnet4>(ctx.subnet_);
4277  ctx.callout_handle_->setArgument("subnet4", subnet4);
4278 
4279  // Is this solicit (fake = true) or request (fake = false)
4280  ctx.callout_handle_->setArgument("fake_allocation", ctx.fake_allocation_);
4281 
4282  // The lease that will be assigned to a client
4283  ctx.callout_handle_->setArgument("lease4", expired);
4284 
4285  // Call the callouts
4286  HooksManager::callCallouts(hook_index_lease4_select_, *ctx.callout_handle_);
4287 
4288  callout_status = ctx.callout_handle_->getStatus();
4289 
4290  // Callouts decided to skip the action. This means that the lease is not
4291  // assigned, so the client will get NoAddrAvail as a result. The lease
4292  // won't be inserted into the database.
4293  if (callout_status == CalloutHandle::NEXT_STEP_SKIP) {
4296  return (Lease4Ptr());
4297  }
4298 
4300 
4301  // Let's use whatever callout returned. Hopefully it is the same lease
4302  // we handed to it.
4303  ctx.callout_handle_->getArgument("lease4", expired);
4304  }
4305 
4306  if (!ctx.fake_allocation_) {
4307  // for REQUEST we do update the lease
4309 
4310  // We need to account for the re-assignment of The lease.
4311  StatsMgr::instance().addValue(
4312  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4313  "assigned-addresses"),
4314  static_cast<int64_t>(1));
4315  StatsMgr::instance().addValue(
4316  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4317  "cumulative-assigned-addresses"),
4318  static_cast<int64_t>(1));
4319  StatsMgr::instance().addValue("cumulative-assigned-addresses",
4320  static_cast<int64_t>(1));
4321  }
4322 
4323  // We do nothing for SOLICIT. We'll just update database when
4324  // the client gets back to us with REQUEST message.
4325 
4326  // it's not really expired at this stage anymore - let's return it as
4327  // an updated lease
4328  return (expired);
4329 }
4330 
4331 Lease4Ptr
4332 AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx,
4333  CalloutHandle::CalloutNextStep& callout_status) {
4334  ctx.conflicting_lease_.reset();
4335 
4336  Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4337  if (exist_lease) {
4338  if (exist_lease->expired()) {
4339  ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4340  // reuseExpiredLease4() will reclaim the use which will
4341  // queue an NCR remove it needed. Clear the DNS fields in
4342  // the old lease to avoid a redundant remove in server logic.
4343  ctx.old_lease_->hostname_.clear();
4344  ctx.old_lease_->fqdn_fwd_ = false;
4345  ctx.old_lease_->fqdn_rev_ = false;
4346  return (reuseExpiredLease4(exist_lease, ctx, callout_status));
4347 
4348  } else {
4349  // If there is a lease and it is not expired, pass this lease back
4350  // to the caller in the context. The caller may need to know
4351  // which lease we're conflicting with.
4352  ctx.conflicting_lease_ = exist_lease;
4353  }
4354 
4355  } else {
4356  return (createLease4(ctx, candidate, callout_status));
4357  }
4358  return (Lease4Ptr());
4359 }
4360 
4361 Lease4Ptr
4362 AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
4363  Lease4Ptr new_lease;
4365  Subnet4Ptr subnet = ctx.subnet_;
4366 
4367  // Need to check if the subnet belongs to a shared network. If so,
4368  // we might be able to find a better subnet for lease allocation,
4369  // for which it is more likely that there are some leases available.
4370  // If we stick to the selected subnet, we may end up walking over
4371  // the entire subnet (or more subnets) to discover that the address
4372  // pools have been exhausted. Using a subnet from which an address
4373  // was assigned most recently is an optimization which increases
4374  // the likelihood of starting from the subnet which address pools
4375  // are not exhausted.
4376  SharedNetwork4Ptr network;
4377  ctx.subnet_->getSharedNetwork(network);
4378  if (network) {
4379  // This would try to find a subnet with the same set of classes
4380  // as the current subnet, but with the more recent "usage timestamp".
4381  // This timestamp is only updated for the allocations made with an
4382  // allocator (unreserved lease allocations), not the static
4383  // allocations or requested addresses.
4384  ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
4385  }
4386 
4387  // We have the choice in the order checking the lease and
4388  // the reservation. The default is to begin by the lease
4389  // if the multi-threading is disabled.
4390  bool check_reservation_first = MultiThreadingMgr::instance().getMode();
4391 
4392  Subnet4Ptr original_subnet = subnet;
4393 
4394  uint64_t total_attempts = 0;
4395 
4396  // The following counter tracks the number of subnets with matching client
4397  // classes from which the allocation engine attempted to assign leases.
4398  uint64_t subnets_with_unavail_leases = 0;
4399  // The following counter tracks the number of subnets in which there were
4400  // no matching pools for the client.
4401  uint64_t subnets_with_unavail_pools = 0;
4402 
4403  while (subnet) {
4404  ClientIdPtr client_id;
4405  if (subnet->getMatchClientId()) {
4406  client_id = ctx.clientid_;
4407  }
4408 
4409  uint64_t possible_attempts =
4410  subnet->getPoolCapacity(Lease::TYPE_V4,
4411  ctx.query_->getClasses());
4412 
4413  // If the number of tries specified in the allocation engine constructor
4414  // is set to 0 (unlimited) or the pools capacity is lower than that number,
4415  // let's use the pools capacity as the maximum number of tries. Trying
4416  // more than the actual pools capacity is a waste of time. If the specified
4417  // number of tries is lower than the pools capacity, use that number.
4418  uint64_t max_attempts = ((attempts_ == 0 || possible_attempts < attempts_) ? possible_attempts : attempts_);
4419 
4420  if (max_attempts > 0) {
4421  // If max_attempts is greater than 0, there are some pools in this subnet
4422  // from which we can potentially get a lease.
4423  ++subnets_with_unavail_leases;
4424  } else {
4425  // If max_attempts is 0, it is an indication that there are no pools
4426  // in the subnet from which we can get a lease.
4427  ++subnets_with_unavail_pools;
4428  }
4429 
4430  CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
4431 
4432  for (uint64_t i = 0; i < max_attempts; ++i) {
4433 
4434  ++total_attempts;
4435 
4436  IOAddress candidate = allocator->pickAddress(subnet,
4437  ctx.query_->getClasses(),
4438  client_id,
4439  ctx.requested_address_);
4440  // First check for reservation when it is the choice.
4441  if (check_reservation_first && addressReserved(candidate, ctx)) {
4442  // Don't allocate.
4443  continue;
4444  }
4445 
4446  // Check if the resource is busy i.e. can be being allocated
4447  // by another thread to another client.
4448  ResourceHandler4 resource_handler;
4449  if (MultiThreadingMgr::instance().getMode() &&
4450  !resource_handler.tryLock4(candidate)) {
4451  // Don't allocate.
4452  continue;
4453  }
4454 
4455  // Check for an existing lease for the candidate address.
4456  Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate);
4457  if (!exist_lease) {
4458  // No existing lease, is it reserved?
4459  if (check_reservation_first || !addressReserved(candidate, ctx)) {
4460  // Not reserved use it.
4461  new_lease = createLease4(ctx, candidate, callout_status);
4462  }
4463  } else {
4464  // An lease exists, is expired, and not reserved use it.
4465  if (exist_lease->expired() &&
4466  (check_reservation_first || !addressReserved(candidate, ctx))) {
4467  ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
4468  new_lease = reuseExpiredLease4(exist_lease, ctx, callout_status);
4469  }
4470  }
4471 
4472  // We found a lease we can use, return it.
4473  if (new_lease) {
4474  return (new_lease);
4475  }
4476 
4477  if (ctx.callout_handle_ && (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
4478  // Don't retry when the callout status is not continue.
4479  subnet.reset();
4480  break;
4481  }
4482  }
4483 
4484  // This pointer may be set to NULL if hooks set SKIP status.
4485  if (subnet) {
4486  subnet = subnet->getNextSubnet(original_subnet, ctx.query_->getClasses());
4487 
4488  if (subnet) {
4489  ctx.subnet_ = subnet;
4490  }
4491  }
4492  }
4493 
4494  if (network) {
4495  // The client is in the shared network. Let's log the high level message
4496  // indicating which shared network the client belongs to.
4498  .arg(ctx.query_->getLabel())
4499  .arg(network->getName())
4500  .arg(subnets_with_unavail_leases)
4501  .arg(subnets_with_unavail_pools);
4502  StatsMgr::instance().addValue("v4-allocation-fail-shared-network",
4503  static_cast<int64_t>(1));
4504  StatsMgr::instance().addValue(
4505  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4506  "v4-allocation-fail-shared-network"),
4507  static_cast<int64_t>(1));
4508  } else {
4509  // The client is not connected to a shared network. It is connected
4510  // to a subnet. Let's log some details about the subnet.
4512  .arg(ctx.query_->getLabel())
4513  .arg(ctx.subnet_->toText())
4514  .arg(ctx.subnet_->getID())
4515  .arg(ctx.subnet_->getSharedNetworkName());
4516  StatsMgr::instance().addValue("v4-allocation-fail-subnet",
4517  static_cast<int64_t>(1));
4518  StatsMgr::instance().addValue(
4519  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4520  "v4-allocation-fail-subnet"),
4521  static_cast<int64_t>(1));
4522  }
4523  if (total_attempts == 0) {
4524  // In this case, it seems that none of the pools in the subnets could
4525  // be used for that client, both in case the client is connected to
4526  // a shared network or to a single subnet. Apparently, the client was
4527  // rejected to use the pools because of the client classes' mismatch.
4529  .arg(ctx.query_->getLabel());
4530  StatsMgr::instance().addValue("v4-allocation-fail-no-pools",
4531  static_cast<int64_t>(1));
4532  StatsMgr::instance().addValue(
4533  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4534  "v4-allocation-fail-no-pools"),
4535  static_cast<int64_t>(1));
4536  } else {
4537  // This is an old log message which provides a number of attempts
4538  // made by the allocation engine to allocate a lease. The only case
4539  // when we don't want to log this message is when the number of
4540  // attempts is zero (condition above), because it would look silly.
4542  .arg(ctx.query_->getLabel())
4543  .arg(total_attempts);
4544  StatsMgr::instance().addValue("v4-allocation-fail",
4545  static_cast<int64_t>(1));
4546  StatsMgr::instance().addValue(
4547  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4548  "v4-allocation-fail"),
4549  static_cast<int64_t>(1));
4550  }
4551 
4552  const ClientClasses& classes = ctx.query_->getClasses();
4553  if (!classes.empty()) {
4555  .arg(ctx.query_->getLabel())
4556  .arg(classes.toText());
4557  StatsMgr::instance().addValue("v4-allocation-fail-classes",
4558  static_cast<int64_t>(1));
4559  StatsMgr::instance().addValue(
4560  StatsMgr::generateName("subnet", ctx.subnet_->getID(),
4561  "v4-allocation-fail-classes"),
4562  static_cast<int64_t>(1));
4563  }
4564 
4565  return (new_lease);
4566 }
4567 
4568 bool
4569 AllocEngine::updateLease4Information(const Lease4Ptr& lease,
4570  AllocEngine::ClientContext4& ctx) const {
4571  bool changed = false;
4572  if (lease->subnet_id_ != ctx.subnet_->getID()) {
4573  changed = true;
4574  lease->subnet_id_ = ctx.subnet_->getID();
4575  }
4576  if ((!ctx.hwaddr_ && lease->hwaddr_) ||
4577  (ctx.hwaddr_ &&
4578  (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) {
4579  changed = true;
4580  lease->hwaddr_ = ctx.hwaddr_;
4581  }
4582  if (ctx.subnet_->getMatchClientId() && ctx.clientid_) {
4583  if (!lease->client_id_ || (*ctx.clientid_ != *lease->client_id_)) {
4584  changed = true;
4585  lease->client_id_ = ctx.clientid_;
4586  }
4587  } else if (lease->client_id_) {
4588  changed = true;
4589  lease->client_id_ = ClientIdPtr();
4590  }
4591  lease->cltt_ = time(NULL);
4592 
4593  // Get the context appropriate valid lifetime.
4594  lease->valid_lft_ = getValidLft(ctx);
4595 
4596  // Reduced valid lifetime is a significant change.
4597  if (lease->valid_lft_ < lease->current_valid_lft_) {
4598  changed = true;
4599  }
4600 
4601  if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) ||
4602  (lease->fqdn_rev_ != ctx.rev_dns_update_) ||
4603  (lease->hostname_ != ctx.hostname_)) {
4604  changed = true;
4605  lease->fqdn_fwd_ = ctx.fwd_dns_update_;
4606  lease->fqdn_rev_ = ctx.rev_dns_update_;
4607  lease->hostname_ = ctx.hostname_;
4608  }
4609 
4610  // Add(update) the extended information on the lease.
4611  if (updateLease4ExtendedInfo(lease, ctx)) {
4612  changed = true;
4613  }
4614 
4615  return (changed);
4616 }
4617 
4618 bool
4620  const AllocEngine::ClientContext4& ctx) const {
4621  bool changed = false;
4622 
4623  // If storage is not enabled then punt.
4624  if (!ctx.subnet_->getStoreExtendedInfo()) {
4625  return (changed);
4626  }
4627 
4628  // Look for relay agent information option (option 82)
4629  OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS);
4630  if (!rai) {
4631  // Pkt4 doesn't have it, so nothing to store (or update).
4632  return (changed);
4633  }
4634 
4635  // Create a StringElement with the hex string for relay-agent-info.
4636  ElementPtr relay_agent(new StringElement(rai->toHexString()));
4637 
4638  // Now we wrap the agent info in a map. This allows for future expansion.
4639  ElementPtr extended_info = Element::createMap();
4640  extended_info->set("relay-agent-info", relay_agent);
4641 
4642  // Get a writable copy of the lease's current user context.
4643  ElementPtr user_context;
4644  if (lease->getContext()) {
4645  user_context = UserContext::toElement(lease->getContext());
4646  } else {
4647  user_context = Element::createMap();
4648  }
4649 
4650  // Add/replace the extended info entry.
4651  ConstElementPtr old_extended_info = user_context->get("ISC");
4652  if (!old_extended_info || (*old_extended_info != *extended_info)) {
4653  changed = true;
4654  user_context->set("ISC", extended_info);
4655  }
4656 
4657  // Update the lease's user_context.
4658  lease->setContext(user_context);
4659 
4660  return (changed);
4661 }
4662 
4663 bool
4665  const AllocEngine::ClientContext6& ctx) const {
4666  bool changed = false;
4667 
4668  // If storage is not enabled then punt.
4669  if (!ctx.subnet_->getStoreExtendedInfo()) {
4670  return (changed);
4671  }
4672 
4673  // If we do not have relay information, then punt.
4674  if (ctx.query_->relay_info_.empty()) {
4675  return (changed);
4676  }
4677 
4678  // We need to convert the vector of RelayInfo instances in
4679  // into an Element hierarchy like this:
4680  // "relay-info": [
4681  // {
4682  // "hop": 123,
4683  // "link": "2001:db8::1",
4684  // "peer": "2001:db8::2",
4685  // "options": "0x..."
4686  // },..]
4687  //
4688  ElementPtr relay_list = Element::createList();
4689  for (auto relay : ctx.query_->relay_info_) {
4690  ElementPtr relay_elem = Element::createMap();
4691  relay_elem->set("hop", ElementPtr(new IntElement(relay.hop_count_)));
4692  relay_elem->set("link", ElementPtr(new StringElement(relay.linkaddr_.toText())));
4693  relay_elem->set("peer", ElementPtr(new StringElement(relay.peeraddr_.toText())));
4694 
4695  // If there are relay options, we'll pack them into a buffer and then
4696  // convert that into a hex string. If there are no options, we omit
4697  // then entry.
4698  if (!relay.options_.empty()) {
4699  OutputBuffer buf(128);
4700  LibDHCP::packOptions6(buf, relay.options_);
4701 
4702  if (buf.getLength() > 0) {
4703  const uint8_t* cp = static_cast<const uint8_t*>(buf.getData());
4704  std::vector<uint8_t>bytes;
4705  std::stringstream ss;
4706 
4707  bytes.assign(cp, cp + buf.getLength());
4708  ss << "0x" << encode::encodeHex(bytes);
4709  relay_elem->set("options", ElementPtr(new StringElement(ss.str())));
4710  }
4711  }
4712 
4713  relay_list->add(relay_elem);
4714  }
4715 
4716  // Now we wrap the list of relays in a map. This allows for future expansion.
4717  ElementPtr extended_info = Element::createMap();
4718  extended_info->set("relays", relay_list);
4719 
4720  // Get a writable copy of the lease's current user context.
4721  ElementPtr user_context;
4722  if (lease->getContext()) {
4723  user_context = UserContext::toElement(lease->getContext());
4724  } else {
4725  user_context = Element::createMap();
4726  }
4727 
4728  // Add/replace the extended info entry.
4729  ConstElementPtr old_extended_info = user_context->get("ISC");
4730  if (!old_extended_info || (*old_extended_info != *extended_info)) {
4731  changed = true;
4732  user_context->set("ISC", extended_info);
4733  }
4734 
4735  // Update the lease's user_context.
4736  lease->setContext(user_context);
4737 
4738  return (changed);
4739 }
4740 
4741 void
4742 AllocEngine::setLeaseReusable(const Lease4Ptr& lease,
4743  const ClientContext4& ctx) const {
4744  // Sanity.
4745  lease->reuseable_valid_lft_ = 0;
4746  const Subnet4Ptr& subnet = ctx.subnet_;
4747  if (!subnet) {
4748  return;
4749  }
4750  if (lease->state_ != Lease::STATE_DEFAULT) {
4751  return;
4752  }
4753 
4754  // Always reuse infinite lifetime leases.
4755  if (lease->valid_lft_ == Lease::INFINITY_LFT) {
4756  lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
4757  return;
4758  }
4759 
4760  // Refuse time not going forward.
4761  if (lease->cltt_ < lease->current_cltt_) {
4762  return;
4763  }
4764 
4765  uint32_t age = lease->cltt_ - lease->current_cltt_;
4766  // Already expired.
4767  if (age >= lease->current_valid_lft_) {
4768  return;
4769  }
4770 
4771  // Try cache max age.
4772  uint32_t max_age = 0;
4773  if (!subnet->getCacheMaxAge().unspecified()) {
4774  max_age = subnet->getCacheMaxAge().get();
4775  if ((max_age == 0) || (age > max_age)) {
4776  return;
4777  }
4778  }
4779 
4780  // Try cache threshold.
4781  if (!subnet->getCacheThreshold().unspecified()) {
4782  double threshold = subnet->getCacheThreshold().get();
4783  if ((threshold <= 0.) || (threshold > 1.)) {
4784  return;
4785  }
4786  max_age = lease->valid_lft_ * threshold;
4787  if (age > max_age) {
4788  return;
4789  }
4790  }
4791 
4792  // No cache.
4793  if (max_age == 0) {
4794  return;
4795  }
4796 
4797  // Seems to be reusable.
4798  lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
4799 }
4800 
4801 void
4802 AllocEngine::setLeaseReusable(const Lease6Ptr& lease,
4803  uint32_t current_preferred_lft,
4804  const ClientContext6& ctx) const {
4805  // Sanity.
4806  lease->reuseable_valid_lft_ = 0;
4807  lease->reuseable_preferred_lft_ = 0;
4808  const Subnet6Ptr& subnet = ctx.subnet_;
4809  if (!subnet) {
4810  return;
4811  }
4812  if (lease->state_ != Lease::STATE_DEFAULT) {
4813  return;
4814  }
4815 
4816  // Refuse time not going forward.
4817  if (lease->cltt_ < lease->current_cltt_) {
4818  return;
4819  }
4820 
4821  uint32_t age = lease->cltt_ - lease->current_cltt_;
4822  // Already expired.
4823  if (age >= lease->current_valid_lft_) {
4824  return;
4825  }
4826 
4827  // Try cache max age.
4828  uint32_t max_age = 0;
4829  if (!subnet->getCacheMaxAge().unspecified()) {
4830  max_age = subnet->getCacheMaxAge().get();
4831  if ((max_age == 0) || (age > max_age)) {
4832  return;
4833  }
4834  }
4835 
4836  // Try cache threshold.
4837  if (!subnet->getCacheThreshold().unspecified()) {
4838  double threshold = subnet->getCacheThreshold().get();
4839  if ((threshold <= 0.) || (threshold > 1.)) {
4840  return;
4841  }
4842  max_age = lease->valid_lft_ * threshold;
4843  if (age > max_age) {
4844  return;
4845  }
4846  }
4847 
4848  // No cache.
4849  if (max_age == 0) {
4850  return;
4851  }
4852 
4853  // Seems to be reusable.
4854  if ((current_preferred_lft == Lease::INFINITY_LFT) ||
4855  (current_preferred_lft == 0)) {
4856  // Keep these values.
4857  lease->reuseable_preferred_lft_ = current_preferred_lft;
4858  } else if (current_preferred_lft > age) {
4859  lease->reuseable_preferred_lft_ = current_preferred_lft - age;
4860  } else {
4861  // Can be a misconfiguration so stay safe...
4862  return;
4863  }
4864  if (lease->current_valid_lft_ == Lease::INFINITY_LFT) {
4865  lease->reuseable_valid_lft_ = Lease::INFINITY_LFT;
4866  } else {
4867  lease->reuseable_valid_lft_ = lease->current_valid_lft_ - age;
4868  }
4869 }
4870 
4871 } // namespace dhcp
4872 } // namespace isc
#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
std::string toText(const std::string &separator=", ") const
Returns all class names as text.
Definition: classify.cc:49
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:136
virtual void updateLease4(const Lease4Ptr &lease4)=0
Updates IPv4 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:376
HWAddrPtr hwaddr_
Hardware/MAC address (if available, may be NULL)
Definition: alloc_engine.h:493
Defines a single hint.
Definition: alloc_engine.h:324
A generic exception that is thrown when a function is not implemented.
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
Lease6Collection changed_leases_
A pointer to any leases that have changed FQDN information.
Definition: alloc_engine.h:564
const isc::log::MessageID ALLOC_ENGINE_V4_REQUEST_IN_USE
Abstract Lease Manager.
Definition: lease_mgr.h:222
HintContainer hints_
Client&#39;s hints.
Definition: alloc_engine.h:547
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
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
virtual ConstHostCollection getAll6(const SubnetID &subnet_id) const
Return all hosts in a DHCPv6 subnet.
Definition: host_mgr.cc:140
bool getDisableSingleQuery() const
Returns the disable single query flag.
Definition: host_mgr.h:596
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:995
Lease6Collection old_leases_
A pointer to any old leases that the client had before update but are no longer valid after the updat...
Definition: alloc_engine.h:556
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
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
bool updateLease6ExtendedInfo(const Lease6Ptr &lease, const ClientContext6 &ctx) const
Stores additional client query parameters on a V6 lease.
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition: lease.h:487
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
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:521
static void getLifetimes6(ClientContext6 &ctx, uint32_t &preferred, uint32_t &valid)
Determines the preferred and valid v6 lease lifetimes.
const isc::log::MessageID ALLOC_ENGINE_V4_DISCOVER_HR
bool early_global_reservations_lookup_
Indicates if early global reservation is enabled.
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:24
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.
bool fwd_dns_update_
A boolean value which indicates that server takes responsibility for the forward DNS Update for this ...
Definition: alloc_engine.h:509
DuidPtr duid_
Client identifier.
Definition: alloc_engine.h:490
std::vector< PoolPtr > PoolCollection
a container for either IPv4 or IPv6 Pools
Definition: pool.h:508
long getTotalMilliseconds() const
Retrieves the total measured duration in milliseconds.
Definition: stopwatch.cc:60
Lease4Ptr allocateLease4(ClientContext4 &ctx)
Returns IPv4 lease.
bool isAllocated(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128) const
Checks if specified address or prefix was allocated.
A configuration holder for IPv4 subnet.
Definition: subnet.h:528
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.
const_iterator cbegin() const
Iterator to the first element.
Definition: classify.h:108
boost::shared_ptr< OptionUint32 > OptionUint32Ptr
Definition: option_int.h:35
DdnsParamsPtr getDdnsParams()
Returns the set of DDNS behavioral parameters based on the selected subnet.
hooks::CalloutHandlePtr callout_handle_
Callout handle associated with the client&#39;s message.
RAII object enabling copying options retrieved from the packet.
Definition: pkt.h:40
const isc::log::MessageID ALLOC_ENGINE_V4_ALLOC_FAIL_SHARED_NETWORK
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_LEASES_NO_HR
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:172
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 early_global_reservations_lookup_
Indicates if early global reservation is enabled.
Definition: alloc_engine.h:479
bool tryLock(Lease::Type type, const asiolink::IOAddress &addr)
Tries to acquires a resource.
ResourceContainer new_resources_
Holds addresses and prefixes allocated for this IA.
Definition: alloc_engine.h:569
#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.
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
ClientClassContainer::const_iterator const_iterator
Type of iterators.
Definition: classify.h:70
Subnet6Ptr subnet_
Subnet selected for the client by the server.
Definition: alloc_engine.h:482
ResourceContainer allocated_resources_
Holds addresses and prefixes allocated for all IAs.
Definition: alloc_engine.h:526
Definition: edns.h:19
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:243
Pool information for IPv6 addresses and prefixes.
Definition: pool.h:321
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:1152
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
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.
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:504
const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_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_V4_REQUEST_OUT_OF_POOL
Random allocator that picks address randomly.
Definition: alloc_engine.h:238
std::vector< IAContext > ias_
Container holding IA specific contexts.
Definition: alloc_engine.h:621
Context information for the DHCPv6 leases allocation.
Definition: alloc_engine.h:459
const isc::log::MessageID ALLOC_ENGINE_V4_REUSE_EXPIRED_LEASE_DATA
ConstHostPtr globalHost() const
Returns global host reservation if there is one.
Subnet6Ptr host_subnet_
Subnet from which host reservations should be retrieved.
Definition: alloc_engine.h:487
A generic exception that is thrown when an unexpected error condition occurs.
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
Notes: IntElement type is changed to int64_t.
Definition: data.h:588
Lease6Collection renewLeases6(ClientContext6 &ctx)
Renews existing DHCPv6 leases for a given IA.
Wrapper class around callout handle which automatically resets handle&#39;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
T get(T hint) const
Returns value with a hint.
Definition: triplet.h:99
bool unknown_requested_addr_
True when the address DHCPREQUEST&#39;ed by client is not within a dynamic pool the server knows about...
Pkt4Ptr query_
A pointer to the client&#39;s message.
const isc::log::MessageID ALLOC_ENGINE_V4_LEASE_RECLAIM
const isc::log::MessageID ALLOC_ENGINE_V6_LEASES_RECLAMATION_START
Option6IAPtr ia_rsp_
A pointer to the IA_NA/IA_PD option to be sent in response.
Definition: alloc_engine.h:573
CalloutNextStep
Specifies allowed next steps.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
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:651
Structure that holds a lease for IPv6 address and/or prefix.
Definition: lease.h:503
Address/prefix allocator that iterates over all addresses.
Definition: alloc_engine.h:148
const isc::log::MessageID ALLOC_ENGINE_V6_ALLOC_FAIL_CLASSES
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:47
bool empty() const
Check if classes is empty.
Definition: classify.h:95
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
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
const isc::log::MessageID ALLOC_ENGINE_V4_OFFER_EXISTING_LEASE
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
ConstHostPtr currentHost() const
Returns host for currently selected subnet.
const isc::log::MessageID ALLOC_ENGINE_V6_RENEW_HR
bool fake_allocation_
Indicates if this is a real or fake allocation.
Definition: alloc_engine.h:474
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-lfc.
const isc::log::MessageID ALLOC_ENGINE_V6_EXPIRED_HINT_RESERVED
bool isNewResource(const asiolink::IOAddress &prefix, const uint8_t prefix_len=128) const
Checks if specified address or prefix was new.
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:580
Lease6Collection new_leases_
A collection of newly allocated leases.
Definition: alloc_engine.h:529
string encodeHex(const vector< uint8_t > &binary)
Encode binary data in the base16 (&#39;hex&#39;) 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:538
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:541
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&#39;s message.
Definition: alloc_engine.h:467
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
Lease6Collection allocateLeases6(ClientContext6 &ctx)
Allocates IPv6 leases for a given IA container.
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
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
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 rev_dns_update_
A boolean value which indicates that server takes responsibility for the reverse DNS Update for this ...
Definition: alloc_engine.h:514
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
virtual ConstHostCollection getAll4(const SubnetID &subnet_id) const
Return all hosts in a DHCPv4 subnet.
Definition: host_mgr.cc:129
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_iterator cend() const
Iterator to the past the end element.
Definition: classify.h:113
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:661
static const uint32_t STATE_DEFAULT
A lease in the default state.
Definition: lease.h:73
Lease::Type pool_type_
Defines pool type allocation.
Definition: alloc_engine.h:131
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.
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:669
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
T getValue() const
Return option value.
Definition: option_int.h:190
bool hasGlobalReservation(const IPv6Resrv &resv) const
Determines if a global reservation exists.
hooks::CalloutHandlePtr callout_handle_
Callout handle associated with the client&#39;s message.
Definition: alloc_engine.h:523
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.
static LeaseMgr & instance()
Return current lease manager.
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
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.
std::string logFormatTotalDuration() const
Returns the total measured duration in the format directly usable in the log messages.
Definition: stopwatch.cc:80
This file provides the classes needed to embody, compose, and decompose DNS update requests that are ...
ConstHostPtr currentHost() const
Returns host from the most preferred subnet.
std::pair< Host::IdentifierType, std::vector< uint8_t > > IdentifierPair
A tuple holding host identifier type and value.
Definition: alloc_engine.h:432