memfile_lease_mgr.cc 19.6 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.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 /* not used yet */,
239
                            const isc::asiolink::IOAddress& addr) const {
240
241
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_ADDR6).arg(addr.toText());
242
243
244
245
246

    Lease6Storage::iterator l = storage6_.find(addr);
    if (l == storage6_.end()) {
        return (Lease6Ptr());
    } else {
247
        return (Lease6Ptr(new Lease6(**l)));
248
249
250
    }
}

251
Lease6Collection
252
Memfile_LeaseMgr::getLeases6(Lease::Type /* not used yet */,
253
                            const DUID& duid, uint32_t iaid) const {
254
255
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_IAID_DUID).arg(iaid).arg(duid.toText());
256

257
258
    /// @todo Not implemented.

259
260
261
    return (Lease6Collection());
}

262
Lease6Collection
263
Memfile_LeaseMgr::getLeases6(Lease::Type /* not used yet */,
264
265
                             const DUID& duid, uint32_t iaid,
                             SubnetID subnet_id) const {
266
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
267
268
269
              DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID)
              .arg(iaid).arg(subnet_id).arg(duid.toText());

270
271
272
273
274
275
276
277
278
279
280
    // 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.
    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 Subnet ID.
    SearchIndex::const_iterator lease =
        idx.find(boost::make_tuple(duid.getDuid(), iaid, subnet_id));
    // Lease was not found. Return empty pointer.
    if (lease == idx.end()) {
281
        return (Lease6Collection());
282
    }
283

284
    // Lease was found, return it to the caller.
285
286
287
288
    /// @todo: allow multiple leases for a single duid+iaid+subnet_id tuple
    Lease6Collection collection;
    collection.push_back(Lease6Ptr(new Lease6(**lease)));
    return (collection);
289
290
}

291
292
void
Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
293
294
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
295

296
297
298
    Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
    if (lease_it == storage4_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
299
                  << lease->addr_ << " - no such lease");
300
    }
301
302
303
304

    // 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.
305
    if (persistLeases(V4)) {
306
307
308
        lease_file4_->append(*lease);
    }

309
    **lease_it = *lease;
310
311
}

312
313
void
Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
314
315
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
316

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

    // 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.
326
    if (persistLeases(V6)) {
327
328
329
        lease_file6_->append(*lease);
    }

330
    **lease_it = *lease;
331
332
}

333
334
bool
Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
335
336
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_DELETE_ADDR).arg(addr.toText());
337
    if (addr.isV4()) {
338
339
340
341
342
343
        // v4 lease
        Lease4Storage::iterator l = storage4_.find(addr);
        if (l == storage4_.end()) {
            // No such lease
            return (false);
        } else {
344
            if (persistLeases(V4)) {
345
346
347
348
349
350
351
352
                // 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);
            }
353
354
355
            storage4_.erase(l);
            return (true);
        }
356

357
    } else {
358
        // v6 lease
359
360
361
362
363
        Lease6Storage::iterator l = storage6_.find(addr);
        if (l == storage6_.end()) {
            // No such lease
            return (false);
        } else {
364
            if (persistLeases(V6)) {
365
366
367
368
369
370
371
372
373
                // 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);
            }

374
375
376
            storage6_.erase(l);
            return (true);
        }
377
378
379
    }
}

380
381
std::string
Memfile_LeaseMgr::getDescription() const {
382
383
384
385
    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."));
}
386
387
388

void
Memfile_LeaseMgr::commit() {
389
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
390
391
392
393
}

void
Memfile_LeaseMgr::rollback() {
394
395
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ROLLBACK);
396
}
397
398
399
400
401
402
403
404
405
406

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

407
408
409
410
411
412
413
414
415
std::string
Memfile_LeaseMgr::getLeaseFilePath(Universe u) const {
    if (u == V4) {
        return (lease_file4_ ? lease_file4_->getFilename() : "");
    }

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

416
417
bool
Memfile_LeaseMgr::persistLeases(Universe u) const {
418
419
420
421
422
423
424
425
    // 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_);
426
427
428
429
}

std::string
Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
430
    std::string persist_val;
431
    try {
432
        persist_val = getParameter("persist");
433
    } catch (const Exception& ex) {
434
435
        // If parameter persist hasn't been specified, we use a default value
        // 'yes'.
436
        persist_val = "true";
437
    }
438
    // If persist_val is 'false' we will not store leases to disk, so let's
439
    // return empty file name.
440
    if (persist_val == "false") {
441
        return ("");
442

443
444
445
    } else if (persist_val != "true") {
        isc_throw(isc::BadValue, "invalid value 'persist="
                  << persist_val << "'");
446
447
    }

448
449
    std::string lease_file;
    try {
450
        lease_file = getParameter("name");
451
452
453
454
455
    } catch (const Exception& ex) {
        lease_file = getDefaultLeaseFilePath(u);
    }
    return (lease_file);
}
456
457
458
459
460

void
Memfile_LeaseMgr::load4() {
    // If lease file hasn't been opened, we are working in non-persistent mode.
    // That's fine, just leave.
461
    if (!persistLeases(V4)) {
462
463
        return;
    }
464
465
466
467

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

468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
    // 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) {
485
486
487
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA,
                      DHCPSRV_MEMFILE_LEASE_LOAD4)
                .arg(lease->toText());
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
            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.
522
    if (!persistLeases(V6)) {
523
524
        return;
    }
525
526
527
528

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

529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
    // 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) {
546
547
548
549
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA,
                      DHCPSRV_MEMFILE_LEASE_LOAD6)
                .arg(lease->toText());

550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
            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;
        }
    }

}