memfile_lease_mgr.cc 20.1 KB
Newer Older
1
// Copyright (C) 2012-2014 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/memfile_lease_mgr.h>
18
#include <exceptions/exceptions.h>
19

20
#include <iostream>
21
22
23

using namespace isc::dhcp;

24
25
Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
    : LeaseMgr(parameters) {
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    // 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()) {
            lease_file4_.reset(new CSVLeaseFile4(file4));
            lease_file4_->open();
            load4();
        }
    } else {
        std::string file6 = initLeaseFilePath(V6);
        if (!file6.empty()) {
            lease_file6_.reset(new CSVLeaseFile6(file6));
            lease_file6_->open();
            load6();
        }
42
    }
43
44
45
46
47
48
49
50

    // 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);
    }
51
52
53
}

Memfile_LeaseMgr::~Memfile_LeaseMgr() {
54
55
56
57
58
59
60
61
    if (lease_file4_) {
        lease_file4_->close();
        lease_file4_.reset();
    }
    if (lease_file6_) {
        lease_file6_->close();
        lease_file6_.reset();
    }
62
63
}

64
65
bool
Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
66
67
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ADD_ADDR4).arg(lease->addr_.toText());
68

69
70
71
72
    if (getLease4(lease->addr_)) {
        // there is a lease with specified address already
        return (false);
    }
73
74
75
76

    // 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.
77
    if (persistLeases(V4)) {
78
79
80
        lease_file4_->append(*lease);
    }

81
82
    storage4_.insert(lease);
    return (true);
83
84
}

85
86
bool
Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
87
88
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ADD_ADDR6).arg(lease->addr_.toText());
89

90
    if (getLease6(lease->type_, lease->addr_)) {
91
92
93
        // there is a lease with specified address already
        return (false);
    }
94
95
96
97

    // 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.
98
    if (persistLeases(V6)) {
99
100
101
        lease_file6_->append(*lease);
    }

102
103
104
105
    storage6_.insert(lease);
    return (true);
}

106
107
Lease4Ptr
Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
108
109
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
110

111
112
113
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
    const SearchIndex& idx = storage4_.get<0>();
    Lease4Storage::iterator l = idx.find(addr);
114
115
116
    if (l == storage4_.end()) {
        return (Lease4Ptr());
    } else {
117
        return (Lease4Ptr(new Lease4(**l)));
118
    }
119
120
}

121
122
Lease4Collection
Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
123
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
124
              DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
125
126
127
128
129
130
131
    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
132
        if ( (*(*lease)->hwaddr_) == hwaddr) {
133
            collection.push_back((*lease));
134
135
        }
    }
136

137
    return (collection);
138
139
}

140
141
Lease4Ptr
Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
142
143
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
144
145
        .arg(hwaddr.toText());

146
147
148
    // 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.
149
    typedef Lease4Storage::nth_index<1>::type SearchIndex;
150
    // Get the index.
151
    const SearchIndex& idx = storage4_.get<1>();
152
153
154
155
    // 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.
156
    if (lease == idx.end()) {
157
        return (Lease4Ptr());
158
159
    }

160
    // Lease was found. Return it to the caller.
161
    return (Lease4Ptr(new Lease4(**lease)));
162
163
}

164
Lease4Collection
165
Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
166
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
167
              DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
168
169
170
171
172
173
174
175
    typedef Memfile_LeaseMgr::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) {

        // 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
176
177
        if((*lease)->client_id_ && *(*lease)->client_id_ == client_id) {
            collection.push_back((*lease));
178
179
180
181
        }
    }

    return (collection);
182
}
183

