diff options
Diffstat (limited to 'src/lib/dhcpsrv/csv_lease_file4.cc')
-rw-r--r-- | src/lib/dhcpsrv/csv_lease_file4.cc | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/csv_lease_file4.cc b/src/lib/dhcpsrv/csv_lease_file4.cc new file mode 100644 index 0000000..71bfd22 --- /dev/null +++ b/src/lib/dhcpsrv/csv_lease_file4.cc @@ -0,0 +1,267 @@ +// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <dhcpsrv/csv_lease_file4.h> +#include <ctime> + +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::util; + +namespace isc { +namespace dhcp { + +CSVLeaseFile4::CSVLeaseFile4(const std::string& filename) + : VersionedCSVFile(filename) { + initColumns(); +} + +void +CSVLeaseFile4::open(const bool seek_to_end) { + // Call the base class to open the file + VersionedCSVFile::open(seek_to_end); + + // and clear any statistics we may have + clearStatistics(); +} + +void +CSVLeaseFile4::append(const Lease4& lease) { + // Bump the number of write attempts + ++writes_; + + CSVRow row(getColumnCount()); + row.writeAt(getColumnIndex("address"), lease.addr_.toText()); + + if (((!lease.hwaddr_) || lease.hwaddr_->hwaddr_.empty()) && + ((!lease.client_id_) || (lease.client_id_->getClientId().empty())) && + (lease.state_ != Lease::STATE_DECLINED)) { + // Bump the error counter + ++write_errs_; + + isc_throw(BadValue, "Lease4: " << lease.addr_.toText() << ", state: " + << Lease::basicStatesToText(lease.state_) + << " has neither hardware address or client id"); + + } + + // Hardware addr may be unset (NULL). + if (lease.hwaddr_) { + row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false)); + } + + // Client id may be unset (NULL). + if (lease.client_id_) { + row.writeAt(getColumnIndex("client_id"), lease.client_id_->toText()); + } + + row.writeAt(getColumnIndex("valid_lifetime"), lease.valid_lft_); + row.writeAt(getColumnIndex("expire"), static_cast<uint64_t>(lease.cltt_) + lease.valid_lft_); + row.writeAt(getColumnIndex("subnet_id"), lease.subnet_id_); + row.writeAt(getColumnIndex("fqdn_fwd"), lease.fqdn_fwd_); + row.writeAt(getColumnIndex("fqdn_rev"), lease.fqdn_rev_); + row.writeAtEscaped(getColumnIndex("hostname"), lease.hostname_); + row.writeAt(getColumnIndex("state"), lease.state_); + // User context is optional. + if (lease.getContext()) { + row.writeAtEscaped(getColumnIndex("user_context"), lease.getContext()->str()); + } + + try { + VersionedCSVFile::append(row); + } catch (const std::exception&) { + // Catch any errors so we can bump the error counter than rethrow it + ++write_errs_; + throw; + } + + // Bump the number of leases written + ++write_leases_; +} + +bool +CSVLeaseFile4::next(Lease4Ptr& lease) { + // Bump the number of read attempts + ++reads_; + + // Read the CSV row and try to create a lease from the values read. + // This may easily result in exception. We don't want this function + // to throw exceptions, so we catch them all and rather return the + // false value. + try { + // Get the row of CSV values. + CSVRow row; + VersionedCSVFile::next(row); + // The empty row signals EOF. + if (row == CSVFile::EMPTY_ROW()) { + lease.reset(); + return (true); + } + + // Get the lease address. + IOAddress addr(readAddress(row)); + + // Get client id. It is possible that the client id is empty and the + // returned pointer is NULL. This is ok, but if the client id is NULL, + // we need to be careful to not use the NULL pointer. + ClientIdPtr client_id = readClientId(row); + std::vector<uint8_t> client_id_vec; + if (client_id) { + client_id_vec = client_id->getClientId(); + } + size_t client_id_len = client_id_vec.size(); + + // Get the HW address. It should never be empty and the readHWAddr checks + // that. + HWAddr hwaddr = readHWAddr(row); + uint32_t state = readState(row); + + if ((hwaddr.hwaddr_.empty()) && (client_id_vec.empty()) && + (state != Lease::STATE_DECLINED)) { + isc_throw(BadValue, "Lease4: " << addr.toText() << ", state: " + << Lease::basicStatesToText(state) + << " has neither hardware address or client id"); + } + + // Get the user context (can be NULL). + ConstElementPtr ctx = readContext(row); + + lease.reset(new Lease4(addr, + HWAddrPtr(new HWAddr(hwaddr)), + client_id_vec.empty() ? NULL : &client_id_vec[0], + client_id_len, + readValid(row), + readCltt(row), + readSubnetID(row), + readFqdnFwd(row), + readFqdnRev(row), + readHostname(row))); + lease->state_ = state; + + if (ctx) { + lease->setContext(ctx); + } + + } catch (const std::exception& ex) { + // bump the read error count + ++read_errs_; + + // The lease might have been created, so let's set it back to NULL to + // signal that lease hasn't been parsed. + lease.reset(); + setReadMsg(ex.what()); + return (false); + } + + // bump the number of leases read + ++read_leases_; + + return (true); +} + +void +CSVLeaseFile4::initColumns() { + addColumn("address", "1.0"); + addColumn("hwaddr", "1.0"); + addColumn("client_id", "1.0"); + addColumn("valid_lifetime", "1.0"); + addColumn("expire", "1.0"); + addColumn("subnet_id", "1.0"); + addColumn("fqdn_fwd", "1.0"); + addColumn("fqdn_rev", "1.0"); + addColumn("hostname", "1.0"); + addColumn("state", "2.0", "0"); + addColumn("user_context", "2.1"); + // Any file with less than hostname is invalid + setMinimumValidColumns("hostname"); +} + +IOAddress +CSVLeaseFile4::readAddress(const CSVRow& row) { + IOAddress address(row.readAt(getColumnIndex("address"))); + return (address); +} + +HWAddr +CSVLeaseFile4::readHWAddr(const CSVRow& row) { + HWAddr hwaddr = HWAddr::fromText(row.readAt(getColumnIndex("hwaddr"))); + return (hwaddr); +} + +ClientIdPtr +CSVLeaseFile4::readClientId(const CSVRow& row) { + std::string client_id = row.readAt(getColumnIndex("client_id")); + // NULL client ids are allowed in DHCPv4. + if (client_id.empty()) { + return (ClientIdPtr()); + } + ClientIdPtr cid = ClientId::fromText(client_id); + return (cid); +} + +uint32_t +CSVLeaseFile4::readValid(const CSVRow& row) { + uint32_t valid = + row.readAndConvertAt<uint32_t>(getColumnIndex("valid_lifetime")); + return (valid); +} + +time_t +CSVLeaseFile4::readCltt(const CSVRow& row) { + time_t cltt = + static_cast<time_t>(row.readAndConvertAt<uint64_t>(getColumnIndex("expire")) + - readValid(row)); + return (cltt); +} + +SubnetID +CSVLeaseFile4::readSubnetID(const CSVRow& row) { + SubnetID subnet_id = + row.readAndConvertAt<SubnetID>(getColumnIndex("subnet_id")); + return (subnet_id); +} + +bool +CSVLeaseFile4::readFqdnFwd(const CSVRow& row) { + bool fqdn_fwd = row.readAndConvertAt<bool>(getColumnIndex("fqdn_fwd")); + return (fqdn_fwd); +} + +bool +CSVLeaseFile4::readFqdnRev(const CSVRow& row) { + bool fqdn_rev = row.readAndConvertAt<bool>(getColumnIndex("fqdn_rev")); + return (fqdn_rev); +} + +std::string +CSVLeaseFile4::readHostname(const CSVRow& row) { + std::string hostname = row.readAtEscaped(getColumnIndex("hostname")); + return (hostname); +} + +uint32_t +CSVLeaseFile4::readState(const util::CSVRow& row) { + uint32_t state = row.readAndConvertAt<uint32_t>(getColumnIndex("state")); + return (state); +} + +ConstElementPtr +CSVLeaseFile4::readContext(const util::CSVRow& row) { + std::string user_context = row.readAtEscaped(getColumnIndex("user_context")); + if (user_context.empty()) { + return (ConstElementPtr()); + } + ConstElementPtr ctx = Element::fromJSON(user_context); + if (!ctx || (ctx->getType() != Element::map)) { + isc_throw(isc::BadValue, "user context '" << user_context + << "' is not a JSON map"); + } + return (ctx); +} + +} // end of namespace isc::dhcp +} // end of namespace isc |