memfile_lease_mgr.cc 17.4 KB
Newer Older
1
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
#include <dhcpsrv/cfgmgr.h>
16
#include <dhcpsrv/dhcpsrv_log.h>
17
#include <dhcpsrv/lease_file_loader.h>
18
#include <dhcpsrv/memfile_lease_mgr.h>
19
#include <exceptions/exceptions.h>
20
#include <iostream>
21
#include <sstream>
22

23
24
25
26
27
28
29
namespace {

/// @brief Maximum number of errors to read the leases from the lease file.
const uint32_t MAX_LEASE_ERRORS = 100;

} // end of anonymous namespace

30
31
using namespace isc::dhcp;

32
33
Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
    : LeaseMgr(parameters) {
34
35
36
37
38
    // Check the universe and use v4 file or v6 file.
    std::string universe = getParameter("universe");
    if (universe == "4") {
        std::string file4 = initLeaseFilePath(V4);
        if (!file4.empty()) {
39
40
            loadLeasesFromFiles<Lease4, CSVLeaseFile4>(file4, lease_file4_,
                                                       storage4_);
41
42
43
44
        }
    } else {
        std::string file6 = initLeaseFilePath(V6);
        if (!file6.empty()) {
45
46
            loadLeasesFromFiles<Lease6, CSVLeaseFile6>(file6, lease_file6_,
                                                       storage6_);
47
        }
48
    }
49
50
51
52
53
54
55
56

    // If lease persistence have been disabled for both v4 and v6,
    // issue a warning. It is ok not to write leases to disk when
    // doing testing, but it should not be done in normal server
    // operation.
    if (!persistLeases(V4) && !persistLeases(V6)) {
        LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_NO_STORAGE);
    }
57
58
59
}

Memfile_LeaseMgr::~Memfile_LeaseMgr() {
60
61
62
63
64
65
66
67
    if (lease_file4_) {
        lease_file4_->close();
        lease_file4_.reset();
    }
    if (lease_file6_) {
        lease_file6_->close();
        lease_file6_.reset();
    }
68
69
}

70
71
bool
Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
72
73
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ADD_ADDR4).arg(lease->addr_.toText());
74

75
76
77
78
    if (getLease4(lease->addr_)) {
        // there is a lease with specified address already
        return (false);
    }
79
80
81
82

    // Try to write a lease to disk first. If this fails, the lease will
    // not be inserted to the memory and the disk and in-memory data will
    // remain consistent.
83
    if (persistLeases(V4)) {
84
85
86
        lease_file4_->append(*lease);
    }

87
88
    storage4_.insert(lease);
    return (true);
89
90
}

91
92
bool
Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
93
94
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ADD_ADDR6).arg(lease->addr_.toText());
95

96
    if (getLease6(lease->type_, lease->addr_)) {
97
98
99
        // there is a lease with specified address already
        return (false);
    }
100
101
102
103

    // Try to write a lease to disk first. If this fails, the lease will
    // not be inserted to the memory and the disk and in-memory data will
    // remain consistent.
104
    if (persistLeases(V6)) {
105
106
107
        lease_file6_->append(*lease);
    }

108
109
110
111
    storage6_.insert(lease);
    return (true);
}

112
113
Lease4Ptr
Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
114
115
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
116

117
118
119
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
    const SearchIndex& idx = storage4_.get<0>();
    Lease4Storage::iterator l = idx.find(addr);
120
121
122
    if (l == storage4_.end()) {
        return (Lease4Ptr());
    } else {
123
        return (Lease4Ptr(new Lease4(**l)));
124
    }
125
126
}

127
128
Lease4Collection
Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
129
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
130
              DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
131
132
133
134
135
136
137
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
    Lease4Collection collection;
    const SearchIndex& idx = storage4_.get<0>();
    for(SearchIndex::const_iterator lease = idx.begin();
        lease != idx.end(); ++lease) {

        // Every Lease4 has a hardware address, so we can compare it
138
        if ( (*(*lease)->hwaddr_) == hwaddr) {
139
            collection.push_back((*lease));
140
141
        }
    }
