Coverage Report

Created: 2026-06-30 07:33

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