184
185
Lease4Ptr
Memfile_LeaseMgr::getLease4(const ClientId& client_id,
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
                            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 {
216
217
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
218
              .arg(client_id.toText());
219

220
221
222
    // 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.
223
    typedef Lease4Storage::nth_index<2>::type SearchIndex;
224
    // Get the index.
225
    const SearchIndex& idx = storage4_.get<2>();
226
227
228
229
    // 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.
230
    if (lease == idx.end()) {
231
        return (Lease4Ptr());
232
    }
233
    // Lease was found. Return it to the caller.
234
    return (Lease4Ptr(new Lease4(**lease)));
235
236
}

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

252
Lease6Collection
253
Memfile_LeaseMgr::getLeases6(Lease::Type type,
254
                            const DUID& duid, uint32_t iaid) const {
255
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
256
257
258
259
              DHCPSRV_MEMFILE_GET_IAID_DUID)
        .arg(iaid)
        .arg(duid.toText())
        .arg(Lease::typeToText(type));
260

261
262
263
264
265
266
267
268
269
270
271
    // 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)));
    }
272

273
    return (collection);
274
275
}

276
Lease6Collection
277
Memfile_LeaseMgr::getLeases6(Lease::Type type,
278
279
                             const DUID& duid, uint32_t iaid,
                             SubnetID subnet_id) const {
280
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
281
              DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID)
282
283
284
285
        .arg(iaid)
        .arg(subnet_id)
        .arg(duid.toText())
        .arg(Lease::typeToText(type));
286

287
288
289
290
    // 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>();
291
292
293
294
295
296
297
298
299
    // 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)));
        }
300
    }
301
302

    return (collection);
303
304
}

305
306
void
Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
307
308
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
309

310
311
312
    Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
    if (lease_it == storage4_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
313
                  << lease->addr_ << " - no such lease");
314
    }
315
316
317
318

    // 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.
319
    if (persistLeases(V4)) {
320
321
322
        lease_file4_->append(*lease);
    }

323
    **lease_it = *lease;
324
325
}

326
327
void
Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
328
329
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
330

331
332
333
    Lease6Storage::iterator lease_it = storage6_.find(lease->addr_);
    if (lease_it == storage6_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
334
                  << lease->addr_ << " - no such lease");
335
    }
336
337
338
339

    // 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.
340
    if (persistLeases(V6)) {
341
342
343
        lease_file6_->append(*lease);
    }

344
    **lease_it = *lease;
345
346
}

347
348
bool
Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
349
350
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_DELETE_ADDR).arg(addr.toText());
351
    if (addr.isV4()) {
352
353
354
355
356
357
        // v4 lease
        Lease4Storage::iterator l = storage4_.find(addr);
        if (l == storage4_.end()) {
            // No such lease
            return (false);
        } else {
358
            if (persistLeases(V4)) {
359
360
361
362
363
364
365
366
                // 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);
            }
367
368
369
            storage4_.erase(l);
            return (true);
        }
370

371
    } else {
372
        // v6 lease
373
374
375
376
377
        Lease6Storage::iterator l = storage6_.find(addr);
        if (l == storage6_.end()) {
            // No such lease
            return (false);
        } else {
378
            if (persistLeases(V6)) {
379
380
381
382
383
384
385
386
387
                // 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);
            }

388
389
390
            storage6_.erase(l);
            return (true);
        }
391
392
393
    }
}

394
395
std::string
Memfile_LeaseMgr::getDescription() const {
396
397
398
399
    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."));
}
400
401
402

void
Memfile_LeaseMgr::commit() {
403
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
404
405
406
407
}

void
Memfile_LeaseMgr::rollback() {
408
409
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ROLLBACK);
410
}
411
412
413
414
415
416
417
418
419
420

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());
}

421
422
423
424
425
426
427
428
429
std::string
Memfile_LeaseMgr::getLeaseFilePath(Universe u) const {
    if (u == V4) {
        return (lease_file4_ ? lease_file4_->getFilename() : "");
    }

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

430
431
bool
Memfile_LeaseMgr::persistLeases(Universe u) const {
432
433
434
435
436
437
438
439
    // 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_);
440
441
442
443
}

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

457
458
459
    } else if (persist_val != "true") {
        isc_throw(isc::BadValue, "invalid value 'persist="
                  << persist_val << "'");
460
461
    }

462
463
    std::string lease_file;
    try {
464
        lease_file = getParameter("name");
465
466
467
468
469
    } catch (const Exception& ex) {
        lease_file = getDefaultLeaseFilePath(u);
    }
    return (lease_file);
}
470
471
472
473
474