142

143
    return (collection);
144
145
}

146
147
Lease4Ptr
Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
148
149
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
150
151
        .arg(hwaddr.toText());

152
153
154
    // We are going to use index #1 of the multi index container.
    // We define SearchIndex locally in this function because
    // currently only this function uses this index.
155
    typedef Lease4Storage::nth_index<1>::type SearchIndex;
156
    // Get the index.
157
    const SearchIndex& idx = storage4_.get<1>();
158
159
160
161
    // Try to find the lease using HWAddr and subnet id.
    SearchIndex::const_iterator lease =
        idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id));
    // Lease was not found. Return empty pointer to the caller.
162
    if (lease == idx.end()) {
163
        return (Lease4Ptr());
164
165
    }

166
    // Lease was found. Return it to the caller.
167
    return (Lease4Ptr(new Lease4(**lease)));
168
169
}

170
Lease4Collection
171
Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
172
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
173
              DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
174
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
175
176
177
178
179
180
181
    Lease4Collection collection;
    const SearchIndex& idx = storage4_.get<0>();
    for(SearchIndex::const_iterator lease = idx.begin();
        lease != idx.end(); ++ lease) {

        // client-id is not mandatory in DHCPv4. There can be a lease that does
        // not have a client-id. Dereferencing null pointer would be a bad thing
182
183
        if((*lease)->client_id_ && *(*lease)->client_id_ == client_id) {
            collection.push_back((*lease));
184
185
186
187
        }
    }

    return (collection);
188
}
189

190
191
Lease4Ptr
Memfile_LeaseMgr::getLease4(const ClientId& client_id,
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
                            const HWAddr& hwaddr,
                            SubnetID subnet_id) const {
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_CLIENTID_HWADDR_SUBID).arg(client_id.toText())
                                                        .arg(hwaddr.toText())
                                                        .arg(subnet_id);

    // We are going to use index #3 of the multi index container.
    // We define SearchIndex locally in this function because
    // currently only this function uses this index.
    typedef Lease4Storage::nth_index<3>::type SearchIndex;
    // Get the index.
    const SearchIndex& idx = storage4_.get<3>();
    // Try to get the lease using client id, hardware address and subnet id.
    SearchIndex::const_iterator lease =
        idx.find(boost::make_tuple(client_id.getClientId(), hwaddr.hwaddr_,
                                   subnet_id));

    if (lease == idx.end()) {
        // Lease was not found. Return empty pointer to the caller.
        return (Lease4Ptr());
    }

    // Lease was found. Return it to the caller.
    return (*lease);
}

Lease4Ptr
Memfile_LeaseMgr::getLease4(const ClientId& client_id,
                            SubnetID subnet_id) const {
222
223
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
224
              .arg(client_id.toText());
225

226
227
228
    // We are going to use index #2 of the multi index container.
    // We define SearchIndex locally in this function because
    // currently only this function uses this index.
229
    typedef Lease4Storage::nth_index<2>::type SearchIndex;
230
    // Get the index.
231
    const SearchIndex& idx = storage4_.get<2>();
232
233
234
235
    // Try to get the lease using client id and subnet id.
    SearchIndex::const_iterator lease =
        idx.find(boost::make_tuple(client_id.getClientId(), subnet_id));
    // Lease was not found. Return empty pointer to the caller.
236
    if (lease == idx.end()) {
237
        return (Lease4Ptr());
238
    }
239
    // Lease was found. Return it to the caller.
240
    return (Lease4Ptr(new Lease4(**lease)));
241
242
}

243
Lease6Ptr
244
Memfile_LeaseMgr::getLease6(Lease::Type type,
245
                            const isc::asiolink::IOAddress& addr) const {
246
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
247
248
249
              DHCPSRV_MEMFILE_GET_ADDR6)
        .arg(addr.toText())
        .arg(Lease::typeToText(type));
250
    Lease6Storage::iterator l = storage6_.find(addr);
251
    if (l == storage6_.end() || !(*l) || ((*l)->type_ != type)) {
252
253
        return (Lease6Ptr());
    } else {
254
        return (Lease6Ptr(new Lease6(**l)));
255
256
257
    }
}