void
Memfile_LeaseMgr::load4() {
    // If lease file hasn't been opened, we are working in non-persistent mode.
    // That's fine, just leave.
475
    if (!persistLeases(V4)) {
476
477
        return;
    }
478
479
480
481

    LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LEASES_RELOAD4)
        .arg(lease_file4_->getFilename());

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
    // Remove existing leases (if any). We will recreate them based on the
    // data on disk.
    storage4_.clear();

    Lease4Ptr lease;
    do {
        /// @todo Currently we stop parsing on first failure. It is possible
        /// that only one (or a few) leases are bad, so in theory we could
        /// continue parsing but that would require some error counters to
        /// prevent endless loops. That is enhancement for later time.
        if (!lease_file4_->next(lease)) {
            isc_throw(DbOperationError, "Failed to parse the DHCPv6 lease in"
                      " the lease file: " << lease_file4_->getReadMsg());
        }
        // If we got the lease, we update the internal container holding
        // leases. Otherwise, we reached the end of file and we leave.
        if (lease) {
499
500
501
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA,
                      DHCPSRV_MEMFILE_LEASE_LOAD4)
                .arg(lease->toText());
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
            loadLease4(lease);
        }
    } while (lease);
}

void
Memfile_LeaseMgr::loadLease4(Lease4Ptr& lease) {
    // Check if the lease already exists.
    Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
    // Lease doesn't exist.
    if (lease_it == storage4_.end()) {
        // Add the lease only if valid lifetime is greater than 0.
        // We use valid lifetime of 0 to indicate that lease should
        // be removed.
        if (lease->valid_lft_ > 0) {
           storage4_.insert(lease);
       }
    } else {
        // We use valid lifetime of 0 to indicate that the lease is
        // to be removed. In such case, erase the lease.
        if (lease->valid_lft_ == 0) {
            storage4_.erase(lease_it);

        } else {
            // Update existing lease.
            **lease_it = *lease;
        }
    }
}

void
Memfile_LeaseMgr::load6() {
    // If lease file hasn't been opened, we are working in non-persistent mode.
    // That's fine, just leave.
536
    if (!persistLeases(V6)) {
537
538
        return;
    }
539
540
541
542

    LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LEASES_RELOAD6)
        .arg(lease_file6_->getFilename());

543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
    // Remove existing leases (if any). We will recreate them based on the
    // data on disk.
    storage6_.clear();

    Lease6Ptr lease;
    do {
        /// @todo Currently we stop parsing on first failure. It is possible
        /// that only one (or a few) leases are bad, so in theory we could
        /// continue parsing but that would require some error counters to
        /// prevent endless loops. That is enhancement for later time.
        if (!lease_file6_->next(lease)) {
            isc_throw(DbOperationError, "Failed to parse the DHCPv6 lease in"
                      " the lease file: " << lease_file6_->getReadMsg());
        }
        // If we got the lease, we update the internal container holding
        // leases. Otherwise, we reached the end of file and we leave.
        if (lease) {
560
561
562
563
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA,
                      DHCPSRV_MEMFILE_LEASE_LOAD6)
                .arg(lease->toText());

564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
            loadLease6(lease);
        }
    } while (lease);
}

void
Memfile_LeaseMgr::loadLease6(Lease6Ptr& lease) {
    // Check if the lease already exists.
    Lease6Storage::iterator lease_it = storage6_.find(lease->addr_);
    // Lease doesn't exist.
    if (lease_it == storage6_.end()) {
        // Add the lease only if valid lifetime is greater than 0.
        // We use valid lifetime of 0 to indicate that lease should
        // be removed.
        if (lease->valid_lft_ > 0) {
            storage6_.insert(lease);
       }
    } else {
        // We use valid lifetime of 0 to indicate that the lease is
        // to be removed. In such case, erase the lease.
        if (lease->valid_lft_ == 0) {
            storage6_.erase(lease_it);

        } else {
            // Update existing lease.
            **lease_it = *lease;
        }
    }

}