258
Lease6Collection
259
Memfile_LeaseMgr::getLeases6(Lease::Type type,
260
                            const DUID& duid, uint32_t iaid) const {
261
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
262
263
264
265
              DHCPSRV_MEMFILE_GET_IAID_DUID)
        .arg(iaid)
        .arg(duid.toText())
        .arg(Lease::typeToText(type));
266

267
268
269
270
271
272
273
274
275
276
277
    // We are going to use index #1 of the multi index container.
    typedef Lease6Storage::nth_index<1>::type SearchIndex;
    // Get the index.
    const SearchIndex& idx = storage6_.get<1>();
    // Try to get the lease using the DUID, IAID and lease type.
    std::pair<SearchIndex::iterator, SearchIndex::iterator> l =
        idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
    Lease6Collection collection;
    for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) {
        collection.push_back(Lease6Ptr(new Lease6(**lease)));
    }
278

279
    return (collection);
280
281
}

282
Lease6Collection
283
Memfile_LeaseMgr::getLeases6(Lease::Type type,
284
285
                             const DUID& duid, uint32_t iaid,
                             SubnetID subnet_id) const {
286
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
287
              DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID)
288
289
290
291
        .arg(iaid)
        .arg(subnet_id)
        .arg(duid.toText())
        .arg(Lease::typeToText(type));
292

293
294
295
296
    // We are going to use index #1 of the multi index container.
    typedef Lease6Storage::nth_index<1>::type SearchIndex;
    // Get the index.
    const SearchIndex& idx = storage6_.get<1>();
297
298
299
300
301
302
303
304
305
    // Try to get the lease using the DUID, IAID and lease type.
    std::pair<SearchIndex::iterator, SearchIndex::iterator> l =
        idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
    Lease6Collection collection;
    for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) {
        // Filter out the leases which subnet id doesn't match.
        if((*lease)->subnet_id_ == subnet_id) {
            collection.push_back(Lease6Ptr(new Lease6(**lease)));
        }
306
    }
307
308

    return (collection);
309
310
}

311
312
void
Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
313
314
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
315

316
317
318
    Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
    if (lease_it == storage4_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
319
                  << lease->addr_ << " - no such lease");
320
    }
321
322
323
324

    // Try to write a lease to disk first. If this fails, the lease will
    // not be inserted to the memory and the disk and in-memory data will
    // remain consistent.
325
    if (persistLeases(V4)) {
326
327
328
        lease_file4_->append(*lease);
    }

329
    **lease_it = *lease;
330
331
}

332
333
void
Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
334
335
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
336

337
338
339
    Lease6Storage::iterator lease_it = storage6_.find(lease->addr_);
    if (lease_it == storage6_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
340
                  << lease->addr_ << " - no such lease");
341
    }
342
343
344
345

    // Try to write a lease to disk first. If this fails, the lease will
    // not be inserted to the memory and the disk and in-memory data will
    // remain consistent.
346
    if (persistLeases(V6)) {
347
348
349
        lease_file6_->append(*lease);
    }

350
    **lease_it = *lease;
351
352
}

353
354
bool
Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
355
356
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_DELETE_ADDR).arg(addr.toText());
357
    if (addr.isV4()) {
358
359
360
361
362
363
        // v4 lease
        Lease4Storage::iterator l = storage4_.find(addr);
        if (l == storage4_.end()) {
            // No such lease
            return (false);
        } else {
364
            if (persistLeases(V4)) {
365
366
367
368
369
370
371
372
                // Copy the lease. The valid lifetime needs to be modified and
                // we don't modify the original lease.
                Lease4 lease_copy = **l;
                // Setting valid lifetime to 0 means that lease is being
                // removed.
                lease_copy.valid_lft_ = 0;
                lease_file4_->append(lease_copy);
            }
373
374
375
            storage4_.erase(l);
            return (true);
        }
376

377
    } else {
378
        // v6 lease
379
380
381
382
383
        Lease6Storage::iterator l = storage6_.find(addr);
        if (l == storage6_.end()) {
            // No such lease
            return (false);
        } else {
384
            if (persistLeases(V6)) {
385
386
387
388
389
390
391
392
393
                // Copy the lease. The lifetimes need to be modified and we
                // don't modify the original lease.
                Lease6 lease_copy = **l;
                // Setting lifetimes to 0 means that lease is being removed.
                lease_copy.valid_lft_ = 0;
                lease_copy.preferred_lft_ = 0;
                lease_file6_->append(lease_copy);
            }

394
395
396
            storage6_.erase(l);
            return (true);
        }
397
398
399
    }
}

400
401
std::string
Memfile_LeaseMgr::getDescription() const {
402
403
404
405
    return (std::string("This is a dummy memfile backend implementation.\n"
                        "It does not offer any useful lease management and its only\n"
                        "purpose is to test abstract lease manager API."));
}
406
407
408

void
Memfile_LeaseMgr::commit() {
409
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
410
411
412
413
}

void
Memfile_LeaseMgr::rollback() {
414
415
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ROLLBACK);
416
}
417
418
419
420
421
422
423
424
425
426

std::string
Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u) const {
    std::ostringstream s;
    s << CfgMgr::instance().getDataDir() << "/kea-leases";
    s << (u == V4 ? "4" : "6");
    s << ".csv";
    return (s.str());
}

427
428
429
430
431
432
433
434
435
std::string
Memfile_LeaseMgr::getLeaseFilePath(Universe u) const {
    if (u == V4) {
        return (lease_file4_ ? lease_file4_->getFilename() : "");
    }

    return (lease_file6_ ? lease_file6_->getFilename() : "");
}

436
437
bool
Memfile_LeaseMgr::persistLeases(Universe u) const {
438
439
440
441
442
443
444
445
    // Currently, if the lease file IO is not created, it means that writes to
    // disk have been explicitly disabled by the administrator. At some point,
    // there may be a dedicated ON/OFF flag implemented to control this.
    if (u == V4 && lease_file4_) {
        return (true);
    }

    return (u == V6 && lease_file6_);
446
447
448
449
}

std::string
Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
450
    std::string persist_val;
451
    try {
452
        persist_val = getParameter("persist");
453
    } catch (const Exception& ex) {
454
455
        // If parameter persist hasn't been specified, we use a default value
        // 'yes'.
456
        persist_val = "true";
457
    }
458
    // If persist_val is 'false' we will not store leases to disk, so let's
459
    // return empty file name.
460
    if (persist_val == "false") {
461
        return ("");
462

463
464
465
    } else if (persist_val != "true") {
        isc_throw(isc::BadValue, "invalid value 'persist="
                  << persist_val << "'");
466
467
    }

468
469
    std::string lease_file;
    try {
470
        lease_file = getParameter("name");
471
472
473
474
475
    } catch (const Exception& ex) {
        lease_file = getDefaultLeaseFilePath(u);
    }
    return (lease_file);
}
476

477
478
479
480
481
482
template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
void Memfile_LeaseMgr::
loadLeasesFromFiles(const std::string& filename,
                    boost::shared_ptr<LeaseFileType>& lease_file,
                    StorageType& storage) {
    storage.clear();
483
484
485
486
487
488
489
490
491
492
493

    for (int i = 2; i >= 0; --i) {
        // Initialize the name of the lease file to parse. For the
        // first two loops we're going to append .2 and .1 to the
        // lease file name.
        std::ostringstream s;
        s << filename;
        if (i > 0) {
            s << "." << i;
        }
        lease_file.reset(new LeaseFileType(s.str()));
494
495
496
497
498
499
500
501
502
503
504
        // Don't open the file if it doesn't exist and it is not the
        // primary lease file - not ending with .1 or .2. Those files
        // are optional and we don't want to create them if they don't
        // exist.
        if (i == 0 || lease_file->exists()) {
            // If the file doesn't exist it will be created as an empty
            // file (with no leases).
            lease_file->open();
            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
                                                   MAX_LEASE_ERRORS);
        }
505
    }
506
}